TDD工作流编排器

一个深度集成的、代理驱动的TDD工作流引擎,用于Pi编码代理,提供本地或云LLMs的手术式文件编辑和自我纠正开发循环。

  • by sdekok
  • TypeScript
  • 本地部署
  • 3 星标
  • 更新于
在 GitHub 查看 sdekok/LocalLLM-TDD-Orchestrator

使用说明

TDD代理工作流编排器(Pi Native)

一个深度集成的代理TDD工作流引擎,用于 Pi编码代理它用原生Pi子代理会话取代了严格的基于JSON的编排,使用本地或云LLM提供手术文件编辑和自校正开发循环。

运作原理

Pi says "/tdd implement JWT auth"
         │
         ▼
   ┌─────────────┐
   │   Planner    │ ← Web search for best practices
   │  (sub-agent) │
   └──────┬──────┘
          │ Subtasks
          ▼
   ┌─────────────┐     ┌───────────────────┐
   │ Implementer  │ ──▶ │  Quality Gates     │
   │ (Sub-Agent)  │     │  lens (Type/AST)   │
   └──────┬──────┘     │  build → tests     │
          │             │  → lint            │
          │             │  + test metrics     │
          │             │  + coverage         │
          │             └──────┬────────────┘
          │ (Read/Edit)        │ Pass/Fail
          ▼                    ▼
   ┌─────────────┐     Algorithm decides:
   │  Reviewer    │     merge or retry
   │ (Sub-Agent)  │     (not the AI)
   └─────────────┘

编排器诞生了 短暂的无头子代理会话 用于规划、实施和审查。这些代理使用Pi的原生 read, write, edit,以及 bash 工具直接放在文件系统上。任务计划器(即时分解和每个工作项细化)作为 只读 agent——在提出子任务之前,它使用read/grep/lent/context模式工具探索代码库,像其他agent一样流式传输到live.log,并且不能修改文件。

  • 自愈:如果质量门失败,执行器将回滚更改并将确定性故障日志注入到 下一个 尝试的系统提示。
  • Git沙盒:每个子任务都在一个独立的git分支中运行。只有经过验证和审查的代码才会被合并。
  • 确定性质量:虽然实现是代理的,但门(TSC、Vitest等)是100%确定的。

快速开始

1.先决条件

  • Node.js 22.19+ (要求 @earendil-works/pi-coding-agent)
  • llama.cpp 跑步(与 Pi-llama.cpp提供者 建议用于自动模型设置)

2.安装并注册

npm install
npm run build

# Register the extension with Pi
pi install local:.

3.配置模型

从任何Pi会话中运行交互式设置:

/setup

这将从llama.cpp中发现可用的模型,允许您将每个模型分配给代理角色,并保存配置。使用 --global 保存为系统范围的默认值(~/.config/tdd-workflow/models.config.json)这适用于所有项目。

4.启动工作流

在任何项目中,使用斜线命令:

  • 设置: /setup --交互式配置模型路由
  • 计划: /plan "Build a secure login system" --分解为Epics/WorkItems。默认为 追加模式:现有史诗 WorkItems/ 被保存并用作上下文。子命令: /plan list, /plan show <epic>, /plan revise [feedback].标志: --replace (从epic-01改写), --from-epic <id> (延伸一个特定的史诗), --brownfield (首先进行代码库探索)。
  • 实施: /tdd 1 --从加载Epic 1 WorkItems/ 并执行
  • 从失败中恢复: /tdd 1 retry --从头开始重试失败的任务; /tdd 1 resume --在保留审阅者反馈的情况下重试; /tdd 1 continue --跳过失败并继续
  • 暂停/停止/恢复 (工作流程中期):
    • /tdd:pause --完成当前代理回合,然后停止。WIP分支+反馈+尝试被保留。
    • /tdd:stop --立即中止正在运行的代理,回滚当前任务,将其重置为挂起。Repo看起来任务从未运行过。
    • /tdd:resume --从暂停的工作流中恢复。
  • 状态: /tdd:status --当前工作流的只读快照:每个任务的进度、尝试次数、当前阶段和最新审阅者反馈。
  • 课程: /tdd:lessons --列出汽车学到的经验教训; /tdd:lessons learn --从现有的反馈日志中追溯性地提取经验教训; /tdd:lessons forget <id> --删除一个。
  • 清理: /tdd:project-cleanup --扫描所有质量门,然后运行TDD工作流来修复每个预先存在的故障
  • 运行测试: /tdd:test --运行项目的测试套件并报告失败
  • 研究: /research "Best practices for React state 2026" --deepweb研究代理
  • 分析: /analyze --建筑蓝图

