Refactor: Modularize Task Master CLI into Modules Directory
Simplified the Task Master CLI by organizing code into modules within the directory. **Why:** - **Better Organization:** Code is now grouped by function (AI, commands, dependencies, tasks, UI, utilities). - **Easier to Maintain:** Smaller modules are simpler to update and fix. - **Scalable:** New features can be added more easily in a structured way. **What Changed:** - Moved code from single _____ _ __ __ _ |_ _|_ _ ___| | __ | \/ | __ _ ___| |_ ___ _ __ | |/ _` / __| |/ / | |\/| |/ _` / __| __/ _ \ '__| | | (_| \__ \ < | | | | (_| \__ \ || __/ | |_|\__,_|___/_|\_\ |_| |_|\__,_|___/\__\___|_| by https://x.com/eyaltoledano ╭────────────────────────────────────────────╮ │ │ │ Version: 0.9.16 Project: Task Master │ │ │ ╰────────────────────────────────────────────╯ ╭─────────────────────╮ │ │ │ Task Master CLI │ │ │ ╰─────────────────────╯ ╭───────────────────╮ │ Task Generation │ ╰───────────────────╯ parse-prd --input=<file.txt> [--tasks=10] Generate tasks from a PRD document generate Create individual task files from tasks… ╭───────────────────╮ │ Task Management │ ╰───────────────────╯ list [--status=<status>] [--with-subtas… List all tasks with their status set-status --id=<id> --status=<status> Update task status (done, pending, etc.) update --from=<id> --prompt="<context>" Update tasks based on new requirements add-task --prompt="<text>" [--dependencies=… Add a new task using AI add-dependency --id=<id> --depends-on=<id> Add a dependency to a task remove-dependency --id=<id> --depends-on=<id> Remove a dependency from a task ╭──────────────────────────╮ │ Task Analysis & Detail │ ╰──────────────────────────╯ analyze-complexity [--research] [--threshold=5] Analyze tasks and generate expansion re… complexity-report [--file=<path>] Display the complexity analysis report expand --id=<id> [--num=5] [--research] [… Break down tasks into detailed subtasks expand --all [--force] [--research] Expand all pending tasks with subtasks clear-subtasks --id=<id> Remove subtasks from specified tasks ╭─────────────────────────────╮ │ Task Navigation & Viewing │ ╰─────────────────────────────╯ next Show the next task to work on based on … show <id> Display detailed information about a sp… ╭─────────────────────────╮ │ Dependency Management │ ╰─────────────────────────╯ validate-dependenci… Identify invalid dependencies without f… fix-dependencies Fix invalid dependencies automatically ╭─────────────────────────╮ │ Environment Variables │ ╰─────────────────────────╯ ANTHROPIC_API_KEY Your Anthropic API key Required MODEL Claude model to use Default: claude-3-7-sonn… MAX_TOKENS Maximum tokens for responses Default: 4000 TEMPERATURE Temperature for model responses Default: 0.7 PERPLEXITY_API_KEY Perplexity API key for research Optional PERPLEXITY_MODEL Perplexity model to use Default: sonar-small-onl… DEBUG Enable debug logging Default: false LOG_LEVEL Console output level (debug,info,warn,error) Default: info DEFAULT_SUBTASKS Default number of subtasks to generate Default: 3 DEFAULT_PRIORITY Default task priority Default: medium PROJECT_NAME Project name displayed in UI Default: Task Master file into these new modules: - : AI interactions (Claude, Perplexity) - : CLI command definitions (Commander.js) - : Task dependency handling - : Core task operations (create, list, update, etc.) - : User interface elements (display, formatting) - : Utility functions and configuration - : Exports all modules - Replaced direct use of _____ _ __ __ _ |_ _|_ _ ___| | __ | \/ | __ _ ___| |_ ___ _ __ | |/ _` / __| |/ / | |\/| |/ _` / __| __/ _ \ '__| | | (_| \__ \ < | | | | (_| \__ \ || __/ | |_|\__,_|___/_|\_\ |_| |_|\__,_|___/\__\___|_| by https://x.com/eyaltoledano ╭────────────────────────────────────────────╮ │ │ │ Version: 0.9.16 Project: Task Master │ │ │ ╰────────────────────────────────────────────╯ ╭─────────────────────╮ │ │ │ Task Master CLI │ │ │ ╰─────────────────────╯ ╭───────────────────╮ │ Task Generation │ ╰───────────────────╯ parse-prd --input=<file.txt> [--tasks=10] Generate tasks from a PRD document generate Create individual task files from tasks… ╭───────────────────╮ │ Task Management │ ╰───────────────────╯ list [--status=<status>] [--with-subtas… List all tasks with their status set-status --id=<id> --status=<status> Update task status (done, pending, etc.) update --from=<id> --prompt="<context>" Update tasks based on new requirements add-task --prompt="<text>" [--dependencies=… Add a new task using AI add-dependency --id=<id> --depends-on=<id> Add a dependency to a task remove-dependency --id=<id> --depends-on=<id> Remove a dependency from a task ╭──────────────────────────╮ │ Task Analysis & Detail │ ╰──────────────────────────╯ analyze-complexity [--research] [--threshold=5] Analyze tasks and generate expansion re… complexity-report [--file=<path>] Display the complexity analysis report expand --id=<id> [--num=5] [--research] [… Break down tasks into detailed subtasks expand --all [--force] [--research] Expand all pending tasks with subtasks clear-subtasks --id=<id> Remove subtasks from specified tasks ╭─────────────────────────────╮ │ Task Navigation & Viewing │ ╰─────────────────────────────╯ next Show the next task to work on based on … show <id> Display detailed information about a sp… ╭─────────────────────────╮ │ Dependency Management │ ╰─────────────────────────╯ validate-dependenci… Identify invalid dependencies without f… fix-dependencies Fix invalid dependencies automatically ╭─────────────────────────╮ │ Environment Variables │ ╰─────────────────────────╯ ANTHROPIC_API_KEY Your Anthropic API key Required MODEL Claude model to use Default: claude-3-7-sonn… MAX_TOKENS Maximum tokens for responses Default: 4000 TEMPERATURE Temperature for model responses Default: 0.7 PERPLEXITY_API_KEY Perplexity API key for research Optional PERPLEXITY_MODEL Perplexity model to use Default: sonar-small-onl… DEBUG Enable debug logging Default: false LOG_LEVEL Console output level (debug,info,warn,error) Default: info DEFAULT_SUBTASKS Default number of subtasks to generate Default: 3 DEFAULT_PRIORITY Default task priority Default: medium PROJECT_NAME Project name displayed in UI Default: Task Master with the global command (see ). - Updated documentation () to reflect the new command. **Benefits:** Code is now cleaner, easier to work with, and ready for future growth. Use the command (or ) to run the CLI. See for command details.
This commit is contained in:
163
.cursor/rules/commands.mdc
Normal file
163
.cursor/rules/commands.mdc
Normal file
@@ -0,0 +1,163 @@
|
||||
---
|
||||
description: Guidelines for implementing CLI commands using Commander.js
|
||||
globs: scripts/modules/commands.js
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Command-Line Interface Implementation Guidelines
|
||||
|
||||
## Command Structure Standards
|
||||
|
||||
- **Basic Command Template**:
|
||||
```javascript
|
||||
// ✅ DO: Follow this structure for all commands
|
||||
programInstance
|
||||
.command('command-name')
|
||||
.description('Clear, concise description of what the command does')
|
||||
.option('-s, --short-option <value>', 'Option description', 'default value')
|
||||
.option('--long-option <value>', 'Option description')
|
||||
.action(async (options) => {
|
||||
// Command implementation
|
||||
});
|
||||
```
|
||||
|
||||
- **Command Handler Organization**:
|
||||
- ✅ DO: Keep action handlers concise and focused
|
||||
- ✅ DO: Extract core functionality to appropriate modules
|
||||
- ✅ DO: Include validation for required parameters
|
||||
- ❌ DON'T: Implement business logic in command handlers
|
||||
|
||||
## Option Naming Conventions
|
||||
|
||||
- **Command Names**:
|
||||
- ✅ DO: Use kebab-case for command names (`analyze-complexity`)
|
||||
- ❌ DON'T: Use camelCase for command names (`analyzeComplexity`)
|
||||
- ✅ DO: Use descriptive, action-oriented names
|
||||
|
||||
- **Option Names**:
|
||||
- ✅ DO: Use camelCase for long-form option names (`--outputFormat`)
|
||||
- ✅ DO: Provide single-letter shortcuts when appropriate (`-f, --file`)
|
||||
- ✅ DO: Use consistent option names across similar commands
|
||||
- ❌ DON'T: Use different names for the same concept (`--file` in one command, `--path` in another)
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Use consistent option naming
|
||||
.option('-f, --file <path>', 'Path to the tasks file', 'tasks/tasks.json')
|
||||
.option('-o, --output <dir>', 'Output directory', 'tasks')
|
||||
|
||||
// ❌ DON'T: Use inconsistent naming
|
||||
.option('-f, --file <path>', 'Path to the tasks file')
|
||||
.option('-p, --path <dir>', 'Output directory') // Should be --output
|
||||
```
|
||||
|
||||
## Input Validation
|
||||
|
||||
- **Required Parameters**:
|
||||
- ✅ DO: Check that required parameters are provided
|
||||
- ✅ DO: Provide clear error messages when parameters are missing
|
||||
- ✅ DO: Use early returns with process.exit(1) for validation failures
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Validate required parameters early
|
||||
if (!prompt) {
|
||||
console.error(chalk.red('Error: --prompt parameter is required. Please provide a task description.'));
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
- **Parameter Type Conversion**:
|
||||
- ✅ DO: Convert string inputs to appropriate types (numbers, booleans)
|
||||
- ✅ DO: Handle conversion errors gracefully
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Parse numeric parameters properly
|
||||
const fromId = parseInt(options.from, 10);
|
||||
if (isNaN(fromId)) {
|
||||
console.error(chalk.red('Error: --from must be a valid number'));
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## User Feedback
|
||||
|
||||
- **Operation Status**:
|
||||
- ✅ DO: Provide clear feedback about the operation being performed
|
||||
- ✅ DO: Display success or error messages after completion
|
||||
- ✅ DO: Use colored output to distinguish between different message types
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Show operation status
|
||||
console.log(chalk.blue(`Parsing PRD file: ${file}`));
|
||||
console.log(chalk.blue(`Generating ${numTasks} tasks...`));
|
||||
|
||||
try {
|
||||
await parsePRD(file, outputPath, numTasks);
|
||||
console.log(chalk.green('Successfully generated tasks from PRD'));
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Error: ${error.message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## Command Registration
|
||||
|
||||
- **Command Grouping**:
|
||||
- ✅ DO: Group related commands together in the code
|
||||
- ✅ DO: Add related commands in a logical order
|
||||
- ✅ DO: Use comments to delineate command groups
|
||||
|
||||
- **Command Export**:
|
||||
- ✅ DO: Export the registerCommands function
|
||||
- ✅ DO: Keep the CLI setup code clean and maintainable
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Follow this export pattern
|
||||
export {
|
||||
registerCommands,
|
||||
setupCLI,
|
||||
runCLI
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Exception Management**:
|
||||
- ✅ DO: Wrap async operations in try/catch blocks
|
||||
- ✅ DO: Display user-friendly error messages
|
||||
- ✅ DO: Include detailed error information in debug mode
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Handle errors properly
|
||||
try {
|
||||
// Command implementation
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Error: ${error.message}`));
|
||||
|
||||
if (CONFIG.debug) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with Other Modules
|
||||
|
||||
- **Import Organization**:
|
||||
- ✅ DO: Group imports by module/functionality
|
||||
- ✅ DO: Import only what's needed, not entire modules
|
||||
- ❌ DON'T: Create circular dependencies
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Organize imports by module
|
||||
import { program } from 'commander';
|
||||
import path from 'path';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { CONFIG, log, readJSON } from './utils.js';
|
||||
import { displayBanner, displayHelp } from './ui.js';
|
||||
import { parsePRD, listTasks } from './task-manager.js';
|
||||
import { addDependency } from './dependency-manager.js';
|
||||
```
|
||||
|
||||
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.
|
||||
224
.cursor/rules/dependencies.mdc
Normal file
224
.cursor/rules/dependencies.mdc
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
description: Guidelines for managing task dependencies and relationships
|
||||
globs: scripts/modules/dependency-manager.js
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Dependency Management Guidelines
|
||||
|
||||
## Dependency Structure Principles
|
||||
|
||||
- **Dependency References**:
|
||||
- ✅ DO: Represent task dependencies as arrays of task IDs
|
||||
- ✅ DO: Use numeric IDs for direct task references
|
||||
- ✅ DO: Use string IDs with dot notation (e.g., "1.2") for subtask references
|
||||
- ❌ DON'T: Mix reference types without proper conversion
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Use consistent dependency formats
|
||||
// For main tasks
|
||||
task.dependencies = [1, 2, 3]; // Dependencies on other main tasks
|
||||
|
||||
// For subtasks
|
||||
subtask.dependencies = [1, "3.2"]; // Dependency on main task 1 and subtask 2 of task 3
|
||||
```
|
||||
|
||||
- **Subtask Dependencies**:
|
||||
- ✅ DO: Allow numeric subtask IDs to reference other subtasks within the same parent
|
||||
- ✅ DO: Convert between formats appropriately when needed
|
||||
- ❌ DON'T: Create circular dependencies between subtasks
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Properly normalize subtask dependencies
|
||||
// When a subtask refers to another subtask in the same parent
|
||||
if (typeof depId === 'number' && depId < 100) {
|
||||
// It's likely a reference to another subtask in the same parent task
|
||||
const fullSubtaskId = `${parentId}.${depId}`;
|
||||
// Now use fullSubtaskId for validation
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Validation
|
||||
|
||||
- **Existence Checking**:
|
||||
- ✅ DO: Validate that referenced tasks exist before adding dependencies
|
||||
- ✅ DO: Provide clear error messages for non-existent dependencies
|
||||
- ✅ DO: Remove references to non-existent tasks during validation
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Check if the dependency exists before adding
|
||||
if (!taskExists(data.tasks, formattedDependencyId)) {
|
||||
log('error', `Dependency target ${formattedDependencyId} does not exist in tasks.json`);
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
- **Circular Dependency Prevention**:
|
||||
- ✅ DO: Check for circular dependencies before adding new relationships
|
||||
- ✅ DO: Use graph traversal algorithms (DFS) to detect cycles
|
||||
- ✅ DO: Provide clear error messages explaining the circular chain
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Check for circular dependencies before adding
|
||||
const dependencyChain = [formattedTaskId];
|
||||
if (isCircularDependency(data.tasks, formattedDependencyId, dependencyChain)) {
|
||||
log('error', `Cannot add dependency ${formattedDependencyId} to task ${formattedTaskId} as it would create a circular dependency.`);
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
- **Self-Dependency Prevention**:
|
||||
- ✅ DO: Prevent tasks from depending on themselves
|
||||
- ✅ DO: Handle both direct and indirect self-dependencies
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Prevent self-dependencies
|
||||
if (String(formattedTaskId) === String(formattedDependencyId)) {
|
||||
log('error', `Task ${formattedTaskId} cannot depend on itself.`);
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Modification
|
||||
|
||||
- **Adding Dependencies**:
|
||||
- ✅ DO: Format task and dependency IDs consistently
|
||||
- ✅ DO: Check for existing dependencies to prevent duplicates
|
||||
- ✅ DO: Sort dependencies for better readability
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Format IDs consistently when adding dependencies
|
||||
const formattedTaskId = typeof taskId === 'string' && taskId.includes('.')
|
||||
? taskId : parseInt(taskId, 10);
|
||||
|
||||
const formattedDependencyId = formatTaskId(dependencyId);
|
||||
```
|
||||
|
||||
- **Removing Dependencies**:
|
||||
- ✅ DO: Check if the dependency exists before removing
|
||||
- ✅ DO: Handle different ID formats consistently
|
||||
- ✅ DO: Provide feedback about the removal result
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Properly handle dependency removal
|
||||
const dependencyIndex = targetTask.dependencies.findIndex(dep => {
|
||||
// Convert both to strings for comparison
|
||||
let depStr = String(dep);
|
||||
|
||||
// Handle relative subtask references
|
||||
if (typeof dep === 'number' && dep < 100 && isSubtask) {
|
||||
const [parentId] = formattedTaskId.split('.');
|
||||
depStr = `${parentId}.${dep}`;
|
||||
}
|
||||
|
||||
return depStr === normalizedDependencyId;
|
||||
});
|
||||
|
||||
if (dependencyIndex === -1) {
|
||||
log('info', `Task ${formattedTaskId} does not depend on ${formattedDependencyId}, no changes made.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the dependency
|
||||
targetTask.dependencies.splice(dependencyIndex, 1);
|
||||
```
|
||||
|
||||
## Dependency Cleanup
|
||||
|
||||
- **Duplicate Removal**:
|
||||
- ✅ DO: Use Set objects to identify and remove duplicates
|
||||
- ✅ DO: Handle both numeric and string ID formats
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Remove duplicate dependencies
|
||||
const uniqueDeps = new Set();
|
||||
const uniqueDependencies = task.dependencies.filter(depId => {
|
||||
// Convert to string for comparison to handle both numeric and string IDs
|
||||
const depIdStr = String(depId);
|
||||
if (uniqueDeps.has(depIdStr)) {
|
||||
log('warn', `Removing duplicate dependency from task ${task.id}: ${depId}`);
|
||||
return false;
|
||||
}
|
||||
uniqueDeps.add(depIdStr);
|
||||
return true;
|
||||
});
|
||||
```
|
||||
|
||||
- **Invalid Reference Cleanup**:
|
||||
- ✅ DO: Check for and remove references to non-existent tasks
|
||||
- ✅ DO: Check for and remove self-references
|
||||
- ✅ DO: Track and report changes made during cleanup
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Filter invalid task dependencies
|
||||
task.dependencies = task.dependencies.filter(depId => {
|
||||
const numericId = typeof depId === 'string' ? parseInt(depId, 10) : depId;
|
||||
if (!validTaskIds.has(numericId)) {
|
||||
log('warn', `Removing invalid task dependency from task ${task.id}: ${depId} (task does not exist)`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
```
|
||||
|
||||
## Dependency Visualization
|
||||
|
||||
- **Status Indicators**:
|
||||
- ✅ DO: Use visual indicators to show dependency status (✅/⏱️)
|
||||
- ✅ DO: Format dependency lists consistently
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Format dependencies with status indicators
|
||||
function formatDependenciesWithStatus(dependencies, allTasks) {
|
||||
if (!dependencies || dependencies.length === 0) {
|
||||
return 'None';
|
||||
}
|
||||
|
||||
return dependencies.map(depId => {
|
||||
const depTask = findTaskById(allTasks, depId);
|
||||
if (!depTask) return `${depId} (Not found)`;
|
||||
|
||||
const isDone = depTask.status === 'done' || depTask.status === 'completed';
|
||||
const statusIcon = isDone ? '✅' : '⏱️';
|
||||
|
||||
return `${statusIcon} ${depId} (${depTask.status})`;
|
||||
}).join(', ');
|
||||
}
|
||||
```
|
||||
|
||||
## Cycle Detection
|
||||
|
||||
- **Graph Traversal**:
|
||||
- ✅ DO: Use depth-first search (DFS) for cycle detection
|
||||
- ✅ DO: Track visited nodes and recursion stack
|
||||
- ✅ DO: Support both task and subtask dependencies
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Use proper cycle detection algorithms
|
||||
function findCycles(subtaskId, dependencyMap, visited = new Set(), recursionStack = new Set()) {
|
||||
// Mark the current node as visited and part of recursion stack
|
||||
visited.add(subtaskId);
|
||||
recursionStack.add(subtaskId);
|
||||
|
||||
const cyclesToBreak = [];
|
||||
const dependencies = dependencyMap.get(subtaskId) || [];
|
||||
|
||||
for (const depId of dependencies) {
|
||||
if (!visited.has(depId)) {
|
||||
const cycles = findCycles(depId, dependencyMap, visited, recursionStack);
|
||||
cyclesToBreak.push(...cycles);
|
||||
}
|
||||
else if (recursionStack.has(depId)) {
|
||||
// Found a cycle, add the edge to break
|
||||
cyclesToBreak.push(depId);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the node from recursion stack before returning
|
||||
recursionStack.delete(subtaskId);
|
||||
|
||||
return cyclesToBreak;
|
||||
}
|
||||
```
|
||||
|
||||
Refer to [`dependency-manager.js`](mdc:scripts/modules/dependency-manager.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines.
|
||||
@@ -308,3 +308,26 @@ alwaysApply: true
|
||||
- Prompts for project settings if not provided
|
||||
- Merges with existing files when appropriate
|
||||
- Can be used to bootstrap a new Task Master project quickly
|
||||
|
||||
- **Code Analysis & Refactoring Techniques**
|
||||
- **Top-Level Function Search**
|
||||
- Use grep pattern matching to find all exported functions across the codebase
|
||||
- Command: `grep -E "export (function|const) \w+|function \w+\(|const \w+ = \(|module\.exports" --include="*.js" -r ./`
|
||||
- Benefits:
|
||||
- Quickly identify all public API functions without reading implementation details
|
||||
- Compare functions between files during refactoring (e.g., monolithic to modular structure)
|
||||
- Verify all expected functions exist in refactored modules
|
||||
- Identify duplicate functionality or naming conflicts
|
||||
- Usage examples:
|
||||
- When migrating from `scripts/dev.js` to modular structure: `grep -E "function \w+\(" scripts/dev.js`
|
||||
- Check function exports in a directory: `grep -E "export (function|const)" scripts/modules/`
|
||||
- Find potential naming conflicts: `grep -E "function (get|set|create|update)\w+\(" -r ./`
|
||||
- Variations:
|
||||
- Add `-n` flag to include line numbers
|
||||
- Add `--include="*.ts"` to filter by file extension
|
||||
- Use with `| sort` to alphabetize results
|
||||
- Integration with refactoring workflow:
|
||||
- Start by mapping all functions in the source file
|
||||
- Create target module files based on function grouping
|
||||
- Verify all functions were properly migrated
|
||||
- Check for any unintentional duplications or omissions
|
||||
|
||||
211
.cursor/rules/new_features.mdc
Normal file
211
.cursor/rules/new_features.mdc
Normal file
@@ -0,0 +1,211 @@
|
||||
---
|
||||
description: Guidelines for integrating new features into the Task Master CLI
|
||||
globs: scripts/modules/*.js
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Task Master Feature Integration Guidelines
|
||||
|
||||
## Feature Placement Decision Process
|
||||
|
||||
- **Identify Feature Type**:
|
||||
- **Data Manipulation**: Features that create, read, update, or delete tasks belong in [`task-manager.js`](mdc:scripts/modules/task-manager.js)
|
||||
- **Dependency Management**: Features that handle task relationships belong in [`dependency-manager.js`](mdc:scripts/modules/dependency-manager.js)
|
||||
- **User Interface**: Features that display information to users belong in [`ui.js`](mdc:scripts/modules/ui.js)
|
||||
- **AI Integration**: Features that use AI models belong in [`ai-services.js`](mdc:scripts/modules/ai-services.js)
|
||||
- **Cross-Cutting**: Features that don't fit one category may need components in multiple modules
|
||||
|
||||
- **Command-Line Interface**:
|
||||
- All new user-facing commands should be added to [`commands.js`](mdc:scripts/modules/commands.js)
|
||||
- Use consistent patterns for option naming and help text
|
||||
- Follow the Commander.js model for subcommand structure
|
||||
|
||||
## Implementation Pattern
|
||||
|
||||
The standard pattern for adding a feature follows this workflow:
|
||||
|
||||
1. **Core Logic**: Implement the business logic in the appropriate module
|
||||
2. **UI Components**: Add any display functions to [`ui.js`](mdc:scripts/modules/ui.js)
|
||||
3. **Command Integration**: Add the CLI command to [`commands.js`](mdc:scripts/modules/commands.js)
|
||||
4. **Configuration**: Update any configuration in [`utils.js`](mdc:scripts/modules/utils.js) if needed
|
||||
5. **Documentation**: Update help text and documentation in [dev_workflow.mdc](mdc:scripts/modules/dev_workflow.mdc)
|
||||
|
||||
```javascript
|
||||
// 1. CORE LOGIC: Add function to appropriate module (example in task-manager.js)
|
||||
/**
|
||||
* Archives completed tasks to archive.json
|
||||
* @param {string} tasksPath - Path to the tasks.json file
|
||||
* @param {string} archivePath - Path to the archive.json file
|
||||
* @returns {number} Number of tasks archived
|
||||
*/
|
||||
async function archiveTasks(tasksPath, archivePath = 'tasks/archive.json') {
|
||||
// Implementation...
|
||||
return archivedCount;
|
||||
}
|
||||
|
||||
// Export from the module
|
||||
export {
|
||||
// ... existing exports ...
|
||||
archiveTasks,
|
||||
};
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 2. UI COMPONENTS: Add display function to ui.js
|
||||
/**
|
||||
* Display archive operation results
|
||||
* @param {string} archivePath - Path to the archive file
|
||||
* @param {number} count - Number of tasks archived
|
||||
*/
|
||||
function displayArchiveResults(archivePath, count) {
|
||||
console.log(boxen(
|
||||
chalk.green(`Successfully archived ${count} tasks to ${archivePath}`),
|
||||
{ padding: 1, borderColor: 'green', borderStyle: 'round' }
|
||||
));
|
||||
}
|
||||
|
||||
// Export from the module
|
||||
export {
|
||||
// ... existing exports ...
|
||||
displayArchiveResults,
|
||||
};
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 3. COMMAND INTEGRATION: Add to commands.js
|
||||
import { archiveTasks } from './task-manager.js';
|
||||
import { displayArchiveResults } from './ui.js';
|
||||
|
||||
// In registerCommands function
|
||||
programInstance
|
||||
.command('archive')
|
||||
.description('Archive completed tasks to separate file')
|
||||
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
||||
.option('-o, --output <file>', 'Archive output file', 'tasks/archive.json')
|
||||
.action(async (options) => {
|
||||
const tasksPath = options.file;
|
||||
const archivePath = options.output;
|
||||
|
||||
console.log(chalk.blue(`Archiving completed tasks from ${tasksPath} to ${archivePath}...`));
|
||||
|
||||
const archivedCount = await archiveTasks(tasksPath, archivePath);
|
||||
displayArchiveResults(archivePath, archivedCount);
|
||||
});
|
||||
```
|
||||
|
||||
## Cross-Module Features
|
||||
|
||||
For features requiring components in multiple modules:
|
||||
|
||||
- ✅ **DO**: Create a clear unidirectional flow of dependencies
|
||||
```javascript
|
||||
// In task-manager.js
|
||||
function analyzeTasksDifficulty(tasks) {
|
||||
// Implementation...
|
||||
return difficultyScores;
|
||||
}
|
||||
|
||||
// In ui.js - depends on task-manager.js
|
||||
import { analyzeTasksDifficulty } from './task-manager.js';
|
||||
|
||||
function displayDifficultyReport(tasks) {
|
||||
const scores = analyzeTasksDifficulty(tasks);
|
||||
// Render the scores...
|
||||
}
|
||||
```
|
||||
|
||||
- ❌ **DON'T**: Create circular dependencies between modules
|
||||
```javascript
|
||||
// In task-manager.js - depends on ui.js
|
||||
import { displayDifficultyReport } from './ui.js';
|
||||
|
||||
function analyzeTasks() {
|
||||
// Implementation...
|
||||
displayDifficultyReport(tasks); // WRONG! Don't call UI functions from task-manager
|
||||
}
|
||||
|
||||
// In ui.js - depends on task-manager.js
|
||||
import { analyzeTasks } from './task-manager.js';
|
||||
```
|
||||
|
||||
## Command-Line Interface Standards
|
||||
|
||||
- **Naming Conventions**:
|
||||
- Use kebab-case for command names (`analyze-complexity`, not `analyzeComplexity`)
|
||||
- Use camelCase for option names (`--outputFormat`, not `--output-format`)
|
||||
- Use the same option names across commands when they represent the same concept
|
||||
|
||||
- **Command Structure**:
|
||||
```javascript
|
||||
programInstance
|
||||
.command('command-name')
|
||||
.description('Clear, concise description of what the command does')
|
||||
.option('-s, --short-option <value>', 'Option description', 'default value')
|
||||
.option('--long-option <value>', 'Option description')
|
||||
.action(async (options) => {
|
||||
// Command implementation
|
||||
});
|
||||
```
|
||||
|
||||
## Utility Function Guidelines
|
||||
|
||||
When adding utilities to [`utils.js`](mdc:scripts/modules/utils.js):
|
||||
|
||||
- Only add functions that could be used by multiple modules
|
||||
- Keep utilities single-purpose and purely functional
|
||||
- Document parameters and return values
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Formats a duration in milliseconds to a human-readable string
|
||||
* @param {number} ms - Duration in milliseconds
|
||||
* @returns {string} Formatted duration string (e.g., "2h 30m 15s")
|
||||
*/
|
||||
function formatDuration(ms) {
|
||||
// Implementation...
|
||||
return formatted;
|
||||
}
|
||||
```
|
||||
|
||||
## Testing New Features
|
||||
|
||||
Before submitting a new feature:
|
||||
|
||||
1. Verify export/import structure with:
|
||||
```bash
|
||||
grep -A15 "export {" scripts/modules/*.js
|
||||
grep -A15 "import {" scripts/modules/*.js | grep -v "^--$"
|
||||
```
|
||||
|
||||
2. Test the feature with valid input:
|
||||
```bash
|
||||
task-master your-command --option1=value
|
||||
```
|
||||
|
||||
3. Test the feature with edge cases:
|
||||
```bash
|
||||
task-master your-command --option1=""
|
||||
task-master your-command # without required options
|
||||
```
|
||||
|
||||
## Documentation Requirements
|
||||
|
||||
For each new feature:
|
||||
|
||||
1. Add help text to the command definition
|
||||
2. Update [`dev_workflow.mdc`](mdc:scripts/modules/dev_workflow.mdc) with command reference
|
||||
3. Add examples to the appropriate sections in [`MODULE_PLAN.md`](mdc:scripts/modules/MODULE_PLAN.md)
|
||||
|
||||
Follow the existing command reference format:
|
||||
```markdown
|
||||
- **Command Reference: your-command**
|
||||
- CLI Syntax: `task-master your-command [options]`
|
||||
- Description: Brief explanation of what the command does
|
||||
- Parameters:
|
||||
- `--option1=<value>`: Description of option1 (default: 'default')
|
||||
- `--option2=<value>`: Description of option2 (required)
|
||||
- Example: `task-master your-command --option1=value --option2=value2`
|
||||
- Notes: Additional details, limitations, or special considerations
|
||||
```
|
||||
|
||||
For more information on module structure, see [`MODULE_PLAN.md`](mdc:scripts/modules/MODULE_PLAN.md) and follow [`self_improve.mdc`](mdc:scripts/modules/self_improve.mdc) for best practices on updating documentation.
|
||||
331
.cursor/rules/tasks.mdc
Normal file
331
.cursor/rules/tasks.mdc
Normal file
@@ -0,0 +1,331 @@
|
||||
---
|
||||
description: Guidelines for implementing task management operations
|
||||
globs: scripts/modules/task-manager.js
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Task Management Guidelines
|
||||
|
||||
## Task Structure Standards
|
||||
|
||||
- **Core Task Properties**:
|
||||
- ✅ DO: Include all required properties in each task object
|
||||
- ✅ DO: Provide default values for optional properties
|
||||
- ❌ DON'T: Add extra properties that aren't in the standard schema
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Follow this structure for task objects
|
||||
const task = {
|
||||
id: nextId,
|
||||
title: "Task title",
|
||||
description: "Brief task description",
|
||||
status: "pending", // "pending", "in-progress", "done", etc.
|
||||
dependencies: [], // Array of task IDs
|
||||
priority: "medium", // "high", "medium", "low"
|
||||
details: "Detailed implementation instructions",
|
||||
testStrategy: "Verification approach",
|
||||
subtasks: [] // Array of subtask objects
|
||||
};
|
||||
```
|
||||
|
||||
- **Subtask Structure**:
|
||||
- ✅ DO: Use consistent properties across subtasks
|
||||
- ✅ DO: Maintain simple numeric IDs within parent tasks
|
||||
- ❌ DON'T: Duplicate parent task properties in subtasks
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Structure subtasks consistently
|
||||
const subtask = {
|
||||
id: nextSubtaskId, // Simple numeric ID, unique within the parent task
|
||||
title: "Subtask title",
|
||||
description: "Brief subtask description",
|
||||
status: "pending",
|
||||
dependencies: [], // Can include numeric IDs (other subtasks) or full task IDs
|
||||
details: "Detailed implementation instructions"
|
||||
};
|
||||
```
|
||||
|
||||
## Task Creation and Parsing
|
||||
|
||||
- **ID Management**:
|
||||
- ✅ DO: Assign unique sequential IDs to tasks
|
||||
- ✅ DO: Calculate the next ID based on existing tasks
|
||||
- ❌ DON'T: Hardcode or reuse IDs
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Calculate the next available ID
|
||||
const highestId = Math.max(...data.tasks.map(t => t.id));
|
||||
const nextTaskId = highestId + 1;
|
||||
```
|
||||
|
||||
- **PRD Parsing**:
|
||||
- ✅ DO: Extract tasks from PRD documents using AI
|
||||
- ✅ DO: Provide clear prompts to guide AI task generation
|
||||
- ✅ DO: Validate and clean up AI-generated tasks
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Validate AI responses
|
||||
try {
|
||||
// Parse the JSON response
|
||||
taskData = JSON.parse(jsonContent);
|
||||
|
||||
// Check that we have the required fields
|
||||
if (!taskData.title || !taskData.description) {
|
||||
throw new Error("Missing required fields in the generated task");
|
||||
}
|
||||
} catch (error) {
|
||||
log('error', "Failed to parse AI's response as valid task JSON:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## Task Updates and Modifications
|
||||
|
||||
- **Status Management**:
|
||||
- ✅ DO: Provide functions for updating task status
|
||||
- ✅ DO: Handle both individual tasks and subtasks
|
||||
- ✅ DO: Consider subtask status when updating parent tasks
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Handle status updates for both tasks and subtasks
|
||||
async function setTaskStatus(tasksPath, taskIdInput, newStatus) {
|
||||
// Check if it's a subtask (e.g., "1.2")
|
||||
if (taskIdInput.includes('.')) {
|
||||
const [parentId, subtaskId] = taskIdInput.split('.').map(id => parseInt(id, 10));
|
||||
|
||||
// Find the parent task and subtask
|
||||
const parentTask = data.tasks.find(t => t.id === parentId);
|
||||
const subtask = parentTask.subtasks.find(st => st.id === subtaskId);
|
||||
|
||||
// Update subtask status
|
||||
subtask.status = newStatus;
|
||||
|
||||
// Check if all subtasks are done
|
||||
if (newStatus === 'done') {
|
||||
const allSubtasksDone = parentTask.subtasks.every(st => st.status === 'done');
|
||||
if (allSubtasksDone) {
|
||||
// Suggest updating parent task
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle regular task
|
||||
const task = data.tasks.find(t => t.id === parseInt(taskIdInput, 10));
|
||||
task.status = newStatus;
|
||||
|
||||
// If marking as done, also mark subtasks
|
||||
if (newStatus === 'done' && task.subtasks && task.subtasks.length > 0) {
|
||||
task.subtasks.forEach(subtask => {
|
||||
subtask.status = newStatus;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Task Expansion**:
|
||||
- ✅ DO: Use AI to generate detailed subtasks
|
||||
- ✅ DO: Consider complexity analysis for subtask counts
|
||||
- ✅ DO: Ensure proper IDs for newly created subtasks
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Generate appropriate subtasks based on complexity
|
||||
if (taskAnalysis) {
|
||||
log('info', `Found complexity analysis for task ${taskId}: Score ${taskAnalysis.complexityScore}/10`);
|
||||
|
||||
// Use recommended number of subtasks if available
|
||||
if (taskAnalysis.recommendedSubtasks && numSubtasks === CONFIG.defaultSubtasks) {
|
||||
numSubtasks = taskAnalysis.recommendedSubtasks;
|
||||
log('info', `Using recommended number of subtasks: ${numSubtasks}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Task File Generation
|
||||
|
||||
- **File Formatting**:
|
||||
- ✅ DO: Use consistent formatting for task files
|
||||
- ✅ DO: Include all task properties in text files
|
||||
- ✅ DO: Format dependencies with status indicators
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Use consistent file formatting
|
||||
let content = `# Task ID: ${task.id}\n`;
|
||||
content += `# Title: ${task.title}\n`;
|
||||
content += `# Status: ${task.status || 'pending'}\n`;
|
||||
|
||||
// Format dependencies with their status
|
||||
if (task.dependencies && task.dependencies.length > 0) {
|
||||
content += `# Dependencies: ${formatDependenciesWithStatus(task.dependencies, data.tasks)}\n`;
|
||||
} else {
|
||||
content += '# Dependencies: None\n';
|
||||
}
|
||||
```
|
||||
|
||||
- **Subtask Inclusion**:
|
||||
- ✅ DO: Include subtasks in parent task files
|
||||
- ✅ DO: Use consistent indentation for subtask sections
|
||||
- ✅ DO: Display subtask dependencies with proper formatting
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Format subtasks correctly in task files
|
||||
if (task.subtasks && task.subtasks.length > 0) {
|
||||
content += '\n# Subtasks:\n';
|
||||
|
||||
task.subtasks.forEach(subtask => {
|
||||
content += `## ${subtask.id}. ${subtask.title} [${subtask.status || 'pending'}]\n`;
|
||||
|
||||
// Format subtask dependencies
|
||||
if (subtask.dependencies && subtask.dependencies.length > 0) {
|
||||
// Format the dependencies
|
||||
content += `### Dependencies: ${formattedDeps}\n`;
|
||||
} else {
|
||||
content += '### Dependencies: None\n';
|
||||
}
|
||||
|
||||
content += `### Description: ${subtask.description || ''}\n`;
|
||||
content += '### Details:\n';
|
||||
content += (subtask.details || '').split('\n').map(line => line).join('\n');
|
||||
content += '\n\n';
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Task Listing and Display
|
||||
|
||||
- **Filtering and Organization**:
|
||||
- ✅ DO: Allow filtering tasks by status
|
||||
- ✅ DO: Handle subtask display in lists
|
||||
- ✅ DO: Use consistent table formats
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Implement clear filtering and organization
|
||||
// Filter tasks by status if specified
|
||||
const filteredTasks = statusFilter
|
||||
? data.tasks.filter(task =>
|
||||
task.status && task.status.toLowerCase() === statusFilter.toLowerCase())
|
||||
: data.tasks;
|
||||
```
|
||||
|
||||
- **Progress Tracking**:
|
||||
- ✅ DO: Calculate and display completion statistics
|
||||
- ✅ DO: Track both task and subtask completion
|
||||
- ✅ DO: Use visual progress indicators
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Track and display progress
|
||||
// Calculate completion statistics
|
||||
const totalTasks = data.tasks.length;
|
||||
const completedTasks = data.tasks.filter(task =>
|
||||
task.status === 'done' || task.status === 'completed').length;
|
||||
const completionPercentage = totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0;
|
||||
|
||||
// Count subtasks
|
||||
let totalSubtasks = 0;
|
||||
let completedSubtasks = 0;
|
||||
|
||||
data.tasks.forEach(task => {
|
||||
if (task.subtasks && task.subtasks.length > 0) {
|
||||
totalSubtasks += task.subtasks.length;
|
||||
completedSubtasks += task.subtasks.filter(st =>
|
||||
st.status === 'done' || st.status === 'completed').length;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Complexity Analysis
|
||||
|
||||
- **Scoring System**:
|
||||
- ✅ DO: Use AI to analyze task complexity
|
||||
- ✅ DO: Include complexity scores (1-10)
|
||||
- ✅ DO: Generate specific expansion recommendations
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Handle complexity analysis properly
|
||||
const report = {
|
||||
meta: {
|
||||
generatedAt: new Date().toISOString(),
|
||||
tasksAnalyzed: tasksData.tasks.length,
|
||||
thresholdScore: thresholdScore,
|
||||
projectName: tasksData.meta?.projectName || 'Your Project Name',
|
||||
usedResearch: useResearch
|
||||
},
|
||||
complexityAnalysis: complexityAnalysis
|
||||
};
|
||||
```
|
||||
|
||||
- **Analysis-Based Workflow**:
|
||||
- ✅ DO: Use complexity reports to guide task expansion
|
||||
- ✅ DO: Prioritize complex tasks for more detailed breakdown
|
||||
- ✅ DO: Use expansion prompts from complexity analysis
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Apply complexity analysis to workflow
|
||||
// Sort tasks by complexity if report exists, otherwise by ID
|
||||
if (complexityReport && complexityReport.complexityAnalysis) {
|
||||
log('info', 'Sorting tasks by complexity...');
|
||||
|
||||
// Create a map of task IDs to complexity scores
|
||||
const complexityMap = new Map();
|
||||
complexityReport.complexityAnalysis.forEach(analysis => {
|
||||
complexityMap.set(analysis.taskId, analysis.complexityScore);
|
||||
});
|
||||
|
||||
// Sort tasks by complexity score (high to low)
|
||||
tasksToExpand.sort((a, b) => {
|
||||
const scoreA = complexityMap.get(a.id) || 0;
|
||||
const scoreB = complexityMap.get(b.id) || 0;
|
||||
return scoreB - scoreA;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Next Task Selection
|
||||
|
||||
- **Eligibility Criteria**:
|
||||
- ✅ DO: Consider dependencies when finding next tasks
|
||||
- ✅ DO: Prioritize by task priority and dependency count
|
||||
- ✅ DO: Skip completed tasks
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Use proper task prioritization logic
|
||||
function findNextTask(tasks) {
|
||||
// Get all completed task IDs
|
||||
const completedTaskIds = new Set(
|
||||
tasks
|
||||
.filter(t => t.status === 'done' || t.status === 'completed')
|
||||
.map(t => t.id)
|
||||
);
|
||||
|
||||
// Filter for pending tasks whose dependencies are all satisfied
|
||||
const eligibleTasks = tasks.filter(task =>
|
||||
(task.status === 'pending' || task.status === 'in-progress') &&
|
||||
task.dependencies &&
|
||||
task.dependencies.every(depId => completedTaskIds.has(depId))
|
||||
);
|
||||
|
||||
// Sort by priority, dependency count, and ID
|
||||
const priorityValues = { 'high': 3, 'medium': 2, 'low': 1 };
|
||||
|
||||
const nextTask = eligibleTasks.sort((a, b) => {
|
||||
// Priority first
|
||||
const priorityA = priorityValues[a.priority || 'medium'] || 2;
|
||||
const priorityB = priorityValues[b.priority || 'medium'] || 2;
|
||||
|
||||
if (priorityB !== priorityA) {
|
||||
return priorityB - priorityA; // Higher priority first
|
||||
}
|
||||
|
||||
// Dependency count next
|
||||
if (a.dependencies.length !== b.dependencies.length) {
|
||||
return a.dependencies.length - b.dependencies.length; // Fewer dependencies first
|
||||
}
|
||||
|
||||
// ID last
|
||||
return a.id - b.id; // Lower ID first
|
||||
})[0];
|
||||
|
||||
return nextTask;
|
||||
}
|
||||
```
|
||||
|
||||
Refer to [`task-manager.js`](mdc:scripts/modules/task-manager.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines.
|
||||
153
.cursor/rules/ui.mdc
Normal file
153
.cursor/rules/ui.mdc
Normal file
@@ -0,0 +1,153 @@
|
||||
---
|
||||
description: Guidelines for implementing and maintaining user interface components
|
||||
globs: scripts/modules/ui.js
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# User Interface Implementation Guidelines
|
||||
|
||||
## Core UI Component Principles
|
||||
|
||||
- **Function Scope Separation**:
|
||||
- ✅ DO: Keep display logic separate from business logic
|
||||
- ✅ DO: Import data processing functions from other modules
|
||||
- ❌ DON'T: Include task manipulations within UI functions
|
||||
- ❌ DON'T: Create circular dependencies with other modules
|
||||
|
||||
- **Standard Display Pattern**:
|
||||
```javascript
|
||||
// ✅ DO: Follow this pattern for display functions
|
||||
/**
|
||||
* Display information about a task
|
||||
* @param {Object} task - The task to display
|
||||
*/
|
||||
function displayTaskInfo(task) {
|
||||
console.log(boxen(
|
||||
chalk.white.bold(`Task: #${task.id} - ${task.title}`),
|
||||
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
|
||||
));
|
||||
}
|
||||
```
|
||||
|
||||
## Visual Styling Standards
|
||||
|
||||
- **Color Scheme**:
|
||||
- Use `chalk.blue` for informational messages
|
||||
- Use `chalk.green` for success messages
|
||||
- Use `chalk.yellow` for warnings
|
||||
- Use `chalk.red` for errors
|
||||
- Use `chalk.cyan` for prompts and highlights
|
||||
- Use `chalk.magenta` for subtask-related information
|
||||
|
||||
- **Box Styling**:
|
||||
```javascript
|
||||
// ✅ DO: Use consistent box styles by content type
|
||||
// For success messages:
|
||||
boxen(content, {
|
||||
padding: 1,
|
||||
borderColor: 'green',
|
||||
borderStyle: 'round',
|
||||
margin: { top: 1 }
|
||||
})
|
||||
|
||||
// For errors:
|
||||
boxen(content, {
|
||||
padding: 1,
|
||||
borderColor: 'red',
|
||||
borderStyle: 'round'
|
||||
})
|
||||
|
||||
// For information:
|
||||
boxen(content, {
|
||||
padding: 1,
|
||||
borderColor: 'blue',
|
||||
borderStyle: 'round',
|
||||
margin: { top: 1, bottom: 1 }
|
||||
})
|
||||
```
|
||||
|
||||
## Table Display Guidelines
|
||||
|
||||
- **Table Structure**:
|
||||
- Use [`cli-table3`](mdc:node_modules/cli-table3/README.md) for consistent table rendering
|
||||
- Include colored headers with bold formatting
|
||||
- Use appropriate column widths for readability
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Create well-structured tables
|
||||
const table = new Table({
|
||||
head: [
|
||||
chalk.cyan.bold('ID'),
|
||||
chalk.cyan.bold('Title'),
|
||||
chalk.cyan.bold('Status'),
|
||||
chalk.cyan.bold('Priority'),
|
||||
chalk.cyan.bold('Dependencies')
|
||||
],
|
||||
colWidths: [5, 40, 15, 10, 20]
|
||||
});
|
||||
|
||||
// Add content rows
|
||||
table.push([
|
||||
task.id,
|
||||
truncate(task.title, 37),
|
||||
getStatusWithColor(task.status),
|
||||
chalk.white(task.priority || 'medium'),
|
||||
formatDependenciesWithStatus(task.dependencies, allTasks, true)
|
||||
]);
|
||||
|
||||
console.log(table.toString());
|
||||
```
|
||||
|
||||
## Loading Indicators
|
||||
|
||||
- **Animation Standards**:
|
||||
- Use [`ora`](mdc:node_modules/ora/readme.md) for spinner animations
|
||||
- Create and stop loading indicators correctly
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Properly manage loading state
|
||||
const loadingIndicator = startLoadingIndicator('Processing task data...');
|
||||
try {
|
||||
// Do async work...
|
||||
stopLoadingIndicator(loadingIndicator);
|
||||
// Show success message
|
||||
} catch (error) {
|
||||
stopLoadingIndicator(loadingIndicator);
|
||||
// Show error message
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
- **Status Formatting**:
|
||||
- Use `getStatusWithColor` for consistent status display
|
||||
- Use `formatDependenciesWithStatus` for dependency lists
|
||||
- Use `truncate` to handle text that may overflow display
|
||||
|
||||
- **Progress Reporting**:
|
||||
- Use visual indicators for progress (bars, percentages)
|
||||
- Include both numeric and visual representations
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Show clear progress indicators
|
||||
console.log(`${chalk.cyan('Tasks:')} ${completedTasks}/${totalTasks} (${completionPercentage.toFixed(1)}%)`);
|
||||
console.log(`${chalk.cyan('Progress:')} ${createProgressBar(completionPercentage)}`);
|
||||
```
|
||||
|
||||
## Command Suggestions
|
||||
|
||||
- **Action Recommendations**:
|
||||
- Provide next step suggestions after command completion
|
||||
- Use a consistent format for suggested commands
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Show suggested next actions
|
||||
console.log(boxen(
|
||||
chalk.white.bold('Next Steps:') + '\n\n' +
|
||||
`${chalk.cyan('1.')} Run ${chalk.yellow('task-master list')} to view all tasks\n` +
|
||||
`${chalk.cyan('2.')} Run ${chalk.yellow('task-master show --id=' + newTaskId)} to view details`,
|
||||
{ padding: 1, borderColor: 'cyan', borderStyle: 'round', margin: { top: 1 } }
|
||||
));
|
||||
```
|
||||
|
||||
Refer to [`ui.js`](mdc:scripts/modules/ui.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines.
|
||||
314
.cursor/rules/utilities.mdc
Normal file
314
.cursor/rules/utilities.mdc
Normal file
@@ -0,0 +1,314 @@
|
||||
---
|
||||
description: Guidelines for implementing utility functions
|
||||
globs: scripts/modules/utils.js
|
||||
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) + '...';
|
||||
}
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
- **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-7-sonnet-20250219',
|
||||
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",
|
||||
projectVersion: "1.5.0" // Version should be hardcoded
|
||||
};
|
||||
```
|
||||
|
||||
## Logging Utilities
|
||||
|
||||
- **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
|
||||
|
||||
- **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
|
||||
- ❌ DON'T: Allow exceptions to propagate unhandled
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Handle file operation errors properly
|
||||
function writeJSON(filepath, data) {
|
||||
try {
|
||||
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
|
||||
|
||||
- **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
|
||||
|
||||
- **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;
|
||||
}
|
||||
```
|
||||
|
||||
## Export Organization
|
||||
|
||||
- **Grouping Related Functions**:
|
||||
- ✅ DO: Export all utility functions in a single statement
|
||||
- ✅ DO: Group related exports together
|
||||
- ✅ DO: Export configuration constants
|
||||
- ❌ DON'T: Use default exports
|
||||
|
||||
```javascript
|
||||
// ✅ DO: Organize exports logically
|
||||
export {
|
||||
// Configuration
|
||||
CONFIG,
|
||||
LOG_LEVELS,
|
||||
|
||||
// Logging
|
||||
log,
|
||||
|
||||
// File operations
|
||||
readJSON,
|
||||
writeJSON,
|
||||
|
||||
// String manipulation
|
||||
sanitizePrompt,
|
||||
truncate,
|
||||
|
||||
// Task utilities
|
||||
readComplexityReport,
|
||||
findTaskInComplexityReport,
|
||||
taskExists,
|
||||
formatTaskId,
|
||||
findTaskById,
|
||||
|
||||
// Graph algorithms
|
||||
findCycles,
|
||||
};
|
||||
```
|
||||
|
||||
Refer to [`utils.js`](mdc:scripts/modules/utils.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines.
|
||||
Reference in New Issue
Block a user