41 KiB
Update Task Migration Plan
Overview
Migrate and unify update-tasks.js and update-subtask-by-id.js into a single update-task command that handles both task and subtask updates. This migration will move from the legacy scripts/modules/task-manager/ structure to the new apps/cli and packages/tm-core architecture.
Current State Analysis
update-tasks.js - Bulk Task Updates
Purpose: Update multiple tasks from a specified ID onwards
Input Format: --from=<id> --prompt="context"
AI Service: generateObjectService with structured schema
update-subtask-by-id.js - Single Subtask Updates
Purpose: Append timestamped information to a specific subtask
Input Format: --id=<parentId.subtaskId> --prompt="notes"
AI Service: generateTextService for freeform content
Unified Command Design
New Command: update-task
# Update single task (replaces update-task)
task-master update-task --id=3 --prompt="changes"
# Update single subtask (replaces update-subtask)
task-master update-task --id=3.2 --prompt="implementation notes"
# Update multiple tasks from ID onwards (replaces update --from)
task-master update-task --from=3 --prompt="changes"
Intelligent Behavior Detection
The command should automatically determine behavior based on:
- ID format: Contains
.→ subtask mode - --from flag: Present → bulk update mode
- Default: Single task update mode
Functionality Checklist
Core Functionality
Input Validation & Parsing
- Validate
tasksPathexists - Validate
idparameter (task: integer, subtask: "parent.child" format) - Validate
fromIdparameter (integer, positive) - Validate
promptparameter (non-empty string) - Parse subtask ID format: split "parentId.subtaskId" and validate both parts
- Determine project root (from context or
findProjectRoot()) - Support both MCP and CLI modes (detect via
mcpLogpresence) - Handle
outputFormat('text' or 'json', auto-detect for MCP)
Task Loading & Filtering
- Load tasks from
tasks.jsonusingreadJSON(tasksPath, projectRoot, tag) - Validate tasks data structure exists
- Bulk mode: Filter tasks where
id >= fromId AND status !== 'done' - Single task mode: Find specific task by ID
- Subtask mode: Find parent task, validate subtasks array, find specific subtask
- Handle "no tasks to update" scenario gracefully
Context Gathering
- Initialize
ContextGathererwith projectRoot and tag - Flatten all tasks with subtasks using
flattenTasksWithSubtasks() - Initialize
FuzzyTaskSearchwith appropriate command type:'update'for bulk/single task mode'update-subtask'for subtask mode
- Bulk/Single task: Search with prompt, max 5 results, include self
- Subtask mode: Search with combined query:
${parentTask.title} ${subtask.title} ${prompt} - Merge task IDs to update with relevant context task IDs
- Gather context in 'research' format
- Handle context gathering errors gracefully (log warning, continue)
Prompt Building
- Initialize
PromptManagerviagetPromptManager() - Bulk/Single task mode: Load 'update-tasks' prompt template with params:
tasks(array of tasks to update)updatePromptuseResearchprojectContext(gathered context)hasCodebaseAnalysis(from config)projectRoot
- Subtask mode: Load 'update-subtask' prompt template with params:
parentTask(id, title)prevSubtask(id, title, status) - if existsnextSubtask(id, title, status) - if existscurrentDetails(existing subtask details or fallback)updatePromptuseResearchgatheredContexthasCodebaseAnalysisprojectRoot
- Subtask mode: Support variant key ('research' or 'default')
- Extract
systemPromptanduserPromptfrom prompt manager
AI Service Integration
- Determine service role:
useResearch ? 'research' : 'main' - Bulk/Single task mode: Call
generateObjectServicewith:role,session,projectRootsystemPrompt,prompt(userPrompt)schema: COMMAND_SCHEMAS['update-tasks']objectName: 'tasks'commandName: 'update-tasks'outputType: isMCP ? 'mcp' : 'cli'
- Subtask mode: Call
generateTextServicewith:prompt(userPrompt),systemPromptrole,session,projectRootmaxRetries: 2commandName: 'update-subtask'outputType: isMCP ? 'mcp' : 'cli'
- Handle empty/invalid AI responses
- Capture
telemetryDataandtagInfofrom response
Data Updates & Persistence
- Bulk/Single task mode:
- Parse
aiServiceResponse.mainResult.tasksarray - Validate array structure
- Create Map for efficient lookup
- Merge updated tasks with existing, preserving subtasks field
- Track actual update count
- Parse
- Subtask mode:
- Extract text string from
aiServiceResponse.mainResult - Generate ISO timestamp
- Format as:
<info added on ${timestamp}>\n${content}\n</info added on ${timestamp}> - Append to
subtask.details(create if doesn't exist) - Store newly added snippet separately for display
- If prompt < 100 chars: append
[Updated: ${date}]to subtask.description
- Extract text string from
- Write updated data using
writeJSON(tasksPath, data, projectRoot, tag) - Optionally call
generateTaskFiles()(currently commented out in both)
CLI Display & UX
- Pre-update display (CLI only, text mode):
- Create table with columns: ID, Title, Status
- Truncate titles appropriately (57 chars for tasks, 52 for subtasks)
- Apply status colors via
getStatusWithColor() - Show boxed header with update count/target
- Bulk mode: Show info box about completed subtasks handling
- Display table
- Loading indicators (CLI only, text mode):
- Start loading indicator before AI call
- Message: "Updating tasks with AI..." (bulk/single) or "Updating subtask..." (subtask)
- Support research variant message
- Stop indicator when complete or on error
- Post-update display (CLI only, text mode):
- Bulk/Single task: Success message with update count
- Subtask mode: Boxed success message with:
- Subtask ID
- Title
- "Newly Added Snippet" section showing timestamped content
- Display AI usage summary via
displayAiUsageSummary(telemetryData, 'cli')
Logging & Debugging
- Use appropriate logger:
mcpLog(MCP) orconsoleLog(CLI) - Log info messages with proper format (MCP vs CLI differences)
- Log start of operation with key parameters
- Log task counts and AI response details
- Log successful completion
- Debug mode (when
getDebugFlag(session)true):- Log subtask details before/after update
- Log writeJSON calls
- Log full error stack traces
Error Handling
- Catch and handle errors at multiple levels:
- Context gathering errors (warn and continue)
- AI service errors (stop and report)
- General operation errors (report and exit/throw)
- CLI mode:
- Print colored error messages
- Show helpful troubleshooting for common errors:
- API key missing/invalid
- Model overloaded
- Task/subtask not found
- Invalid ID format
- Empty prompt
- Empty AI response
- Exit with code 1
- MCP mode: Re-throw errors for caller handling
- Always stop loading indicators on error
Return Values
-
Success returns (both modes):
{ success: true, // bulk/single task only updatedTasks: [...], // bulk/single task only updatedSubtask: {...}, // subtask only telemetryData: {...}, tagInfo: {...} } -
Failure returns:
- CLI: exits with code 1
- MCP: throws error
- Subtask mode: returns
nullon error
Special Features
Completed Subtasks Handling (Bulk Mode)
- Display informational box explaining:
- Done/completed subtasks are preserved
- New subtasks build upon completed work
- Revisions create new subtasks instead of modifying done items
- Maintains clear record of progress
Subtask Context Awareness
- Provide parent task context (id, title) to AI
- Provide previous subtask context (if exists) to AI
- Provide next subtask context (if exists) to AI
- Include current subtask details in prompt
Timestamp Tracking
- Use ISO format timestamps for subtask updates
- Wrap appended content in timestamped tags
- Update description field with simple date stamp (short prompts only)
Migration Architecture
Object-Oriented Design Philosophy
This migration will follow the established patterns in tm-core and apps/cli:
- Domain separation with clear bounded contexts
- Dependency injection for testability and flexibility
- Abstract base classes for shared behavior
- Interfaces for contracts and loose coupling
- Service layer for business logic orchestration
- Factory pattern for object creation
- Single Responsibility Principle throughout
Package Structure
packages/tm-core/
src/
commands/
update-task/
# Core Interfaces & Types
types.ts # Shared types, enums, interfaces
interfaces/
update-strategy.interface.ts # IUpdateStrategy contract
update-context.interface.ts # IUpdateContext contract
display.interface.ts # IDisplayManager contract
# Services (Business Logic)
update-task.service.ts # Main orchestrator service
context-builder.service.ts # Builds AI context (uses ContextGatherer, FuzzySearch)
prompt-builder.service.ts # Builds prompts (uses PromptManager)
data-merger.service.ts # Merges AI results with existing data
# Strategies (Update Mode Logic)
strategies/
base-update.strategy.ts # Abstract base class for all strategies
bulk-update.strategy.ts # Bulk task update implementation
single-task-update.strategy.ts # Single task update implementation
subtask-update.strategy.ts # Subtask update implementation
# Utilities & Helpers
validators/
update-input.validator.ts # Validates all input parameters
task-id.validator.ts # Parses and validates task/subtask IDs
display/
cli-display.manager.ts # CLI output formatting
json-display.manager.ts # JSON output formatting
update-display.factory.ts # Creates appropriate display manager
factories/
update-strategy.factory.ts # Creates appropriate strategy based on mode
# Main Entry Point
index.ts # Public API export
apps/cli/
src/
commands/
update-task.command.ts # CLI command definition (uses UpdateTaskService)
Core Classes & Their Responsibilities
1. UpdateTaskService (Main Orchestrator)
/**
* Main service that coordinates the entire update process
* Handles initialization, strategy selection, and result aggregation
*/
export class UpdateTaskService {
constructor(
private readonly configManager: ConfigManager,
private readonly storage: IStorage,
private readonly logger: Logger,
private readonly strategyFactory: UpdateStrategyFactory,
private readonly contextBuilder: ContextBuilderService,
private readonly displayFactory: UpdateDisplayFactory
) {}
async updateTask(options: UpdateTaskOptions): Promise<UpdateTaskResult> {
// 1. Validate inputs
// 2. Detect mode and create strategy
// 3. Build context
// 4. Execute strategy
// 5. Display results
// 6. Return result
}
}
Uses (existing classes):
ConfigManager- Project configurationIStorage- Task persistenceLogger- LoggingContextGatherer- Gather related contextFuzzyTaskSearch- Find relevant tasksPromptManager- Load prompt templates
Uses (new classes):
UpdateStrategyFactory- Create update strategyContextBuilderService- Build AI contextUpdateDisplayFactory- Create display manager
2. IUpdateStrategy (Strategy Interface)
/**
* Contract for all update strategies
* Defines the common interface for bulk, single, and subtask updates
*/
export interface IUpdateStrategy {
/**
* Validate that the strategy can handle the given context
*/
validate(context: IUpdateContext): Promise<void>;
/**
* Load and filter tasks that need updating
*/
loadTasks(context: IUpdateContext): Promise<TaskLoadResult>;
/**
* Build prompts for AI service
*/
buildPrompts(
context: IUpdateContext,
tasks: TaskLoadResult
): Promise<PromptResult>;
/**
* Call appropriate AI service
*/
callAIService(
context: IUpdateContext,
prompts: PromptResult
): Promise<AIServiceResult>;
/**
* Merge AI results with existing data
*/
mergeResults(
context: IUpdateContext,
aiResult: AIServiceResult,
originalTasks: TaskLoadResult
): Promise<MergeResult>;
/**
* Get the mode this strategy handles
*/
getMode(): UpdateMode;
}
3. BaseUpdateStrategy (Abstract Base Class)
/**
* Provides common functionality for all update strategies
* Implements template method pattern for the update workflow
*/
export abstract class BaseUpdateStrategy implements IUpdateStrategy {
protected readonly logger: Logger;
constructor(
protected readonly contextBuilder: ContextBuilderService,
protected readonly promptBuilder: PromptBuilderService,
protected readonly dataMerger: DataMergerService,
protected readonly aiService: AIService // wrapper around generate[Object|Text]Service
) {
this.logger = getLogger(`UpdateStrategy:${this.getMode()}`);
}
// Template method - defines the workflow
async execute(context: IUpdateContext): Promise<UpdateStrategyResult> {
await this.validate(context);
const tasks = await this.loadTasks(context);
const prompts = await this.buildPrompts(context, tasks);
const aiResult = await this.callAIService(context, prompts);
const merged = await this.mergeResults(context, aiResult, tasks);
return merged;
}
// Subclasses must implement these
abstract validate(context: IUpdateContext): Promise<void>;
abstract loadTasks(context: IUpdateContext): Promise<TaskLoadResult>;
abstract getMode(): UpdateMode;
// Shared implementations with extensibility
async buildPrompts(
context: IUpdateContext,
tasks: TaskLoadResult
): Promise<PromptResult> {
// Delegates to PromptBuilderService with mode-specific params
}
protected abstract getPromptParams(
context: IUpdateContext,
tasks: TaskLoadResult
): PromptParams;
}
4. BulkUpdateStrategy (Concrete Strategy)
/**
* Handles bulk task updates (--from flag)
* Uses generateObjectService for structured updates
*/
export class BulkUpdateStrategy extends BaseUpdateStrategy {
getMode(): UpdateMode {
return UpdateMode.BULK;
}
async validate(context: IUpdateContext): Promise<void> {
if (!context.options.from) {
throw new TaskMasterError('Bulk mode requires --from parameter');
}
// Additional validations...
}
async loadTasks(context: IUpdateContext): Promise<TaskLoadResult> {
// Filter tasks where id >= fromId AND status !== 'done'
}
async callAIService(
context: IUpdateContext,
prompts: PromptResult
): Promise<AIServiceResult> {
// Call generateObjectService with update-tasks schema
}
protected getPromptParams(
context: IUpdateContext,
tasks: TaskLoadResult
): PromptParams {
return {
tasks: tasks.tasks,
updatePrompt: context.options.prompt,
useResearch: context.options.useResearch,
projectContext: tasks.gatheredContext,
// ...
};
}
}
5. SubtaskUpdateStrategy (Concrete Strategy)
/**
* Handles single subtask updates (--id with dot notation)
* Uses generateTextService for timestamped appends
*/
export class SubtaskUpdateStrategy extends BaseUpdateStrategy {
getMode(): UpdateMode {
return UpdateMode.SUBTASK;
}
async validate(context: IUpdateContext): Promise<void> {
const parsed = TaskIdValidator.parseSubtaskId(context.options.id);
if (!parsed) {
throw new TaskMasterError('Invalid subtask ID format');
}
}
async loadTasks(context: IUpdateContext): Promise<TaskLoadResult> {
// Find parent task, locate specific subtask
// Build context with prev/next subtask info
}
async callAIService(
context: IUpdateContext,
prompts: PromptResult
): Promise<AIServiceResult> {
// Call generateTextService for freeform content
}
async mergeResults(
context: IUpdateContext,
aiResult: AIServiceResult,
originalTasks: TaskLoadResult
): Promise<MergeResult> {
// Append timestamped content to subtask.details
const timestamp = new Date().toISOString();
const formatted = `<info added on ${timestamp}>\n${aiResult.text}\n</info>`;
// ...
}
}
6. SingleTaskUpdateStrategy (Concrete Strategy)
/**
* Handles single task updates (--id without dot)
* Uses generateObjectService for structured updates
*/
export class SingleTaskUpdateStrategy extends BaseUpdateStrategy {
getMode(): UpdateMode {
return UpdateMode.SINGLE;
}
async validate(context: IUpdateContext): Promise<void> {
TaskIdValidator.validateTaskId(context.options.id);
}
async loadTasks(context: IUpdateContext): Promise<TaskLoadResult> {
// Find single task by ID
}
// Similar to BulkUpdateStrategy but operates on single task
}
7. ContextBuilderService (Helper Service)
/**
* Builds context for AI prompts
* Coordinates ContextGatherer and FuzzyTaskSearch
*/
export class ContextBuilderService {
constructor(
private readonly logger: Logger
) {}
async buildContext(
options: ContextBuildOptions
): Promise<BuiltContext> {
try {
const gatherer = new ContextGatherer(
options.projectRoot,
options.tag
);
const allTasksFlat = flattenTasksWithSubtasks(options.allTasks);
const fuzzySearch = new FuzzyTaskSearch(
allTasksFlat,
options.searchMode // 'update' or 'update-subtask'
);
const searchResults = fuzzySearch.findRelevantTasks(
options.searchQuery,
{ maxResults: 5, includeSelf: true }
);
const relevantTaskIds = fuzzySearch.getTaskIds(searchResults);
const finalTaskIds = [
...new Set([...options.targetTaskIds, ...relevantTaskIds])
];
const contextResult = await gatherer.gather({
tasks: finalTaskIds,
format: 'research'
});
return {
context: contextResult.context || '',
taskIds: finalTaskIds
};
} catch (error) {
this.logger.warn(`Context gathering failed: ${error.message}`);
return { context: '', taskIds: options.targetTaskIds };
}
}
}
Uses (existing):
ContextGathererFuzzyTaskSearch
8. PromptBuilderService (Helper Service)
/**
* Builds system and user prompts for AI services
* Wraps PromptManager with strategy-specific logic
*/
export class PromptBuilderService {
constructor(
private readonly promptManager: PromptManager,
private readonly logger: Logger
) {}
async buildPrompt(
templateName: string,
params: PromptParams,
variant?: string
): Promise<PromptResult> {
const { systemPrompt, userPrompt } = await this.promptManager.loadPrompt(
templateName,
params,
variant
);
return {
systemPrompt,
userPrompt,
templateName,
params
};
}
}
Uses (existing):
PromptManager
9. DataMergerService (Helper Service)
/**
* Merges AI service results with existing task data
* Handles different merge strategies for different modes
*/
export class DataMergerService {
constructor(private readonly logger: Logger) {}
/**
* Merge for bulk/single task mode (structured updates)
*/
mergeTasks(
existingTasks: Task[],
updatedTasks: Task[],
options: MergeOptions
): MergeResult {
const updatedTasksMap = new Map(
updatedTasks.map(t => [t.id, t])
);
let updateCount = 0;
const merged = existingTasks.map(task => {
if (updatedTasksMap.has(task.id)) {
const updated = updatedTasksMap.get(task.id)!;
updateCount++;
return {
...task,
...updated,
// Preserve subtasks if not provided by AI
subtasks: updated.subtasks !== undefined
? updated.subtasks
: task.subtasks
};
}
return task;
});
return {
tasks: merged,
updateCount,
mode: 'structured'
};
}
/**
* Merge for subtask mode (timestamped append)
*/
mergeSubtask(
parentTask: Task,
subtaskIndex: number,
newContent: string,
options: SubtaskMergeOptions
): SubtaskMergeResult {
const subtask = parentTask.subtasks![subtaskIndex];
const timestamp = new Date().toISOString();
const formatted = `<info added on ${timestamp}>\n${newContent.trim()}\n</info added on ${timestamp}>`;
subtask.details = (subtask.details ? subtask.details + '\n' : '') + formatted;
// Short prompts get description timestamp
if (options.prompt.length < 100 && subtask.description) {
subtask.description += ` [Updated: ${new Date().toLocaleDateString()}]`;
}
return {
updatedSubtask: subtask,
newlyAddedSnippet: formatted,
parentTask
};
}
}
10. IDisplayManager (Display Interface)
/**
* Contract for display managers
* Allows different output formats (CLI, JSON, etc.)
*/
export interface IDisplayManager {
/**
* Show tasks before update
*/
showPreUpdate(tasks: Task[], mode: UpdateMode): void;
/**
* Show loading indicator
*/
startLoading(message: string): void;
stopLoading(success?: boolean): void;
/**
* Show post-update results
*/
showPostUpdate(result: UpdateStrategyResult, mode: UpdateMode): void;
/**
* Show telemetry/usage data
*/
showTelemetry(telemetry: TelemetryData): void;
/**
* Show errors
*/
showError(error: Error): void;
}
11. CLIDisplayManager (Concrete Display)
/**
* Formats output for CLI with colors, tables, and boxes
*/
export class CLIDisplayManager implements IDisplayManager {
constructor(
private readonly logger: Logger,
private readonly isSilent: boolean
) {}
showPreUpdate(tasks: Task[], mode: UpdateMode): void {
// Create table with ID, Title, Status columns
// Show boxed header
// For bulk mode: show completed subtasks info box
}
startLoading(message: string): void {
// startLoadingIndicator(message)
}
// ... implement other methods with chalk, boxen, cli-table3
}
12. UpdateStrategyFactory (Factory)
/**
* Creates the appropriate update strategy based on mode
*/
export class UpdateStrategyFactory {
constructor(
private readonly contextBuilder: ContextBuilderService,
private readonly promptBuilder: PromptBuilderService,
private readonly dataMerger: DataMergerService,
private readonly aiService: AIService
) {}
createStrategy(mode: UpdateMode): IUpdateStrategy {
switch (mode) {
case UpdateMode.BULK:
return new BulkUpdateStrategy(
this.contextBuilder,
this.promptBuilder,
this.dataMerger,
this.aiService
);
case UpdateMode.SINGLE:
return new SingleTaskUpdateStrategy(
this.contextBuilder,
this.promptBuilder,
this.dataMerger,
this.aiService
);
case UpdateMode.SUBTASK:
return new SubtaskUpdateStrategy(
this.contextBuilder,
this.promptBuilder,
this.dataMerger,
this.aiService
);
default:
throw new TaskMasterError(`Unknown update mode: ${mode}`);
}
}
detectMode(options: UpdateTaskOptions): UpdateMode {
if (options.from !== undefined) {
return UpdateMode.BULK;
}
if (options.id && typeof options.id === 'string' && options.id.includes('.')) {
return UpdateMode.SUBTASK;
}
if (options.id !== undefined) {
return UpdateMode.SINGLE;
}
throw new TaskMasterError('Must provide either --id or --from parameter');
}
}
13. Validators (Utility Classes)
/**
* Validates all update task inputs
*/
export class UpdateInputValidator {
static validate(options: UpdateTaskOptions): void {
// Validate tasksPath, prompt, etc.
}
}
/**
* Parses and validates task/subtask IDs
*/
export class TaskIdValidator {
static validateTaskId(id: any): number {
// Parse and validate task ID
}
static parseSubtaskId(id: string): SubtaskIdParts | null {
// Parse "parentId.subtaskId" format
}
}
Class Diagram (Relationships)
┌─────────────────────────┐
│ UpdateTaskService │ ◄─── Main Orchestrator
│ (Coordinates) │
└───────┬─────────────────┘
│ uses
├──► UpdateStrategyFactory ──creates──► IUpdateStrategy
│ │
├──► ContextBuilderService │ implements
│ ▼
├──► IDisplayManager ◄──creates── UpdateDisplayFactory
│ │
│ ├── CLIDisplayManager
│ └── JSONDisplayManager
│
└──► ConfigManager (existing)
IStorage (existing)
Logger (existing)
┌────────────────────────────────────────────────────┐
│ IUpdateStrategy │
└────────────────────────────────────────────────────┘
△
│ extends
┌───────────┴────────────┐
│ │
┌───────────────────────┐ ┌──────────────────────┐
│ BaseUpdateStrategy │ │ Abstract base with │
│ (Template Method) │ │ common workflow │
└───────────┬───────────┘ └──────────────────────┘
│ extends
┌───────┼──────────┬─────────────┐
│ │ │ │
┌───▼───┐ ┌─▼────┐ ┌─▼──────────┐ │
│ Bulk │ │Single│ │ Subtask │ │
│Update │ │Task │ │ Update │ │
│ │ │Update│ │ │ │
└───────┘ └──────┘ └────────────┘ │
│
├──► ContextBuilderService
│ ├─uses─► ContextGatherer (existing)
│ └─uses─► FuzzyTaskSearch (existing)
│
├──► PromptBuilderService
│ └─uses─► PromptManager (existing)
│
└──► DataMergerService
Dependency Injection & Initialization
// In packages/tm-core/src/commands/update-task/index.ts
/**
* Factory function to create a fully initialized UpdateTaskService
*/
export async function createUpdateTaskService(
configManager: ConfigManager,
storage: IStorage
): Promise<UpdateTaskService> {
const logger = getLogger('UpdateTaskService');
// Create helper services
const contextBuilder = new ContextBuilderService(logger);
const promptManager = getPromptManager(); // existing
const promptBuilder = new PromptBuilderService(promptManager, logger);
const dataMerger = new DataMergerService(logger);
const aiService = new AIService(); // wrapper around generateObjectService/generateTextService
// Create factory
const strategyFactory = new UpdateStrategyFactory(
contextBuilder,
promptBuilder,
dataMerger,
aiService
);
// Create display factory
const displayFactory = new UpdateDisplayFactory();
// Create service
return new UpdateTaskService(
configManager,
storage,
logger,
strategyFactory,
contextBuilder,
displayFactory
);
}
Types & Interfaces
// packages/tm-core/src/commands/update-task/types.ts
export enum UpdateMode {
BULK = 'bulk',
SINGLE = 'single',
SUBTASK = 'subtask'
}
export interface UpdateTaskOptions {
tasksPath: string;
id?: number | string;
from?: number;
prompt: string;
useResearch?: boolean;
context?: UpdateContext;
outputFormat?: 'text' | 'json';
}
export interface UpdateContext {
session?: any;
mcpLog?: any;
projectRoot?: string;
tag?: string;
}
export interface UpdateTaskResult {
success: boolean;
mode: UpdateMode;
updatedTasks?: Task[];
updatedSubtask?: Subtask;
updateCount?: number;
telemetryData?: TelemetryData;
tagInfo?: TagInfo;
}
export interface IUpdateContext {
options: UpdateTaskOptions;
projectRoot: string;
tag?: string;
mode: UpdateMode;
isMCP: boolean;
logger: Logger;
}
export interface TaskLoadResult {
tasks: Task[];
gatheredContext: string;
originalData: TasksData;
}
export interface PromptResult {
systemPrompt: string;
userPrompt: string;
templateName: string;
params: PromptParams;
}
export interface AIServiceResult {
mainResult: any; // structured object or text string
telemetryData?: TelemetryData;
tagInfo?: TagInfo;
}
export interface MergeResult {
tasks?: Task[];
updatedSubtask?: Subtask;
newlyAddedSnippet?: string;
updateCount: number;
mode: 'structured' | 'timestamped';
}
Implementation Phases
Phase 1: Foundation & Core Types
Goal: Establish the type system and interfaces
New Files to Create:
packages/tm-core/src/commands/update-task/types.ts- Define
UpdateModeenum - Define all shared interfaces (
UpdateTaskOptions,UpdateTaskResult, etc.)
- Define
packages/tm-core/src/commands/update-task/interfaces/update-strategy.interface.ts- Define
IUpdateStrategyinterface
- Define
packages/tm-core/src/commands/update-task/interfaces/update-context.interface.ts- Define
IUpdateContextinterface
- Define
packages/tm-core/src/commands/update-task/interfaces/display.interface.ts- Define
IDisplayManagerinterface
- Define
Existing Classes to Study:
BaseExecutor- For abstract class patternsTaskService- For service patternsIStorage- For interface patterns
Phase 2: Validator & Helper Utilities
Goal: Build validation and utility classes
New Files to Create:
packages/tm-core/src/commands/update-task/validators/update-input.validator.ts- Create
UpdateInputValidatorclass - Port validation logic from both old files
- Create
packages/tm-core/src/commands/update-task/validators/task-id.validator.ts- Create
TaskIdValidatorclass - Implement
validateTaskId()andparseSubtaskId()methods
- Create
Tests to Create:
update-input.validator.spec.tstask-id.validator.spec.ts
Phase 3: Service Layer
Goal: Build the helper services that strategies will use
New Files to Create:
-
packages/tm-core/src/commands/update-task/context-builder.service.ts- Create
ContextBuilderServiceclass - Uses existing:
ContextGatherer,FuzzyTaskSearch - Port context gathering logic from both old files
- Create
-
packages/tm-core/src/commands/update-task/prompt-builder.service.ts- Create
PromptBuilderServiceclass - Uses existing:
PromptManager(viagetPromptManager()) - Port prompt building logic
- Create
-
packages/tm-core/src/commands/update-task/data-merger.service.ts- Create
DataMergerServiceclass - Implement
mergeTasks()method (fromupdate-tasks.jslines 250-273) - Implement
mergeSubtask()method (fromupdate-subtask-by-id.jslines 291-332)
- Create
Tests to Create:
context-builder.service.spec.tsprompt-builder.service.spec.tsdata-merger.service.spec.ts
Existing Classes Used:
ContextGatherer(fromscripts/modules/utils/contextGatherer.js)FuzzyTaskSearch(fromscripts/modules/utils/fuzzyTaskSearch.js)PromptManager(fromscripts/modules/prompt-manager.js)
Phase 4: Strategy Pattern Implementation
Goal: Implement the update strategies
New Files to Create:
-
packages/tm-core/src/commands/update-task/strategies/base-update.strategy.ts- Create
BaseUpdateStrategyabstract class implementingIUpdateStrategy - Implement template method pattern
- Define abstract methods for subclasses
- Create
-
packages/tm-core/src/commands/update-task/strategies/bulk-update.strategy.ts- Create
BulkUpdateStrategyclass extendingBaseUpdateStrategy - Port logic from
update-tasks.jslines 79-293 - Uses:
generateObjectServicewithCOMMAND_SCHEMAS['update-tasks']
- Create
-
packages/tm-core/src/commands/update-task/strategies/single-task-update.strategy.ts- Create
SingleTaskUpdateStrategyclass extendingBaseUpdateStrategy - Similar to bulk but for single task
- Uses:
generateObjectServicewithCOMMAND_SCHEMAS['update-tasks']
- Create
-
packages/tm-core/src/commands/update-task/strategies/subtask-update.strategy.ts- Create
SubtaskUpdateStrategyclass extendingBaseUpdateStrategy - Port logic from
update-subtask-by-id.jslines 67-378 - Uses:
generateTextServicefor freeform content
- Create
Tests to Create:
bulk-update.strategy.spec.tssingle-task-update.strategy.spec.tssubtask-update.strategy.spec.ts
Existing Classes/Functions Used:
generateObjectService(fromscripts/modules/ai-services-unified.js)generateTextService(fromscripts/modules/ai-services-unified.js)COMMAND_SCHEMAS(fromsrc/schemas/registry.js)readJSON,writeJSON,flattenTasksWithSubtasks(fromscripts/modules/utils.js)
Phase 5: Display Layer
Goal: Implement display managers for different output formats
New Files to Create:
-
packages/tm-core/src/commands/update-task/display/cli-display.manager.ts- Create
CLIDisplayManagerclass implementingIDisplayManager - Port CLI display logic from both old files
- Uses existing:
chalk,boxen,cli-table3,getStatusWithColor,truncate
- Create
-
packages/tm-core/src/commands/update-task/display/json-display.manager.ts- Create
JSONDisplayManagerclass implementingIDisplayManager - Implement JSON output format (for MCP)
- Create
-
packages/tm-core/src/commands/update-task/display/update-display.factory.ts- Create
UpdateDisplayFactoryclass - Factory method to create appropriate display manager
- Create
Tests to Create:
cli-display.manager.spec.tsjson-display.manager.spec.ts
Existing Functions Used:
getStatusWithColor,startLoadingIndicator,stopLoadingIndicator,displayAiUsageSummary(fromscripts/modules/ui.js)truncate,isSilentMode(fromscripts/modules/utils.js)
Phase 6: Factory Pattern
Goal: Implement factory for creating strategies
New Files to Create:
packages/tm-core/src/commands/update-task/factories/update-strategy.factory.ts- Create
UpdateStrategyFactoryclass - Implement
createStrategy(mode)method - Implement
detectMode(options)method - Handles dependency injection for all strategies
- Create
Tests to Create:
update-strategy.factory.spec.ts(test mode detection and strategy creation)
Phase 7: Main Service Orchestrator
Goal: Create the main service that ties everything together
New Files to Create:
-
packages/tm-core/src/commands/update-task/update-task.service.ts- Create
UpdateTaskServiceclass - Main orchestrator that coordinates all components
- Implements high-level workflow
- Create
-
packages/tm-core/src/commands/update-task/index.ts- Export all public types and interfaces
- Export
createUpdateTaskService()factory function - Export
UpdateTaskServiceclass
Tests to Create:
update-task.service.spec.ts(integration tests)
Existing Classes Used:
ConfigManager(frompackages/tm-core/src/config/config-manager.ts)IStorage(frompackages/tm-core/src/interfaces/storage.interface.ts)Logger,getLogger(frompackages/tm-core/src/logger/)
Phase 8: CLI Integration
Goal: Wire up the new service to the CLI
New Files to Create:
apps/cli/src/commands/update-task.command.ts- CLI command definition using
commander - Calls
createUpdateTaskService()and executes - Handles CLI-specific argument parsing
- CLI command definition using
Files to Modify:
apps/cli/src/index.ts(or main CLI entry point)- Register new
update-taskcommand - Optionally add aliases for backward compatibility
- Register new
Existing Patterns to Follow:
- Study existing CLI commands in
apps/cli/src/commands/ - Follow same pattern for option parsing and service invocation
Phase 9: Integration & Testing
Goal: Ensure everything works together
Tasks:
-
Run full integration tests
- Test bulk update workflow end-to-end
- Test single task update workflow
- Test subtask update workflow
- Test MCP mode vs CLI mode
- Test all edge cases from checklist
-
Verify against original functionality
- Use the functionality checklist
- Ensure no regressions
- Test with real task data
-
Performance testing
- Compare execution time with old implementation
- Ensure context gathering performs well
Tests to Create:
update-task.integration.spec.ts- Full workflow tests- End-to-end tests with real task files
Phase 10: Documentation & Migration
Goal: Document the new system and deprecate old code
Tasks:
-
Update documentation
- Update
apps/docs/command-reference.mdx - Add JSDoc comments to all public APIs
- Create migration guide for users
- Update
-
Add deprecation warnings
- Mark old
updateandupdate-subtaskcommands as deprecated - Add console warnings directing users to new command
- Mark old
-
Create changeset
- Document breaking changes (if any)
- Document new features (unified command)
- Note backward compatibility
Files to Modify:
apps/docs/command-reference.mdx- Update command documentation- Legacy files (add deprecation warnings):
scripts/modules/task-manager/update-tasks.jsscripts/modules/task-manager/update-subtask-by-id.js
Phase 11: Cleanup
Goal: Remove deprecated code (future version)
Tasks:
-
Remove old files:
scripts/modules/task-manager/update-tasks.jsscripts/modules/task-manager/update-subtask-by-id.js- Any related old command handlers
-
Clean up any temporary compatibility shims
-
Update all references in codebase to use new command
Testing Strategy
Unit Tests
- Mode detection logic
- ID parsing and validation
- Context gathering integration
- Prompt building for each mode
- Data merging logic
Integration Tests
- Bulk update workflow
- Single task update workflow
- Single subtask update workflow
- MCP mode operation
- CLI mode operation
Edge Cases
- Empty tasks.json
- Invalid ID formats
- Non-existent IDs
- Tasks with no subtasks
- Empty AI responses
- Context gathering failures
Backward Compatibility
Deprecation Strategy
- Keep old commands working initially
- Add deprecation warnings
- Update all documentation
- Remove old commands in next major version
Alias Support (Optional)
# Could maintain old command names as aliases
task-master update --from=3 --prompt="..." # Still works, calls update-task
task-master update-subtask --id=3.2 --prompt="..." # Still works, calls update-task
Risk Mitigation
High-Risk Areas
- Data integrity: Ensure writeJSON doesn't corrupt existing data
- AI service compatibility: Both generateObjectService and generateTextService must work
- Subtask detail format: Maintain timestamp format consistency
- Context gathering: Same behavior across all modes
Rollback Plan
- Keep old files until new version is fully tested
- Version bump allows reverting if issues found
- Comprehensive test coverage before release
Success Criteria
- All checklist items verified working
- Tests passing for all modes
- MCP integration functional
- CLI display matches existing behavior
- Documentation updated
- No regression in existing functionality
- Performance comparable or better than current implementation