如何用 ECC 开发 F# 项目并做函数式代码审查?
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
在 Claude Code 里开发 F# 项目时,通常要同时解决两件事:代码要符合函数式惯例(不可变、模式匹配、Option/Result而不是 null 和异常),以及改完代码后有一道可重复的审查关卡。ECC 提供的是一套组合方案:rules/fsharp规则包负责在编码阶段约束.fs/.fsx文件,fsharp-testing技能给出测试栈和运行方式,fsharp-reviewer子代理负责在改动完成后按固定优先级做函数式代码审查,并给出 Approve / Warning / Block 结论。
准备条件
- 已安装并登录
claudeCLI,ECC 插件已装入。可用下面的命令确认插件状态:
/plugin list ecc@ecc- 注意:Claude Code 插件机制无法随插件分发
rules,所以 F# 规则包必须手动复制到规则目录,这一步无法省略(参见 README 中 "Claude Code plugins cannot distributerules" 的说明)。 - 审查与测试命令依赖本机可用的
dotnetCLI;格式检查依赖fantomas(文档说明“if available”,即没有时可跳过该检查)。
第一步:安装 F# 规则包
规则按“common 通用层 + 语言层”组织,F# 语言层扩展 common 层,并通过../common/相对路径引用通用规则。因此必须复制整个目录,不能用/*把文件拍平,否则相对引用会断掉、同名文件会互相覆盖(见 rules/README.md)。
用户级安装(对所有 Claude Code 会话生效):
mkdir -p ~/.claude/rules/ecc cp -R rules/common ~/.claude/rules/ecc/ cp -R rules/fsharp ~/.claude/rules/ecc/项目级安装(只对当前仓库生效,规则放在项目根目录的.claude/rules/ecc下):
mkdir -p .claude/rules/ecc cp -R rules/common .claude/rules/ecc/ cp -R rules/fsharp .claude/rules/ecc/注意这里复制的是rules/fsharp整个目录而不是其中的单个文件,原因同上。README 建议规则从common+ 一个你实际使用的语言包起步,因为规则是始终加载的上下文,装多了会占用 token。fsharp不在./install.sh文档示例列出的语言清单里,手动复制就是文档给出的安装方式。
第二步:按 F# 规则写代码
rules/fsharp下每个文件的 frontmatter 都声明了适用路径**/*.fs、**/*.fsx(testing.md等还包含**/*.fsproj),即只要你编辑的是 F# 文件,这些规则就会作为始终加载的上下文生效。核心约束如下:
类型与不变性(coding-style.md):
- 默认不可变,
mutable只在性能有正当理由时使用; - 领域建模用可辨识联合(discriminated union)而不是类层次,数据用 record;
- 用单例 union 包装原始类型做类型安全边界,例如
type EmailAddress = EmailAddress of string; - 用
Option代替 null,用Result处理可能失败的操作,优先|>管道而不是 if/else 链。
错误处理(patterns.md):预期失败走Result<'T, 'TError>的铁路式编程,而不是抛异常;顺序可失败操作可以用result { }计算表达式:
let placeOrder request = result { let! validated = validateOrder request let! inventory = checkInventory validated.Items let! order = createOrder validated inventory return order }系统边界验证(security.md):在应用边界用智能构造器拒绝非法输入,用private单例 union 保证只能从构造器进入类型:
type ValidatedEmail = private ValidatedEmail of string module ValidatedEmail = let create (input: string) = if System.Text.RegularExpressions.Regex.IsMatch(input, @"^[^@]+@[^@]+\.[^@]+$") then Ok(ValidatedEmail input) else Error "Invalid email address" let value (ValidatedEmail v) = v同文件还要求:数据库访问必须用参数化查询(Dapper/EF Core 示例用@customerId参数占位),API key、连接串等秘密不得硬编码,本地开发用 user secrets,生产用 secret manager。
open 声明顺序(coding-style.md):open语句分四组、组间空行分隔、组内按字典序排序:System.*→Microsoft.*→ 第三方命名空间 → 项目自身命名空间。
第三步:组织并运行测试
测试栈由 fsharp-testing 技能 与 rules/fsharp/testing.md 共同定义:
| 工具 | 用途 |
|---|---|
| xUnit | 测试框架 |
| FsUnit.xUnit | F# 风格断言(should equal) |
| Unquote | 基于 F# quotation 的断言,失败信息展示完整表达式 |
| FsCheck.xUnit | 属性测试 |
| NSubstitute | mock .NET 依赖 |
| WebApplicationFactory | ASP.NET Core 集成测试 |
目录约定是测试镜像src/结构放在tests/下,并区分 Unit / Integration / Properties / Helpers。一个用 Unquote 断言Result的最小单测(文档示例):
open Xunit open Swensen.Unquote [<Fact>] let ``PlaceOrder returns success when request is valid`` () = let request = { CustomerId = "cust-123"; Items = [ validItem ] } let result = OrderService.placeOrder request test <@ Result.isOk result @>运行与验证命令(tests/MyApp.Tests/是文档示例中的测试项目路径,替换为你的测试项目):
dotnet build # 编译检查 dotnet test # 运行全部测试 dotnet test --filter "FullyQualifiedName~OrderService" # 按测试名过滤 dotnet test --collect:"XPlat Code Coverage" # 收集覆盖率 dotnet watch test --project tests/MyApp.Tests/ # 开发期 watch 模式dotnet test --collect:"XPlat Code Coverage"会额外收集覆盖率报告,运行时间会比普通dotnet test长;文档建议的行覆盖目标是 80%+,重点放在领域逻辑、验证、认证和失败路径上。
可选:用 PostToolUse 钩子自动把关
rules/fsharp/hooks.md 建议在~/.claude/settings.json中配置 PostToolUse 钩子(会修改你的用户级 Claude Code 配置):
- fantomas:编辑 F# 文件后自动格式化;
- dotnet build:每次编辑后验证解决方案仍可编译;
- dotnet test --no-build:行为变更后重跑最近相关的测试项目。
Stop 钩子则用于:大范围 F# 改动结束会话前跑一次最终dotnet build,以及在修改过appsettings*.json时给出警告,避免把秘密提交进仓库。不配置钩子也不影响后面的人工审查流程,钩子只是把编译与格式检查前移到每次编辑之后。
第四步:调用 fsharp-reviewer 做函数式代码审查
README 的 agent 对照表明确:F# 代码审查没有对应的 slash 命令,需要直接调用fsharp-reviewer子代理。其完整定义在 agents/fsharp-reviewer.md,frontmatter 声明工具为Read, Grep, Glob, Bash,模型为sonnet,描述要求“Use for all F# code changes. MUST BE USED for F# projects”。
被调用后,代理会按固定顺序动作:
git diff -- '*.fs' '*.fsx' # 查看本次 F# 文件改动 dotnet build # 编译检查 fantomas --check . # 格式检查(可用时执行)然后只审查本次修改过的.fs/.fsx文件,按以下优先级分级:
- CRITICAL(安全):SQL 注入(查询字符串拼接)、命令注入(
Process.Start未验证输入)、路径穿越、不安全反序列化(BinaryFormatter)、硬编码秘密、CSRF/XSS; - CRITICAL(错误处理):吞掉异常(
with _ -> ())、IDisposable未用use/use!释放、阻塞式 async(.Result、.Wait()、GetAwaiter().GetResult(),应改用let!/do!)、库代码中的裸failwith; - HIGH(函数式惯例):领域逻辑中的
mutable/ref、不完整的模式匹配(含隐藏新 union case 的_兜底)、可用List.map/Seq.filter/Array.fold表达的命令式循环、用 null 代替Option<'T>、能用模块 + 函数 + record 解决的类设计; - HIGH(类型安全):原始字符串/整数承载领域概念(应改单例 DU)、边界缺少验证(应用智能构造器)、无类型测试的
:?>下转型、obj装箱; - HIGH(代码质量):超过 40 行的函数、超过 3 层的嵌套、缺少
[<RequireQualifiedAccess>]、未使用的open; - MEDIUM(性能):热路径上重复求值的懒
Seq(应Seq.toList/Seq.toArray物化)、循环内字符串拼接(应StringBuilder或String.concat)、值类型经obj传递造成的装箱、EF Core 循环中的 N+1 查询; - MEDIUM(惯例):命名(函数/值 camelCase,类型/模块/DU case PascalCase)、过长的管道链拆成命名中间绑定、
task { task { } }嵌套压平为let!。
如何解读审查输出
代理按固定格式输出每条发现:
[SEVERITY] Issue title File: path/to/File.fs:42 Issue: Description Fix: What to change判定标准是明确的三分支:
- Approve:没有 CRITICAL 或 HIGH 问题;
- Warning:只有 MEDIUM 问题,可谨慎合并;
- Block:发现 CRITICAL 或 HIGH 问题,需要修完再提。
对 ASP.NET Core、EF Core、Fable 项目还有框架专项检查(Giraffe/Saturn handler 与中间件顺序、EF Core 迁移安全与AsNoTracking、Fable 的 Elmish 架构),只在你使用这些框架时相关。
限制与下一步
- 规则是始终加载的上下文,
rules/fsharp与rules/common合计只有 10 个文件,但 README 建议只装common+ 一个实际语言包,不要在无需要求下叠加更多 pack。 - 当语言规则与 common 规则冲突时,语言规则优先(specific overrides general),这是 rules/README.md 声明的覆盖规则。
- 审查聚焦于本次 diff 中的
.fs/.fsx改动;如果fantomas未安装,格式检查会被跳过,审查只剩编译检查与人工规则核对。 - 更深入的 .NET 模式与测试参考,代理文档指向
dotnet-patterns与fsharp-testing两个技能,仓库内可直接阅读 skills/fsharp-testing/SKILL.md。
完成一轮的判定标准就是上面三条可核对的结果:dotnet build通过、dotnet test(可按需带覆盖率收集)通过、fsharp-reviewer给出的结论落在 Approve 或只有 MEDIUM 的 Warning 档。出现 Block 时,按[SEVERITY]清单从 CRITICAL/HIGH 项逐条修复后重新触发审查即可。
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考