mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2025-11-10 14:57:34 +00:00
3.8 KiB
3.8 KiB
CLI Commander Class Pattern
Overview
We're using Commander.js's native class pattern instead of custom abstractions. This is cleaner, more maintainable, and uses the framework as designed.
Architecture
@tm/core (Business Logic) @tm/cli (Presentation)
┌─────────────────────┐ ┌──────────────────────────┐
│ TaskMasterCore │◄───────────│ ListTasksCommand │
│ - getTaskList() │ │ extends Commander.Command│
│ - getTask() │ │ - display logic only │
│ - getNextTask() │ │ - formatting │
└─────────────────────┘ └──────────────────────────┘
▲ ▲
│ │
└──────── Gets Data ──────────────────┘
Displays Data
Implementation
Command Class Pattern
// apps/cli/src/commands/list-tasks-commander.ts
export class ListTasksCommand extends Command {
constructor(name?: string) {
super(name || 'list');
this
.description('List tasks')
.option('-s, --status <status>', 'Filter by status')
.action(async (options) => {
// 1. Get data from @tm/core
const result = await this.tmCore.getTaskList(options);
// 2. Display data (presentation only)
this.displayResults(result, options);
});
}
}
Main CLI Class
// apps/cli/src/cli-commander.ts
class TaskMasterCLI extends Command {
createCommand(name?: string): Command {
switch (name) {
case 'list':
return new ListTasksCommand(name);
default:
return new Command(name);
}
}
}
Integration with Existing Scripts
Gradual Migration Path
// scripts/modules/commands.js
// OLD WAY (keep working during migration)
program
.command('old-list')
.action(async (options) => {
await listTasksV2(...);
});
// NEW WAY (add alongside old)
import { ListTasksCommand } from '@tm/cli';
program.addCommand(new ListTasksCommand());
Benefits
- No Custom Abstractions: Using Commander.js as designed
- Clean Separation: Business logic in core, presentation in CLI
- Gradual Migration: Can migrate one command at a time
- Type Safety: Full TypeScript support with Commander types
- Framework Native: Better documentation, examples, and community support
Migration Steps
- Phase 1: Build command classes in @tm/cli (current)
- Phase 2: Import in scripts/modules/commands.js
- Phase 3: Replace old implementations one by one
- Phase 4: Remove old code when all migrated
Example Usage
In New Code
import { ListTasksCommand } from '@tm/cli';
const program = new Command();
program.addCommand(new ListTasksCommand());
In Existing Scripts
// Gradual adoption
const listCmd = new ListTasksCommand();
program.addCommand(listCmd);
Programmatic Usage
const listCommand = new ListTasksCommand();
await listCommand.parseAsync(['node', 'script', '--format', 'json']);
POC Status
✅ Completed:
- ListTasksCommand extends Commander.Command
- Clean separation of concerns
- Integration examples
- Build configuration
🚧 Next Steps:
- Migrate more commands
- Update existing scripts to use new classes
- Remove old implementations gradually
This POC proves the pattern works and provides a clean migration path!