Task Master update-task 命令统一迁移指南:策略模式重构任务与子任务更新架构
【免费下载链接】claude-task-masterAn AI-powered task-management system you can drop into Cursor, Lovable, Windsurf, Roo, and others.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-task-master
本指南围绕 claude-task-master 仓库中的update-task-migration-plan.md迁移计划展开,系统讲解如何将遗留的update-tasks.js(批量更新)与update-subtask-by-id.js(单条子任务更新)合并为统一的update-task命令,并迁移到packages/tm-core+apps/cli的新架构。读者读完将掌握:智能行为检测规则、策略模式 + 模板方法模式 + 工厂模式的具体落地方式、上下文采集与提示词构建的调用链,以及分 11 个阶段的渐进式迁移与回滚策略。
迁移背景:为什么需要统一 update 命令
在 claude-task-master 的旧架构中,任务更新逻辑分散在两个独立文件中,职责重叠且维护成本高:
| 旧模块 | 职责 | 输入格式 | AI 服务 |
|---|---|---|---|
| update-tasks.js | 从指定 ID 起批量更新多个任务 | --from=<id> --prompt="context" | generateObjectService(结构化 schema 输出) |
| update-subtask-by-id.js | 向特定子任务追加带时间戳的信息 | --id=<parentId.subtaskId> --prompt="notes" | generateTextService(自由文本输出) |
两者共享大量相同逻辑(任务加载、上下文采集、提示词构建、CLI 展示、错误处理),却各自维护一份拷贝。迁移计划的核心目标是把它们合并为单一update-task命令,并按照tm-core与apps/cli的既有模式完成面向对象重构——这一点可以从 packages/tm-core/src/modules/commands/index.ts 的占位注释得到印证,该文件明确写着 "Placeholder for future migration",任务命令的执行编排正是计划中的待迁移内容。
统一命令设计与智能行为检测
迁移后的新命令语法如下:
# 更新单个任务(替代 update-task) task-master update-task --id=3 --prompt="changes" # 更新单个子任务(替代 update-subtask) task-master update-task --id=3.2 --prompt="implementation notes" # 从 ID 起批量更新多个任务(替代 update --from) task-master update-task --from=3 --prompt="changes"命令不需要显式声明模式,而是通过智能行为检测自动判定,判定顺序如下:
- ID 包含
.→ 子任务模式(subtask mode),例如--id=3.2 - 存在
--from标志→ 批量更新模式(bulk mode) - 默认→ 单任务更新模式(single task mode)
这一检测逻辑在计划中由UpdateStrategyFactory.detectMode()实现,代码骨架清晰地体现了判定优先级(先判--from,再判带点号的--id,最后是普通--id,三者均缺失则抛出TaskMasterError)。结合旧实现可以验证该设计的一致性:update-tasks.js 中批量筛选条件为task.id >= fromId && task.status !== 'done',而 update-subtask-by-id.js 通过parentId.subtaskId拆分字符串并校验两段均为正整数——这些规则全部被吸收进新设计的TaskIdValidator与各策略的validate()中。
功能清单:从输入校验到持久化的完整链路
迁移计划给出了一个覆盖全流程的功能清单,它实际上是对旧实现行为的逐条核对。以下结合源码逐层展开。
输入校验与解析
- 校验
tasksPath文件存在(旧实现中update-subtask-by-id.js通过fs.existsSync(tasksPath)显式检查); - 校验
id参数:任务必须为整数,子任务必须为"parent.child"格式; - 校验
fromId:正整数; - 校验
prompt:非空字符串(子任务模式例外——见下文的 metadata-only 快速路径); - 解析子任务 ID:拆分
parentId.subtaskId并分别校验; - 项目根目录:优先取上下文传入的
projectRoot,否则调用findProjectRoot(),两者都拿不到则抛出Could not determine project root directory; - 双模式支持:通过
mcpLog是否存在判定 MCP 模式(isMCP = !!mcpLog),并据此选择mcpLog或consoleLog作为日志函数; - 输出格式:
'text'或'json',MCP 模式下自动为'json'(update-subtask-by-id.js中默认值即为context.mcpLog ? 'json' : 'text')。
任务加载与过滤
统一通过readJSON(tasksPath, projectRoot, tag)加载tasks.json,三种模式过滤逻辑不同:
- 批量模式:
id >= fromId 且 status !== 'done';若筛选结果为空,则优雅地提示 "No tasks to update" 并直接返回; - 单任务模式:按 ID 精确查找;
- 子任务模式:先按父 ID 找到父任务,校验其
subtasks数组存在,再在数组中定位具体子任务。
上下文采集:ContextGatherer 与 FuzzyTaskSearch 的协作
两种旧实现都使用同一套上下文采集流程,迁移计划将其收敛到ContextBuilderService:
- 以
projectRoot和tag初始化ContextGatherer——该类的构造器会从.taskmaster/tasks/tasks.json预加载全部任务(见 contextGatherer.js); - 用
flattenTasksWithSubtasks()拍平所有任务与子任务; - 初始化
FuzzyTaskSearch,按命令类型选择搜索配置:批量/单任务模式用'update',子任务模式用'update-subtask'; - 批量/单任务模式:直接用 prompt 搜索,最多 5 条结果并包含自身;
- 子任务模式:用
${parentTask.title} ${subtask.title} ${prompt}组合查询串搜索(见 update-subtask-by-id.js); - 将"待更新任务 ID 集合"与"相关上下文任务 ID 集合"合并去重;
- 以
'research'格式调用gatherer.gather({ tasks, format: 'research' }); - 采集失败仅告警并继续(
Could not gather additional context)。
值得说明的是FuzzyTaskSearch的搜索算法(见 fuzzyTaskSearch.js):底层基于 Fuse.js,按 title(权重 2.0)、description(权重 1.5)、details(权重 1.0)、dependencyTitles(权重 0.5)加权模糊匹配,并按 relevance 阈值(high < 0.25、medium < 0.4、low < 0.6)分层,最后补充最多 5 条最近任务与类别匹配任务。计划中的ContextBuilderService.buildContext()骨架正是这套流程的封装,且在 catch 中返回{ context: '', taskIds: options.targetTaskIds }兜底。
提示词构建:PromptManager 双模板
通过getPromptManager()获取PromptManager后加载模板,两种模式参数不同:
批量/单任务模式('update-tasks'模板):
{ tasks: tasksToUpdate, // 待更新任务数组 updatePrompt: prompt, // 用户提示词 useResearch, // 是否使用 research AI 角色 projectContext: gatheredContext, // 采集到的上下文 hasCodebaseAnalysis: hasCodebaseAnalysis(useResearch, projectRoot, session), projectRoot }(对应 update-tasks.js 的实际调用。)
子任务模式('update-subtask'模板):额外传入parentTask(id、title)、prevSubtask(若存在,含 id/title/status)、nextSubtask(若存在)、currentDetails(现有 details 或兜底文案),并支持'research'/'default'变体键——旧实现通过const variantKey = useResearch ? 'research' : 'default'传入loadPrompt第三参。
AI 服务集成:结构化对象 vs 自由文本
计划要求根据useResearch决定服务角色('research' | 'main'),再按模式调用不同的统一 AI 服务:
- 批量/单任务模式:
generateObjectService({ role, session, projectRoot, systemPrompt, prompt, schema: COMMAND_SCHEMAS['update-tasks'], objectName: 'tasks', commandName: 'update-tasks', outputType: isMCP ? 'mcp' : 'cli' })。旧实现随后对返回的mainResult.tasks做字段归一化(为 dependencies、priority、details、testStrategy、subtasks 等字段填充默认值,见 update-tasks.js); - 子任务模式:
generateTextService({ prompt, systemPrompt, role, session, projectRoot, maxRetries: 2, commandName: 'update-subtask', outputType }),并处理空/非法响应。
两条服务都要求捕获telemetryData与tagInfo用于用量展示。
数据更新与持久化
批量/单任务模式的合并策略(对应旧实现 update-tasks.js,计划中移植到DataMergerService.mergeTasks()):
- 解析
aiServiceResponse.mainResult.tasks数组并校验结构; - 用
Map按任务 ID 建立索引实现高效查找; - 遍历原数据,命中则
{ ...task, ...updatedTask, subtasks: updatedTask.subtasks !== undefined ? updatedTask.subtasks : task.subtasks }——保留 AI 未返回的 subtasks 字段是关键防数据丢失点; - 统计真实更新条数
actualUpdateCount。
子任务模式的追加策略(对应 update-subtask-by-id.js,移植到DataMergerService.mergeSubtask()):
- 提取
mainResult文本;生成 ISO 时间戳; - 格式化为
<info added on ${timestamp}>\n${content}\n</info added on ${timestamp}>块; - 追加到
subtask.details(不存在则创建),并单独保存新增片段用于展示; - 若 prompt 长度 < 100 字符,向
subtask.description追加[Updated: ${date}]日期标记。
最后统一writeJSON(tasksPath, data, projectRoot, tag)写回,并保留(当前被注释的)generateTaskFiles()调用点。此外旧实现还包含一个metadata-only 快速路径(update-subtask-by-id.js):当只传metadata而不传 prompt 时,跳过 AI 直接合并 metadata 字段并写盘,这一行为也应在新架构中保留。
CLI 展示、日志与错误处理
- 更新前展示(仅 CLI text 模式):用
cli-table3生成 ID/Title/Status 三列表格(任务标题截断 57 字符、子任务 52 字符),状态通过getStatusWithColor()着色,用boxen输出带边框的标题;批量模式额外输出"已完成子任务如何处理"的信息框; - 加载指示器:AI 调用前
startLoadingIndicator('Updating tasks with AI...')或'Updating subtask...',完成或出错时stopLoadingIndicator(),research 变体有独立文案; - 更新后展示:批量模式输出成功条数;子任务模式用绿色边框 boxen 展示子任务 ID、标题与 "Newly Added Snippet"(时间戳内容);最后通过
displayAiUsageSummary(telemetryData, 'cli')展示 AI 用量; - 日志与调试:按
mcpLog/consoleLog分流;getDebugFlag(session)为真时输出子任务更新前后 details、writeJSON 调用、完整错误堆栈; - 错误处理分级:上下文采集失败(告警继续)、AI 服务失败(停止并上报)、一般错误(CLI 打印红色错误并
process.exit(1),MCP 直接 re-throw)。CLI 模式下对常见错误(API key 缺失、模型过载、任务/子任务不存在、ID 格式非法、空 prompt、空 AI 响应)提供针对性排查提示——例如子任务未找到时建议运行task-master list --with-subtasks查看可用 ID; - 返回值契约:成功时批量/单任务返回
{ success: true, updatedTasks, telemetryData, tagInfo },子任务返回{ updatedSubtask, telemetryData, tagInfo };失败时 CLI 退出码 1、MCP 抛错、子任务模式返回null。
新架构设计:tm-core 中的策略模式实现
迁移计划为update-task在packages/tm-core下设计了完整的目录结构,遵循 tm-core 的既有约定:领域隔离、依赖注入、抽象基类、接口契约、服务层编排、工厂模式与单一职责原则。
包结构总览
packages/tm-core/ src/commands/update-task/ types.ts # 共享类型、枚举、接口 interfaces/ update-strategy.interface.ts # IUpdateStrategy 契约 update-context.interface.ts # IUpdateContext 契约 display.interface.ts # IDisplayManager 契约 update-task.service.ts # 主编排服务 context-builder.service.ts # 构建 AI 上下文 prompt-builder.service.ts # 构建提示词 >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; }子类只需实现validate()、loadTasks()、getMode()与受保护的getPromptParams(),共享逻辑(提示词构建、AI 调用、数据合并)由基类与三个辅助服务完成。三种具体策略的分工:
- BulkUpdateStrategy:校验
--from存在;加载id >= fromId && status !== 'done'的任务;调用generateObjectService; - SingleTaskUpdateStrategy:通过
TaskIdValidator.validateTaskId()校验整数 ID;加载单个任务;AI 调用与批量相同; - SubtaskUpdateStrategy:通过
TaskIdValidator.parseSubtaskId()校验点号格式;定位父任务与子任务并携带前后子任务上下文;调用generateTextService;mergeResults()中生成<info added on ${timestamp}>时间戳块追加到subtask.details。
ContextBuilderService / PromptBuilderService / DataMergerService三个辅助服务分别封装上下文采集、模板加载与结果合并,各自只依赖已有工具类(ContextGatherer、FuzzyTaskSearch、PromptManager),保证可独立单测。
UpdateStrategyFactory的detectMode()实现了本文第二节的行为检测规则;createStrategy(mode)按枚举创建对应策略并注入依赖,未知模式抛出TaskMasterError。
IDisplayManager接口定义showPreUpdate / startLoading / stopLoading / showPostUpdate / showTelemetry / showError六个方法,CLIDisplayManager用 chalk、boxen、cli-table3 实现终端渲染,JSONDisplayManager面向 MCP 输出结构化结果,UpdateDisplayFactory按运行环境选择实现。
依赖注入与初始化
计划在packages/tm-core/src/commands/update-task/index.ts提供工厂函数createUpdateTaskService(configManager, storage),依次创建 logger、ContextBuilderService、PromptManager(复用现有getPromptManager())、PromptBuilderService、DataMergerService、AIService(对generateObjectService/generateTextService的包装),组装UpdateStrategyFactory与UpdateDisplayFactory,最后注入UpdateTaskService。CLI 侧apps/cli/src/commands/update-task.command.ts只需调用该工厂并执行——这与现有 CLI 命令的模式一致,例如 next.command.ts 继承Commander.Command作为薄展示层、内部委托TmCore的做法。
分阶段实施路线:11 个 Phase
迁移计划将实施拆分为 11 个阶段,每阶段都有明确的新增文件、测试与复用对象,可按序增量交付:
| Phase | 内容 | 关键产出 |
|---|---|---|
| 1 | 基础与核心类型 | types.ts、三个接口文件;研究BaseExecutor、TaskService、IStorage的模式 |
| 2 | 校验器与工具 | UpdateInputValidator、TaskIdValidator(移植两个旧文件的校验逻辑)+ 对应 spec |
| 3 | 服务层 | ContextBuilderService(复用 ContextGatherer/FuzzyTaskSearch)、PromptBuilderService(复用 PromptManager)、DataMergerService(移植 update-tasks.js L250-273 与 update-subtask-by-id.js L291-332 的合并逻辑)+ spec |
| 4 | 策略模式实现 | 抽象基类 + 三种策略;Bulk/Single 用generateObjectService+COMMAND_SCHEMAS['update-tasks'],Subtask 用generateTextService+ spec |
| 5 | 展示层 | CLIDisplayManager(复用 chalk/boxen/cli-table3/getStatusWithColor)、JSONDisplayManager、UpdateDisplayFactory+ spec |
| 6 | 工厂模式 | UpdateStrategyFactory:createStrategy()与detectMode()+ spec |
| 7 | 主服务编排 | UpdateTaskService、index.ts导出(类型 + 工厂 + 服务类);集成 spec |
| 8 | CLI 集成 | update-task.command.ts(commander 定义);在 CLI 入口注册命令,可选兼容别名 |
| 9 | 集成与测试 | 三模式端到端、MCP vs CLI、全清单边界用例、性能对比 |
| 10 | 文档与弃用 | 更新命令参考文档、JSDoc、为旧命令加弃用警告、changeset |
| 11 | 清理(未来版本) | 删除旧文件与兼容垫片,更新全部引用 |
计划中提到的COMMAND_SCHEMAS来自 src/schemas/registry.js,统一 AI 服务来自 scripts/modules/ai-services-unified.js,这两处是策略层移植时的既有依赖。
测试策略与边界用例
单元测试覆盖:模式检测逻辑、ID 解析与校验、上下文采集集成、各模式提示词构建、数据合并逻辑。集成测试覆盖:批量/单任务/单子任务三条工作流、MCP 与 CLI 双模式运行。边界用例清单包括:
- 空
tasks.json - 非法 ID 格式(如负数、非数字、
5.、.2) - 不存在的 ID
- 无子任务的任务
- 空 AI 响应
- 上下文采集失败(应告警继续而非中断)
计划中的update-task.service.spec.ts集成测试与data-merger.service.spec.ts单元测试可直接对照旧实现的合并逻辑逐条断言。
向后兼容、风险缓解与成功标准
向后兼容采用渐进式弃用:旧命令保持可用 → 添加弃用警告 → 更新文档 → 下个大版本移除。可选方案是保留旧命令名作为别名内部转发:
task-master update --from=3 --prompt="..." # 仍可用,实际调用 update-task task-master update-subtask --id=3.2 --prompt="..." # 仍可用,实际调用 update-task高风险区域与对策:
- 数据完整性——确保
writeJSON不损坏既有数据(保留 subtasks 字段、Map 合并、原子写); - AI 服务兼容性——
generateObjectService与generateTextService必须同时工作; - 子任务 details 格式——维持时间戳块格式一致性(
<info added on ${timestamp}>标签必须成对闭合); - 上下文采集——各模式行为保持一致。
回滚计划:旧文件保留至新版本充分测试;通过版本号升级支持回退;发布前完成全量测试覆盖。
成功标准:清单全部核验通过、各模式测试通过、MCP 集成可用、CLI 展示与既有行为一致、文档更新、无功能回归、性能不劣于现有实现。
结语
这份迁移计划的价值在于:它不是一次简单"搬文件",而是把两个行为相似、实现重复的遗留模块,按照策略模式、模板方法模式、工厂模式和服务层模式重构成单一命令的完整工程蓝图。packages/tm-core的目录骨架、接口契约(如 storage.interface.ts)与apps/cli的 Commander 命令模式均已就绪,迁移者只需按 11 个 Phase 顺序实施,即可在保持 CLI 与 MCP 双模式行为一致的前提下,把任务更新逻辑收敛到可单测、可扩展、可替换策略的新架构中。
【免费下载链接】claude-task-masterAn AI-powered task-management system you can drop into Cursor, Lovable, Windsurf, Roo, and others.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-task-master
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考