Skip to content
dsh.fish
Bundle

@kiwifruit/dsh-syntax-checker

Multi-language syntax checking service (JS/TS/YAML/Python): bracket matching, invisible chars, syntax errors, via Tree-sitter + Ruff

Source
kiwifruit13
License
MIT
Updated
Updated 2 days ago

Readme

# DSH Syntax Checker

多语言语法检查服务(JS / TS / YAML / Python):括号匹配、不可见字符、语法错误检测与自动修复。

同一套引擎支持两种运行形态:

1. **DSH Cordis 插件**(`@kiwifruit/dsh-syntax-checker`)—— 由 DSH 宿主经纤程加载,向模型暴露 `syntax_check` 工具,并向其他插件提供 `syntaxChecker` 服务;
2. **独立 Node.js 服务 / 库** —— Fastify HTTP API(含 OpenAPI 文档),或 `createSyntaxChecker()` 直接嵌入上游系统。

## 功能特性

| 能力 | 说明 |
|------|------|
| 括号匹配 | `()` / `[]` / `{}` / 字符串字面量内的括号不参与匹配 |
| 不可见字符 | 检测零宽空格、BOM、双向控制符等隐形陷阱 |
| 语法错误 | Tree-sitter 解析(JS/TS/YAML)+ Ruff(Python) |
| 行尾空白 | 通用规则 |
| YAML 专项 | 重复键、Tab 缩进 |
| 自动修复 | Ruff 诊断自带 fix.edits + 编辑应用器(行/列 → offset),多轮迭代收敛 |

## 技术栈

- **运行时**:Node.js ≥ 20,ESM
- **解析**:`web-tree-sitter`(WASM 语法,位于 `assets/`)+ `ruff`(外部 CLI,Python 后端)
- **HTTP**:Fastify 5 + `@fastify/swagger`(OpenAPI 文档,`openapi.yaml` 为导出产物)
- **插件体系**:`@deepseek-ai/cordis`(纤程 / waterfall 事件)+ `@deepseek-ai/dsh-tools`(`defineTool`)
- **配置校验**:`@deepseek-ai/schemastery`(Standard Schema)
- **质量门禁**:TypeScript 5 严格模式 + ESLint 9 flat config + Vitest 4(覆盖率 v8)

## 快速开始

### 独立 HTTP 服务

```bash
npm install
npm run build
npm start          # 生产:node dist/api/server.js
npm run dev        # 开发:tsx src/api/server.ts
```

| 方法 | 路径 | 用途 |
|------|------|------|
| GET | `/health` | 健康检查(含 ruff 可用性探测) |
| POST | `/api/v1/lint` | 单文件语法检查 |
| POST | `/api/v1/lint/batch` | 批量检查(有上限,见 `schemas.ts` 的 `MAX_BATCH_SIZE`) |
| POST | `/api/v1/fix` | 自动修复,返回修复后源码 |

响应采用统一包装(`code` / `message` / `timestamp` / `data`),HTTP 状态码表达传输层状态,业务错误码在响应体中。完整契约见 `openapi.yaml` 或启动服务后访问 Swagger UI;错误码清单见 `docs/error-codes.md`。

### 作为库嵌入(独立模式)

```ts
import { createSyntaxChecker } from '@kiwifruit/dsh-syntax-checker';

const checker = createSyntaxChecker(undefined, {
  timeoutMs: 30_000,
  maxRetries: 2,
  policies: { timeout: true, retry: true, observation: true },
});

const result = await checker.lint({
  filePath: 'a.py',
  sourceText: 'import os\nimport os\n',
});
// result.diagnostics / errorCount / warningCount / infoCount
```

独立模式无 Cordis 纤程,配置在此处经 `Config` schema 同步校验并填充默认值。

### 作为 DSH 插件安装

> **推荐安装**:插件已发布到 npm(scoped 包 `@kiwifruit/dsh-syntax-checker`)。裸包名解析到 `latest`,刚发版受 pnpm 24 小时最小发布年龄冷却限制——立即使用请写精确版本号(精确版本自动豁免冷却期)。

**安装命令**(`--profile` 是必选参数,指定目标配置文件,如 `web` / `headless`):

```bash
dsh plugin --profile web add @kiwifruit/dsh-syntax-checker@0.1.0
```

`<spec>` 支持多种来源:

