Instruction file imported from Dampipal/cliai (
.cursor/rules/new_features.mdc). Copyright stays with the author.
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 - Dependency Management: Features that handle task relationships belong in
dependency-manager.js - User Interface: Features that display information to users belong in
ui.js - AI Integration: Features that use AI models belong in
ai-services.js - Cross-Cutting: Features that don't fit one category may need components in multiple modules
- Data Manipulation: Features that create, read, update, or delete tasks belong in
-
Command-Line Interface:
- All new user-facing commands should be added to
commands.js - Use consistent patterns for option naming and help text
- Follow the Commander.js model for subcommand structure
- All new user-facing commands should be added to
Implementation Pattern
The standard pattern for adding a feature follows this workflow:
- Core Logic: Implement the business logic in the appropriate module
- UI Components: Add any display functions to
ui.js - Command Integration: Add the CLI command to
commands.js - Testing: Write tests for all components of the feature (following
tests.mdc) - Configuration: Update any configuration in
utils.jsif needed - Documentation: Update help text and documentation in dev_workflow.mdc
// 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,
};
// 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,
};
// 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
// 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
// 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, notanalyzeComplexity) - Use kebab-case for option names (
--output-format, not--outputFormat) - Use the same option names across commands when they represent the same concept
- Use kebab-case for command names (
-
Command Structure:
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:
- Only add functions that could be used by multiple modules
- Keep utilities single-purpose and purely functional
- Document parameters and return values
/**
* 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;
}
Writing Testable Code
When implementing new features, follow these guidelines to ensure your code is testable:
-
Dependency Injection
- Design functions to accept dependencies as parameters
- Avoid hard-coded dependencies that are difficult to mock
// ✅ DO: Accept dependencies as parameters function processTask(task, fileSystem, logger) { fileSystem.writeFile('task.json', JSON.stringify(task)); logger.info('Task processed'); } // ❌ DON'T: Use hard-coded dependencies function processTask(task) { fs.writeFile('task.json', JSON.stringify(task)); console.log('Task processed'); } -
Separate Logic from Side Effects
- Keep pure logic separate from I/O operations or UI rendering
- This allows testing the logic without mocking complex dependencies
// ✅ DO: Separate logic from side effects function calculateTaskPriority(task, dependencies) { // Pure logic that returns a value return computedPriority; } function displayTaskPriority(task, dependencies) { const priority = calculateTaskPriority(task, dependencies); console.log(`Task priority: ${priority}`); } -
Callback Functions and Testing
- When using callbacks (like in Commander.js commands), define them separately
- This allows testing the callback logic independently
// ✅ DO: Define callbacks separately for testing function getVersionString() { // Logic to determine version return version; } // In setupCLI programInstance.version(getVersionString); // In tests test('getVersionString returns correct version', () => { expect(getVersionString()).toBe('1.5.0'); }); -
UI Output Testing
- For UI components, focus on testing conditional logic rather than exact output
- Use string pattern matching (like
expect(result).toContain('text')) - Pay attention to emojis and formatting which can make exact string matching difficult
// ✅ DO: Test the essence of the output, not exact formatting test('statusFormatter shows done status correctly', () => { const result = formatStatus('done'); expect(result).toContain('done'); expect(result).toContain('✅'); });
Testing Requirements
Every new feature must include comprehensive tests following the guidelines in tests.mdc. Testing should include:
-
Unit Tests: Test individual functions and components in isolation
// Example unit test for a new utility function describe('newFeatureUtil', () => { test('should perform expected operation with valid input', () => { expect(newFeatureUtil('valid input')).toBe('expected result'); }); test('should handle edge cases appropriately', () => { expect(newFeatureUtil('')).toBeNull(); }); }); -
Integration Tests: Verify the feature works correctly with other components
// Example integration test for a new command describe('newCommand integration', () => { test('should call the correct service functions with parsed arguments', () => { const mockService = jest.fn().mockResolvedValue('success'); // Set up test with mocked dependencies // Call the command handler // Verify service was called with expected arguments }); }); -
Edge Cases: Test boundary conditions and error handling
- Invalid inputs
- Missing dependencies
- File system errors
- API failures
-
Test Coverage: Aim for at least 80% coverage for all new code
-
Jest Mocking Best Practices
When submitting a new feature, always run the full test suite to ensure nothing was broken:
npm test
Documentation Requirements
For each new feature:
- Add help text to the command definition
- Update
dev_workflow.mdcwith command reference - Add examples to the appropriate sections in
MODULE_PLAN.md
Follow the existing command reference format:
- **Command Reference: your-command**
- CLI Syntax: `hackmindai 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: `hackmindai your-command --option1=value --option2=value2`
- Notes: Additional details, limitations, or special considerations
For more information on module structure, see MODULE_PLAN.md and follow self_improve.mdc for best practices on updating documentation.