Files
claude-task-master/.cursor/rules/commands.mdc
Eyal Toledano 05095c4745 feat: enhance commands with multi-subtask support, MCP integration, and update notifications
- Add support for comma-separated subtask IDs in remove-subtask command
- Implement MCP configuration in project initialization
- Add package update notification system with version comparison
- Improve command documentation with boolean flag conventions
- Add comprehensive error handling for unknown options
- Update help text with better examples and formatting
- Implement proper validation for command inputs
- Add global error handling patterns with helpful user messages
2025-03-27 16:14:12 -04:00

307 lines
11 KiB
Plaintext

---
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 kebab-case for long-form option names (`--output-format`)
- ✅ 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
```
> **Note**: Although options are defined with kebab-case (`--num-tasks`), Commander.js stores them internally as camelCase properties. Access them in code as `options.numTasks`, not `options['num-tasks']`.
- **Boolean Flag Conventions**:
- ✅ DO: Use positive flags with `--skip-` prefix for disabling behavior
- ❌ DON'T: Use negated boolean flags with `--no-` prefix
- ✅ DO: Use consistent flag handling across all commands
```javascript
// ✅ DO: Use positive flag with skip- prefix
.option('--skip-generate', 'Skip generating task files')
// ❌ DON'T: Use --no- prefix
.option('--no-generate', 'Skip generating task files')
```
> **Important**: When handling boolean flags in the code, make your intent clear:
```javascript
// ✅ DO: Use clear variable naming that matches the flag's intent
const generateFiles = !options.skipGenerate;
// ❌ DON'T: Use confusing double negatives
const dontSkipGenerate = !options.skipGenerate;
```
## 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);
}
```
- **Unknown Options Handling**:
- ✅ DO: Provide clear error messages for unknown options
- ✅ DO: Show available options when an unknown option is used
- ✅ DO: Include command-specific help displays for common errors
- ❌ DON'T: Allow unknown options with `.allowUnknownOption()`
```javascript
// ✅ DO: Register global error handlers for unknown options
programInstance.on('option:unknown', function(unknownOption) {
const commandName = this._name || 'unknown';
console.error(chalk.red(`Error: Unknown option '${unknownOption}'`));
console.error(chalk.yellow(`Run 'task-master ${commandName} --help' to see available options`));
process.exit(1);
});
// ✅ DO: Add command-specific help displays
function showCommandHelp() {
console.log(boxen(
chalk.white.bold('Command Help') + '\n\n' +
chalk.cyan('Usage:') + '\n' +
` task-master command --option1=<value> [options]\n\n` +
chalk.cyan('Options:') + '\n' +
' --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',
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
));
}
```
- **Global Error Handling**:
- ✅ DO: Set up global error handlers for uncaught exceptions
- ✅ DO: Detect and format Commander-specific errors
- ✅ DO: Provide suitable guidance for fixing common errors
```javascript
// ✅ DO: Set up global error handlers with helpful messages
process.on('uncaughtException', (err) => {
// Handle Commander-specific errors
if (err.code === 'commander.unknownOption') {
const option = err.message.match(/'([^']+)'/)?.[1];
console.error(chalk.red(`Error: Unknown option '${option}'`));
console.error(chalk.yellow(`Run 'task-master <command> --help' to see available options`));
process.exit(1);
}
// Handle other error types...
console.error(chalk.red(`Error: ${err.message}`));
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';
```
## Subtask Management Commands
- **Add Subtask Command Structure**:
```javascript
// ✅ DO: Follow this structure for adding subtasks
programInstance
.command('add-subtask')
.description('Add a new subtask to a parent task or convert an existing task to a subtask')
.option('-f, --file <path>', 'Path to the tasks file', 'tasks/tasks.json')
.option('-p, --parent <id>', 'ID of the parent task (required)')
.option('-e, --existing <id>', 'ID of an existing task to convert to a subtask')
.option('-t, --title <title>', 'Title for the new subtask (when not converting)')
.option('-d, --description <description>', 'Description for the new subtask (when not converting)')
.option('--details <details>', 'Implementation details for the new subtask (when not converting)')
.option('--dependencies <ids>', 'Comma-separated list of subtask IDs this subtask depends on')
.option('--status <status>', 'Initial status for the subtask', 'pending')
.action(async (options) => {
// Validate required parameters
if (!options.parent) {
console.error(chalk.red('Error: --parent parameter is required'));
process.exit(1);
}
// Validate that either existing task ID or title is provided
if (!options.existing && !options.title) {
console.error(chalk.red('Error: Either --existing or --title must be provided'));
process.exit(1);
}
try {
// Implementation
} catch (error) {
// Error handling
}
});
```
- **Remove Subtask Command Structure**:
```javascript
// ✅ DO: Follow this structure for removing subtasks
programInstance
.command('remove-subtask')
.description('Remove a subtask from its parent task, optionally converting it to a standalone task')
.option('-f, --file <path>', 'Path to the tasks file', 'tasks/tasks.json')
.option('-i, --id <id>', 'ID of the subtask to remove in format "parentId.subtaskId" (required)')
.option('-c, --convert', 'Convert the subtask to a standalone task')
.action(async (options) => {
// Validate required parameters
if (!options.id) {
console.error(chalk.red('Error: --id parameter is required'));
process.exit(1);
}
// Validate subtask ID format
if (!options.id.includes('.')) {
console.error(chalk.red('Error: Subtask ID must be in format "parentId.subtaskId"'));
process.exit(1);
}
try {
// Implementation
} catch (error) {
// Error handling
}
});
```
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.