Tab 补全:上面的每个斜线命令都支持Pi编辑器中的参数自动补全。按命令名称后的Tab键以获取建议——史诗ID /tdd <epic> 和 /plan show <epic>,之后的工作项ID task,git分支后 /review branch,研究会议之后 /research --resume等等。

模型配置

模型路由由以下因素驱动 models.config.json系统检查两个位置并将其合并,项目配置在任何冲突中获胜:

地点 目的
~/.config/tdd-workflow/models.config.json 全系统默认值(所有项目)
<project>/models.config.json 项目特定覆盖

创建或更新任一文件的最简单方法是通过 /setup 在Pi。您也可以直接编辑JSON。

最小配置形状:

{
  "models": {
    "my-fast-model": {
      "name": "Qwen3 30B-A3B",
      "ggufFilename": "qwen3-30b-a3b-q4.gguf",
      "provider": "local",
      "contextWindow": 40960,
      "maxOutputTokens": 8192,
      "architecture": "moe",
      "speed": "fast",
      "enableThinking": false
    },
    "my-thinking-model": {
      "name": "Gemma 4 27B",
      "ggufFilename": "gemma-4-27b-q4.gguf",
      "provider": "local",
      "contextWindow": 128000,
      "maxOutputTokens": 8192,
      "architecture": "dense",
      "speed": "slow",
      "enableThinking": true
    }
  },
  "routing": {
    "plan":         "my-thinking-model",
    "project-plan": "my-thinking-model",
    "implement":    "my-fast-model",
    "review":       "my-thinking-model",
    "arbitrate":    "my-thinking-model",
    "research":     "my-fast-model"
  }
}

路由密钥 除了你实际使用的那些之外,其他都是可选的。任何未明确路由的角色都将回退到 plan 模型。 /setup 配置 plan, project-plan, implement, review,以及 research --您可以添加 arbitrate 如果您希望死锁打破仲裁器与计划器位于不同的模型上,请手动操作。

sessionRefreshTokens (可选) 控制何时将实现者的长时间运行的会话替换为新会话:一旦会话 实际的 提示大小(提供者报告的输入+缓存读取令牌)超过了这个阈值。默认为 contextWindow / 2。将其设置为模型的长上下文质量开始下降的点,通常远低于广告窗口。遗产 sessionRefreshAfter round cadence现在仅在提供者报告没有令牌使用时适用。旧的思维块也是会话上下文修剪器回收的第一件事,因为过时的推理对保留的思维模型几乎没有正向价值。

enableThinking 告诉指挥者激活Pi的推理模式(setThinkingLevel('medium'))并从多回合消息历史中去除思维障碍,以保持高质量。推理令牌注入是在llama.cpp/chat模板级别处理的——不需要插件端的提示突变。

云提供商 也得到了支持。API密钥必须通过环境变量提供-从不硬编码:

{
  "modelId": "anthropic/claude-sonnet-4",
  "provider": "openrouter",
  "apiKeyEnvVar": "OPENROUTER_API_KEY"
}

models.config.json 和 models.config.local.json 列在 .gitignore 以防止意外的秘密犯罪。

实施者→ 审阅者交接

