关于 DecisionLint: Jev/Luna Lint
DecisionLint 检查源文件和代码中的 Jev 与 Luna 决策模型问题,静态规则无需 API 调用或密钥。开发者和 Agent 可通过编辑器、命令行、CI 与 MCP 使用它。
主要功能
- 01检查 JSON 和六种编程语言
- 02编辑器诊断与机械式修复
- 03命令行和 CI 多格式报告
- 04可选模型审查与布局探测
- 05Jev 和 Luna 请求形状共用规则
适用场景
- 01编辑时发现问题措辞缺陷
- 02在 CI 中检查决策请求
- 03让 Agent 检查并安全修复请求文本
- 04测试问题答案是否随布局变化
MCP 相关
MCP 服务提供八种工具,可检查和修复文本或文件、列出并解释规则,也可选择模型审查和问题探测。可选模型调用使用服务器环境或本地环境文件中的密钥。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
8收录的工具列表 · 更新于 4 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| text | string | 是 | 请求 JSON 或源代码。 |
| rules | object | 否 | 要覆盖的规则级别,例如 { "DEC004": "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 保证的是答案的类型,而不是答案本身。格式不当的问题仍可能得到一个看似笃定的数字。这些规则可以发现那些能从文本中识别出的错误。
| 代码 | 名称 | 默认级别 | 含义 |
|---|---|---|---|
| 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”条件描述的是否定情况 |
| DEC110 | degree-levels | warning | Score 级别是程度词、裸标签,或同一个词的高低变化。没有任何内容可供 Jev 用来匹配状态 |
| DEC111 | numeric-encoding | off | 问题通过十六进制或 RGB 编码指代某个值 |
| DEC112 | undefined-boundary | info | 一个没有 criteria 的 Noul 以“大”“经常”或“足够”等词作为判断条件 |
| 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 文档。 |
检查范围
- TypeSafe 或 OpenAI 格式的 JSON 和 JSONC 请求正文。
- 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-16 通过实时 API 在 Luna 上运行。Jev 的阈值能捕获 Luna 上的大多数缺陷,但会漏掉更多缺陷,因此 Luna 保留了这些阈值;所有通过询问 Luna 得出的发现都会说明其阈值是在 Jev 上设定的。措辞规则对 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,用于设定截断值。 - 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,以及 Vercel AI Gateway,openai/gpt-6-luna-decisions,其回答方式与 OpenAI 相同。 - Liquid AI,
d1。
Luna 和 d1 沿用 Jev 的截断值。它们都能发现大多数缺陷,但发现的缺陷比 Jev 少;它们给出的每条检查结果都会注明截断值的设定位置。docs/cutoffs.md 列出了各个截断值、每个模型捕获的已知缺陷数量和误报的正确问题数量,以及各平台的回答与同一模型在其他平台上的回答相差多远。该文档根据已保存的运行结果编写,因此其中的数字就是这些运行结果本身的统计数据。样本数量较少,应将其视为粗略证据。LiteLLM、Eden AI 和 llama.cpp 尚未运行测试。
询问模型关于某个文件的问题
规则 DEC301 到 DEC312 不属于 linting。只有运行 Ask 命令之一,或在命令行中传入 --ask 时,它们才会运行。每条规则都会将文件中的所有问题发送给决策模型,并询问它一些文本模式无法发现的问题:选项之间存在重叠、选项名称与其描述相矛盾、评判标准针对了错误的对象、某个 Choice 应该是 Score、两个问题询问的是同一件事,或某个问题需要另一个问题的答案。
每个平台都有一个对应的命令,并且各自使用独立的密钥:
| 命令 | 发送至 | 默认模型 | 密钥 |
|---|---|---|---|
| Ask Jev About This File | TypeSafe | jev-1.13.0 | Set TypeSafe API Key,或 TYPESAFE_API_KEY |
| Ask Luna About This File | OpenAI | gpt-6-luna | Set OpenAI API Key,或 OPENAI_API_KEY |
| Ask via OpenRouter About This File | OpenRouter | typesafe/jev-1.13 | Set OpenRouter API Key,或 OPENROUTER_API_KEY |
| Ask via Vercel AI Gateway About This File | Vercel AI Gateway | typesafe-ai/jev | Set Vercel AI Gateway API Key,或 AI_GATEWAY_API_KEY |
| Ask via LLM Gateway About This File | LLM Gateway | jev-1.13.0 | Set LLM Gateway API Key,或 LLM_GATEWAY_API_KEY |
| Ask Liquid d1 About This File | Liquid AI | d1 | Set Liquid AI API Key,或 LIQUID_API_KEY |
| Ask the Model at My Endpoint About This File | decisionlint.ask.endpoint | decisionlint.ask.endpointModel | Set Endpoint API Key,或无 |
- 通过以下三种方式之一提供平台的密钥。运行该平台的 Set ... API Key 命令并粘贴密钥,密钥会保存在操作系统的密钥链中。或者,在 Settings 中的
decisionlint.ask.keys下,按平台 ID 输入密钥;该设置只能在用户设置中配置,并且不会纳入 Settings Sync。还可以将密钥放入该平台所命名的环境变量中,并设置在 VS Code 运行的环境里。 - 打开包含问题的文件,并运行该平台的 Ask 命令。
每条命令发送的模型由 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 密钥,命令会发送该密钥;否则会发送 TYPESAFE_API_KEY 或 OPENAI_API_KEY 中由该形态对应的 SDK 读取的密钥;如果两者都没有,则不发送密钥,这也是连接本地 llama.cpp 的方式。此端点只能在用户设置中配置,因此代码库无法将你的密钥指向它自行选择的主机。
运行前须知:
- 使用你自己的密钥和额度。 每个问题发送一个请求,每个请求正文额外发送一个请求;Jev 每次约 800 个输入 token,Luna 则约为两倍。按 TypeSafe 公布的价格,每个问题只需几千分之一美分;按 OpenAI 的价格,则需一两百分之一美分。发送前显示的数量是向上取整的估算值,平台实际收费可能有所不同。
- 只会发送问题:类型、说明、标准和 ID。不会发送你的
state,除非你启用decisionlint.ask.sendState。启用后,文件中写出的状态也会发送,并且会运行另外两项检查:状态是否包含每个问题所询问的内容,以及其中是否包含向读者发出指令的文本。 - 包含运行时构建部分的问题不会发送。 摘要会说明有多少个问题被跳过。
- 每次运行最多发送
decisionlint.ask.maxCalls个请求,默认值为 25。 - 在不受信任的工作区中会禁用此功能。 运行 Workspaces: Manage Workspace Trust,并信任该文件夹即可启用。
- 编辑文件后,检查结果会消失,因为结果针对的是旧文本。检查期间编辑或关闭文件都会停止检查。
- 每条检查结果都会显示 Jev 给出的概率。 请将其视为附有数字的论据,而非结论。
启用 decisionlint.ask.confirm 后,系统会先告知将发送多少个请求、大约使用多少个 token 以及费用,然后等待你确认。此选项默认关闭:运行命令即表示决定发送。
探测问题
将光标放在某个问题中,然后运行 DecisionLint: Probe the Jev Question at the Cursor。它会针对旁边写出的状态发送该问题:先按原样发送三次,然后更改布局后再发送,具体包括反转选项顺序、隐藏选项名称、反转等级顺序以及移除标准。随后会打开报告,显示答案是否发生变化。
如果只改变选项顺序,答案就发生变化,那么这个问题就无法据此作出判断。如果三次相同的请求结果不一致,则输入内容难以判断。
此功能需要文件中写出状态,会发送该状态,并且会消耗五到六个请求。
此扩展中的其他功能都不会使用网络。在命令行中,只有 --ask 会使用网络。
项目设置
在项目中放置一个 decisionlint.json,编辑器和命令行都会读取它,因此你编写时看到的内容与 CI 报告的内容一致。
{
"rules": { "DEC004": "error", "DEC112": "off" },
"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上没有触发错误,因此处于关闭状态。可在decisionlint.rules中启用其中任何一条。另外还有 2 条规则因每次触发时都判断错误,已在 0.4.0 中移除。 - 关于非英文问题的任何信息。
- 单独一个对象缺少的内容。
questions映射之外的{ type, instructions }可能是模板,也可能是构建器的一部分,因此只有在请求中才会报告缺少criteria。
DEC004 仅提供信息,因为其建议是有条件的:当选项可能无法涵盖所有输入时,添加一个回退选项。它会在 TypeSafe 自身文档的 11 个 Choice 示例中触发 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 键写成了 yes,而 API 要求 true;问题类型拼写错误;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。
未安装时,扩展会使用自身的副本,无需任何设置。
当项目副本无法使用时,扩展也会使用自身的副本;状态栏会显示 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.0.1
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 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。