tqakdev/ctxlint

GitHub: tqakdev/ctxlint

专为 AI 编程 agent 上下文文件打造的静态审计与覆盖率工具,帮助团队评估、清理和优化 context 健康状况。

Stars: 1 | Forks: 0

# ctxlint **Linter + profiler + coverage,但专为 agent context 打造。** `ctxlint` 审计、分析并评分用于引导 AI 编程 agent 的 context 文件 — `AGENTS.md`、`CLAUDE.md`、`.cursor/rules/*.mdc`、`.github/copilot-instructions.md`、 `.windsurf/rules/*.md` 以及 skills。它回答了目前没人能回答的关于他们 repo 的问题: - **我的 agent 到底加载了什么?** 按工具、按目录划分,加载顺序是什么,以及为什么。 - **成本是多少?** 每次请求注入的 context 的 token 总量。 - **哪些规则失效了?** 在不同工具间重复、发生偏差、相互矛盾、 指向已不存在的文件,或者被深埋以至于被忽略。 - **它们有帮助吗?** 根据你的规则评判最近的 commit,以发现违规情况和 对任何内容都不起作用的死规则。 ## 安装 npm 包是 **`@tqakdev/ctxlint`**;它安装的命令是 **`ctxlint`**。 ``` npx @tqakdev/ctxlint scan # one-off, no install npm i -g @tqakdev/ctxlint # then run `ctxlint` anywhere ``` ## 快速开始(30 秒) ``` npx @tqakdev/ctxlint scan ``` 就是这样 —— 无需配置。你将获得一个 Context 健康分数(0–100)、一个精确到每个 agent 加载了哪些文件及其成本的按工具划分的表格,以及一个让你在不到一分钟内就能采取行动的发现列表。 ## 报告示例 在一个包含手动维护且已发生偏差的 context 文件的 repo 上运行 `ctxlint scan`: ``` ctxlint — 5 context file(s), 95 rules Context Health Score: 46/100 freshness 0 uniqueness 8 consistency 76 budget 82 structure 84 Context files file kind tools tokens≈ rules .cursor/rules/broken.mdc cursor-rule cursor ≈37 2 .cursor/rules/style.mdc cursor-rule cursor ≈184 7 .cursorrules other — ≈56 5 .github/copilot-instructions.md copilot-instructions copilot ≈210 9 CLAUDE.md claude-md claude-code ≈2812 72 claude-code @ . — ≈2812 tokens always-on # file tokens≈ why 1 CLAUDE.md ≈2812 CLAUDE.md at repo root cursor @ . — ≈184 tokens always-on (+ ≈37 conditional) # file tokens≈ why 1 .cursor/rules/broken.mdc ≈37 frontmatter unparseable — activation unknown, assumed not auto-attached 2 .cursor/rules/style.mdc ≈184 alwaysApply: true Findings: 15 error(s), 6 warning(s), 2 info ✖ [duplication] Same rule maintained twice for different tools: .cursor/rules/style.mdc:10-11 and CLAUDE.md:65-66 are 100% identical. Keep one canonical copy (prefer AGENTS.md) and delete the other. ✖ [stale-reference] CLAUDE.md:45-49 references `src/api/v1/` which does not exist — actively misleading the agent. Update the reference or delete the rule. ▲ [drift] These rules started identical and diverged (66% similar): .github/copilot-instructions.md:7-8 vs CLAUDE.md:104-105. diff: … against the [-main-] {+develop+} branch and … ▲ [contradiction] Contradictory instructions about the same thing (polarity heuristic is English-only): A: "Always use named exports in shared modules." B: "Never use named exports for React components." ▲ [budget] 9 critical rule(s) buried past 70% depth of an oversized file (CLAUDE.md, deepest at line 217 = 99%) — likely lost in long sessions. Move critical rules to the front. ℹ [load-semantics] .cursorrules is read by no tool ctxlint knows (legacy format) — migrate its rules into .cursor/rules/*.mdc or AGENTS.md, then delete this file. ``` ## 命令 ### `ctxlint scan [path]` — 静态分析(默认命令) 发现每一个 context 表面(遵循 `.gitignore` 和 `discovery.exclude`, 跳过符号链接和超过 1 MB 的文件),将它们拆分为原子规则,解析 特定于工具的加载语义,运行五个分析器,并打印报告。 加载顺序语义是根据每个工具的官方文档建模的;报告带有 文档链接、最后验证日期,以及模型做出的每个假设 (`--format md`/`json` 中的“加载顺序模型出处”表),因此当工具改变 行为时,过时的假设是可见的,而不是默默出错。 | flag | 作用 | |---|---| | `--format text\|json\|md\|sarif` | 输出格式(默认 `text`);`sarif` 可接入 GitHub code scanning | | `--output ` | 将报告写入文件 | | `--ci` | 当存在 error 级别的发现时以退出码 1 退出 | | `--max-files ` | 在超大型 monorepo 上限制发现遍历 | | `--no-user-global` | 忽略 `~/.claude/CLAUDE.md`(在 `fix` 时也是如此)—— 默认情况下它被视为真实的 context,这意味着每台机器上的发现可能不同;在 CI 中请传入此参数 | ### `ctxlint fix [path]` — autofix 规划器 写入 `ctxlint-fixes.md`,将每个修复分组为 **安全**(可自动应用)和 **建议**(需要人工判断)。`--write` 仅应用安全集合: 1. 删除完全重复的内容,保留规范副本(AGENTS.md 优先级最高); 2. 当 git 历史显示存在唯一的重命名目标时更新过时路径 —— 且仅当 该目标仍存在于代码树中,并且仅限发现指出的确切引用 (规则中其他有效的路径绝不会被动); 3. 将被深埋的关键规则移至前面 —— 仅限作者大写 (NEVER/MUST/ALWAYS)的规则;小写的判断性建议保持为建议。 `--write` 拒绝在脏的 git 树上运行,因此每次更改都是可审查和 可回滚的。 ### `ctxlint compliance [path]` — 规则真的有帮助吗?(需要 `ANTHROPIC_API_KEY`) 抽样你最近的 N 次合并更改(默认 30 次;回退到普通 commit),跳过 lockfile/vendored/generated 代码,根据文件和 关键字重叠预过滤 (rule, diff-chunk) 对,然后让模型对每对输出严格的 JSON 判定: `followed | violated | not-applicable`,并带有一行证据引用。 - **预算上限**:总成本 —— 包括 `--calibrate` 的第二个模型样本 —— 都会 预先估算;任何超过 $1(可配置)的消耗都需要 `--yes`。 - **磁盘缓存**:判定以 (rule, chunk, model) 为键,因此重新运行是增量的 且几乎是免费的。 - **死规则**:会指出在整个样本中不适用于任何内容的规则 —— 它们 在每次请求时都会消耗 token 且从不改变行为。 - **`--calibrate`**:使用第二个模型重新判定 10% 的样本,并打印 一致率。低于 80% 时,报告会醒目地 告诉你将每条规则的分数仅视为方向性参考。 ### `ctxlint report` — 重新渲染上次扫描 从 `.ctxlint-cache/last-scan.json` 重新生成报告,无需重新扫描 (`--format`、`--output` 同上)。 ## GitHub Action 添加一个 Context 健康检查,它会在每个 PR 上发表评论: ``` # .github/workflows/ctxlint.yml name: context-health on: [pull_request] permissions: contents: read pull-requests: write jobs: ctxlint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: tqakdev/ctxlint/action@action-v1 with: fail-on-error: "true" # gate the job on error-severity findings fail-on-score: "0" # optionally require a minimum Context Health Score (0 disables) comment: "true" # post/update the report as a PR comment ``` 该操作还会将报告写入作业摘要,并公开 `score` 和 `errors` 作为输出。 ### GitHub code scanning (SARIF) 通过标准的 SARIF 上传,发现的问题可以进入 repo 的 Security(安全)选项卡 — 并在 PR 上带有文件/行注释 — : ``` - run: npx @tqakdev/ctxlint scan --format sarif --output ctxlint.sarif - uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ctxlint.sarif ``` ## Pre-commit hook 在损坏的 context 文件提交之前捕获它们。纯粹的 git hook —— 无需额外依赖: ``` # .git/hooks/pre-commit (chmod +x) #!/bin/sh # 仅当 commit 中包含 context file 时运行。 if git diff --cached --name-only | grep -qE \ '(^|/)(AGENTS|CLAUDE|SKILL)\.md$|\.cursor(rules)?(/|$)|\.mdc$|\.windsurf(rules)?(/|$)|copilot-instructions\.md$'; then npx @tqakdev/ctxlint scan --ci --no-user-global || { echo "ctxlint: error-severity findings — fix them or commit with --no-verify" >&2 exit 1 } fi ``` 使用 [husky](https://typicode.github.io/husky/): ``` echo "npx @tqakdev/ctxlint scan --ci --no-user-global" > .husky/pre-commit ``` `--ci` 仅在遇到 error 级别的发现(过时引用、跨工具 重复)时以退出码 1 退出;警告和信息永远不会阻止 commit。`--no-user-global` 保持 门禁确定性 —— 没有它,发现可能涉及你个人的 `~/.claude/CLAUDE.md`,而队友的机器上并没有这个文件。 ## 配置 一切都有针对典型 repo 的默认值;创建 `ctxlint.config.json` 仅用于 更改某些内容(schema 见 [`ctxlint.config.schema.json`](./ctxlint.config.schema.json)): ``` { "budgets": { "surfaceWarnTokens": 1500, "effectiveContextWarnTokens": 4000 }, "compliance": { "model": "claude-haiku-4-5", "commits": 30, "spendCapUsd": 1 } } ``` | section | option | default | meaning | |---|---|---:|---| | budgets | `surfaceWarnTokens` | 1500 | 当单个文件超过此估算 token 数时发出警告 | | budgets | `effectiveContextWarnTokens` | 4000 | 当单个工具的常驻 context 超过此限制时发出警告 | | budgets | `buriedRuleDepthRatio` | 0.7 | 标记深度超过超大文件此比例的关键规则 | | discovery | `maxFiles` | 20000 | 对 repo 遍历的硬性上限(`--max-files` 可覆盖) | | discovery | `exclude` | `[]` | 用于非活跃表面 context 文件的 glob(测试固定装置、示例)—— 被分析跳过但保留在 repo 索引中,因此对它们的引用仍然有效 | | analysis | `maxRules` | 5000 | 成对分析器在此限制之上会优雅退出 | | compliance | `model` / `calibrationModel` | haiku / sonnet | 判定模型和第二意见模型 | | compliance | `spendCapUsd` | 1 | 超过此估算花费时需要 `--yes` | ## 分数意味着什么 —— 以及不意味着什么 诚实说明部分。在相信一个数字之前,请阅读此内容。 - **Token 计数是估算值。** 离线计数使用 js-tiktoken (`o200k_base`) 并 在各处标记为 `≈`;不同的供应商的 tokenizer 会有所不同,因此请将计数视为 ±10–20%,对于 CJK(中日韩)内容丰富的场景误差会更大。设置了 `ANTHROPIC_API_KEY` 后,`scan` 会获取 精确的 Anthropic 计数并将其标记为 exact。 - **Context 健康分数是确定性的,而不是神谕。** 相同的输入,相同的分数 —— 公式记录在 `src/core/scoring.ts` 中(五个加权子分数;error/warn/info 的每项发现 惩罚分别为 25/10/4,子分数内的每次重复计为前一次的 0.8 倍,这样一个糟糕的文件就不会导致整个子分数降至最低,尽管约 8 个 error 仍会 使其降至 0)。它是一个趋势指标:在 CI 中观察它的变化,不要迷信 绝对数字。 - **合规性判定是基于判定者的。** LLM 阅读规则和 diff 并给出 意见。运行 `--calibrate` 来衡量跨模型的一致性;低于 80% 时,报告 本身会告诉你分数仅供参考。死规则检测取决于你的 样本规模 —— 在 30 次 commit 中都不适用的规则可能下周就会适用。 - **加载语义是尽力而为的。** 工具行为是根据文档化的 行为编码的;在未文档化的地方,表格会写上 "(assumed)" 而不是将 猜测作为事实呈现。旧版 `.cursorrules` 被视为没有被任何东西加载(假设); 旧版 `.windsurfrules` 被视为仍被 Windsurf 读取(已弃用的格式)。 Cursor 的 `.mdc` frontmatter 按照 Cursor 读取它的方式读取:未加引号的 glob (`globs: **/*.ts` —— Cursor 自身编辑器写入的内容)不是有效的 YAML,但会 被恢复,并且 `{ts,tsx}` 花括号 glob 会正确激活;只有没有 工具能读取的 frontmatter 才会被标记为损坏。 - **某些启发式方法仅支持英语。** 重复/偏差的 shingle 适用于任何 语言;基于极性的矛盾检测只能理解英语的 always/never 表达方式,并在每个发现中予以说明。 - **成对分析是 O(n²) 的。** 超过 5000 条规则(可配置)时,重复/偏差 分析会带着提示优雅退出,而不会挂起你的 CI。 ## 基准测试:在真实 repo 上测量的准确率 ctxlint 在七个指定的开源 repo(openai/codex、 sst/opencode、All-Hands-AI/OpenHands、cline/cline、block/goose、vercel/ai、 browser-use/browser-use — `bench/corpus.json`)上生成的每个发现都是根据实际的 checkout(`bench/labels.json`,120 项发现)手动标记为真阳性/假阳性 : | category | precision | tp / fp | notes | |---|---:|---|---| | budget | 98% | 57 / 1 | token 数学就是 token 数学 | | stale-reference | 91% | 52 / 5 | 旗舰分析器 | | duplication | 100% | 2 / 0 | 小样本 | | contradiction | 33% | 1 / 2 | 小样本 —— 正在重新制作 | | structure | — | 0 / 0 | 早期所有的误报已修复 | | **overall** | **93%** | **112 / 8** | | 第一次标记阶段测得的总体准确率为 67%(stale-reference 为 52%)。而不是 发布它就完事了,被标记的假阳性成为了修复列表: 根据祖先目录和 `cd` context 解析引用,补全 import 指定符的扩展名(`./native-request` → `native-request.ts`),将 任何地方存在的纯文件名视为可找到的,理解创建/移除/ 条件语句("do not create X"、"when `.pr/` exists"),并丢弃 非路径 token(`text/*`、`Schema.Json`、`kebab-case.ts`、省略号路径)。 同样的方式发现了两个解析 bug。存留下来的真阳性是 真实的:cline 的整个 `copilot-instructions.md` 描述了一个 不再存在的 repo 布局,而 OpenHands 的 AGENTS.md 仍然指向其 重构前的代码树。 使用 `pnpm bench` 复现(克隆指定的 SHA,约 200 MB);如果 分析器输出偏离了已提交的快照,`pnpm bench --check` 将会失败。每当快照发生变化时都会重新审计标签 —— 精度声明始终与 赚取它们的精确代码绑定。 ## 开发 ``` pnpm install pnpm test # vitest, fixture-driven — no network calls anywhere in tests pnpm typecheck # TS strict pnpm lint # biome pnpm build # emits dist/, runnable as node dist/cli.js ``` `test/fixtures/` 下的三个 fixture repo 是测试床:`clean-repo` 得分为 100,`messy-repo` 触发每个发现类别,`monorepo` 运行子树和 glob 范围的解析。 ## 许可证 MIT
标签:AI编程助手, MITM代理, SOC Prime, 云安全监控, 开发工具, 性能分析, 暗色界面, 自动化攻击, 静态分析