# lspz MVP 产品需求文档 v2.0

> NOTE: LSP 服务器信息已整合到 [docs/specs/003-lsp-compatibility.md](./specs/lsp-compatibility.md)

---

## 0. 调研发现 (Research Findings)

### 0.1 OpenCode 的诊断处理分析

通过分析 OpenCode（流行的 AI Coding Agent），发现其对 LSP 诊断做了一层有损压缩：

| 措施 | 效果 | 丢失的信号 |
|------|------|-----------|
| 只保留 `severity===1` (ERROR) | 大幅减少 token | WARNING/INFO/HINT 全部丢弃 |
| 每文件上限 `MAX_PER_FILE=20` | 限制长度 | 超出部分丢失 |
| 格式化为 `"ERROR [L:C] msg"` | 紧凑文本 | 结构化信息丢失 |
| 工具输出截断 2000 chars/50KB | 防止溢出 | 可能截断诊断 |

**结论**: Agent 端的有损压缩会丢弃 WARNING 级别信息。lspz 可以在协议层以紧凑格式保留更多信号，同时保持结构化。去重合并是收益最大的策略，因为大量同类型错误（如 30 个 "unused variable"）可以被合并为 1 条 + 30 个 range 引用。

### 0.2 真实 LSP 服务器字段使用分析

| 字段 | jinja-lsp | typescript-LS | yaml-LS | lspz 处理 |
|------|-----------|---------------|---------|-----------|
| range | ✅ | ✅ | ✅ | ✅ 保留 |
| message | ✅ | ✅ | ✅ | ✅ 保留 |
| severity | ✅ | ✅ | ✅ | ✅ 编码为 E/W/I/H |
| source | ✅ (固定"jinja-lsp") | ✅ | ✅ (固定"YAML") | ❌ 丢弃 (AI 不需要) |
| code | ❌ | ✅ | ✅ | ⚠️ 可选保留 |
| relatedInformation | ❌ | ✅ | ❌ | ❌ 丢弃 |
| tags | ❌ | ✅ | ❌ | ⚠️ 编码为 U/D |
| data/codeDescription | ❌ | ❌ | ❌ | ❌ 丢弃 |

**结论**: 字段裁剪的收益有限，去重合并才是核心。大多数服务器只发射 4-6 个字段。

### 0.3 核心策略调整

根据调研，压缩策略优先级调整：