实现者和审阅者是通过结构化工件而不是共享内存进行通信的独立代理:

  1. 实现注意事项 --在会话结束时,实现者写道 .tdd-workflow/implementation-notes.md 解释设计决策、权衡以及它故意留下的任何预先存在的问题。
  2. Git 差异 --在实现者完成之后,执行者将捕获与其基础相对应的完整工作项分支差异(git diff <base>...HEAD,加上任何未提交的更改),并将其与更改的文件列表和每次提交日志一起直接注入到审阅者的提示符中。在简历中,执行者首先检查工作项分支,因此审阅者总是检查已提交的工作,而不是基础分支。
  3. 范围审查 --审阅者被指示将diff视为其主要事实来源,只有在diff本身不足以评估类型或测试路径时才读取其他文件。
  4. 简明修复检查表 --当审阅者拒绝时,完整的反馈将写入 .tdd-workflow/logs/feedback-history-<task>.md 执行者会收到一份简短的行动项目清单(摘自审阅者编号的问题,不包括审阅者指出的任何“非问题”)。这使得每个重试提示都保持小而集中,而完整的细节则保留在磁盘上,以便代理在需要时读取。

这意味着审阅者总是确切地知道发生了什么变化以及为什么——它不需要通过探索文件系统来发现变化。

安全和失控保护

守卫 它捕获了什么 行为
最大尝试次数 (5/任务) 持续失败 放弃前触发中立仲裁器
仲裁者 (尝试5次后) 实施者/审阅者死锁 批准、最多额外授予3轮或升级给您
输出相似性 (>90%) 代理人陷入困境 在浪费审稿人的时间之前,立即保释
执行器超时 (60分钟) 挂起执行者会话 扔进接球区;下一次尝试重新开始
审阅者超时 (60分钟) 暂停评审环节 相同——不受执行者预算限制
仲裁员超时 (20分钟) 暂停仲裁会话 默认升级
断路器 (连续3次失败) 系统问题 停止整个工作流程

每个代理通过以下方式独立执行超时 Promise.race当一个任务用尽所有尝试时 中立仲裁者 审查最终差异、质量门状态和审查员反馈,然后决定:

  • 批准 --QA通过,审核人员过于严格;按原样合并
  • 继续N --额外资助1-3轮实施
  • 升级 --将情况发布到Pi聊天,等待您的回复 approve, continue 1–3,或 stop

当任务最终失败时,工作流停止并发布一条聊天消息,其中包含分支名称、状态文件位置和确切的恢复命令。故障的分支会被保留以供检查,不会自动清理任何内容。

自学课程

编排者从自己的审查周期中学习。在任何需要反馈的任务之后,提取步骤(使用 arbitrate 模型)将审阅者/关卡反馈提炼为简短、通用、强制性的规则——例如。 “将多语句Postgres交易打包到一个专用客户端上”.生活中的教训 .tdd-workflow/lessons.json (项目)与 ~/.config/tdd-workflow/lessons.json (全球,项目获胜)。

  • 注入:观察到的经验教训 2+不同任务 (或标记 "confirmed": true)根据任务描述,按标签匹配、出现次数和最近度进行评分;前10个被添加到实现者的第一个转弯提示中。一次性观察结果会被存储,但不会被注入——单个审阅者不应该成为政策。
  • 强化:在a中再次出现 新 任务打乱了课程的进度;同一任务报告两次则不会。重新运行提取是幂等的。
  • 逆向学习: /tdd:lessons learn [N] 扫描最新的N(默认值30) feedback-history-*.md 文件在 .tdd-workflow/logs/ 并合并它所发现的内容。
  • 策展: /tdd:lessons 列出所有内容(🟢 = 目前注射); /tdd:lessons forget <id> 删除了一条糟糕的规则。手动编辑JSON以进行设置 "confirmed": true 关于应该始终适用的规则。
  • 这家店最多100节课;最弱的(出现次数最少,出现时间最长)首先被修剪,确认的课程总是能存活下来。

回报指标是每个任务的尝试次数:随着存储收敛到模型的习惯性失败模式,首次尝试的批准应该会增加——每个任务的使用情况摘要会给你前后的数字。

先前存在的故障(基线)

