关于 JevLint-LE
JevLint-LE 可在代码和 JSON 中检查 Jev 与 Luna 决策模型问题,离线规则无需 API 密钥。它面向构建决策请求的开发者和 Agent,并可用用户自己的密钥进行可选模型检查。
主要功能
- 01检查 JSON 和六种编程语言
- 02应用安全修复并忽略指定发现
- 03编辑器、CLI、CI 与 MCP 共用规则
- 04提供检查、修复和规则查询的 8 个 MCP 工具
- 05可选模型检查与布局探测
适用场景
- 01发送请求前发现格式不当的决策问题
- 02在编辑器和 CI 中运行一致检查
- 03让 Agent 检查并安全修复生成的请求
- 04请模型审查问题或测试布局敏感性
MCP 相关
stdio MCP 服务提供 8 个工具,可检查文本或文件、应用安全修复、列出或解释规则,并执行可选的模型检查和探测。调用模型的工具使用用户配置的 API 密钥。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
8收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| text | string | 是 | 请求 JSON 或源代码。 |
| rules | object | 否 | 要覆盖的规则级别,例如 { "JEV004": "off" }。级别:off、hint、info、warning、error。 |
| filename | string | 否 | 文件名,其扩展名用于指示文本类型:.json、.ts、.js、.py、.rs 或 .go。默认为 request.jev.json。 |
详细介绍
DecisionLint:Jev/Luna Lint
发送前检查 Jev 和 Luna 决策模型问题
支持 JSON、JavaScript、TypeScript、Python、Rust 和 Go,适用于 TypeSafe、OpenAI 及所有提供其中任一平台的服务
觉得有用? 点赞或评分可以帮助其他开发者找到它 — ★ GitHub · ★ Open VSX · ★ Marketplace
功能
发送 Jev 和 Luna 问题前,先对它们进行 Lint。DecisionLint 会检查你为决策模型编写的问题:TypeSafe 的 Jev、OpenAI 的 Decisions API(gpt-6-luna),以及提供这两种格式的所有平台,从 OpenRouter 和 Vercel AI Gateway,到你自己机器上的 llama.cpp。DecisionLint 会从 JSON 和代码中读取这些问题,并报告那些经评估会导致不佳回答的写法。Lint 不会调用 API,也不需要密钥。有一条可选命令可以让模型自行检查文件,并使用你自己的密钥将请求发送到你选择的平台。
- 输入时即时 Lint,支持 JSON、JavaScript、TypeScript、Python、Rust 和 Go。
- 对机械性错误,点击灯泡即可修复;也可以忽略你有异议的检查结果。
- CI、命令行和 AI Agent 的 MCP 服务使用相同规则。一个设置文件即可覆盖这三种用法。
- 一个规则集,适用于所有平台。 TypeSafe 的
/v1/systemone格式和 OpenAI 的/v1/decisions格式,以及predicate、choices和levels,都会按照同一套规则进行检查;AI SDK 中针对任一格式的decide()调用也一样。请求遵循的限制取决于平台自身,并根据模型 ID 的写法、文件中的 URL 或target设置读取:Vercel 限制为 255 个选项,LiteLLM 限制为 128 个问题;平台未公布限制时则不设限制。 - 两条可选命令,使用你自己的密钥:一条会询问模型,你的问题是否存在文本模式无法发现的问题;另一条会调整问题的布局并重新发送,以检查答案是否稳定。两条命令都会将请求发送到模型 ID 所指的平台,也可以发送到你设置的端点,包括本地 llama.cpp。
像这样的请求看起来没问题,但实际上有三个问题:
{
"model": "jev-latest",
"state": "My parcel came three days late and the lid is cracked.",
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": { "billing": "Charges and refunds", "delivery": "Late or damaged parcels" }
},
"late": { "type": "noul", "instructions": "Did more than two parcels arrive late?" }
}
}
| 检查结果 | 重要原因 |
|---|---|
jev-latest 上的 DEC001 | 别名会随每次发布而变,因此即使你这边没有任何改动,答案也可能变化 |
team 上的 DEC004 | 关于两支队伍都无关的消息,仍会被强行归入其中一支 |
late 上的 DEC102 | Jev 计数并不可靠。请分别询问每个包裹,并在代码中计数 |
安装
| 适用平台 | 提供功能 | 安装方式 |
|---|---|---|
| VS Code | 输入时即时 Lint、点击灯泡即可修复,以及 Ask 和 Probe 命令 | Marketplace · ext install nolindnaidoo.decisionlint |
| Cursor、VSCodium、Windsurf | 相同的扩展 | Open VSX |
| 终端或 CI 步骤 | 对整个目录树应用相同规则,并提供退出代码、SARIF 和 JUnit | npx decisionlint · npm |
| 任何 MCP Agent,通过 Node 使用 | 通过 stdio 提供 8 个工具 | npx -y decisionlint-mcp · npm |
在 AI Agent 中使用
同一个程序也是 MCP 服务,因此,能够编写 Jev 或 OpenAI Decisions 问题的 Agent 可以检查自己写的内容,并在你看到之前修正问题。工具说明和服务指令列出了这两家供应商以及全部三种请求格式,因此 Agent 知道遇到任何一种情况都应调用它。
| 编辑器 | 使用方法 |
|---|---|
| VS Code 1.101+ | 无需安装。扩展会向编辑器提供该服务,Agent 调用工具时,编辑器会启动它 |
| Claude Code | claude mcp add decisionlint -- npx -y decisionlint-mcp |
| Cursor、Windsurf 及其他工具 | 指向 npx -y decisionlint-mcp |
它提供八种工具。lint_text 检查以文本形式传入的请求或源代码,lint_paths 检查磁盘上的文件,每项发现都会附带可修正该问题的编辑内容。fix_text 返回应用安全修复后的文本以及剩余问题的报告,不会写入任何内容。list_rules 列出每条规则及其含义,explain_rule 返回规则自身的页面,其中包含一个会被标记的示例和一个不会被标记的示例。这些工具不会发送任何内容,也不需要密钥。
另外三种工具是供 Agent 使用的编辑器 Ask 命令和 Probe。ask_model 会请求 Jev 或 Luna(使用 model: gpt-6-luna)检查文本或文件中的问题,并返回包含模型发现的 lint 报告。plan_ask 会说明将发送哪些内容,但不会发送任何内容。probe_question 会以不同排版方式发送一个问题,并报告答案是否稳定。会发送内容的两种工具会使用你的密钥并产生费用,其说明会告知 Agent 只有在你提出要求时才能调用。密钥会从客户端启动服务时所处环境中的 TYPESAFE_API_KEY 或 OPENAI_API_KEY 读取,也会从服务启动目录中的 .env.local 或 .env 读取,不会从工具参数中读取,因此密钥不会出现在 Agent 的对话记录中。每种工具都会在文本旁返回结构化内容,而且都不会写入文件。
它在 MCP 注册表中的条目为 io.github.nolindnaidoo/decisionlint,供从该处安装服务的客户端使用。
手动配置:任何使用 MCP 配置文件的宿主
大多数宿主都会读取 JSON 配置。添加一条配置:
{
"mcpServers": {
"decisionlint": { "command": "npx", "args": ["-y", "decisionlint-mcp"] }
}
}
-y 可跳过首次运行时的安装提示。如需每次启动时都跳过 npx,可使用 npm install -g decisionlint-mcp 安装一次,然后将 "command": "decisionlint-mcp" 设为命令。
对于已为 CI 固定 decisionlint 版本的项目,可通过 npx decisionlint --mcp 使用同一个服务。提供给客户端时,应使用不带标志的包,因为直接启动 decisionlint 会运行一个随后退出的 lint 工具,而不是服务。
它能检查哪些问题
Jev 和 Luna 保证的是答案的类型,而不是答案本身。格式不当的问题仍可能得到一个看似笃定的数字。这些规则可以发现那些能从文本中识别出的错误。
| Code | Name | Default | 含义 |
|---|---|---|---|
| DEC000 | unreadable | hint | 问题的一部分是在运行时构建的,因此需要这部分内容的规则未能运行 |
| DEC001 | unpinned-model | warning | jev-latest 之类的别名会随每次发布而变化,因此即使你这边没有任何更改,答案也可能改变 |
| DEC002 | choice-option-limit | error | Choice 的选项超过 255 个。API 会拒绝它 |
| DEC003 | score-level-limit | error | Score 的等级超过 10 个。API 会拒绝它 |
| DEC004 | no-fallback-option | info | Choice 没有 other 或 none of the above,因此不符合任何选项的输入会被强行归入某个选项。这是任何地方测得的最大成本,也是最难从文本中判断的一项 |
| DEC005 | duplicate | error | 问题 ID、选项或等级重复出现。在对象中,后出现的项会静默覆盖先出现的项 |
| DEC006 | criteria-shape | error | Choice 需要选项映射,Score 需要等级数组,而 Noul 接受 true 和 false |
| DEC007 | invalid-question | error | 类型缺失或未知,或者说明是空字符串 |
| DEC008 | numeric-levels | warning | Score 等级是裸数字。Jev 会根据每个描述将状态与之匹配,而不会看到其位置 |
| DEC009 | too-few-options | info | 只有一个选项的 Choice 或只有一个等级的 Score 会对所有输入给出相同答案 |
| DEC010 | unused-disable | warning | decisionlint-disable 注释没有禁用任何内容,因此它只可能掩盖之后新增的问题 |
| DEC011 | description-repeats-name | info | 选项的描述只是在重复其名称,或者 Noul 的 criteria 只说了是与否。Jev 会根据描述匹配状态,而这段描述没有提供任何信息 |
| DEC012 | terse-instructions | info | 说明只有一两个词,因此问题依赖其 ID。Jev 收到的是说明,而不是 ID |
| DEC013 | newer-model | info | model 指定的 Jev 版本比这些规则所依据的版本更新,因此某个问题在该版本中可能没有任何成本,而该版本中新出现的弱点也未被检查 |
| DEC014 | question-limit | error | 请求中的问题数量超过了目标平台单次调用所能接受的数量。平台会拒绝该请求 |
| DEC101 | double-negative | info | 一个子句中的问题包含双重否定,或者一个否定直接嵌套在另一个否定上 |
| DEC102 | arithmetic | warning | 问题要求 Jev 计数或比较数字 |
| DEC103 | date-comparison | info | 问题要求 Jev 对两个时间进行排序,或测量它们之间的间隔 |
| DEC104 | compound | off | Noul 用“and”连接两个判断 |
| DEC105 | generation | warning | 问题要求给出一个值或撰写一段文本 |
| DEC107 | negated-noul | off | 没有 criteria 的 Noul,其措辞使“是”表示某物不存在 |
| DEC108 | inverted-criteria | off | Noul 的“true” criterion 描述的是否定情况 |
| DEC110 | degree-levels | off | Score 等级是程度词、裸标签,或同一个词的强弱变化。没有可供 Jev 用来匹配状态的内容 |
| DEC111 | numeric-encoding | off | 问题通过十六进制或 RGB 编码指代某个值 |
| DEC112 | undefined-boundary | off | 没有 criteria 的 Noul 由 large、often 或 enough 之类的词触发 |
| DEC301 | model-counting | warning | Jev 将问题理解为需要计数或算术运算 |
| DEC302 | model-undefined-boundary | info | Jev 将 Noul 理解为需要判断未明确界限的程度问题 |
| DEC303 | model-overlapping-options | info | Jev 将 Choice 的两个选项理解为涵盖了相同情况 |
| DEC304 | model-overlapping-levels | warning | Jev 将 Score 的两个等级理解为描述相同情况 |
| DEC305 | model-label-mismatch | warning | Jev 将某个选项理解为名称指向一件事、描述却指向另一件事 |
| DEC306 | model-criteria-off-topic | info | Jev 将 criteria 理解为用于判断说明未要求判断的内容 |
| DEC307 | model-ordered-options | info | Jev 将 Choice 的选项理解为同一尺度上的不同步骤,而 Score 才能表示步骤之间的等级 |
| DEC308 | model-unordered-levels | info | Jev 将 Score 的等级理解为无序类别,而 Choice 才会从中进行选择 |
| DEC309 | model-depends-on-sibling | info | Jev 将问题理解为需要同一请求中另一个问题的答案 |
| DEC310 | model-overlapping-questions | info | Jev 将同一请求中的两个问题理解为询问相同的判断 |
| DEC311 | model-answer-not-in-state | info | Jev 将文件中写入的状态理解为不包含问题所询问的内容 |
| DEC312 | model-orders-in-state | info | Jev 将文件中写入的部分状态理解为在向其读者发出指令 |
| 每条发现都链接到自己的页面,其中包含一个会被标记的示例、一个不会被标记的示例、修复方法以及如何忽略它。每个页面还会链接到该规则所依据的 TypeSafe 文档。 |
检查范围
- JSON 和 JSONC 请求正文,支持 TypeSafe 或 OpenAI 的格式。
- JavaScript 和 TypeScript 中的对象字面量,包括内联传给
client.systemOne(...)和client.decisions.create(...)的请求,以及适用于任一供应商的 Vercel AI SDK 的decide({ questions });其中typeSafeAi.decisionModel('...')内的模型 ID 会被识别为模型。 - 使用
@typesafe-ai/sdk的文件中的noul()、choice()和score()调用。 - Python:
typesafe_sdk中的字典、Noul(...)、Choice(...)和Score(...),LangChain 的langchain_typesafe中的NoulCriteria(...),或封装了上述任一项的库中的相应对象,以及传给system_one(...)或client.decisions.create(...)的请求。 - Rust:
json!中的 JSON、以问题类型命名的结构体和枚举变体,以及::noul(...)风格的构造函数。 - Go:键为字符串的映射、带有
Type字段的结构体、以问题类型命名的结构体,以及 OpenAI 的 Go SDK 中带有OfPredicate、OfChoice和OfScore问题的openai.DecisionNewParams{...}。 - 在上述任意语言中,以 JSON 形式粘贴到字符串里的请求。
- 其他任何语言:让程序将它发送的请求写入
.jev.json文件,然后打开该文件。每项检查都会在该文件上运行,且其中没有任何内容是在运行时构建的,因此不会漏检。 oxlint-plugin-jev配置中的jev/ask规则。
OpenAI 的 Decisions API
OpenAI 的 Decisions API 使用模型 gpt-6-luna,提出的问题类型与 Jev 相同,但格式不同。DecisionLint 从 0.5.0 开始读取这类请求。具体覆盖范围如下:
- 读取。
/v1/decisions请求正文、TypeScript 和 Python 中的client.decisions.create(...),以及 Vercel AI SDK 的decide()。predicate会被识别为 Noul,choices: [{ value, description }]会被识别为 Choice 的选项,levels: [{ label, description }]会被识别为 Score 的等级,并套用相同的规则。 - 检查。 有证据支持的每条规则:缺少回退选项、描述只是重复名称、指令过于简短、等级程度、重复项、计数、未定义的边界等。发现中会提及 Luna。OpenAI 自己的指南也要求这些内容。
- 不检查。
DEC002和DEC003检查平台公布的限制,而 OpenAI 没有公布此类限制,因此发往 OpenAI 的请求不会触发这两项检查。发往有此类限制的网关(例如 Vercel)的请求则会触发这两项检查。DEC001针对的是 Jev 会变动的别名,而 OpenAI 只有一个模型 ID。 - 格式错误会按 OpenAI 自己的字段名报告:
choices被写成映射或裸名称、Choice 没有choices、Score 没有levels。拼错的predicate可以通过灯泡修复。重塑格式的修复仅适用于 Jev。 - 询问 Luna。 运行 Ask Luna About This File,或传入
--ask-platform openai,并提供 OpenAI 密钥。下一节会说明操作方法。 - 以 Jev 为基准,并为 Luna 单独设定阈值。 所有措辞规则的默认值都是通过衡量缺陷对
jev-1.13.0的影响而设定的。模型驱动的检查于 2026-10-24 在 Luna 上针对整个校准集运行,并根据 Luna 自己的回答为每项检查设定了适用的阈值,例如十二项中七项。其余五项不会用于检查 Luna,因为没有阈值能区分有缺陷的示例和没有缺陷的示例;docs/cutoffs.md 列出了每项检查无法区分示例的原因。目前尚未测量措辞规则对 Luna 的影响。
每个平台
Jev 和 Luna 是两种模型,TypeSafe 和 OpenAI 则是请求可以发送到的两个平台。Microsoft、Liquid AI 和 Cloudflare 以 TypeSafe 的格式提供各自的模型。OpenRouter、Vercel AI Gateway、LiteLLM 和 LLM Gateway 会以任一种格式转发到其中任意平台。llama.cpp、vLLM 和 Ollaya 可在你自己的机器上运行开源模型。DecisionLint 能读取这两种格式,因此这些平台都适用同一套规则。它们之间的差异集中在一个表格中:src/lint/targets.ts,并附有每项信息的来源页面:
| 平台 | 调用方式 | 密钥变量 | 发布的限制 |
|---|---|---|---|
| TypeSafe | jev-1.13.0、jev-latest | TYPESAFE_API_KEY | 255 个选项、10 个层级 |
| OpenAI | gpt-6-luna | OPENAI_API_KEY | 无 |
| OpenRouter | ~typesafe/jev-latest、typesafe/jev-1.13 | OPENROUTER_API_KEY | 无 |
| Vercel AI Gateway | typesafe-ai/jev、openai/gpt-6-luna-decisions | AI_GATEWAY_API_KEY | 255 个选项、10 个层级 |
| LLM Gateway | gpt-6-luna-decisions、microsoft-decision-1 | LLM_GATEWAY_API_KEY | 无 |
| Liquid AI | d1、d1:free | LIQUID_API_KEY | 无 |
| LiteLLM | 你设置的端点 | 对应格式的密钥 | 每个请求 128 个问题 |
| Eden AI | 它自己的路径 | 它自己的密钥 | 255 个选项 |
| llama.cpp | 你设置的端点 | 无 | 10 个层级 |
从表格可以得出三点:
- 请求受到的平台限制约束。 平台信息会从模型 ID 中的平台名称判断,从文件中 URL 所指的平台判断,或从
decisionlint.json中的target,或从decisionlint.target判断,后者适用于所有请求都发送到同一平台的项目。平台未发布限制时,不会针对该限制报告问题。请求发往何处并不重要:TypeSafe 格式的问题仍遵循 TypeSafe 的限制,其他格式则不受任何限制。 - 无版本别名的各种写法都会被检测到,并按平台文档中的写法固定版本:
jev-latest固定为jev-1.13.0,~typesafe/jev-latest固定为typesafe/jev-1.13。 - 使用模型进行的检查会发送到 ID 所指的平台,并使用该平台的密钥变量。像
openai/gpt-6-luna-decisions这样无法指明托管平台的写法——所有网关都采用这种写法——会被拒绝,而不是猜测目标平台;除非你设置端点。接下来的两个部分会介绍具体方法。
“Open Jev”并不是其中一个平台。至少有九个互不相关的项目使用这个名称来重新实现 /v1/systemone;llama.cpp 提供的那个项目是 ggml-org/OpenJev-GGUF。每个项目都通过你设置的端点调用。
测试过的平台
我们通过每个平台的实时 API 发送了真实请求,并在每个平台上对整个校准集运行了模型检查:
- TypeSafe,
jev-1.13.0,Jev 的阈值在此平台上设定。 - OpenRouter,
typesafe/jev-1.13。它的回答与 TypeSafe 相同,因此其检查结果视为已校准。 - LLM Gateway,
jev-1.13.0,以及 Vercel AI Gateway,typesafe-ai/jev。这两个平台接收 OpenAI 格式的请求,这会让 Jev 的回答略有变化,因此其检查结果会注明阈值是在 TypeSafe 上设定的。 - OpenAI,
gpt-6-luna,Luna 的阈值在此平台上拟合;以及 Vercel AI Gateway,openai/gpt-6-luna-decisions,其回答与 OpenAI 相同。 - Liquid AI,
d1,d1 的阈值在此平台上拟合。
从 1.1.0 起,Luna 和 d1 各自都有专属阈值,这些阈值分别根据对应模型对整个校准集的运行结果拟合:某个阈值不会标记任何正确示例,并且至少能检出十分之六的错误示例。如果某项检查无法达到这个标准,就不会对该模型运行:Luna 有五项检查,d1 有两项。其他模型,或这些模型以其他 ID 或通过其他路由使用时,会按所属系列的阈值,或 Jev 的阈值进行判断,并且其每条检查结果都会注明这一点。docs/cutoffs.md 列出了每个阈值、在每个模型上检出的已知缺陷数量和误标的正确问题数量、未运行的每项检查为何不运行,以及各平台的回答与其他地方相同模型的回答相差多远。该文档根据保存的运行结果编写,因此其中的数字均来自实际运行结果。尚未运行 LiteLLM、Eden AI 和 llama.cpp。
询问模型关于文件的问题
规则 DEC301 至 DEC312 不属于 lint 检查。只有运行某个 询问 命令,或在命令行中传入 --ask 时才会执行。每次运行都会将文件中的每个问题发送给决策模型,并请它检查文本模式无法发现的问题:选项之间有重叠、选项名称与其描述矛盾、标准针对了错误的对象、应该使用 Score 而非 Choice、两个问题询问的是同一件事,或某个问题需要依赖另一个问题的答案。
每个平台各有一个命令,并且各自使用单独的密钥:
| 命令 | 发送至 | 默认模型 | 密钥 |
|---|---|---|---|
| 询问 Jev 关于此文件 | TypeSafe | jev-1.13.0 | 设置 TypeSafe API 密钥,或 TYPESAFE_API_KEY |
| 询问 Luna 关于此文件 | OpenAI | gpt-6-luna | 设置 OpenAI API 密钥,或 OPENAI_API_KEY |
| 通过 OpenRouter 询问关于此文件 | OpenRouter | typesafe/jev-1.13 | 设置 OpenRouter API 密钥,或 OPENROUTER_API_KEY |
| 通过 Vercel AI Gateway 询问关于此文件 | Vercel AI Gateway | typesafe-ai/jev | 设置 Vercel AI Gateway API 密钥,或 AI_GATEWAY_API_KEY |
| 通过 LLM Gateway 询问关于此文件 | LLM Gateway | jev-1.13.0 | 设置 LLM Gateway API 密钥,或 LLM_GATEWAY_API_KEY |
| 询问 Liquid d1 关于此文件 | Liquid AI | d1 | 设置 Liquid AI API 密钥,或 LIQUID_API_KEY |
| 询问关于此文件的自定义端点模型 | decisionlint.ask.endpoint | decisionlint.ask.endpointModel | 设置端点 API 密钥,或不设置 |
- 通过以下三种方式之一提供该平台的密钥。运行其 设置……API 密钥 命令并粘贴密钥,此操作会将密钥存储在操作系统的密钥链中。或者,在
Settings中的decisionlint.ask.keys下,按平台 ID 输入密钥;该设置只能在用户设置中配置,并且不会包含在 Settings Sync 中。也可以将密钥放入该平台指定的环境变量中,并设置在 VS Code 运行的环境里。 - 打开包含问题的文件,并运行该平台的 询问 命令。
每个命令发送的模型由 decisionlint.ask.models 按平台指定。TypeSafe 的默认模型是设定这些检查阈值时使用的版本;若检查结果来自其他模型,包括 Luna,都会注明这一点。经测量,OpenRouter 的 typesafe/jev-1.13 回答与该版本相同,因此其检查结果不会注明。探测功能会使用 TypeSafe 的密钥和模型,在 TypeSafe 上询问 Jev。
对于你托管的平台,例如 llama.cpp、vLLM、LiteLLM 或 Ollaya,请将
decisionlint.ask.endpoint 设置为其监听的完整 URL,
将 decisionlint.ask.endpointModel 设置为它提供的模型 ID,并且当路径
不以 /systemone 或 /decisions 结尾时,将 decisionlint.ask.endpointShape 设置为 typesafe 或 openai。然后运行 Ask the Model at My
Endpoint。如果你设置了 endpoint,它会发送 endpoint 键;否则会发送
TYPESAFE_API_KEY 或 OPENAI_API_KEY 中对应形态的 SDK 会读取的密钥;如果没有密钥,则不发送密钥,这也是连接本地 llama.cpp 的方式。端点只能在你的用户设置中设置,因此存储库无法将你的密钥指向它自行选择的主机。
运行前需要了解:
- 它会使用你的密钥和额度。 每个问题发送一个请求,每个请求正文再发送一个请求;Jev 每次约 800 个输入 token,Luna 则约为两倍。按 TypeSafe 公布的价格,每个问题只需几千分之一美分;按 OpenAI 的价格,则需百分之一到百分之二美分。发送前显示的数量是向上取整后的估算值,平台实际计费可能有所不同。
- 只会发送问题内容:类型、说明、标准和 ID。除非启用
decisionlint.ask.sendState,否则不会发送你的state。启用后,文件中写出的 state 也会发送,并额外运行两项检查:state 是否包含每个问题所询问的内容,以及其中是否包含向阅读者发号施令的文本。 - 包含运行时构造部分的问题不会发送。 摘要会说明有多少个问题未发送。
- 每次运行最多发送
decisionlint.ask.maxCalls个请求,默认为 25 个。 - 在不受信任的工作区中会禁用此功能。 运行 Workspaces: Manage Workspace Trust 并信任该文件夹即可启用。
- 编辑文件后,检查结果会消失,因为它们针对的是旧文本。检查期间编辑或关闭文件都会停止检查。
- 每条检查结果都会显示 Jev 给出的概率。 请将其视为附带数字的论据,而不是裁决。
启用 decisionlint.ask.confirm 后,它会先告知你将发送多少个请求、大约会使用多少个 token 以及费用,然后等待你确认。此选项默认关闭:运行命令即表示决定发送。
探测问题
将光标放在一个问题中,然后运行 DecisionLint: Probe the Jev Question at the Cursor。它会根据旁边写出的 state 对该问题进行检查:先按原样发送三次,然后改变布局再发送:反转选项顺序、隐藏选项名称、反转级别顺序、移除标准。打开的报告会显示答案是否发生变化。
如果仅仅改变选项顺序就导致答案变化,说明这个问题并没有真正做出判断。如果三次相同请求得到不同结果,说明输入难以判断。
文件中必须写出 state;它会发送该 state,且需要五到六个请求。
此扩展中没有其他功能会使用网络。在命令行中,只有 --ask 会使用网络。
项目设置
在项目中放入 decisionlint.json,编辑器和命令行都会读取它,因此你编写时看到的内容就是 CI 报告的内容。
{
"rules": { "DEC004": "error", "DEC110": "warning" },
"fallbackOptions": ["other", "none", "unsure"],
"ignore": ["DEC004:department"],
"exclude": ["fixtures", "**/*.generated.ts"]
}
exclude 列出无需 lint 的文件和文件夹,路径相对于设置文件。** 可跨越文件夹,* 只匹配当前文件夹,而不含斜杠的名称可匹配任意深度。编辑器和命令行会排除相同的文件。
从被 lint 的文件开始向上查找,最近的设置文件优先,因此文件夹可以有自己的设置文件。适用时,它会替换编辑器中的 rules、fallbackOptions 和 ignore 设置。如果无法读取该文件,则不会 lint 任何内容,状态栏会说明原因。不认识的键会被视为错误,因此拼写错误不会悄悄导致某条规则仍然生效。
要忽略某条检查结果,请使用灯泡菜单:Disable DEC004 for this line
或 for this file 会为你写入注释。严格 JSON 不支持注释,因此要在其中忽略检查结果,请使用 ignore。
它不会告诉你的内容
- 格式正确的问题在你的数据上是否正确。 这需要带标签的示例和对 Jev 的调用。
- 关于它无法看到的值的任何信息。 由变量构成的问题会被报告为无法读取,并计入状态栏。文件的部分内容尚未读取时,绝不会显示为干净。
- 关于措辞的所有信息。
DEC101到DEC112是针对英文文本的启发式规则。只有当措辞不佳的问题在我们的运行或他人的运行中导致 Jev 给出的答案或置信度下降时,才会启用相应规则;而且只有当它在未用于调优的公开代码中,对至少 10 条发现里的 9 条判断正确时,才会继续启用。TypeSafe 文档中描述故障模式的 4 条规则——DEC104、DEC107、DEC108和DEC111——在jev-1.13.0上没有失效,因此处于关闭状态。DEC110和DEC112会导致答案减少,但在第二个公开样本中未达到 10 条中 9 条的标准,分别为 9 条中 8 条和 3 条中 2 条,因此从 1.1.0 起处于关闭状态。在decisionlint.rules中启用其中任意一条。另有两条规则因每次触发都判断错误,已在 0.4.0 中移除。 - 关于非英文问题的任何信息。
- 单独一个对象缺少什么。
questions映射之外的{ type, instructions }可能是模板,也可能是构造器的一部分,因此只有在请求中才会报告缺少criteria的问题。
DEC004 仅提供信息,因为它背后的建议有条件:当选项可能无法涵盖所有输入时,添加回退选项。TypeSafe 自身文档中的 Choice 示例有 11 个,其中 9 个会触发此规则。如果你的选项已穷尽所有情况,可针对该问题关闭此规则。
忽略发现
在代码中:
// decisionlint-disable-next-line DEC004
const side = { type: 'choice', instructions: 'Heads or tails?', criteria: { heads: null, tails: null } };
decisionlint-disable-line 会忽略同一行,decisionlint-disable 会忽略整个文件。
不指定代码即可关闭所有规则。
不会忽略任何内容的注释会被报告为 DEC010,因此修复发现后遗留的注释无法隐藏之后的新发现。
为 JevLint-LE 编写的内容仍然有效:jevlint-le-disable 注释,以及注释、rules 或 ignore 中的 JEV 代码,都会被永久读取。每条发现都会附带 DEC 代码。
修复
有些发现无需你做决定即可修复:Noul 的 criteria 键写成了 API 要求 true 的 yes、问题类型拼写错误、criteria 格式不正确。npx decisionlint --fix 会写入这些修复内容,每次运行的摘要也会说明可以修复多少项。
在编辑器中,保存时修复会执行相同操作:
{ "editor.codeActionsOnSave": { "source.fixAll.decisionlint": "explicit" } }
添加回退选项和固定模型会改变正常请求的行为,因此绝不会替你写入。这些操作会保留在灯泡菜单中。
JSON 不支持注释,因此请使用此设置:
{ "decisionlint.ignore": ["DEC004:side"] }
CLI
相同的 lint 检查也可在编辑器之外运行,供 CI 和 VS Code 以外的任何编辑器使用。
编辑器和 CI 使用同一版本
像安装其他 linter 一样,将它安装到项目中:
npm install --save-dev decisionlint
之后,编辑器会使用该副本进行 lint,而不是扩展自带的副本,因此你在输入时看到的结果,就是 npx decisionlint 在 CI 中报告的结果。版本会随 package.json 一起变更,并同时对所有人生效。使用项目副本时,状态栏会显示 project 0.5.2。
未安装时,扩展会使用自带副本进行 lint,无需任何设置。
当项目副本无法使用时,扩展也会改用自带副本;状态栏会显示 project has 0.2.0,并在工具提示中说明原因:
- 工作区不受信任,因为加载该副本会运行其中的代码
- 该副本早于 0.3.0,这是编辑器能够加载的首个版本
- 该副本加载失败,或无法读取这类文件
如果无法读取副本的 package.json,则没有版本可显示,因此状态栏会显示 project copy unreadable,并同样在工具提示中说明原因。
无论项目安装了什么,Ask 命令和探测功能始终运行扩展自带的代码。
npx decisionlint src/ # a directory, searched for files it reads
npx decisionlint request.jev.json # one file
npx decisionlint --format github . # annotations on a pull request
运行通过时,它会以 0 退出;发现的问题导致运行失败时,以 1 退出;无法完成所要求的操作时,以 2 退出,例如遇到未知选项、缺少路径或没有可读取的内容。错误总会导致运行失败。只有警告数量超过 --max-warnings 时,警告才会导致运行失败。
| 选项 | 作用 |
|---|---|
--format <stylish|compact|json|github|sarif|junit> | 指定发现的问题的打印方式。stylish 按文件分组,是默认格式。compact 每行显示一项,格式为 path:line:column。sarif 用于代码扫描,junit 用于测试报告工具。也可使用 -f |
--rule <CODE=level> | 将一条规则设为 off、hint、info、warning 或 error。也可以用规则名称代替代码,warn 表示 warning |
--config <file> | 使用此设置文件,并且不在文件附近查找 decisionlint.json。也可使用 -c |
--max-warnings <n> | 报告的警告超过 n 条时,运行失败 |
--stdin-filename <path> | 将标准输入作为该文件进行检查 |
--fix | 写入仅修复 API 会拒绝内容的修复 |
--quiet | 仅打印错误 |
--ask | 还会就每个问题询问模型。请求会发送到模型 ID 所指的平台:默认使用 TYPESAFE_API_KEY 发送到 TypeSafe;使用 --ask-model gpt-6-luna 时,使用 OPENAI_API_KEY 发送到 OpenAI;也可以按各平台的名称指定 OpenRouter、Vercel AI Gateway、LLM Gateway 或 Liquid AI,或者发送到 --ask-endpoint 指定的位置 |
--ask-plan | 显示 --ask 将发送的内容,但不发送任何内容 |
--ask-platform <id> | 指定 --ask 的发送平台:typesafe、openai、openrouter、vercel、llmgateway 或 liquid。每个平台都有自己的密钥变量和对应名称格式的默认模型。不指定时,模型 ID 会将请求发送到其名称所指的平台 |
--ask-model <id> | 指定 --ask 使用的模型:Jev 版本、OpenAI Decisions API 的 gpt-6-luna,或其他平台名称格式的模型 ID。默认值为 jev-1.13.0 |
--ask-endpoint <url> | 指定 --ask 发送请求的完整 URL,适用于你托管的平台,或模型名称未指定主机的网关。也可从 DECISIONLINT_ASK_ENDPOINT 读取。如果工具不识别该主机,请求会发送对应格式的密钥,或不发送密钥 |
--ask-shape <shape> | 当 --ask-endpoint 的路径不以 /systemone 或 /decisions 结尾时,指定发送到该地址的请求格式:typesafe 或 openai。也可从 DECISIONLINT_ASK_SHAPE 读取 |
--ask-max-calls <n> | 指定一次运行中 --ask 最多可发送的请求数。默认值为 25 |
--ask-send-state | 使用 --ask 时,也发送写入文件的状态 |
--color、--no-color | 为默认格式启用或关闭颜色。不指定任一选项时,终端中会启用颜色,除非设置了 NO_COLOR |
--no-error-on-unmatched-pattern | 路径中没有可检查的文件时,运行仍通过 |
--mcp | 以 MCP 服务模式运行 |
摘要行始终会说明有多少问题未能完整读取,并列出因大小而跳过的文件,以及无法读取的文件或文件夹。它不会跟随链接文件夹。除非传入 --ask,否则不会运行需要询问 Jev 的检查,也不会使用网络。
在 CI 中及提交前
在 GitHub 上,此 action 会为拉取请求添加注释:
- uses: nolindnaidoo/[email protected]
with:
paths: src
对于 GitHub 代码扫描,请生成 SARIF 并上传:
- run: npx decisionlint --format sarif src > decisionlint.sarif
continue-on-error: true
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: decisionlint.sarif
--format junit 会写入 Jenkins、GitLab 和大多数测试报告工具可读取的 XML。只有错误会被视为失败用例,因为只有错误会导致运行失败。
搭配 pre-commit 使用:
- repo: https://github.com/nolindnaidoo/decisionlint
rev: v1.1.0
hooks:
- id: decisionlint
询问模型
--ask 会在 lint 后运行向决策模型提问的检查项 DEC301 至 DEC312。这是唯一会使用网络的选项,且无法通过设置文件启用。
npx decisionlint --ask-plan src/ # what it would send. No key, nothing sent
TYPESAFE_API_KEY=... npx decisionlint --ask src/ # send it
--ask-platform用于指定请求发送到哪里:typesafe、openai、openrouter、vercel、llmgateway或liquid。每个平台都有以自身拼写命名的默认模型,以及各自的密钥变量:TYPESAFE_API_KEY、OPENAI_API_KEY、OPENROUTER_API_KEY、AI_GATEWAY_API_KEY、LLM_GATEWAY_API_KEY或LIQUID_API_KEY。未指定该选项时,若--ask-model使用平台对应的拼写,或使用适用于 OpenRouter 的~typesafe/jev-latest,请求就会发送到该平台;裸 ID 则发送到其厂商。密钥只会从相应变量中读取,不会从其他地方读取,也绝不会打印出来。--ask-endpoint或DECISIONLINT_ASK_ENDPOINT会将请求发送到你托管的平台;如果路径未说明其采用哪种格式,可通过--ask-shape指定。若名称未指向任何主机,例如openai/gpt-6-luna-decisions,则在未指定该选项时会拒绝请求,不会发送任何内容。- 问题中包含运行时构建的部分时,绝不会发送该问题。摘要会统计被拦截的问题数。
- 除非添加
--ask-send-state,否则不会发送状态信息。 --ask-max-calls限制整个运行过程中的调用次数。若运行所需调用次数超出限制,将以状态码 2 退出,因此作业不会在只检查项目部分内容后通过。- 密钥被拒绝、请求失败或运行被你中止时,也会以状态码 2 退出,并说明已收到答复的请求数和计划请求总数。
- 默认情况下,这些发现项的级别为警告或更低,因此只有在通过
--rule提高级别,或指定--max-warnings时才会导致运行失败。
命令
每个命令都可以设置快捷键。打开 键盘快捷方式,搜索 DecisionLint,然后为列表中的任意命令按下你想使用的按键。默认情况下没有绑定任何快捷键,因此不会与现有快捷键冲突。
也可以将它们写入 keybindings.json。以下按键仅为示例:
[
{ "key": "ctrl+alt+j", "command": "decisionlint.lintFile", "when": "editorTextFocus" },
{ "key": "ctrl+alt+k", "command": "decisionlint.askJev", "when": "editorTextFocus" }
]
| 命令 | ID |
|---|---|
| 检查此文件中的问题 | decisionlint.lintFile |
| 检查工作区中的问题 | decisionlint.lintWorkspace |
| 打开设置 | decisionlint.openSettings |
| 就此文件询问 Jev | decisionlint.askJev |
| 就此文件询问 Luna | decisionlint.askLuna |
| 通过 OpenRouter 就此文件提问 | decisionlint.askOpenRouter |
| 通过 Vercel AI Gateway 就此文件提问 | decisionlint.askVercel |
| 通过 LLM Gateway 就此文件提问 | decisionlint.askLlmGateway |
| 通过 Liquid d1 就此文件提问 | decisionlint.askLiquid |
| 就此文件询问我端点上的模型 | decisionlint.askEndpoint |
| 设置 TypeSafe API 密钥 | decisionlint.setTypeSafeApiKey |
| 设置 OpenAI API 密钥 | decisionlint.setOpenAIApiKey |
| 设置 OpenRouter API 密钥 | decisionlint.setOpenRouterApiKey |
| 设置 Vercel AI Gateway API 密钥 | decisionlint.setVercelApiKey |
| 设置 LLM Gateway API 密钥 | decisionlint.setLlmGatewayApiKey |
| 设置 Liquid AI API 密钥 | decisionlint.setLiquidApiKey |
| 设置端点 API 密钥 | decisionlint.setEndpointApiKey |
| 清除 API 密钥 | decisionlint.clearApiKeys |
| 检查光标处的问题 | decisionlint.probeQuestion |
Ask 命令和探测命令会使用你的密钥发送请求,因此请为它们选择不容易误触的快捷键。
设置
| 设置项 | 默认值 | 含义 |
|---|---|---|
decisionlint.rules | {} | 按规则代码或名称设置严重级别:off、hint、info、warning、error。如果条目指定了不存在的规则或级别,检查会停止并说明原因,与 decisionlint.json 中的行为一致 |
decisionlint.fallbackOptions | other、none、unknown、unclear、uncertain、unsure、ambiguous、undetermined、neither、not stated、not applicable、insufficient evidence、cannot tell、n/a,以及另外五种语言中的“other” | 使选项成为 DEC004 后备选项的词。选项名称中只要包含其中一个词,就会被视为符合条件,因此 none_implied 和 other_or_unclear 也符合条件 |
decisionlint.ignore | [] | 要忽略的 CODE:questionId 条目 |
decisionlint.target | "" | 所有请求发送到的平台,用于确定其限制。留空时,会根据每个模型 ID 读取平台 |
decisionlint.ask.models | typesafe 对应 jev-1.13.0,其余平台使用各自的默认值 | 各个平台的 Ask 命令发送的模型 ID。TypeSafe 的默认值是校准检查时使用的版本 |
decisionlint.ask.keys | 空 | 各个平台的 API 密钥,以及你指定的主机对应的 endpoint。如果不想使用钥匙串,可以在此输入密钥。仅限用户设置,不会同步 |
decisionlint.ask.endpoint | "" | Ask the Model at My Endpoint 要向其发送请求的平台托管 URL。仅限用户设置 |
decisionlint.ask.endpointShape | "" | 发送到该端点的数据格式:typesafe 或 openai。当路径未表明格式时使用 |
decisionlint.ask.endpointModel | "" | 发送到该端点的模型 ID |
decisionlint.ask.maxCalls | 25 | 单个 Ask 命令最多可发送的请求数 |
decisionlint.ask.sendState | false | 同时发送文件中写入的状态,以便模型进行检查 |
decisionlint.ask.confirm | false | 付费命令发送任何内容前,显示将要发送的内容及费用,并等待确认 |
decisionlint.include | JSON、JavaScript、TypeScript、Python、Rust 和 Go 文件 | 工作区命令要检查的文件 |
decisionlint.exclude | node_modules 和构建输出 | 工作区命令要跳过的文件 |
decisionlint.maxFileSizeBytes | 1000000 | 超过此大小的文件会被报告为已跳过 |
decisionlint.notificationsLevel | important | 通知中显示的信息量。all 会在每条命令后添加摘要,silent 仅在命令无法运行时显示消息。检查结果始终会显示在编辑器中 |
隐私与安全
- 检查绝不会使用网络。 扩展、命令行和 MCP 服务无需密钥即可进行检查,也不会发送任何内容。不会收集遥测数据。
- 只有 Ask 命令、探测命令、
--ask和两个会发送数据的 MCP 工具 会连接到平台,并使用你的密钥;且只有在你运行它们时才会连接。它们会发送 问题,不会发送你的state,除非你启用decisionlint.ask.sendState或--ask-send-state。探测命令会发送问题旁边写入的状态。 - 密钥不会留在代码库中。 Set ... API Key 命令会将
密钥存储在操作系统钥匙串中。
decisionlint.ask.keys和decisionlint.ask.endpoint只能在用户设置中配置;这些设置不会纳入设置同步, 因此代码库无法将你的密钥发送到任意主机。命令行只会从环境变量 读取密钥,且绝不会打印密钥。 - 不受信任的工作区无法发送数据。 其中的 Ask 命令会被禁用,也不会加载 项目自带的检查器,因为加载它会运行工作区中的代码。
- MCP 服务不会写入任何内容,并且只会从客户端启动它时提供的环境、
或其目录中的
.env.local或.env读取密钥,绝不会从工具参数中读取, 因此密钥不会进入 Agent 的对话记录。
文档
| 内容 | 位置 |
|---|---|
| 产品是什么,以及计划中的内容 | SPEC.md |
| 产品如何构建和维系:架构、不变量、发布 | AGENTS.md |
| 每条规则,以及一个会触发标记的示例和一个不会触发的示例 | docs/rules |
| 每个阈值,以及它在每种模型和平台上捕获的内容 | docs/cutoffs.md |
| 更改内容 | CHANGELOG.md |
| 工具页面及其余同系列工具 | letools.dev/tools/decisionlint |
开发
bun install
bun run typecheck && bun run lint && bun run test
bun run build
bun run test:integration # real VS Code against samples/
bun run package # release/decisionlint-<version>.vsix
bun run test:e2e-vsix # the packaged VSIX in a clean profile
AGENTS.md 是工程标准。SPEC.md 说明了产品是什么,以及计划中的内容。samples/ 是一个放有预设错误的工作区,供 bun run test:integration 使用;该命令会驱动一个真实的 VS Code。
更多 LE 系列工具
面向每个模型日常工作的 16 款单一用途工具。每款都提供 Rust CLI 和 MCP 服务。详情请访问:letools.dev
提取
- String-LE — 提取代码库中的所有字符串,方便人们阅读
- Numbers-LE — 提取代码库中的所有硬编码数字,方便人们检查
- Units-LE — 提取所有带单位的量并进行规范化,遇到含义不明确的量时按名称拒绝处理
- Dates-LE — 提取所有日期和时间戳,并给出每个值解析出的确切时刻
- IDs-LE — 提取所有 UUID、ULID、NanoID、ObjectId 和 Snowflake,并解码其中的时间信息
- IPs-LE — 提取所有 IP 地址、CIDR 网段和 MAC 地址,进行规范化并按作用域分类
- URLs-LE — 提取代码库中的所有 URL,并给出其协议和确切位置
- Paths-LE — 提取代码库中的所有文件路径,并指出它们是否仍然指向有效目标
- Colors-LE — 提取代码库中的所有颜色,并指出哪些颜色不在你的调色板中
检查
- Regex-LE — 查找代码库中的所有正则表达式,并报告哪些可能导致灾难性回溯
- Versions-LE — 查找仓库各清单文件中对同一依赖的约束不一致之处
- i18n-LE — 识别项目使用的 i18n 库,再依据该库的规则审计其语言目录
- Scrape-LE — 在编写抓取器之前检查页面是否可抓取,并说明何时无法判断
防护
- Secrets-LE — 查找代码库中的硬编码凭据,并且绝不在报告中打印凭据
- EnvSync-LE — 比较目录树中的 dotenv 文件,并指出哪些文件缺少哪些键
- Unicode-LE — 查找隐藏含义的 Unicode 字符——双向控制符、不可见字符、同形异义字符、混合文字脚本
每款工具都独立运行:没有共享 crate,也没有发布的核心库。若其中两款工具给出相同结论,那是因为同一个答案恰好都正确。
联系 — nolindnaidoo.com · GitHub · LinkedIn
nolindnaidoo 的其他项目
Rust — pixelcoords 和 pixelactions 构成一个循环:pixelcoords 负责回答在哪里,pixelactions 则在该位置执行操作。它们各有自己的工具和风格,并不属于 LE 系列。
- pixelcoords — 冻结屏幕画面、标记区域,并获取像素级精确的坐标和裁剪图 pixelcoords.dev · crates.io · docs.rs
- pixelactions — 使用经人工验证的坐标执行交互,并确认操作已生效 pixelactions.dev · crates.io · docs.rs
许可证
MIT © nolindnaidoo
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。