Files
claude-task-master/.cursor/rules/utilities.mdc
Eyal Toledano 3cdaff6c66 feat(mcp): major MCP server improvements and documentation overhaul
- Enhance MCP server robustness and usability:
  - Implement smart project root detection with hierarchical fallbacks
  - Make projectRoot parameter optional across all MCP tools
  - Add comprehensive PROJECT_MARKERS for reliable project detection
  - Improve error messages and logging for better debugging
  - Split monolithic core into focused direct-function files

- Implement full suite of MCP commands:
  - Add task management: update-task, update-subtask, generate
  - Add task organization: expand-task, expand-all, clear-subtasks
  - Add dependency handling: add/remove/validate/fix dependencies
  - Add analysis tools: analyze-complexity, complexity-report
  - Rename commands for better API consistency (list-tasks → get-tasks)

- Enhance documentation and developer experience:
  - Create and bundle new taskmaster.mdc as comprehensive reference
  - Document all tools with natural language patterns and examples
  - Clarify project root auto-detection in documentation
  - Standardize naming conventions across MCP components
  - Add cross-references between related tools and commands

- Improve UI and progress tracking:
  - Add color-coded progress bars with status breakdown
  - Implement cancelled/deferred task status handling
  - Enhance status visualization and counting
  - Optimize display for various terminal sizes

This major update significantly improves the robustness and usability
of the MCP server while providing comprehensive documentation for both
users and developers. The changes make Task Master more intuitive to
use programmatically while maintaining full CLI functionality.
2025-04-01 03:48:05 -04:00