在任何代理运行之前,编排器都会捕获 基线 每个阻塞门都有一个,这样已经被破坏的问题就不会被归咎于或阻塞实施者。只有执行者失败 介绍 被视为回归;预先存在的报告一次,然后忽略。

基线为 缓存 在 .tdd-workflow/cache/gate-baseline.json,按HEAD sha+工作树状态键控:使用未更改的树启动或恢复工作流会完全跳过整个工作区扫描(在monorepo上通常需要几分钟)。任何提交、合并或未提交的编辑都会使其无效;条目在24小时后过期。禁用 TDD_BASELINE_CACHE=0基线的覆盖率运行(最慢的部分——仪表化的完整测试通过)现在只在以下情况下发生 tddConfig.coverageThresholds 已配置;没有阈值,最终的史诗级审查只是报告当前的覆盖范围,而没有基线增量。

基线运行在 完整工作空间范围。在Nx monorepo中,每个任务门使用 nx affected (快速——仅涉及差异所涉及的项目),但基线使用 nx run-many (每个项目)。这很重要,因为 nx affected 与最初的空差异相反 无 并记录没有预先存在的债务——那么第一个涉及项目的任务将使整个项目“受到影响”,并显示其预先存在的棉绒警告,就像任务引入了它们一样。捕获整个工作空间的基线记录了实际的先前状态,因此每个任务 affected 故障被正确地识别为预先存在并被掩盖。

暂停和停止工作流

编排器在后台运行一次 /tdd 被调用,但您可以随时中断它的聊天:

操作 它的作用 当前任务最终为 分支 反馈/尝试
/tdd:pause 完成当前代理回合,然后停止工作流。 paused 保存 保存
/tdd:stop 立即中止正在运行的代理,将任务分支回滚到基。 pending (重置) 回滚 清除
/tdd:resume 恢复暂停的工作流程——拾取暂停的任务,其WIP分支+反馈保持不变。 pending → 运行到完成 重用 保留

何时使用哪个:

  • 暂停 当您需要退出、重新启动或上下文切换,并希望稍后在代理所在的位置继续时。保留任务的进度和审阅者的反馈。
  • 停止 当你意识到当前的任务毫无进展,你想要一个全新的开始——例如,计划员对工作范围界定错误,或者你想手工编辑和重新计划。史诗中的其他任务未被触及。

/tdd:resume 拿起任何 paused 自动任务。您还可以使用现有的 /tdd N resume / /tdd N retry / /tdd N continue 子命令——它们与暂停/停止一起工作。

项目清理

/tdd:project-cleanup 在任何代理启动之前运行质量门,总结聊天中的每个失败门,然后将结构化的清理简报交给标准TDD执行者。即时计划器将“修复这些特定故障”分解为每个门的子任务,每个子任务都要经过正常的实施→ 审查→ 合并循环。

  • 实现者被指示只修复其正在修改的文件中的故障,因此清理保持作用域,不会导致无关的漂移。
  • 在清理过程中始终会收集覆盖率快照(即使没有 coverageThresholds)因此,规划者可以看到覆盖率低的地方,并在有意义的情况下添加测试改进子任务。除非明确配置了阈值,否则覆盖率数字永远不会阻止清理工作流程。
  • 全栅极输出(可能较大)被写入 .tdd-workflow/logs/gate-report-<timestamp>.log。清理简报嵌入了指向此文件的截断摘要;实现者代理可以在需要更多上下文时读取完整日志。
  • 描述中提到“覆盖率”或“添加测试”的子任务会自动告诉实现者使用覆盖率命令进行验证,并在 DONE: 消息。

规划工作流程(/plan)

/plan 将请求分解为Epics和WorkItems。它在一个新的子代理会话中分两个阶段运行:第一阶段生成史诗概述,第二阶段填充每个史诗的工作项(每个史诗一个短暂的会话)。

追加与替换(模式):

