fix: replace tool parameter inputs with root directory paths (#147)
* wip: replace tool parameter inputs with root directory paths * fix: moved path resolving responsibility to tools - made path in parameters to optional for AI - internalised path resolving using session roots * chore: update package-lock.json * chore: fix regressions and fix CI * fix: make projectRoot required * fix: add-task tool * fix: updateTask tool * fix: remove reportProgress * chore: cleanup * fix: expand-task tool * chore: remove usless logs * fix: dependency manager logging in mcp server
This commit is contained in:
@@ -4,3 +4,4 @@ coverage
|
|||||||
.changeset
|
.changeset
|
||||||
tasks
|
tasks
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
tests/fixture/*.json
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { addDependency } from '../../../../scripts/modules/dependency-manager.js';
|
import { addDependency } from '../../../../scripts/modules/dependency-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -14,19 +13,32 @@ import {
|
|||||||
* Direct function wrapper for addDependency with error handling.
|
* Direct function wrapper for addDependency with error handling.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments
|
* @param {Object} args - Command arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string|number} args.id - Task ID to add dependency to
|
* @param {string|number} args.id - Task ID to add dependency to
|
||||||
* @param {string|number} args.dependsOn - Task ID that will become a dependency
|
* @param {string|number} args.dependsOn - Task ID that will become a dependency
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Result object with success status and data/error information
|
* @returns {Promise<Object>} - Result object with success status and data/error information
|
||||||
*/
|
*/
|
||||||
export async function addDependencyDirect(args, log) {
|
export async function addDependencyDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, id, dependsOn } = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Adding dependency with args: ${JSON.stringify(args)}`);
|
log.info(`Adding dependency with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
|
// Check if tasksJsonPath was provided
|
||||||
|
if (!tasksJsonPath) {
|
||||||
|
log.error('addDependencyDirect called without tasksJsonPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Validate required parameters
|
// Validate required parameters
|
||||||
if (!args.id) {
|
if (!id) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -36,7 +48,7 @@ export async function addDependencyDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.dependsOn) {
|
if (!dependsOn) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -46,18 +58,16 @@ export async function addDependencyDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Use provided path
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Format IDs for the core function
|
// Format IDs for the core function
|
||||||
const taskId =
|
const taskId =
|
||||||
args.id.includes && args.id.includes('.')
|
id && id.includes && id.includes('.') ? id : parseInt(id, 10);
|
||||||
? args.id
|
|
||||||
: parseInt(args.id, 10);
|
|
||||||
const dependencyId =
|
const dependencyId =
|
||||||
args.dependsOn.includes && args.dependsOn.includes('.')
|
dependsOn && dependsOn.includes && dependsOn.includes('.')
|
||||||
? args.dependsOn
|
? dependsOn
|
||||||
: parseInt(args.dependsOn, 10);
|
: parseInt(dependsOn, 10);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Adding dependency: task ${taskId} will depend on ${dependencyId}`
|
`Adding dependency: task ${taskId} will depend on ${dependencyId}`
|
||||||
@@ -66,7 +76,7 @@ export async function addDependencyDirect(args, log) {
|
|||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// Call the core function
|
// Call the core function using the provided path
|
||||||
await addDependency(tasksPath, taskId, dependencyId);
|
await addDependency(tasksPath, taskId, dependencyId);
|
||||||
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { addSubtask } from '../../../../scripts/modules/task-manager.js';
|
import { addSubtask } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Add a subtask to an existing task
|
* Add a subtask to an existing task
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} args.id - Parent task ID
|
* @param {string} args.id - Parent task ID
|
||||||
* @param {string} [args.taskId] - Existing task ID to convert to subtask (optional)
|
* @param {string} [args.taskId] - Existing task ID to convert to subtask (optional)
|
||||||
* @param {string} [args.title] - Title for new subtask (when creating a new subtask)
|
* @param {string} [args.title] - Title for new subtask (when creating a new subtask)
|
||||||
@@ -19,17 +19,39 @@ import {
|
|||||||
* @param {string} [args.details] - Implementation details for new subtask
|
* @param {string} [args.details] - Implementation details for new subtask
|
||||||
* @param {string} [args.status] - Status for new subtask (default: 'pending')
|
* @param {string} [args.status] - Status for new subtask (default: 'pending')
|
||||||
* @param {string} [args.dependencies] - Comma-separated list of dependency IDs
|
* @param {string} [args.dependencies] - Comma-separated list of dependency IDs
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
|
||||||
* @param {boolean} [args.skipGenerate] - Skip regenerating task files
|
* @param {boolean} [args.skipGenerate] - Skip regenerating task files
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: string}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: string}>}
|
||||||
*/
|
*/
|
||||||
export async function addSubtaskDirect(args, log) {
|
export async function addSubtaskDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const {
|
||||||
|
tasksJsonPath,
|
||||||
|
id,
|
||||||
|
taskId,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
details,
|
||||||
|
status,
|
||||||
|
dependencies: dependenciesStr,
|
||||||
|
skipGenerate
|
||||||
|
} = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
|
log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
if (!args.id) {
|
// Check if tasksJsonPath was provided
|
||||||
|
if (!tasksJsonPath) {
|
||||||
|
log.error('addSubtaskDirect called without tasksJsonPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -40,7 +62,7 @@ export async function addSubtaskDirect(args, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Either taskId or title must be provided
|
// Either taskId or title must be provided
|
||||||
if (!args.taskId && !args.title) {
|
if (!taskId && !title) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -50,26 +72,26 @@ export async function addSubtaskDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Use provided path
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Parse dependencies if provided
|
// Parse dependencies if provided
|
||||||
let dependencies = [];
|
let dependencies = [];
|
||||||
if (args.dependencies) {
|
if (dependenciesStr) {
|
||||||
dependencies = args.dependencies.split(',').map((id) => {
|
dependencies = dependenciesStr.split(',').map((depId) => {
|
||||||
// Handle both regular IDs and dot notation
|
// Handle both regular IDs and dot notation
|
||||||
return id.includes('.') ? id.trim() : parseInt(id.trim(), 10);
|
return depId.includes('.') ? depId.trim() : parseInt(depId.trim(), 10);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert existingTaskId to a number if provided
|
// Convert existingTaskId to a number if provided
|
||||||
const existingTaskId = args.taskId ? parseInt(args.taskId, 10) : null;
|
const existingTaskId = taskId ? parseInt(taskId, 10) : null;
|
||||||
|
|
||||||
// Convert parent ID to a number
|
// Convert parent ID to a number
|
||||||
const parentId = parseInt(args.id, 10);
|
const parentId = parseInt(id, 10);
|
||||||
|
|
||||||
// Determine if we should generate files
|
// Determine if we should generate files
|
||||||
const generateFiles = !args.skipGenerate;
|
const generateFiles = !skipGenerate;
|
||||||
|
|
||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
@@ -101,10 +123,10 @@ export async function addSubtaskDirect(args, log) {
|
|||||||
log.info(`Creating new subtask for parent task ${parentId}`);
|
log.info(`Creating new subtask for parent task ${parentId}`);
|
||||||
|
|
||||||
const newSubtaskData = {
|
const newSubtaskData = {
|
||||||
title: args.title,
|
title: title,
|
||||||
description: args.description || '',
|
description: description || '',
|
||||||
details: args.details || '',
|
details: details || '',
|
||||||
status: args.status || 'pending',
|
status: status || 'pending',
|
||||||
dependencies: dependencies
|
dependencies: dependencies
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { addTask } from '../../../../scripts/modules/task-manager.js';
|
import { addTask } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -38,12 +37,27 @@ import {
|
|||||||
* @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
|
* @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string } }
|
||||||
*/
|
*/
|
||||||
export async function addTaskDirect(args, log, context = {}) {
|
export async function addTaskDirect(args, log, context = {}) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, prompt, dependencies, priority, research } = args;
|
||||||
try {
|
try {
|
||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Check if tasksJsonPath was provided
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
if (!tasksJsonPath) {
|
||||||
|
log.error('addTaskDirect called without tasksJsonPath');
|
||||||
|
disableSilentMode(); // Disable before returning
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use provided path
|
||||||
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Check if this is manual task creation or AI-driven task creation
|
// Check if this is manual task creation or AI-driven task creation
|
||||||
const isManualCreation = args.title && args.description;
|
const isManualCreation = args.title && args.description;
|
||||||
@@ -65,15 +79,15 @@ export async function addTaskDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract and prepare parameters
|
// Extract and prepare parameters
|
||||||
const prompt = args.prompt;
|
const taskPrompt = prompt;
|
||||||
const dependencies = Array.isArray(args.dependencies)
|
const taskDependencies = Array.isArray(dependencies)
|
||||||
? args.dependencies
|
? dependencies
|
||||||
: args.dependencies
|
: dependencies
|
||||||
? String(args.dependencies)
|
? String(dependencies)
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((id) => parseInt(id.trim(), 10))
|
.map((id) => parseInt(id.trim(), 10))
|
||||||
: [];
|
: [];
|
||||||
const priority = args.priority || 'medium';
|
const taskPriority = priority || 'medium';
|
||||||
|
|
||||||
// Extract context parameters for advanced functionality
|
// Extract context parameters for advanced functionality
|
||||||
const { session } = context;
|
const { session } = context;
|
||||||
@@ -90,14 +104,14 @@ export async function addTaskDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Adding new task manually with title: "${args.title}", dependencies: [${dependencies.join(', ')}], priority: ${priority}`
|
`Adding new task manually with title: "${args.title}", dependencies: [${taskDependencies.join(', ')}], priority: ${priority}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Call the addTask function with manual task data
|
// Call the addTask function with manual task data
|
||||||
const newTaskId = await addTask(
|
const newTaskId = await addTask(
|
||||||
tasksPath,
|
tasksPath,
|
||||||
null, // No prompt needed for manual creation
|
null, // No prompt needed for manual creation
|
||||||
dependencies,
|
taskDependencies,
|
||||||
priority,
|
priority,
|
||||||
{
|
{
|
||||||
mcpLog: log,
|
mcpLog: log,
|
||||||
@@ -121,7 +135,7 @@ export async function addTaskDirect(args, log, context = {}) {
|
|||||||
} else {
|
} else {
|
||||||
// AI-driven task creation
|
// AI-driven task creation
|
||||||
log.info(
|
log.info(
|
||||||
`Adding new task with prompt: "${prompt}", dependencies: [${dependencies.join(', ')}], priority: ${priority}`
|
`Adding new task with prompt: "${prompt}", dependencies: [${taskDependencies.join(', ')}], priority: ${priority}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize AI client with session environment
|
// Initialize AI client with session environment
|
||||||
@@ -207,7 +221,7 @@ export async function addTaskDirect(args, log, context = {}) {
|
|||||||
const newTaskId = await addTask(
|
const newTaskId = await addTask(
|
||||||
tasksPath,
|
tasksPath,
|
||||||
prompt,
|
prompt,
|
||||||
dependencies,
|
taskDependencies,
|
||||||
priority,
|
priority,
|
||||||
{
|
{
|
||||||
mcpLog: log,
|
mcpLog: log,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { analyzeTaskComplexity } from '../../../../scripts/modules/task-manager.js';
|
import { analyzeTaskComplexity } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode,
|
disableSilentMode,
|
||||||
@@ -16,45 +15,60 @@ import path from 'path';
|
|||||||
/**
|
/**
|
||||||
* Analyze task complexity and generate recommendations
|
* Analyze task complexity and generate recommendations
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} [args.output] - Output file path for the report
|
* @param {string} args.outputPath - Explicit absolute path to save the report.
|
||||||
* @param {string} [args.model] - LLM model to use for analysis
|
* @param {string} [args.model] - LLM model to use for analysis
|
||||||
* @param {string|number} [args.threshold] - Minimum complexity score to recommend expansion (1-10)
|
* @param {string|number} [args.threshold] - Minimum complexity score to recommend expansion (1-10)
|
||||||
* @param {boolean} [args.research] - Use Perplexity AI for research-backed complexity analysis
|
* @param {boolean} [args.research] - Use Perplexity AI for research-backed complexity analysis
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @param {Object} [context={}] - Context object containing session data
|
* @param {Object} [context={}] - Context object containing session data
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||||
*/
|
*/
|
||||||
export async function analyzeTaskComplexityDirect(args, log, context = {}) {
|
export async function analyzeTaskComplexityDirect(args, log, context = {}) {
|
||||||
const { session } = context; // Only extract session, not reportProgress
|
const { session } = context; // Only extract session, not reportProgress
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, outputPath, model, threshold, research } = args;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info(`Analyzing task complexity with args: ${JSON.stringify(args)}`);
|
log.info(`Analyzing task complexity with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Check if required paths were provided
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
if (!tasksJsonPath) {
|
||||||
|
log.error('analyzeTaskComplexityDirect called without tasksJsonPath');
|
||||||
// Determine output path
|
return {
|
||||||
let outputPath = args.output || 'scripts/task-complexity-report.json';
|
success: false,
|
||||||
if (!path.isAbsolute(outputPath) && args.projectRoot) {
|
error: {
|
||||||
outputPath = path.join(args.projectRoot, outputPath);
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!outputPath) {
|
||||||
|
log.error('analyzeTaskComplexityDirect called without outputPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: { code: 'MISSING_ARGUMENT', message: 'outputPath is required' }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Analyzing task complexity from: ${tasksPath}`);
|
// Use the provided paths
|
||||||
log.info(`Output report will be saved to: ${outputPath}`);
|
const tasksPath = tasksJsonPath;
|
||||||
|
const resolvedOutputPath = outputPath;
|
||||||
|
|
||||||
if (args.research) {
|
log.info(`Analyzing task complexity from: ${tasksPath}`);
|
||||||
|
log.info(`Output report will be saved to: ${resolvedOutputPath}`);
|
||||||
|
|
||||||
|
if (research) {
|
||||||
log.info('Using Perplexity AI for research-backed complexity analysis');
|
log.info('Using Perplexity AI for research-backed complexity analysis');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create options object for analyzeTaskComplexity
|
// Create options object for analyzeTaskComplexity using provided paths
|
||||||
const options = {
|
const options = {
|
||||||
file: tasksPath,
|
file: tasksPath,
|
||||||
output: outputPath,
|
output: resolvedOutputPath,
|
||||||
model: args.model,
|
model: model,
|
||||||
threshold: args.threshold,
|
threshold: threshold,
|
||||||
research: args.research === true
|
research: research === true
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
@@ -95,7 +109,7 @@ export async function analyzeTaskComplexityDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify the report file was created
|
// Verify the report file was created
|
||||||
if (!fs.existsSync(outputPath)) {
|
if (!fs.existsSync(resolvedOutputPath)) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -108,7 +122,7 @@ export async function analyzeTaskComplexityDirect(args, log, context = {}) {
|
|||||||
// Read the report file
|
// Read the report file
|
||||||
let report;
|
let report;
|
||||||
try {
|
try {
|
||||||
report = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
|
report = JSON.parse(fs.readFileSync(resolvedOutputPath, 'utf8'));
|
||||||
|
|
||||||
// Important: Handle different report formats
|
// Important: Handle different report formats
|
||||||
// The core function might return an array or an object with a complexityAnalysis property
|
// The core function might return an array or an object with a complexityAnalysis property
|
||||||
@@ -130,8 +144,8 @@ export async function analyzeTaskComplexityDirect(args, log, context = {}) {
|
|||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
message: `Task complexity analysis complete. Report saved to ${outputPath}`,
|
message: `Task complexity analysis complete. Report saved to ${resolvedOutputPath}`,
|
||||||
reportPath: outputPath,
|
reportPath: resolvedOutputPath,
|
||||||
reportSummary: {
|
reportSummary: {
|
||||||
taskCount: analysisArray.length,
|
taskCount: analysisArray.length,
|
||||||
highComplexityTasks,
|
highComplexityTasks,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { clearSubtasks } from '../../../../scripts/modules/task-manager.js';
|
import { clearSubtasks } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -13,19 +12,32 @@ import fs from 'fs';
|
|||||||
/**
|
/**
|
||||||
* Clear subtasks from specified tasks
|
* Clear subtasks from specified tasks
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} [args.id] - Task IDs (comma-separated) to clear subtasks from
|
* @param {string} [args.id] - Task IDs (comma-separated) to clear subtasks from
|
||||||
* @param {boolean} [args.all] - Clear subtasks from all tasks
|
* @param {boolean} [args.all] - Clear subtasks from all tasks
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||||
*/
|
*/
|
||||||
export async function clearSubtasksDirect(args, log) {
|
export async function clearSubtasksDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, id, all } = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
|
log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
|
// Check if tasksJsonPath was provided
|
||||||
|
if (!tasksJsonPath) {
|
||||||
|
log.error('clearSubtasksDirect called without tasksJsonPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Either id or all must be provided
|
// Either id or all must be provided
|
||||||
if (!args.id && !args.all) {
|
if (!id && !all) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -36,8 +48,8 @@ export async function clearSubtasksDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Use provided path
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Check if tasks.json exists
|
// Check if tasks.json exists
|
||||||
if (!fs.existsSync(tasksPath)) {
|
if (!fs.existsSync(tasksPath)) {
|
||||||
@@ -53,7 +65,7 @@ export async function clearSubtasksDirect(args, log) {
|
|||||||
let taskIds;
|
let taskIds;
|
||||||
|
|
||||||
// If all is specified, get all task IDs
|
// If all is specified, get all task IDs
|
||||||
if (args.all) {
|
if (all) {
|
||||||
log.info('Clearing subtasks from all tasks');
|
log.info('Clearing subtasks from all tasks');
|
||||||
const data = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
const data = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
|
||||||
if (!data || !data.tasks || data.tasks.length === 0) {
|
if (!data || !data.tasks || data.tasks.length === 0) {
|
||||||
@@ -68,7 +80,7 @@ export async function clearSubtasksDirect(args, log) {
|
|||||||
taskIds = data.tasks.map((t) => t.id).join(',');
|
taskIds = data.tasks.map((t) => t.id).join(',');
|
||||||
} else {
|
} else {
|
||||||
// Use the provided task IDs
|
// Use the provided task IDs
|
||||||
taskIds = args.id;
|
taskIds = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Clearing subtasks from tasks: ${taskIds}`);
|
log.info(`Clearing subtasks from tasks: ${taskIds}`);
|
||||||
|
|||||||
@@ -8,37 +8,34 @@ import {
|
|||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Direct function wrapper for displaying the complexity report with error handling and caching.
|
* Direct function wrapper for displaying the complexity report with error handling and caching.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments containing file path option
|
* @param {Object} args - Command arguments containing reportPath.
|
||||||
|
* @param {string} args.reportPath - Explicit path to the complexity report file.
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Result object with success status and data/error information
|
* @returns {Promise<Object>} - Result object with success status and data/error information
|
||||||
*/
|
*/
|
||||||
export async function complexityReportDirect(args, log) {
|
export async function complexityReportDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { reportPath } = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);
|
log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Get tasks file path to determine project root for the default report location
|
// Check if reportPath was provided
|
||||||
let tasksPath;
|
if (!reportPath) {
|
||||||
try {
|
log.error('complexityReportDirect called without reportPath');
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
return {
|
||||||
} catch (error) {
|
success: false,
|
||||||
log.warn(
|
error: { code: 'MISSING_ARGUMENT', message: 'reportPath is required' },
|
||||||
`Tasks file not found, using current directory: ${error.message}`
|
fromCache: false
|
||||||
);
|
};
|
||||||
// Continue with default or specified report path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get report file path from args or use default
|
// Use the provided report path
|
||||||
const reportPath =
|
|
||||||
args.file ||
|
|
||||||
path.join(process.cwd(), 'scripts', 'task-complexity-report.json');
|
|
||||||
|
|
||||||
log.info(`Looking for complexity report at: ${reportPath}`);
|
log.info(`Looking for complexity report at: ${reportPath}`);
|
||||||
|
|
||||||
// Generate cache key based on report path
|
// Generate cache key based on report path
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
disableSilentMode,
|
disableSilentMode,
|
||||||
isSilentMode
|
isSilentMode
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import { getAnthropicClientForMCP } from '../utils/ai-client-utils.js';
|
import { getAnthropicClientForMCP } from '../utils/ai-client-utils.js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
@@ -16,34 +15,51 @@ import fs from 'fs';
|
|||||||
/**
|
/**
|
||||||
* Expand all pending tasks with subtasks
|
* Expand all pending tasks with subtasks
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {number|string} [args.num] - Number of subtasks to generate
|
* @param {number|string} [args.num] - Number of subtasks to generate
|
||||||
* @param {boolean} [args.research] - Enable Perplexity AI for research-backed subtask generation
|
* @param {boolean} [args.research] - Enable Perplexity AI for research-backed subtask generation
|
||||||
* @param {string} [args.prompt] - Additional context to guide subtask generation
|
* @param {string} [args.prompt] - Additional context to guide subtask generation
|
||||||
* @param {boolean} [args.force] - Force regeneration of subtasks for tasks that already have them
|
* @param {boolean} [args.force] - Force regeneration of subtasks for tasks that already have them
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @param {Object} context - Context object containing session
|
* @param {Object} context - Context object containing session
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||||
*/
|
*/
|
||||||
export async function expandAllTasksDirect(args, log, context = {}) {
|
export async function expandAllTasksDirect(args, log, context = {}) {
|
||||||
const { session } = context; // Only extract session, not reportProgress
|
const { session } = context; // Only extract session, not reportProgress
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, num, research, prompt, force } = args;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info(`Expanding all tasks with args: ${JSON.stringify(args)}`);
|
log.info(`Expanding all tasks with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
|
// Check if tasksJsonPath was provided
|
||||||
|
if (!tasksJsonPath) {
|
||||||
|
log.error('expandAllTasksDirect called without tasksJsonPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Enable silent mode early to prevent any console output
|
// Enable silent mode early to prevent any console output
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Find the tasks.json path
|
// Remove internal path finding
|
||||||
|
/*
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
const tasksPath = findTasksJsonPath(args, log);
|
||||||
|
*/
|
||||||
|
// Use provided path
|
||||||
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Parse parameters
|
// Parse parameters
|
||||||
const numSubtasks = args.num ? parseInt(args.num, 10) : undefined;
|
const numSubtasks = num ? parseInt(num, 10) : undefined;
|
||||||
const useResearch = args.research === true;
|
const useResearch = research === true;
|
||||||
const additionalContext = args.prompt || '';
|
const additionalContext = prompt || '';
|
||||||
const forceFlag = args.force === true;
|
const forceFlag = force === true;
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Expanding all tasks with ${numSubtasks || 'default'} subtasks each...`
|
`Expanding all tasks with ${numSubtasks || 'default'} subtasks each...`
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
disableSilentMode,
|
disableSilentMode,
|
||||||
isSilentMode
|
isSilentMode
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
getAnthropicClientForMCP,
|
getAnthropicClientForMCP,
|
||||||
getModelConfig
|
getModelConfig
|
||||||
@@ -23,12 +22,20 @@ import fs from 'fs';
|
|||||||
* Direct function wrapper for expanding a task into subtasks with error handling.
|
* Direct function wrapper for expanding a task into subtasks with error handling.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments
|
* @param {Object} args - Command arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
|
* @param {string} args.id - The ID of the task to expand.
|
||||||
|
* @param {number|string} [args.num] - Number of subtasks to generate.
|
||||||
|
* @param {boolean} [args.research] - Enable Perplexity AI for research-backed subtask generation.
|
||||||
|
* @param {string} [args.prompt] - Additional context to guide subtask generation.
|
||||||
|
* @param {boolean} [args.force] - Force expansion even if subtasks exist.
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @param {Object} context - Context object containing session and reportProgress
|
* @param {Object} context - Context object containing session and reportProgress
|
||||||
* @returns {Promise<Object>} - Task expansion result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
* @returns {Promise<Object>} - Task expansion result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
||||||
*/
|
*/
|
||||||
export async function expandTaskDirect(args, log, context = {}) {
|
export async function expandTaskDirect(args, log, context = {}) {
|
||||||
const { session } = context;
|
const { session } = context;
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, id, num, research, prompt, force } = args;
|
||||||
|
|
||||||
// Log session root data for debugging
|
// Log session root data for debugging
|
||||||
log.info(
|
log.info(
|
||||||
@@ -40,48 +47,26 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
})}`
|
})}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let tasksPath;
|
// Check if tasksJsonPath was provided
|
||||||
try {
|
if (!tasksJsonPath) {
|
||||||
// If a direct file path is provided, use it directly
|
log.error('expandTaskDirect called without tasksJsonPath');
|
||||||
if (args.file && fs.existsSync(args.file)) {
|
|
||||||
log.info(
|
|
||||||
`[expandTaskDirect] Using explicitly provided tasks file: ${args.file}`
|
|
||||||
);
|
|
||||||
tasksPath = args.file;
|
|
||||||
} else {
|
|
||||||
// Find the tasks path through standard logic
|
|
||||||
log.info(
|
|
||||||
`[expandTaskDirect] No direct file path provided or file not found at ${args.file}, searching using findTasksJsonPath`
|
|
||||||
);
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
log.error(
|
|
||||||
`[expandTaskDirect] Error during tasksPath determination: ${error.message}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Include session roots information in error
|
|
||||||
const sessionRootsInfo = session
|
|
||||||
? `\nSession.roots: ${JSON.stringify(session.roots)}\n` +
|
|
||||||
`Current Working Directory: ${process.cwd()}\n` +
|
|
||||||
`Args.projectRoot: ${args.projectRoot}\n` +
|
|
||||||
`Args.file: ${args.file}\n`
|
|
||||||
: '\nSession object not available';
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'FILE_NOT_FOUND_ERROR',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: `Error determining tasksPath: ${error.message}${sessionRootsInfo}`
|
message: 'tasksJsonPath is required'
|
||||||
},
|
},
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`[expandTaskDirect] Determined tasksPath: ${tasksPath}`);
|
// Use provided path
|
||||||
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
|
log.info(`[expandTaskDirect] Using tasksPath: ${tasksPath}`);
|
||||||
|
|
||||||
// Validate task ID
|
// Validate task ID
|
||||||
const taskId = args.id ? parseInt(args.id, 10) : null;
|
const taskId = id ? parseInt(id, 10) : null;
|
||||||
if (!taskId) {
|
if (!taskId) {
|
||||||
log.error('Task ID is required');
|
log.error('Task ID is required');
|
||||||
return {
|
return {
|
||||||
@@ -95,9 +80,10 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process other parameters
|
// Process other parameters
|
||||||
const numSubtasks = args.num ? parseInt(args.num, 10) : undefined;
|
const numSubtasks = num ? parseInt(num, 10) : undefined;
|
||||||
const useResearch = args.research === true;
|
const useResearch = research === true;
|
||||||
const additionalContext = args.prompt || '';
|
const additionalContext = prompt || '';
|
||||||
|
const forceFlag = force === true;
|
||||||
|
|
||||||
// Initialize AI client if needed (for expandTask function)
|
// Initialize AI client if needed (for expandTask function)
|
||||||
try {
|
try {
|
||||||
@@ -172,15 +158,16 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for existing subtasks
|
// Check for existing subtasks and force flag
|
||||||
const hasExistingSubtasks = task.subtasks && task.subtasks.length > 0;
|
const hasExistingSubtasks = task.subtasks && task.subtasks.length > 0;
|
||||||
|
if (hasExistingSubtasks && !forceFlag) {
|
||||||
// If the task already has subtasks, just return it (matching core behavior)
|
log.info(
|
||||||
if (hasExistingSubtasks) {
|
`Task ${taskId} already has ${task.subtasks.length} subtasks. Use --force to overwrite.`
|
||||||
log.info(`Task ${taskId} already has ${task.subtasks.length} subtasks`);
|
);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
|
message: `Task ${taskId} already has subtasks. Expansion skipped.`,
|
||||||
task,
|
task,
|
||||||
subtasksAdded: 0,
|
subtasksAdded: 0,
|
||||||
hasExistingSubtasks
|
hasExistingSubtasks
|
||||||
@@ -189,6 +176,14 @@ export async function expandTaskDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If force flag is set, clear existing subtasks
|
||||||
|
if (hasExistingSubtasks && forceFlag) {
|
||||||
|
log.info(
|
||||||
|
`Force flag set. Clearing existing subtasks for task ${taskId}.`
|
||||||
|
);
|
||||||
|
task.subtasks = [];
|
||||||
|
}
|
||||||
|
|
||||||
// Keep a copy of the task before modification
|
// Keep a copy of the task before modification
|
||||||
const originalTask = JSON.parse(JSON.stringify(task));
|
const originalTask = JSON.parse(JSON.stringify(task));
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { fixDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
import { fixDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -13,17 +12,30 @@ import fs from 'fs';
|
|||||||
/**
|
/**
|
||||||
* Fix invalid dependencies in tasks.json automatically
|
* Fix invalid dependencies in tasks.json automatically
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||||
*/
|
*/
|
||||||
export async function fixDependenciesDirect(args, log) {
|
export async function fixDependenciesDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath } = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Fixing invalid dependencies in tasks...`);
|
log.info(`Fixing invalid dependencies in tasks: ${tasksJsonPath}`);
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Check if tasksJsonPath was provided
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
if (!tasksJsonPath) {
|
||||||
|
log.error('fixDependenciesDirect called without tasksJsonPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use provided path
|
||||||
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Verify the file exists
|
// Verify the file exists
|
||||||
if (!fs.existsSync(tasksPath)) {
|
if (!fs.existsSync(tasksPath)) {
|
||||||
@@ -39,7 +51,7 @@ export async function fixDependenciesDirect(args, log) {
|
|||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// Call the original command function
|
// Call the original command function using the provided path
|
||||||
await fixDependenciesCommand(tasksPath);
|
await fixDependenciesCommand(tasksPath);
|
||||||
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
|
|||||||
@@ -8,40 +8,46 @@ import {
|
|||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Direct function wrapper for generateTaskFiles with error handling.
|
* Direct function wrapper for generateTaskFiles with error handling.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments containing file and output path options.
|
* @param {Object} args - Command arguments containing tasksJsonPath and outputDir.
|
||||||
* @param {Object} log - Logger object.
|
* @param {Object} log - Logger object.
|
||||||
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
||||||
*/
|
*/
|
||||||
export async function generateTaskFilesDirect(args, log) {
|
export async function generateTaskFilesDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, outputDir } = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Generating task files with args: ${JSON.stringify(args)}`);
|
log.info(`Generating task files with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Get tasks file path
|
// Check if paths were provided
|
||||||
let tasksPath;
|
if (!tasksJsonPath) {
|
||||||
try {
|
const errorMessage = 'tasksJsonPath is required but was not provided.';
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
log.error(errorMessage);
|
||||||
} catch (error) {
|
|
||||||
log.error(`Error finding tasks file: ${error.message}`);
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'TASKS_FILE_ERROR', message: error.message },
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
||||||
|
fromCache: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!outputDir) {
|
||||||
|
const errorMessage = 'outputDir is required but was not provided.';
|
||||||
|
log.error(errorMessage);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get output directory (defaults to the same directory as the tasks file)
|
// Use the provided paths
|
||||||
let outputDir = args.output;
|
const tasksPath = tasksJsonPath;
|
||||||
if (!outputDir) {
|
const resolvedOutputDir = outputDir;
|
||||||
outputDir = path.dirname(tasksPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info(`Generating task files from ${tasksPath} to ${outputDir}`);
|
log.info(`Generating task files from ${tasksPath} to ${resolvedOutputDir}`);
|
||||||
|
|
||||||
// Execute core generateTaskFiles function in a separate try/catch
|
// Execute core generateTaskFiles function in a separate try/catch
|
||||||
try {
|
try {
|
||||||
@@ -49,7 +55,7 @@ export async function generateTaskFilesDirect(args, log) {
|
|||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// The function is synchronous despite being awaited elsewhere
|
// The function is synchronous despite being awaited elsewhere
|
||||||
generateTaskFiles(tasksPath, outputDir);
|
generateTaskFiles(tasksPath, resolvedOutputDir);
|
||||||
|
|
||||||
// Restore normal logging after task generation
|
// Restore normal logging after task generation
|
||||||
disableSilentMode();
|
disableSilentMode();
|
||||||
@@ -70,8 +76,8 @@ export async function generateTaskFilesDirect(args, log) {
|
|||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
message: `Successfully generated task files`,
|
message: `Successfully generated task files`,
|
||||||
tasksPath,
|
tasksPath: tasksPath,
|
||||||
outputDir,
|
outputDir: resolvedOutputDir,
|
||||||
taskFiles:
|
taskFiles:
|
||||||
'Individual task files have been generated in the output directory'
|
'Individual task files have been generated in the output directory'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import path from 'path';
|
import { initializeProject } from '../../../../scripts/init.js'; // Import core function and its logger if needed separately
|
||||||
import { initializeProject, log as initLog } from '../../../../scripts/init.js'; // Import core function and its logger if needed separately
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { listTasks } from '../../../../scripts/modules/task-manager.js';
|
import { listTasks } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -14,38 +13,30 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Direct function wrapper for listTasks with error handling and caching.
|
* Direct function wrapper for listTasks with error handling and caching.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments (projectRoot is expected to be resolved).
|
* @param {Object} args - Command arguments (now expecting tasksJsonPath explicitly).
|
||||||
* @param {Object} log - Logger object.
|
* @param {Object} log - Logger object.
|
||||||
* @returns {Promise<Object>} - Task list result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }.
|
* @returns {Promise<Object>} - Task list result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }.
|
||||||
*/
|
*/
|
||||||
export async function listTasksDirect(args, log) {
|
export async function listTasksDirect(args, log) {
|
||||||
let tasksPath;
|
// Destructure the explicit tasksJsonPath from args
|
||||||
try {
|
const { tasksJsonPath, status, withSubtasks } = args;
|
||||||
// Find the tasks path first - needed for cache key and execution
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
if (!tasksJsonPath) {
|
||||||
} catch (error) {
|
log.error('listTasksDirect called without tasksJsonPath');
|
||||||
if (error.code === 'TASKS_FILE_NOT_FOUND') {
|
|
||||||
log.error(`Tasks file not found: ${error.message}`);
|
|
||||||
// Return the error structure expected by the calling tool/handler
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: error.code, message: error.message },
|
error: {
|
||||||
fromCache: false
|
code: 'MISSING_ARGUMENT',
|
||||||
};
|
message: 'tasksJsonPath is required'
|
||||||
}
|
},
|
||||||
log.error(`Unexpected error finding tasks file: ${error.message}`);
|
|
||||||
// Re-throw for outer catch or return structured error
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: { code: 'FIND_TASKS_PATH_ERROR', message: error.message },
|
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate cache key *after* finding tasksPath
|
// Use the explicit tasksJsonPath for cache key
|
||||||
const statusFilter = args.status || 'all';
|
const statusFilter = status || 'all';
|
||||||
const withSubtasks = args.withSubtasks || false;
|
const withSubtasksFilter = withSubtasks || false;
|
||||||
const cacheKey = `listTasks:${tasksPath}:${statusFilter}:${withSubtasks}`;
|
const cacheKey = `listTasks:${tasksJsonPath}:${statusFilter}:${withSubtasksFilter}`;
|
||||||
|
|
||||||
// Define the action function to be executed on cache miss
|
// Define the action function to be executed on cache miss
|
||||||
const coreListTasksAction = async () => {
|
const coreListTasksAction = async () => {
|
||||||
@@ -54,12 +45,13 @@ export async function listTasksDirect(args, log) {
|
|||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Executing core listTasks function for path: ${tasksPath}, filter: ${statusFilter}, subtasks: ${withSubtasks}`
|
`Executing core listTasks function for path: ${tasksJsonPath}, filter: ${statusFilter}, subtasks: ${withSubtasksFilter}`
|
||||||
);
|
);
|
||||||
|
// Pass the explicit tasksJsonPath to the core function
|
||||||
const resultData = listTasks(
|
const resultData = listTasks(
|
||||||
tasksPath,
|
tasksJsonPath,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
withSubtasks,
|
withSubtasksFilter,
|
||||||
'json'
|
'json'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import { findNextTask } from '../../../../scripts/modules/task-manager.js';
|
import { findNextTask } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { readJSON } from '../../../../scripts/modules/utils.js';
|
import { readJSON } from '../../../../scripts/modules/utils.js';
|
||||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -16,28 +15,28 @@ import {
|
|||||||
* Direct function wrapper for finding the next task to work on with error handling and caching.
|
* Direct function wrapper for finding the next task to work on with error handling and caching.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments
|
* @param {Object} args - Command arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Next task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
* @returns {Promise<Object>} - Next task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
||||||
*/
|
*/
|
||||||
export async function nextTaskDirect(args, log) {
|
export async function nextTaskDirect(args, log) {
|
||||||
let tasksPath;
|
// Destructure expected args
|
||||||
try {
|
const { tasksJsonPath } = args;
|
||||||
// Find the tasks path first - needed for cache key and execution
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
if (!tasksJsonPath) {
|
||||||
} catch (error) {
|
log.error('nextTaskDirect called without tasksJsonPath');
|
||||||
log.error(`Tasks file not found: ${error.message}`);
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'FILE_NOT_FOUND_ERROR',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: error.message
|
message: 'tasksJsonPath is required'
|
||||||
},
|
},
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate cache key using task path
|
// Generate cache key using the provided task path
|
||||||
const cacheKey = `nextTask:${tasksPath}`;
|
const cacheKey = `nextTask:${tasksJsonPath}`;
|
||||||
|
|
||||||
// Define the action function to be executed on cache miss
|
// Define the action function to be executed on cache miss
|
||||||
const coreNextTaskAction = async () => {
|
const coreNextTaskAction = async () => {
|
||||||
@@ -45,16 +44,17 @@ export async function nextTaskDirect(args, log) {
|
|||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
log.info(`Finding next task from ${tasksPath}`);
|
log.info(`Finding next task from ${tasksJsonPath}`);
|
||||||
|
|
||||||
// Read tasks data
|
// Read tasks data using the provided path
|
||||||
const data = readJSON(tasksPath);
|
const data = readJSON(tasksJsonPath);
|
||||||
if (!data || !data.tasks) {
|
if (!data || !data.tasks) {
|
||||||
|
disableSilentMode(); // Disable before return
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'INVALID_TASKS_FILE',
|
code: 'INVALID_TASKS_FILE',
|
||||||
message: `No valid tasks found in ${tasksPath}`
|
message: `No valid tasks found in ${tasksJsonPath}`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,21 +47,9 @@ export async function parsePRDDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Parameter validation and path resolution ---
|
// Validate required parameters
|
||||||
if (!args.input) {
|
|
||||||
const errorMessage =
|
|
||||||
'No input file specified. Please provide an input PRD document path.';
|
|
||||||
log.error(errorMessage);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: { code: 'MISSING_INPUT_FILE', message: errorMessage },
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate projectRoot
|
|
||||||
if (!args.projectRoot) {
|
if (!args.projectRoot) {
|
||||||
const errorMessage = 'Project root is required but was not provided';
|
const errorMessage = 'Project root is required for parsePRDDirect';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -70,53 +58,32 @@ export async function parsePRDDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const homeDir = os.homedir();
|
if (!args.input) {
|
||||||
// Disallow invalid projectRoot values
|
const errorMessage = 'Input file path is required for parsePRDDirect';
|
||||||
if (args.projectRoot === '/' || args.projectRoot === homeDir) {
|
|
||||||
const errorMessage = `Invalid project root: ${args.projectRoot}. Cannot use root or home directory.`;
|
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: { code: 'INVALID_PROJECT_ROOT', message: errorMessage },
|
error: { code: 'MISSING_INPUT_PATH', message: errorMessage },
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve input path (relative to validated project root)
|
if (!args.output) {
|
||||||
|
const errorMessage = 'Output file path is required for parsePRDDirect';
|
||||||
|
log.error(errorMessage);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: { code: 'MISSING_OUTPUT_PATH', message: errorMessage },
|
||||||
|
fromCache: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve input path (expecting absolute path or path relative to project root)
|
||||||
const projectRoot = args.projectRoot;
|
const projectRoot = args.projectRoot;
|
||||||
log.info(`Using validated project root: ${projectRoot}`);
|
|
||||||
|
|
||||||
// Make sure the project root directory exists
|
|
||||||
if (!fs.existsSync(projectRoot)) {
|
|
||||||
const errorMessage = `Project root directory does not exist: ${projectRoot}`;
|
|
||||||
log.error(errorMessage);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: { code: 'PROJECT_ROOT_NOT_FOUND', message: errorMessage },
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve input path relative to validated project root
|
|
||||||
const inputPath = path.isAbsolute(args.input)
|
const inputPath = path.isAbsolute(args.input)
|
||||||
? args.input
|
? args.input
|
||||||
: path.resolve(projectRoot, args.input);
|
: path.resolve(projectRoot, args.input);
|
||||||
|
|
||||||
log.info(`Resolved input path: ${inputPath}`);
|
|
||||||
|
|
||||||
// Determine output path
|
|
||||||
let outputPath;
|
|
||||||
if (args.output) {
|
|
||||||
outputPath = path.isAbsolute(args.output)
|
|
||||||
? args.output
|
|
||||||
: path.resolve(projectRoot, args.output);
|
|
||||||
} else {
|
|
||||||
// Default to tasks/tasks.json in the project root
|
|
||||||
outputPath = path.resolve(projectRoot, 'tasks', 'tasks.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info(`Resolved output path: ${outputPath}`);
|
|
||||||
|
|
||||||
// Verify input file exists
|
// Verify input file exists
|
||||||
if (!fs.existsSync(inputPath)) {
|
if (!fs.existsSync(inputPath)) {
|
||||||
const errorMessage = `Input file not found: ${inputPath}`;
|
const errorMessage = `Input file not found: ${inputPath}`;
|
||||||
@@ -132,6 +99,18 @@ export async function parsePRDDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve output path (expecting absolute path or path relative to project root)
|
||||||
|
const outputPath = path.isAbsolute(args.output)
|
||||||
|
? args.output
|
||||||
|
: path.resolve(projectRoot, args.output);
|
||||||
|
|
||||||
|
// Ensure output directory exists
|
||||||
|
const outputDir = path.dirname(outputPath);
|
||||||
|
if (!fs.existsSync(outputDir)) {
|
||||||
|
log.info(`Creating output directory: ${outputDir}`);
|
||||||
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
// Parse number of tasks - handle both string and number values
|
// Parse number of tasks - handle both string and number values
|
||||||
let numTasks = 10; // Default
|
let numTasks = 10; // Default
|
||||||
if (args.numTasks) {
|
if (args.numTasks) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { removeDependency } from '../../../../scripts/modules/dependency-manager.js';
|
import { removeDependency } from '../../../../scripts/modules/dependency-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -12,19 +11,32 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Remove a dependency from a task
|
* Remove a dependency from a task
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string|number} args.id - Task ID to remove dependency from
|
* @param {string|number} args.id - Task ID to remove dependency from
|
||||||
* @param {string|number} args.dependsOn - Task ID to remove as a dependency
|
* @param {string|number} args.dependsOn - Task ID to remove as a dependency
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||||
*/
|
*/
|
||||||
export async function removeDependencyDirect(args, log) {
|
export async function removeDependencyDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, id, dependsOn } = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Removing dependency with args: ${JSON.stringify(args)}`);
|
log.info(`Removing dependency with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
|
// Check if tasksJsonPath was provided
|
||||||
|
if (!tasksJsonPath) {
|
||||||
|
log.error('removeDependencyDirect called without tasksJsonPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Validate required parameters
|
// Validate required parameters
|
||||||
if (!args.id) {
|
if (!id) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -34,7 +46,7 @@ export async function removeDependencyDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.dependsOn) {
|
if (!dependsOn) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -44,18 +56,16 @@ export async function removeDependencyDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Use provided path
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Format IDs for the core function
|
// Format IDs for the core function
|
||||||
const taskId =
|
const taskId =
|
||||||
args.id.includes && args.id.includes('.')
|
id && id.includes && id.includes('.') ? id : parseInt(id, 10);
|
||||||
? args.id
|
|
||||||
: parseInt(args.id, 10);
|
|
||||||
const dependencyId =
|
const dependencyId =
|
||||||
args.dependsOn.includes && args.dependsOn.includes('.')
|
dependsOn && dependsOn.includes && dependsOn.includes('.')
|
||||||
? args.dependsOn
|
? dependsOn
|
||||||
: parseInt(args.dependsOn, 10);
|
: parseInt(dependsOn, 10);
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Removing dependency: task ${taskId} no longer depends on ${dependencyId}`
|
`Removing dependency: task ${taskId} no longer depends on ${dependencyId}`
|
||||||
@@ -64,7 +74,7 @@ export async function removeDependencyDirect(args, log) {
|
|||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// Call the core function
|
// Call the core function using the provided tasksPath
|
||||||
await removeDependency(tasksPath, taskId, dependencyId);
|
await removeDependency(tasksPath, taskId, dependencyId);
|
||||||
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { removeSubtask } from '../../../../scripts/modules/task-manager.js';
|
import { removeSubtask } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -12,22 +11,37 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Remove a subtask from its parent task
|
* Remove a subtask from its parent task
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} args.id - Subtask ID in format "parentId.subtaskId" (required)
|
* @param {string} args.id - Subtask ID in format "parentId.subtaskId" (required)
|
||||||
* @param {boolean} [args.convert] - Whether to convert the subtask to a standalone task
|
* @param {boolean} [args.convert] - Whether to convert the subtask to a standalone task
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
|
||||||
* @param {boolean} [args.skipGenerate] - Skip regenerating task files
|
* @param {boolean} [args.skipGenerate] - Skip regenerating task files
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||||
*/
|
*/
|
||||||
export async function removeSubtaskDirect(args, log) {
|
export async function removeSubtaskDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, id, convert, skipGenerate } = args;
|
||||||
try {
|
try {
|
||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
|
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
if (!args.id) {
|
// Check if tasksJsonPath was provided
|
||||||
|
if (!tasksJsonPath) {
|
||||||
|
log.error('removeSubtaskDirect called without tasksJsonPath');
|
||||||
|
disableSilentMode(); // Disable before returning
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
disableSilentMode(); // Disable before returning
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
@@ -39,32 +53,34 @@ export async function removeSubtaskDirect(args, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate subtask ID format
|
// Validate subtask ID format
|
||||||
if (!args.id.includes('.')) {
|
if (!id.includes('.')) {
|
||||||
|
disableSilentMode(); // Disable before returning
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'INPUT_VALIDATION_ERROR',
|
code: 'INPUT_VALIDATION_ERROR',
|
||||||
message: `Invalid subtask ID format: ${args.id}. Expected format: "parentId.subtaskId"`
|
message: `Invalid subtask ID format: ${id}. Expected format: "parentId.subtaskId"`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the tasks.json path
|
// Use provided path
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Convert convertToTask to a boolean
|
// Convert convertToTask to a boolean
|
||||||
const convertToTask = args.convert === true;
|
const convertToTask = convert === true;
|
||||||
|
|
||||||
// Determine if we should generate files
|
// Determine if we should generate files
|
||||||
const generateFiles = !args.skipGenerate;
|
const generateFiles = !skipGenerate;
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Removing subtask ${args.id} (convertToTask: ${convertToTask}, generateFiles: ${generateFiles})`
|
`Removing subtask ${id} (convertToTask: ${convertToTask}, generateFiles: ${generateFiles})`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Use the provided tasksPath
|
||||||
const result = await removeSubtask(
|
const result = await removeSubtask(
|
||||||
tasksPath,
|
tasksPath,
|
||||||
args.id,
|
id,
|
||||||
convertToTask,
|
convertToTask,
|
||||||
generateFiles
|
generateFiles
|
||||||
);
|
);
|
||||||
@@ -77,7 +93,7 @@ export async function removeSubtaskDirect(args, log) {
|
|||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
message: `Subtask ${args.id} successfully converted to task #${result.id}`,
|
message: `Subtask ${id} successfully converted to task #${result.id}`,
|
||||||
task: result
|
task: result
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -86,7 +102,7 @@ export async function removeSubtaskDirect(args, log) {
|
|||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
message: `Subtask ${args.id} successfully removed`
|
message: `Subtask ${id} successfully removed`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,35 +8,35 @@ import {
|
|||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Direct function wrapper for removeTask with error handling.
|
* Direct function wrapper for removeTask with error handling.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments
|
* @param {Object} args - Command arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
|
* @param {string} args.id - The ID of the task or subtask to remove.
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Remove task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: false }
|
* @returns {Promise<Object>} - Remove task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: false }
|
||||||
*/
|
*/
|
||||||
export async function removeTaskDirect(args, log) {
|
export async function removeTaskDirect(args, log) {
|
||||||
|
// Destructure expected args
|
||||||
|
const { tasksJsonPath, id } = args;
|
||||||
try {
|
try {
|
||||||
// Find the tasks path first
|
// Check if tasksJsonPath was provided
|
||||||
let tasksPath;
|
if (!tasksJsonPath) {
|
||||||
try {
|
log.error('removeTaskDirect called without tasksJsonPath');
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
|
||||||
} catch (error) {
|
|
||||||
log.error(`Tasks file not found: ${error.message}`);
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'FILE_NOT_FOUND_ERROR',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: error.message
|
message: 'tasksJsonPath is required'
|
||||||
},
|
},
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate task ID parameter
|
// Validate task ID parameter
|
||||||
const taskId = args.id;
|
const taskId = id;
|
||||||
if (!taskId) {
|
if (!taskId) {
|
||||||
log.error('Task ID is required');
|
log.error('Task ID is required');
|
||||||
return {
|
return {
|
||||||
@@ -50,14 +50,14 @@ export async function removeTaskDirect(args, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Skip confirmation in the direct function since it's handled by the client
|
// Skip confirmation in the direct function since it's handled by the client
|
||||||
log.info(`Removing task with ID: ${taskId} from ${tasksPath}`);
|
log.info(`Removing task with ID: ${taskId} from ${tasksJsonPath}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// Call the core removeTask function
|
// Call the core removeTask function using the provided path
|
||||||
const result = await removeTask(tasksPath, taskId);
|
const result = await removeTask(tasksJsonPath, taskId);
|
||||||
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
disableSilentMode();
|
disableSilentMode();
|
||||||
@@ -70,7 +70,7 @@ export async function removeTaskDirect(args, log) {
|
|||||||
data: {
|
data: {
|
||||||
message: result.message,
|
message: result.message,
|
||||||
taskId: taskId,
|
taskId: taskId,
|
||||||
tasksPath: tasksPath,
|
tasksPath: tasksJsonPath,
|
||||||
removedTask: result.removedTask
|
removedTask: result.removedTask
|
||||||
},
|
},
|
||||||
fromCache: false
|
fromCache: false
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { setTaskStatus } from '../../../../scripts/modules/task-manager.js';
|
import { setTaskStatus } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode,
|
disableSilentMode,
|
||||||
@@ -14,16 +13,29 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Direct function wrapper for setTaskStatus with error handling.
|
* Direct function wrapper for setTaskStatus with error handling.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments containing id, status and file path options.
|
* @param {Object} args - Command arguments containing id, status and tasksJsonPath.
|
||||||
* @param {Object} log - Logger object.
|
* @param {Object} log - Logger object.
|
||||||
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
||||||
*/
|
*/
|
||||||
export async function setTaskStatusDirect(args, log) {
|
export async function setTaskStatusDirect(args, log) {
|
||||||
|
// Destructure expected args, including the resolved tasksJsonPath
|
||||||
|
const { tasksJsonPath, id, status } = args;
|
||||||
try {
|
try {
|
||||||
log.info(`Setting task status with args: ${JSON.stringify(args)}`);
|
log.info(`Setting task status with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Check required parameters
|
// Check if tasksJsonPath was provided
|
||||||
if (!args.id) {
|
if (!tasksJsonPath) {
|
||||||
|
const errorMessage = 'tasksJsonPath is required but was not provided.';
|
||||||
|
log.error(errorMessage);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
||||||
|
fromCache: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check required parameters (id and status)
|
||||||
|
if (!id) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No task ID specified. Please provide a task ID to update.';
|
'No task ID specified. Please provide a task ID to update.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -34,7 +46,7 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.status) {
|
if (!status) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No status specified. Please provide a new status value.';
|
'No status specified. Please provide a new status value.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -45,32 +57,16 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get tasks file path
|
// Use the provided path
|
||||||
let tasksPath;
|
const tasksPath = tasksJsonPath;
|
||||||
try {
|
|
||||||
// The enhanced findTasksJsonPath will now search in parent directories if needed
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
|
||||||
log.info(`Found tasks file at: ${tasksPath}`);
|
|
||||||
} catch (error) {
|
|
||||||
log.error(`Error finding tasks file: ${error.message}`);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: 'TASKS_FILE_ERROR',
|
|
||||||
message: `${error.message}\n\nPlease ensure you are in a Task Master project directory or use the --project-root parameter to specify the path to your project.`
|
|
||||||
},
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute core setTaskStatus function
|
// Execute core setTaskStatus function
|
||||||
const taskId = args.id;
|
const taskId = id;
|
||||||
const newStatus = args.status;
|
const newStatus = status;
|
||||||
|
|
||||||
log.info(`Setting task ${taskId} status to "${newStatus}"`);
|
log.info(`Setting task ${taskId} status to "${newStatus}"`);
|
||||||
|
|
||||||
// Call the core function with proper silent mode handling
|
// Call the core function with proper silent mode handling
|
||||||
let result;
|
|
||||||
enableSilentMode(); // Enable silent mode before calling core function
|
enableSilentMode(); // Enable silent mode before calling core function
|
||||||
try {
|
try {
|
||||||
// Call the core function
|
// Call the core function
|
||||||
@@ -79,19 +75,20 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
log.info(`Successfully set task ${taskId} status to ${newStatus}`);
|
log.info(`Successfully set task ${taskId} status to ${newStatus}`);
|
||||||
|
|
||||||
// Return success data
|
// Return success data
|
||||||
result = {
|
const result = {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
message: `Successfully updated task ${taskId} status to "${newStatus}"`,
|
message: `Successfully updated task ${taskId} status to "${newStatus}"`,
|
||||||
taskId,
|
taskId,
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
tasksPath
|
tasksPath: tasksPath // Return the path used
|
||||||
},
|
},
|
||||||
fromCache: false // This operation always modifies state and should never be cached
|
fromCache: false // This operation always modifies state and should never be cached
|
||||||
};
|
};
|
||||||
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`Error setting task status: ${error.message}`);
|
log.error(`Error setting task status: ${error.message}`);
|
||||||
result = {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'SET_STATUS_ERROR',
|
code: 'SET_STATUS_ERROR',
|
||||||
@@ -103,8 +100,6 @@ export async function setTaskStatusDirect(args, log) {
|
|||||||
// ALWAYS restore normal logging in finally block
|
// ALWAYS restore normal logging in finally block
|
||||||
disableSilentMode();
|
disableSilentMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ensure silent mode is disabled if there was an uncaught error in the outer try block
|
// Ensure silent mode is disabled if there was an uncaught error in the outer try block
|
||||||
if (isSilentMode()) {
|
if (isSilentMode()) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import { findTaskById } from '../../../../scripts/modules/utils.js';
|
import { findTaskById } from '../../../../scripts/modules/utils.js';
|
||||||
import { readJSON } from '../../../../scripts/modules/utils.js';
|
import { readJSON } from '../../../../scripts/modules/utils.js';
|
||||||
import { getCachedOrExecute } from '../../tools/utils.js';
|
import { getCachedOrExecute } from '../../tools/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -16,28 +15,29 @@ import {
|
|||||||
* Direct function wrapper for showing task details with error handling and caching.
|
* Direct function wrapper for showing task details with error handling and caching.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments
|
* @param {Object} args - Command arguments
|
||||||
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
|
* @param {string} args.id - The ID of the task or subtask to show.
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Task details result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
* @returns {Promise<Object>} - Task details result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
|
||||||
*/
|
*/
|
||||||
export async function showTaskDirect(args, log) {
|
export async function showTaskDirect(args, log) {
|
||||||
let tasksPath;
|
// Destructure expected args
|
||||||
try {
|
const { tasksJsonPath, id } = args;
|
||||||
// Find the tasks path first - needed for cache key and execution
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
if (!tasksJsonPath) {
|
||||||
} catch (error) {
|
log.error('showTaskDirect called without tasksJsonPath');
|
||||||
log.error(`Tasks file not found: ${error.message}`);
|
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'FILE_NOT_FOUND_ERROR',
|
code: 'MISSING_ARGUMENT',
|
||||||
message: error.message
|
message: 'tasksJsonPath is required'
|
||||||
},
|
},
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate task ID
|
// Validate task ID
|
||||||
const taskId = args.id;
|
const taskId = id;
|
||||||
if (!taskId) {
|
if (!taskId) {
|
||||||
log.error('Task ID is required');
|
log.error('Task ID is required');
|
||||||
return {
|
return {
|
||||||
@@ -50,8 +50,8 @@ export async function showTaskDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate cache key using task path and ID
|
// Generate cache key using the provided task path and ID
|
||||||
const cacheKey = `showTask:${tasksPath}:${taskId}`;
|
const cacheKey = `showTask:${tasksJsonPath}:${taskId}`;
|
||||||
|
|
||||||
// Define the action function to be executed on cache miss
|
// Define the action function to be executed on cache miss
|
||||||
const coreShowTaskAction = async () => {
|
const coreShowTaskAction = async () => {
|
||||||
@@ -59,16 +59,19 @@ export async function showTaskDirect(args, log) {
|
|||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
log.info(`Retrieving task details for ID: ${taskId} from ${tasksPath}`);
|
log.info(
|
||||||
|
`Retrieving task details for ID: ${taskId} from ${tasksJsonPath}`
|
||||||
|
);
|
||||||
|
|
||||||
// Read tasks data
|
// Read tasks data using the provided path
|
||||||
const data = readJSON(tasksPath);
|
const data = readJSON(tasksJsonPath);
|
||||||
if (!data || !data.tasks) {
|
if (!data || !data.tasks) {
|
||||||
|
disableSilentMode(); // Disable before returning
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'INVALID_TASKS_FILE',
|
code: 'INVALID_TASKS_FILE',
|
||||||
message: `No valid tasks found in ${tasksPath}`
|
message: `No valid tasks found in ${tasksJsonPath}`
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -77,6 +80,7 @@ export async function showTaskDirect(args, log) {
|
|||||||
const task = findTaskById(data.tasks, taskId);
|
const task = findTaskById(data.tasks, taskId);
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
|
disableSilentMode(); // Disable before returning
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
getAnthropicClientForMCP,
|
getAnthropicClientForMCP,
|
||||||
getPerplexityClientForMCP
|
getPerplexityClientForMCP
|
||||||
@@ -17,19 +16,31 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Direct function wrapper for updateSubtaskById with error handling.
|
* Direct function wrapper for updateSubtaskById with error handling.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments containing id, prompt, useResearch and file path options.
|
* @param {Object} args - Command arguments containing id, prompt, useResearch and tasksJsonPath.
|
||||||
* @param {Object} log - Logger object.
|
* @param {Object} log - Logger object.
|
||||||
* @param {Object} context - Context object containing session data.
|
* @param {Object} context - Context object containing session data.
|
||||||
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
||||||
*/
|
*/
|
||||||
export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
||||||
const { session } = context; // Only extract session, not reportProgress
|
const { session } = context; // Only extract session, not reportProgress
|
||||||
|
const { tasksJsonPath, id, prompt, research } = args;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
|
log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Check required parameters
|
// Check if tasksJsonPath was provided
|
||||||
if (!args.id) {
|
if (!tasksJsonPath) {
|
||||||
|
const errorMessage = 'tasksJsonPath is required but was not provided.';
|
||||||
|
log.error(errorMessage);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
||||||
|
fromCache: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check required parameters (id and prompt)
|
||||||
|
if (!id) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No subtask ID specified. Please provide a subtask ID to update.';
|
'No subtask ID specified. Please provide a subtask ID to update.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -40,7 +51,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.prompt) {
|
if (!prompt) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No prompt specified. Please provide a prompt with information to add to the subtask.';
|
'No prompt specified. Please provide a prompt with information to add to the subtask.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -52,7 +63,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate subtask ID format
|
// Validate subtask ID format
|
||||||
const subtaskId = args.id;
|
const subtaskId = id;
|
||||||
if (typeof subtaskId !== 'string' && typeof subtaskId !== 'number') {
|
if (typeof subtaskId !== 'string' && typeof subtaskId !== 'number') {
|
||||||
const errorMessage = `Invalid subtask ID type: ${typeof subtaskId}. Subtask ID must be a string or number.`;
|
const errorMessage = `Invalid subtask ID type: ${typeof subtaskId}. Subtask ID must be a string or number.`;
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -74,24 +85,14 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get tasks file path
|
// Use the provided path
|
||||||
let tasksPath;
|
const tasksPath = tasksJsonPath;
|
||||||
try {
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
|
||||||
} catch (error) {
|
|
||||||
log.error(`Error finding tasks file: ${error.message}`);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: { code: 'TASKS_FILE_ERROR', message: error.message },
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get research flag
|
// Get research flag
|
||||||
const useResearch = args.research === true;
|
const useResearch = research === true;
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Updating subtask with ID ${subtaskIdStr} with prompt "${args.prompt}" and research: ${useResearch}`
|
`Updating subtask with ID ${subtaskIdStr} with prompt "${prompt}" and research: ${useResearch}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize the appropriate AI client based on research flag
|
// Initialize the appropriate AI client based on research flag
|
||||||
@@ -134,7 +135,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
|||||||
const updatedSubtask = await updateSubtaskById(
|
const updatedSubtask = await updateSubtaskById(
|
||||||
tasksPath,
|
tasksPath,
|
||||||
subtaskIdStr,
|
subtaskIdStr,
|
||||||
args.prompt,
|
prompt,
|
||||||
useResearch,
|
useResearch,
|
||||||
{
|
{
|
||||||
session,
|
session,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { updateTaskById } from '../../../../scripts/modules/task-manager.js';
|
import { updateTaskById } from '../../../../scripts/modules/task-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -17,19 +16,32 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Direct function wrapper for updateTaskById with error handling.
|
* Direct function wrapper for updateTaskById with error handling.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments containing id, prompt, useResearch and file path options.
|
* @param {Object} args - Command arguments containing id, prompt, useResearch and tasksJsonPath.
|
||||||
* @param {Object} log - Logger object.
|
* @param {Object} log - Logger object.
|
||||||
* @param {Object} context - Context object containing session data.
|
* @param {Object} context - Context object containing session data.
|
||||||
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
||||||
*/
|
*/
|
||||||
export async function updateTaskByIdDirect(args, log, context = {}) {
|
export async function updateTaskByIdDirect(args, log, context = {}) {
|
||||||
const { session } = context; // Only extract session, not reportProgress
|
const { session } = context; // Only extract session, not reportProgress
|
||||||
|
// Destructure expected args, including the resolved tasksJsonPath
|
||||||
|
const { tasksJsonPath, id, prompt, research } = args;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info(`Updating task with args: ${JSON.stringify(args)}`);
|
log.info(`Updating task with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Check required parameters
|
// Check if tasksJsonPath was provided
|
||||||
if (!args.id) {
|
if (!tasksJsonPath) {
|
||||||
|
const errorMessage = 'tasksJsonPath is required but was not provided.';
|
||||||
|
log.error(errorMessage);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
||||||
|
fromCache: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check required parameters (id and prompt)
|
||||||
|
if (!id) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No task ID specified. Please provide a task ID to update.';
|
'No task ID specified. Please provide a task ID to update.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -40,7 +52,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.prompt) {
|
if (!prompt) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No prompt specified. Please provide a prompt with new information for the task update.';
|
'No prompt specified. Please provide a prompt with new information for the task update.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -53,15 +65,15 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
|
|
||||||
// Parse taskId - handle both string and number values
|
// Parse taskId - handle both string and number values
|
||||||
let taskId;
|
let taskId;
|
||||||
if (typeof args.id === 'string') {
|
if (typeof id === 'string') {
|
||||||
// Handle subtask IDs (e.g., "5.2")
|
// Handle subtask IDs (e.g., "5.2")
|
||||||
if (args.id.includes('.')) {
|
if (id.includes('.')) {
|
||||||
taskId = args.id; // Keep as string for subtask IDs
|
taskId = id; // Keep as string for subtask IDs
|
||||||
} else {
|
} else {
|
||||||
// Parse as integer for main task IDs
|
// Parse as integer for main task IDs
|
||||||
taskId = parseInt(args.id, 10);
|
taskId = parseInt(id, 10);
|
||||||
if (isNaN(taskId)) {
|
if (isNaN(taskId)) {
|
||||||
const errorMessage = `Invalid task ID: ${args.id}. Task ID must be a positive integer or subtask ID (e.g., "5.2").`;
|
const errorMessage = `Invalid task ID: ${id}. Task ID must be a positive integer or subtask ID (e.g., "5.2").`;
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -71,24 +83,14 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
taskId = args.id;
|
taskId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get tasks file path
|
// Use the provided path
|
||||||
let tasksPath;
|
const tasksPath = tasksJsonPath;
|
||||||
try {
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
|
||||||
} catch (error) {
|
|
||||||
log.error(`Error finding tasks file: ${error.message}`);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: { code: 'TASKS_FILE_ERROR', message: error.message },
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get research flag
|
// Get research flag
|
||||||
const useResearch = args.research === true;
|
const useResearch = research === true;
|
||||||
|
|
||||||
// Initialize appropriate AI client based on research flag
|
// Initialize appropriate AI client based on research flag
|
||||||
let aiClient;
|
let aiClient;
|
||||||
@@ -113,7 +115,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Updating task with ID ${taskId} with prompt "${args.prompt}" and research: ${useResearch}`
|
`Updating task with ID ${taskId} with prompt "${prompt}" and research: ${useResearch}`
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -133,7 +135,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
await updateTaskById(
|
await updateTaskById(
|
||||||
tasksPath,
|
tasksPath,
|
||||||
taskId,
|
taskId,
|
||||||
args.prompt,
|
prompt,
|
||||||
useResearch,
|
useResearch,
|
||||||
{
|
{
|
||||||
mcpLog: logWrapper, // Use our wrapper object that has the expected method structure
|
mcpLog: logWrapper, // Use our wrapper object that has the expected method structure
|
||||||
@@ -149,7 +151,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
|||||||
data: {
|
data: {
|
||||||
message: `Successfully updated task with ID ${taskId} based on the prompt`,
|
message: `Successfully updated task with ID ${taskId} based on the prompt`,
|
||||||
taskId,
|
taskId,
|
||||||
tasksPath,
|
tasksPath: tasksPath, // Return the used path
|
||||||
useResearch
|
useResearch
|
||||||
},
|
},
|
||||||
fromCache: false // This operation always modifies state and should never be cached
|
fromCache: false // This operation always modifies state and should never be cached
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
getAnthropicClientForMCP,
|
getAnthropicClientForMCP,
|
||||||
getPerplexityClientForMCP
|
getPerplexityClientForMCP
|
||||||
@@ -17,19 +16,31 @@ import {
|
|||||||
/**
|
/**
|
||||||
* Direct function wrapper for updating tasks based on new context/prompt.
|
* Direct function wrapper for updating tasks based on new context/prompt.
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments containing fromId, prompt, useResearch and file path options.
|
* @param {Object} args - Command arguments containing fromId, prompt, useResearch and tasksJsonPath.
|
||||||
* @param {Object} log - Logger object.
|
* @param {Object} log - Logger object.
|
||||||
* @param {Object} context - Context object containing session data.
|
* @param {Object} context - Context object containing session data.
|
||||||
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
||||||
*/
|
*/
|
||||||
export async function updateTasksDirect(args, log, context = {}) {
|
export async function updateTasksDirect(args, log, context = {}) {
|
||||||
const { session } = context; // Only extract session, not reportProgress
|
const { session } = context; // Only extract session, not reportProgress
|
||||||
|
const { tasksJsonPath, from, prompt, research } = args;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info(`Updating tasks with args: ${JSON.stringify(args)}`);
|
log.info(`Updating tasks with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
|
// Check if tasksJsonPath was provided
|
||||||
|
if (!tasksJsonPath) {
|
||||||
|
const errorMessage = 'tasksJsonPath is required but was not provided.';
|
||||||
|
log.error(errorMessage);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: { code: 'MISSING_ARGUMENT', message: errorMessage },
|
||||||
|
fromCache: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Check for the common mistake of using 'id' instead of 'from'
|
// Check for the common mistake of using 'id' instead of 'from'
|
||||||
if (args.id !== undefined && args.from === undefined) {
|
if (args.id !== undefined && from === undefined) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
"You specified 'id' parameter but 'update' requires 'from' parameter. Use 'from' for this tool or use 'update_task' tool if you want to update a single task.";
|
"You specified 'id' parameter but 'update' requires 'from' parameter. Use 'from' for this tool or use 'update_task' tool if you want to update a single task.";
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -46,7 +57,7 @@ export async function updateTasksDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check required parameters
|
// Check required parameters
|
||||||
if (!args.from) {
|
if (!from) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No from ID specified. Please provide a task ID to start updating from.';
|
'No from ID specified. Please provide a task ID to start updating from.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -57,7 +68,7 @@ export async function updateTasksDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.prompt) {
|
if (!prompt) {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
'No prompt specified. Please provide a prompt with new context for task updates.';
|
'No prompt specified. Please provide a prompt with new context for task updates.';
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
@@ -70,10 +81,10 @@ export async function updateTasksDirect(args, log, context = {}) {
|
|||||||
|
|
||||||
// Parse fromId - handle both string and number values
|
// Parse fromId - handle both string and number values
|
||||||
let fromId;
|
let fromId;
|
||||||
if (typeof args.from === 'string') {
|
if (typeof from === 'string') {
|
||||||
fromId = parseInt(args.from, 10);
|
fromId = parseInt(from, 10);
|
||||||
if (isNaN(fromId)) {
|
if (isNaN(fromId)) {
|
||||||
const errorMessage = `Invalid from ID: ${args.from}. Task ID must be a positive integer.`;
|
const errorMessage = `Invalid from ID: ${from}. Task ID must be a positive integer.`;
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -82,24 +93,11 @@ export async function updateTasksDirect(args, log, context = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fromId = args.from;
|
fromId = from;
|
||||||
}
|
|
||||||
|
|
||||||
// Get tasks file path
|
|
||||||
let tasksPath;
|
|
||||||
try {
|
|
||||||
tasksPath = findTasksJsonPath(args, log);
|
|
||||||
} catch (error) {
|
|
||||||
log.error(`Error finding tasks file: ${error.message}`);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: { code: 'TASKS_FILE_ERROR', message: error.message },
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get research flag
|
// Get research flag
|
||||||
const useResearch = args.research === true;
|
const useResearch = research === true;
|
||||||
|
|
||||||
// Initialize appropriate AI client based on research flag
|
// Initialize appropriate AI client based on research flag
|
||||||
let aiClient;
|
let aiClient;
|
||||||
@@ -124,16 +122,25 @@ export async function updateTasksDirect(args, log, context = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Updating tasks from ID ${fromId} with prompt "${args.prompt}" and research: ${useResearch}`
|
`Updating tasks from ID ${fromId} with prompt "${prompt}" and research: ${useResearch}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Create the logger wrapper to ensure compatibility with core functions
|
||||||
|
const logWrapper = {
|
||||||
|
info: (message, ...args) => log.info(message, ...args),
|
||||||
|
warn: (message, ...args) => log.warn(message, ...args),
|
||||||
|
error: (message, ...args) => log.error(message, ...args),
|
||||||
|
debug: (message, ...args) => log.debug && log.debug(message, ...args), // Handle optional debug
|
||||||
|
success: (message, ...args) => log.info(message, ...args) // Map success to info if needed
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// Execute core updateTasks function, passing the AI client and session
|
// Execute core updateTasks function, passing the AI client and session
|
||||||
await updateTasks(tasksPath, fromId, args.prompt, useResearch, {
|
await updateTasks(tasksJsonPath, fromId, prompt, useResearch, {
|
||||||
mcpLog: log,
|
mcpLog: logWrapper, // Pass the wrapper instead of the raw log object
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -144,7 +151,7 @@ export async function updateTasksDirect(args, log, context = {}) {
|
|||||||
data: {
|
data: {
|
||||||
message: `Successfully updated tasks from ID ${fromId} based on the prompt`,
|
message: `Successfully updated tasks from ID ${fromId} based on the prompt`,
|
||||||
fromId,
|
fromId,
|
||||||
tasksPath,
|
tasksPath: tasksJsonPath,
|
||||||
useResearch
|
useResearch
|
||||||
},
|
},
|
||||||
fromCache: false // This operation always modifies state and should never be cached
|
fromCache: false // This operation always modifies state and should never be cached
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { validateDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
import { validateDependenciesCommand } from '../../../../scripts/modules/dependency-manager.js';
|
||||||
import { findTasksJsonPath } from '../utils/path-utils.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode
|
disableSilentMode
|
||||||
@@ -13,17 +12,30 @@ import fs from 'fs';
|
|||||||
/**
|
/**
|
||||||
* Validate dependencies in tasks.json
|
* Validate dependencies in tasks.json
|
||||||
* @param {Object} args - Function arguments
|
* @param {Object} args - Function arguments
|
||||||
* @param {string} [args.file] - Path to the tasks file
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} [args.projectRoot] - Project root directory
|
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
* @returns {Promise<{success: boolean, data?: Object, error?: {code: string, message: string}}>}
|
||||||
*/
|
*/
|
||||||
export async function validateDependenciesDirect(args, log) {
|
export async function validateDependenciesDirect(args, log) {
|
||||||
try {
|
// Destructure the explicit tasksJsonPath
|
||||||
log.info(`Validating dependencies in tasks...`);
|
const { tasksJsonPath } = args;
|
||||||
|
|
||||||
// Find the tasks.json path
|
if (!tasksJsonPath) {
|
||||||
const tasksPath = findTasksJsonPath(args, log);
|
log.error('validateDependenciesDirect called without tasksJsonPath');
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: {
|
||||||
|
code: 'MISSING_ARGUMENT',
|
||||||
|
message: 'tasksJsonPath is required'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.info(`Validating dependencies in tasks: ${tasksJsonPath}`);
|
||||||
|
|
||||||
|
// Use the provided tasksJsonPath
|
||||||
|
const tasksPath = tasksJsonPath;
|
||||||
|
|
||||||
// Verify the file exists
|
// Verify the file exists
|
||||||
if (!fs.existsSync(tasksPath)) {
|
if (!fs.existsSync(tasksPath)) {
|
||||||
@@ -39,7 +51,7 @@ export async function validateDependenciesDirect(args, log) {
|
|||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
enableSilentMode();
|
enableSilentMode();
|
||||||
|
|
||||||
// Call the original command function
|
// Call the original command function using the provided tasksPath
|
||||||
await validateDependenciesCommand(tasksPath);
|
await validateDependenciesCommand(tasksPath);
|
||||||
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
|
|||||||
@@ -291,3 +291,103 @@ function findTasksWithNpmConsideration(startDir, log) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds potential PRD document files based on common naming patterns
|
||||||
|
* @param {string} projectRoot - The project root directory
|
||||||
|
* @param {string|null} explicitPath - Optional explicit path provided by the user
|
||||||
|
* @param {Object} log - Logger object
|
||||||
|
* @returns {string|null} - The path to the first found PRD file, or null if none found
|
||||||
|
*/
|
||||||
|
export function findPRDDocumentPath(projectRoot, explicitPath, log) {
|
||||||
|
// If explicit path is provided, check if it exists
|
||||||
|
if (explicitPath) {
|
||||||
|
const fullPath = path.isAbsolute(explicitPath)
|
||||||
|
? explicitPath
|
||||||
|
: path.resolve(projectRoot, explicitPath);
|
||||||
|
|
||||||
|
if (fs.existsSync(fullPath)) {
|
||||||
|
log.info(`Using provided PRD document path: ${fullPath}`);
|
||||||
|
return fullPath;
|
||||||
|
} else {
|
||||||
|
log.warn(
|
||||||
|
`Provided PRD document path not found: ${fullPath}, will search for alternatives`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common locations and file patterns for PRD documents
|
||||||
|
const commonLocations = [
|
||||||
|
'', // Project root
|
||||||
|
'scripts/'
|
||||||
|
];
|
||||||
|
|
||||||
|
const commonFileNames = ['PRD.md', 'prd.md', 'PRD.txt', 'prd.txt'];
|
||||||
|
|
||||||
|
// Check all possible combinations
|
||||||
|
for (const location of commonLocations) {
|
||||||
|
for (const fileName of commonFileNames) {
|
||||||
|
const potentialPath = path.join(projectRoot, location, fileName);
|
||||||
|
if (fs.existsSync(potentialPath)) {
|
||||||
|
log.info(`Found PRD document at: ${potentialPath}`);
|
||||||
|
return potentialPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn(`No PRD document found in common locations within ${projectRoot}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the tasks output directory path
|
||||||
|
* @param {string} projectRoot - The project root directory
|
||||||
|
* @param {string|null} explicitPath - Optional explicit output path provided by the user
|
||||||
|
* @param {Object} log - Logger object
|
||||||
|
* @returns {string} - The resolved tasks directory path
|
||||||
|
*/
|
||||||
|
export function resolveTasksOutputPath(projectRoot, explicitPath, log) {
|
||||||
|
// If explicit path is provided, use it
|
||||||
|
if (explicitPath) {
|
||||||
|
const outputPath = path.isAbsolute(explicitPath)
|
||||||
|
? explicitPath
|
||||||
|
: path.resolve(projectRoot, explicitPath);
|
||||||
|
|
||||||
|
log.info(`Using provided tasks output path: ${outputPath}`);
|
||||||
|
return outputPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default output path: tasks/tasks.json in the project root
|
||||||
|
const defaultPath = path.resolve(projectRoot, 'tasks', 'tasks.json');
|
||||||
|
log.info(`Using default tasks output path: ${defaultPath}`);
|
||||||
|
|
||||||
|
// Ensure the directory exists
|
||||||
|
const outputDir = path.dirname(defaultPath);
|
||||||
|
if (!fs.existsSync(outputDir)) {
|
||||||
|
log.info(`Creating tasks directory: ${outputDir}`);
|
||||||
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves various file paths needed for MCP operations based on project root
|
||||||
|
* @param {string} projectRoot - The project root directory
|
||||||
|
* @param {Object} args - Command arguments that may contain explicit paths
|
||||||
|
* @param {Object} log - Logger object
|
||||||
|
* @returns {Object} - An object containing resolved paths
|
||||||
|
*/
|
||||||
|
export function resolveProjectPaths(projectRoot, args, log) {
|
||||||
|
const prdPath = findPRDDocumentPath(projectRoot, args.input, log);
|
||||||
|
const tasksJsonPath = resolveTasksOutputPath(projectRoot, args.output, log);
|
||||||
|
|
||||||
|
// You can add more path resolutions here as needed
|
||||||
|
|
||||||
|
return {
|
||||||
|
projectRoot,
|
||||||
|
prdPath,
|
||||||
|
tasksJsonPath
|
||||||
|
// Add additional path properties as needed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { addDependencyDirect } from '../core/task-master-core.js';
|
import { addDependencyDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the addDependency tool with the MCP server
|
* Register the addDependency tool with the MCP server
|
||||||
@@ -32,39 +33,52 @@ export function registerAddDependencyTool(server) {
|
|||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(
|
log.info(
|
||||||
`Adding dependency for task ${args.id} to depend on ${args.dependsOn}`
|
`Adding dependency for task ${args.id} to depend on ${args.dependsOn}`
|
||||||
);
|
);
|
||||||
reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
// Get project root using the utility function
|
// Get project root from args or session
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
// Fallback to args.projectRoot if session didn't provide one
|
// Ensure project root was determined
|
||||||
if (!rootFolder && args.projectRoot) {
|
if (!rootFolder) {
|
||||||
rootFolder = args.projectRoot;
|
return createErrorResponse(
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the direct function with the resolved rootFolder
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the direct function with the resolved path
|
||||||
const result = await addDependencyDirect(
|
const result = await addDependencyDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
dependsOn: args.dependsOn
|
||||||
},
|
},
|
||||||
log,
|
log
|
||||||
{ reportProgress, mcpLog: log, session }
|
// Remove context object
|
||||||
);
|
);
|
||||||
|
|
||||||
reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
// Log result
|
// Log result
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(`Successfully added dependency: ${result.data.message}`);
|
log.info(`Successfully added dependency: ${result.data.message}`);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { addSubtaskDirect } from '../core/task-master-core.js';
|
import { addSubtaskDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the addSubtask tool with the MCP server
|
* Register the addSubtask tool with the MCP server
|
||||||
@@ -57,29 +58,48 @@ export function registerAddSubtaskTool(server) {
|
|||||||
.describe('Skip regenerating task files'),
|
.describe('Skip regenerating task files'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
|
log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
if (!rootFolder) {
|
||||||
rootFolder = args.projectRoot;
|
return createErrorResponse(
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await addSubtaskDirect(
|
const result = await addSubtaskDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
tasksJsonPath: tasksJsonPath,
|
||||||
...args
|
id: args.id,
|
||||||
|
taskId: args.taskId,
|
||||||
|
title: args.title,
|
||||||
|
description: args.description,
|
||||||
|
details: args.details,
|
||||||
|
status: args.status,
|
||||||
|
dependencies: args.dependencies,
|
||||||
|
skipGenerate: args.skipGenerate
|
||||||
},
|
},
|
||||||
log,
|
log
|
||||||
{ reportProgress, mcpLog: log, session }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
handleApiResult
|
handleApiResult
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { addTaskDirect } from '../core/task-master-core.js';
|
import { addTaskDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the addTask tool with the MCP server
|
* Register the addTask tool with the MCP server
|
||||||
@@ -58,35 +59,54 @@ export function registerAddTaskTool(server) {
|
|||||||
.describe('Path to the tasks file (default: tasks/tasks.json)'),
|
.describe('Path to the tasks file (default: tasks/tasks.json)'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.'),
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
),
|
|
||||||
research: z
|
research: z
|
||||||
.boolean()
|
.boolean()
|
||||||
.optional()
|
.optional()
|
||||||
.describe('Whether to use research capabilities for task creation')
|
.describe('Whether to use research capabilities for task creation')
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, reportProgress, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Starting add-task with args: ${JSON.stringify(args)}`);
|
log.info(`Starting add-task with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Get project root from session
|
// Get project root from args or session
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the direct function
|
// Call the direct function
|
||||||
const result = await addTaskDirect(
|
const result = await addTaskDirect(
|
||||||
{
|
{
|
||||||
...args,
|
// Pass the explicitly resolved path
|
||||||
projectRoot: rootFolder
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
prompt: args.prompt,
|
||||||
|
dependencies: args.dependencies,
|
||||||
|
priority: args.priority,
|
||||||
|
research: args.research
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ reportProgress, session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Return the result
|
// Return the result
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { analyzeTaskComplexityDirect } from '../core/task-master-core.js';
|
import { analyzeTaskComplexityDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the analyze tool with the MCP server
|
* Register the analyze tool with the MCP server
|
||||||
@@ -53,10 +55,7 @@ export function registerAnalyzeTool(server) {
|
|||||||
.describe('Use Perplexity AI for research-backed complexity analysis'),
|
.describe('Use Perplexity AI for research-backed complexity analysis'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
@@ -64,17 +63,40 @@ export function registerAnalyzeTool(server) {
|
|||||||
`Analyzing task complexity with args: ${JSON.stringify(args)}`
|
`Analyzing task complexity with args: ${JSON.stringify(args)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
if (!rootFolder) {
|
||||||
rootFolder = args.projectRoot;
|
return createErrorResponse(
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputPath = args.output
|
||||||
|
? path.resolve(rootFolder, args.output)
|
||||||
|
: path.resolve(rootFolder, 'scripts', 'task-complexity-report.json');
|
||||||
|
|
||||||
const result = await analyzeTaskComplexityDirect(
|
const result = await analyzeTaskComplexityDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
tasksJsonPath: tasksJsonPath,
|
||||||
...args
|
outputPath: outputPath,
|
||||||
|
model: args.model,
|
||||||
|
threshold: args.threshold,
|
||||||
|
research: args.research
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { clearSubtasksDirect } from '../core/task-master-core.js';
|
import { clearSubtasksDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the clearSubtasks tool with the MCP server
|
* Register the clearSubtasks tool with the MCP server
|
||||||
@@ -34,38 +35,53 @@ export function registerClearSubtasksTool(server) {
|
|||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.refine((data) => data.id || data.all, {
|
.refine((data) => data.id || data.all, {
|
||||||
message: "Either 'id' or 'all' parameter must be provided",
|
message: "Either 'id' or 'all' parameter must be provided",
|
||||||
path: ['id', 'all']
|
path: ['id', 'all']
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
|
log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
|
||||||
await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await clearSubtasksDirect(
|
const result = await clearSubtasksDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
all: args.all
|
||||||
},
|
},
|
||||||
log,
|
log
|
||||||
{ reportProgress, mcpLog: log, session }
|
// Remove context object as clearSubtasksDirect likely doesn't need session/reportProgress
|
||||||
);
|
);
|
||||||
|
|
||||||
reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(`Subtasks cleared successfully: ${result.data.message}`);
|
log.info(`Subtasks cleared successfully: ${result.data.message}`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { complexityReportDirect } from '../core/task-master-core.js';
|
import { complexityReportDirect } from '../core/task-master-core.js';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the complexityReport tool with the MCP server
|
* Register the complexityReport tool with the MCP server
|
||||||
@@ -28,35 +29,40 @@ export function registerComplexityReportTool(server) {
|
|||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(
|
log.info(
|
||||||
`Getting complexity report with args: ${JSON.stringify(args)}`
|
`Getting complexity report with args: ${JSON.stringify(args)}`
|
||||||
);
|
);
|
||||||
// await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve the path to the complexity report file
|
||||||
|
// Default to scripts/task-complexity-report.json relative to root
|
||||||
|
const reportPath = args.file
|
||||||
|
? path.resolve(rootFolder, args.file)
|
||||||
|
: path.resolve(rootFolder, 'scripts', 'task-complexity-report.json');
|
||||||
|
|
||||||
const result = await complexityReportDirect(
|
const result = await complexityReportDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
reportPath: reportPath
|
||||||
|
// No other args specific to this tool
|
||||||
},
|
},
|
||||||
log /*, { reportProgress, mcpLog: log, session}*/
|
log
|
||||||
);
|
);
|
||||||
|
|
||||||
// await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(
|
log.info(
|
||||||
`Successfully retrieved complexity report${result.fromCache ? ' (from cache)' : ''}`
|
`Successfully retrieved complexity report${result.fromCache ? ' (from cache)' : ''}`
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { expandAllTasksDirect } from '../core/task-master-core.js';
|
import { expandAllTasksDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the expandAll tool with the MCP server
|
* Register the expandAll tool with the MCP server
|
||||||
@@ -48,26 +49,46 @@ export function registerExpandAllTool(server) {
|
|||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Expanding all tasks with args: ${JSON.stringify(args)}`);
|
log.info(`Expanding all tasks with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await expandAllTasksDirect(
|
const result = await expandAllTasksDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
num: args.num,
|
||||||
|
research: args.research,
|
||||||
|
prompt: args.prompt,
|
||||||
|
force: args.force
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { expandTaskDirect } from '../core/task-master-core.js';
|
import { expandTaskDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
@@ -23,10 +24,7 @@ export function registerExpandTaskTool(server) {
|
|||||||
description: 'Expand a task into subtasks for detailed implementation',
|
description: 'Expand a task into subtasks for detailed implementation',
|
||||||
parameters: z.object({
|
parameters: z.object({
|
||||||
id: z.string().describe('ID of task to expand'),
|
id: z.string().describe('ID of task to expand'),
|
||||||
num: z
|
num: z.string().optional().describe('Number of subtasks to generate'),
|
||||||
.union([z.string(), z.number()])
|
|
||||||
.optional()
|
|
||||||
.describe('Number of subtasks to generate'),
|
|
||||||
research: z
|
research: z
|
||||||
.boolean()
|
.boolean()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -38,42 +36,52 @@ export function registerExpandTaskTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.'),
|
||||||
.describe(
|
force: z.boolean().optional().describe('Force the expansion')
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Starting expand-task with args: ${JSON.stringify(args)}`);
|
log.info(`Starting expand-task with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Get project root from session
|
// Get project root from args or session
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Project root resolved to: ${rootFolder}`);
|
log.info(`Project root resolved to: ${rootFolder}`);
|
||||||
|
|
||||||
// Check for tasks.json in the standard locations
|
// Resolve the path to tasks.json using the utility
|
||||||
const tasksJsonPath = path.join(rootFolder, 'tasks', 'tasks.json');
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
if (fs.existsSync(tasksJsonPath)) {
|
tasksJsonPath = findTasksJsonPath(
|
||||||
log.info(`Found tasks.json at ${tasksJsonPath}`);
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
// Add the file parameter directly to args
|
log
|
||||||
args.file = tasksJsonPath;
|
);
|
||||||
} else {
|
} catch (error) {
|
||||||
log.warn(`Could not find tasks.json at ${tasksJsonPath}`);
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call direct function with only session in the context, not reportProgress
|
// Call direct function with only session in the context, not reportProgress
|
||||||
// Use the pattern recommended in the MCP guidelines
|
// Use the pattern recommended in the MCP guidelines
|
||||||
const result = await expandTaskDirect(
|
const result = await expandTaskDirect(
|
||||||
{
|
{
|
||||||
...args,
|
// Pass the explicitly resolved path
|
||||||
projectRoot: rootFolder
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
num: args.num,
|
||||||
|
research: args.research,
|
||||||
|
prompt: args.prompt,
|
||||||
|
force: args.force // Need to add force to parameters
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { fixDependenciesDirect } from '../core/task-master-core.js';
|
import { fixDependenciesDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the fixDependencies tool with the MCP server
|
* Register the fixDependencies tool with the MCP server
|
||||||
@@ -23,34 +24,42 @@ export function registerFixDependenciesTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Fixing dependencies with args: ${JSON.stringify(args)}`);
|
log.info(`Fixing dependencies with args: ${JSON.stringify(args)}`);
|
||||||
await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
if (!rootFolder) {
|
||||||
rootFolder = args.projectRoot;
|
return createErrorResponse(
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await fixDependenciesDirect(
|
const result = await fixDependenciesDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
tasksJsonPath: tasksJsonPath
|
||||||
...args
|
|
||||||
},
|
},
|
||||||
log,
|
log
|
||||||
{ reportProgress, mcpLog: log, session }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(`Successfully fixed dependencies: ${result.data.message}`);
|
log.info(`Successfully fixed dependencies: ${result.data.message}`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { generateTaskFilesDirect } from '../core/task-master-core.js';
|
import { generateTaskFilesDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the generate tool with the MCP server
|
* Register the generate tool with the MCP server
|
||||||
@@ -28,33 +30,52 @@ export function registerGenerateTool(server) {
|
|||||||
.describe('Output directory (default: same directory as tasks file)'),
|
.describe('Output directory (default: same directory as tasks file)'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Generating task files with args: ${JSON.stringify(args)}`);
|
log.info(`Generating task files with args: ${JSON.stringify(args)}`);
|
||||||
// await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine output directory: use explicit arg or default to tasks.json directory
|
||||||
|
const outputDir = args.output
|
||||||
|
? path.resolve(rootFolder, args.output) // Resolve relative to root if needed
|
||||||
|
: path.dirname(tasksJsonPath);
|
||||||
|
|
||||||
const result = await generateTaskFilesDirect(
|
const result = await generateTaskFilesDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved paths
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
outputDir: outputDir
|
||||||
|
// No other args specific to this tool
|
||||||
},
|
},
|
||||||
log /*, { reportProgress, mcpLog: log, session}*/
|
log
|
||||||
);
|
);
|
||||||
|
|
||||||
// await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(`Successfully generated task files: ${result.data.message}`);
|
log.info(`Successfully generated task files: ${result.data.message}`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { showTaskDirect } from '../core/task-master-core.js';
|
import { showTaskDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom processor function that removes allTasks from the response
|
* Custom processor function that removes allTasks from the response
|
||||||
@@ -42,12 +43,9 @@ export function registerShowTaskTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
// Log the session right at the start of execute
|
// Log the session right at the start of execute
|
||||||
log.info(
|
log.info(
|
||||||
`Session object received in execute: ${JSON.stringify(session)}`
|
`Session object received in execute: ${JSON.stringify(session)}`
|
||||||
@@ -60,26 +58,43 @@ export function registerShowTaskTool(server) {
|
|||||||
`Session object received in execute: ${JSON.stringify(session)}`
|
`Session object received in execute: ${JSON.stringify(session)}`
|
||||||
); // Use JSON.stringify for better visibility
|
); // Use JSON.stringify for better visibility
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
} else if (!rootFolder) {
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
// Ensure we always have *some* root, even if session failed and args didn't provide one
|
|
||||||
rootFolder = process.cwd();
|
|
||||||
log.warn(
|
|
||||||
`Session and args failed to provide root, using CWD: ${rootFolder}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Attempting to use project root: ${rootFolder}`); // Log the final resolved root
|
log.info(`Attempting to use project root: ${rootFolder}`); // Log the final resolved root
|
||||||
|
|
||||||
log.info(`Root folder: ${rootFolder}`); // Log the final resolved root
|
log.info(`Root folder: ${rootFolder}`); // Log the final resolved root
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info(`Attempting to use tasks file path: ${tasksJsonPath}`);
|
||||||
|
|
||||||
const result = await showTaskDirect(
|
const result = await showTaskDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id
|
||||||
},
|
},
|
||||||
log
|
log
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { listTasksDirect } from '../core/task-master-core.js';
|
import { listTasksDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the getTasks tool with the MCP server
|
* Register the getTasks tool with the MCP server
|
||||||
@@ -39,33 +40,47 @@ export function registerListTasksTool(server) {
|
|||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: automatically detected from session or CWD)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Getting tasks with filters: ${JSON.stringify(args)}`);
|
log.info(`Getting tasks with filters: ${JSON.stringify(args)}`);
|
||||||
// await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
// Use the error message from findTasksJsonPath for better context
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await listTasksDirect(
|
const result = await listTasksDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
tasksJsonPath: tasksJsonPath,
|
||||||
...args
|
status: args.status,
|
||||||
|
withSubtasks: args.withSubtasks
|
||||||
},
|
},
|
||||||
log /*, { reportProgress, mcpLog: log, session}*/
|
log
|
||||||
);
|
);
|
||||||
|
|
||||||
// await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Retrieved ${result.success ? result.data?.tasks?.length || 0 : 0} tasks${result.fromCache ? ' (from cache)' : ''}`
|
`Retrieved ${result.success ? result.data?.tasks?.length || 0 : 0} tasks${result.fromCache ? ' (from cache)' : ''}`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { nextTaskDirect } from '../core/task-master-core.js';
|
import { nextTaskDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the next-task tool with the MCP server
|
* Register the next-task tool with the MCP server
|
||||||
@@ -24,33 +25,46 @@ export function registerNextTaskTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Finding next task with args: ${JSON.stringify(args)}`);
|
log.info(`Finding next task with args: ${JSON.stringify(args)}`);
|
||||||
// await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await nextTaskDirect(
|
const result = await nextTaskDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath
|
||||||
|
// No other args specific to this tool
|
||||||
},
|
},
|
||||||
log /*, { reportProgress, mcpLog: log, session}*/
|
log
|
||||||
);
|
);
|
||||||
|
|
||||||
// await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(
|
log.info(
|
||||||
`Successfully found next task: ${result.data?.task?.id || 'No available tasks'}`
|
`Successfully found next task: ${result.data?.task?.id || 'No available tasks'}`
|
||||||
|
|||||||
@@ -5,11 +5,16 @@
|
|||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import {
|
import {
|
||||||
|
getProjectRootFromSession,
|
||||||
handleApiResult,
|
handleApiResult,
|
||||||
createErrorResponse,
|
createErrorResponse
|
||||||
getProjectRootFromSession
|
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { parsePRDDirect } from '../core/task-master-core.js';
|
import { parsePRDDirect } from '../core/task-master-core.js';
|
||||||
|
import {
|
||||||
|
resolveProjectPaths,
|
||||||
|
findPRDDocumentPath,
|
||||||
|
resolveTasksOutputPath
|
||||||
|
} from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the parsePRD tool with the MCP server
|
* Register the parsePRD tool with the MCP server
|
||||||
@@ -23,6 +28,7 @@ export function registerParsePRDTool(server) {
|
|||||||
parameters: z.object({
|
parameters: z.object({
|
||||||
input: z
|
input: z
|
||||||
.string()
|
.string()
|
||||||
|
.optional()
|
||||||
.default('scripts/prd.txt')
|
.default('scripts/prd.txt')
|
||||||
.describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
|
.describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
|
||||||
numTasks: z
|
numTasks: z
|
||||||
@@ -35,7 +41,7 @@ export function registerParsePRDTool(server) {
|
|||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe(
|
||||||
'Output absolute path for tasks.json file (default: tasks/tasks.json)'
|
'Output path for tasks.json file (default: tasks/tasks.json)'
|
||||||
),
|
),
|
||||||
force: z
|
force: z
|
||||||
.boolean()
|
.boolean()
|
||||||
@@ -43,39 +49,44 @@ export function registerParsePRDTool(server) {
|
|||||||
.describe('Allow overwriting an existing tasks.json file.'),
|
.describe('Allow overwriting an existing tasks.json file.'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.describe(
|
.describe('The directory of the project. Must be absolute path.')
|
||||||
'Absolute path to the root directory of the project. Required - ALWAYS SET THIS TO THE PROJECT ROOT DIRECTORY.'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Parsing PRD with args: ${JSON.stringify(args)}`);
|
log.info(`Parsing PRD with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
// Make sure projectRoot is passed directly in args or derive from session
|
// Get project root from args or session
|
||||||
// We prioritize projectRoot from args over session-derived path
|
const rootFolder =
|
||||||
let rootFolder = args.projectRoot;
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
// Only if args.projectRoot is undefined or null, try to get it from session
|
|
||||||
if (!rootFolder) {
|
if (!rootFolder) {
|
||||||
log.warn(
|
return createErrorResponse(
|
||||||
'projectRoot not provided in args, attempting to derive from session'
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
);
|
);
|
||||||
rootFolder = getProjectRootFromSession(session, log);
|
|
||||||
|
|
||||||
if (!rootFolder) {
|
|
||||||
const errorMessage =
|
|
||||||
'Could not determine project root directory. Please provide projectRoot parameter.';
|
|
||||||
log.error(errorMessage);
|
|
||||||
return createErrorResponse(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Using project root: ${rootFolder} for PRD parsing`);
|
// Resolve input (PRD) and output (tasks.json) paths using the utility
|
||||||
|
const { projectRoot, prdPath, tasksJsonPath } = resolveProjectPaths(
|
||||||
|
rootFolder,
|
||||||
|
args,
|
||||||
|
log
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if PRD path was found (resolveProjectPaths returns null if not found and not provided)
|
||||||
|
if (!prdPath) {
|
||||||
|
return createErrorResponse(
|
||||||
|
'No PRD document found or provided. Please ensure a PRD file exists (e.g., PRD.md) or provide a valid input file path.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the direct function with fully resolved paths
|
||||||
const result = await parsePRDDirect(
|
const result = await parsePRDDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
projectRoot: projectRoot,
|
||||||
...args
|
input: prdPath,
|
||||||
|
output: tasksJsonPath,
|
||||||
|
numTasks: args.numTasks,
|
||||||
|
force: args.force
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { removeDependencyDirect } from '../core/task-master-core.js';
|
import { removeDependencyDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the removeDependency tool with the MCP server
|
* Register the removeDependency tool with the MCP server
|
||||||
@@ -30,35 +31,50 @@ export function registerRemoveDependencyTool(server) {
|
|||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(
|
log.info(
|
||||||
`Removing dependency for task ${args.id} from ${args.dependsOn} with args: ${JSON.stringify(args)}`
|
`Removing dependency for task ${args.id} from ${args.dependsOn} with args: ${JSON.stringify(args)}`
|
||||||
);
|
);
|
||||||
// await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await removeDependencyDirect(
|
const result = await removeDependencyDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
dependsOn: args.dependsOn
|
||||||
},
|
},
|
||||||
log /*, { reportProgress, mcpLog: log, session}*/
|
log
|
||||||
);
|
);
|
||||||
|
|
||||||
// await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(`Successfully removed dependency: ${result.data.message}`);
|
log.info(`Successfully removed dependency: ${result.data.message}`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { removeSubtaskDirect } from '../core/task-master-core.js';
|
import { removeSubtaskDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the removeSubtask tool with the MCP server
|
* Register the removeSubtask tool with the MCP server
|
||||||
@@ -43,33 +44,49 @@ export function registerRemoveSubtaskTool(server) {
|
|||||||
.describe('Skip regenerating task files'),
|
.describe('Skip regenerating task files'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
|
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
|
||||||
// await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await removeSubtaskDirect(
|
const result = await removeSubtaskDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
convert: args.convert,
|
||||||
|
skipGenerate: args.skipGenerate
|
||||||
},
|
},
|
||||||
log /*, { reportProgress, mcpLog: log, session}*/
|
log
|
||||||
);
|
);
|
||||||
|
|
||||||
// await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(`Subtask removed successfully: ${result.data.message}`);
|
log.info(`Subtask removed successfully: ${result.data.message}`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { removeTaskDirect } from '../core/task-master-core.js';
|
import { removeTaskDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the remove-task tool with the MCP server
|
* Register the remove-task tool with the MCP server
|
||||||
@@ -26,10 +27,7 @@ export function registerRemoveTaskTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.'),
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
),
|
|
||||||
confirm: z
|
confirm: z
|
||||||
.boolean()
|
.boolean()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -39,28 +37,40 @@ export function registerRemoveTaskTool(server) {
|
|||||||
try {
|
try {
|
||||||
log.info(`Removing task with ID: ${args.id}`);
|
log.info(`Removing task with ID: ${args.id}`);
|
||||||
|
|
||||||
// Get project root from session
|
// Get project root from args or session
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
} else if (!rootFolder) {
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
// Ensure we have a default if nothing else works
|
|
||||||
rootFolder = process.cwd();
|
|
||||||
log.warn(
|
|
||||||
`Session and args failed to provide root, using CWD: ${rootFolder}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(`Using project root: ${rootFolder}`);
|
log.info(`Using project root: ${rootFolder}`);
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info(`Using tasks file path: ${tasksJsonPath}`);
|
||||||
|
|
||||||
// Assume client has already handled confirmation if needed
|
// Assume client has already handled confirmation if needed
|
||||||
const result = await removeTaskDirect(
|
const result = await removeTaskDirect(
|
||||||
{
|
{
|
||||||
id: args.id,
|
tasksJsonPath: tasksJsonPath,
|
||||||
file: args.file,
|
id: args.id
|
||||||
projectRoot: rootFolder
|
|
||||||
},
|
},
|
||||||
log
|
log
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { setTaskStatusDirect } from '../core/task-master-core.js';
|
import { setTaskStatusDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the setTaskStatus tool with the MCP server
|
* Register the setTaskStatus tool with the MCP server
|
||||||
@@ -33,28 +34,45 @@ export function registerSetTaskStatusTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: automatically detected)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Setting status of task(s) ${args.id} to: ${args.status}`);
|
log.info(`Setting status of task(s) ${args.id} to: ${args.status}`);
|
||||||
|
|
||||||
// Get project root from session
|
// Get project root from args or session
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the direct function with the project root
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the direct function with the resolved path
|
||||||
const result = await setTaskStatusDirect(
|
const result = await setTaskStatusDirect(
|
||||||
{
|
{
|
||||||
...args,
|
// Pass the explicitly resolved path
|
||||||
projectRoot: rootFolder
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
status: args.status
|
||||||
},
|
},
|
||||||
log
|
log
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { updateSubtaskByIdDirect } from '../core/task-master-core.js';
|
import { updateSubtaskByIdDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the update-subtask tool with the MCP server
|
* Register the update-subtask tool with the MCP server
|
||||||
@@ -34,26 +35,45 @@ export function registerUpdateSubtaskTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
|
log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await updateSubtaskByIdDirect(
|
const result = await updateSubtaskByIdDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
prompt: args.prompt,
|
||||||
|
research: args.research
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { updateTaskByIdDirect } from '../core/task-master-core.js';
|
import { updateTaskByIdDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the update-task tool with the MCP server
|
* Register the update-task tool with the MCP server
|
||||||
@@ -34,26 +35,45 @@ export function registerUpdateTaskTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Updating task with args: ${JSON.stringify(args)}`);
|
log.info(`Updating task with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await updateTaskByIdDirect(
|
const result = await updateTaskByIdDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
// Pass the explicitly resolved path
|
||||||
...args
|
tasksJsonPath: tasksJsonPath,
|
||||||
|
// Pass other relevant args
|
||||||
|
id: args.id,
|
||||||
|
prompt: args.prompt,
|
||||||
|
research: args.research
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { updateTasksDirect } from '../core/task-master-core.js';
|
import { updateTasksDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the update tool with the MCP server
|
* Register the update tool with the MCP server
|
||||||
@@ -36,26 +37,43 @@ export function registerUpdateTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Updating tasks with args: ${JSON.stringify(args)}`);
|
log.info(`Updating tasks with args: ${JSON.stringify(args)}`);
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
// Ensure project root was determined
|
||||||
rootFolder = args.projectRoot;
|
if (!rootFolder) {
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
return createErrorResponse(
|
||||||
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the path to tasks.json
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await updateTasksDirect(
|
const result = await updateTasksDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
tasksJsonPath: tasksJsonPath,
|
||||||
...args
|
from: args.from,
|
||||||
|
prompt: args.prompt,
|
||||||
|
research: args.research
|
||||||
},
|
},
|
||||||
log,
|
log,
|
||||||
{ session }
|
{ session }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
getProjectRootFromSession
|
getProjectRootFromSession
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { validateDependenciesDirect } from '../core/task-master-core.js';
|
import { validateDependenciesDirect } from '../core/task-master-core.js';
|
||||||
|
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the validateDependencies tool with the MCP server
|
* Register the validateDependencies tool with the MCP server
|
||||||
@@ -24,34 +25,42 @@ export function registerValidateDependenciesTool(server) {
|
|||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.describe('The directory of the project. Must be an absolute path.')
|
||||||
.describe(
|
|
||||||
'Root directory of the project (default: current working directory)'
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
execute: async (args, { log, session, reportProgress }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Validating dependencies with args: ${JSON.stringify(args)}`);
|
log.info(`Validating dependencies with args: ${JSON.stringify(args)}`);
|
||||||
await reportProgress({ progress: 0 });
|
|
||||||
|
|
||||||
let rootFolder = getProjectRootFromSession(session, log);
|
// Get project root from args or session
|
||||||
|
const rootFolder =
|
||||||
|
args.projectRoot || getProjectRootFromSession(session, log);
|
||||||
|
|
||||||
if (!rootFolder && args.projectRoot) {
|
if (!rootFolder) {
|
||||||
rootFolder = args.projectRoot;
|
return createErrorResponse(
|
||||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tasksJsonPath;
|
||||||
|
try {
|
||||||
|
tasksJsonPath = findTasksJsonPath(
|
||||||
|
{ projectRoot: rootFolder, file: args.file },
|
||||||
|
log
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Error finding tasks.json: ${error.message}`);
|
||||||
|
return createErrorResponse(
|
||||||
|
`Failed to find tasks.json: ${error.message}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await validateDependenciesDirect(
|
const result = await validateDependenciesDirect(
|
||||||
{
|
{
|
||||||
projectRoot: rootFolder,
|
tasksJsonPath: tasksJsonPath
|
||||||
...args
|
|
||||||
},
|
},
|
||||||
log,
|
log
|
||||||
{ reportProgress, mcpLog: log, session }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await reportProgress({ progress: 100 });
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
log.info(
|
log.info(
|
||||||
`Successfully validated dependencies: ${result.data.message}`
|
`Successfully validated dependencies: ${result.data.message}`
|
||||||
|
|||||||
@@ -23,23 +23,7 @@ import chalk from 'chalk';
|
|||||||
import figlet from 'figlet';
|
import figlet from 'figlet';
|
||||||
import boxen from 'boxen';
|
import boxen from 'boxen';
|
||||||
import gradient from 'gradient-string';
|
import gradient from 'gradient-string';
|
||||||
import {
|
import { isSilentMode } from './modules/utils.js';
|
||||||
isSilentMode,
|
|
||||||
enableSilentMode,
|
|
||||||
disableSilentMode
|
|
||||||
} from './modules/utils.js';
|
|
||||||
|
|
||||||
// Only log if not in silent mode
|
|
||||||
if (!isSilentMode()) {
|
|
||||||
console.log('Starting task-master-ai...');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug information - only log if not in silent mode
|
|
||||||
if (!isSilentMode()) {
|
|
||||||
console.log('Node version:', process.version);
|
|
||||||
console.log('Current directory:', process.cwd());
|
|
||||||
console.log('Script path:', import.meta.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = dirname(__filename);
|
const __dirname = dirname(__filename);
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
writeJSON,
|
writeJSON,
|
||||||
taskExists,
|
taskExists,
|
||||||
formatTaskId,
|
formatTaskId,
|
||||||
findCycles
|
findCycles,
|
||||||
|
isSilentMode
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
import { displayBanner } from './ui.js';
|
import { displayBanner } from './ui.js';
|
||||||
@@ -320,6 +321,7 @@ async function removeDependency(tasksPath, taskId, dependencyId) {
|
|||||||
`Removed dependency: Task ${formattedTaskId} no longer depends on ${formattedDependencyId}`
|
`Removed dependency: Task ${formattedTaskId} no longer depends on ${formattedDependencyId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!isSilentMode()) {
|
||||||
// Display a more visually appealing success message
|
// Display a more visually appealing success message
|
||||||
console.log(
|
console.log(
|
||||||
boxen(
|
boxen(
|
||||||
@@ -333,6 +335,7 @@ async function removeDependency(tasksPath, taskId, dependencyId) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Regenerate task files
|
// Regenerate task files
|
||||||
await generateTaskFiles(tasksPath, 'tasks');
|
await generateTaskFiles(tasksPath, 'tasks');
|
||||||
@@ -553,8 +556,11 @@ function cleanupSubtaskDependencies(tasksData) {
|
|||||||
* Validate dependencies in task files
|
* Validate dependencies in task files
|
||||||
* @param {string} tasksPath - Path to tasks.json
|
* @param {string} tasksPath - Path to tasks.json
|
||||||
*/
|
*/
|
||||||
async function validateDependenciesCommand(tasksPath) {
|
async function validateDependenciesCommand(tasksPath, options = {}) {
|
||||||
|
// Only display banner if not in silent mode
|
||||||
|
if (!isSilentMode()) {
|
||||||
displayBanner();
|
displayBanner();
|
||||||
|
}
|
||||||
|
|
||||||
log('info', 'Checking for invalid dependencies in task files...');
|
log('info', 'Checking for invalid dependencies in task files...');
|
||||||
|
|
||||||
@@ -659,7 +665,8 @@ async function validateDependenciesCommand(tasksPath) {
|
|||||||
if (changesDetected) {
|
if (changesDetected) {
|
||||||
log('success', 'Invalid dependencies were removed from tasks.json');
|
log('success', 'Invalid dependencies were removed from tasks.json');
|
||||||
|
|
||||||
// Show detailed stats in a nice box
|
// Show detailed stats in a nice box - only if not in silent mode
|
||||||
|
if (!isSilentMode()) {
|
||||||
console.log(
|
console.log(
|
||||||
boxen(
|
boxen(
|
||||||
chalk.green(`Dependency Validation Results:\n\n`) +
|
chalk.green(`Dependency Validation Results:\n\n`) +
|
||||||
@@ -685,6 +692,7 @@ async function validateDependenciesCommand(tasksPath) {
|
|||||||
console.log(` ${warning}`);
|
console.log(` ${warning}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Regenerate task files to reflect the changes
|
// Regenerate task files to reflect the changes
|
||||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||||
@@ -695,7 +703,8 @@ async function validateDependenciesCommand(tasksPath) {
|
|||||||
'No invalid dependencies found - all dependencies are valid'
|
'No invalid dependencies found - all dependencies are valid'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Show validation summary
|
// Show validation summary - only if not in silent mode
|
||||||
|
if (!isSilentMode()) {
|
||||||
console.log(
|
console.log(
|
||||||
boxen(
|
boxen(
|
||||||
chalk.green(`All Dependencies Are Valid\n\n`) +
|
chalk.green(`All Dependencies Are Valid\n\n`) +
|
||||||
@@ -711,6 +720,7 @@ async function validateDependenciesCommand(tasksPath) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('error', 'Error validating dependencies:', error);
|
log('error', 'Error validating dependencies:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -747,9 +757,13 @@ function countAllDependencies(tasks) {
|
|||||||
/**
|
/**
|
||||||
* Fixes invalid dependencies in tasks.json
|
* Fixes invalid dependencies in tasks.json
|
||||||
* @param {string} tasksPath - Path to tasks.json
|
* @param {string} tasksPath - Path to tasks.json
|
||||||
|
* @param {Object} options - Options object
|
||||||
*/
|
*/
|
||||||
async function fixDependenciesCommand(tasksPath) {
|
async function fixDependenciesCommand(tasksPath, options = {}) {
|
||||||
|
// Only display banner if not in silent mode
|
||||||
|
if (!isSilentMode()) {
|
||||||
displayBanner();
|
displayBanner();
|
||||||
|
}
|
||||||
|
|
||||||
log('info', 'Checking for and fixing invalid dependencies in tasks.json...');
|
log('info', 'Checking for and fixing invalid dependencies in tasks.json...');
|
||||||
|
|
||||||
@@ -1086,6 +1100,7 @@ async function fixDependenciesCommand(tasksPath) {
|
|||||||
stats.duplicateDependenciesRemoved +
|
stats.duplicateDependenciesRemoved +
|
||||||
stats.circularDependenciesFixed;
|
stats.circularDependenciesFixed;
|
||||||
|
|
||||||
|
if (!isSilentMode()) {
|
||||||
if (totalFixedAll > 0) {
|
if (totalFixedAll > 0) {
|
||||||
log('success', `Fixed ${totalFixedAll} dependency issues in total!`);
|
log('success', `Fixed ${totalFixedAll} dependency issues in total!`);
|
||||||
|
|
||||||
@@ -1107,7 +1122,10 @@ async function fixDependenciesCommand(tasksPath) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log('success', 'No dependency issues found - all dependencies are valid');
|
log(
|
||||||
|
'success',
|
||||||
|
'No dependency issues found - all dependencies are valid'
|
||||||
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
boxen(
|
boxen(
|
||||||
@@ -1123,6 +1141,7 @@ async function fixDependenciesCommand(tasksPath) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('error', 'Error in fix-dependencies command:', error);
|
log('error', 'Error in fix-dependencies command:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user