393 lines
14 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
description: Guidelines for implementing utility functions
globs: scripts/modules/utils.js, mcp-server/src/**/*
alwaysApply: false
---
# Utility Function Guidelines
## General Principles
- **Function Scope**:
- ✅ DO: Create utility functions that serve multiple modules
- ✅ DO: Keep functions single-purpose and focused
- ❌ DON'T: Include business logic in utility functions
- ❌ DON'T: Create utilities with side effects
```javascript
// ✅ DO: Create focused, reusable utilities
/**
* Truncates text to a specified length
* @param {string} text - The text to truncate
* @param {number} maxLength - The maximum length
* @returns {string} The truncated text
*/
function truncate(text, maxLength) {
if (!text || text.length <= maxLength) {
return text;
}
return text.slice(0, maxLength - 3) + '...';
}
```
```javascript
// ❌ DON'T: Add side effects to utilities
function truncate(text, maxLength) {
if (!text || text.length <= maxLength) {
return text;
}
// Side effect - modifying global state or logging
console.log(`Truncating text from ${text.length} to ${maxLength} chars`);
return text.slice(0, maxLength - 3) + '...';
}
```
- **Location**:
- **Core CLI Utilities**: Place utilities used primarily by the core `task-master` CLI logic and command modules (`scripts/modules/*`) into [`scripts/modules/utils.js`](mdc:scripts/modules/utils.js).
- **MCP Server Utilities**: Place utilities specifically designed to support the MCP server implementation into the appropriate subdirectories within `mcp-server/src/`.
- Path/Core Logic Helpers: [`mcp-server/src/core/utils/`](mdc:mcp-server/src/core/utils/) (e.g., `path-utils.js`).
- Tool Execution/Response Helpers: [`mcp-server/src/tools/utils.js`](mdc:mcp-server/src/tools/utils.js).
## Documentation Standards
- **JSDoc Format**:
- ✅ DO: Document all parameters and return values
- ✅ DO: Include descriptions for complex logic
- ✅ DO: Add examples for non-obvious usage
- ❌ DON'T: Skip documentation for "simple" functions
```javascript
// ✅ DO: Provide complete JSDoc documentation
/**
* Reads and parses a JSON file
* @param {string} filepath - Path to the JSON file
* @returns {Object|null} Parsed JSON data or null if error occurs
*/
function readJSON(filepath) {
try {
const rawData = fs.readFileSync(filepath, 'utf8');
return JSON.parse(rawData);
} catch (error) {
log('error', `Error reading JSON file ${filepath}:`, error.message);
if (CONFIG.debug) {
console.error(error);
}
return null;
}
}
```
## Configuration Management (in `scripts/modules/utils.js`)
- **Environment Variables**:
- ✅ DO: Provide default values for all configuration
- ✅ DO: Use environment variables for customization
- ✅ DO: Document available configuration options
- ❌ DON'T: Hardcode values that should be configurable
```javascript
// ✅ DO: Set up configuration with defaults and environment overrides
const CONFIG = {
model: process.env.MODEL || 'claude-3-opus-20240229', // Updated default model
maxTokens: parseInt(process.env.MAX_TOKENS || '4000'),
temperature: parseFloat(process.env.TEMPERATURE || '0.7'),
debug: process.env.DEBUG === "true",
logLevel: process.env.LOG_LEVEL || "info",
defaultSubtasks: parseInt(process.env.DEFAULT_SUBTASKS || "3"),
defaultPriority: process.env.DEFAULT_PRIORITY || "medium",
projectName: process.env.PROJECT_NAME || "Task Master Project", // Generic project name
projectVersion: "1.5.0" // Version should be updated via release process
};
```
## Logging Utilities (in `scripts/modules/utils.js`)
- **Log Levels**:
- ✅ DO: Support multiple log levels (debug, info, warn, error)
- ✅ 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
```javascript
// ✅ DO: Implement a proper logging utility
const LOG_LEVELS = {
debug: 0,
info: 1,
warn: 2,
error: 3
};
function log(level, ...args) {
const icons = {
debug: chalk.gray('🔍'),
info: chalk.blue(''),
warn: chalk.yellow('⚠️'),
error: chalk.red('❌'),
success: chalk.green('✅')
};
if (LOG_LEVELS[level] >= LOG_LEVELS[CONFIG.logLevel]) {
const icon = icons[level] || '';
console.log(`${icon} ${args.join(' ')}`);
}
}
```
## File Operations (in `scripts/modules/utils.js`)
- **Error Handling**:
- ✅ DO: Use try/catch blocks for all file operations
- ✅ DO: Return null or a default value on failure
- ✅ DO: Log detailed error information using the `log` utility
- ❌ DON'T: Allow exceptions to propagate unhandled from simple file reads/writes
```javascript
// ✅ DO: Handle file operation errors properly in core utils
function writeJSON(filepath, data) {
try {
// Ensure directory exists (example)
const dir = path.dirname(filepath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filepath, JSON.stringify(data, null, 2));
} catch (error) {
log('error', `Error writing JSON file ${filepath}:`, error.message);
if (CONFIG.debug) {
console.error(error);
}
}
}
```
## Task-Specific Utilities (in `scripts/modules/utils.js`)
- **Task ID Formatting**:
- ✅ DO: Create utilities for consistent ID handling
- ✅ DO: Support different ID formats (numeric, string, dot notation)
- ❌ DON'T: Duplicate formatting logic across modules
```javascript
// ✅ DO: Create utilities for common operations
/**
* Formats a task ID as a string
* @param {string|number} id - The task ID to format
* @returns {string} The formatted task ID
*/
function formatTaskId(id) {
if (typeof id === 'string' && id.includes('.')) {
return id; // Already formatted as a string with a dot (e.g., "1.2")
}
if (typeof id === 'number') {
return id.toString();
}
return id;
}
```
- **Task Search**:
- ✅ DO: Implement reusable task finding utilities
- ✅ DO: Support both task and subtask lookups
- ✅ DO: Add context to subtask results
```javascript
// ✅ DO: Create comprehensive search utilities
/**
* Finds a task by ID in the tasks array
* @param {Array} tasks - The tasks array
* @param {string|number} taskId - The task ID to find
* @returns {Object|null} The task object or null if not found
*/
function findTaskById(tasks, taskId) {
if (!taskId || !tasks || !Array.isArray(tasks)) {
return null;
}
// Check if it's a subtask ID (e.g., "1.2")
if (typeof taskId === 'string' && taskId.includes('.')) {
const [parentId, subtaskId] = taskId.split('.').map(id => parseInt(id, 10));
const parentTask = tasks.find(t => t.id === parentId);
if (!parentTask || !parentTask.subtasks) {
return null;
}
const subtask = parentTask.subtasks.find(st => st.id === subtaskId);
if (subtask) {
// Add reference to parent task for context
subtask.parentTask = {
id: parentTask.id,
title: parentTask.title,
status: parentTask.status
};
subtask.isSubtask = true;
}
return subtask || null;
}
const id = parseInt(taskId, 10);
return tasks.find(t => t.id === id) || null;
}
```
## Cycle Detection (in `scripts/modules/utils.js`)
- **Graph Algorithms**:
- ✅ DO: Implement cycle detection using graph traversal
- ✅ DO: Track visited nodes and recursion stack
- ✅ DO: Return specific information about cycles
```javascript
// ✅ DO: Implement proper cycle detection
/**
* Find cycles in a dependency graph using DFS
* @param {string} subtaskId - Current subtask ID
* @param {Map} dependencyMap - Map of subtask IDs to their dependencies
* @param {Set} visited - Set of visited nodes
* @param {Set} recursionStack - Set of nodes in current recursion stack
* @returns {Array} - List of dependency edges that need to be removed to break cycles
*/
function findCycles(subtaskId, dependencyMap, visited = new Set(), recursionStack = new Set(), path = []) {
// Mark the current node as visited and part of recursion stack
visited.add(subtaskId);
recursionStack.add(subtaskId);
path.push(subtaskId);
const cyclesToBreak = [];
// Get all dependencies of the current subtask
const dependencies = dependencyMap.get(subtaskId) || [];
// For each dependency
for (const depId of dependencies) {
// If not visited, recursively check for cycles
if (!visited.has(depId)) {
const cycles = findCycles(depId, dependencyMap, visited, recursionStack, [...path]);
cyclesToBreak.push(...cycles);
}
// If the dependency is in the recursion stack, we found a cycle
else if (recursionStack.has(depId)) {
// The last edge in the cycle is what we want to remove
cyclesToBreak.push(depId);
}
}
// Remove the node from recursion stack before returning
recursionStack.delete(subtaskId);
return cyclesToBreak;
}
```
## MCP Server Core Utilities (`mcp-server/src/core/utils/`)
### Project Root and Task File Path Detection (`path-utils.js`)
- **Purpose**: This module (`mcp-server/src/core/utils/path-utils.js`) provides the **primary mechanism for locating the user's project root and `tasks.json` file**, specifically for the MCP server context.
- **`findTasksJsonPath(args, log)`**:
- ✅ **DO**: Call this function from within **direct function wrappers** (e.g., `listTasksDirect` in `mcp-server/src/core/direct-functions/`) to get the absolute path to the relevant `tasks.json`.
- Pass the *entire `args` object* received by the MCP tool and the `log` object.
- Implements a **hierarchical precedence system** for finding the project:
1. `TASK_MASTER_PROJECT_ROOT` environment variable.
2. Explicit `projectRoot` passed in `args`.
3. Cached `lastFoundProjectRoot` from a previous successful search.
4. Search upwards from `process.cwd()` for `tasks.json` or project markers (like `.git`, `package.json`).
5. Fallback to checking the Taskmaster package directory (for development).
- Throws a specific error if the `tasks.json` file cannot be located.
- Updates the `lastFoundProjectRoot` cache on success.
- **`PROJECT_MARKERS`**: An exported array of common file/directory names used to identify a likely project root during the search.
- **`getPackagePath()`**: Utility to find the installation path of the `task-master-ai` package itself.
## MCP Server Tool Utilities (`mcp-server/src/tools/utils.js`)
- **Purpose**: These utilities specifically support the MCP server tools (`mcp-server/src/tools/*.js`), handling MCP communication patterns, response formatting, caching integration, and the CLI fallback mechanism.
- **Refer to [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc)** for detailed usage patterns within the MCP tool `execute` methods and direct function wrappers.
- **`getProjectRoot(projectRootRaw, log)`**:
- Primarily a helper for `executeTaskMasterCommand` or other specific cases where only the root is needed, *not* the full `tasks.json` path.
- Normalizes a potentially relative path and applies defaults.
- ❌ **DON'T**: Use this as the primary way to find `tasks.json`. Use `findTasksJsonPath` from `path-utils.js` for that purpose within direct functions.
- **`handleApiResult(result, log, errorPrefix, processFunction)`**:
- ✅ **DO**: Call this from the MCP tool's `execute` method after receiving the result from the `*Direct` function wrapper.
- Takes the standard `{ success, data/error, fromCache }` object.
- Formats the standard MCP success or error response, including the `fromCache` flag.
- Uses `processMCPResponseData` by default to filter response data.
- **`executeTaskMasterCommand(command, log, args, projectRootRaw)`**:
- Executes a Task Master CLI command as a child process.
- Handles fallback between global `task-master` and local `node scripts/dev.js`.
- ❌ **DON'T**: Use this as the primary method for MCP tools. Prefer direct function calls via `*Direct` wrappers.
- **`processMCPResponseData(taskOrData, fieldsToRemove)`**:
- Filters task data (e.g., removing `details`, `testStrategy`) before sending to the MCP client. Called by `handleApiResult`.
- **`createContentResponse(content)` / `createErrorResponse(errorMessage)`**:
- Formatters for standard MCP success/error responses.
- **`getCachedOrExecute({ cacheKey, actionFn, log })`**:
- ✅ **DO**: Use this utility *inside direct function wrappers* to implement caching.
- Checks cache, executes `actionFn` on miss, stores result.
- Returns standard `{ success, data/error, fromCache: boolean }`.
## Export Organization
- **Grouping Related Functions**:
- ✅ DO: Keep utilities relevant to their location (e.g., core CLI utils in `scripts/modules/utils.js`, MCP path utils in `mcp-server/src/core/utils/path-utils.js`, MCP tool utils in `mcp-server/src/tools/utils.js`).
- ✅ DO: Export all utility functions in a single statement per file.
- ✅ DO: Group related exports together.
- ✅ DO: Export configuration constants (from `scripts/modules/utils.js`).
- ❌ DON'T: Use default exports.
- ❌ DON'T: Create circular dependencies (See [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc)).
```javascript
// Example export from scripts/modules/utils.js
export {
// Configuration
CONFIG,
LOG_LEVELS,
// Logging
log,
// File operations
readJSON,
writeJSON,
// String manipulation
sanitizePrompt,
truncate,
// Task utilities
// ... (taskExists, formatTaskId, findTaskById, etc.)
// Graph algorithms
findCycles,
};
// Example export from mcp-server/src/core/utils/path-utils.js
export {
findTasksJsonPath,
getPackagePath,
PROJECT_MARKERS,
lastFoundProjectRoot // Exporting for potential direct use/reset if needed
};
// Example export from mcp-server/src/tools/utils.js
export {
getProjectRoot,
handleApiResult,
executeTaskMasterCommand,
processMCPResponseData,
createContentResponse,
createErrorResponse,
getCachedOrExecute
};
```
Refer to [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) and [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc) for more context on MCP server architecture and integration.