附加(默认) 替换(--replace)
现有史诗 WorkItems/ 保存;作为上下文出现在规划者面前,并指示不要重新定义它们 规划者忽略了
新的史诗编号 从开始 maxIndex + 1 (没有重击) 从开始 01 (用相同的索引抨击之前的史诗)
匹配slug 更新现有 epic-NN-slug.md 到位 写一篇新文章 epic-01-slug.md 无论之前的状态如何
_overview.md 之前的总结+决策与新的决策合并,删除 从头开始重写

使用 附加 在扩展具有新范围的计划时;使用 --replace 只有当你真的想放弃现有的计划时。

子命令:

  • /plan list --打印现有史诗及其工作项计数。
  • /plan show <epic> --发布一部史诗的完整降价以聊天。接受史诗ID、slug或文件名。
  • /plan revise [feedback] --以追加模式重新运行计划,从中获取之前的请求 .tdd-workflow/planning/_request.json 并整合您的反馈。当您想要更改时,您不再需要重新键入原始请求。如果不内联传递反馈,命令会提示输入。

标志(可与子命令自由组合):

  • --from-epic <id> --将现有史诗的标记作为规划者上下文内联,以便新计划扩展该特定史诗。
  • --brownfield --在提出史诗之前,强制规划者探索代码库。在添加到成熟项目时很有用。
  • --replace --选择退出附加模式(见上表)。

迭代反馈回路:

当您在计划审核提示下给出反馈时,它现在保存到 .tdd-workflow/planning/_pending_feedback.txt.快跑 /plan revise 计划器将从它停止的地方开始——在追加模式下,原始请求加上您的修订请求——而不是从头开始。

多语言支持

编排器包括一个本机代码分析器,支持:

  • Types/JavaScript:通过以下方式进行完整的AST分析 ts-morph.
  • C:通过Roslyn sidecar进行分析(需要.NET 10 SDK)。
  • C:AST分析 tree-sitter.

提交消息

每次尝试的实施者提交都包括质量门结果和测试/覆盖率指标:

TDD [Attempt 1]: Create JWT token generation

---
Attempt: 1

Quality Gates:
  ✅ build (blocking)
  ✅ tests (blocking)
  ✅ coverage (blocking)
  ✅ lint (blocking)

Tests: 47/47 passed
Coverage: 87.3% lines, 72.1% branches, 91.0% functions
LLM Usage: 36,520 in / 8,522 out tokens · 3 calls (implementer 2, reviewer 1) · 12m03s

发展

npm run test          # Run unit tests (vitest)
npm run test:watch    # Run tests in watch mode
npm run test:coverage # Run tests with coverage
npm run build         # Compile TypeScript + bundle
npm run build:csharp  # Build the Roslyn C# analyzer (requires .NET 10 SDK)
npm run build:all     # Full build (csharp + typescript)
npm run deploylocal   # Symlink into ~/.pi/extensions for live development

项目配置(tddConfig 在 package.json)

可选设置可以放置在 tddConfig 项目的关键 package.json:

"tddConfig": {
  "coverageThresholds": {
    "lines": 80,
    "functions": 80,
    "branches": 70
  },
  "fileSafetyAllowlist": ["fixtures/", "custom-dir/"]
}
密钥 默认值 描述
coverageThresholds (未设置) 选择加入。 当存在时,覆盖门会阻塞并强制执行这些最小值。完全省略此键以禁用阻塞覆盖门(仍会收集覆盖指标以进行报告)。支持的阈值: lines, functions, branches, statements.
fileSafetyAllowlist [] 不应标记文件安全门的额外路径前缀。请参阅下面的内置前缀。

内置文件安全前缀 (始终允许--无需配置): src/, tests/, test/, __tests__/, e2e/, lib/, libs/, apps/, packages/, docs/, scripts/, config/, public/, static/, assets/, styles/, schemas/, migrations/, prisma/, coverage/, .github/, .vscode/, .pi-lens/, .tdd-workflow/

