关于 moai-workflow-loop
moai-workflow-loop 是用于 Claude Code 的迭代式代码质量检查与修复技能,结合 LSP 诊断、AST-grep 扫描和测试验证,直至满足配置的完成条件。
主要功能
- 01文件编辑后的 LSP 诊断
- 02AST-grep 安全与质量扫描
- 03带完成检查的迭代修复循环
- 04Claude Code PostToolUse 与 Stop 钩子
适用场景
- 01持续修复代码,直至诊断通过
- 02检查代码安全与质量规则
- 03修复过程中运行测试和覆盖率检查
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ modu-ai/ moai-adk --skill moai-workflow-loop安装后在 Claude Code 中输入/moai-workflow-loop确认已加载。
附带文件references/examples.mdreferences/reference.md
详细介绍
Ralph Engine
自动化反馈循环系统,集成 LSP 诊断、AST-grep 安全扫描和测试验证,持续提升代码质量。
预设架构 — Ralph Engine 作为目标预设
/moai loop 技能(~/.claude/skills/moai/workflows/loop.md)将这个 Ralph engine 重新表述为构建于目标引擎之上的目标预设:扫描阶段会构建有限的问题队列,而持续迭代直至完成的决策则交由目标引擎(stop-goal)处理,并预先填入条件“队列清空 + 诊断无误”。在这种预设架构中,本 SKILL.md 文档记录了目标预设扫描所驱动的底层 LSP / AST-grep / 测试 / 覆盖率诊断机制;预设在该机制之上叠加目标引擎的组合方式,不会改变该机制。四种循环象限(基于目标的扫描、基于轮次的 /moai fix、基于时间的周期执行、主动监控 CI)是同一个目标引擎上的预设,而非独立引擎。
快速参考
核心功能:
- LSP 集成:从语言服务器获取实时诊断信息
- AST-grep 扫描:结构化代码分析与安全检查
- 反馈循环:反复修正错误,直至满足完成条件
- 钩子系统:通过 PostToolUse 和 Stop 钩子与 Claude Code 无缝集成
核心组件:
- .claude/hooks/moai/handle-post-tool.sh (PostToolUse):在 Write/Edit 操作后运行 LSP 诊断
- .claude/hooks/moai/handle-stop.sh (Stop):控制循环迭代
- ralph.yaml:配置设置
命令:
- /moai:一键完成 Plan-Run-Sync 自动化(默认)
- /moai loop:启动反馈循环
- /moai fix:执行一次自动修复
适用场景:
- 以零错误为目标实现功能
- 自动提升代码质量
- 持续集成工作流
- 错误驱动的开发模式
实现指南
架构概述
Ralph Engine 采用分层架构。用户命令(如 /moai:loop、/moai:fix 和 /moai)进入命令层。命令层调用钩子系统,其中包含用于 LSP 诊断的 PostToolUse 钩子和用于循环控制的 Stop 钩子。钩子系统连接到后端服务,包括 LSP 客户端(MoAILSPClient)、AST-grep 扫描器和测试运行器。后端服务将结果传递给完成检查,由其评估错误是否为零、测试是否通过以及覆盖率是否达标。根据完成检查的结果,系统会继续循环或完成任务。
配置
ralph.yaml 配置文件包含以下部分和设置。
在 ralph 部分,enabled 控制 Ralph 是否启用(默认值为 true)。
在 lsp 部分,auto_start 控制是否自动启动语言服务器(默认值为 true),timeout_seconds 设置连接超时时间(默认值为 30 秒),graceful_degradation 用于在 LSP 不可用时启用回退到代码检查工具(默认值为 true)。
在 ast_grep 部分,enabled 控制是否集成 AST-grep(默认值为 true),security_scan 启用安全规则检查(默认值为 true),quality_scan 启用代码质量规则检查(默认值为 true)。
在 loop 部分,max_iterations 设置最大循环次数(默认值为 10),auto_fix 控制是否自动应用修复(默认值为 false,需要确认),require_confirmation 要求在修复前获得用户批准(默认值为 true)。
在 loop 的 completion 子部分,zero_errors 要求不存在 LSP 或编译器错误(默认值为 true),zero_warnings 要求不存在警告(默认值为 false,可选),tests_pass 要求所有测试通过(默认值为 true),coverage_threshold 设置最低覆盖率百分比(默认值为 85)。
在 hooks 部分,post_tool_lsp 的 enabled 为 true(默认值),severity_threshold 为 error(默认值)。stop_loop_controller 的 enabled 默认设为 true。
钩子集成
PostToolUse Hook
PostToolUse Hook 会在 Write 和 Edit 操作后触发。调用时,Claude Code 会提供 Hook 输入,其中包含 tool_name(例如 Write)和包含 file_path 与 content 的 tool_input。
Hook 会处理诊断结果,并返回包含 hookSpecificOutput 的 Hook 输出,其中包括 hookEventName(PostToolUse)和用于描述诊断结果的 additionalContext。例如,上下文可能会报告 LSP 在 file.py 中发现 2 个错误和 3 个警告,并提供包含行号的具体错误消息。
退出代码 0 表示无需采取任何操作。退出代码 2 表示发现错误,需要关注。
循环控制器的 Stop Hook
Stop Hook 会在每次 Claude 响应后触发。Hook 会读取位于 .moai/cache/.moai_loop_state.json 的循环状态文件。此状态包含 active 状态(true 或 false)、当前迭代编号、max_iterations 限制、上一轮迭代的 last_error_count,以及完成时的 completion_reason。
Hook 会返回包含 hookSpecificOutput 的输出,其中包括 hookEventName(Stop)和用于报告循环状态的 additionalContext。例如,它可能会报告 Ralph Loop CONTINUE,当前为第 3 轮迭代(共 10 轮),有 2 个错误,并给出修复剩余错误的后续操作。
退出代码 0 表示循环已完成或处于非活动状态。退出代码 1 表示继续循环,还需要执行更多工作。
LSP 客户端用法
Go LSP 客户端已集成到 Hook 系统中。LSP 诊断会通过 PostToolUse Hook(moai hook post-tool-use)自动收集。
要获取某个文件的诊断结果,请使用文件路径异步调用 get_diagnostics 方法。
遍历返回的每个诊断对象来处理诊断结果。检查 severity 属性是否为 DiagnosticSeverity.ERROR,以识别错误。通过 diag.range.start.line 获取行号,通过 diag.message 获取消息。
完成条件
所有启用的条件都满足时,循环即完成。
zero_errors 条件(默认值为 true)要求不存在 LSP 或编译器错误。
zero_warnings 条件(默认值为 false)可选地要求不存在警告。
tests_pass 条件(默认值为 true)要求所有测试通过。
coverage_threshold 条件(默认值为 85)要求达到最低覆盖率百分比。
高级模式
自定义完成条件
通过实现检查函数,为循环控制器扩展自定义条件。例如,创建一个函数来统计代码库中的 TODO 注释,并仅在数量为零时返回 true。
与 CI/CD 集成
要集成 GitHub Actions,请创建一个工作流步骤,使用 /moai:loop 命令和 max-iterations 标志运行 Claude。将 MOAI_LOOP_ACTIVE 环境变量设为 true,以启用循环模式。
优雅降级
LSP 不可用时,系统会回退到基于 linter 的诊断,使用 ruff 或 eslint 等工具;然后回退到编译器错误检测,最后回退到测试失败检测。
故障排除
循环未启动
检查配置中的 ralph.enabled 是否设为 true。确认未设置 MOAI_DISABLE_LOOP_CONTROLLER 环境变量。确保状态文件所在位置可写。
缺少 LSP 诊断
检查 .moai/config/sections/lsp.yaml 中的 LSP 服务配置。确认已为你的语言安装语言服务器。检查是否未设置 MOAI_DISABLE_LSP_DIAGNOSTIC 环境变量。
循环卡住
检查 max_iterations 设置,确保允许足够的迭代次数。检查完成条件,确认这些条件可以实现。发送任意消息以中断循环,或删除状态文件(.moai/cache/.moai_loop_state.json)以重置。
适配良好的工具
技能:
- moai-foundation-quality:TRUST 5 验证
moai ast-grep/moai ast-edit:安全扫描模式与重写- moai-workflow-testing:DDD 集成
.claude/rules/moai/languages/python.md:Python 特定模式(通过 paths frontmatter 自动加载).claude/rules/moai/languages/typescript.md:TypeScript 模式(通过 paths frontmatter 自动加载)
Agent:
- manager-develop:DDD 实现
- manager-develop (cycle_type=autofix):复杂调试与修复方案(前 manager-quality 调试角色)
- /moai gate 技能或 sync-phase-quality-gate.sh Stop 钩子:质量验证(前 manager-quality gate 角色)
命令:
- /moai:run:DDD 实现
- /moai:sync:文档同步
参考
环境变量
设置 MOAI_DISABLE_LSP_DIAGNOSTIC 后会禁用 LSP 钩子。
设置 MOAI_DISABLE_LOOP_CONTROLLER 后会禁用循环钩子。
MOAI_LOOP_ACTIVE 表示循环当前是否处于活动状态。
MOAI_LOOP_ITERATION 包含当前迭代编号。
CLAUDE_PROJECT_DIR 包含项目根目录路径。
文件位置
配置存储在 .moai/config/sections/ralph.yaml 中。
循环状态存储在 .moai/cache/.moai_loop_state.json 中。
LSP 钩子位于 .claude/hooks/moai/handle-post-tool.sh。
循环钩子位于 .claude/hooks/moai/handle-stop.sh。
支持的语言
LSP 诊断适用于 MoAI 支持的全部 16 种语言:C++、C#、Elixir、Flutter、Go、Java、JavaScript、Kotlin、PHP、Python、R、Ruby、Rust、Scala、Swift 和 TypeScript。Ralph 通过标记文件(例如,Go 使用 go.mod、Python 使用 pyproject.toml、TypeScript 使用 tsconfig.json、Rust 使用 Cargo.toml、Flutter 使用 pubspec.yaml)检测项目语言,并按需启动相应的语言服务器。用户只需安装项目实际需要的服务器;缺少服务器时会发出警告并跳过,同时提示安装方式,不会导致硬性失败。完整的语言服务器到二进制文件映射表请参见 references/reference.md。根据 .claude/rules/moai/development/coding-standards.md § Language Policy(16 种语言中立性约定),任何语言都不会获得优先权。
版本:1.2.0 状态:Active 集成:Claude Code Hooks、LSP Protocol、AST-grep 技能名称:moai-workflow-loop(前称 moai-ralph)
常见托词
| 托词 | 实际情况 |
|---|---|
| “我会在循环结束后手动修复剩余错误。” | 后续手动修复意味着循环没有完成任务。遗留错误就是已交付的错误。 |
| “循环达到最大迭代次数了,剩余问题都不重要。” | 迭代次数限制是为了控制成本,不是质量保证。请报告尚未解决的问题列表。 |
| “LSP 警告不是真正的错误。” | 跨迭代持续存在的警告表明有结构性问题被忽略了。请处理这些问题,或明确抑制它们。 |
| “我修复了根本原因,其他诊断结果会自行消失。” | 级联修复并无保证。请再次运行诊断,并验证每项问题都已解决。 |
| “ast-grep 规则对这个代码库来说太严格了。” | 规则体现了项目约定。请说明理由后禁用特定规则,不要跳过整个扫描。 |
| “这次迭代没有进展,我应该停下来。” | 停滞 2 次迭代后应升级处理,而不是悄然退出。请遵循升级处理流程。 |
500 行规则:涉及超过 500 行的更改,应单独进行一次循环迭代并针对性地运行诊断。大规模更改会掩盖个别回归。
危险信号
- 循环退出时仍有未解决的错误,且没有停滞报告
- 同一诊断结果连续多个迭代出现,却没有得到处理
- 完全禁用了 ast-grep 扫描,而非禁用特定规则
- 循环开始迭代时未采集 LSP 基线
- 循环迭代之间进行了手动代码更改,却没有重新运行诊断
验证
- 进度日志中记录了每次迭代及错误数量变化
- 最后一次迭代显示错误数量为零,或包含明确的停滞报告
- 在循环开始时捕获了 LSP 基线(显示诊断数量)
- 循环期间至少运行过一次 ast-grep 扫描(显示输出)
- 记录了未解决项及无法修复的原因
- 连续迭代之间的诊断数量没有增加