ECC TypeScript/JavaScript Hooks 规则:在 Agent 编辑 JS/TS 文件后自动格式化、类型检查与 console.log 审计
本文以 ECC 仓库中的 .cursor/rules/typescript-hooks.md 规则文件为主体,完整解读它为 TypeScript/JavaScript 项目定义的 Hook 策略:在每次编辑文件之后(PostToolUse)执行 Prettier 自动格式化、tsc 类型检查与 console.log 警告,在会话结束前(Stop)对所有修改过的文件做最终的 console.log 审计。读完本文,你将理解这套规则在 ECC 中如何被 .cursor/hooks.json 注册、如何通过 .cursor/hooks/adapter.js 适配到 Claude Code 风格的 hook 协议,以及底层 scripts/hooks 目录下各检查脚本的具体实现逻辑与容错设计。
一、规则文件定位:按 glob 精确生效的 TypeScript 扩展规则
.cursor/rules/typescript-hooks.md 是一个 Cursor 规则(rules)文件,其 frontmatter 决定了两件事——在什么文件上生效、是否总是注入:
---
description: "TypeScript hooks extending common rules"
globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
alwaysApply: false
---
globs限定了规则只在 TS/TSX/JS/JSX 文件被操作时加载,覆盖典型的前端与 Node.js 项目文件类型;alwaysApply: false表示该规则不会常驻上下文,只在文件模式命中时按需生效,这是一种控制 token 开销的写法。
文件开头明确说明其定位是"在通用 hooks 规则的基础上扩展 TypeScript/JavaScript 专属内容"。与之配合的是通用规则 .cursor/rules/common-hooks.md,它定义了 Hook 体系的三个基本类型,是本规则文件的"父集":
| Hook 类型 | 触发时机 | 典型用途 |
|---|---|---|
| PreToolUse | 工具执行前 | 参数校验、参数修改、拦截危险操作 |
| PostToolUse | 工具执行后 | 自动格式化、质量检查 |
| Stop | 会话(响应)结束时 | 最终验证与收尾审计 |
typescript-hooks.md 的全部规范内容即围绕 PostToolUse 与 Stop 两类展开,这正是它在通用规则之上"只补差异"的设计意图:通用层定义框架,语言专属层只声明该语言需要哪些具体检查。
二、PostToolUse Hooks:编辑文件后的三道即时防线
规则文件为 PostToolUse 阶段声明了三项配置(文档指出配置入口为 ~/.claude/settings.json):
- Prettier:编辑 JS/TS 文件后自动格式化;
- TypeScript check:编辑
.ts/.tsx文件后运行tsc做类型检查; - console.log warning:对被编辑文件中的
console.log发出警告。
这三项并非纸面约定,在仓库中有完整对应的实现链路。
2.1 事件注册:afterFileEdit 钩子
.cursor/hooks.json 将 afterFileEdit 事件绑定到 .cursor/hooks/after-file-edit.js,其描述直接对应规则文件中的三条能力:"Auto-format, TypeScript check, console.log warning, and frontend design-quality reminder"。
2.2 适配层:Cursor 事件如何落到共享脚本
.cursor/hooks/after-file-edit.js 本身是一个极薄的分发器:它通过 adapter.js 读取 stdin 中的 Cursor 事件 JSON,转换为 Claude Code 风格的 hook 输入(tool_input.file_path 等字段),然后依次委托给三个共享脚本:
// 累积被编辑路径,供 Stop 时批量 format + typecheck
runExistingHook('post-edit-accumulator.js', claudeStr);
// 对刚编辑的文件做 console.log 即时警告
runExistingHook('post-edit-console-warn.js', claudeStr);
// 前端设计质量提醒(standard/strict profile 下启用)
if (hookEnabled('post:edit:design-quality-check', ['standard', 'strict'])) {
runExistingHook('design-quality-check.js', claudeStr);
}
.cursor/hooks/adapter.js 中的 transformToClaude 负责字段映射(path/file/args.filePath 归一到 tool_input.file_path),runExistingHook 通过 execFileSync 以 15 秒超时调用 scripts/hooks/ 下的实际脚本,并且会在子进程返回退出码 2 时向上传播该码——这是 hook 协议中"阻断当前操作"的约定。
2.3 console.log 即时警告的实现
scripts/hooks/post-edit-console-warn.js 是规则中"console.log warning"的直接实现。它对编辑路径做扩展名过滤(\.(ts|tsx|js|jsx)$),命中后逐行扫描文件内容,输出带行号的警告:
const matches = content
.split('\n')
.map((line, index) => ({ line, index }))
.filter(item => /console\.log/.test(item.line))
.map(item => `${item.index + 1}: ${item.line.trim()}`);
if (matches.length > 0) {
warnings.push(`[Hook] WARNING: console.log found in ${filePath}`);
warnings.push(...matches.slice(0, 5)); // 最多展示前 5 处
warnings.push('[Hook] Remove console.log before committing');
}
值得注意的两个细节:警告写入 stderr、原始 stdin JSON 原样回写 stdout(保持 hook 链路透传约定),且任何输入异常都静默降级为 pass-through——保证检查失败永远不会阻塞编辑操作。
2.4 "批量延迟"策略:格式化和 tsc 为何不在每次编辑后跑
规则文档写的是"编辑后自动 Prettier、编辑 .ts/.tsx 后运行 tsc",但实现上采取了延迟批量策略,其动机在 scripts/hooks/post-edit-accumulator.js 的头注释中说明得很清楚:每次编辑都跑格式化 + 类型检查会产生可观的延迟,因此该脚本只负责把编辑过的 JS/TS 路径按行追加写入一个会话级临时文件($TMPDIR/ecc-edited-<sessionId>.txt),由 Stop 阶段的 stop-format-typecheck.js 一次性对全部文件执行 format + typecheck。
从源码结构看,这里的工程考量包括:
- 使用
appendFileSync追加写入,使并发 hook 进程互不覆盖; - 会话 ID 取自
CLAUDE_SESSION_ID,缺失时退化为 cwd 的 SHA1 前 12 位,并对 ID 做字符清洗(replace(/[^a-zA-Z0-9_-]/g, '_'))防止路径注入; - 同时处理
Edit/Write(单个file_path)与MultiEdit(edits数组)两种工具输入; - 去重被刻意推迟到 Stop 阶段完成。
这也解释了 hooks/hooks.json 中 Claude Code 侧 Stop 事件里 stop:format-typecheck 条目的描述:"Batch format (Biome/Prettier) and typecheck (tsc) all JS/TS files edited this response — runs once at Stop instead of after every Edit",其超时被放宽到 300 秒,因为批量 tsc 在大型项目中耗时较长。
三、Stop Hooks:会话结束前的 console.log 全量审计
规则文件的第二段要求"在会话结束前检查所有被修改文件的 console.log"。这一能力的注册与实现如下:
3.1 事件注册与 profile 门控
.cursor/hooks.json 将 stop 事件绑定到 .cursor/hooks/stop.js,描述为 "Console.log audit on all modified files"。stop.js 在分发前通过 hookEnabled 做 profile 门控:stop:check-console-log 仅在 standard/strict profile 下运行。
hookEnabled 的判定逻辑(见 adapter.js)由两个环境变量控制:
| 环境变量 | 取值 | 作用 |
|---|---|---|
ECC_HOOK_PROFILE |
minimal / standard / strict(默认 standard,非法值回退 standard) |
决定哪些 profile 允许该 hook 运行 |
ECC_DISABLED_HOOKS |
逗号分隔的 hook ID 列表 | 显式禁用指定 hook,优先级最高 |
这意味着用户可以按项目阶段整体降档(如 minimal 只保留最核心的会话生命周期钩子),或单独关闭某一项检查,而无需改动任何 hook 配置文件。
3.2 审计脚本:只查"工作区实际改过的文件"
scripts/hooks/check-console-log.js 是 Stop 审计的实现,其工作方式与编辑后的即时警告形成互补:
- 数据来源不是 hook 事件里的单个文件,而是
getGitModifiedFiles(['\\.tsx?$', '\\.jsx?$']),即通过 git 获取当前工作区所有已修改的 JS/TS 文件——这正对应规则文档中"Check all modified files"的语义; - 内置排除规则
EXCLUDED_PATTERNS,测试文件(*.test.*、*.spec.*、__tests__/、__mocks__/)、配置文件(*.config.*)以及scripts/目录下的文件不触发警告,因为"console.log 在这些地方往往是有意为之"; - 命中时输出两条日志:
WARNING: console.log found in <file>与收尾提示Remove console.log statements before committing; - 非 git 仓库直接跳过检查。
容错设计同样值得一提:脚本对 stdin 设置 1MB 上限(MAX_STDIN),若输入被截断则不回传 stdin 数据——注释说明这是为避免半截 JSON 被 harness 判定为 Stop hook 校验失败(对应 issue #2090);任何异常都走 fail-open,最终 passThroughAndExit 保证 hook 永远不会阻断会话结束。
3.3 两级检查的分工
综合源码可以看到,console.log 治理实际上是一个两级漏斗:
- 编辑后即时层(PostToolUse → post-edit-console-warn.js):只看刚编辑的那一个文件,输出精确行号,最多展示 5 处命中,帮助 Agent/开发者在编码当下立即清理;
- 会话收尾层(Stop → check-console-log.js):基于 git 修改清单做全量兜底审计,覆盖即时层可能漏掉的文件(例如通过 shell 命令修改的文件),并以测试/配置目录白名单降低误报。
四、与 Claude Code 侧配置的对应关系
ECC 的 hook 体系是"一套底层脚本、多端注册"的结构:Cursor 侧通过 .cursor/hooks.json + .cursor/hooks 薄适配器注册,Claude Code 侧则通过 hooks/hooks.json 直接以 matcher 匹配工具名注册。以本文主题相关的检查为例,Claude 侧的对应条目包括:
PostToolUse的post:dispatcher:sync/post:dispatcher:async:将同步与后台 PostToolUse 检查收敛到 scripts/hooks/posttooluse-dispatcher.js 单进程内分发,同步通道 30 秒、异步通道 45 秒超时;Stop的stop:check-console-log(调用 check-console-log.js)、stop:format-typecheck(调用 stop-format-typecheck.js,300 秒超时)、stop:session-end、stop:evaluate-session、stop:cost-tracker等,均带minimal,standard,strict或standard,strict的 profile 门控;- 此外 PreToolUse 中还有
pre:config-protection等互补机制——阻断 Agent 修改 linter/formatter 配置文件,"引导 Agent 去修代码而不是削弱配置",与本文的自动格式化 hook 构成一攻一防的组合。
需要说明的适用前提:.cursor/ 目录下的 hook 依赖 Cursor 的 hooks 能力加载 .cursor/hooks.json;而规则文件本身(frontmatter + 自然语言)主要服务于 Cursor 的规则注入场景。~/.claude/settings.json 是 Claude Code 的配置入口,ECC 仓库提供的 hooks/hooks.json(其 $schema 指向 claude-code-settings)即面向该入口的等价实现。两端事件命名不同(如 afterFileEdit vs PostToolUse),但委托的底层脚本是同一批,行为保持一致。
五、要点总结与实践参考
从 .cursor/rules/typescript-hooks.md 这一份不到 20 行的规则文件出发,结合仓库源码可以提炼出 ECC 处理"Agent 写 TS/JS 代码"的完整质量闭环:
| 阶段 | 能力 | 注册位置 | 底层实现 |
|---|---|---|---|
| PostToolUse | 编辑后 console.log 行级警告 | .cursor/hooks.json afterFileEdit |
scripts/hooks/post-edit-console-warn.js |
| PostToolUse | 累积编辑路径(供批量处理) | 同上 | scripts/hooks/post-edit-accumulator.js |
| Stop | 批量 Prettier/Biome 格式化 + tsc 类型检查 | hooks/hooks.json stop:format-typecheck |
scripts/hooks/stop-format-typecheck.js |
| Stop | 全量修改文件 console.log 审计 | 两端 Stop 事件 | scripts/hooks/check-console-log.js |
其设计取舍对搭建自有 Agent hook 体系有直接参考价值:高频轻检查(正则扫描)放在编辑后即时执行并给出行号;重检查(tsc)通过路径累积文件延迟到 Stop 批量执行以消除逐次延迟;所有检查脚本统一 1MB stdin 上限、fail-open 容错与"原样回传 stdin"的链路透传约定;检查项通过 ECC_HOOK_PROFILE 与 ECC_DISABLED_HOOKS 环境变量实现 profile 降档与按 ID 的精细禁用;敏感目录(测试、脚本、配置)以白名单排除误报。这些机制均可在 scripts/hooks 目录下逐文件查证,测试用例位于 tests 与 tests/hooks 对应文件中,可作为行为验证的入口。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00