内置文件安全模式 (在回购根目录匹配):

  • 包裹清单/锁文件(package.json, pnpm-lock.yaml, yarn.lock, bun.lockb, pnpm-workspace.yaml)
  • TS/lint/格式化器配置(tsconfig*.json, .eslintrc*, eslint.config.*, vitest.config.*, jest.config.*, prettier.config.*, .prettierrc*)
  • 根点文件(.gitignore, .gitattributes, .editorconfig, .nvmrc, .dockerignore, .env.example等等)
  • 框架/单回购/捆绑商配置(turbo.json, nx.json, project.json, vite.config.*, next.config.*, tailwind.config.*等等)
  • Docker(Dockerfile*, docker-compose*.yml, .docker/)
  • 根文档(README*, CHANGELOG*, LICENSE*, CONTRIBUTING*,以及任何 *.md/*.mdx 根)

环境变量

变量 默认值 描述
LLAMA_CPP_URL http://localhost:8080/v1 llama.cpp服务器URL
SEARXNG_URL http://localhost:8888 SearXNG搜索URL
OPENROUTER_API_KEY - OpenRouter模型的API密钥
OPENAI_API_KEY - OpenAI模型的API密钥
TDD_WORKFLOW_CONFIG_DIR -- 覆盖配置文件搜索目录
LENS_FAIL_POLICY fail-closed fail-open 碰撞时跳过镜头门; fail-closed 将碰撞视为失败
TDD_SLOT_RECOVERY_MS 500 子代理处理后,在启动下一个代理之前等待毫秒。自Pi SDK 0.78以来 dispose() hard中止飞行中的请求,因此这是一个安全裕度;为那些在中止请求上徘徊的服务器提高它
TDD_MCP_STARTUP_MS 5000 会话创建后等待MCP服务器(上下文模式,searxng)注册工具的毫秒数
TDD_BASELINE_CACHE 1 设置为 0 禁用质量门基线缓存,并在每次启动/恢复时重新运行完整的工作区扫描

碰撞取证

每个子代理会话(实现者、审阅者、仲裁者等)都会将其完整记录保存到 .tdd-workflow/sessions/<timestamp>_<role>-<id>.jsonl 当它运行时。如果会话崩溃、挂起或进程在任务执行过程中终止,则记录会一直保存到最后一条模型消息——打开它,查看代理看到和说了什么。7天后文件会自动修剪;从未收到模型响应的会话不会留下任何文件。该目录将自动从git中排除。

Pi版本说明

需要 @earendil-works/pi-coding-agent ≥ 0.79:

  • 项目信托 (Pi 0.79+):子代理会话明确信任它们运行的项目(编排器只在你指向的地方运行)。对于 主机 Pi会话,在repo中首次运行时接受信任提示——或设置 defaultProjectTrust 在 ~/.pi/agent/settings.json --否则,将无法加载项目本地扩展(上下文模式、pi-less)。
  • 提供程序重试:在编排器的模型无法访问的快速故障开始之前,SDK会重试瞬态提供程序故障(例如,在工作流中期重新启动推理服务器) retry.provider.maxRetries 在 ~/.pi/agent/settings.json.
  • 缓存命中可见性:Pi的交互式页脚显示了最新的提示缓存命中率(CH)--对于确认TDD运行期间保留的思维前缀缓存是否正常工作非常有用。

诊断

每个子代理会话都会向标准插件日志发送每个LLM调用的遥测数据:

[SUBAGENT TELEMETRY implement] status=200 latency=1843ms rate-remaining=99
[SUBAGENT TELEMETRY review]    status=429 latency=420ms  rate-remaining=0

status HTTP延迟来自SDK after_provider_response 钩;任何非2xx响应都会记录为警告,因此无需仪表板即可显示速率限制和提供程序错误。这 taskType 标记在grepping日志时区分实现者/审阅者/计划者/仲裁者/研究调用。

令牌/时间会计:每个任务都会累积每个角色的令牌使用情况(输入/输出/缓存)和墙时间。摘要将附加到每次尝试的提交消息中(LLM Usage: 预告片),在任务完成或失败时发布到聊天中(📊 WI-x usage: …),并在史诗结束时发布整个工作流程的总计。使用它来发现哪些任务消耗的预算最多,并相应地调整模型路由。