Recovers lost files and commits work from the past 5-6 days. Holy shit that was a close call.
This commit is contained in:
@@ -155,7 +155,114 @@ alwaysApply: false
|
||||
- **UI for Presentation**: [`ui.js`](mdc:scripts/modules/ui.js) is used by command handlers and task/dependency managers to display information to the user. UI functions primarily consume data and format it for output, without modifying core application state.
|
||||
- **Utilities for Common Tasks**: [`utils.js`](mdc:scripts/modules/utils.js) provides helper functions used by all other modules for configuration, logging, file operations, and common data manipulations.
|
||||
- **AI Services Integration**: AI functionalities (complexity analysis, task expansion, PRD parsing) are invoked from [`task-manager.js`](mdc:scripts/modules/task-manager.js) and potentially [`commands.js`](mdc:scripts/modules/commands.js), likely using functions that would reside in a dedicated `ai-services.js` module or be integrated within `utils.js` or `task-manager.js`.
|
||||
- **MCP Server Interaction**: External tools interact with the `mcp-server`. MCP Tool `execute` methods use `getProjectRootFromSession` to find the project root, then call direct function wrappers (in `mcp-server/src/core/direct-functions/`) passing the root in `args`. These wrappers handle path finding for `tasks.json` (using `path-utils.js`), validation, caching, call the core logic from `scripts/modules/`, and return a standardized result. The final MCP response is formatted by `mcp-server/src/tools/utils.js`. See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details.
|
||||
- **MCP Server Interaction**: External tools interact with the `mcp-server`. MCP Tool `execute` methods use `getProjectRootFromSession` to find the project root, then call direct function wrappers (in `mcp-server/src/core/direct-functions/`) passing the root in `args`. These wrappers handle path finding for `tasks.json` (using `path-utils.js`), validation, caching, call the core logic from `scripts/modules/` (passing logging context via the standard wrapper pattern detailed in mcp.mdc), and return a standardized result. The final MCP response is formatted by `mcp-server/src/tools/utils.js`. See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details.
|
||||
|
||||
## Silent Mode Implementation Pattern in MCP Direct Functions
|
||||
|
||||
Direct functions (the `*Direct` functions in `mcp-server/src/core/direct-functions/`) need to carefully implement silent mode to prevent console logs from interfering with the structured JSON responses required by MCP. This involves both using `enableSilentMode`/`disableSilentMode` around core function calls AND passing the MCP logger via the standard wrapper pattern (see mcp.mdc). Here's the standard pattern for correct implementation:
|
||||
|
||||
1. **Import Silent Mode Utilities**:
|
||||
```javascript
|
||||
import { enableSilentMode, disableSilentMode, isSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
```
|
||||
|
||||
2. **Parameter Matching with Core Functions**:
|
||||
- ✅ **DO**: Ensure direct function parameters match the core function parameters
|
||||
- ✅ **DO**: Check the original core function signature before implementing
|
||||
- ❌ **DON'T**: Add parameters to direct functions that don't exist in core functions
|
||||
```javascript
|
||||
// Example: Core function signature
|
||||
// async function expandTask(tasksPath, taskId, numSubtasks, useResearch, additionalContext, options)
|
||||
|
||||
// Direct function implementation - extract only parameters that exist in core
|
||||
export async function expandTaskDirect(args, log, context = {}) {
|
||||
// Extract parameters that match the core function
|
||||
const taskId = parseInt(args.id, 10);
|
||||
const numSubtasks = args.num ? parseInt(args.num, 10) : undefined;
|
||||
const useResearch = args.research === true;
|
||||
const additionalContext = args.prompt || '';
|
||||
|
||||
// Later pass these parameters in the correct order to the core function
|
||||
const result = await expandTask(
|
||||
tasksPath,
|
||||
taskId,
|
||||
numSubtasks,
|
||||
useResearch,
|
||||
additionalContext,
|
||||
{ mcpLog: log, session: context.session }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
3. **Checking Silent Mode State**:
|
||||
- ✅ **DO**: Always use `isSilentMode()` function to check current status
|
||||
- ❌ **DON'T**: Directly access the global `silentMode` variable or `global.silentMode`
|
||||
```javascript
|
||||
// CORRECT: Use the function to check current state
|
||||
if (!isSilentMode()) {
|
||||
// Only create a loading indicator if not in silent mode
|
||||
loadingIndicator = startLoadingIndicator('Processing...');
|
||||
}
|
||||
|
||||
// INCORRECT: Don't access global variables directly
|
||||
if (!silentMode) { // ❌ WRONG
|
||||
loadingIndicator = startLoadingIndicator('Processing...');
|
||||
}
|
||||
```
|
||||
|
||||
4. **Wrapping Core Function Calls**:
|
||||
- ✅ **DO**: Use a try/finally block pattern to ensure silent mode is always restored
|
||||
- ✅ **DO**: Enable silent mode before calling core functions that produce console output
|
||||
- ✅ **DO**: Disable silent mode in a finally block to ensure it runs even if errors occur
|
||||
- ❌ **DON'T**: Enable silent mode without ensuring it gets disabled
|
||||
```javascript
|
||||
export async function someDirectFunction(args, log) {
|
||||
try {
|
||||
// Argument preparation
|
||||
const tasksPath = findTasksJsonPath(args, log);
|
||||
const someArg = args.someArg;
|
||||
|
||||
// Enable silent mode to prevent console logs
|
||||
enableSilentMode();
|
||||
|
||||
try {
|
||||
// Call core function which might produce console output
|
||||
const result = await someCoreFunction(tasksPath, someArg);
|
||||
|
||||
// Return standardized result object
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
fromCache: false
|
||||
};
|
||||
} finally {
|
||||
// ALWAYS disable silent mode in finally block
|
||||
disableSilentMode();
|
||||
}
|
||||
} catch (error) {
|
||||
// Standard error handling
|
||||
log.error(`Error in direct function: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'OPERATION_ERROR', message: error.message },
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. **Mixed Parameter and Global Silent Mode Handling**:
|
||||
- For functions that need to handle both a passed `silentMode` parameter and check global state:
|
||||
```javascript
|
||||
// Check both the function parameter and global state
|
||||
const isSilent = options.silentMode || (typeof options.silentMode === 'undefined' && isSilentMode());
|
||||
|
||||
if (!isSilent) {
|
||||
console.log('Operation starting...');
|
||||
}
|
||||
```
|
||||
|
||||
By following these patterns consistently, direct functions will properly manage console output suppression while ensuring that silent mode is always properly reset, even when errors occur. This creates a more robust system that helps prevent unexpected silent mode states that could cause logging problems in subsequent operations.
|
||||
|
||||
- **Testing Architecture**:
|
||||
|
||||
@@ -205,7 +312,7 @@ Follow these steps to add MCP support for an existing Task Master command (see [
|
||||
|
||||
1. **Ensure Core Logic Exists**: Verify the core functionality is implemented and exported from the relevant module in `scripts/modules/`.
|
||||
|
||||
2. **Create Direct Function File in `mcp-server/src/core/direct-functions/`**:
|
||||
2. **Create Direct Function File in `mcp-server/src/core/direct-functions/`:**
|
||||
- Create a new file (e.g., `your-command.js`) using **kebab-case** naming.
|
||||
- Import necessary core functions, **`findTasksJsonPath` from `../utils/path-utils.js`**, and **silent mode utilities**.
|
||||
- Implement `async function yourCommandDirect(args, log)` using **camelCase** with `Direct` suffix:
|
||||
|
||||
@@ -152,8 +152,8 @@ When implementing commands that delete or remove data (like `remove-task` or `re
|
||||
```javascript
|
||||
// ✅ DO: Suggest alternatives for destructive operations
|
||||
console.log(chalk.yellow('Note: If you just want to exclude this task from active work, consider:'));
|
||||
console.log(chalk.cyan(` task-master set-status --id=${taskId} --status=cancelled`));
|
||||
console.log(chalk.cyan(` task-master set-status --id=${taskId} --status=deferred`));
|
||||
console.log(chalk.cyan(` task-master set-status --id='${taskId}' --status='cancelled'`));
|
||||
console.log(chalk.cyan(` task-master set-status --id='${taskId}' --status='deferred'`));
|
||||
console.log('This preserves the task and its history for reference.');
|
||||
```
|
||||
|
||||
@@ -253,7 +253,7 @@ When implementing commands that delete or remove data (like `remove-task` or `re
|
||||
const taskId = parseInt(options.id, 10);
|
||||
if (isNaN(taskId) || taskId <= 0) {
|
||||
console.error(chalk.red(`Error: Invalid task ID: ${options.id}. Task ID must be a positive integer.`));
|
||||
console.log(chalk.yellow('Usage example: task-master update-task --id=23 --prompt="Update with new information"'));
|
||||
console.log(chalk.yellow('Usage example: task-master update-task --id=\'23\' --prompt=\'Update with new information.\nEnsure proper error handling.\''));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -299,8 +299,8 @@ When implementing commands that delete or remove data (like `remove-task` or `re
|
||||
(dependencies.length > 0 ? chalk.white(`Dependencies: ${dependencies.join(', ')}`) + '\n' : '') +
|
||||
'\n' +
|
||||
chalk.white.bold('Next Steps:') + '\n' +
|
||||
chalk.cyan(`1. Run ${chalk.yellow(`task-master show ${parentId}`)} to see the parent task with all subtasks`) + '\n' +
|
||||
chalk.cyan(`2. Run ${chalk.yellow(`task-master set-status --id=${parentId}.${subtask.id} --status=in-progress`)} to start working on it`),
|
||||
chalk.cyan(`1. Run ${chalk.yellow(`task-master show '${parentId}'`)} to see the parent task with all subtasks`) + '\n' +
|
||||
chalk.cyan(`2. Run ${chalk.yellow(`task-master set-status --id='${parentId}.${subtask.id}' --status='in-progress'`)} to start working on it`),
|
||||
{ padding: 1, borderColor: 'green', borderStyle: 'round', margin: { top: 1 } }
|
||||
));
|
||||
```
|
||||
@@ -375,7 +375,7 @@ When implementing commands that delete or remove data (like `remove-task` or `re
|
||||
' --option1 <value> Description of option1 (required)\n' +
|
||||
' --option2 <value> Description of option2\n\n' +
|
||||
chalk.cyan('Examples:') + '\n' +
|
||||
' task-master command --option1=value --option2=value',
|
||||
' task-master command --option1=\'value1\' --option2=\'value2\'',
|
||||
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
|
||||
));
|
||||
}
|
||||
@@ -418,7 +418,7 @@ When implementing commands that delete or remove data (like `remove-task` or `re
|
||||
// Provide more helpful error messages for common issues
|
||||
if (error.message.includes('task') && error.message.includes('not found')) {
|
||||
console.log(chalk.yellow('\nTo fix this issue:'));
|
||||
console.log(' 1. Run task-master list to see all available task IDs');
|
||||
console.log(' 1. Run \'task-master list\' to see all available task IDs');
|
||||
console.log(' 2. Use a valid task ID with the --id parameter');
|
||||
} else if (error.message.includes('API key')) {
|
||||
console.log(chalk.yellow('\nThis error is related to API keys. Check your environment variables.'));
|
||||
@@ -561,4 +561,46 @@ When implementing commands that delete or remove data (like `remove-task` or `re
|
||||
}
|
||||
```
|
||||
|
||||
Refer to [`commands.js`](mdc:scripts/modules/commands.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines.
|
||||
Refer to [`commands.js`](mdc:scripts/modules/commands.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines.
|
||||
// Helper function to show add-subtask command help
|
||||
function showAddSubtaskHelp() {
|
||||
console.log(boxen(
|
||||
chalk.white.bold('Add Subtask Command Help') + '\n\n' +
|
||||
chalk.cyan('Usage:') + '\n' +
|
||||
` task-master add-subtask --parent=<id> [options]\n\n` +
|
||||
chalk.cyan('Options:') + '\n' +
|
||||
' -p, --parent <id> Parent task ID (required)\n' +
|
||||
' -i, --task-id <id> Existing task ID to convert to subtask\n' +
|
||||
' -t, --title <title> Title for the new subtask\n' +
|
||||
' -d, --description <text> Description for the new subtask\n' +
|
||||
' --details <text> Implementation details for the new subtask\n' +
|
||||
' --dependencies <ids> Comma-separated list of dependency IDs\n' +
|
||||
' -s, --status <status> Status for the new subtask (default: "pending")\n' +
|
||||
' -f, --file <file> Path to the tasks file (default: "tasks/tasks.json")\n' +
|
||||
' --skip-generate Skip regenerating task files\n\n' +
|
||||
chalk.cyan('Examples:') + '\n' +
|
||||
' task-master add-subtask --parent=\'5\' --task-id=\'8\'\n' +
|
||||
' task-master add-subtask -p \'5\' -t \'Implement login UI\' -d \'Create the login form\'\n' +
|
||||
' task-master add-subtask -p \'5\' -t \'Handle API Errors\' --details $\'Handle 401 Unauthorized.\nHandle 500 Server Error.\'',
|
||||
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
|
||||
));
|
||||
}
|
||||
|
||||
// Helper function to show remove-subtask command help
|
||||
function showRemoveSubtaskHelp() {
|
||||
console.log(boxen(
|
||||
chalk.white.bold('Remove Subtask Command Help') + '\n\n' +
|
||||
chalk.cyan('Usage:') + '\n' +
|
||||
` task-master remove-subtask --id=<parentId.subtaskId> [options]\n\n` +
|
||||
chalk.cyan('Options:') + '\n' +
|
||||
' -i, --id <id> Subtask ID(s) to remove in format "parentId.subtaskId" (can be comma-separated, required)\n' +
|
||||
' -c, --convert Convert the subtask to a standalone task instead of deleting it\n' +
|
||||
' -f, --file <file> Path to the tasks file (default: "tasks/tasks.json")\n' +
|
||||
' --skip-generate Skip regenerating task files\n\n' +
|
||||
chalk.cyan('Examples:') + '\n' +
|
||||
' task-master remove-subtask --id=\'5.2\'\n' +
|
||||
' task-master remove-subtask --id=\'5.2,6.3,7.1\'\n' +
|
||||
' task-master remove-subtask --id=\'5.2\' --convert',
|
||||
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
|
||||
));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ Task Master offers two primary ways to interact:
|
||||
|
||||
## Standard Development Workflow Process
|
||||
|
||||
- Start new projects by running `init` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input=<prd-file.txt>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to generate initial tasks.json
|
||||
- Start new projects by running `init` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input='<prd-file.txt>'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to generate initial tasks.json
|
||||
- Begin coding sessions with `get_tasks` / `task-master list` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to see current tasks, status, and IDs
|
||||
- Determine the next task to work on using `next_task` / `task-master next` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Analyze task complexity with `analyze_complexity` / `task-master analyze-complexity --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) before breaking down tasks
|
||||
@@ -45,7 +45,7 @@ Task Master offers two primary ways to interact:
|
||||
- Update dependent tasks when implementation differs from original plan using `update` / `task-master update --from=<id> --prompt="..."` or `update_task` / `task-master update-task --id=<id> --prompt="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc))
|
||||
- Add new tasks discovered during implementation using `add_task` / `task-master add-task --prompt="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Add new subtasks as needed using `add_subtask` / `task-master add-subtask --parent=<id> --title="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Append notes or details to subtasks using `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Append notes or details to subtasks using `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='Add implementation notes here...\nMore details...'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
- Generate task files with `generate` / `task-master generate` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) after updating tasks.json
|
||||
- Maintain valid dependency structure with `add_dependency`/`remove_dependency` tools or `task-master add-dependency`/`remove-dependency` commands, `validate_dependencies` / `task-master validate-dependencies`, and `fix_dependencies` / `task-master fix-dependencies` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) when needed
|
||||
- Respect dependency chains and task priorities when selecting work
|
||||
@@ -74,8 +74,8 @@ Task Master offers two primary ways to interact:
|
||||
- When implementation differs significantly from planned approach
|
||||
- When future tasks need modification due to current implementation choices
|
||||
- When new dependencies or requirements emerge
|
||||
- Use `update` / `task-master update --from=<futureTaskId> --prompt="<explanation>"` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to update multiple future tasks.
|
||||
- Use `update_task` / `task-master update-task --id=<taskId> --prompt="<explanation>"` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to update a single specific task.
|
||||
- Use `update` / `task-master update --from=<futureTaskId> --prompt='<explanation>\nUpdate context...'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to update multiple future tasks.
|
||||
- Use `update_task` / `task-master update-task --id=<taskId> --prompt='<explanation>\nUpdate context...'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to update a single specific task.
|
||||
|
||||
## Task Status Management
|
||||
|
||||
@@ -150,6 +150,59 @@ Task Master offers two primary ways to interact:
|
||||
- Task files are automatically regenerated after dependency changes
|
||||
- Dependencies are visualized with status indicators in task listings and files
|
||||
|
||||
## Iterative Subtask Implementation
|
||||
|
||||
Once a task has been broken down into subtasks using `expand_task` or similar methods, follow this iterative process for implementation:
|
||||
|
||||
1. **Understand the Goal (Preparation):**
|
||||
* Use `get_task` / `task-master show <subtaskId>` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to thoroughly understand the specific goals and requirements of the subtask.
|
||||
|
||||
2. **Initial Exploration & Planning (Iteration 1):**
|
||||
* This is the first attempt at creating a concrete implementation plan.
|
||||
* Explore the codebase to identify the precise files, functions, and even specific lines of code that will need modification.
|
||||
* Determine the intended code changes (diffs) and their locations.
|
||||
* Gather *all* relevant details from this exploration phase.
|
||||
|
||||
3. **Log the Plan:**
|
||||
* Run `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<detailed plan>'` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
* Provide the *complete and detailed* findings from the exploration phase in the prompt. Include file paths, line numbers, proposed diffs, reasoning, and any potential challenges identified. Do not omit details. The goal is to create a rich, timestamped log within the subtask's `details`.
|
||||
|
||||
4. **Verify the Plan:**
|
||||
* Run `get_task` / `task-master show <subtaskId>` again to confirm that the detailed implementation plan has been successfully appended to the subtask's details.
|
||||
|
||||
5. **Begin Implementation:**
|
||||
* Set the subtask status using `set_task_status` / `task-master set-status --id=<subtaskId> --status=in-progress` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)).
|
||||
* Start coding based on the logged plan.
|
||||
|
||||
6. **Refine and Log Progress (Iteration 2+):**
|
||||
* As implementation progresses, you will encounter challenges, discover nuances, or confirm successful approaches.
|
||||
* **Before appending new information**: Briefly review the *existing* details logged in the subtask (using `get_task` or recalling from context) to ensure the update adds fresh insights and avoids redundancy.
|
||||
* **Regularly** use `update_subtask` / `task-master update-subtask --id=<subtaskId> --prompt='<update details>\n- What worked...\n- What didn't work...'` to append new findings.
|
||||
* **Crucially, log:**
|
||||
* What worked ("fundamental truths" discovered).
|
||||
* What didn't work and why (to avoid repeating mistakes).
|
||||
* Specific code snippets or configurations that were successful.
|
||||
* Decisions made, especially if confirmed with user input.
|
||||
* Any deviations from the initial plan and the reasoning.
|
||||
* The objective is to continuously enrich the subtask's details, creating a log of the implementation journey that helps the AI (and human developers) learn, adapt, and avoid repeating errors.
|
||||
|
||||
7. **Review & Update Rules (Post-Implementation):**
|
||||
* Once the implementation for the subtask is functionally complete, review all code changes and the relevant chat history.
|
||||
* Identify any new or modified code patterns, conventions, or best practices established during the implementation.
|
||||
* Create new or update existing Cursor rules in the `.cursor/rules/` directory to capture these patterns, following the guidelines in [`cursor_rules.mdc`](mdc:.cursor/rules/cursor_rules.mdc) and [`self_improve.mdc`](mdc:.cursor/rules/self_improve.mdc).
|
||||
|
||||
8. **Mark Task Complete:**
|
||||
* After verifying the implementation and updating any necessary rules, mark the subtask as completed: `set_task_status` / `task-master set-status --id=<subtaskId> --status=done`.
|
||||
|
||||
9. **Commit Changes (If using Git):**
|
||||
* Stage the relevant code changes and any updated/new rule files (`git add .`).
|
||||
* Craft a comprehensive Git commit message summarizing the work done for the subtask, including both code implementation and any rule adjustments.
|
||||
* Execute the commit command directly in the terminal (e.g., `git commit -m 'feat(module): Implement feature X for subtask <subtaskId>\n\n- Details about changes...\n- Updated rule Y for pattern Z'`).
|
||||
* Consider if a Changeset is needed according to [`changeset.mdc`](mdc:.cursor/rules/changeset.mdc). If so, run `npm run changeset`, stage the generated file, and amend the commit or create a new one.
|
||||
|
||||
10. **Proceed to Next Subtask:**
|
||||
* Identify the next subtask in the dependency chain (e.g., using `next_task` / `task-master next`) and repeat this iterative process starting from step 1.
|
||||
|
||||
## Code Analysis & Refactoring Techniques
|
||||
|
||||
- **Top-Level Function Search**:
|
||||
|
||||
@@ -67,65 +67,127 @@ When implementing a new direct function in `mcp-server/src/core/direct-functions
|
||||
```
|
||||
|
||||
4. **Comprehensive Error Handling**:
|
||||
- ✅ **DO**: Wrap core function calls in try/catch blocks
|
||||
- ✅ **DO**: Wrap core function calls *and AI calls* in try/catch blocks
|
||||
- ✅ **DO**: Log errors with appropriate severity and context
|
||||
- ✅ **DO**: Return standardized error objects with code and message
|
||||
- ✅ **DO**: Handle file system errors separately from function-specific errors
|
||||
- ✅ **DO**: Return standardized error objects with code and message (`{ success: false, error: { code: '...', message: '...' } }`)
|
||||
- ✅ **DO**: Handle file system errors, AI client errors, AI processing errors, and core function errors distinctly with appropriate codes.
|
||||
- **Example**:
|
||||
```javascript
|
||||
try {
|
||||
// Core function call
|
||||
// Core function call or AI logic
|
||||
} catch (error) {
|
||||
log.error(`Failed to execute command: ${error.message}`);
|
||||
log.error(`Failed to execute direct function logic: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: error.code || 'DIRECT_FUNCTION_ERROR',
|
||||
code: error.code || 'DIRECT_FUNCTION_ERROR', // Use specific codes like AI_CLIENT_ERROR, etc.
|
||||
message: error.message,
|
||||
details: error.stack
|
||||
details: error.stack // Optional: Include stack in debug mode
|
||||
},
|
||||
fromCache: false
|
||||
fromCache: false // Ensure this is included if applicable
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
5. **Silent Mode Implementation**:
|
||||
- ✅ **DO**: Import silent mode utilities at the top of your file
|
||||
5. **Handling Logging Context (`mcpLog`)**:
|
||||
- **Requirement**: Core functions that use the internal `report` helper function (common in `task-manager.js`, `dependency-manager.js`, etc.) expect the `options` object to potentially contain an `mcpLog` property. This `mcpLog` object **must** have callable methods for each log level (e.g., `mcpLog.info(...)`, `mcpLog.error(...)`).
|
||||
- **Challenge**: The `log` object provided by FastMCP to the direct function's context, while functional, might not perfectly match this expected structure or could change in the future. Passing it directly can lead to runtime errors like `mcpLog[level] is not a function`.
|
||||
- **Solution: The Logger Wrapper Pattern**: To reliably bridge the FastMCP `log` object and the core function's `mcpLog` expectation, use a simple wrapper object within the direct function:
|
||||
```javascript
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
// Standard logWrapper pattern within a Direct Function
|
||||
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
|
||||
};
|
||||
|
||||
// ... later when calling the core function ...
|
||||
await coreFunction(
|
||||
// ... other arguments ...
|
||||
tasksPath,
|
||||
taskId,
|
||||
{
|
||||
mcpLog: logWrapper, // Pass the wrapper object
|
||||
session
|
||||
},
|
||||
'json' // Pass 'json' output format if supported by core function
|
||||
);
|
||||
```
|
||||
- ✅ **DO**: Wrap core function calls with silent mode control
|
||||
```javascript
|
||||
// Enable silent mode before the core function call
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core function
|
||||
const result = await coreFunction(param1, param2);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
```
|
||||
- ✅ **DO**: Add proper error handling to ensure silent mode is disabled
|
||||
```javascript
|
||||
try {
|
||||
enableSilentMode();
|
||||
// Core function execution
|
||||
const result = await coreFunction(param1, param2);
|
||||
disableSilentMode();
|
||||
return { success: true, data: result };
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
log.error(`Error in function: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'ERROR_CODE', message: error.message }
|
||||
};
|
||||
}
|
||||
```
|
||||
- ❌ **DON'T**: Forget to disable silent mode when errors occur
|
||||
- ❌ **DON'T**: Leave silent mode enabled outside a direct function's scope
|
||||
- ❌ **DON'T**: Skip silent mode for core function calls that generate logs
|
||||
- **Critical For JSON Output Format**: Passing the `logWrapper` as `mcpLog` serves a dual purpose:
|
||||
1. **Prevents Runtime Errors**: It ensures the `mcpLog[level](...)` calls within the core function succeed
|
||||
2. **Controls Output Format**: In functions like `updateTaskById` and `updateSubtaskById`, the presence of `mcpLog` in the options triggers setting `outputFormat = 'json'` (instead of 'text'). This prevents UI elements (spinners, boxes) from being generated, which would break the JSON response.
|
||||
- **Proven Solution**: This pattern has successfully fixed multiple issues in our MCP tools (including `update-task` and `update-subtask`), where direct passing of the `log` object or omitting `mcpLog` led to either runtime errors or JSON parsing failures from UI output.
|
||||
- **When To Use**: Implement this wrapper in any direct function that calls a core function with an `options` object that might use `mcpLog` for logging or output format control.
|
||||
- **Why it Works**: The `logWrapper` explicitly defines the `.info()`, `.warn()`, `.error()`, etc., methods that the core function's `report` helper needs, ensuring the `mcpLog[level](...)` call succeeds. It simply forwards the logging calls to the actual FastMCP `log` object.
|
||||
- **Combined with Silent Mode**: Remember that using the `logWrapper` for `mcpLog` is **necessary *in addition* to using `enableSilentMode()` / `disableSilentMode()`** (see next point). The wrapper handles structured logging *within* the core function, while silent mode suppresses direct `console.log` and UI elements (spinners, boxes) that would break the MCP JSON response.
|
||||
|
||||
6. **Silent Mode Implementation**:
|
||||
- ✅ **DO**: Import silent mode utilities at the top: `import { enableSilentMode, disableSilentMode, isSilentMode } from '../../../../scripts/modules/utils.js';`
|
||||
- ✅ **DO**: Ensure core Task Master functions called from direct functions do **not** pollute `stdout` with console output (banners, spinners, logs) that would break MCP's JSON communication.
|
||||
- **Preferred**: Modify the core function to accept an `outputFormat: 'json'` parameter and check it internally before printing UI elements. Pass `'json'` from the direct function.
|
||||
- **Required Fallback/Guarantee**: If the core function cannot be modified or its output suppression is unreliable, **wrap the core function call** within the direct function using `enableSilentMode()` / `disableSilentMode()` in a `try/finally` block. This guarantees no console output interferes with the MCP response.
|
||||
- ✅ **DO**: Use `isSilentMode()` function to check global silent mode status if needed (rare in direct functions), NEVER access the global `silentMode` variable directly.
|
||||
- ❌ **DON'T**: Wrap AI client initialization or AI API calls in `enable/disableSilentMode`; their logging is controlled via the `log` object (passed potentially within the `logWrapper` for core functions).
|
||||
- ❌ **DON'T**: Assume a core function is silent just because it *should* be. Verify or use the `enable/disableSilentMode` wrapper.
|
||||
- **Example (Direct Function Guaranteeing Silence and using Log Wrapper)**:
|
||||
```javascript
|
||||
export async function coreWrapperDirect(args, log, context = {}) {
|
||||
const { session } = context;
|
||||
const tasksPath = findTasksJsonPath(args, log);
|
||||
|
||||
// Create the logger wrapper
|
||||
const logWrapper = { /* ... as defined above ... */ };
|
||||
|
||||
enableSilentMode(); // Ensure silence for direct console output
|
||||
try {
|
||||
// Call core function, passing wrapper and 'json' format
|
||||
const result = await coreFunction(
|
||||
tasksPath,
|
||||
args.param1,
|
||||
{ mcpLog: logWrapper, session },
|
||||
'json' // Explicitly request JSON format if supported
|
||||
);
|
||||
return { success: true, data: result };
|
||||
} catch (error) {
|
||||
log.error(`Error: ${error.message}`);
|
||||
// Return standardized error object
|
||||
return { success: false, error: { /* ... */ } };
|
||||
} finally {
|
||||
disableSilentMode(); // Critical: Always disable in finally
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
7. **Debugging MCP/Core Logic Interaction**:
|
||||
- ✅ **DO**: If an MCP tool fails with unclear errors (like JSON parsing failures), run the equivalent `task-master` CLI command in the terminal. The CLI often provides more detailed error messages originating from the core logic (e.g., `ReferenceError`, stack traces) that are obscured by the MCP layer.
|
||||
|
||||
### Specific Guidelines for AI-Based Direct Functions
|
||||
|
||||
Direct functions that interact with AI (e.g., `addTaskDirect`, `expandTaskDirect`) have additional responsibilities:
|
||||
|
||||
- **Context Parameter**: These functions receive an additional `context` object as their third parameter. **Critically, this object should only contain `{ session }`**. Do NOT expect or use `reportProgress` from this context.
|
||||
```javascript
|
||||
export async function yourAIDirect(args, log, context = {}) {
|
||||
const { session } = context; // Only expect session
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- **AI Client Initialization**:
|
||||
- ✅ **DO**: Use the utilities from [`mcp-server/src/core/utils/ai-client-utils.js`](mdc:mcp-server/src/core/utils/ai-client-utils.js) (e.g., `getAnthropicClientForMCP(session, log)`) to get AI client instances. These correctly use the `session` object to resolve API keys.
|
||||
- ✅ **DO**: Wrap client initialization in a try/catch block and return a specific `AI_CLIENT_ERROR` on failure.
|
||||
- **AI Interaction**:
|
||||
- ✅ **DO**: Build prompts using helper functions where appropriate (e.g., from `ai-prompt-helpers.js`).
|
||||
- ✅ **DO**: Make the AI API call using appropriate helpers (e.g., `_handleAnthropicStream`). Pass the `log` object to these helpers for internal logging. **Do NOT pass `reportProgress`**.
|
||||
- ✅ **DO**: Parse the AI response using helpers (e.g., `parseTaskJsonResponse`) and handle parsing errors with a specific code (e.g., `RESPONSE_PARSING_ERROR`).
|
||||
- **Calling Core Logic**:
|
||||
- ✅ **DO**: After successful AI interaction, call the relevant core Task Master function (from `scripts/modules/`) if needed (e.g., `addTaskDirect` calls `addTask`).
|
||||
- ✅ **DO**: Pass necessary data, including potentially the parsed AI results, to the core function.
|
||||
- ✅ **DO**: If the core function can produce console output, call it with an `outputFormat: 'json'` argument (or similar, depending on the function) to suppress CLI output. Ensure the core function is updated to respect this. Use `enableSilentMode/disableSilentMode` around the core function call as a fallback if `outputFormat` is not supported or insufficient.
|
||||
- **Progress Indication**:
|
||||
- ❌ **DON'T**: Call `reportProgress` within the direct function.
|
||||
- ✅ **DO**: If intermediate progress status is needed *within* the long-running direct function, use standard logging: `log.info('Progress: Processing AI response...')`.
|
||||
|
||||
## Tool Definition and Execution
|
||||
|
||||
@@ -159,14 +221,21 @@ server.addTool({
|
||||
The `execute` function receives validated arguments and the FastMCP context:
|
||||
|
||||
```javascript
|
||||
// Standard signature
|
||||
execute: async (args, context) => {
|
||||
// Tool implementation
|
||||
}
|
||||
|
||||
// Destructured signature (recommended)
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
// Tool implementation
|
||||
}
|
||||
```
|
||||
|
||||
- **args**: The first parameter contains all the validated parameters defined in the tool's schema.
|
||||
- **context**: The second parameter is an object containing `{ log, reportProgress, session }` provided by FastMCP.
|
||||
- ✅ **DO**: `execute: async (args, { log, reportProgress, session }) => {}`
|
||||
- ✅ **DO**: Use `{ log, session }` when calling direct functions.
|
||||
- ⚠️ **WARNING**: Avoid passing `reportProgress` down to direct functions due to client compatibility issues. See Progress Reporting Convention below.
|
||||
|
||||
### Standard Tool Execution Pattern
|
||||
|
||||
@@ -174,20 +243,27 @@ The `execute` method within each MCP tool (in `mcp-server/src/tools/*.js`) shoul
|
||||
|
||||
1. **Log Entry**: Log the start of the tool execution with relevant arguments.
|
||||
2. **Get Project Root**: Use the `getProjectRootFromSession(session, log)` utility (from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js)) to extract the project root path from the client session. Fall back to `args.projectRoot` if the session doesn't provide a root.
|
||||
3. **Call Direct Function**: Invoke the corresponding `*Direct` function wrapper (e.g., `listTasksDirect` from [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js)), passing an updated `args` object that includes the resolved `projectRoot`, along with the `log` object: `await someDirectFunction({ ...args, projectRoot: resolvedRootFolder }, log);`
|
||||
3. **Call Direct Function**: Invoke the corresponding `*Direct` function wrapper (e.g., `listTasksDirect` from [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js)), passing an updated `args` object that includes the resolved `projectRoot`. Crucially, the third argument (context) passed to the direct function should **only include `{ log, session }`**. **Do NOT pass `reportProgress`**.
|
||||
```javascript
|
||||
// Example call to a non-AI direct function
|
||||
const result = await someDirectFunction({ ...args, projectRoot }, log);
|
||||
|
||||
// Example call to an AI-based direct function
|
||||
const resultAI = await someAIDirect({ ...args, projectRoot }, log, { session });
|
||||
```
|
||||
4. **Handle Result**: Receive the result object (`{ success, data/error, fromCache }`) from the `*Direct` function.
|
||||
5. **Format Response**: Pass this result object to the `handleApiResult` utility (from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js)) for standardized MCP response formatting and error handling.
|
||||
6. **Return**: Return the formatted response object provided by `handleApiResult`.
|
||||
|
||||
```javascript
|
||||
// Example execute method structure
|
||||
// Example execute method structure for a tool calling an AI-based direct function
|
||||
import { getProjectRootFromSession, handleApiResult, createErrorResponse } from './utils.js';
|
||||
import { someDirectFunction } from '../core/task-master-core.js';
|
||||
import { someAIDirectFunction } from '../core/task-master-core.js';
|
||||
|
||||
// ... inside server.addTool({...})
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
execute: async (args, { log, session }) => { // Note: reportProgress is omitted here
|
||||
try {
|
||||
log.info(`Starting tool execution with args: ${JSON.stringify(args)}`);
|
||||
log.info(`Starting AI tool execution with args: ${JSON.stringify(args)}`);
|
||||
|
||||
// 1. Get Project Root
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
@@ -196,17 +272,17 @@ execute: async (args, { log, reportProgress, session }) => {
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
|
||||
// 2. Call Direct Function (passing resolved root)
|
||||
const result = await someDirectFunction({
|
||||
// 2. Call AI-Based Direct Function (passing only log and session in context)
|
||||
const result = await someAIDirectFunction({
|
||||
...args,
|
||||
projectRoot: rootFolder // Ensure projectRoot is explicitly passed
|
||||
}, log);
|
||||
}, log, { session }); // Pass session here, NO reportProgress
|
||||
|
||||
// 3. Handle and Format Response
|
||||
return handleApiResult(result, log);
|
||||
|
||||
} catch (error) {
|
||||
log.error(`Error during tool execution: ${error.message}`);
|
||||
log.error(`Error during AI tool execution: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
}
|
||||
@@ -214,15 +290,17 @@ execute: async (args, { log, reportProgress, session }) => {
|
||||
|
||||
### Using AsyncOperationManager for Background Tasks
|
||||
|
||||
For tools that execute long-running operations, use the AsyncOperationManager to run them in the background:
|
||||
For tools that execute potentially long-running operations *where the AI call is just one part* (e.g., `expand-task`, `update`), use the AsyncOperationManager. The `add-task` command, as refactored, does *not* require this in the MCP tool layer because the direct function handles the primary AI work and returns the final result synchronously from the perspective of the MCP tool.
|
||||
|
||||
For tools that *do* use `AsyncOperationManager`:
|
||||
|
||||
```javascript
|
||||
import { asyncOperationManager } from '../core/utils/async-manager.js';
|
||||
import { AsyncOperationManager } from '../utils/async-operation-manager.js'; // Correct path assuming utils location
|
||||
import { getProjectRootFromSession, createContentResponse, createErrorResponse } from './utils.js';
|
||||
import { someIntensiveDirect } from '../core/task-master-core.js';
|
||||
|
||||
// ... inside server.addTool({...})
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
execute: async (args, { log, session }) => { // Note: reportProgress omitted
|
||||
try {
|
||||
log.info(`Starting background operation with args: ${JSON.stringify(args)}`);
|
||||
|
||||
@@ -232,53 +310,59 @@ execute: async (args, { log, reportProgress, session }) => {
|
||||
rootFolder = args.projectRoot;
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
|
||||
// Create operation description
|
||||
const operationDescription = `Expanding task ${args.id}...`; // Example
|
||||
|
||||
// 2. Add operation to the async manager
|
||||
const operationId = asyncOperationManager.addOperation(
|
||||
someIntensiveDirect, // The direct function to execute
|
||||
{ ...args, projectRoot: rootFolder }, // Args to pass
|
||||
{ log, reportProgress, session } // Context to preserve
|
||||
// 2. Start async operation using AsyncOperationManager
|
||||
const operation = AsyncOperationManager.createOperation(
|
||||
operationDescription,
|
||||
async (reportProgressCallback) => { // This callback is provided by AsyncOperationManager
|
||||
// This runs in the background
|
||||
try {
|
||||
// Report initial progress *from the manager's callback*
|
||||
reportProgressCallback({ progress: 0, status: 'Starting operation...' });
|
||||
|
||||
// Call the direct function (passing only session context)
|
||||
const result = await someIntensiveDirect(
|
||||
{ ...args, projectRoot: rootFolder },
|
||||
log,
|
||||
{ session } // Pass session, NO reportProgress
|
||||
);
|
||||
|
||||
// Report final progress *from the manager's callback*
|
||||
reportProgressCallback({
|
||||
progress: 100,
|
||||
status: result.success ? 'Operation completed' : 'Operation failed',
|
||||
result: result.data, // Include final data if successful
|
||||
error: result.error // Include error object if failed
|
||||
});
|
||||
|
||||
return result; // Return the direct function's result
|
||||
} catch (error) {
|
||||
// Handle errors within the async task
|
||||
reportProgressCallback({
|
||||
progress: 100,
|
||||
status: 'Operation failed critically',
|
||||
error: { message: error.message, code: error.code || 'ASYNC_OPERATION_FAILED' }
|
||||
});
|
||||
throw error; // Re-throw for the manager to catch
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 3. Return immediate response with operation ID
|
||||
return createContentResponse({
|
||||
message: "Operation started successfully",
|
||||
operationId,
|
||||
status: "pending"
|
||||
});
|
||||
return {
|
||||
status: 202, // StatusCodes.ACCEPTED
|
||||
body: {
|
||||
success: true,
|
||||
message: 'Operation started',
|
||||
operationId: operation.id
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
log.error(`Error starting background operation: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Clients should then use the `get_operation_status` tool to check on operation progress:
|
||||
|
||||
```javascript
|
||||
// In get-operation-status.js
|
||||
import { asyncOperationManager } from '../core/utils/async-manager.js';
|
||||
import { createContentResponse, createErrorResponse } from './utils.js';
|
||||
|
||||
// ... inside server.addTool({...})
|
||||
execute: async (args, { log }) => {
|
||||
try {
|
||||
const { operationId } = args;
|
||||
log.info(`Checking status of operation: ${operationId}`);
|
||||
|
||||
const status = asyncOperationManager.getStatus(operationId);
|
||||
|
||||
if (status.status === 'not_found') {
|
||||
return createErrorResponse(status.error.message);
|
||||
}
|
||||
|
||||
return createContentResponse({
|
||||
...status,
|
||||
message: `Operation status: ${status.status}`
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(`Error checking operation status: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
return createErrorResponse(`Failed to start operation: ${error.message}`); // Use standard error response
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -322,7 +406,7 @@ export function registerInitializeProjectTool(server) {
|
||||
|
||||
### Logging Convention
|
||||
|
||||
The `log` object (destructured from `context`) provides standardized logging methods. Use it within both the `execute` method and the `*Direct` functions.
|
||||
The `log` object (destructured from `context`) provides standardized logging methods. Use it within both the `execute` method and the `*Direct` functions. **If progress indication is needed within a direct function, use `log.info()` instead of `reportProgress`**.
|
||||
|
||||
```javascript
|
||||
// Proper logging usage
|
||||
@@ -330,19 +414,14 @@ log.info(`Starting ${toolName} with parameters: ${JSON.stringify(sanitizedArgs)}
|
||||
log.debug("Detailed operation info", { data });
|
||||
log.warn("Potential issue detected");
|
||||
log.error(`Error occurred: ${error.message}`, { stack: error.stack });
|
||||
log.info('Progress: 50% - AI call initiated...'); // Example progress logging
|
||||
```
|
||||
|
||||
### Progress Reporting Convention
|
||||
|
||||
Use `reportProgress` (destructured from `context`) for long-running operations. It expects an object `{ progress: number, total?: number }`.
|
||||
|
||||
```javascript
|
||||
await reportProgress({ progress: 0 }); // Start
|
||||
// ... work ...
|
||||
await reportProgress({ progress: 50 }); // Intermediate (total optional)
|
||||
// ... more work ...
|
||||
await reportProgress({ progress: 100 }); // Complete
|
||||
```
|
||||
- ⚠️ **DEPRECATED within Direct Functions**: The `reportProgress` function passed in the `context` object should **NOT** be called from within `*Direct` functions. Doing so can cause client-side validation errors due to missing/incorrect `progressToken` handling.
|
||||
- ✅ **DO**: For tools using `AsyncOperationManager`, use the `reportProgressCallback` function *provided by the manager* within the background task definition (as shown in the `AsyncOperationManager` example above) to report progress updates for the *overall operation*.
|
||||
- ✅ **DO**: If finer-grained progress needs to be indicated *during* the execution of a `*Direct` function (whether called directly or via `AsyncOperationManager`), use `log.info()` statements (e.g., `log.info('Progress: Parsing AI response...')`).
|
||||
|
||||
### Session Usage Convention
|
||||
|
||||
@@ -350,32 +429,39 @@ The `session` object (destructured from `context`) contains authenticated sessio
|
||||
|
||||
- **Authentication**: Access user-specific data (`session.userId`, etc.) if authentication is implemented.
|
||||
- **Project Root**: The primary use in Task Master is accessing `session.roots` to determine the client's project root directory via the `getProjectRootFromSession` utility (from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js)). See the Standard Tool Execution Pattern above.
|
||||
- **Environment Variables**: The `session.env` object is critical for AI tools. Pass the `session` object to the `*Direct` function's context, and then to AI client utility functions (like `getAnthropicClientForMCP`) which will extract API keys and other relevant environment settings (e.g., `MODEL`, `MAX_TOKENS`) from `session.env`.
|
||||
- **Capabilities**: Can be used to check client capabilities (`session.clientCapabilities`).
|
||||
|
||||
## Direct Function Wrappers (`*Direct`)
|
||||
|
||||
These functions, located in `mcp-server/src/core/direct-functions/`, form the core logic execution layer for MCP tools.
|
||||
|
||||
- **Purpose**: Bridge MCP tools and core Task Master modules (`scripts/modules/*`).
|
||||
- **Purpose**: Bridge MCP tools and core Task Master modules (`scripts/modules/*`). Handle AI interactions if applicable.
|
||||
- **Responsibilities**:
|
||||
- Receive `args` (including the `projectRoot` determined by the tool) and `log` object.
|
||||
- **Find `tasks.json`**: Use `findTasksJsonPath(args, log)` from [`core/utils/path-utils.js`](mdc:mcp-server/src/core/utils/path-utils.js). This function prioritizes the provided `args.projectRoot`.
|
||||
- Receive `args` (including the `projectRoot` determined by the tool), `log` object, and optionally a `context` object (containing **only `{ session }` if needed).
|
||||
- **Find `tasks.json`**: Use `findTasksJsonPath(args, log)` from [`core/utils/path-utils.js`](mdc:mcp-server/src/core/utils/path-utils.js).
|
||||
- Validate arguments specific to the core logic.
|
||||
- **Implement Silent Mode**: Import and use `enableSilentMode` and `disableSilentMode` around core function calls.
|
||||
- **Implement Caching**: Use `getCachedOrExecute` from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) for read operations.
|
||||
- Call the underlying function from the core Task Master modules.
|
||||
- Handle errors gracefully.
|
||||
- Return a standardized result object: `{ success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }`.
|
||||
- **Handle AI Logic (if applicable)**: Initialize AI clients (using `session` from context), build prompts, make AI calls, parse responses.
|
||||
- **Implement Caching (if applicable)**: Use `getCachedOrExecute` from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) for read operations.
|
||||
- **Call Core Logic**: Call the underlying function from the core Task Master modules, passing necessary data (including AI results if applicable).
|
||||
- ✅ **DO**: Pass `outputFormat: 'json'` (or similar) to the core function if it might produce console output.
|
||||
- ✅ **DO**: Wrap the core function call with `enableSilentMode/disableSilentMode` if necessary.
|
||||
- Handle errors gracefully (AI errors, core logic errors, file errors).
|
||||
- Return a standardized result object: `{ success: boolean, data?: any, error?: { code: string, message: string }, fromCache?: boolean }`.
|
||||
- ❌ **DON'T**: Call `reportProgress`. Use `log.info` for progress indication if needed.
|
||||
|
||||
## Key Principles
|
||||
|
||||
- **Prefer Direct Function Calls**: MCP tools should always call `*Direct` wrappers instead of `executeTaskMasterCommand`.
|
||||
- **Standardized Execution Flow**: Follow the pattern: MCP Tool -> `getProjectRootFromSession` -> `*Direct` Function -> Core Logic.
|
||||
- **Standardized Execution Flow**: Follow the pattern: MCP Tool -> `getProjectRootFromSession` -> `*Direct` Function -> Core Logic / AI Logic.
|
||||
- **Path Resolution via Direct Functions**: The `*Direct` function is responsible for finding the exact `tasks.json` path using `findTasksJsonPath`, relying on the `projectRoot` passed in `args`.
|
||||
- **Silent Mode in Direct Functions**: Wrap all core function calls with `enableSilentMode()` and `disableSilentMode()` to prevent logs from interfering with JSON responses.
|
||||
- **Async Processing for Intensive Operations**: Use AsyncOperationManager for CPU-intensive or long-running operations.
|
||||
- **AI Logic in Direct Functions**: For AI-based tools, the `*Direct` function handles AI client initialization, calls, and parsing, using the `session` object passed in its context.
|
||||
- **Silent Mode in Direct Functions**: Wrap *core function* calls (from `scripts/modules`) with `enableSilentMode()` and `disableSilentMode()` if they produce console output not handled by `outputFormat`. Do not wrap AI calls.
|
||||
- **Selective Async Processing**: Use `AsyncOperationManager` in the *MCP Tool layer* for operations involving multiple steps or long waits beyond a single AI call (e.g., file processing + AI call + file writing). Simple AI calls handled entirely within the `*Direct` function (like `addTaskDirect`) may not need it at the tool layer.
|
||||
- **No `reportProgress` in Direct Functions**: Do not pass or use `reportProgress` within `*Direct` functions. Use `log.info()` for internal progress or report progress from the `AsyncOperationManager` callback in the MCP tool layer.
|
||||
- **Output Formatting**: Ensure core functions called by `*Direct` functions can suppress CLI output, ideally via an `outputFormat` parameter.
|
||||
- **Project Initialization**: Use the initialize_project tool for setting up new projects in integrated environments.
|
||||
- **Centralized Utilities**: Use helpers from `mcp-server/src/tools/utils.js` (like `handleApiResult`, `getProjectRootFromSession`, `getCachedOrExecute`) and `mcp-server/src/core/utils/path-utils.js` (`findTasksJsonPath`). See [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc).
|
||||
- **Centralized Utilities**: Use helpers from `mcp-server/src/tools/utils.js`, `mcp-server/src/core/utils/path-utils.js`, and `mcp-server/src/core/utils/ai-client-utils.js`. See [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc).
|
||||
- **Caching in Direct Functions**: Caching logic resides *within* the `*Direct` functions using `getCachedOrExecute`.
|
||||
|
||||
## Resources and Resource Templates
|
||||
@@ -392,32 +478,38 @@ Resources provide LLMs with static or dynamic data without executing tools.
|
||||
|
||||
Follow these steps to add MCP support for an existing Task Master command (see [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for more detail):
|
||||
|
||||
1. **Ensure Core Logic Exists**: Verify the core functionality is implemented and exported from the relevant module in `scripts/modules/`.
|
||||
1. **Ensure Core Logic Exists**: Verify the core functionality is implemented and exported from the relevant module in `scripts/modules/`. Ensure the core function can suppress console output (e.g., via an `outputFormat` parameter).
|
||||
|
||||
2. **Create Direct Function File in `mcp-server/src/core/direct-functions/`**:
|
||||
- Create a new file (e.g., `your-command.js`) using **kebab-case** naming.
|
||||
- Import necessary core functions, **`findTasksJsonPath` from `../utils/path-utils.js`**, and **silent mode utilities**.
|
||||
- Implement `async function yourCommandDirect(args, log)` using **camelCase** with `Direct` suffix:
|
||||
- **Path Resolution**: Obtain the tasks file path using `const tasksPath = findTasksJsonPath(args, log);`. This handles project root detection automatically based on `args.projectRoot`.
|
||||
- Import necessary core functions, `findTasksJsonPath`, silent mode utilities, and potentially AI client/prompt utilities.
|
||||
- Implement `async function yourCommandDirect(args, log, context = {})` using **camelCase** with `Direct` suffix. **Remember `context` should only contain `{ session }` if needed (for AI keys/config).**
|
||||
- **Path Resolution**: Obtain `tasksPath` using `findTasksJsonPath(args, log)`.
|
||||
- Parse other `args` and perform necessary validation.
|
||||
- **Implement Silent Mode**: Wrap core function calls with enableSilentMode/disableSilentMode.
|
||||
- **If Caching**: Implement caching using `getCachedOrExecute` from `../../tools/utils.js`.
|
||||
- **If Not Caching**: Directly call the core logic function within a try/catch block.
|
||||
- Format the return as `{ success: true/false, data/error, fromCache: boolean }`.
|
||||
- **Handle AI (if applicable)**: Initialize clients using `get*ClientForMCP(session, log)`, build prompts, call AI, parse response. Handle AI-specific errors.
|
||||
- **Implement Caching (if applicable)**: Use `getCachedOrExecute`.
|
||||
- **Call Core Logic**:
|
||||
- Wrap with `enableSilentMode/disableSilentMode` if necessary.
|
||||
- Pass `outputFormat: 'json'` (or similar) if applicable.
|
||||
- Handle errors from the core function.
|
||||
- Format the return as `{ success: true/false, data/error, fromCache?: boolean }`.
|
||||
- ❌ **DON'T**: Call `reportProgress`.
|
||||
- Export the wrapper function.
|
||||
|
||||
3. **Update `task-master-core.js` with Import/Export**: Import and re-export your `*Direct` function and add it to the `directFunctions` map.
|
||||
|
||||
4. **Create MCP Tool (`mcp-server/src/tools/`)**:
|
||||
- Create a new file (e.g., `your-command.js`) using **kebab-case**.
|
||||
- Import `zod`, `handleApiResult`, `createErrorResponse`, **`getProjectRootFromSession`**, and your `yourCommandDirect` function.
|
||||
- Import `zod`, `handleApiResult`, `createErrorResponse`, `getProjectRootFromSession`, and your `yourCommandDirect` function. Import `AsyncOperationManager` if needed.
|
||||
- Implement `registerYourCommandTool(server)`.
|
||||
- Define the tool `name` using **snake_case** (e.g., `your_command`).
|
||||
- Define the `parameters` using `zod`. **Crucially, define `projectRoot` as optional**: `projectRoot: z.string().optional().describe(...)`. Include `file` if applicable.
|
||||
- Implement the standard `async execute(args, { log, reportProgress, session })` method:
|
||||
- Get `rootFolder` using `getProjectRootFromSession` (with fallback to `args.projectRoot`).
|
||||
- Call `yourCommandDirect({ ...args, projectRoot: rootFolder }, log)`.
|
||||
- Pass the result to `handleApiResult(result, log, 'Error Message')`.
|
||||
- Define the `parameters` using `zod`. Include `projectRoot: z.string().optional()`.
|
||||
- Implement the `async execute(args, { log, session })` method (omitting `reportProgress` from destructuring).
|
||||
- Get `rootFolder` using `getProjectRootFromSession(session, log)`.
|
||||
- **Determine Execution Strategy**:
|
||||
- **If using `AsyncOperationManager`**: Create the operation, call the `*Direct` function from within the async task callback (passing `log` and `{ session }`), report progress *from the callback*, and return the initial `ACCEPTED` response.
|
||||
- **If calling `*Direct` function synchronously** (like `add-task`): Call `await yourCommandDirect({ ...args, projectRoot }, log, { session });`. Handle the result with `handleApiResult`.
|
||||
- ❌ **DON'T**: Pass `reportProgress` down to the direct function in either case.
|
||||
|
||||
5. **Register Tool**: Import and call `registerYourCommandTool` in `mcp-server/src/tools/index.js`.
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ The standard pattern for adding a feature follows this workflow:
|
||||
## Critical Checklist for New Features
|
||||
|
||||
- **Comprehensive Function Exports**:
|
||||
- ✅ **DO**: Export all helper functions and utility methods needed by your new function
|
||||
- ✅ **DO**: Review dependencies and ensure functions like `findTaskById`, `taskExists` are exported
|
||||
- ❌ **DON'T**: Assume internal functions are already exported - always check and add them explicitly
|
||||
- ✅ **DO**: Export **all core functions, helper functions (like `generateSubtaskPrompt`), and utility methods** needed by your new function or command from their respective modules.
|
||||
- ✅ **DO**: **Explicitly review the module's `export { ... }` block** at the bottom of the file to ensure every required dependency (even seemingly minor helpers like `findTaskById`, `taskExists`, specific prompt generators, AI call handlers, etc.) is included.
|
||||
- ❌ **DON'T**: Assume internal functions are already exported - **always verify**. A missing export will cause runtime errors (e.g., `ReferenceError: generateSubtaskPrompt is not defined`).
|
||||
- **Example**: If implementing a feature that checks task existence, ensure the helper function is in exports:
|
||||
```javascript
|
||||
// At the bottom of your module file:
|
||||
@@ -45,14 +45,21 @@ The standard pattern for adding a feature follows this workflow:
|
||||
yourNewFunction,
|
||||
taskExists, // Helper function used by yourNewFunction
|
||||
findTaskById, // Helper function used by yourNewFunction
|
||||
generateSubtaskPrompt, // Helper needed by expand/add features
|
||||
getSubtasksFromAI, // Helper needed by expand/add features
|
||||
};
|
||||
```
|
||||
|
||||
- **Parameter Completeness**:
|
||||
- **Parameter Completeness and Matching**:
|
||||
- ✅ **DO**: Pass all required parameters to functions you call within your implementation
|
||||
- ✅ **DO**: Check function signatures before implementing calls to them
|
||||
- ✅ **DO**: Verify that direct function parameters match their core function counterparts
|
||||
- ✅ **DO**: When implementing a direct function for MCP, ensure it only accepts parameters that exist in the core function
|
||||
- ✅ **DO**: Verify the expected *internal structure* of complex object parameters (like the `mcpLog` object, see mcp.mdc for the required logger wrapper pattern)
|
||||
- ❌ **DON'T**: Add parameters to direct functions that don't exist in core functions
|
||||
- ❌ **DON'T**: Assume default parameter values will handle missing arguments
|
||||
- **Example**: When calling file generation, pass both required parameters:
|
||||
- ❌ **DON'T**: Assume object parameters will work without verifying their required internal structure or methods.
|
||||
- **Example**: When calling file generation, pass all required parameters:
|
||||
```javascript
|
||||
// ✅ DO: Pass all required parameters
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
@@ -60,12 +67,59 @@ The standard pattern for adding a feature follows this workflow:
|
||||
// ❌ DON'T: Omit required parameters
|
||||
await generateTaskFiles(tasksPath); // Error - missing outputDir parameter
|
||||
```
|
||||
|
||||
**Example**: Properly match direct function parameters to core function:
|
||||
```javascript
|
||||
// Core function signature
|
||||
async function expandTask(tasksPath, taskId, numSubtasks, useResearch = false, additionalContext = '', options = {}) {
|
||||
// Implementation...
|
||||
}
|
||||
|
||||
// ✅ DO: Match direct function parameters to core function
|
||||
export async function expandTaskDirect(args, log, context = {}) {
|
||||
// Extract only parameters that exist in the core function
|
||||
const taskId = parseInt(args.id, 10);
|
||||
const numSubtasks = args.num ? parseInt(args.num, 10) : undefined;
|
||||
const useResearch = args.research === true;
|
||||
const additionalContext = args.prompt || '';
|
||||
|
||||
// Call core function with matched parameters
|
||||
const result = await expandTask(
|
||||
tasksPath,
|
||||
taskId,
|
||||
numSubtasks,
|
||||
useResearch,
|
||||
additionalContext,
|
||||
{ mcpLog: log, session: context.session }
|
||||
);
|
||||
|
||||
// Return result
|
||||
return { success: true, data: result, fromCache: false };
|
||||
}
|
||||
|
||||
// ❌ DON'T: Use parameters that don't exist in the core function
|
||||
export async function expandTaskDirect(args, log, context = {}) {
|
||||
// DON'T extract parameters that don't exist in the core function!
|
||||
const force = args.force === true; // ❌ WRONG - 'force' doesn't exist in core function
|
||||
|
||||
// DON'T pass non-existent parameters to core functions
|
||||
const result = await expandTask(
|
||||
tasksPath,
|
||||
args.id,
|
||||
args.num,
|
||||
args.research,
|
||||
args.prompt,
|
||||
force, // ❌ WRONG - this parameter doesn't exist in the core function
|
||||
{ mcpLog: log }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
- **Consistent File Path Handling**:
|
||||
- ✅ **DO**: Use consistent file naming conventions: `task_${id.toString().padStart(3, '0')}.txt`
|
||||
- ✅ **DO**: Use `path.join()` for composing file paths
|
||||
- ✅ **DO**: Use appropriate file extensions (.txt for tasks, .json for data)
|
||||
- ❌ **DON'T**: Hardcode path separators or inconsistent file extensions
|
||||
- ✅ DO: Use consistent file naming conventions: `task_${id.toString().padStart(3, '0')}.txt`
|
||||
- ✅ DO: Use `path.join()` for composing file paths
|
||||
- ✅ DO: Use appropriate file extensions (.txt for tasks, .json for data)
|
||||
- ❌ DON'T: Hardcode path separators or inconsistent file extensions
|
||||
- **Example**: Creating file paths for tasks:
|
||||
```javascript
|
||||
// ✅ DO: Use consistent file naming and path.join
|
||||
@@ -79,10 +133,10 @@ The standard pattern for adding a feature follows this workflow:
|
||||
```
|
||||
|
||||
- **Error Handling and Reporting**:
|
||||
- ✅ **DO**: Use structured error objects with code and message properties
|
||||
- ✅ **DO**: Include clear error messages identifying the specific problem
|
||||
- ✅ **DO**: Handle both function-specific errors and potential file system errors
|
||||
- ✅ **DO**: Log errors at appropriate severity levels
|
||||
- ✅ DO: Use structured error objects with code and message properties
|
||||
- ✅ DO: Include clear error messages identifying the specific problem
|
||||
- ✅ DO: Handle both function-specific errors and potential file system errors
|
||||
- ✅ DO: Log errors at appropriate severity levels
|
||||
- **Example**: Structured error handling in core functions:
|
||||
```javascript
|
||||
try {
|
||||
@@ -98,33 +152,43 @@ The standard pattern for adding a feature follows this workflow:
|
||||
```
|
||||
|
||||
- **Silent Mode Implementation**:
|
||||
- ✅ **DO**: Import silent mode utilities in direct functions: `import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';`
|
||||
- ✅ **DO**: Wrap core function calls with silent mode:
|
||||
```javascript
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
const result = await coreFunction(...);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
```
|
||||
- ✅ **DO**: Ensure silent mode is disabled in error handling:
|
||||
```javascript
|
||||
try {
|
||||
enableSilentMode();
|
||||
// Core function call
|
||||
disableSilentMode();
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
```
|
||||
- ✅ **DO**: Add silent mode handling in all direct functions that call core functions
|
||||
- ❌ **DON'T**: Forget to disable silent mode, which would suppress all future logs
|
||||
- ❌ **DON'T**: Enable silent mode outside of direct functions in the MCP server
|
||||
- ✅ **DO**: Import all silent mode utilities together:
|
||||
```javascript
|
||||
import { enableSilentMode, disableSilentMode, isSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
```
|
||||
- ✅ **DO**: Always use `isSilentMode()` function to check global silent mode status, never reference global variables.
|
||||
- ✅ **DO**: Wrap core function calls **within direct functions** using `enableSilentMode()` and `disableSilentMode()` in a `try/finally` block if the core function might produce console output (like banners, spinners, direct `console.log`s) that isn't reliably controlled by an `outputFormat` parameter.
|
||||
```javascript
|
||||
// Direct Function Example:
|
||||
try {
|
||||
// Prefer passing 'json' if the core function reliably handles it
|
||||
const result = await coreFunction(...args, 'json');
|
||||
// OR, if outputFormat is not enough/unreliable:
|
||||
// enableSilentMode(); // Enable *before* the call
|
||||
// const result = await coreFunction(...args);
|
||||
// disableSilentMode(); // Disable *after* the call (typically in finally)
|
||||
|
||||
return { success: true, data: result };
|
||||
} catch (error) {
|
||||
log.error(`Error: ${error.message}`);
|
||||
return { success: false, error: { message: error.message } };
|
||||
} finally {
|
||||
// If you used enable/disable, ensure disable is called here
|
||||
// disableSilentMode();
|
||||
}
|
||||
```
|
||||
- ✅ **DO**: Core functions themselves *should* ideally check `outputFormat === 'text'` before displaying UI elements (banners, spinners, boxes) and use internal logging (`log`/`report`) that respects silent mode. The `enable/disableSilentMode` wrapper in the direct function is a safety net.
|
||||
- ✅ **DO**: Handle mixed parameter/global silent mode correctly for functions accepting both (less common now, prefer `outputFormat`):
|
||||
```javascript
|
||||
// Check both the passed parameter and global silent mode
|
||||
const isSilent = silentMode || (typeof silentMode === 'undefined' && isSilentMode());
|
||||
```
|
||||
- ❌ **DON'T**: Forget to disable silent mode in a `finally` block if you enabled it.
|
||||
- ❌ **DON'T**: Access the global `silentMode` flag directly.
|
||||
|
||||
- **Debugging Strategy**:
|
||||
- ✅ **DO**: If an MCP tool fails with vague errors (e.g., JSON parsing issues like `Unexpected token ... is not valid JSON`), **try running the equivalent CLI command directly in the terminal** (e.g., `task-master expand --all`). CLI output often provides much more specific error messages (like missing function definitions or stack traces from the core logic) that pinpoint the root cause.
|
||||
- ❌ **DON'T**: Rely solely on MCP logs if the error is unclear; use the CLI as a complementary debugging tool for core logic issues.
|
||||
|
||||
```javascript
|
||||
// 1. CORE LOGIC: Add function to appropriate module (example in task-manager.js)
|
||||
|
||||
@@ -10,6 +10,8 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
|
||||
**Note:** For interacting with Taskmaster programmatically or via integrated tools, using the **MCP tools is strongly recommended** due to better performance, structured data, and error handling. The CLI commands serve as a user-friendly alternative and fallback. See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for MCP implementation details and [`commands.mdc`](mdc:.cursor/rules/commands.mdc) for CLI implementation guidelines.
|
||||
|
||||
**Important:** Several MCP tools involve AI processing and are long-running operations that may take up to a minute to complete. When using these tools, always inform users that the operation is in progress and to wait patiently for results. The AI-powered tools include: `parse_prd`, `analyze_project_complexity`, `update_subtask`, `update_task`, `update`, `expand_all`, `expand_task`, and `add_task`.
|
||||
|
||||
---
|
||||
|
||||
## Initialization & Setup
|
||||
@@ -49,6 +51,7 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `force`: `Use this to allow Taskmaster to overwrite an existing 'tasks.json' without asking for confirmation.` (CLI: `-f, --force`)
|
||||
* **Usage:** Useful for bootstrapping a project from an existing requirements document.
|
||||
* **Notes:** Task Master will strictly adhere to any specific requirements mentioned in the PRD (libraries, database schemas, frameworks, tech stacks, etc.) while filling in any gaps where the PRD isn't fully specified. Tasks are designed to provide the most direct implementation path while avoiding over-engineering.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
---
|
||||
|
||||
@@ -99,6 +102,7 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `priority`: `Set the priority for the new task ('high', 'medium', 'low'; default: 'medium').` (CLI: `--priority <priority>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Quickly add newly identified tasks during development.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 7. Add Subtask (`add_subtask`)
|
||||
|
||||
@@ -127,7 +131,8 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `prompt`: `Required. Explain the change or new context for Taskmaster to apply to the tasks (e.g., "We are now using React Query instead of Redux Toolkit for data fetching").` (CLI: `-p, --prompt <text>`)
|
||||
* `research`: `Enable Taskmaster to use Perplexity AI for more informed updates based on external knowledge (requires PERPLEXITY_API_KEY).` (CLI: `-r, --research`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Handle significant implementation changes or pivots that affect multiple future tasks.
|
||||
* **Usage:** Handle significant implementation changes or pivots that affect multiple future tasks. Example CLI: `task-master update --from='18' --prompt='Switching to React Query.\nNeed to refactor data fetching...'`
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 9. Update Task (`update_task`)
|
||||
|
||||
@@ -139,19 +144,21 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `prompt`: `Required. Explain the specific changes or provide the new information Taskmaster should incorporate into this task.` (CLI: `-p, --prompt <text>`)
|
||||
* `research`: `Enable Taskmaster to use Perplexity AI for more informed updates (requires PERPLEXITY_API_KEY).` (CLI: `-r, --research`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Refine a specific task based on new understanding or feedback.
|
||||
* **Usage:** Refine a specific task based on new understanding or feedback. Example CLI: `task-master update-task --id='15' --prompt='Clarification: Use PostgreSQL instead of MySQL.\nUpdate schema details...'`
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 10. Update Subtask (`update_subtask`)
|
||||
|
||||
* **MCP Tool:** `update_subtask`
|
||||
* **CLI Command:** `task-master update-subtask [options]`
|
||||
* **Description:** `Append timestamped notes or details to a specific Taskmaster subtask without overwriting existing content.`
|
||||
* **Description:** `Append timestamped notes or details to a specific Taskmaster subtask without overwriting existing content. Intended for iterative implementation logging.`
|
||||
* **Key Parameters/Options:**
|
||||
* `id`: `Required. The specific ID of the Taskmaster subtask (e.g., '15.2') you want to add information to.` (CLI: `-i, --id <id>`)
|
||||
* `prompt`: `Required. Provide the information or notes Taskmaster should append to the subtask's details.` (CLI: `-p, --prompt <text>`)
|
||||
* `prompt`: `Required. Provide the information or notes Taskmaster should append to the subtask's details. Ensure this adds *new* information not already present.` (CLI: `-p, --prompt <text>`)
|
||||
* `research`: `Enable Taskmaster to use Perplexity AI for more informed updates (requires PERPLEXITY_API_KEY).` (CLI: `-r, --research`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Add implementation notes, code snippets, or clarifications to a subtask during development.
|
||||
* **Usage:** Add implementation notes, code snippets, or clarifications to a subtask during development. Before calling, review the subtask's current details to append only fresh insights, helping to build a detailed log of the implementation journey and avoid redundancy. Example CLI: `task-master update-subtask --id='15.2' --prompt='Discovered that the API requires header X.\nImplementation needs adjustment...'`
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 11. Set Task Status (`set_task_status`)
|
||||
|
||||
@@ -193,6 +200,7 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `force`: `Use this to make Taskmaster replace existing subtasks with newly generated ones.` (CLI: `--force`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Generate a detailed implementation plan for a complex task before starting coding.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 14. Expand All Tasks (`expand_all`)
|
||||
|
||||
@@ -206,6 +214,7 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
* `force`: `Make Taskmaster replace existing subtasks.` (CLI: `--force`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Useful after initial task generation or complexity analysis to break down multiple tasks at once.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 15. Clear Subtasks (`clear_subtasks`)
|
||||
|
||||
@@ -278,45 +287,67 @@ This document provides a detailed reference for interacting with Taskmaster, cov
|
||||
|
||||
## Analysis & Reporting
|
||||
|
||||
### 21. Analyze Complexity (`analyze_complexity`)
|
||||
### 21. Analyze Project Complexity (`analyze_project_complexity`)
|
||||
|
||||
* **MCP Tool:** `analyze_complexity`
|
||||
* **MCP Tool:** `analyze_project_complexity`
|
||||
* **CLI Command:** `task-master analyze-complexity [options]`
|
||||
* **Description:** `Let Taskmaster analyze the complexity of your tasks and generate a report with recommendations for which ones need breaking down.`
|
||||
* **Description:** `Have Taskmaster analyze your tasks to determine their complexity and suggest which ones need to be broken down further.`
|
||||
* **Key Parameters/Options:**
|
||||
* `output`: `Where Taskmaster should save the JSON complexity analysis report (default: 'scripts/task-complexity-report.json').` (CLI: `-o, --output <file>`)
|
||||
* `threshold`: `The minimum complexity score (1-10) for Taskmaster to recommend expanding a task.` (CLI: `-t, --threshold <number>`)
|
||||
* `research`: `Enable Taskmaster to use Perplexity AI for more informed complexity analysis (requires PERPLEXITY_API_KEY).` (CLI: `-r, --research`)
|
||||
* `output`: `Where to save the complexity analysis report (default: 'scripts/task-complexity-report.json').` (CLI: `-o, --output <file>`)
|
||||
* `threshold`: `The minimum complexity score (1-10) that should trigger a recommendation to expand a task.` (CLI: `-t, --threshold <number>`)
|
||||
* `research`: `Enable Perplexity AI for more accurate complexity analysis (requires PERPLEXITY_API_KEY).` (CLI: `-r, --research`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Identify which tasks are likely too large and need further breakdown before implementation.
|
||||
* **Usage:** Used before breaking down tasks to identify which ones need the most attention.
|
||||
* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress.
|
||||
|
||||
### 22. Complexity Report (`complexity_report`)
|
||||
### 22. View Complexity Report (`complexity_report`)
|
||||
|
||||
* **MCP Tool:** `complexity_report`
|
||||
* **CLI Command:** `task-master complexity-report [options]`
|
||||
* **Description:** `Display the Taskmaster task complexity analysis report generated by 'analyze-complexity'.`
|
||||
* **Description:** `Display the task complexity analysis report in a readable format.`
|
||||
* **Key Parameters/Options:**
|
||||
* `file`: `Path to the JSON complexity report file (default: 'scripts/task-complexity-report.json').` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** View the formatted results of the complexity analysis to guide task expansion.
|
||||
* `file`: `Path to the complexity report (default: 'scripts/task-complexity-report.json').` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Review and understand the complexity analysis results after running analyze-complexity.
|
||||
|
||||
---
|
||||
|
||||
## File Generation
|
||||
## File Management
|
||||
|
||||
### 23. Generate Task Files (`generate`)
|
||||
|
||||
* **MCP Tool:** `generate`
|
||||
* **CLI Command:** `task-master generate [options]`
|
||||
* **Description:** `Generate individual markdown files for each task and subtask defined in your Taskmaster 'tasks.json'.`
|
||||
* **Description:** `Create or update individual Markdown files for each task based on your tasks.json.`
|
||||
* **Key Parameters/Options:**
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file containing the task data (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* `output`: `The directory where Taskmaster should save the generated markdown task files (default: 'tasks').` (CLI: `-o, --output <dir>`)
|
||||
* **Usage:** Create/update the individual `.md` files in the `tasks/` directory, useful for tracking changes in git or viewing tasks individually.
|
||||
* `output`: `The directory where Taskmaster should save the task files (default: in a 'tasks' directory).` (CLI: `-o, --output <directory>`)
|
||||
* `file`: `Path to your Taskmaster 'tasks.json' file (default relies on auto-detection).` (CLI: `-f, --file <file>`)
|
||||
* **Usage:** Run this after making changes to tasks.json to keep individual task files up to date.
|
||||
|
||||
---
|
||||
|
||||
## Configuration & Metadata
|
||||
## Environment Variables Configuration
|
||||
|
||||
- **Environment Variables**: Taskmaster relies on environment variables for configuration (API keys, model preferences, default settings). See [`dev_workflow.mdc`](mdc:.cursor/rules/dev_workflow.mdc) or the project README for a list.
|
||||
- **`tasks.json`**: The core data file containing the array of tasks and their details. See [`tasks.mdc`](mdc:.cursor/rules/tasks.mdc) for details.
|
||||
- **`task_xxx.md` files**: Individual markdown files generated by the `generate` command/tool, reflecting the content of `tasks.json`.
|
||||
Taskmaster's behavior can be customized via environment variables. These affect both CLI and MCP server operation:
|
||||
|
||||
* **ANTHROPIC_API_KEY** (Required): Your Anthropic API key for Claude.
|
||||
* **MODEL**: Claude model to use (default: `claude-3-opus-20240229`).
|
||||
* **MAX_TOKENS**: Maximum tokens for AI responses (default: 8192).
|
||||
* **TEMPERATURE**: Temperature for AI model responses (default: 0.7).
|
||||
* **DEBUG**: Enable debug logging (`true`/`false`, default: `false`).
|
||||
* **LOG_LEVEL**: Console output level (`debug`, `info`, `warn`, `error`, default: `info`).
|
||||
* **DEFAULT_SUBTASKS**: Default number of subtasks for `expand` (default: 5).
|
||||
* **DEFAULT_PRIORITY**: Default priority for new tasks (default: `medium`).
|
||||
* **PROJECT_NAME**: Project name used in metadata.
|
||||
* **PROJECT_VERSION**: Project version used in metadata.
|
||||
* **PERPLEXITY_API_KEY**: API key for Perplexity AI (for `--research` flags).
|
||||
* **PERPLEXITY_MODEL**: Perplexity model to use (default: `sonar-medium-online`).
|
||||
|
||||
Set these in your `.env` file in the project root or in your environment before running Taskmaster.
|
||||
|
||||
---
|
||||
|
||||
For implementation details:
|
||||
* CLI commands: See [`commands.mdc`](mdc:.cursor/rules/commands.mdc)
|
||||
* MCP server: See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc)
|
||||
* Task structure: See [`tasks.mdc`](mdc:.cursor/rules/tasks.mdc)
|
||||
* Workflow: See [`dev_workflow.mdc`](mdc:.cursor/rules/dev_workflow.mdc)
|
||||
|
||||
@@ -109,6 +109,29 @@ alwaysApply: false
|
||||
- ✅ DO: Use appropriate icons for different log levels
|
||||
- ✅ DO: Respect the configured log level
|
||||
- ❌ DON'T: Add direct console.log calls outside the logging utility
|
||||
- **Note on Passed Loggers**: When a logger object (like the FastMCP `log` object) is passed *as a parameter* (e.g., as `mcpLog`) into core Task Master functions, the receiving function often expects specific methods (`.info`, `.warn`, `.error`, etc.) to be directly callable on that object (e.g., `mcpLog[level](...)`). If the passed logger doesn't have this exact structure, a wrapper object may be needed. See the **Handling Logging Context (`mcpLog`)** section in [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for the standard pattern used in direct functions.
|
||||
|
||||
- **Logger Wrapper Pattern**:
|
||||
- ✅ DO: Use the logger wrapper pattern when passing loggers to prevent `mcpLog[level] is not a function` errors:
|
||||
```javascript
|
||||
// Standard logWrapper pattern to wrap FastMCP's log object
|
||||
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),
|
||||
success: (message, ...args) => log.info(message, ...args) // Map success to info
|
||||
};
|
||||
|
||||
// Pass this wrapper as mcpLog to ensure consistent method availability
|
||||
// This also ensures output format is set to 'json' in many core functions
|
||||
const options = { mcpLog: logWrapper, session };
|
||||
```
|
||||
- ✅ DO: Implement this pattern in any direct function that calls core functions expecting `mcpLog`
|
||||
- ✅ DO: Use this solution in conjunction with silent mode for complete output control
|
||||
- ❌ DON'T: Pass the FastMCP `log` object directly as `mcpLog` to core functions
|
||||
- **Important**: This pattern has successfully fixed multiple issues in MCP tools (e.g., `update-task`, `update-subtask`) where using or omitting `mcpLog` incorrectly led to runtime errors or JSON parsing failures.
|
||||
- For complete implementation details, see the **Handling Logging Context (`mcpLog`)** section in [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc).
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Implement a proper logging utility
|
||||
@@ -135,6 +158,107 @@ alwaysApply: false
|
||||
}
|
||||
```
|
||||
|
||||
## Silent Mode Utilities (in `scripts/modules/utils.js`)
|
||||
|
||||
- **Silent Mode Control**:
|
||||
- ✅ DO: Use the exported silent mode functions rather than accessing global variables
|
||||
- ✅ DO: Always use `isSilentMode()` to check the current silent mode state
|
||||
- ✅ DO: Ensure silent mode is disabled in a `finally` block to prevent it from staying enabled
|
||||
- ❌ DON'T: Access the global `silentMode` variable directly
|
||||
- ❌ DON'T: Forget to disable silent mode after enabling it
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Use the silent mode control functions properly
|
||||
|
||||
// Example of proper implementation in utils.js:
|
||||
|
||||
// Global silent mode flag (private to the module)
|
||||
let silentMode = false;
|
||||
|
||||
// Enable silent mode
|
||||
function enableSilentMode() {
|
||||
silentMode = true;
|
||||
}
|
||||
|
||||
// Disable silent mode
|
||||
function disableSilentMode() {
|
||||
silentMode = false;
|
||||
}
|
||||
|
||||
// Check if silent mode is enabled
|
||||
function isSilentMode() {
|
||||
return silentMode;
|
||||
}
|
||||
|
||||
// Example of proper usage in another module:
|
||||
import { enableSilentMode, disableSilentMode, isSilentMode } from './utils.js';
|
||||
|
||||
// Check current status
|
||||
if (!isSilentMode()) {
|
||||
console.log('Silent mode is not enabled');
|
||||
}
|
||||
|
||||
// Use try/finally pattern to ensure silent mode is disabled
|
||||
try {
|
||||
enableSilentMode();
|
||||
// Do something that should suppress console output
|
||||
performOperation();
|
||||
} finally {
|
||||
disableSilentMode();
|
||||
}
|
||||
```
|
||||
|
||||
- **Integration with Logging**:
|
||||
- ✅ DO: Make the `log` function respect silent mode
|
||||
```javascript
|
||||
function log(level, ...args) {
|
||||
// Skip logging if silent mode is enabled
|
||||
if (isSilentMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rest of logging logic...
|
||||
}
|
||||
```
|
||||
|
||||
- **Common Patterns for Silent Mode**:
|
||||
- ✅ DO: In **direct functions** (`mcp-server/src/core/direct-functions/*`) that call **core functions** (`scripts/modules/*`), ensure console output from the core function is suppressed to avoid breaking MCP JSON responses.
|
||||
- **Preferred Method**: Update the core function to accept an `outputFormat` parameter (e.g., `outputFormat = 'text'`) and make it check `outputFormat === 'text'` before displaying any UI elements (banners, spinners, boxes, direct `console.log`s). Pass `'json'` from the direct function.
|
||||
- **Necessary Fallback/Guarantee**: If the core function *cannot* be modified or its output suppression via `outputFormat` is unreliable, **wrap the core function call within the direct function** using `enableSilentMode()` and `disableSilentMode()` in a `try/finally` block. This acts as a safety net.
|
||||
```javascript
|
||||
// Example in a direct function
|
||||
export async function someOperationDirect(args, log) {
|
||||
let result;
|
||||
const tasksPath = findTasksJsonPath(args, log); // Get path first
|
||||
|
||||
// Option 1: Core function handles 'json' format (Preferred)
|
||||
try {
|
||||
result = await coreFunction(tasksPath, ...otherArgs, 'json'); // Pass 'json'
|
||||
return { success: true, data: result, fromCache: false };
|
||||
} catch (error) {
|
||||
// Handle error...
|
||||
}
|
||||
|
||||
// Option 2: Core function output unreliable (Fallback/Guarantee)
|
||||
try {
|
||||
enableSilentMode(); // Enable before call
|
||||
result = await coreFunction(tasksPath, ...otherArgs); // Call without format param
|
||||
} catch (error) {
|
||||
// Handle error...
|
||||
log.error(`Failed: ${error.message}`);
|
||||
return { success: false, error: { /* ... */ } };
|
||||
} finally {
|
||||
disableSilentMode(); // ALWAYS disable in finally
|
||||
}
|
||||
return { success: true, data: result, fromCache: false }; // Assuming success if no error caught
|
||||
}
|
||||
```
|
||||
- ✅ DO: For functions that accept a silent mode parameter but also need to check global state (less common):
|
||||
```javascript
|
||||
// Check both the passed parameter and global silent mode
|
||||
const isSilent = options.silentMode || (typeof options.silentMode === 'undefined' && isSilentMode());
|
||||
```
|
||||
|
||||
## File Operations (in `scripts/modules/utils.js`)
|
||||
|
||||
- **Error Handling**:
|
||||
|
||||
Reference in New Issue
Block a user