| 来源类型 | 命令格式 | 示例 |
|---------|---------|------|
| npm 包 | `dsh plugin --profile web add <包名>` | `dsh plugin --profile web add @kiwifruit/dsh-syntax-checker` |
| 精确版本 | `dsh plugin --profile web add <包名>@<版本>` | `dsh plugin --profile web add @kiwifruit/dsh-syntax-checker@0.1.0` |
| GitHub 仓库 | `dsh plugin --profile web add github:<owner>/<repo>` | `dsh plugin --profile web add github:owner/dsh-syntax-checker` |
| 本地目录(符号链接) | `dsh plugin --profile web add link:<路径>` | `dsh plugin --profile web add link:D:/Git/gitee/DSH-Syntax-Checker-main` |
| 本地目录(复制安装) | `dsh plugin --profile web add file:<路径>` | `dsh plugin --profile web add file:./dsh-syntax-checker` |

> 用 `link:` 时需先 `npm run build`(它不触发 `prepare`,加载的是 `dist/`),并在源码目录自备依赖(pnpm 不为 `link:` 依赖解析依赖)。

> 裸包名解析到 npm `latest`,受 pnpm 24 小时最小发布年龄冷却限制——**刚发版要立即安装,请写精确版本号**(精确版本自动豁免冷却期)。

> **GitHub 来源须知**(依据 DSH 官方构建陷阱,工具示例 第十二步):git install 拉取的是**源码不是构建产物**。本包已提供自包含的 `prepare` 脚本(安装后自动 `tsc`),但 pnpm ≥10 默认拒绝运行 git 依赖的 prepare 脚本,用户需在 profile 的 `pnpm-workspace.yaml` 中放行:
>
> ```yaml
> allowBuilds:
>   dsh-syntax-checker: true
> ```
>
> ⚠️ 该放行等于授权安装时在沙箱外执行本包构建脚本,建议 pin commit(`github:owner/dsh-syntax-checker#<sha>`)。更稳的方式是 npm 包 / tarball 安装(预构建产物,无需 allowBuilds)。

安装后生效链路:`dsh plugin` 是 **pnpm 转发器 + bundle 自动登记**,不是补丁合并——先 `pnpm add` 落包,再按已安装状态对账 `dsh.profile.bundles`(包的 manifest 声明 `dsh.bundle` 即登记进去),启动时由 bundle 层读取 `cordis.patch.yml` 参与分层组合。因此 profile 的 `cordis.patch.yml` 不必再写本插件条目。增删 bundle 后需重启 DSH 进程才生效(`patchReload: live` 只覆盖补丁层)。

**安装后验证**:

```bash
dsh --profile web --dump-config | tail -20   # syntax-checker 行是否进入组合树
```

然后启动 `dsh --profile web`,向模型确认 `syntax_check` 工具已出现在工具列表中(装配验证的两个信号:工具列表 + 服务就绪)。

**升级**:npm 来源与安装命令相同,再次执行 `dsh plugin --profile web add @kiwifruit/dsh-syntax-checker` 即升级;`link:` 来源改完源码 `npm run build` 即可。**卸载**:`dsh plugin --profile web remove @kiwifruit/dsh-syntax-checker`。

> 安全提醒:安装插件等同于运行第三方代码。安装前请查看插件源码,不熟悉的插件建议在隔离环境先测试。

### 插件装配清单(本地 loader 装配 / 开发态)

仓库根目录的 `cordis.yml` 即装配清单(格式遵循 `约束/001接口/工具扩展.md` §3.1–3.2),供本地 loader 加载;随包分发的 `cordis.patch.yml` 以 `- insert:` 语义携带同一条目,供 DSH 的 bundle 层读取。二者由 `package.json` 的 `dsh.bundle.patch` 串起来——**该字段是 `dsh plugin add` 判定"本包是 bundle"的依据**,据此登记进 `dsh.profile.bundles`:

```yaml
- id: syntax-checker
  name: '@kiwifruit/dsh-syntax-checker'
  inject: [tools]
  config:
    timeoutMs: 30000
    maxRetries: 2
    verbose: false
    policies: { timeout: true, retry: true, observation: true }
```

插件入口**以原名**导出 Cordis 契约:`apply` / `name` / `inject` / `provide` / `Config`。这是硬性前提——若只以别名导出,Cordis 会跳过 schema 校验与默认值填充(`Config` 缺原名时是**静默**跳过,故障在运行期才炸出)。

加载后:

- **向模型**:注册 `syntax_check` 工具(经 `defineTool`,参数 `filePath` / `sourceText` / `language?` / `fix?`);
- **向其他插件**:`ctx.provide('syntaxChecker', engine)`,消费方声明 `inject: ['syntaxChecker']` 即可调用 `lint` / `fix`。

## 配置项

| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `timeoutMs` | number | `30000` | 单次语法检查的超时预算(毫秒);单次调用可用 `options.timeoutMs` 覆盖 |
| `maxRetries` | number | `2` | 后端瞬时故障的最大重试次数 |
| `verbose` | boolean | `false` | 是否记录成功事件(默认只记录 fail / timeout) |
| `policies.timeout` | boolean | `true` | 超时伴生策略开关 |
| `policies.retry` | boolean | `true` | 重试伴生策略开关 |
| `policies.observation` | boolean | `true` | 观测伴生策略开关 |

