opencode-idea 0.0.26 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -392
- package/package.json +1 -1
- package/skills/idea-standalone/SKILL.md +1 -2
- package/src/ide-guidance.js +14 -2
- package/src/ide-guidance.template.md +90 -78
- package/src/mcp/idea.js +1 -1
- package/src/plugin.js +33 -15
package/README.md
CHANGED
|
@@ -12,21 +12,19 @@ OpenCode 插件,面向 JetBrains 项目 (IntelliJ IDEA / PyCharm / WebStorm 等)
|
|
|
12
12
|
> **需要项目环境的命令走 Run Configuration**:跑 `java` / `python` / `npm` 这类依赖项目配好的 JDK / Node / Python 版本的
|
|
13
13
|
> 命令时,引导模型用 `ide_execute_run_configuration`(配置 **由用户**先在 IDE 里建好),而不是原生 shell;不需要项目
|
|
14
14
|
> 环境的命令照常走原生 shell。 **IDE 内置终端 (`ide_execute_terminal_command`) 已被隐藏** —— 实测它并不继承项目环境,
|
|
15
|
-
> 对原生 shell
|
|
15
|
+
> 对原生 shell 没有任何优势。详见「Run Configuration 全套」。
|
|
16
16
|
|
|
17
17
|
非 JetBrains 项目完全不受影响。 **手动接入**:插件启动不探测、不连接任何 IDE。三个命令管全部:
|
|
18
18
|
|
|
19
|
-
| 命令
|
|
20
|
-
|
|
|
21
|
-
| `/open-in-ide [ide]`
|
|
22
|
-
| `/add-sub-module-in-ide <路径>` | 登记一个子项目(相对 root 的路径);随后由 AI 问用户它用哪个 IDE
|
|
23
|
-
| `/delete-in-ide`
|
|
19
|
+
| 命令 | 作用 |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `/open-in-ide [ide]` | 接入 root 及其全部已登记子项目;第一次**没有配置**时由 AI 问用户 root 用哪个 IDE |
|
|
22
|
+
| `/add-sub-module-in-ide <路径>` | 登记一个子项目(相对 root 的路径);随后由 AI 问用户它用哪个 IDE |
|
|
23
|
+
| `/delete-in-ide` | 删掉**整个**配置(root + 全部子项目)并注销全部 IDE MCP |
|
|
24
24
|
|
|
25
|
-
配置持久化在 `ctx.storage`(`project/<root 路径>`),重启 OpenCode
|
|
26
|
-
插件发现"该有
|
|
27
|
-
|
|
28
|
-
root
|
|
29
|
-
保持全手动、零自主动作。
|
|
25
|
+
配置持久化在 `ctx.storage`(`project/<root 路径>`),重启 OpenCode 后仍在。之后**没有任何后台轮询**——恢复完全靠请求驱动:
|
|
26
|
+
插件发现"该有 IDE 工具却没有"(新实例)或"IDE 工具调用失败"(掉线)时,**在下一条消息发出前静默接回**(用户无感)。
|
|
27
|
+
没跑过 `/open-in-ide` 的 root 保持全手动、零自主动作。
|
|
30
28
|
|
|
31
29
|
> **为什么改成请求驱动**:OpenCode 把一个 Location (项目)的全部服务缓存在 `LayerMap` 里,空闲 **60 分钟**
|
|
32
30
|
> (`idleTimeToLive`)后整体驱逐——插件实例连同全部 IDE MCP 注册一起消失。这一点两种传输都躲不过,所以与其后台探活
|
|
@@ -36,8 +34,7 @@ root
|
|
|
36
34
|
|
|
37
35
|
```text
|
|
38
36
|
setup(启动, 每 root 一次):
|
|
39
|
-
a. 读配置(ctx.storage: project/<root
|
|
40
|
-
—— root 用哪个 IDE,登记了哪些子项目、各自用哪个 IDE
|
|
37
|
+
a. 读配置(ctx.storage: project/<root 路径>)—— root 用哪个 IDE,登记了哪些子项目、各自用哪个 IDE
|
|
41
38
|
b. 注册 3 个命令 + session context/prompt 钩子 + tool 钩子 + configure_ide_project 工具
|
|
42
39
|
不探测、不注册任何 MCP
|
|
43
40
|
|
|
@@ -58,7 +55,7 @@ setup(启动, 每 root 一次):
|
|
|
58
55
|
|
|
59
56
|
/add-sub-module-in-ide <路径>(手动触发):
|
|
60
57
|
1. 没有配置 → 直接报错,提示先执行 /open-in-ide(不提问、不写任何东西)
|
|
61
|
-
2. 有配置 → 校验路径:必须在 root
|
|
58
|
+
2. 有配置 → 校验路径:必须在 root 下、目录存在、目录名合法、前缀不与已登记项目重名
|
|
62
59
|
3. 通过 → 发给模型一条**任务**:请它问用户这个子项目用哪个 IDE,答复后调用
|
|
63
60
|
configure_ide_project 回写。(带多个参数 = 报错,不猜)
|
|
64
61
|
|
|
@@ -67,21 +64,16 @@ setup(启动, 每 root 一次):
|
|
|
67
64
|
2. 发结果通知
|
|
68
65
|
|
|
69
66
|
工具失败触发(仅在有配置的 root 里):
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
IDE 工具调用失败 → tool execute.after 钩子**分类**:
|
|
68
|
+
- IDE MCP 不可用(掉了)→ 重跑接入流程
|
|
69
|
+
- IDE 在、但项目没打开("Unable to determine the target project" / "doesn't correspond to any open project")
|
|
70
|
+
→ 让**这个工具所属项目**的 IDE 重新打开它(不会去动别的项目)
|
|
71
|
+
- 普通业务错误(File not found 等)→ 不动作
|
|
72
|
+
触发成功 → 把该错误改写为"已重连 / 已重新打开,请重试",模型同一回合重试;失败 → 保持原错误
|
|
76
73
|
|
|
77
74
|
请求驱动的恢复(没有后台轮询;每个 Location(项目)各一份插件实例):
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
2. `context` 钩子——某条请求"曾经有 IDE 工具、现在没有" → 立刻锁存并在后台重建(不等下一条消息)
|
|
81
|
-
3. `execute.after` 钩子——IDE 工具调用以"不可用"告终(`MCP server "<名>" is not available` /
|
|
82
|
-
`Error POSTing to endpoint`)→ 重跑接入流程,并把错误改写成"已重连,请重试";
|
|
83
|
-
普通业务错误(文件不存在等)不动作
|
|
84
|
-
※ 三者都只在**确实有事发生**时动;没人用这个项目时,插件完全静止
|
|
75
|
+
prompt / context / execute.after 三个钩子各管一件事,只在**确实有事发生**时动;
|
|
76
|
+
没人用这个项目时,插件完全静止。三个钩子分别做什么见「行为」。
|
|
85
77
|
```
|
|
86
78
|
|
|
87
79
|
## 接入 (`/open-in-ide` 与 `/add-sub-module-in-ide`)
|
|
@@ -122,13 +114,9 @@ fast-fail,不会在后台轮询;重复触发共享同一次打开请求, **每
|
|
|
122
114
|
把一个子项目登记进配置。参数是 **相对 root 的路径**(例如 `modelserver`),而且 **只接受一个参数**:
|
|
123
115
|
|
|
124
116
|
1. 没有配置 → 直接报错,提示先执行 `/open-in-ide`(不提问、不写任何东西);
|
|
125
|
-
2. 校验:必须位于 root
|
|
117
|
+
2. 校验:必须位于 root 之下、目录真实存在、**目录名合法**(规则见「限制」)、前缀不与已登记项目重名;
|
|
126
118
|
3. 通过后发给模型一条 **任务**:请它问用户这个子项目用哪个 IDE,答复后调用 `configure_ide_project` 回写。
|
|
127
119
|
|
|
128
|
-
**目录名的硬规则**(它就是工具前缀,所以不给猜的余地):必须匹配 `^[a-z0-9][a-z0-9_-]*$` —— 小写字母或数字开头,
|
|
129
|
-
只含小写字母、数字、`-`、`_`。不合规 (大写、点号、空格、中文……)或与已登记项目重名,插件 **一律拒绝**并说明原因,
|
|
130
|
-
由用户改目录名。插件 **绝不**自动改名或自动消歧。
|
|
131
|
-
|
|
132
120
|
### 让模型回写:`configure_ide_project`
|
|
133
121
|
|
|
134
122
|
插件注册的一个 **自己的**工具,是"用户选的 IDE"回到配置里的 **唯一通道**(插件不能弹选择框,也不能挂起等回答):
|
|
@@ -150,109 +138,115 @@ configure_ide_project({ path: "modelserver", ide: "pycharm" }) // 配置一个
|
|
|
150
138
|
|
|
151
139
|
## 按能力选工具 (直接调用引导)
|
|
152
140
|
|
|
153
|
-
IDE MCP 可用时,插件通过 **系统提示注入**告诉模型 **哪些事该走 IDE
|
|
154
|
-
的定义、描述或可用性:
|
|
155
|
-
|
|
156
|
-
1. **系统提示注入**:通过 `ctx.session.hook("context", …)` 往每次请求的 system
|
|
157
|
-
`src/ide-guidance.template.md
|
|
158
|
-
|
|
159
|
-
输出是 Markdown,`<jar>!/<entry>` 和反引号必须原样过)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
`
|
|
193
|
-
|
|
194
|
-
|
|
141
|
+
IDE MCP 可用时,插件通过 **系统提示注入**告诉模型 **哪些事该走 IDE**;除下面「工具参数」那一处刻意的例外
|
|
142
|
+
(只做减法:从 schema 里删掉一个参数),不修改任何工具 (原生或 IDEA) 的定义、描述或可用性:
|
|
143
|
+
|
|
144
|
+
1. **系统提示注入**:通过 `ctx.session.hook("context", …)` 往每次请求的 system 里追加一段引导。
|
|
145
|
+
**文本本身在 `src/ide-guidance.template.md`** —— 它是一份要给人 review 的提示词文档,条件直接用 `{{#if …}}`
|
|
146
|
+
写在那几行外面,所以打开模板就能看到最终渲染成什么样;`src/template.js` 是渲染器(支持 `{{var}}` / `{{#if}}` /
|
|
147
|
+
`{{else}}`,**不做 HTML 转义** —— 输出是 Markdown,`<jar>!/<entry>` 和反引号必须原样过)。
|
|
148
|
+
含工具名(`<项目前缀>_<name>`;root 是 `ide_*`,子项目是 `<目录名>_*`)与关键参数名。
|
|
149
|
+
|
|
150
|
+
**路由总纲** —— 整个引导的第一条,按「路径在不在项目里」分路:
|
|
151
|
+
|
|
152
|
+
- **项目内** —— **OpenCode 自带的工具为主**,它们更快。**它们做不了、或者做得很差的,才用 IDE 工具**:
|
|
153
|
+
符号检索、依赖实现、重构、格式化、校验、运行配置、跨文件补丁。
|
|
154
|
+
**两边都不顺手时,你自己判断用 `shell` 里的命令** —— 那是**第三选择,不是第一选择**。
|
|
155
|
+
- **项目外** —— **只有 OpenCode 自带的工具**,IDE 工具不参与。这不是保守,是硬事实:**请求的路径必须落在某个
|
|
156
|
+
已打开项目的目录树内,IDE 才会去解析它**;项目外的路径(`/tmp`、别的仓库、用户主目录)两个 IDE 都会直接报错。
|
|
157
|
+
|
|
158
|
+
**OpenCode 自带的工具做不到、或做得很差的能力才写进去**:
|
|
159
|
+
|
|
160
|
+
| 能力 | 工具 |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| 符号 | `ide_search_symbol` / `ide_get_symbol_info` / `ide_analyze_calls` |
|
|
163
|
+
| 项目与编辑器 | `ide_get_project_modules` / `ide_get_project_dependencies` / `ide_list_directory_tree` / `ide_get_all_open_file_paths` / `ide_open_file_in_editor` |
|
|
164
|
+
| 重构 / 格式化 | `ide_rename_refactoring` / `ide_reformat_file` |
|
|
165
|
+
| 批量改文件 | `ide_apply_patch` |
|
|
166
|
+
| 校验 | `ide_lint_files` / `ide_get_file_problems` / `ide_build_project` |
|
|
167
|
+
| 运行配置 | `ide_get_run_configurations` / `ide_execute_run_configuration`(+ 配套插件的启停工具) |
|
|
168
|
+
| 部署 | `ide_list_deployment_servers` / `ide_upload_to_deployment` / `ide_download_from_deployment` / `ide_get_deployment_task_status`(**仅配套插件 + Ultimate**) |
|
|
169
|
+
|
|
170
|
+
**读、找文本、找文件、改单个文件一律用 OpenCode 自带的工具**(`read` / `grep` / `glob` / `write` / `edit`):
|
|
171
|
+
这几件事走 IDE 只是多一跳,所以映射表里**不出现** `ide_search_text` / `ide_search_regex` / `ide_search_file`。
|
|
172
|
+
唯一例外是 `ide_read_file` —— 它是读**包内文件**的唯一手段(`<jar>!/<entry>` 的依赖 jar 内文件、`jrt://` 的 JDK 镜像)。
|
|
173
|
+
|
|
174
|
+
**有七处不只写"工具能干什么",还写了"什么时候走它 / 不要走哪条替代路"**。每一处都是被实测打回来才补上的:
|
|
175
|
+
|
|
176
|
+
| 处 | 当初错在哪 | 现在怎么写 |
|
|
177
|
+
| --- | --- | --- |
|
|
178
|
+
| 路由 | 只写"用 IDE 工具",没分项目内 / 项目外 | 见上面的总纲 |
|
|
179
|
+
| 校验 | 清单里写着 lint / build,模型当没看见,自己去 `shell` 里临时编译一遍就宣布完成 | 先讲"改完自己先查一遍",再把判断权交回模型;两个失效方向(查太勤 / 压根不查)都点名 |
|
|
180
|
+
| 符号 | 写成"找符号声明位置的技巧",而模型当时的动因是"这个平台方法到底怎么实现的"——两边对不上,触发点永远不 fire | 先写动因("要读源码就两步"),再显式禁掉默认手艺:自己拼 jar 路径 + `javap` / `unzip` |
|
|
181
|
+
| 搜索 | 只写"用原生工具(read / grep / glob)",`grep` 是个裸词,模型读成了命令行的 `grep` | 每次提到都带限定词(原生侧「`grep` 工具」/ shell 侧「`shell` 里的 `grep`」),并点名禁掉 `shell` 里的 `grep` / `rg` / `find` |
|
|
182
|
+
| 写文件 | 只写"写文件一律用原生 write / edit",但那俩都只管一个文件 | 放开 `apply_patch` 并点名;禁掉默认手艺:在 `shell` 里写脚本整文件重写 |
|
|
183
|
+
| 改名 | 只写"按符号改名",模型读成"手动改一处",没想到它能跨文件按语义更新所有引用 | 写明"改符号优先走重构、改文字走文本工具",并说明它**不碰注释与字符串** |
|
|
184
|
+
| 读文件 | 引导一个字没提 `read` 的 `offset` / `limit`,模型只看见 `sed` 这条路 | 点明"要读某一段就传 offset + limit",并禁掉 `shell` 里的 `sed -n 'a,bp'` |
|
|
185
|
+
|
|
186
|
+
**后三条是拿全量会话统计出来的**,不是推测 —— 查 OpenCode 的会话库(`~/.local/share/opencode/opencode.db`),
|
|
187
|
+
33231 次工具调用里:`shell` 里 `sed -n 'a,bp'` 2512 次、`shell` 里 python 脚本 3345 次,
|
|
188
|
+
而 `ide_rename_refactoring` 只出现过 **1 次**(还是探针)。**工具是有的,是引导没告诉它什么时候用。**
|
|
189
|
+
|
|
190
|
+
上面的规则在**总则里各有一句同义的**(总则才是每条路由规则都会路过的地方),而这几类失败恰恰都是"整段跳过"。
|
|
195
191
|
|
|
196
192
|
不会把整份工具目录重复塞进 system prompt,也不要求模型先写编排代码。IDEA MCP 工具自身的
|
|
197
193
|
description/schema 仍是参数使用的权威来源;工具返回业务错误时先修正参数,不要误判成工具不可用。MCP 不可用时立即
|
|
198
194
|
提示用户执行 `/open-in-ide`,不重复探测或等待。
|
|
199
195
|
|
|
200
|
-
**总则里最靠前的两条是 Code Mode
|
|
201
|
-
直接调用,不需要也不应该用 Code Mode 的 `search` 去找`;第二条才落到 `ide_
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
196
|
+
**总则里最靠前的两条是 Code Mode 规则**,而且第一条是**通用**的,不是 `ide_*` 特例:`工具列表里已经直接列出的原生工具,
|
|
197
|
+
直接调用,不需要也不应该用 Code Mode 的 `search` 去找`;第二条才落到 `ide_*`。放在所有路由规则之前,因为它们是前提 ——
|
|
198
|
+
模型拿不到工具,后面写什么都没用。理由是实测过的失效:工具明明在原生工具列表里,模型却进 Code Mode 跑了一次工具搜索
|
|
199
|
+
(Code Mode 自己的指令「用 `search` 找工具」读起来像是访问任何东西的通用姿势),白绕一圈之后还可能得出
|
|
200
|
+
"工具不可用"的错误结论。
|
|
201
|
+
|
|
202
|
+
### `ide_rename_refactoring` 的实测能力边界
|
|
206
203
|
|
|
207
|
-
|
|
204
|
+
在 `mcp-test-project`(Java / Python / Go / JS 四种语言各一个服务)上实测:
|
|
208
205
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
206
|
+
| 场景 | 结果 |
|
|
207
|
+
| --- | --- |
|
|
208
|
+
| 跨文件改名(3 个文件引用同一个符号) | ✅ 声明 + 全部调用点一起改,返回 `with 2 usages` |
|
|
209
|
+
| 改类名 | ✅ 连**文件名**一起改(`Calculator.java` → `PriceCalculator.java`) |
|
|
210
|
+
| 局部变量 / 对象字段 | ✅ 同一文件内全部引用一起改 |
|
|
211
|
+
| **注释里的同名文本** | ✅ **原样不动** |
|
|
212
|
+
| **字符串字面量里的同名文本** | ✅ **原样不动**(`with 0 usages`) |
|
|
213
|
+
| 符号不存在 / 文件不存在 / 大小写不符 | ✅ 明确报错(`isError=true` + 原因) |
|
|
214
|
+
| Java / Python / Go / ESM(`.mjs`) | ✅ 跨文件全对 |
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
`
|
|
216
|
-
用一个布尔覆盖两者,部署工具就会替"启停工具在不在"作答。
|
|
216
|
+
关键是**注释和字符串都不动**:同一文件里符号出现两次(1 处声明 + 1 处字符串),语义重构只改声明,
|
|
217
|
+
字符串原样不动 —— 文本替换(`apply_patch` / `edit` / `sed` / python)会把字符串一起改,那就是**改了程序的输出**。
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
**但它不是硬规矩** —— 改的本来就是文字(配置 key、模板占位符、文档、SQL)、**你就是想改那句字符串**、
|
|
220
|
+
或者那个文件 IDE 根本没索引(生成物、vendor 目录)时,用文本工具才是对的。
|
|
221
|
+
|
|
222
|
+
**一个已知的坑**:`.js` + CommonJS `require()` 的跨文件解析,取决于 `package.json` 有没有被 IDEA 挂成 JS 模块。
|
|
223
|
+
没挂上时 `.js` 的模块系统不确定,`require()` 解析不了,改名就只改声明处;换成 **`.mjs`**(无条件 ESM)立刻正常。
|
|
224
|
+
所以引导里写了:返回的 `usages` 数比预期少,就是有引用没被解析到 —— **先确认 IDE 索引到那个模块**,
|
|
225
|
+
别拿文本替换去"补"漏掉的引用。
|
|
222
226
|
|
|
223
227
|
## 工具参数 (schema 收敛 + `execute.before`)
|
|
224
228
|
|
|
225
|
-
有些 IDEA
|
|
229
|
+
有些 IDEA 工具**不传某个参数就会回答一个你没问的问题**。这种参数由本插件完全接管,所以要**两半一起做** ——
|
|
226
230
|
只做一半都是错的:
|
|
227
231
|
|
|
228
|
-
| 一半
|
|
229
|
-
|
|
|
230
|
-
| 从模型的工具 schema 里**删掉** | `ctx.tool.transform` 改 input schema | 模型会设它、会据此推理,而那个值被无声丢弃
|
|
231
|
-
| 每次调用**设成固定值**
|
|
232
|
-
|
|
233
|
-
两半由 `src/mcp/idea-tool-defaults.js` 的 **同一张表** `FORCED_ARGUMENTS` 驱动,所以不会漂移。目前一条:
|
|
232
|
+
| 一半 | 做法 | 不做会怎样 |
|
|
233
|
+
| --- | --- | --- |
|
|
234
|
+
| 从模型的工具 schema 里**删掉** | `ctx.tool.transform` 改 input schema | 模型会设它、会据此推理,而那个值被无声丢弃 |
|
|
235
|
+
| 每次调用**设成固定值** | `ctx.tool.hook("execute.before")` | 模型不传时拿到的是那个可能出错的默认值本身 |
|
|
234
236
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
两半由 `src/mcp/idea-tool-defaults.js` 的**同一张表** `FORCED_ARGUMENTS` 驱动,所以不会漂移。目前一条:
|
|
238
|
+
`ide_build_project` 的 `rebuild` 强制为 `true` —— 增量构建比对的是**IDE 自己那份文件新鲜度**,IDE 外部改的代码
|
|
239
|
+
可能被判成"没有脏文件",构建于是变成 no-op 并报成功(实测 IntelliJ 2026.2.3:`build_project` 落到
|
|
240
|
+
`ProjectTaskManager.createAllModulesBuildTask(project, isIncrementalBuild = !rebuild)`,6 个场景全部识别到外部改动)。
|
|
238
241
|
|
|
239
|
-
- **强制**而不是"缺省才补":`rebuild: false`
|
|
240
|
-
|
|
241
|
-
- 依据是实测 (IntelliJ 2026.2.3):`build_project` 落到
|
|
242
|
-
`ProjectTaskManager.createAllModulesBuildTask(project, isIncrementalBuild = !rebuild)`;用 `rebuild: true` 跑的 6 个场景
|
|
243
|
-
(改已有文件 / 新增文件、任意包、在编辑器里打开与否)**全部**识别到外部改动。
|
|
244
|
-
- 调用方自己也传了非空 `filesToRebuild` 时,IDE 走的是另一条分支、根本不读 `rebuild`,所以设不设都无副作用。
|
|
242
|
+
- **强制**而不是"缺省才补":`rebuild: false` 请求的正是那个可能出错的答案,而调用方从结果里分辨不出来;`rebuild: true`
|
|
243
|
+
唯一的代价是时间。调用方自己传了非空 `filesToRebuild` 时 IDE 走另一条分支、根本不读 `rebuild`,所以设不设都无副作用。
|
|
245
244
|
- **引导文本里一个字都不提 `rebuild`** —— schema 都不暴露了,再写进提示词等于把刚拿掉的旋钮又放回去。
|
|
246
|
-
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
**这是对早先「绝不改工具定义」那条规则的一次有意例外**(见 `141b1bd`),且刻意收得很窄:这里 **只做减法,不发明、不解释**。
|
|
252
|
-
IDE 自己的 `build_project` 工具描述本来就没提这个参数 (只有属性级描述提),所以删掉属性之后 schema 依然自洽、也依然完全是
|
|
253
|
-
IDE 的。schema 变换 **绝不能**用来加建议 —— 那是引导文本的职责。
|
|
254
|
-
|
|
255
|
-
两半都 **不依赖项目配置**:它们修的是工具调用本身,与"这个项目是否接入 IDE"无关。
|
|
245
|
+
- 安全性照着 OpenCode 源码确认过:一次工具调用**先**按该请求发布出去的 schema 校验,**然后**才进 `execute`,而
|
|
246
|
+
`execute.before` 在真正的工具逻辑之前触发。所以"schema 里没有、钩子里补上"这个组合不会撞校验。
|
|
247
|
+
- **这是对早先「绝不改工具定义」那条规则的一次有意例外**,且刻意收得很窄:这里**只做减法,不发明、不解释** ——
|
|
248
|
+
schema 变换**绝不能**用来加建议,那是引导文本的职责。
|
|
249
|
+
- 只对**点名的工具**生效,其余工具的参数一字不改。两半都**不依赖项目配置**。
|
|
256
250
|
|
|
257
251
|
## Run Configuration 全套 (配套插件 `idea-mcp-enhance`)
|
|
258
252
|
|
|
@@ -270,8 +264,14 @@ IDE 内置的 MCP Server 只能 **启动已有**的 run configuration (`execute_
|
|
|
270
264
|
| `ide_stop_run_configuration` | 停掉该配置的**全部**实例并等它们退出;没在跑也返回成功(幂等) |
|
|
271
265
|
| `ide_rerun_run_configuration` | 等同 IDE 的 **Rerun** 按钮:在跑就重启、没在跑就启动,读 `started` 判断是否真起来了 |
|
|
272
266
|
|
|
273
|
-
|
|
274
|
-
|
|
267
|
+
**需要项目环境的命令走 run configuration,不用原生 shell** —— 判据是这条命令 **依赖项目的运行时版本**
|
|
268
|
+
(IDE 里配好的 JDK / Node / Python 等)。是 → run configuration;不是 (看文件、git、临时脚本)→ 原生 shell。
|
|
269
|
+
run configuration 会继承开发时具体配的那套运行时,所以它就是个应用;配置必须 **先在 IDEA 里存在**(由用户建),
|
|
270
|
+
没有就请用户建一个再跑。
|
|
271
|
+
|
|
272
|
+
> **为什么不再用 IDE 终端**:`ide_execute_terminal_command` 之前被当成"自动继承 IDE 项目环境"的通道而保留暴露,但实测
|
|
273
|
+
> **它拿不到项目配好的 JDK / Node / Python 版本**,和原生 shell 没有区别。既然没有优势,就把它从模型可见的工具表里
|
|
274
|
+
> 隐藏掉 (`HIDDEN_IDE_TOOL_SUFFIXES`),命令执行的路由改由 run configuration 承担。
|
|
275
275
|
|
|
276
276
|
配置名有自己的文法:**`[os@]base[@env]`** —— os 段 (`win` = Windows,缺省 = macOS / Linux)、base 段 (配置本体)、env 段 (环境名,
|
|
277
277
|
缺省 = 默认环境 —— 起一批时逐条解析:有 `@<env>` 用它,没有就用默认)。`win@base` 与 `base@env` 都是同一个 base 的 **变体**,档案里只登记 base。解析 **交给工具**:
|
|
@@ -280,25 +280,12 @@ IDE 内置的 MCP Server 只能 **启动已有**的 run configuration (`execute_
|
|
|
280
280
|
|
|
281
281
|
opencode-idea 对这一组工具做三件事:
|
|
282
282
|
|
|
283
|
-
1. **透传**:IDE MCP server 以 `codemode: false`
|
|
283
|
+
1. **透传**:IDE MCP server 以 `codemode: false` 注册,工具目录整体转发,所以这七个工具自动以 `ide_*` 出现在模型的
|
|
284
284
|
工具表里 —— 插件侧不需要单独声明 (也因此 **装不装这个 IDEA 插件,插件侧的代码都不用改**);
|
|
285
|
-
2.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
- 「运行配置」段写明 **启动 / 重启常驻进程就用 `ide_rerun_run_configuration`,不要用 `pkill` / `kill` / shell**
|
|
290
|
-
—— 这正是它要解决的问题:外部杀掉的进程 IDE 并不知道,Run 窗口仍显示运行中,下次启动就会撞端口;
|
|
291
|
-
- 「运行配置」段的 **新建那条**把 run configuration 定性成 **项目资产**:一个项目通常只建几个 (固定要跑的构建 /
|
|
292
|
-
装依赖 / dev server / 迁移), **不是随手一条命令就建一个**;判据是 **这件事以后会不会以同样形态再发生** —— 会就该建、模型必须
|
|
293
|
-
**主动**向用户提议 (摊开配置名 /
|
|
294
|
-
实际命令 / 工作目录 / 理由),用户同意才动手;不会就裸 `shell` 跑完算 (那条里写明了这层区分:`shell` 就地执行不留痕、
|
|
295
|
-
建配置是固化成一条可反复用的)。判据只写在这一处, **「命令执行」段不重复**,只留
|
|
296
|
-
「配置一律不由你写」这条无条件的,以及"该建的用哪条路建"那一条按工具是否存在分流:有工具时指向上面那条、
|
|
297
|
-
并说明非 Shell 类型与删 / 改交给用户调 `idea-run-config-manager` skill;
|
|
298
|
-
没有工具时换成「讲清楚配置名 / 命令 / 工作目录 / 理由,请用户在 IDE 里自己建」—— 那时 skill 本身仍在
|
|
299
|
-
(它不按工具门禁),
|
|
300
|
-
但缺 create 工具时它也只能把流程交回用户,所以引导里顺手把这一点说明了,免得以后有人把它当成漏写而"补"上一句调不到的指令。
|
|
301
|
-
**没装插件时绝不会提到那个工具。**
|
|
285
|
+
2. **条件引导**:只有这次请求的工具快照里真的有它们,才在「运行配置」节里逐个点名。**没装插件时整节连同标题一起消失**,
|
|
286
|
+
引导不会提到一个调不到的工具。其中一条值得单独说:**启动 / 重启常驻进程就用 `ide_rerun_run_configuration`,
|
|
287
|
+
不要用 `pkill` / `kill` / shell** —— 这正是它要解决的问题:外部杀掉的进程 IDE 并不知道,Run 窗口仍显示运行中,
|
|
288
|
+
下次启动就会撞端口;
|
|
302
289
|
3. **工具名清单**写在 `src/mcp/enhance.js`(与原生 IDE 的 `src/mcp/idea.js` 分开放:原生走原生,插件的走插件的),
|
|
303
290
|
与该仓库的 `RunLifecycleSchemas.kt` 保持同步。工具按 **存在的子集**逐个点名 (而不是「有就全写」),这样旧版本的配套
|
|
304
291
|
插件少给一两个工具时,引导也不会提到它们。同理, **每条命令名只列真的提供它的项目**(逐项目核对,见「条件注入」)——
|
|
@@ -308,61 +295,83 @@ opencode-idea 对这一组工具做三件事:
|
|
|
308
295
|
|
|
309
296
|
## Run Configuration:文件由工具写,档案由 skill 写
|
|
310
297
|
|
|
311
|
-
内置 MCP
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
- **运行配置只有工具写** —— `ide_create_run_configuration` / `ide_update_run_configuration` /
|
|
318
|
-
`ide_delete_run_configuration`
|
|
319
|
-
是唯一写入口。理由不是"不许写",而是 **手写失败时是静默的**:`type` 写错 IDE 整个忽略那个文件,字段名拼错则静默用默认值;
|
|
320
|
-
- **建之前先问用户** —— run configuration 是 **项目资产**,一个项目通常只建几个。判据是
|
|
321
|
-
**这件事以后会不会以同样形态再发生**:
|
|
322
|
-
会就该建、模型 **主动**向用户提议 (摊开配置名 / 实际命令 / 工作目录 / 理由),同意才动手;不会就裸 `shell` 跑完算;
|
|
323
|
-
- **字段名不确定时走 `cloneFrom`** —— 复制一个同类型的现成配置再 patch 它,这是唯一可靠的路子。没有模板的类型
|
|
324
|
-
(Application、JAR 以及 JUnit / Docker / Spring Boot / Flyway …)也 **照样能建**:`assets/types/*.md` 只是 **字段参考**
|
|
325
|
-
,不是前置条件;
|
|
326
|
-
- **建完看工具的返回值** —— `canRun` 是"现在能不能跑"的权威答案,`unknownOptions` 非空就是字段名拼错了,`storagePath`
|
|
327
|
-
是文件落在哪;
|
|
328
|
-
- **档案**(`idea-run-config-manager` skill 写进项目根 `AGENTS.md` 的标记块)仍然要跟上:新增 / 删除 / 改名之后同步档案 ——
|
|
329
|
-
档案和项目里的配置对不上,比没有档案更糟;
|
|
330
|
-
- **没装配套插件** → 工具不存在,引导也不提,模型继续按原来的方式工作 (在 IDE 里建)。
|
|
298
|
+
内置 MCP 只能**查**(`get_run_configurations`)和**执行**(`execute_run_configuration`);增 / 删 / 改 / 改名 / 查详情
|
|
299
|
+
靠配套插件的那一族(见上面的「Run Configuration 全套」)。
|
|
300
|
+
|
|
301
|
+
**运行配置只有工具写** —— `ide_create_run_configuration` / `ide_update_run_configuration` / `ide_delete_run_configuration`
|
|
302
|
+
是唯一写入口。理由不是"不许写",而是 **手写失败时是静默的**:`type` 写错 IDE 整个忽略那个文件,字段名拼错则静默用默认值。
|
|
331
303
|
|
|
332
304
|
> **曾经有一个 `idea-run-config` skill 教模型凭记忆手写配置文件,已经删掉。** 理由不是"不许写",而是
|
|
333
|
-
>
|
|
334
|
-
> **静默忽略整个文件、日志里也不报**,凭记忆猜 option 名就是这么错的。现在这件事由 `idea-run-config-manager` 接手 —— 但
|
|
335
|
-
> **每个类型都得有一份实测模板**
|
|
336
|
-
> (`assets/types/`,字段名全部来自真实文件),没模板的类型宁可交给用户。
|
|
337
|
-
|
|
338
|
-
**skill 在插件加载时一次性注册,不跟随工具目录**:
|
|
339
|
-
|
|
340
|
-
- **判据只有"插件加载了"** —— 不看项目有没有 `/open-in-ide`、也不看 IDE 此刻serve 了哪些工具。
|
|
341
|
-
- **为什么不再按工具门禁**:早先的做法是"IDE 真的提供了这个 skill 绕着的工具才注册"(frontmatter 的 `requires:` +
|
|
342
|
-
`ctx.tool.list()`)。
|
|
343
|
-
门本身是对的,但 **注册时机错了**:它发生在 `/open-in-ide` 完成之后,而客户端在这之前就已经把 skill
|
|
344
|
-
列表快照给当前会话了 ——
|
|
345
|
-
结果是"服务端注册了、`@` 补全里看不到", **必须刷新页面才出现**。注册提前到 `setup`,就落在快照之前。
|
|
346
|
-
- **工具缺失的代价放回正文** —— skill 里本来就写了"工具列表里没有 `<前缀>_rerun_run_configuration` 时交回用户"这类话;
|
|
347
|
-
一个缺席的工具是模型能读到并处理的事实,一个看不见的 skill 不是。
|
|
348
|
-
- **`requires:` 这个键已彻底删除** —— 它曾是本插件自己的门禁元数据,OpenCode 不认,注册走 `ctx.skill.transform` 时也不会带上它;
|
|
349
|
-
门禁取消后它就成了"看起来在设门、实际没人读"的死字段,留着只会误导下一个改代码的人。skill 绕着哪几件工具写,由正文自己说明。
|
|
305
|
+
> **不许凭空写** —— `type` 写错 IDE 会**静默忽略整个文件、日志里也不报**。现在这件事由 `idea-run-config-manager` 接手。
|
|
350
306
|
|
|
351
|
-
|
|
352
|
-
扫描加载 —— **加新 skill 只需加一个目录,不用改代码**。两个 shipped skill 都让模型自己加载:manager 一度是用户点名才可用,结果
|
|
353
|
-
guidance 里"建配置走这个 skill"成了死路 —— 模型在可选列表里看不到它,只能自己去调工具。
|
|
307
|
+
`idea-run-config-manager` skill 做三件事:
|
|
354
308
|
|
|
355
|
-
|
|
309
|
+
1. **建 / 改 / 删** —— 直接动手,**走工具不手写文件**。建之前先问用户要不要建:run configuration 是**项目资产**,
|
|
310
|
+
一个项目通常只建几个。判据是 **这件事以后会不会以同样形态再发生**:会就该建、模型**主动**向用户提议
|
|
311
|
+
(摊开配置名 / 实际命令 / 工作目录 / 理由),同意才动手;不会就裸 `shell` 跑完算。
|
|
312
|
+
字段名不确定时走 `cloneFrom` —— 复制一个同类型的现成配置再 patch 它,这是唯一可靠的路子。
|
|
313
|
+
建完看工具的返回值:`canRun` 是"现在能不能跑"的权威答案,`unknownOptions` 非空就是字段名拼错了。
|
|
314
|
+
2. **建档** —— 过滤后剩下的每条配置在项目根 `AGENTS.md` 的标记块里占一个条目,五项固定
|
|
315
|
+
(`when` / `before run` / `run` / `after run` / `desc`);只登记 base(`win@base` / `base@env` 是同一个 base 的变体,
|
|
316
|
+
写进 `desc`)。`when` 回答"谁能起它":`用户授权触发` / `AI 自己触发` **进条目**,`用户手动触发` 只在块末尾登记名字、不进条目。
|
|
317
|
+
每一项各选一种写法(行内 / 子列表),同一配置里混用是常态 —— 判据只有"这一项自己有几句话"。
|
|
318
|
+
3. **过滤** —— 服务化(常驻应用)的配置归 `idea-standalone` 的块,**两个块永不重叠**。
|
|
356
319
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
320
|
+
这个档案块的用途是**能被下次读到**:要办同类事情时先查它,有现成的就用,不再重复建 —— 所以 **联动是硬要求**:
|
|
321
|
+
配置被**新建 / 删除 / 修改**(无论谁做的:工具建的、用户自己建的、用户删掉的)都要在文档里同步
|
|
322
|
+
**加条目 / 删条目 / 改条目**,否则它就开始骗人。**档案和项目里的配置对不上,比没有档案更糟。**
|
|
323
|
+
|
|
324
|
+
每项怎么写、两种写法各长什么样、两个示例,都在模板的生成提示里,这里不再抄一遍。
|
|
325
|
+
**两个块都是条目式**(一条一个条目,每项一行):`desc` 常是一段话,塞进表格格子里既难读、又要靠补空格维持对齐。
|
|
326
|
+
两边靠**同名同序的四项**(`before run` / `run` / `after run` / `desc`)加各自独有的项 —— 在 `AGENTS.md` 里一眼就能看出
|
|
327
|
+
哪几条是服务、哪几条是固定流程。
|
|
328
|
+
|
|
329
|
+
**SKILL.md 只有一份五步流程**(读 → 枚举 → 建 / 删 / 改 → 同步 → 核对),外加**支持矩阵**与**硬性契约**
|
|
330
|
+
(落地位置、固定信封、复查、路径宏、不要写模板外的字段)—— 它自己就带着规矩;两类内容另放在两个地方,各管一段:
|
|
331
|
+
|
|
332
|
+
| 模板 | 装什么 |
|
|
333
|
+
| --- | --- |
|
|
334
|
+
| `skills/idea-run-config-manager/assets/types/<类型>.md` | **一个类型一份**:那个类型的实测样本 + 字段表 + 坑(索引就是 SKILL.md 里的**支持矩阵**) |
|
|
335
|
+
| `skills/idea-run-config-manager/assets/AGENTS.template.md` | 产物(档案)的骨架:条目怎么写、五项分别写什么、枚举与过滤的要求,都在它的生成提示里 |
|
|
336
|
+
|
|
337
|
+
**支持矩阵 (7 份已落、2 份待实物)**:
|
|
361
338
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
339
|
+
| 类型 | `type` id | 验证 |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| Shell Script | `ShConfigurationType` | ✅ 本机 6 例(含 Windows) |
|
|
342
|
+
| Maven / Gradle / npm / Python / Go Build | `MavenRunConfiguration` / `GradleRunConfiguration` / `js.build_tools.npm` / `PythonConfigurationType` / `GoApplicationRunConfiguration` | ✅ 实物 |
|
|
343
|
+
| Node.js | `NodeJSConfigurationType` | ⚠️ 三个属性已验证,其余字段未验证 |
|
|
344
|
+
| Application / JAR | `Application` / `JarApplication` | ⏳ type 已验证,**字段层无实物 → 暂不写模板,交给用户** |
|
|
345
|
+
|
|
346
|
+
矩阵之外(JUnit / TestNG / Kotlin / Docker / Spring Boot / Tomcat / Flyway / Liquibase / HTTP Request …)一律
|
|
347
|
+
**没有模板** → 交给用户在 IDE 里配。
|
|
348
|
+
|
|
349
|
+
**给某个类型补模板的流程**:在 IDE 里建一个最简配置 → 勾 Store as project file → 用 `ide_inspect_run_configuration`
|
|
350
|
+
读它的 `configurationXml` 照抄结构 → 标清哪些字段实测 → 把矩阵里那个类型从 ⏳ 改成 ✅。
|
|
351
|
+
**每个字段名都来自真实文件,不靠猜** —— `js.build_tools.npm`(属性 + 自定义元素)、`ShConfigurationType`(`<option>` 子元素)、
|
|
352
|
+
`NodeJSConfigurationType`(全属性)三种写法并存,套错就是静默失效。
|
|
353
|
+
|
|
354
|
+
**这个档案块以带前缀的工具为准**,不围绕 IDE 界面说话:各项目各有自己的前缀(root 是 `ide`,子项目是它的目录名),
|
|
355
|
+
枚举与执行都用**它自己那个前缀**的 `<前缀>_get_run_configurations` / `<前缀>_execute_run_configuration` …;
|
|
356
|
+
**条目标题括号里写的就是这个前缀**。
|
|
357
|
+
|
|
358
|
+
**别的文档都指向这里**:引导文本碰到"要建 / 删 / 改一个配置",不再让模型现编流程,而是让它加载本 skill。
|
|
359
|
+
**两个 skill 都是模型可自取的**(frontmatter 不带 `metadata: opencode/autoinvoke: false`):模型判断到场景就加载。
|
|
360
|
+
「该不该建」的判断留在引导文本里(它每次请求都在),skill 负责动手时的流程与写法。
|
|
361
|
+
|
|
362
|
+
### skill 怎么加载
|
|
363
|
+
|
|
364
|
+
文档放在 `skills/<id>/SKILL.md`(每个 skill 一个目录,`name` / `description` 走 YAML frontmatter),由 `src/skill/loader.js`
|
|
365
|
+
扫描加载 —— **加新 skill 只需加一个目录,不用改代码**。
|
|
366
|
+
|
|
367
|
+
**skill 在插件加载时一次性注册,不跟随工具目录** —— 判据只有"插件加载了",不看项目有没有 `/open-in-ide`、
|
|
368
|
+
也不看 IDE 此刻 serve 了哪些工具。早先的做法是按工具门禁(frontmatter 的 `requires:` + `ctx.tool.list()`),
|
|
369
|
+
**门本身是对的,但注册时机错了**:它发生在 `/open-in-ide` 完成之后,而客户端在这之前就已经把 skill 列表快照给当前会话了 ——
|
|
370
|
+
结果是"服务端注册了、`@` 补全里看不到",**必须刷新页面才出现**。注册提前到 `setup`,就落在快照之前。
|
|
371
|
+
|
|
372
|
+
工具缺失的代价放回正文 —— skill 里本来就写了"工具列表里没有 `<前缀>_rerun_run_configuration` 时交回用户"这类话;
|
|
373
|
+
一个缺席的工具是模型能读到并处理的事实,一个看不见的 skill 不是。**`requires:` 这个键已彻底删除** —— 它曾是本插件自己的
|
|
374
|
+
门禁元数据,OpenCode 不认,门禁取消后它就成了"看起来在设门、实际没人读"的死字段。
|
|
366
375
|
|
|
367
376
|
## Standalone 模式纪律 (skill)
|
|
368
377
|
|
|
@@ -383,80 +392,6 @@ loader 读的是 OpenCode 文档里的那个嵌套写法 (它自己的读取器
|
|
|
383
392
|
事实一律先查后写:配置名来自 `ide_get_run_configurations`,`热重载` 来自运行方式,端口读不到就问用户 —— 不猜。
|
|
384
393
|
**只动自己的标记块**:根 `AGENTS.md` 是共享文件 (`project-scaffold` 的宪法、项目自己的内容都在里面),标记之外一字不改。
|
|
385
394
|
|
|
386
|
-
## run configuration 增删改 + 档案 (skill)
|
|
387
|
-
|
|
388
|
-
`idea-run-config-manager` skill 做三件事:
|
|
389
|
-
|
|
390
|
-
1. **建 / 改 / 删** —— 直接动手, **走工具不手写文件**:`create_run_configuration` / `update_run_configuration` /
|
|
391
|
-
`delete_run_configuration`,字段拿不准就用 `cloneFrom` 复制一个同类型的现成配置再 patch;建之前先问用户要不要建,建完看工具
|
|
392
|
-
返回的 `canRun` / `unknownOptions`。
|
|
393
|
-
2. **建档** —— 过滤后剩下的每条配置在项目根 `AGENTS.md` 的标记块里占一个条目,五项固定
|
|
394
|
-
(`when` / `before run` / `run` / `after run` / `desc`);只登记 base (`win@base` / `base@env` 是同一个 base 的变体,
|
|
395
|
-
写进 `desc`)。`when` 回答"谁能起它":`用户授权触发` / `AI 自己触发` **进条目**,`用户手动触发` 只在块末尾登记名字、不进条目。
|
|
396
|
-
每一项各选一种写法 (行内 / 子列表),同一配置里混用是常态 —— 判据只有"这一项自己有几句话"。
|
|
397
|
-
3. **过滤** —— 服务化 (长驻应用)的配置归 `idea-standalone` 的块, **两个块永不重叠**。
|
|
398
|
-
|
|
399
|
-
每项怎么写、两种写法各长什么样、两个示例,都在模板的生成提示里,这里不再抄一遍。
|
|
400
|
-
|
|
401
|
-
> **两个块都是条目式**(一条一个条目,每项一行):`desc` 常是一段话,塞进表格格子里既难读、又要靠补空格维持对齐。
|
|
402
|
-
> 两边靠 **同名同序的四项**(`before run` / `run` / `after run` / `desc`)加各自独有的项 —— 在 `AGENTS.md` 里一眼就能看出
|
|
403
|
-
> 哪几条是服务、哪几条是固定流程。
|
|
404
|
-
|
|
405
|
-
**SKILL.md 只有一份五步流程**(读 → 枚举 → 建 / 删 / 改 → 同步 → 核对),外加 **支持矩阵**与 **硬性契约**(落地位置、固定信封、复查、
|
|
406
|
-
路径宏、不要写模板外的字段)—— **它自己就带着规矩**;两类内容另放在两个地方,各管一段:
|
|
407
|
-
|
|
408
|
-
| 模板 | 装什么 |
|
|
409
|
-
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
410
|
-
| `skills/idea-run-config-manager/assets/types/<类型>.md` | **一个类型一份**:那个类型的实测样本 + 字段表 + 坑(索引就是 SKILL.md 里的**支持矩阵**) |
|
|
411
|
-
| `skills/idea-run-config-manager/assets/AGENTS.template.md` | 产物(档案)的骨架:条目怎么写、五项分别写什么、枚举与过滤的要求,都在它的生成提示里 |
|
|
412
|
-
|
|
413
|
-
**支持矩阵 (7 份已落、2 份待实物)**:
|
|
414
|
-
|
|
415
|
-
| 类型 | `type` id | 验证 |
|
|
416
|
-
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
417
|
-
| Shell Script | `ShConfigurationType` | ✅ 本机 6 例(含 Windows) |
|
|
418
|
-
| Maven / Gradle / npm / Python / Go Build | `MavenRunConfiguration` / `GradleRunConfiguration` / `js.build_tools.npm` / `PythonConfigurationType` / `GoApplicationRunConfiguration` | ✅ 实物 |
|
|
419
|
-
| Node.js | `NodeJSConfigurationType` | ⚠️ 三个属性已验证,其余字段未验证 |
|
|
420
|
-
| Application / JAR | `Application` / `JarApplication` | ⏳ type 已验证,**字段层无实物 → 暂不写模板,交给用户** |
|
|
421
|
-
|
|
422
|
-
矩阵之外 (JUnit / TestNG / Kotlin / Docker / Spring Boot / Tomcat / Flyway / Liquibase / HTTP Request …)一律
|
|
423
|
-
**没有模板** → 交给用户在 IDE 里配。
|
|
424
|
-
|
|
425
|
-
**给某个类型补模板的流程**:在 IDE 里建一个最简配置 → 勾 Store as project file → 用 `ide_inspect_run_configuration` 读它的
|
|
426
|
-
`configurationXml` 照抄结构 →
|
|
427
|
-
标清哪些字段实测 →
|
|
428
|
-
把矩阵里那个类型从 ⏳ 改成 ✅。 **每个字段名都来自真实文件,不靠猜** —— `js.build_tools.npm`(属性 + 自定义元素)、
|
|
429
|
-
`ShConfigurationType`
|
|
430
|
-
(`<option>` 子元素)、`NodeJSConfigurationType`(全属性)三种写法并存,套错就是静默失效。
|
|
431
|
-
|
|
432
|
-
**先过滤,再建档**:**服务化的配置不进这个块** —— 它们已经登记在 `AGENTS.md` 的 `idea-standalone` 块里 (端口 /
|
|
433
|
-
热重载也写在那里),
|
|
434
|
-
所以建档第一步是读那个块、把它登记过的名字记下来并排除, **剩下的**才是本块内容。
|
|
435
|
-
|
|
436
|
-
这个档案块 **以带前缀的工具为准**,不围绕 IDE 界面说话:各项目各有自己的前缀 (root 是 `ide`,子项目是它的目录名),枚举与执行都用
|
|
437
|
-
**它自己那个前缀**的
|
|
438
|
-
`<前缀>_get_run_configurations` / `<前缀>_execute_run_configuration` …; **条目标题括号里写的就是这个前缀**
|
|
439
|
-
,一条配置属于谁、该用哪套工具跑由它决定。
|
|
440
|
-
|
|
441
|
-
这个块的用途是 **能被下次读到**:要办同类事情时先查它,有现成的就用,不再重复建 —— 所以 **联动是硬要求**:配置被 **新建 /
|
|
442
|
-
删除 / 修改**
|
|
443
|
-
(无论谁做的:工具建的、用户自己建的、用户删掉的)都要在文档里同步 **加条目 / 删条目 / 改条目**,否则它就开始骗人。
|
|
444
|
-
|
|
445
|
-
**别的文档都指向这里**:引导文本与 `idea-standalone` skill 碰到"要建 / 删 / 改一个配置",不再让用户自己去 IDE 里摸,而是让用户调本
|
|
446
|
-
skill ——
|
|
447
|
-
增 / 删 / 改的流程入口只有这一个,而它只由用户触发。
|
|
448
|
-
|
|
449
|
-
**它绕着"新建配置"这件事写**(新建只有配套插件 `idea-mcp-enhance` 能做),但 **这不影响它出不出现** —— 两个 skill
|
|
450
|
-
都在插件加载时注册,
|
|
451
|
-
不缺任何工具也照样在列表里;缺工具时由正文把流程交回用户 (`idea-standalone` 只要 `rerun_run_configuration`,门更松)
|
|
452
|
-
。这是刻意的:
|
|
453
|
-
一个只教模型"去建配置"、却没给建配置工具的 skill,只会把用户拖进一次注定失败的调用。
|
|
454
|
-
|
|
455
|
-
**而且只能用户调**:它的 frontmatter 带 `metadata: opencode/autoinvoke: false`,loader 翻成定义的 `autoinvoke: false` ——
|
|
456
|
-
skill 照常注册,但 **不进模型的可选列表**,模型不会自己挑上它,只有用户显式指定 (`@idea-run-config-manager`)才会加载。
|
|
457
|
-
理由是它 **直接动手改项目文件与项目资产**:运行配置与 `AGENTS.md` 都由用户自己按这个 skill 的流程来改,模型不主动挑上它。
|
|
458
|
-
「该不该建」的时机判断仍在引导文本的判据里 (那是模型该管的事), **动手权**留给人。
|
|
459
|
-
|
|
460
395
|
## Deployment 全套 (配套插件 `idea-mcp-enhance`)
|
|
461
396
|
|
|
462
397
|
IDE 内置的 MCP Server **完全不碰部署** —— 既不报 IDE 里配了哪些 Deployment 服务器,也不能把文件传上去 / 拉下来。
|
|
@@ -469,20 +404,15 @@ IDE 内置的 MCP Server **完全不碰部署** —— 既不报 IDE 里配了
|
|
|
469
404
|
| `ide_download_from_deployment` | 把服务器上映射目录的内容拉回本地(等同 IDE 的 Download from …),**以服务器为准**:两边都有的文件被远端覆盖(相同则不写)。**没有"保留我的版本"这个选项** |
|
|
470
405
|
| `ide_get_deployment_task_status` | 用 `handle` 查一次传输的状态与计数;**永不启动任何传输** |
|
|
471
406
|
|
|
472
|
-
opencode-idea
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
所以 Community IDE 上这一节永远不出现;
|
|
482
|
-
3. **工具名清单**写在 `src/mcp/enhance.js`(`DEPLOYMENT_SUFFIXES` + `deploymentCapabilities`),与该仓库的
|
|
483
|
-
`DeploymentSchemas.kt` 保持同步。 **四个工具逐个点名**(而不是"有就全写"),而且每条命令名只列 **真的提供它**的项目
|
|
484
|
-
(见「条件注入」的逐项目核对):它们通常同进同出 (同一道插件前提),但"通常"不是"总是" —— 点名一个模型调不到的工具
|
|
485
|
-
就是一次注定失败的调用。
|
|
407
|
+
opencode-idea 对它做的是**同一套三件事**(透传 / 条件引导 / 工具名清单),差异只有两处:
|
|
408
|
+
|
|
409
|
+
- **条件引导更严** —— 部署工具除了配套插件,还要求 `com.jetbrains.plugins.webDeployment`,而它只在 **Ultimate** 里存在,
|
|
410
|
+
所以 Community IDE 上这一节永远不出现。而且 **这一节整节 (含标题) 都由配套插件提供**,一个工具都没有时它 **完全消失**
|
|
411
|
+
—— 不能像「运行配置」那样留一个空标题 (那一节还有内置的 `get_run_configurations` / `execute_run_configuration` 撑着)。
|
|
412
|
+
整节**锚在一个工具上**(模板里 `{{#if canListDeploymentServers}}` 直接包住标题):这四个由同一道前提注册、同进同出,
|
|
413
|
+
拿其中一个当判据就够,不需要再造一个"四个里任一个"的聚合标志;
|
|
414
|
+
- **工具名清单**在 `src/mcp/enhance.js` 的 `DEPLOYMENT_SUFFIXES` + `deploymentCapabilities`,与该仓库的
|
|
415
|
+
`DeploymentSchemas.kt` 保持同步。
|
|
486
416
|
|
|
487
417
|
这节存在的理由是 **一条路由规则**:往服务器传 / 取文件要走这些工具, **不要自己拼 `ssh` / `rsync` / `scp`** ——
|
|
488
418
|
IDE 的连接、路径映射、排除规则都在它后面,自己拼的那份与 IDE 看到、管理的不是同一份。文案里同时点明两件容易读错的事:
|
|
@@ -496,22 +426,6 @@ IDE 的连接、路径映射、排除规则都在它后面,自己拼的那份与
|
|
|
496
426
|
> 模板 (`assets/types/`);部署没有这层知识 —— 四个工具的 schema 已经说清 `serverName` / `mode` 怎么填,而"该不该传、传给谁"
|
|
497
427
|
> 是模型当场看 `list_deployment_servers` 的判断。再加一个 skill 只是把同一段话抄第二遍。
|
|
498
428
|
|
|
499
|
-
## 项目命令执行 (Run Configuration)
|
|
500
|
-
|
|
501
|
-
需要 **项目环境**的命令 (依赖用户在 IDE 里配好的 JDK / Node / Python 版本等),模型使用 **
|
|
502
|
-
`ide_execute_run_configuration`**
|
|
503
|
-
执行 —— 走 run configuration,不用原生 shell。
|
|
504
|
-
|
|
505
|
-
- 判据是:这条命令 **依赖项目的运行时版本**。是 → run configuration;不是 (看文件、git、临时脚本)→ 原生 shell。
|
|
506
|
-
- run configuration 会继承开发时具体配的那套运行时,或者干脆是你那个应用配好的运行时,所以这就是个应用。
|
|
507
|
-
- 配置必须 **先在 IDEA 里存在**(由用户建)。没有就请用户建一个,再跑;
|
|
508
|
-
|
|
509
|
-
> 为什么不再用 IDE 终端:`ide_execute_terminal_command` 之前被当成"自动继承 IDE 项目环境"的通道而保留暴露,但实测
|
|
510
|
-
> **它拿不到项目配好的 JDK / Node / Python 版本**,和原生 shell 没有区别。既然没有优势,就把它从模型可见的工具表里
|
|
511
|
-
> 隐藏掉 (`HIDDEN_IDE_TOOL_SUFFIXES`),命令执行的路由改由 run configuration 承担。
|
|
512
|
-
|
|
513
|
-
引导文本由 `src/ide-guidance.js` 注入 (`### 命令执行` 段),只在 IDE MCP 可用时生效。
|
|
514
|
-
|
|
515
429
|
## 前置条件
|
|
516
430
|
|
|
517
431
|
- JetBrains IDE 已开启 MCP 服务 (IDE 设置里的 MCP Server / AI Assistant MCP),版本需 **2026.2+**(提供 Streamable HTTP 端点
|
|
@@ -586,7 +500,7 @@ opencode plugin list
|
|
|
586
500
|
|
|
587
501
|
```js
|
|
588
502
|
// ~/.config/opencode/plugins/opencode-idea.js
|
|
589
|
-
export { default } from "/Users/yutao/
|
|
503
|
+
export { default } from "/Users/yutao/IdeaProjects/opencode-idea/src/index.js";
|
|
590
504
|
```
|
|
591
505
|
|
|
592
506
|
## 配置
|
|
@@ -626,111 +540,52 @@ export { default } from "/Users/yutao/Projects/opencode-idea/src/index.js";
|
|
|
626
540
|
|
|
627
541
|
## 行为
|
|
628
542
|
|
|
629
|
-
-
|
|
630
|
-
|
|
631
|
-
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
(
|
|
638
|
-
-
|
|
639
|
-
|
|
640
|
-
await `prompt` 才构建请求,所以 **触发它的那条消息本身**就带上了 IDE 工具,没有空窗。这正是 Location 被空闲驱逐 (60
|
|
641
|
-
分钟 `idleTimeToLive`)或 OpenCode 重启后,新实例自动接回连接的路径。
|
|
642
|
-
- **`context` 钩子**:发现「本会话见过 IDE 工具、本请求却没有」→ 立刻锁存并在后台重建 (不等下一条消息)。工具缺失时注入的
|
|
643
|
-
恢复引导会 **明确禁止**调用任何 IDE 工具 (此前复用"重试一次"的文案,反而诱导模型去调一个不在工具表里的工具,
|
|
644
|
-
得到 `No tool named ... available`)。
|
|
645
|
-
- **`execute.after` 钩子**:见下一条。
|
|
646
|
-
恢复是 **每个 Location (项目)各一份**;没有任何定时器,插件在不使用时完全静止。
|
|
647
|
-
- **失败触发的自动接入 (仅已配置的 root)**:`tool.execute.after` 钩子监听 IDE 工具报错并 **分类**:
|
|
648
|
-
- IDE MCP 不可用 (掉了)→ 重跑 `/open-in-ide` 的接入流程;
|
|
649
|
-
- IDE 在、但项目没打开 (`Unable to determine the target project` / `doesn't correspond to any open project`)→ 让
|
|
650
|
-
**这个工具
|
|
651
|
-
所属项目**的 IDE 重新打开它 (**不会**去动别的项目);
|
|
652
|
-
- 普通业务错误 (`File not found` 等)→ **不动作**。判定只认 **传输层专有标记**(`MCP server "<名>" is not available` /
|
|
653
|
-
`MCP server is not connected` / `Error POSTing to endpoint`), **不认**工具输出里出现的通用串 (例如 SQL 工具报的
|
|
654
|
-
`Connection refused`) —— MCP 工具错误的 message 就是工具自己的输出,认通用串会把正常业务失败误判成掉线并重建。
|
|
655
|
-
触发成功会把该工具错误改写为"已重连 / 已重新打开,请重试",模型在同一回合内重试;失败则保持原错误。非 IDE 工具、未配置
|
|
656
|
-
的 root 都不受影响。启动保护 (`launchCooldownMs` / `launchMaxAttempts`) 限制重复拉起 IDE, **按项目各自计**。
|
|
657
|
-
- **`/open-in-ide`**:手动接入 root 与全部子项目; **等每个项目的 IDE 就绪 → 先注销全部旧注册 → 再按项目各注册一个
|
|
658
|
-
server →
|
|
659
|
-
等 IDE 工具就绪**才返回。 **`/delete-in-ide`**:注销全部 IDE MCP + 删整个配置 + 清空注入的环境。
|
|
543
|
+
- **未配置的 root**:不改环境、不注册、不注入任何 IDE 提示(每请求零开销;setup 时只做一次配置读取)。
|
|
544
|
+
- **已配置的 root**:启动读回配置,全部 IDE MCP 由 `/open-in-ide` 注册。
|
|
545
|
+
- **`context` 钩子是请求的纯函数**:按「每次模型调用」触发(含工具续跑),只看本次请求自带的工具快照 ——
|
|
546
|
+
有任一项目前缀的工具就**按每个前缀**剥离隐藏工具并注入**逐项目**引导;没有则只在已配置时给恢复提示。
|
|
547
|
+
该钩子本身**不做任何 I/O**。
|
|
548
|
+
- **错误分类只认传输层专有标记**(`MCP server "<名>" is not available` / `MCP server is not connected` /
|
|
549
|
+
`Error POSTing to endpoint`),**不认**工具输出里出现的通用串(例如 SQL 工具报的 `Connection refused`) ——
|
|
550
|
+
MCP 工具错误的 message 就是工具自己的输出,认通用串会把正常业务失败误判成掉线并重建。
|
|
551
|
+
业务错误(`File not found` 等)、非 IDE 工具、未配置的 root 都不动作。
|
|
552
|
+
- 恢复是**每个 Location(项目)各一份**;没有任何定时器,插件在不使用时完全静止。
|
|
553
|
+
启动保护(`launchCooldownMs` / `launchMaxAttempts`)限制重复拉起 IDE,**按项目各自计**。
|
|
660
554
|
|
|
661
555
|
## 限制
|
|
662
556
|
|
|
663
|
-
- 仅支持 **OpenCode V2** 插件 API
|
|
664
|
-
`ctx.location`)。
|
|
557
|
+
- 仅支持 **OpenCode V2** 插件 API(`ctx.tool` / `ctx.mcp` / `ctx.shell` / `ctx.command` / `ctx.session` / `ctx.location`)。
|
|
665
558
|
- IDE MCP 注册需要 IDE **2026.2+** 的 Streamable HTTP 端点 `/stream`;更早版本只有 `/sse`,OpenCode V2 无法连接。
|
|
666
|
-
- skill 定义的字段名 **以 schema 为准**,不要照官方文档:`Skill.Info` 需要 `path`
|
|
667
|
-
不存在 —— 用错会让 `ctx.skill.transform` 抛 `SchemaError
|
|
668
|
-
`/open-in-ide`)
|
|
669
|
-
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
IDE
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
`IJ_MCP_SERVER_PROJECT_PATH`):返回模块 = 认得 → 绑定;报
|
|
694
|
-
`Unable to determine the target project` = 不认得 → `open -a` 打开它并继续轮询到认得 (上限 60s)。
|
|
695
|
-
项目刚打开时的短暂"未就绪"由工具失败分类兜底 (「IDE 在、但项目没打开」→ 重新打开并让模型重试)。
|
|
696
|
-
- **IDE 的「目录包含」语义 (实测)**:请求路径落在某个 **已打开项目的目录树内**时,IDE 会把它解析到
|
|
697
|
-
那个项目,而不是报错。所以「子项目与 root 配同一个 IDE」时,只要那个 IDE 打开了 root,子项目就被判成
|
|
698
|
-
"已打开" —— 它确实是 root 项目的一部分,工具能操作它的文件,但语言支持按 **root 项目**走 (例如
|
|
699
|
-
Python 子项目被 root 的 Web 模块解析,符号检索与检查会不准)。这是 IDE 的语义,绕过不了;要让子项目
|
|
700
|
-
按自己的模块解析,就得让它在那个 IDE 里 **单独打开**成一个项目,或者 (正常做法)给它配它真正被打开的
|
|
701
|
-
那个 IDE。完全不在任何已打开项目下的路径 (`/tmp`、不存在的路径)两个 IDE 都会正确报错。
|
|
702
|
-
- **项目路径按 IDE 的写法归一化**:IDE 收的是系统无关形式 (正斜杠),其自带配置示例在 Windows 上也是
|
|
703
|
-
`"E:/gitea/secondev-demo"`。OpenCode 的 `location` 在 Windows 上是原生反斜杠,所以 `src/mcp/idea.js` 的
|
|
704
|
-
`ideProjectPath()` 把它转成正斜杠, **注册与探测走同一个函数**,两者必须一致,否则会用一种写法注册、又用另一种
|
|
705
|
-
写法去问,永远匹配不上。macOS 上是原样透传。
|
|
706
|
-
- IDE **2026.2+** 提供 Streamable HTTP 端点 `/stream`,这是插件 **唯一**使用的传输 (`type: "remote"`)。注册本身不启动任何子进程,
|
|
707
|
-
所以没有"桥进程被 IDE 拖死后变成僵尸"这一类问题。`/open-in-ide` 仍要求 IDE MCP 已在监听 (冷启动约数十秒),因此会先等就绪再注册。
|
|
708
|
-
- **空闲驱逐 (已实测)**:OpenCode 把一个 Location 的服务缓存在 `LayerMap` 里,`idleTimeToLive` = **60 分钟**;空闲到期即整体驱逐,
|
|
709
|
-
插件实例连同 **全部** IDE MCP 注册一起消失。 **两种传输都躲不过**,所以对策不是换传输,而是请求驱动的恢复 (见「行为」):
|
|
710
|
-
- 新实例上第一条用户消息的 `prompt` 钩子会把连接接回来 —— 常态下 **不需要用户手动做什么**;
|
|
711
|
-
- 手动重建仍可用:执行 `/open-in-ide`(先注销全部旧注册、再重新注册);
|
|
712
|
-
- 插件给每个 server 配 `executionTimeoutMs`(默认 **10 分钟**)作为硬上限:兜住端点卡住时的单次调用,不再沿用 OpenCode
|
|
713
|
-
默认的 12 小时;
|
|
714
|
-
- "IDE 被关掉"这一类会被下一次 IDE 工具调用暴露 (HTTP 直接报错),由 `execute.after` 钩子分类后自愈;不产生 error 的场景
|
|
715
|
-
(例如
|
|
716
|
-
IDE 进程还在但项目没打开)由「IDE 在、项目没打开」分支处理。
|
|
717
|
-
- 配置存在插件名下 KV (`plugin:opencode-idea:project/<root 路径>`),内容是
|
|
718
|
-
`{ root: { path, app }, children: [{ path, app }] }` —— root 与每个子项目 **各自的 IDE** 都记在里面。插件实例按
|
|
719
|
-
**Location**(项目/打开目录)创建,配置按 **root 路径**共享;旧版存过的布尔 `true` 会按"root 用 idea 的 v1 配置"读入。
|
|
720
|
-
- `/delete-in-ide` 会 **注销全部 IDE MCP** 并删 **整个**配置 + 清空注入的环境。
|
|
721
|
-
- **目录名就是工具前缀,不合规一律拒绝**:必须匹配 `^[a-z0-9][a-z0-9_-]*$`(小写字母或数字开头,只含小写字母、数字、`-`、`_`)、
|
|
722
|
-
不与已登记项目重名、不占用保留名 `ide`。插件 **绝不**自动改名或自动消歧 —— 静默改一个名字,等于用户的目录和模型看到的工具名
|
|
723
|
-
对不上,而且没人会发现。
|
|
724
|
-
- 插件 **完全不再检查 `.idea` 目录**:是不是 IDE 项目只由配置 (`/open-in-ide` 设置、`/delete-in-ide` 清除)决定。
|
|
725
|
-
(`/open-in-ide` 会让对应 IDE 打开并导入该项目,`.idea` 由 IDE 自己生成,插件无需也不应据此判断。)
|
|
726
|
-
- **自动拉起 IDE 仅实现 macOS** (`open -a`)。其他平台 (包括 Windows)没有对应的启动命令, **请手动打开 IDE 和项目**,再执行
|
|
727
|
-
`/open-in-ide` —— 端口在监听、项目被认得就会正常接入,其余能力 (注册、探测、自愈、工具暴露)与 macOS 完全一致。
|
|
728
|
-
IDE 里打开 MCP 服务 (2026.2+)并启用 Brave Mode;首次打开若弹 Trust 对话框,需确认后重试。
|
|
729
|
-
- IDE MCP 没有「打开项目」工具,因此打开动作走 OS/CLI,而非 MCP。
|
|
559
|
+
- skill 定义的字段名 **以 schema 为准**,不要照官方文档:`Skill.Info` 需要 `path`(AbsolutePath),文档里写的 `location`
|
|
560
|
+
不存在 —— 用错会让 `ctx.skill.transform` 抛 `SchemaError`,而**任何 transform 失败都会禁用整个插件**
|
|
561
|
+
(0.0.6 就是这样丢了 `/open-in-ide`)。完整字段是 `id` / `name` / `description?` / `autoinvoke?` / `path` / `content`。
|
|
562
|
+
- 一批用不到的 IDE MCP 工具被硬编码隐藏(`src/plugin.js` 的 `HIDDEN_IDE_TOOL_SUFFIXES`,**共 48 个后缀**):
|
|
563
|
+
VCS(`git_status` / `get_repositories`)、Router 派发工具(`execute_tool`)、Debugger(全部 `xdebug_*`)、Dev Kit MCP、
|
|
564
|
+
Inspection KTS MCP、解释器环境 MCP、**IDE 内置终端**(`execute_terminal_command`:实测不继承项目环境)、
|
|
565
|
+
**整个 Database 组**(数据源由用户在 IDE 里配置)、**有原生对应的文件搜索**(`search_text` / `search_regex` / `search_file`)、
|
|
566
|
+
以及**建文件**(`create_new_file`)。配套插件自带的那个写文件工具同样隐藏。**`apply_patch` 不在隐藏列表里**。
|
|
567
|
+
存的是**后缀**,清洗时按当前每个项目前缀**各删一遍** —— 只处理 `ide_` 会让子项目那几份原样送到模型面前。
|
|
568
|
+
实测(拿运行中的 IDE MCP 拉 `tools/list`,单个项目):IDE 暴露 **75** 个 → 隐藏 47 → **模型可见 28 个**,
|
|
569
|
+
而且这 28 个**恰好等于引导里点名的 28 个**。多项目就是 N × 28。
|
|
570
|
+
- **IDE 的「目录包含」语义(实测)**:请求路径落在某个**已打开项目的目录树内**时,IDE 会把它解析到那个项目,而不是报错。
|
|
571
|
+
所以「子项目与 root 配同一个 IDE」时,只要那个 IDE 打开了 root,子项目就被判成"已打开" —— 它确实是 root 项目的一部分,
|
|
572
|
+
工具能操作它的文件,但语言支持按 **root 项目**走(例如 Python 子项目被 root 的 Web 模块解析,符号检索与检查会不准)。
|
|
573
|
+
这是 IDE 的语义,绕过不了;要让子项目按自己的模块解析,就得让它在那个 IDE 里**单独打开**成一个项目。
|
|
574
|
+
- **项目路径按 IDE 的写法归一化**:IDE 收的是系统无关形式(正斜杠),OpenCode 的 `location` 在 Windows 上是原生反斜杠,
|
|
575
|
+
所以 `src/mcp/idea.js` 的 `ideProjectPath()` 把它转成正斜杠,**注册与探测走同一个函数**,两者必须一致,
|
|
576
|
+
否则会用一种写法注册、又用另一种写法去问,永远匹配不上。macOS 上是原样透传。
|
|
577
|
+
- 配置存在插件名下 KV(`plugin:opencode-idea:project/<root 路径>`),内容是
|
|
578
|
+
`{ root: { path, app }, children: [{ path, app }] }`。插件实例按 **Location**(项目/打开目录)创建,配置按 **root 路径**共享。
|
|
579
|
+
- **目录名就是工具前缀,不合规一律拒绝**:必须匹配 `^[a-z0-9][a-z0-9_-]*$`、不与已登记项目重名、不占用保留名 `ide`。
|
|
580
|
+
插件**绝不**自动改名或自动消歧 —— 静默改一个名字,等于用户的目录和模型看到的工具名对不上,而且没人会发现。
|
|
581
|
+
- 插件**完全不再检查 `.idea` 目录**:是不是 IDE 项目只由配置决定(`/open-in-ide` 会让 IDE 打开并导入该项目,
|
|
582
|
+
`.idea` 由 IDE 自己生成)。
|
|
583
|
+
- **自动拉起 IDE 仅实现 macOS**(`open -a`)。其他平台(包括 Windows)没有对应的启动命令,**请手动打开 IDE 和项目**,
|
|
584
|
+
再执行 `/open-in-ide` —— 端口在监听、项目被认得就会正常接入,其余能力与 macOS 完全一致。
|
|
585
|
+
IDE MCP 没有「打开项目」工具,因此打开动作走 OS/CLI,而非 MCP。
|
|
730
586
|
- 插件热重载会断开 IDE 连接,需重跑 `/open-in-ide`。
|
|
731
|
-
-
|
|
732
|
-
`
|
|
733
|
-
唯一的例外是 `rebuild` 参数 (见「工具参数」),它对每个项目前缀各处理一遍。
|
|
587
|
+
- IDE 工具以 `<项目前缀>_*` 直接暴露给模型(`codemode: false`),不用 `execute` 包裹;
|
|
588
|
+
插件不修改任何**它不接管**的工具定义或描述,唯一的例外是 `rebuild` 参数(见「工具参数」)。
|
|
734
589
|
|
|
735
590
|
## License
|
|
736
591
|
|