# pi-acp-jetbrain

[![npm version](https://img.shields.io/npm/v/@wbline%2Fpi-acp-jetbrain)](https://www.npmjs.com/package/@wbline/pi-acp-jetbrain)
[![npm downloads](https://img.shields.io/npm/dm/@wbline%2Fpi-acp-jetbrain)](https://www.npmjs.com/package/@wbline/pi-acp-jetbrain)
[![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)

一个为 **pi coding agent（pi 编码代理）** 编写的 Agent Client Protocol (ACP) 适配器。主要宿主为 JetBrains IntelliJ。其他 ACP 客户端也能使用，但覆盖不完整。

该适配器以 ACP 服务端的形式运行于标准输入/输出（stdio）上。每个 ACP 会话会启动一个 `pi --mode rpc` 子进程。适配器负责在客户端与 pi 之间翻译消息。

npm 包名：`@wbline/pi-acp-jetbrain`（当前版本见上方徽章）。

## 覆盖范围

该适配器覆盖了会话面：`session/new`、`session/prompt`、`session/cancel`、`session/list`、`session/load`、`session/fork`、
`session/resume`、`session/close`、`session/delete`、`session/mode`。pi 会自行保存会话文件。适配器在
`~/.pi/pi-acp/session-map.json` 处维护一张小型映射表，以便 load 时重新挂载已保存的会话。

助手文本以 `agent_message_chunk` 流式输出。推理内容在提供端发送时以 `agent_thought_chunk` 流式输出。工具调用映射为 `tool_call` 与 `tool_call_update` 事件。

当 pi 上报文件位置时，编辑事件会携带文件位置。适配器依据会话工作目录解析相对路径。对于文本编辑，它会从唯一匹配处找出被修改的行，并上报结构化的 diff。

每个会话都以一段 pi 启动信息（startup block）开始。可在 pi 设置中设置 `quietStartup: true` 隐藏它。

每轮（turn）结束后，适配器会依据 pi 会话统计上报 token 用量与成本。它把这些数据放在不稳定的 `usage` 字段中发送。

当客户端具备时，文本输入请求使用不稳定的 ACP 引导（elicitation）API。符合权限要求的请求走 ACP 权限通道。编辑器请求会显示取消通知，因为引导表单只承载基础字段。

模型选择器通过将 pi 模型映射为 ACP 供应商信息来实现。pi 将供应商凭据保存在 RPC 面之外。

斜杠命令可加载 pi 基于文件提示词以及在 pi 中启用的工具。同样还有内建命令 `/compact`、`/export`、`/session`、`/name`、`/changelog`、`/steering`、`/follow-up`、`/autocompact`。当 pi 设置中启用 skills 时，它们会以 `/skill:<名称>` 的形式出现。

`/reload` 命令会重新加载 MCP bridge（IDE 工具）和 pi 组件（扩展、技能、提示词、主题、上下文文件）。

## JetBrains IDE 桥接

IntelliJ 每次聊天都会发送其内置 MCP 服务端的描述信息。适配器将这些 IDE 工具作为 `ide_<server>_<tool>` 扩展工具暴露给 pi。

当描述信息携带端口时，桥接会针对 `http://127.0.0.1:<IJ_MCP_SERVER_PORT>/sse` 打开一个直接的 MCP-over-SSE 客户端。仅当该端点不可达时才会启动 stdio 子进程。

两道允许名单守护这些 IDE 工具。IDE 端从 `~/.jetbrains/acp.json` 读取 `idea_mcp_allowed_tools`。在已安装的构建中，缺省该项意味着 AllowAll。适配器端会拒绝 `execute_tool` 以及所有 `xdebug_*` 名称。可设置 `PI_ACP_IDE_EXTRA_TOOLS` 为逗号分隔的远程名列表，以重新放行你审核过的工具。

会话目录从不改变。修改完 IntelliJ MCP 设置或允许名单后，请开启一个新聊天。

## 安装

Node.js 20 或更高版本。你的 `PATH` 上需要有 pi 可执行文件。

以 Pi 包的方式安装，以激活随附的桥接扩展：

```bash
pi install npm:@wbline/pi-acp-jetbrain
```

pi 会在 `~/.pi/agent/settings.json` 中记录该包，并自动启用其声明的扩展。仅用 `npm install -g` 安装只会提供可执行文件，不会激活 Pi 包资源。

当你希望直接在 `PATH` 上使用 `pi-acp-jetbrain` 命令时，可全局安装：

```bash
npm install -g @wbline/pi-acp-jetbrain
```

包名是 `@wbline/pi-acp-jetbrain`。安装后的命令为 `pi-acp-jetbrain`。

在 `~/.jetbrains/acp.json` 中注册该适配器：

```json
{
  "agent_servers": {
    "pi-acp-jetbrain": {
      "command": "pi-acp-jetbrain",
      "args": [],
      "env": {}
    }
  }
}
```

npx 方式亦可。建议固定版本，以免后续启动拉到不同发布版：

将 `<version>` 替换为当前发布版本（见上方徽章）：

```json
{
  "agent_servers": {
    "pi-acp-jetbrain": {
      "command": "npx",
      "args": ["-y", "@wbline/pi-acp-jetbrain@<version>"],
      "env": {}
    }
  }
}
```

从源码运行：

```bash
npm install
npm run build
```

将入口指向 `dist/index.js`：

```json
{
  "agent_servers": {
    "pi-acp-jetbrain": {
      "command": "node",
      "args": ["/path/to/pi-acp-jetbrain/dist/index.js"],
      "env": {}
    }
  }
}
```

一个使用精简工具子集的开发配置：

```json
{
  "agent_servers": {
    "pi-acp-jetbrain": {
      "command": "/path/to/pi-acp-jetbrain/dist/index.js",
      "args": [],
      "env": {
        "PI_ACP_PI_COMMAND": "/path/to/pi",
        "PI_ACP_DEBUG_BRIDGE": "1"
      },
      "idea_mcp_allowed_tools": [
        "search_symbol",
        "get_symbol_info",
        "analyze_calls",
        "search_text",
        "search_regex",
        "get_file_problems",
        "lint_files",
        "build_project",
        "execute_run_configuration",
        "git_status",
        "get_repositories",
        "get_project_modules",
        "get_project_dependencies",
        "list_directory_tree",
        "read_file",
        "search_file",
        "open_file_in_editor",
        "get_all_open_file_paths",
        "skill_search"
      ]
    }
  }
}
```

`idea_mcp_allowed_tools` 起到“除所列工具外一律拒绝”的作用。按需添加工具即可。

### 可选：Plan/Build 模式插件

要使用 `session/mode` 功能切换到 `plan` 或 `build` 模式，需要安装额外的 pi 插件：

```bash
pi install npm:@wbline/pi-plan-build
```

安装完成后，即可在 JetBrains IDE 中使用 plan/build 模式进行项目规划和构建。

## 环境变量

| 变量                                    | 作用                                                                                                                            |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `PI_ACP_PI_COMMAND`                     | pi 可执行文件的路径。默认：`pi`。                                                                                               |
| `PI_ACP_DEBUG_BRIDGE=1`                 | 将脱敏后的 `session/new` MCP 描述信息记录到 stderr。IntelliJ 会把这些 stderr 写入 `idea.log`。                                  |
| `PI_ACP_ENABLE_EMBEDDED_CONTEXT=true`   | 声明支持 `embeddedContext`。                                                                                                    |
| `PI_ACP_ENFORCE_IDE_INSPECT=0`          | 关闭每轮结束后运行的代码检视门。                                                                                                |
| `PI_ACP_ENFORCE_IDE_KTS=0\|1`           | 仓库 KTS 脚本：`0` 从不运行；`1` 免确认直接运行。未设置时首次使用会通过权限请求询问，并记住“总是允许”。                         |
| `PI_ACP_ENFORCE_IDE_MUTATIONS=0`        | 关闭回合后的变更溯源审计（IntelliJ 优先模式的第二道防线）。                                                                     |
| `PI_ACP_IDE_MODE=off\|prefer\|required` | 会话的 IntelliJ 优先编码模式。默认：`prefer`（IDE 不可用时降级为原生工具；`off` 为显式关闭）。详见“IntelliJ 优先编码模式”一节。 |
| `PI_ACP_IDE_INSPECT_DIR`                | 将检视报告移到项目树之外。                                                                                                      |
| `PI_ACP_SESSION_MAP`                    | 覆盖会话映射表路径。默认：`~/.pi/pi-acp/session-map.json`。                                                                     |
| `PI_ACP_IDE_EXTRA_TOOLS`                | 重新放行进入拒绝名单的 IDE 工具。以逗号分隔的远程名。                                                                           |

## IntelliJ 优先编码模式

设置 `PI_ACP_IDE_MODE` 以控制会话如何让 IntelliJ 承担常规编码工作。所有实现仍由 pi 生成；IntelliJ 负责打开、读取、搜索、应用变更、重命名、重新格式化并验证。

- `off` 显式关闭：IDE 工具与原生工具并列暴露，不移除任何工具，不加额外提示引导。
- `prefer`（默认）在所需 IDE 能力注册后，从活动工具集中移除原生 `read`、`edit`、`write`、`grep`、`find` 与 `ls` 工具。若 IDE 桥接降级，这些工具会恢复，会话会收到明确的降级通知。
- `required` 立即移除上述原生工具，并且在 IDE 桥接缺失或断开连接期间保持移除。中断期间任务被视为受阻；适配器会以有上限的指数退避重试 IPC 连接，桥接恢复健康后自动回到活动状态。

必需能力：`read_file`、`open_file_in_editor`、`apply_patch`、`create_new_file`、一个搜索工具、一个检视工具。工具名从实时目录中发现，从不猜测。

在活动模式下，变更操作经 IntelliJ 执行并打开受影响文件：已有文件在 `apply_patch` 之前打开，新建/移动文件在之后打开。补丁目标与路径参数被限定在 ACP 项目根目录内；范围外的路径会被拒绝，包括符号链接逃逸。搜索与检视结果中的结构化路径字段会对照项目根目录检查。在 `prefer` 模式下，指到根目录之外文件的结果会在结果上标注诊断；在 `required` 模式下则被拒绝。非结构化结果文本原样透传：通用文本扫描不是安全的判别依据，因此按工具区分的结构化适配器仍是边界（这是上游限制）。

在 `prefer` 模式下，`bash` 仍可用于 Git、测试、构建与诊断。不受限的 bash 仍可改动文件，因此该模式仅是对常规编码工具的策略约束，而非文件系统沙箱。请勿将其当作安全边界。

在 `prefer` 与 `required` 且目录激活时，直接在 `fabric_exec` 内对 Fabric/Schema 文件做修改（`schema.commit`、`pi.write`，`pi.edit`）会在执行前被一道启发式 `tool_call` 闸门拦截（剥离注释后的文本扫描：空白混淆的直接调用能被识别，但别名或间接分发调用可能绕过——下文的溯源审计是兜底）。变更必须经由 IDE 工具（`ide_idea_apply_patch`、`ide_idea_create_new_file`、重命名、重新格式化）进行，这些工具会打开受影响文件并把补丁/路径参数限定在项目根目录内。只读的 Fabric 代码（`pi.read`、`pi.grep`、IDE 工具调用）不受影响。这收窄了扩展工具绕过活动集过滤的路径；`bash` 仍是刻意的、有文档说明的例外。

IDE 上报的项目根目录必须与会话工作目录解析到同一目录（Windows 上忽略大小写；符号链接根目录通过 realpath 比较）。不一致时模式会安全失效——`prefer` 降级为 `native_fallback`，`required` 进入 `required_unavailable`——并附带一条诊断信息，否则变更围栏与基于 git 的变更溯源审计会以不同的根目录归一化同一个文件。

每轮之后还有第二道防线：扩展通过鉴权后的 IPC 上报成功的 IDE 变更工具所应用的路径（`mutations_applied`），适配器将这些路径与“本轮新变脏”的文件（以轮次开始时的 git status 为基线做增量，再合并轮次触碰的工具路径；仅当 IDE 未主导活动集时，原生 edit/write 路径才视为已覆盖）进行比对。失败与被取消的轮次仍会运行这道审计——失败前的变更是真实发生的，不能被下一轮基线洗掉——检视摘要则仅在正常结束的轮次运行。未伴随 IDE 变更事件而变化的文件会在聊天中作为 `Mutation provenance`（变更溯源）违规浮出（与检视摘要合并为一个“回合后检查”块，报告路径为项目相对路径），并记录在 `PromptResponse._meta.piAcp.mutationViolations` 下。当 git status 超过 200 条上限时，两道门禁都会附加截断说明，而不是静默漏报。可用 `PI_ACP_ENFORCE_IDE_MUTATIONS=0` 关闭。被删除的文件以及轮次中途被外部自动提交监听器提交的文件无法被这层检测到（与代码检视门使用相同的 git-status 语义）。

### 模式可能降级或失效的情形

该模式是由本适配器实现的按会话适应机制，而非 IDE 的持久设置。其强制逻辑由一个基于 IDE 桥的实时能力探测驱动，因此可能在以下情形中降级或失效：

- **桥缺失 / IPC 断开**：强制逻辑是有状态（`active`、`native_fallback`、`required_unavailable`）的。在 `prefer` 下，桥丢失或断开会使会话落入 `native_fallback`，原生工具回归，并伴随明确的降级提示。在 `required` 下，会话变为 `required_unavailable`，中断期间任务被视为受阻。扩展会以有上限的指数退避重试 IPC 连接（注册成功后计数重置），两种状态都会自动恢复——重新过滤工具、收回原生工具——一旦桥接重新注册成功。
- **能力探测未命中**：活动模式依赖所有必需能力都成功注册（`read_file`、`open_file_in_editor`、`apply_patch`、`create_new_file`、一个搜索工具和一个检视工具）。若任一必需能力缺失——例如 IDE 升级后重命名或移除了某个 MCP 工具——`prefer` 会回退、`required` 会不可用。工具名是从活目录发现的，从不猜测，因此 IDE 侧的重命名可能静默破坏探测。
- **配置变更需要新建对话**：桥和能力探测在会话开始时建立。修改 `idea_mcp_allowed_tools`、`PI_ACP_IDE_MODE` 值或 IDE MCP 设置后，请新建对话；已打开的会话不会重新连接或重新评估其模式。
- **非法值安全关闭到 required**：任何不是 `off`/`prefer`/`required` 的 `PI_ACP_IDE_MODE` 值（例如拼写错误）都会被当作 `required` 并安全关闭，可能阻塞任务，而非回到默认的 `prefer`。
- **并非安全沙箱**：模式强制仅覆盖常规编码工具。Bash 仍可用（`prefer` 下）且仍能改文件；溯源门禁只上报违规而不回滚。

为适配器进程设置该变量，例如在 `~/.jetbrains/acp.json` 中：

```json
{
  "agent_servers": {
    "pi-acp-jetbrain": {
      "command": "/path/to/pi-acp-jetbrain/dist/index.js",
      "env": { "PI_ACP_IDE_MODE": "prefer", "PI_ACP_PI_COMMAND": "/path/to/pi" }
    }
  }
}
```

## 认证

适配器会声明终端认证元数据。如需交互式供应商登录，可运行：

```bash
pi-acp-jetbrain --terminal-login
```

ACP 客户端可从其认证界面启动同一命令。

## 开发

```bash
npm install
npm run dev        # 使用 tsx 从 src 运行
npm run build
npm run lint
npm run test
npm run typecheck
npm run format
npm run smoke      # 核心 stdio 冒烟测试
npm run smoke:full # 完整矩阵；发布前请运行
node scripts/check.mjs
```

代码布局：

- `src/acp/` 存放 ACP 服务端、会话与翻译逻辑（`acp/translate/`、`acp/commands/`）。
- `src/mcp/` 存放 ACP 侧 MCP 桥接及其传输层（stdio、SSE、IPC）。
- `src/pi-extension/` 存放打包进 pi 的扩展；`src/policy/` 存放两侧共享的编码策略。
- `src/pi/` 存放 pi 子进程包装及 RPC、会话、设置的封装。

## 限制

### 信任边界

适配器信任它的 ACP 宿主。宿主提供会话工作目录与客户端提供的 MCP 描述信息；描述信息中点名的 stdio MCP 服务端会以适配器进程的环境、在该工作目录中被启动。只与你控制的宿主和 MCP 配置一起运行该适配器。切勿将其作为服务暴露给不受信任的客户端，也不要在适配器环境中放置不必要的密钥。

`PI_ACP_IDE_MODE=prefer|required` 仅是对常规编码工具的策略约束，而非文件系统沙箱：不受限的 Bash 仍可用，轮后门禁只上报违规而不回滚变更。关于模式可能静默回退或阻塞会话的具体情形，见[模式可能降级或失效的情形](#模式可能降级或失效的情形)一节。

适配器不暴露 ACP 文件系统或终端委托能力。pi 在本地读取文件、运行命令。

`providers/set` 与 `providers/disable` 返回“找不到方法”（method-not-found）错误。pi 在 RPC 面之外配置供应商。

ACP 计划（plan）面保持未接线状态。所安装的 SDK 未定义计划方法。

调试器工具仅在 IDE 调试会话存活期间注册。启动一个调试会话并开启一个新聊天即可看到它们。

重建适配器后请开启一个新聊天。Node 会保留旧文件，而 IntelliJ 会复用正在运行的代理进程。

### 轮后门禁依赖 git

轮后门禁中的变更来源（provenance）审计依赖 `git status` 快照对比，因此只在 git 检出中生效。
在非 git 目录里审计无法运行；此时适配器会每会话发出一次提示，而不是保持静默。关闭开关
（`PI_ACP_ENFORCE_IDE_MUTATIONS`、`PI_ACP_ENFORCE_IDE_INSPECT`）的行为不受影响。

### 斜杠命令分为三层路由

内建命令（`/compact`、`/session`、`/name`、`/steering`、`/follow-up`、`/changelog`、
`/export`、`/autocompact`、`/reload`）由适配器在 `src/acp/commands/builtin-commands.ts` 中处理，
不会到达 pi。`.pi/` markdown 中的文件命令在 `session.prompt()` 内展开。其余命令都会转发给
pi，由 pi 解析自身的命令与扩展命令（例如随附桥接扩展注册的 `/acp-reload`）。命令行为异常时，
请先排查对应的层。

## 商标

JetBrains、IntelliJ IDEA 与 WebStorm 是 JetBrains s.r.o. 的商标。本项目是一个独立的社区维护适配器，与 JetBrains
没有关联、未获其背书或赞助。商标归各自权利人所有，此处仅用于描述本适配器所互操作的工具。

## 许可证

MIT。见 [LICENSE](LICENSE)。

## 致谢

本项目 fork 自 [ryan-brosas/pi-acp-jetbrain](https://github.com/ryan-brosas/pi-acp-jetbrain)，感谢原作者 Sergii Kozak（2025）奠定的基础；fork 之后的改动与维护由 `@wbline` 以相同 MIT 条款发布。

同时感谢 [pi](https://github.com/badlogic/pi-mono) 编码代理——本适配器所服务的底层 agent。