> `cordis.yml` 的 `config` 是**整行替换、非深度合并**——覆盖时必须写全整个对象。
> 关闭某项策略后,可外接同名伴生插件替换它(策略可装卸,不重复注册)。

## 架构

### 三角色接缝

```
Definition(definition.ts)      抽象接口 + 事件词汇表 + 错误码
    ▲                    │
    │ 实现                │ 消费
Provider                Consumer
├─ RuffBackend(Python) ├─ ParserEngine(中间层)
└─ TreeSitterBackend    └─ syntax_check Tool / syntaxChecker 服务
   (JS/TS/YAML)
```

### 事件模型(Cordis waterfall 语义)

- **`syntax/check`**(waterfall):lint 的执行门。伴生策略通过它挂载横切逻辑:
  - `TimeoutPolicy` 以 `prepend` 注册,**恒为最外层**——保证「超时包住重试」,与插件加载顺序无关;
  - `RetryPolicy` 默认追加,**位于链尾**——waterfall 内部 `cbs.shift()` 共享游标,多次调 `next()` 的监听器若不在链尾,重试会跳过其后所有监听器;
  - 不调 `next()` 即短路整条链(含默认行为)。
- **`syntax/check-end`**(emit):检查结束通知(`status: success | fail | timeout`、耗时、诊断列表、traceId),观测策略经此记录结构化日志。

### 平台抽象(platform.ts)

`PlatformContext` 屏蔽「是否运行在 Cordis 宿主内」的差异:

- 有宿主:waterfall / emit / 日志 / 工具服务走真实 ctx;
- 无宿主:Node 等价实现回退;
- 宿主服务**存在但抛错**时同样回退(空字符串是合法返回值);
- `fs` / `bash` 在 cordis 4.0.2 上并非必需依赖,`inject` 只声明 `['tools']`,多声明会让纤程卡在 PENDING。

### 目录结构

```
src/
├── core/           # 领域核心:Diagnostic / Rule / Linter / Fixer / Errors / Language
├── rules/          # 规则实现:universal(括号、不可见字符…)、js-ts、yaml
├── parsers/        # 解析适配:tree-sitter-parser、ruff-runner(spawn + stdin)、ruff-adapter
├── plugin/         # Cordis 插件层
│   ├── definition.ts    # 服务契约 + 事件词汇表(declare module 合并进 Cordis 类型)
│   ├── config.ts        # Schemastery Config schema(Standard Schema)
│   ├── platform.ts      # 宿主/独立双模式平台抽象
│   ├── engine.ts        # ParserEngine 中间层(lint/fix 编排)
│   ├── providers/       # RuffBackend / TreeSitterBackend
│   ├── policies/        # Timeout / Retry / Observation 伴生策略
│   ├── tool.ts          # syntax_check 工具(defineTool)
│   └── index.ts         # 原名导出 apply / name / inject / provide / Config
├── api/            # Fastify 应用:路由、schema、统一错误处理、Swagger
└── index.ts        # 库形态总出口(含别名导出,原名契约单列)
```

## 开发

```bash
npm run typecheck   # tsc --noEmit
npm run lint        # eslint src tests scripts
npm test            # vitest run --coverage
npm run test:watch  # vitest watch
npm run docs:openapi# 导出 OpenAPI 文档(代码为唯一真相源)
```

测试分两层:

- `tests/`:core / rules / parsers / api 的单元与集成测试;
- `tests/plugin/`:插件层测试(事件门、平台回退、工具定义、装配——**装配测试须打 dist 后测**,避免「源码已修、产物漂移」的假绿)。

Python 相关用例需要本机可用 `ruff`,缺失时自动跳过。

## 已知边界

- `ruff fix` 子命令不存在,自动修复走 `ruff check --fix`;exit ≥ 2 视为调用级失败。
- Windows 下从 `file://` URL 取路径必须用 `fileURLToPath`(`URL.pathname` 会产生 `/D:/...` 坏路径)。
- Tree-sitter 的 `Language.load` 接收 `Buffer` 而非 `ArrayBuffer`。

## 文档索引

- `docs/error-codes.md` —— 业务错误码清单
- `docs/dsh-integration-plan.md` / `docs/integration-review.md` —— DSH 集成方案与审查记录
- `openapi.yaml` —— API 契约导出产物
- DSH 插件生态约束文档(装配清单格式、工具定义、事件语义)——本地工作区 `约束/` 目录,未随本仓库发布

## License

MIT

Install

dsh plugin --profile web add github:kiwifruit13/DSH-Syntax-Checker

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source