opencode-idea 0.0.8 → 0.0.10
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 +59 -77
- package/package.json +6 -7
- package/skills/idea-run-config/SKILL.md +118 -0
- package/src/ide-guidance.js +58 -23
- package/src/{ide-env.js → mcp/ide-env.js} +1 -1
- package/src/{idea-mcp.js → mcp/idea.js} +2 -2
- package/src/plugin.js +271 -54
- package/src/project.js +0 -15
- package/src/skill/loader.js +105 -0
- package/CHANGELOG.md +0 -80
- package/src/ide-run-config-skill.js +0 -61
- package/src/ide-run-config-skill.md +0 -92
package/README.md
CHANGED
|
@@ -6,21 +6,23 @@ OpenCode 插件,面向 JetBrains 项目 (IntelliJ IDEA / PyCharm / WebStorm 等)
|
|
|
6
6
|
检索、读、改、构建、重构、调试、数据库……)注册进 OpenCode。
|
|
7
7
|
2. **优先使用 IDE 工具** —— IDE 可用时,向模型注入"项目内操作优先使用 IDEA MCP"的系统提示引导 + 能力映射;IDE 不可用时回退原生工具
|
|
8
8
|
(正常回退,无需声明)。
|
|
9
|
-
3. **注入 IDE 环境变量** —— 从 IDE
|
|
10
|
-
注入每个 OpenCode shell。
|
|
9
|
+
3. **注入 IDE 环境变量** —— 从 IDE 集成终端读取它实际运行的环境,注入每个 OpenCode shell。
|
|
11
10
|
|
|
12
|
-
非 JetBrains 项目完全不受影响。
|
|
13
|
-
|
|
11
|
+
非 JetBrains 项目完全不受影响。 **手动接入**:插件启动不探测、不连接 IDE,只有跑 `/open-in-idea` 才接入。跑过一次后,该**项目**
|
|
12
|
+
被「标记」(持久化在 `ctx.storage`,重启 OpenCode 后仍在):之后每条用户消息都默认 IDE 仍在,一旦发现 IDE MCP 未连接,插件会在
|
|
13
|
+
构建本次请求前自动重跑一遍接入流程并按需补一条通知。`/close-in-idea` 删除该标记。没跑过 `/open-in-idea` 的项目保持全手动、零自主动作。
|
|
14
14
|
|
|
15
15
|
## 工作原理
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
setup(
|
|
19
|
-
a.
|
|
20
|
-
b.
|
|
18
|
+
setup(启动, 每项目一次):
|
|
19
|
+
a. 读项目标记(ctx.storage: project/<项目路径>; OpenCode 重启后仍生效)
|
|
20
|
+
b. 读 IDE 集成终端环境 → 通过 shell create.before 钩子注入每个 shell(null: 未连 IDE 时为空)
|
|
21
|
+
c. 注册 /open-in-idea 与 /close-in-idea 命令 + session context/prompt 钩子
|
|
21
22
|
不探测、不注册 MCP
|
|
22
23
|
|
|
23
24
|
/open-in-idea(手动触发):
|
|
25
|
+
0. 写项目标记 = true(持久化)+ 清内存缓存(下次读取者回源)
|
|
24
26
|
1. 探测本地 JetBrains IDE MCP 服务(/stream)
|
|
25
27
|
2. 已就绪 → 直接连接
|
|
26
28
|
3. 未就绪 → open -a "IntelliJ IDEA" <项目目录>(复用已运行实例),然后立即提示:
|
|
@@ -31,6 +33,16 @@ setup(启动):
|
|
|
31
33
|
b. 读 IDE 终端环境 → 更新注入每个 shell 的环境变量
|
|
32
34
|
c. 启用系统提示引导(直接调用 `idea_*` 工具)+ IDEA MCP 工具描述前缀
|
|
33
35
|
6. 发一条结果通知(用户看到干净气泡,模型只回"收到")
|
|
36
|
+
|
|
37
|
+
/close-in-idea(手动触发):
|
|
38
|
+
1. 删项目标记 + 清内存缓存
|
|
39
|
+
2. 发一条结果通知(不改动已建立的连接)
|
|
40
|
+
|
|
41
|
+
后续用户消息(仅在被标记的项目里):
|
|
42
|
+
1. 项目已标记 → 默认 IDE 仍在线
|
|
43
|
+
2. 查 OpenCode 里 `idea` MCP server 状态;不是 connected/pending(真断了)→ 在 prompt 钩子里同步重跑上面的接入流程
|
|
44
|
+
3. 成功 → 本条消息即可用 `idea_*` 工具,并补一条"已自动重新连接"通知(排在用户消息之后)
|
|
45
|
+
4. 失败 → 静默,沿用 fast-fail 恢复提示(不会每条消息都重复拉起 IDE)
|
|
34
46
|
```
|
|
35
47
|
|
|
36
48
|
## 在 IDE 中打开项目 (`/open-in-idea`)
|
|
@@ -54,6 +66,11 @@ Brave Mode 后再次执行 `/open-in-idea` 即可接上。
|
|
|
54
66
|
**热更新会断开**:插件源码变更会触发 V2 热重载,注册随旧实例一起释放,`activePort` 归零。这是手动模式的预期行为 ——
|
|
55
67
|
**改完插件后重跑一次 `/open-in-idea`** 即可恢复。
|
|
56
68
|
|
|
69
|
+
## 关闭接入 (`/close-in-idea`)
|
|
70
|
+
|
|
71
|
+
输入 `/close-in-idea`:删除当前项目的接入标记(持久化 + 内存缓存一并清),之后 IDE MCP 即使断开也**不再自动重连**。命令只删标记,
|
|
72
|
+
**不主动断开已建立的连接**——当前已注册的工具会保留到连接自然中断为止。想重新接入,再次执行 `/open-in-idea`。
|
|
73
|
+
|
|
57
74
|
## IDE 工具优先 (直接调用引导)
|
|
58
75
|
|
|
59
76
|
IDE MCP 可用时,插件通过 **系统提示注入**提高模型主动使用 IDEA MCP 的概率;不修改任何工具 (原生或 IDEA)
|
|
@@ -64,13 +81,13 @@ IDE MCP 可用时,插件通过 **系统提示注入**提高模型主动使用 ID
|
|
|
64
81
|
- 检索 → `idea_search_text` / `idea_search_regex` / `idea_search_file`(参数 `q`,可带 `paths` 收窄)
|
|
65
82
|
- 读文件 → `idea_read_file`(`file_path` / `offset` / `limit`);目录 → `idea_list_directory_tree`
|
|
66
83
|
- 改文件 → `idea_apply_patch`(`input`);新建 → `idea_create_new_file`(`pathInProject`)
|
|
67
|
-
- 校验 → `idea_lint_files` / `idea_get_file_problems`
|
|
84
|
+
- 校验 → `idea_lint_files` / `idea_get_file_problems`
|
|
68
85
|
- 重构/格式化 → `idea_rename_refactoring` / `idea_reformat_file`
|
|
69
86
|
- 数据库 → `idea_list_database_connections` / `idea_execute_sql_query` / `idea_preview_table_data` 等;没有数据源时先建议用户在
|
|
70
87
|
IDEA 里配置
|
|
71
88
|
- 边界:只约束 **代码与文件操作**;未暴露的领域 (版本控制、shell/终端等) 工具表里没有 `idea_*` 选项,模型自然走原生
|
|
72
89
|
|
|
73
|
-
不会把整份工具目录重复塞进 system prompt
|
|
90
|
+
不会把整份工具目录重复塞进 system prompt,也不要求模型先写编排代码。IDEA MCP 工具自身的
|
|
74
91
|
description/schema 仍是参数使用的权威来源;工具返回业务错误时先修正参数,不要误判成工具不可用。MCP 不可用时立即
|
|
75
92
|
提示用户执行 `/open-in-idea`,不重复探测或等待。
|
|
76
93
|
|
|
@@ -79,7 +96,7 @@ IDE MCP 可用时,插件通过 **系统提示注入**提高模型主动使用 ID
|
|
|
79
96
|
## Run Configuration 管理 (skill)
|
|
80
97
|
|
|
81
98
|
IDE MCP 只能 **查**(`get_run_configurations`)和 **执行**(`execute_run_configuration`)run configuration, **没有增删改**
|
|
82
|
-
|
|
99
|
+
。插件在**被标记的项目**里注册一个 `idea-run-config` skill 来补上这块:
|
|
83
100
|
|
|
84
101
|
- 只操作 **项目级** `.run/<name>.run.xml`(可提交 git、团队共享), **不碰** `.idea/workspace.xml`;
|
|
85
102
|
- 不内置任何按类型的模板 —— 先找项目里的 **真实样本**(`**/*.run.xml`,或让用户在 IDEA 里勾 "Store as project file"
|
|
@@ -87,15 +104,16 @@ IDE MCP 只能 **查**(`get_run_configurations`)和 **执行**(`execute_run_conf
|
|
|
87
104
|
- 写完必须用 `idea_get_run_configurations` **校验它能被列出**、并用 `idea_execute_run_configuration` **实跑确认**;
|
|
88
105
|
- 改配置用 `idea_apply_patch` **定向改 option**,不整文件重写。
|
|
89
106
|
|
|
90
|
-
skill
|
|
91
|
-
`
|
|
107
|
+
**skill 的生命周期跟随项目标记**:`/open-in-idea` 打标记时注册、`/close-in-idea` 清标记时注销;曾被标记过的项目在
|
|
108
|
+
`setup` 时(读回标记)就会注册。未标记的项目不受影响。文档放在 `skills/<id>/SKILL.md`(每个 skill 一个目录,`name` /
|
|
109
|
+
`description` 走 YAML frontmatter),由 `src/skill/loader.js` 扫描加载 —— **加新 skill 只需加一个目录,不用改代码**。
|
|
92
110
|
|
|
93
111
|
## 环境变量注入
|
|
94
112
|
|
|
95
|
-
**唯一来源:IDE 集成终端**(`execute_terminal_command printenv`)。它反映 IDE 实际运行的环境 ——
|
|
96
|
-
|
|
113
|
+
**唯一来源:IDE 集成终端**(`execute_terminal_command printenv`)。它反映 IDE 实际运行的环境 —— 这些环境往往在 .idea
|
|
114
|
+
配置里读不到。
|
|
97
115
|
|
|
98
|
-
抽取的变量 (`src/ide-env.js` 的 `SDK_ENV_KEYS`):`JAVA_HOME` / `JRE_HOME` / `JDK_HOME` / `VIRTUAL_ENV` / `PYTHONPATH` /
|
|
116
|
+
抽取的变量 (`src/mcp/ide-env.js` 的 `SDK_ENV_KEYS`):`JAVA_HOME` / `JRE_HOME` / `JDK_HOME` / `VIRTUAL_ENV` / `PYTHONPATH` /
|
|
99
117
|
`PYTHONHOME` / `GOROOT` / `GOPATH` / `GOBIN` / `NODE_PATH` / `NVM_BIN` / `NVM_DIR` / `MAVEN_HOME` / `M2_HOME` /
|
|
100
118
|
`GRADLE_HOME` / `SDKMAN_DIR` / `CONDA_PREFIX`。
|
|
101
119
|
|
|
@@ -212,16 +230,23 @@ export { default } from "/Users/yutao/Projects/opencode-idea/src/index.js";
|
|
|
212
230
|
|
|
213
231
|
## 行为
|
|
214
232
|
|
|
215
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
233
|
+
- **被标记的项目**:启动读回标记后挂上 shell 环境钩子 (环境值待接入后填充)、注册 `idea-run-config` skill;
|
|
234
|
+
`/open-in-idea` 接入 IDE MCP + 启用引导。
|
|
235
|
+
- **未标记的项目**:不改环境、不注册、不注入任何 IDEA 提示 (每请求零开销;setup 时只做一次项目标记读取)。
|
|
236
|
+
- **手动接入**:启动 **不探测、不连接 IDE MCP**;注册、**环境重读**、引导注入全部由 `/open-in-idea` 触发。
|
|
218
237
|
`context` 钩子按「每次模型调用」触发 (含工具续跑),是**请求的纯函数**:它只看本次请求自带的工具快照 ——
|
|
219
|
-
有 `idea_*`
|
|
220
|
-
|
|
238
|
+
有 `idea_*` 就剥离隐藏工具并注入引导;没有则只在项目被标记时给恢复提示。**不做任何 I/O(get 只看内存缓存),没有定时器,
|
|
239
|
+
没有事件订阅**。一次失败的调用不会触发探测或注销。
|
|
240
|
+
- **项目标记自动重连**:`/open-in-idea` 会持久化一个**项目级**标记(插件名下 KV,key 为 `project/<项目根路径>`,重启 OpenCode 后
|
|
241
|
+
仍在),并在内存缓存。之后每条用户消息经 `prompt` 钩子处理时,先查 OpenCode 里 `idea` MCP server 的状态:不是
|
|
242
|
+
`connected`/`pending`(真断了)才同步重跑一遍完整接入流程 (探测 → `open -a` → 等 MCP → 注册 + `tool.reload`),让**这条消息
|
|
243
|
+
本身**就能用上 IDEA 工具;恢复成功后补一条会话通知。`/close-in-idea` 删标记后即不再自愈。失败时沿用启动保护的冷却与次数上限
|
|
244
|
+
(`launchCooldownMs` / `launchMaxAttempts`),不会每条消息都重复拉起 IDE。
|
|
245
|
+
- **判定只看 MCP 状态,不看工具报错**:是否重连只取决于 `idea` MCP server 的连接状态(`ctx.mcp.list()`)。工具调用的业务报错
|
|
246
|
+
(如 `File not found`)只可能发生在 server 仍 `connected` 时,所以**永远不会**被误判成断开;插件也不注册 `tool.execute.after`
|
|
247
|
+
钩子,一次失败的调用不会触发探测或注销。
|
|
221
248
|
- **`/open-in-idea`**:手动打开当前项目并即时加载能力 (已在 IDE 打开时则只重新加载);MCP 未开启时只发一次提示并
|
|
222
|
-
fast-fail,用户手动开启 MCP + Brave Mode
|
|
223
|
-
- **不响应工具报错**:插件**不注册** `tool.execute.after` 钩子。一次 `idea_*` 调用失败 (业务错误如 `File not found`、
|
|
224
|
-
或偶发传输抖动) 不会触发任何探测、注销或重连 —— 连接状态只由 `/open-in-idea` 决定。
|
|
249
|
+
fast-fail,用户手动开启 MCP + Brave Mode 后重试。**`/close-in-idea`**:删项目标记,不改动已建立的连接。
|
|
225
250
|
|
|
226
251
|
## 限制
|
|
227
252
|
|
|
@@ -234,68 +259,25 @@ export { default } from "/Users/yutao/Projects/opencode-idea/src/index.js";
|
|
|
234
259
|
- 环境变量依赖 `execute_terminal_command` + Brave Mode;拿不到时环境为空。
|
|
235
260
|
- 一批用不到的 IDEA MCP 工具被硬编码隐藏 (`src/plugin.js` 的 `HIDDEN_IDEA_TOOLS`,共 31 个):VCS (`idea_git_status` /
|
|
236
261
|
`idea_get_repositories`,版本控制走原生 `git`)、Router 派发工具 (`idea_execute_tool`,未启用 router-only 时冗余)、Debugger
|
|
237
|
-
(全部 `idea_xdebug_*`)、Dev Kit MCP、Inspection KTS MCP
|
|
238
|
-
IDEA 里配置,AI 只读和查询)。`idea_execute_terminal_command` 也不暴露给模型
|
|
239
|
-
|
|
262
|
+
(全部 `idea_xdebug_*`)、Dev Kit MCP、Inspection KTS MCP、解释器环境 MCP、以及数据库的建/改数据源 (数据源由用户在
|
|
263
|
+
IDEA 里配置,AI 只读和查询)。`idea_execute_terminal_command` 也不暴露给模型 —— 它只由插件内部读取 IDE 环境,而那是插件直接调
|
|
264
|
+
MCP,不经过模型工具表。
|
|
240
265
|
- 探测只确认「MCP 服务是否在监听」,不校验「当前项目是否已在 IDE 打开」;MCP 关闭时会用一次短探测并立即给出设置提示。
|
|
241
266
|
- OpenCode 的 MCP 自动重连**只覆盖 legacy Streamable HTTP 的 session 过期**(`SessionExpiredError`:有 session id + 404 /
|
|
242
267
|
"Server not initialized" → `recover()` + 重试该次调用);modern 端点 (IDE 2026.2,protocol 2025-06-18,不带 session id)
|
|
243
|
-
的连接被关只会置 `failed`
|
|
268
|
+
的连接被关只会置 `failed` 并移除工具,**不会自动重连**。**被标记的项目**由插件在 `prompt` 钩子里查 MCP 状态并按需重跑接入
|
|
269
|
+
流程补这个缺口;未标记的项目仍走手动 `/open-in-idea`。
|
|
270
|
+
- 项目标记存在插件名下 KV (`plugin:opencode-idea:project/<项目根路径>`)。插件实例按 **Location**(项目/打开目录)创建,标记按
|
|
271
|
+
**项目根路径**共享;但两个不同 Location(例如项目根与某子目录)各有自己的内存缓存 —— 在其中一个执行 open/close 命令,另一个
|
|
272
|
+
实例要等它下次回源(目前只在 setup 读一次)才会看到。单目录打开项目的常见用法不受影响。
|
|
273
|
+
- `/close-in-idea` **只删标记,不断开已建立的连接**:已注册的 IDEA 工具会保留到连接自然中断,期间标记与实际连接可能短暂不一致。
|
|
274
|
+
- 插件**完全不再检查 `.idea` 目录**:是否是 IDEA 项目只由项目标记(`/open-in-idea` 设置、`/close-in-idea` 清除)决定。
|
|
275
|
+
(`/open-in-idea` 会让 IDEA 打开并导入该项目,`.idea` 由 IDEA 自己生成,插件无需也不应据此判断。)
|
|
244
276
|
- 打开 IDE 仅实现 macOS (`open -a`);首次打开若弹 Trust 对话框,需确认后重试。
|
|
245
277
|
- IDE MCP 没有「打开项目」工具,因此打开动作走 OS/CLI,而非 MCP。
|
|
246
278
|
- 插件热重载会断开 IDE 连接,需重跑 `/open-in-idea`。
|
|
247
279
|
- 插件不修改任何工具的定义、描述或可用性;IDE MCP 工具以 `idea_*` 直接暴露给模型 (`codemode: false`),不用 `execute` 包裹。
|
|
248
280
|
|
|
249
|
-
### `execute_terminal_command` 与原生 shell:互补,不是替代
|
|
250
|
-
|
|
251
|
-
> **结论:已加入 `HIDDEN_IDEA_TOOLS`,不暴露给模型** —— 下面的对比数据用于说明这个决定,也供手工调用时参考。
|
|
252
|
-
> 理由:① AI 拿不到长任务结果 (约 60 秒硬截止 + 没有工具能读终端缓冲);② "看结尾"被原生覆盖 (原生超限时保尾部,
|
|
253
|
-
> 并把完整输出存成文件);③ 唯一实质价值是"让用户看到 / 接手",而模型不需要它 —— 插件自己的 `printenv` 是
|
|
254
|
-
> **直接调 MCP**(`src/ide-env.js`),不经过模型工具表。
|
|
255
|
-
|
|
256
|
-
两者能力相近但取舍不同 (macOS + IDEA 2026.2.3 实测):
|
|
257
|
-
|
|
258
|
-
| 维度 | 原生 `shell` | IDE `execute_terminal_command` |
|
|
259
|
-
| ---------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
|
|
260
|
-
| 每次调用增量开销 | ~9ms(OS spawn) | ~90ms(多一次 MCP 往返) |
|
|
261
|
-
| 会话常驻 | ❌ 每次新 shell | ❌ 每次新 shell(`reuseExistingTerminalWindow` 也不保留变量 / `cd`) |
|
|
262
|
-
| `timeout` | ✅ 生效:到点即中止并杀掉进程(长任务须自行调大) | **约 60 秒硬截止**,`timeout` 参数无法改变:到点返回已有输出 + `is_timed_out`,**不杀进程** |
|
|
263
|
-
| 输出截断 | `tool_output` 上限(默认 8000 行 / 256KB);想看结尾得在命令里自己接 `tail` | `maxLinesCount` + `truncateMode`,可**只留结尾** |
|
|
264
|
-
| 用户可见性 | 只在 agent 侧 | 在 IDE 里,用户可看 / 可接手 |
|
|
265
|
-
|
|
266
|
-
**各自优势**
|
|
267
|
-
|
|
268
|
-
- **原生**:更快、超时可控 (到点杀进程)、输出上限更宽 —— **默认走这条**。
|
|
269
|
-
- **IDE**:
|
|
270
|
-
① **长任务**(安装 / 构建 / 迁移):`reuseExistingTerminalWindow: true` 让它在 IDE 终端里跑,用户 **能看进度、能随时取消**。
|
|
271
|
-
但工具 **约 60 秒**就返回已产生的输出 (`is_timed_out: true`),之后命令仍在后台跑、AI 拿不到后续 —— 所以它适合
|
|
272
|
-
"**交给用户盯**",不适合"AI 等最终结果"(那要用原生,或让命令把结果写文件再读)。
|
|
273
|
-
② **精确控制返回哪一段 + 行数**:`truncateMode` 取 `START`(保头)/`MIDDLE`(头+尾)/`END`(保尾)+ `maxLinesCount`。
|
|
274
|
-
其中 **`MIDDLE` 是真正独有的** —— 原生超限时 **固定保尾部**,并把 **完整输出存成文件**返回路径,所以 `END` 与原生默认重复,
|
|
275
|
-
而原生那份全量文件反而是 IDE 侧拿不到的 (IDE 丢弃的部分只留在终端)。
|
|
276
|
-
|
|
277
|
-
判据 (手工调用时的参考):**要"人看" 或 要"头 + 尾一起看" (`MIDDLE`) → IDE;其余 → 原生。**
|
|
278
|
-
|
|
279
|
-
**`truncateMode` 速查 (语义反直觉,以实测为准)**
|
|
280
|
-
|
|
281
|
-
| 传值 | 保留哪部分 |
|
|
282
|
-
| -------------- | -------------------------- |
|
|
283
|
-
| 不传 / `START` | 开头 |
|
|
284
|
-
| `MIDDLE` | 头 + 尾 |
|
|
285
|
-
| **`END`** | **结尾**(截断标记在最上面) |
|
|
286
|
-
| `NONE` | 全部,不截断 |
|
|
287
|
-
|
|
288
|
-
默认 `maxLinesCount` ≈ 1000 行,要更多就显式传。注意 `END` 是"**保留结尾**",不是"截掉结尾"。
|
|
289
|
-
|
|
290
|
-
## 测试
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
npm test
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
使用 [Vitest](https://vitest.dev/),含纯函数测试、IDE 环境解析、环境合并、启动保护状态机、IDE 启动器、引导文本,以及「假 IDE MCP
|
|
297
|
-
服务」的端到端探测与插件集成测试。
|
|
298
|
-
|
|
299
281
|
## License
|
|
300
282
|
|
|
301
283
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-idea",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "OpenCode plugin that connects to JetBrains IDE MCP servers and prefers IDE tools when the current project is open in the IDE",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -8,16 +8,15 @@
|
|
|
8
8
|
"index.js",
|
|
9
9
|
"src/index.js",
|
|
10
10
|
"src/plugin.js",
|
|
11
|
-
"src/idea
|
|
12
|
-
"src/ide-env.js",
|
|
11
|
+
"src/mcp/idea.js",
|
|
12
|
+
"src/mcp/ide-env.js",
|
|
13
13
|
"src/project.js",
|
|
14
14
|
"src/env.js",
|
|
15
15
|
"src/ide-launcher.js",
|
|
16
16
|
"src/ide-guidance.js",
|
|
17
|
-
"src/
|
|
18
|
-
"
|
|
19
|
-
"README.md"
|
|
20
|
-
"CHANGELOG.md"
|
|
17
|
+
"src/skill/loader.js",
|
|
18
|
+
"skills",
|
|
19
|
+
"README.md"
|
|
21
20
|
],
|
|
22
21
|
"scripts": {
|
|
23
22
|
"test": "vitest run",
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: idea-run-config
|
|
3
|
+
description: "管理 JetBrains IDE 的 run configuration:在项目级 `.run/*.run.xml` 里创建、修改、删除运行配置。当用户要新建运行配置、修改或删除已有配置、把脚本或执行流程包成可运行的配置,或让 AI 执行用户手配的配置时使用。只操作项目级 `.run/` 文件,不碰 `.idea/workspace.xml`。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# JetBrains Run Configuration 管理
|
|
7
|
+
|
|
8
|
+
## 怎么操作:查询走 MCP,增删改走文件
|
|
9
|
+
|
|
10
|
+
| 动作 | 手段 |
|
|
11
|
+
| ----------------------------- | ------------------------------------------------------------------------- |
|
|
12
|
+
| 查看有哪些配置 / 判断是否生效 | **MCP 工具** `idea_get_run_configurations` |
|
|
13
|
+
| 新增 / 修改 / 删除 | **直接操作文件** `.run/<name>.run.xml` |
|
|
14
|
+
| 执行 / 调试 | **MCP 工具** `idea_execute_run_configuration` |
|
|
15
|
+
| 找同类型样本 | 只读 **`.run/`** 下的 `*.run.xml`(`idea_search_file` + `paths: [".run"]`) |
|
|
16
|
+
|
|
17
|
+
## 存放位置:只有 `.run/`
|
|
18
|
+
|
|
19
|
+
所有配置都写 **项目根 `.run/<name>.run.xml`**。
|
|
20
|
+
|
|
21
|
+
**只做项目级配置,不存在全局(IDE 级)配置** —— 所以永远不写 IDE 的全局配置目录,也不写 `.idea/workspace.xml`。
|
|
22
|
+
|
|
23
|
+
新建配置时 `.run/` 的结构:
|
|
24
|
+
|
|
25
|
+
```xml
|
|
26
|
+
|
|
27
|
+
<component name="ProjectRunConfigurationManager">
|
|
28
|
+
<configuration default="false" name="<名字>" type="<类型 ID>">
|
|
29
|
+
<option name="..." value="..."/>
|
|
30
|
+
<envs/>
|
|
31
|
+
<method v="2"/>
|
|
32
|
+
</configuration>
|
|
33
|
+
</component>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 铁律
|
|
37
|
+
|
|
38
|
+
1. 不要凭空写 option 名。先在 **`.run/` 里**找真实样本:
|
|
39
|
+
- 用 `idea_search_file` 传 `q="*.run.xml"` + `paths: [".run"]` 看项目里已有的配置(**只看 `.run/`**,不去翻 `.idea/`)。
|
|
40
|
+
- 没有目标类型 → 请用户在 IDEA 里新建一个配置,勾选 "Store as project file" 并**存到项目根的 `.run/`**,再读生成的 XML 当模板。
|
|
41
|
+
- 仍然没有 → 直接说明需要该类型的真实样本,不要猜 option 名。
|
|
42
|
+
2. 写完必须校验:
|
|
43
|
+
- **唯一判据**是 `idea_get_run_configurations` 里能看到它。**看不到就是错的** —— 不许用"应该生效了"结案。
|
|
44
|
+
- 看不到的常见原因(按概率):`type` 不是 IDE 当前启用的类型(**IDEA 会静默忽略整个文件,日志里也不报**)、根 component 名写错、
|
|
45
|
+
结构或 option 名不对。改掉重试。
|
|
46
|
+
- 能看到不等于能跑:`idea_execute_run_configuration` 传 `configurationName` 实跑一次确认。Shell Script 必须
|
|
47
|
+
`EXECUTE_IN_TERMINAL=false`,否则实跑直接失败(见"样本"一节)。
|
|
48
|
+
- IDE MCP 不可用 → 提示用户执行 `/open-in-idea` 后再校验,不要跳过校验当成成功。
|
|
49
|
+
3. 改配置用定向修改:只用 `idea_apply_patch` 改需要的那几行 option,不要整文件重写。
|
|
50
|
+
4. 删除配置 = 删除对应的 `.run/<name>.run.xml`;不要试图从 `.idea/workspace.xml` 里删。
|
|
51
|
+
|
|
52
|
+
## 常见字段
|
|
53
|
+
|
|
54
|
+
- `$PROJECT_DIR$`:项目根;`$MODULE_WORKING_DIR$`:模块目录。
|
|
55
|
+
- 环境变量:
|
|
56
|
+
|
|
57
|
+
```xml
|
|
58
|
+
|
|
59
|
+
<envs>
|
|
60
|
+
<env name="KEY" value="VALUE"/>
|
|
61
|
+
</envs>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- Before launch 流程:`<method v="2">` 里加 `Make` 选项会先构建。
|
|
65
|
+
|
|
66
|
+
## 起步参考 (不是契约;动手前一律以真实样本为准)
|
|
67
|
+
|
|
68
|
+
| 用途 | type | 关键 option |
|
|
69
|
+
| ---------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
70
|
+
| Shell 脚本 | `ShConfigurationType` | 内联脚本:`SCRIPT_TEXT` + `EXECUTE_SCRIPT_FILE=false`;脚本文件:`SCRIPT_PATH` + `EXECUTE_SCRIPT_FILE=true`;`INTERPRETER_PATH`;`SCRIPT_WORKING_DIRECTORY` |
|
|
71
|
+
| Java 应用 | `Application` | `MAIN_CLASS_NAME`;`WORKING_DIRECTORY`;`PROGRAM_PARAMETERS`;`VM_PARAMETERS` |
|
|
72
|
+
| JUnit | `JUnit` | `TEST_OBJECT`;`MAIN_CLASS_NAME`;`WORKING_DIRECTORY` |
|
|
73
|
+
| Gradle | `GradleRunConfiguration` | `ExternalProjectPath`;`TaskList` |
|
|
74
|
+
| npm | `js.build_tools.npm` | `package-json`;`command`;`scripts` |
|
|
75
|
+
| 串行流程 | `CompoundRunConfiguration` | `method` 里多个 `RunConfigurationTask`(带 `run_configuration_name` / `run_configuration_type`) |
|
|
76
|
+
|
|
77
|
+
### 样本:Shell Script(IDEA 自己生成的 `.run/*.run.xml`)
|
|
78
|
+
|
|
79
|
+
```xml
|
|
80
|
+
<component name="ProjectRunConfigurationManager">
|
|
81
|
+
<configuration default="false" name="Unnamed" type="ShConfigurationType" nameIsGenerated="true">
|
|
82
|
+
<option name="SCRIPT_TEXT" value="" />
|
|
83
|
+
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
|
84
|
+
<option name="SCRIPT_PATH" value="" />
|
|
85
|
+
<option name="SCRIPT_OPTIONS" value="" />
|
|
86
|
+
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
|
87
|
+
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
|
88
|
+
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
|
89
|
+
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
|
90
|
+
<option name="INTERPRETER_OPTIONS" value="" />
|
|
91
|
+
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
|
92
|
+
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
|
93
|
+
<envs />
|
|
94
|
+
<method v="2" />
|
|
95
|
+
</configuration>
|
|
96
|
+
</component>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
要点:`INDEPENDENT_*` 三件套表示该字段**独立于模板**(true = 用本配置里的值)。**内联脚本**要 `EXECUTE_SCRIPT_FILE=false` 并填
|
|
100
|
+
`SCRIPT_TEXT`;**跑脚本文件**要 `EXECUTE_SCRIPT_FILE=true` 并填 `SCRIPT_PATH`(上面的样本是未配置的空模板,所以两处都是空串)。
|
|
101
|
+
|
|
102
|
+
AI 自己写的配置一律给 `EXECUTE_IN_TERMINAL=false`;用户已有的 `true` 配置要执行,先问要不要改。
|
|
103
|
+
|
|
104
|
+
## 命名
|
|
105
|
+
|
|
106
|
+
- 把 `name` 转成文件名:去掉 `/ \ : * ? " < > |` 等非法字符,写成 `.run/<净化后的名字>.run.xml`。
|
|
107
|
+
- 目标文件已存在时,先确认是要覆盖。
|
|
108
|
+
|
|
109
|
+
## 流程
|
|
110
|
+
|
|
111
|
+
1. **查现状**:`idea_get_run_configurations`(有哪些配置)。要找样本则在 `.run/` 里读 `*.run.xml`(限定 `paths: [".run"]`)。
|
|
112
|
+
2. **定结构**:有同类样本 → 照它的结构改写;没有 → 请用户在 IDEA 里建一个并**存到 `.run/`**;仍然没有 → 说明需要真实样本,
|
|
113
|
+
不猜 option 名。
|
|
114
|
+
3. **写文件**:`idea_create_new_file` 写 `.run/<name>.run.xml`。
|
|
115
|
+
4. **校验(必做)**:`idea_get_run_configurations` 必须能看到它 —— 看不到就是错的,不许用"应该生效了"结案。
|
|
116
|
+
5. **实跑(必做)**:`idea_execute_run_configuration` 传 `configurationName` 确认能跑(Shell Script 必须
|
|
117
|
+
`EXECUTE_IN_TERMINAL=false`)。
|
|
118
|
+
6. **改 / 删**:改 = `idea_apply_patch` 定向改 option,不要整文件重写;删 = 删除 `.run/<name>.run.xml`,再确认它从列表里消失。
|
package/src/ide-guidance.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// IDEA MCP routing guidance injected into the model's system prompt.
|
|
2
2
|
//
|
|
3
3
|
// Two rules for this file:
|
|
4
|
-
// 1.
|
|
4
|
+
// 1. Per tool: what it does, its parameters, and the positive flow for using
|
|
5
|
+
// it. No caveats or edge cases, and nothing about languages or frameworks —
|
|
6
|
+
// the project's own setup decides that.
|
|
5
7
|
// 2. The boundary is the project path. Inside it -> idea_* tools.
|
|
6
8
|
// Outside it -> whatever else is available.
|
|
7
9
|
//
|
|
8
10
|
// The IDE MCP server is registered with `codemode: false`, so every idea_*
|
|
9
|
-
// tool is exposed directly on the provider tool list with its full schema.
|
|
10
|
-
//
|
|
11
|
-
//
|
|
11
|
+
// tool is exposed directly on the provider tool list with its full schema. This
|
|
12
|
+
// text only says which tool to use for which operation, plus the parameters and
|
|
13
|
+
// result shapes that are not obvious from the schema alone.
|
|
12
14
|
|
|
13
15
|
/** Build the guidance text for one project path. */
|
|
14
16
|
export function buildIdeGuidance(projectPath) {
|
|
@@ -17,28 +19,61 @@ export function buildIdeGuidance(projectPath) {
|
|
|
17
19
|
``,
|
|
18
20
|
`项目路径: \`${projectPath}\``,
|
|
19
21
|
``,
|
|
20
|
-
`##
|
|
22
|
+
`## 总则`,
|
|
21
23
|
`- 路径在 \`${projectPath}\` 内的代码与文件操作,必须使用 \`idea_*\` 工具。`,
|
|
22
|
-
`-
|
|
23
|
-
`-
|
|
24
|
-
`-
|
|
25
|
-
`- 读文件用 \`idea_read_file\`,不要用 \`read\`。`,
|
|
26
|
-
`- 浏览目录用 \`idea_list_directory_tree\`,不要用 \`ls\` / \`find\`。`,
|
|
27
|
-
`- 修改已有文件用 \`idea_apply_patch\`,不要用 \`edit\` / \`patch\`。`,
|
|
28
|
-
`- 新建文件用 \`idea_create_new_file\`,不要用 \`write\`。`,
|
|
29
|
-
`- 改完代码用 \`idea_lint_files\` 或 \`idea_get_file_problems\` 校验。`,
|
|
30
|
-
`- 工具参数按该工具的 schema 传。`,
|
|
24
|
+
`- 路径在 \`${projectPath}\` 外:用其他可用工具。`,
|
|
25
|
+
`- 直接调用工具,不要用 \`execute\` 包裹;参数按各工具 schema 传。`,
|
|
26
|
+
`- \`idea_*\` 工具缺失、连接失败或超时:提示用户执行 \`/open-in-idea\`,不要重试。`,
|
|
31
27
|
``,
|
|
32
|
-
`##
|
|
33
|
-
`- 数据库操作走 IDEA 的 Database 工具(\`idea_list_database_connections\` / \`idea_introspect_schema\` / \`idea_execute_sql_query\` / \`idea_preview_table_data\` 等),不要用原生 CLI 客户端。`,
|
|
34
|
-
`- 先 \`idea_list_database_connections\` 看有没有现成数据源。`,
|
|
35
|
-
`- 没有数据源时:若目标数据库在 IDEA Database Tools 支持范围内(PostgreSQL / MySQL / MariaDB / Oracle / SQL Server / SQLite / DB2 / H2 / Derby / Sybase / Vertica / Greenplum / ClickHouse / CockroachDB / Snowflake / BigQuery / Redshift / MongoDB / Redis / Cassandra / Couchbase / Hive / DynamoDB / Exasol 等,或任何提供 JDBC 驱动的库),先建议用户在 IDEA 里配好数据源,再回来用工具操作。`,
|
|
36
|
-
`- 不要自己建或改数据源,不要向用户索取数据库凭据。`,
|
|
28
|
+
`## 工具补充(每条只写补充说明;工具定义以自身 schema 为准)`,
|
|
37
29
|
``,
|
|
38
|
-
|
|
39
|
-
`-
|
|
40
|
-
`-
|
|
41
|
-
`- \`
|
|
30
|
+
`### 检索`,
|
|
31
|
+
`- \`idea_search_text\`:不要用 \`grep\`。`,
|
|
32
|
+
`- \`idea_search_regex\`:不要用 \`grep -E\`;结果带行列坐标。`,
|
|
33
|
+
`- \`idea_search_file\`:不要用 \`glob\`。`,
|
|
34
|
+
`- \`idea_search_symbol\`:按名字找符号的声明位置;要看签名 / 文档接着用 \`idea_get_symbol_info\`。`,
|
|
35
|
+
``,
|
|
36
|
+
`### 阅读`,
|
|
37
|
+
`- \`idea_read_file\`:不要用 \`read\`;支持 \`<jar>!/<entry>\` 与 \`jrt://\` 形式的包内读取。`,
|
|
38
|
+
`- \`idea_list_directory_tree\`:不要用 \`ls\` / \`find\`;\`maxDepth\` 控深度。`,
|
|
39
|
+
`- \`idea_get_symbol_info\`:一个符号的签名 / 文档 / 声明位置,\`filePath\` + \`line\` + \`column\`。`,
|
|
40
|
+
`- \`idea_get_all_open_file_paths\`:用户在 IDE 里打开了哪些文件(含当前激活的那个)。`,
|
|
41
|
+
`- \`idea_open_file_in_editor\`:把文件在 IDE 里打开并激活,给用户看。`,
|
|
42
|
+
``,
|
|
43
|
+
`### 写入`,
|
|
44
|
+
`- \`idea_apply_patch\`:不要用 \`edit\` / \`patch\`;只接受项目内路径。`,
|
|
45
|
+
`- \`idea_create_new_file\`:不要用 \`write\`;\`overwrite\` 控制同名覆盖。`,
|
|
46
|
+
`- \`idea_rename_refactoring\`:按符号改名,项目内的引用一并更新。`,
|
|
47
|
+
`- \`idea_reformat_file\`:按 IDE 的代码风格重排。`,
|
|
48
|
+
``,
|
|
49
|
+
`### 校验`,
|
|
50
|
+
`- \`idea_lint_files\`:批量检查,\`files\` 传数组;只列有问题的文件。`,
|
|
51
|
+
`- \`idea_get_file_problems\`:单文件检查,\`filePath\`;结果在 \`errors\`。`,
|
|
52
|
+
`- \`idea_build_project\`:构建校验,\`filesToRebuild\` 可只构建指定文件;有问题返回在 \`problems\`,没返回即没问题。`,
|
|
53
|
+
``,
|
|
54
|
+
`### 分析`,
|
|
55
|
+
`- \`idea_analyze_calls\`:调用关系,\`symbolFqn\` 传符号名,\`analysisKind\` 取 \`INCOMING_CALLS\` / \`OUTGOING_CALLS\`;\`depth\` 控层数,\`childOffset\` 翻页。`,
|
|
56
|
+
`- \`idea_get_project_modules\`:项目模块及类型。`,
|
|
57
|
+
`- \`idea_get_project_dependencies\`:项目直接声明的依赖。`,
|
|
58
|
+
``,
|
|
59
|
+
`### 运行配置`,
|
|
60
|
+
`- \`idea_get_run_configurations\`:读项目级 \`.run/*.run.xml\`(增删改直接改文件);传 \`filePath\` 可列该文件的运行入口。`,
|
|
61
|
+
`- \`idea_execute_run_configuration\`:跑已有配置;Shell Script 要 \`EXECUTE_IN_TERMINAL: false\`;覆盖参数(\`programArguments\` / \`workingDirectory\` / \`envs\`)需配置支持 \`supportsDynamicLaunchOverrides\`。`,
|
|
62
|
+
``,
|
|
63
|
+
`### 数据库 (数据源由用户在 IDE 里配置:不要自己建/改,不要向用户索取数据库凭据)`,
|
|
64
|
+
`- \`idea_list_database_connections\`:先看有没有现成数据源;每条给 \`id\` / \`dbms\` / \`jdbcUrl\` / \`readOnly\` / \`isDDL\`。`,
|
|
65
|
+
`- \`idea_test_database_connection\`(\`id\`):\`hasProblems\` 为 \`NO\` 才算通,另带 DBMS / 驱动版本与 ping。`,
|
|
66
|
+
`- \`idea_list_database_schemas\`(\`connectionId\`):MySQL 下 \`databaseName\` 为空、库名在 \`schemaName\`;带 \`isIntrospected\`。`,
|
|
67
|
+
`- \`idea_list_schema_object_kinds\`(\`connectionId\`):先看有哪些类型可查(\`table\` / \`view\` / \`routine\` / \`scheduled-event\`)。`,
|
|
68
|
+
`- \`idea_list_schema_objects\`(\`connectionId\` + \`databaseName\` + \`schemaName\` + \`kind\`):列该 schema 下的对象。`,
|
|
69
|
+
`- \`idea_introspect_schema\`(\`connectionId\` + \`databaseName\` + \`schemaName\`):拉元数据,返回该 schema 的 \`isIntrospected\`。`,
|
|
70
|
+
`- \`idea_get_database_object_description\`(\`kind\` + \`objectName\`):层级文本,含列类型 / 列注释 / 索引 / 主键。`,
|
|
71
|
+
`- \`idea_execute_sql_query\`:跑 SQL;结果在 \`text\`(CSV)与 \`resultSetId\`,一次 10 行,后续用 \`fetch_query_result\` 翻页。`,
|
|
72
|
+
`- \`idea_fetch_query_result\`(\`resultSetId\` + \`offset\`):按页继续取结果,10 行一页。`,
|
|
73
|
+
`- \`idea_preview_table_data\`(\`tableName\` + \`maxRowCount\`):直接回 CSV。`,
|
|
74
|
+
`- \`idea_list_recent_sql_queries\`(\`connectionId\`):最近(含正在跑)的查询,带 \`sessionId\` / \`state\` / \`status\`。`,
|
|
75
|
+
`- \`idea_cancel_sql_query\`(\`sessionId\`):取消正在跑的查询,成功后该条 \`status\` 变 \`CANCELLED\`。`,
|
|
76
|
+
`- 没有数据源时:若目标库在 IDEA Database Tools 支持范围内(PostgreSQL / MySQL / MariaDB / Oracle / SQL Server / SQLite / DB2 / H2 / Derby / Sybase / Vertica / Greenplum / ClickHouse / CockroachDB / Snowflake / BigQuery / Redshift / MongoDB / Redis / Cassandra / Couchbase / Hive / DynamoDB / Exasol 等,或任何提供 JDBC 驱动的库),先建议用户在 IDEA 里配好数据源,再回来用工具操作。`,
|
|
42
77
|
].join('\n');
|
|
43
78
|
}
|
|
44
79
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// variables. The IDE terminal is the single source of truth for the environment
|
|
3
3
|
// the IDE actually runs with (version-managed Node, goenv Go, SDKMAN Java, ...).
|
|
4
4
|
|
|
5
|
-
import { callTool } from './idea
|
|
5
|
+
import { callTool } from './idea.js';
|
|
6
6
|
|
|
7
7
|
/** SDK-related variables worth mirroring into OpenCode's shell environment. */
|
|
8
8
|
export const SDK_ENV_KEYS = [
|
|
@@ -81,7 +81,7 @@ export async function probePort(port, options = {}) {
|
|
|
81
81
|
params: {
|
|
82
82
|
protocolVersion: PROTOCOL_VERSION,
|
|
83
83
|
capabilities: {},
|
|
84
|
-
clientInfo: { name: 'opencode-idea', version: '0.0.
|
|
84
|
+
clientInfo: { name: 'opencode-idea', version: '0.0.10' },
|
|
85
85
|
},
|
|
86
86
|
}),
|
|
87
87
|
signal: controller.signal,
|
|
@@ -151,7 +151,7 @@ export async function callTool(port, projectPath, name, args = {}, options = {})
|
|
|
151
151
|
params: {
|
|
152
152
|
protocolVersion: PROTOCOL_VERSION,
|
|
153
153
|
capabilities: {},
|
|
154
|
-
clientInfo: { name: 'opencode-idea', version: '0.0.
|
|
154
|
+
clientInfo: { name: 'opencode-idea', version: '0.0.10' },
|
|
155
155
|
},
|
|
156
156
|
});
|
|
157
157
|
const response = await post({
|