1. **去重合并** (新 #1): 相同 message+severity+code 的诊断合并，range 平铺
2. **Range 编码** (新 #2): 数组格式 + delta 编码，消除 JSON 对象嵌套
3. **枚举缩减** (新 #3): severity→E/W/I/H，tags→单字符
4. **字段裁剪** (新 #4): 移除 source/data/codeDescription/relatedInformation

---

## 1. 项目代号与含义
**lspz**
- 全称：**lsp zip**（LSP 压缩代理）
- 象形：字母 `z` 可视作双向箭头 `→←`，象征透明代理的双向通信
- 定位：轻薄、高速、无感，像拉链一样把 LSP 输出“压缩”起来

## 2. 项目目标
构建一个**对 AI Coding Agent 极其友好的 LSP 代理层**，既可作为独立二进制透明部署，更可以作为 Rust 库被直接嵌入任何 Agent CLI 中。
核心优化：对 **Server → Client** 方向的结构化 LSP 消息（优先诊断）进行 Token 敏感的无损压缩，让 LLM 用更少的上下文理解更多代码问题，同时保持所有 LSP 标准接口的兼容性。

## 3. 核心用户与使用场景
- **首要用户**：即将开发的 **Coding Agent CLI**（由你本人维护），需要原生集成 `lspz`，对外表现为标准 LSP Server，对内进行智能压缩。
- **次要用户**：其他 AI Coding Agent 的开发者（如 Continue、Cody 等），可直接使用 `lspz` 独立二进制，无需修改 Agent 代码。

## 4. MVP 范围
### 4.1 核心功能：`textDocument/publishDiagnostics` 压缩
**为什么先做诊断**：诊断消息是最常见、最冗余的 LSP 输出，也是 Agent 依赖的关键上下文。

**支持的压缩策略（均可通过 config 开启/关闭）**：

| 策略 | 描述 |
|------|------|
| 去重合并 | 同一文件中相同 `message` 且相同 `severity` 的诊断合并为一个条目，`ranges` 平铺 |
| 字段裁剪 | 移除非关键字段（`data`, `codeDescription`, `relatedInformation` 等），可配置保留哪些字段 |
| 枚举缩减 | `severity` 转为单字符：`1→E, 2→W, 3→I, 4→H`；`tags` 同理 |
| Range 编码 | 多个 range 记录时采用数组+delta 编码，避免重复字段名 |

**压缩流程**：

```mermaid
flowchart TB
    subgraph Input["Input"]
        RAW["Raw Diagnostics Array"]
    end

    subgraph Process["Compression Pipeline"]
        direction TB

        subgraph Step1["Step 1: Deduplicate"]
            D1["Group by:<br/>message + severity"]
            D2["Merge ranges"]
            D1 --> D2
        end

        subgraph Step2["Step 2: Field Pruning"]
            F1["Remove fields:<br/>data, codeDescription,<br/>relatedInformation"]
            F2["Keep:<br/>message, severity,<br/>range, code"]
            F1 --> F2
        end

        subgraph Step3["Step 3: Enum Compression"]
            E1["Severity:<br/>1→E, 2→W, 3→I, 4→H"]
            E2["Tags:<br/>Similar encoding"]
            E1 --> E2
        end

        subgraph Step4["Step 4: Range Encoding"]
            R1["Delta encoding<br/>for multiple ranges"]
            R2["Array format<br/>avoid repetition"]
            R1 --> R2
        end
    end

    subgraph Output["Output"]
        COMPACT["Compact Format JSON"]
    end

    RAW --> Step1
    Step1 --> Step2
    Step2 --> Step3
    Step3 --> Step4
    Step4 --> COMPACT

    classDef data fill:#7ED321,stroke:#5BA01A,stroke-width:2px,color:#fff
    classDef process fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef result fill:#F5A623,stroke:#D4880F,stroke-width:2px,color:#fff

    class RAW,COMPACT data
    class Step1,Step2,Step3,Step4 process
    class D1,D2,F1,F2,E1,E2,R1,R2 result
```

**输出格式**：压缩后仍为合法的 JSON-RPC `textDocument/publishDiagnostics` 通知，但 `params.diagnostics` 结构可为自定义紧凑格式（Agent 端需能解析，提供参考实现）。

### 4.2 兼容性与标准
- 遵循 LSP 3.17 核心规范（参考 [specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/)）
- 正确处理 `initialize` / `initialized` 握手与能力协商
- 对不支持的请求/通知透明转发，不破坏标准

## 5. 架构设计（核心）
### 5.1 双模式交付，统一核心

```mermaid
flowchart LR
    subgraph Client["AI Agent / LSP Client"]
        direction LR
        A1["Requests"]
        A2["Responses"]
    end

    subgraph Delivery["Delivery Modes"]
        direction LR
        B1["Binary Mode<br/>(CLI: main.rs)"]
        B2["Library Mode<br/>(lib.rs)"]
    end

    subgraph Core["lspz-core"]
        direction TB
        C1["Proxy Core"]
        C2["Interceptor Chain"]
        C3["Codec Layer"]
        C4["Config Manager"]
        C1 --> C2 --> C3
        C4 -.-> C1 & C2 & C3
    end

    subgraph Transport["Transport Layer"]
        direction LR
        T1["stdio"]
        T2["tcp (future)"]
        T3["websocket (future)"]
    end

    subgraph Server["LSP Server"]
        direction LR
        S1["Language Server"]
        S2["File System"]
    end

    A1 --> Delivery
    Delivery --> Core
    Core --> Transport
    Transport --> S1
    S1 --> S2
    S2 --> Transport
    Transport --> Core
    Core --> A2

    classDef userStyle fill:#7ED321,stroke:#5BA01A,stroke-width:2px,color:#fff
    classDef coreStyle fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef transportStyle fill:#F5A623,stroke:#D4880F,stroke-width:2px,color:#fff
    classDef serverStyle fill:#9013FE,stroke:#6A0DAD,stroke-width:2px,color:#fff

    class A1,A2,B1,B2 userStyle
    class C1,C2,C3,C4 coreStyle
    class T1,T2,T3 transportStyle
    class S1,S2 serverStyle
```

**解释**：
- `lspz-core` 是纯逻辑库，不依赖任何特定传输方式，可以被任何 Rust 项目（比如你的 Coding Agent CLI）通过 Cargo 依赖直接使用。
- 独立二进制 `lspz` 只是 `lspz-core` 的一个薄壳，负责从命令行参数构造 config 并启动 stdio 通信。
- 未来你开发 Agent CLI 时，可以直接在代码里 `use lspz_core::Proxy;`，设置配置，然后就能在以库的形式获得完整的代理能力，Agent 本身可以同时扮演 LSP Client 的角色。

### 5.2 内部模块划分（lspz-core）
```
src/
├── main.rs               # 独立二进制入口
├── lib.rs                # 库入口，暴露 public API
├── proxy.rs              # 核心代理：管理生命周期、消息转发、拦截器调用
├── interceptors/
│   ├── mod.rs
│   └── diagnostics.rs    # 诊断压缩拦截器
├── codec/
│   ├── mod.rs
│   ├── json_rpc.rs       # JSON-RPC 2.0 消息解析与序列化
│   └── compact.rs        # 紧凑格式的实现（用于压缩后的诊断表示）
├── transport/
│   ├── mod.rs
│   └── stdio.rs          # stdio 通信实现
├── config.rs             # 配置结构与解析
└── error.rs              # 统一错误类型
```

**模块依赖关系**：

```mermaid
graph TD
    subgraph Entry["Entry Points"]
        MAIN["main.rs<br/>(Binary)"]
        LIB["lib.rs<br/>(Library)"]
    end

    subgraph Core["Core Modules"]
        PROXY["proxy.rs<br/>(Proxy Core)"]
        CONFIG["config.rs<br/>(Configuration)"]
        ERROR["error.rs<br/>(Error Types)"]
    end

    subgraph Interceptors["Interceptors"]
        DIAG["interceptors/<br/>diagnostics.rs<br/>(Diagnostics)"]
        COMP["interceptors/<br/>completion.rs<br/>(Future)"]
    end

    subgraph Codec["Codec Layer"]
        JSON["codec/<br/>json_rpc.rs<br/>(JSON-RPC 2.0)"]
        COMPACT["codec/<br/>compact.rs<br/>(Compact Format)"]
    end

    subgraph Transport["Transport Layer"]
        STDIO["transport/<br/>stdio.rs<br/>(Stdio)"]
        TCP["transport/<br/>tcp.rs<br/>(Future)"]
        WS["transport/<br/>websocket.rs<br/>(Future)"]
    end

    MAIN --> PROXY
    LIB --> PROXY

    PROXY --> DIAG
    PROXY --> JSON
    PROXY --> STDIO

    DIAG --> JSON
    DIAG --> COMPACT

    PROXY -.-> COMP
    PROXY -.-> TCP
    PROXY -.-> WS

    PROXY --> CONFIG
    PROXY --> ERROR
    DIAG --> ERROR
    JSON --> ERROR

    classDef entry fill:#7ED321,stroke:#5BA01A,stroke-width:2px,color:#fff
    classDef core fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef ext fill:#F5A623,stroke:#D4880F,stroke-width:2px,color:#fff
    classDef future fill:#B8B8B8,stroke:#808080,stroke-width:2px,stroke-dasharray: 5 5,color:#fff

    class MAIN,LIB entry
    class PROXY,CONFIG,ERROR,DIAG,JSON,COMPACT,STDIO core
    class COMP,TCP,WS ext
    class COMP,TCP,WS future
```

**关键设计原则**：
- **拦截器链**：`proxy.rs` 维护一个拦截器列表，每个拦截器都可以对 Server→Client 消息进行检查和变形。诊断压缩只是第一个拦截器，后续可添加补全压缩、hover 压缩等。
- **配置驱动**：所有压缩行为通过 `Config` 控制，Agent 可以轻松在代码中构建特定配置的代理实例。
- **错误隔离**：压缩逻辑失败不应导致代理崩溃，应记录错误日志并回退到透明转发。

**拦截器链处理流程**：

```mermaid
flowchart LR
    subgraph Server["LSP Server"]
        S1["Response"]
    end

    subgraph Chain["Interceptor Chain"]
        direction TB
        I1["Inspect"]
        I2["Transform"]
        I3["Forward/Drop"]

        I1 --> I2
        I2 --> I3

        subgraph Interceptors
            D1["DiagnosticsCompressor"]
            D2["CompletionCompressor<br/>(Future)"]
            D3["HoverCompressor<br/>(Future)"]
        end

        D1 & D2 & D3 -.-> I1
    end

    subgraph Client["AI Agent"]
        C1["Request"]
    end

    subgraph Error["Error Handling"]
        E1["Compression Fails"]
        E2["Transparent Forward"]
    end

    S1 --> Chain
    I1 --> I2
    I2 -->|Success| I3
    I2 -->|Failure| E1
    E1 --> E2
    I3 --> C1
    E2 --> C1

    classDef core fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef success fill:#7ED321,stroke:#5BA01A,stroke-width:2px,color:#fff
    classDef error fill:#F5A623,stroke:#D4880F,stroke-width:2px,color:#fff

    class I1,I2,I3 core
    class I3,Chain success
    class E1,E2 error
```

### 5.3 请求/响应流程图（Mermaid）

```mermaid
sequenceDiagram
    participant Agent as AI Agent<br/>(LSP Client)
    participant Lspz as lspz<br/>(Proxy)
    participant Server as LSP Server

    rect rgb(240, 248, 255)
        Note over Agent,Server: Initialization Phase
        Agent->>Lspz: initialize request
        Lspz->>Server: initialize (forward)
        Server-->>Lspz: initialize response (capabilities)
        Lspz-->>Agent: initialize response (forward)
        Agent->>Lspz: initialized notification
        Lspz->>Server: initialized (forward)
    end

    rect rgb(240, 255, 240)
        Note over Agent,Server: Working Phase - Diagnostics
        Agent->>Lspz: textDocument/didOpen
        Lspz->>Server: textDocument/didOpen
        Server-->>Lspz: textDocument/publishDiagnostics (raw)
        Note over Lspz: Interceptor Chain:<br/>Compress Diagnostics
        Lspz-->>Agent: textDocument/publishDiagnostics (compressed)
    end

    rect rgb(255, 248, 220)
        Note over Agent,Server: Other Requests (Transparent)
        Agent->>Lspz: textDocument/hover
        Lspz->>Server: textDocument/hover
        Server-->>Lspz: textDocument/hover response
        Lspz-->>Agent: textDocument/hover response
    end
```

## 6. 配置与环境变量
MVP 期主要使用环境变量，库模式使用 Rust 结构体。

| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `LSPZ_BACKEND_CMD` | 后端 LSP 启动命令 | 无（库模式可编程指定） |
| `LSPZ_ENABLE_DIAG_COMPRESS` | 启用诊断压缩 | `false` |
| `LSPZ_DIAG_FIELDS` | 诊断保留字段列表 | `message,severity,range,code` |
| `LSPZ_DIAG_MERGE` | 是否合并相同诊断 | `true` |
| `LSPZ_LOG_LEVEL` | 日志级别 | `warn` |

库模式对应 `Config` struct，字段清晰。

## 7. 测试与验证目标
### 7.1 集成测试目标 LSP 服务器
- **basedpyright** (Python)
- **gopls** (Go)
- **typescript-language-server** 或 **typescript-go** (微软新版)
- **rust-analyzer** (Rust)

### 7.2 测试方法
- 使用测试代码（Rust 集成测试）加载 `lspz-core`，直接创建 Proxy 并向后端 LSP 注入消息，不依赖 stdio。
- 模拟打开带有错误的文件，观察 `publishDiagnostics` 输出，对比压缩前后字符数及 Token 数（使用 `tiktoken-rs` 模拟 GPT-4 tokenizer）。
- 目标：典型场景下 Token 数降低 ≥ 40%，且压缩后的内容能被标准解析器还原到原始关键信息（至少文件、行号、严重级别、消息无误）。

## 8. 交付物
- `Cargo.toml` 工作区（workspace），包含：
  - `lspz-core` 库
  - `lspz` 二进制
- README.md：项目介绍、架构图、快速开始、压缩策略说明
- 示例文件夹 `/examples/agent_demo.rs`：展示如何以库模式在 Rust Agent 中集成 lspz
- 参考压缩格式文档 `docs/compact_format.md`
- CI：`cargo test` 包含单元测试与集成测试，至少运行 gopls 与 rust-analyzer 的简单场景

## 9. 后续扩展规划（非 MVP）
- 其他消息类型压缩（`completion`, `hover`, `documentSymbol` 等）
- 支持 TCP / WebSocket 传输
- 动态拦截器配置热加载
- 详细的 metrics 与 tracing
- Agent 端标准解压库（Rust 及其他语言）

## 10. 任务拆分（逻辑顺序，可并行）
1. **项目初始化**
   - 创建 workspace，搭建 `lspz-core` 库与 `lspz` 二进制骨架
   - 引入核心依赖：`tokio`, `serde`, `serde_json`, `tower-lsp`（或自研 LSP 框架）、`tiktoken-rs`

2. **消息解析与基础通信层**
   - 实现 JSON-RPC 2.0 消息编解码
   - 实现 stdio transport
   - 实现消息循环：接收→解析→分发

3. **透明代理核心**
   - 实现 `initialize` / `initialized` 握手逻辑，能力协商
   - 实现普通请求/响应的透明转发

4. **拦截器框架**
   - 定义拦截器 trait
   - 在代理主循环中插入拦截器调用点
   - 实现第一个拦截器：诊断压缩

5. **诊断压缩实现**
   - 去重合并算法
   - 字段裁剪
   - 枚举编码
   - Range 数组压缩

6. **配置管理**
   - 环境变量解析
   - 库模式 Config 构建

7. **测试**
   - 单元测试（算法正确性）
   - 集成测试（与至少两个真实 LSP 后端交互，验证压缩率和语义完整性）

8. **文档与示例**
   - README
   - `agent_demo.rs` 示例
   - 压缩格式说明
