opencode-idea 0.1.3 → 0.1.5
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 +116 -63
- package/package.json +6 -1
- package/src/ide-guidance.js +32 -0
- package/src/ide-guidance.template.md +9 -64
- package/src/mcp/idea.js +1 -1
- package/src/plugin-tools-guidance.template.md +18 -0
- package/src/plugin.js +445 -20
- package/src/remote/hosts.js +269 -0
- package/src/remote/hosts.test.js +171 -0
- package/src/remote/persist.js +106 -0
- package/src/remote/persist.test.js +100 -0
- package/src/remote/prompts.js +52 -0
- package/src/remote/prompts.test.js +38 -0
- package/src/remote/sftp.js +242 -0
- package/src/remote/sftp.test.js +47 -0
- package/src/remote/ssh.js +195 -0
- package/src/remote/ssh.test.js +48 -0
- package/src/remote/sync.test.js +204 -0
- package/src/remote/tools.js +403 -0
- package/src/remote/tools.test.js +238 -0
package/README.md
CHANGED
|
@@ -9,10 +9,9 @@ OpenCode 插件,面向 JetBrains 项目 (IntelliJ IDEA / PyCharm / WebStorm 等)
|
|
|
9
9
|
2. **优先使用 IDE 工具** —— IDE 可用时,向模型注入 **逐项目**的引导:在哪个项目里工作,就用哪个项目前缀的工具;IDE 不可用时
|
|
10
10
|
回退原生工具 (正常回退,无需声明)。
|
|
11
11
|
|
|
12
|
-
>
|
|
13
|
-
>
|
|
14
|
-
> **IDE 内置的 `ide_execute_terminal_command` 继续隐藏** ——
|
|
15
|
-
> 详见「Run Configuration 全套」与「终端全套」。
|
|
12
|
+
> **需要项目环境的命令也走原生 shell**:项目配置的 JDK / Node / Go / Maven / Python 由配套插件的 `get_project_env` 取出,
|
|
13
|
+
> 本插件在每条 shell 创建前注入到它的 `PATH` 与环境变量里;**反复发生的流程**仍固化成 Run Configuration。
|
|
14
|
+
> **IDE 内置的 `ide_execute_terminal_command` 继续隐藏** —— 实测它不继承项目环境。详见「Run Configuration 全套」与「终端全套」。
|
|
16
15
|
|
|
17
16
|
非 JetBrains 项目完全不受影响。 **手动接入**:插件启动不探测、不连接任何 IDE。三个命令管全部:
|
|
18
17
|
|
|
@@ -136,6 +135,51 @@ configure_ide_project({ path: "modelserver", ide: "pycharm" }) // 配置一个
|
|
|
136
135
|
|
|
137
136
|
**热更新会断开**:插件源码变更会触发 V2 热重载,注册随旧实例一起释放。 **改完插件后重跑一次 `/open-in-ide`** 即可恢复。
|
|
138
137
|
|
|
138
|
+
## 远程配置 (`/persist-remote-config` 与两条提示词命令)
|
|
139
|
+
|
|
140
|
+
### `/persist-remote-config`
|
|
141
|
+
|
|
142
|
+
把 IDE 里配置的 server 与 deployment 写进 `<项目>/.opencode/remote-shell/hosts.json`:
|
|
143
|
+
|
|
144
|
+
- 读配套插件的 `get_deployment_servers`(IDE 必须连着;没连上会提示先 `/open-in-ide`)
|
|
145
|
+
- **同名覆盖、项目独有保留**:IDE 有的整体更新,IDE 没有的原样留着
|
|
146
|
+
- 写之前备份 `hosts.json.bak`
|
|
147
|
+
- 写完整凭据(`password` / `privateKeyPath`) —— **这个文件要不要提交进 git,由你决定**,插件不改 `.gitignore`
|
|
148
|
+
- 写完立刻让服务器缓存失效,紧接着的 `remote_exec` 就能用
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"hosts": {
|
|
153
|
+
"mbptest": {
|
|
154
|
+
"host": "yutaodeMacBook-Pro.local",
|
|
155
|
+
"port": 22,
|
|
156
|
+
"username": "yutao",
|
|
157
|
+
"authType": "PASSWORD",
|
|
158
|
+
"password": "…",
|
|
159
|
+
"deployments": [
|
|
160
|
+
{
|
|
161
|
+
"name": "mcp-test-project",
|
|
162
|
+
"localPath": "/Users/yutao/IdeaProjects/mcp-test-project",
|
|
163
|
+
"deployPath": "/Users/yutao/Desktop/data/test"
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `/remote-shell` 与 `/remote-deployment`
|
|
172
|
+
|
|
173
|
+
这两条是**提示词命令**:不执行任何东西,只往会话里注入一段规则(和 `~/.config/opencode/commands/readonly.md` 同一个思路),
|
|
174
|
+
让模型走 `remote_exec` / `remote_upload` / `remote_download`,而不是自己拼 `ssh` / `scp` / `rsync`。
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
/remote-shell mbp 看下磁盘
|
|
178
|
+
/remote-deployment mcp-test-project download
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
参数原样附在提示词末尾;**没带参数时,提示词会要求模型先问清楚**(服务器 / 命令 / 哪条 deployment),不会自己挑一台。
|
|
182
|
+
|
|
139
183
|
## 按能力选工具 (直接调用引导)
|
|
140
184
|
|
|
141
185
|
IDE MCP 可用时,插件通过 **系统提示注入**告诉模型 **哪些事该走 IDE**;除下面「工具参数与输出」那几处刻意的接管
|
|
@@ -168,8 +212,7 @@ IDE MCP 可用时,插件通过 **系统提示注入**告诉模型 **哪些事该
|
|
|
168
212
|
| 批量改文件 | `ide_apply_patch` |
|
|
169
213
|
| 校验 | `ide_lint_files` / `ide_get_file_problems` / `ide_build_project` |
|
|
170
214
|
| 运行配置 | `ide_get_run_configurations` / `ide_execute_run_configuration`(+ 配套插件的启停工具) |
|
|
171
|
-
|
|
|
172
|
-
| 部署 | `ide_list_server_deployments` / `ide_upload_to_deployment` / `ide_download_from_deployment` / `ide_get_deployment_task_status`(要求 Ultimate) |
|
|
215
|
+
| 远程执行 / 同步 | `remote_exec` / `remote_list_servers` / `remote_upload` / `remote_download`(本插件自带,凭据与路径映射来自 IDE 的 Deployment 配置,IDE 没开时退到 hosts.json);本机环境变量由 `get_project_env` + shell hook 注入 |
|
|
173
216
|
|
|
174
217
|
**读、找文本、找文件、改单个文件一律用 OpenCode 自带的工具**(`read` / `grep` / `glob` / `write` / `edit`):
|
|
175
218
|
这几件事走 IDE 只是多一跳,所以映射表里**不出现** `ide_search_text` / `ide_search_regex` / `ide_search_file`。
|
|
@@ -280,15 +323,20 @@ IDE 内置的 MCP Server 只能 **启动已有**的 run configuration (`execute_
|
|
|
280
323
|
| `ide_stop_run_configuration` | 停掉该配置的**全部**实例并等它们退出;没在跑也返回成功(幂等) |
|
|
281
324
|
| `ide_rerun_run_configuration` | 等同 IDE 的 **Rerun** 按钮:在跑就重启、没在跑就启动,读 `started` 判断是否真起来了 |
|
|
282
325
|
|
|
283
|
-
|
|
284
|
-
(
|
|
285
|
-
|
|
286
|
-
|
|
326
|
+
**需要项目环境的命令也走原生 shell** —— 项目配置的 JDK / Node / Go / Maven / Python 由配套插件的 `get_project_env`
|
|
327
|
+
取出来,再由本插件的 `shell.hook("create.before")` **注入到每个新 shell 的 `PATH` 与环境变量里**,所以 `java` / `node` /
|
|
328
|
+
`go` / `mvn` / `python` 在原生 shell 里就是项目配置的版本。**会反复发生的流程 / 常驻服务**仍然固化成 run configuration;
|
|
329
|
+
看文件、git、临时脚本也走原生 shell。配置必须 **先在 IDEA 里存在**(由用户建),没有就请用户建一个再跑;要不要建、建什么,
|
|
330
|
+
按 `idea-run-config-manager` skill 的判据走。
|
|
287
331
|
|
|
288
332
|
> **为什么内置 IDE 终端仍然隐藏**:`ide_execute_terminal_command` 之前被当成"自动继承 IDE 项目环境"的通道而保留暴露,但实测
|
|
289
333
|
> **它拿不到项目配好的 JDK / Node / Python 版本**,和原生 shell 没有区别。既然没有优势,就把它从模型可见的工具表里
|
|
290
|
-
> 隐藏掉 (`HIDDEN_IDE_TOOL_SUFFIXES`)
|
|
291
|
-
>
|
|
334
|
+
> 隐藏掉 (`HIDDEN_IDE_TOOL_SUFFIXES`)。
|
|
335
|
+
>
|
|
336
|
+
> **项目环境现在由本插件自己注入**:连接 IDE 时调一次配套插件的 `get_project_env`(不进模型可见的工具表),把返回的
|
|
337
|
+
> `PATH` **前置**拼到每条新 shell 的 PATH 上,其余变量(`GOROOT` / `MAVEN_HOME` / `VIRTUAL_ENV` …)直接覆盖。所以原生
|
|
338
|
+
> shell 就是"项目 shell",不需要一个单独的 `project_sdk_terminal` 工具;配套插件里那套本地终端能力已经整体移除。
|
|
339
|
+
> 拉不到环境变量时连接按**没做完**处理(`env-unready`),不会静默降级成"工具有、环境是空的"。
|
|
292
340
|
|
|
293
341
|
配置名有自己的文法:**`[os@]base[@env]`** —— os 段 (`win` = Windows,缺省 = macOS / Linux)、base 段 (配置本体)、env 段 (环境名,
|
|
294
342
|
缺省 = 默认环境 —— 起一批时逐条解析:有 `@<env>` 用它,没有就用默认)。`win@base` 与 `base@env` 都是同一个 base 的 **变体**,档案里只登记 base。解析 **交给工具**:
|
|
@@ -405,72 +453,76 @@ opencode-idea 对这一组工具做两件事:
|
|
|
405
453
|
事实一律先查后写:配置名来自 `ide_get_run_configurations`,`热重载` 来自运行方式,端口读不到就问用户 —— 不猜。
|
|
406
454
|
**只动自己的标记块**:根 `AGENTS.md` 是共享文件 (`project-scaffold` 的宪法、项目自己的内容都在里面),标记之外一字不改。
|
|
407
455
|
|
|
408
|
-
##
|
|
456
|
+
## 文件同步 (自带,不再依赖 IDE 的 deployment 工具)
|
|
409
457
|
|
|
410
|
-
|
|
411
|
-
|
|
458
|
+
上传 / 下载由本插件的 `remote_upload` / `remote_download` 直接走 SFTP,不再经过 IDE 的
|
|
459
|
+
`upload_to_deployment` / `download_from_deployment`:
|
|
412
460
|
|
|
413
|
-
| 工具
|
|
414
|
-
|
|
|
415
|
-
| `
|
|
416
|
-
| `
|
|
417
|
-
| `ide_download_from_deployment` | 把**一条 deployment** 从服务器拉回本地(等同 IDE 的 Download from …),**以服务器为准**:两边都有的文件被远端覆盖(相同则不写)。同样传 `deploymentName`。**没有"保留我的版本"这个选项** |
|
|
418
|
-
| `ide_get_deployment_task_status` | 用 `handle` 查一次传输的状态与计数;**永不启动任何传输** |
|
|
461
|
+
| 工具 | 作用 |
|
|
462
|
+
| --- | --- |
|
|
463
|
+
| `remote_upload` | 把一条 deployment 的本地目录传到远程目录。`mode` 默认 `overwrite`(只覆盖 / 新增),`mirror` 额外删掉远程多余的文件 |
|
|
464
|
+
| `remote_download` | 反向拉回本地,语义相同(默认 `overwrite`,`mirror` 删本地多余) |
|
|
419
465
|
|
|
420
|
-
|
|
466
|
+
路径映射(本地目录 ↔ 远程目录)和服务器一样走三级配置,同名以高优先级为准:
|
|
421
467
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
-
|
|
468
|
+
1. **IDE** —— `get_deployment_servers` 返回的 `deployments[]`(`name` / `localPath` / `deployPath`;
|
|
469
|
+
`deployPath` 已拼上 server 的 `rootFolder`,是能直接用的绝对路径)
|
|
470
|
+
2. **项目级** `<项目>/.opencode/remote-shell/hosts.json` 里 host 条目下的 `deployments`
|
|
471
|
+
3. **全局** `~/.config/remote-shell/hosts.json` 同上
|
|
425
472
|
|
|
426
|
-
|
|
427
|
-
IDE 的连接、路径映射、排除规则都在它后面,自己拼的那份与 IDE 看到、管理的不是同一份。文案里同时点明两件容易读错的事:
|
|
428
|
-
`mirror` 是 **默认值**且 **会删远端文件**(而 MCP 路径上没有确认步骤),下载 **以远端为准**、没有"保留我的版本"。
|
|
473
|
+
`remote_list_servers` 会把每条 mapping 一起列出来(`本地目录 ↔ 远程目录`)。
|
|
429
474
|
|
|
430
|
-
|
|
431
|
-
走 `execute_in_remote_host`(`serverName` 用服务器名,不是 deployment 名)。
|
|
475
|
+
**`mirror` 会删文件**:上传时删远程多余,下载时删本地多余,而且没有确认步骤。默认 `overwrite` 只覆盖 / 新增。
|
|
432
476
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
`counters` 才可信,而且数的是**操作次数**,不是文件数(删掉整个目录也只算 1)。
|
|
477
|
+
**同步只是上半段**:每条 deployment 都属于某台服务器,传完之后在它上面启动刚传上去的二进制、重启服务、看日志,
|
|
478
|
+
走 `remote_exec`。这两步都不要用 `scp` / `rsync` / `ssh` 手工顶替 —— 手工那条路既没有 IDE 的路径映射,
|
|
479
|
+
也常常根本没有权限。
|
|
437
480
|
|
|
438
|
-
>
|
|
439
|
-
>
|
|
440
|
-
>
|
|
481
|
+
> 配套插件里那四个 deployment 工具(`list_server_deployments` / `upload_to_deployment` /
|
|
482
|
+
> `download_from_deployment` / `get_deployment_task_status`)**已经删除** —— 传输完全由这里的
|
|
483
|
+
> `remote_upload` / `remote_download` 负责。
|
|
441
484
|
|
|
442
|
-
##
|
|
485
|
+
## 远程执行 (自带,不再依赖 IDE 的远程终端)
|
|
443
486
|
|
|
444
|
-
|
|
445
|
-
补上的是一个**真正的 IDE 终端**,外加远程 SSH 终端和一套"接着用同一个 tab"的读写工具:
|
|
487
|
+
远程命令由本插件的 `remote_exec` 直接跑(SSH),不再经过 IDE 的 `execute_in_remote_host`:
|
|
446
488
|
|
|
447
|
-
| 工具
|
|
489
|
+
| 工具 | 作用 |
|
|
448
490
|
| --- | --- |
|
|
449
|
-
| `
|
|
450
|
-
| `
|
|
451
|
-
|
|
452
|
-
|
|
491
|
+
| `remote_exec` | 在远程服务器上跑一条命令。参数 `server` / `command` / `workingDirectory?`,返回 `[服务器名] exitCode N` 加输出 |
|
|
492
|
+
| `remote_list_servers` | 列出全部可用服务器,带 `[IDE]` / `[项目]` / `[全局]` 来源标记。名字不确定时先调它 |
|
|
493
|
+
|
|
494
|
+
认证方式三种都支持:`PASSWORD`(密码)、`KEY_PAIR`(私钥 + 可选口令)、`OPEN_SSH`(ssh-agent /
|
|
495
|
+
`~/.ssh/config`,靠 `SSH_AUTH_SOCK`)。
|
|
496
|
+
|
|
497
|
+
服务器与凭据有三级来源,同名以高优先级为准:
|
|
498
|
+
|
|
499
|
+
1. **IDE** —— 配套插件的 `get_deployment_servers`(只返回 SFTP server;凭据由 IDE 进程从 Credentials 直接读出,
|
|
500
|
+
不经过任何配置文件,也不碰 Keychain)
|
|
501
|
+
2. **项目级** `<项目>/.opencode/remote-shell/hosts.json`(与全局同名同格式,跟着仓库走)
|
|
502
|
+
3. **全局** `~/.config/remote-shell/hosts.json`
|
|
503
|
+
|
|
504
|
+
IDE 没开时自动退到 2 / 3,所以 `remote_exec` 在 IDE 之外也能用。列表缓存 60 秒;**名字找不到时会全量刷新一次**
|
|
505
|
+
再匹配,仍找不到就把全部服务器列给模型 —— 不做模糊猜测(用户说 "dev1"、配置的可能是 "deploy1",猜错比列出来更糟)。
|
|
506
|
+
命中但连接失败时,会刷新一次凭据再重试(应对"刚在 IDE 里改了密码"),仍失败才报错。
|
|
453
507
|
|
|
454
|
-
|
|
455
|
-
`
|
|
456
|
-
|
|
508
|
+
**每条命令一条独立 SSH 连接**:`cd` / `export` 不跨命令保留,要目录就传 `workingDirectory`(内部拼
|
|
509
|
+
`cd '<dir>' && <command>`);命令同步返回,没有 tab、没有增量读、没有 Ctrl-C 通道,长任务自己在远程用 `nohup` /
|
|
510
|
+
`tmux`。
|
|
457
511
|
|
|
458
|
-
|
|
512
|
+
上传 / 下载见上一节:同样是本插件的 `remote_upload` / `remote_download`,映射来自同一套三级配置。
|
|
459
513
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
用完不必主动关,用户可以直接在 IDE 里接手。
|
|
514
|
+
**项目环境怎么进原生 shell**:连接 IDE 时(以及 `/open-in-ide` 每一次完整连接)调 `get_project_env`,拿到
|
|
515
|
+
`{ PATH, variables }` 后缓存;每条 shell 创建前由 `shell.hook("create.before")` 把 `PATH` **前置**拼上、其余变量直接覆盖。
|
|
516
|
+
`PATH` 里只有五个 provider(Java / Node / Go / Maven / Python)的 bin,IDE 进程自己的 PATH 不掺进来 —— 所以 OpenCode 的
|
|
517
|
+
PATH 不会丢。**env 拉不到 = 连接没做完**:`/open-in-ide` 报 `env-unready`,不会静默降级成"工具有、环境是空的"。
|
|
465
518
|
|
|
466
|
-
|
|
467
|
-
`execute_in_remote_host`
|
|
468
|
-
契约以该仓库的 `TerminalSchemas.kt` 为准。
|
|
519
|
+
`get_project_env` 与 `get_deployment_servers` 都是**隐藏**的(数据通道,不是模型任务),IDE 侧那套远程终端
|
|
520
|
+
(`execute_in_remote_host` / `terminal_read` / `terminal_interrupt` / `list_terminals`)也一并隐藏 —— 本插件不再需要它们。
|
|
469
521
|
|
|
470
522
|
## 前置条件
|
|
471
523
|
|
|
472
524
|
- **必须安装配套插件 [`idea-mcp-enhance`](https://github.com/syllr/idea-mcp-enhance)(IDEA 2026.2+)**。引导默认它的工具都在
|
|
473
|
-
(运行配置启停 / 部署 /
|
|
525
|
+
(运行配置启停 / 部署 / 环境变量),**不做"装没装 / 有没有这个工具"的判断**;部署那组还要求 Ultimate。
|
|
474
526
|
- JetBrains IDE 已开启 MCP 服务 (IDE 设置里的 MCP Server / AI Assistant MCP),版本需 **2026.2+**(提供 Streamable HTTP 端点
|
|
475
527
|
`/stream`);若关闭,`/open-in-ide` 会立即提示如何重新开启。
|
|
476
528
|
- IDE 侧需开启 **Brave Mode**(否则需要确认的 MCP 工具调用会停下来等用户确认);MCP 未就绪时通知会同时提醒开启它。
|
|
@@ -602,15 +654,16 @@ export { default } from "/Users/yutao/IdeaProjects/opencode-idea/src/index.js";
|
|
|
602
654
|
- skill 定义的字段名 **以 schema 为准**,不要照官方文档:`Skill.Info` 需要 `path`(AbsolutePath),文档里写的 `location`
|
|
603
655
|
不存在 —— 用错会让 `ctx.skill.transform` 抛 `SchemaError`,而**任何 transform 失败都会禁用整个插件**
|
|
604
656
|
(0.0.6 就是这样丢了 `/open-in-ide`)。完整字段是 `id` / `name` / `description?` / `autoinvoke?` / `path` / `content`。
|
|
605
|
-
- 一批用不到的 IDE MCP 工具被硬编码隐藏(`src/plugin.js` 的 `HIDDEN_IDE_TOOL_SUFFIXES`,**共
|
|
657
|
+
- 一批用不到的 IDE MCP 工具被硬编码隐藏(`src/plugin.js` 的 `HIDDEN_IDE_TOOL_SUFFIXES`,**共 58 个后缀**):
|
|
606
658
|
VCS(`git_status` / `get_repositories`)、Router 派发工具(`execute_tool`)、Debugger(全部 `xdebug_*`)、Dev Kit MCP、
|
|
607
|
-
Inspection KTS MCP、解释器环境 MCP、**IDE 内置终端**(`execute_terminal_command
|
|
608
|
-
|
|
659
|
+
Inspection KTS MCP、解释器环境 MCP、**IDE 内置终端**(`execute_terminal_command`:实测不继承项目环境)、
|
|
660
|
+
**`get_project_env`**(本插件 shell hook 的数据通道,不是模型任务)、
|
|
609
661
|
**整个 Database 组**(数据源由用户在 IDE 里配置)、**有原生对应的文件搜索**(`search_text` / `search_regex` / `search_file`)、
|
|
610
662
|
以及**建文件**(`create_new_file`)。配套插件自带的那个写文件工具同样隐藏。**`apply_patch` 不在隐藏列表里**。
|
|
611
663
|
存的是**后缀**,清洗时按当前每个项目前缀**各删一遍** —— 只处理 `ide_` 会让子项目那几份原样送到模型面前。
|
|
612
|
-
|
|
613
|
-
|
|
664
|
+
其中有几个后缀(`execute_in_remote_host` / `terminal_read` / `terminal_interrupt` / `list_terminals`)对应的工具
|
|
665
|
+
已经从配套插件里删掉了,留着是为了挡住**没升级**的安装。具体数字随 IDE 与配套插件版本变化,每次工具集调整都要重测;
|
|
666
|
+
判据是 **模型可见的 IDE 工具 = 引导里点名的那批**,不增不减。
|
|
614
667
|
- **IDE 的「目录包含」语义(实测)**:请求路径落在某个**已打开项目的目录树内**时,IDE 会把它解析到那个项目,而不是报错。
|
|
615
668
|
所以「子项目与 root 配同一个 IDE」时,只要那个 IDE 打开了 root,子项目就被判成"已打开" —— 它确实是 root 项目的一部分,
|
|
616
669
|
工具能操作它的文件,但语言支持按 **root 项目**走(例如 Python 子项目被 root 的 Web 模块解析,符号检索与检查会不准)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-idea",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"src/plugin.js",
|
|
11
11
|
"src/template.js",
|
|
12
12
|
"src/ide-guidance.template.md",
|
|
13
|
+
"src/plugin-tools-guidance.template.md",
|
|
13
14
|
"src/ide-projects.js",
|
|
14
15
|
"src/mcp/idea.js",
|
|
15
16
|
"src/mcp/idea-tool-defaults.js",
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
"src/project.js",
|
|
18
19
|
"src/ide-launcher.js",
|
|
19
20
|
"src/ide-guidance.js",
|
|
21
|
+
"src/remote/",
|
|
20
22
|
"src/skill/loader.js",
|
|
21
23
|
"skills",
|
|
22
24
|
"README.md"
|
|
@@ -52,5 +54,8 @@
|
|
|
52
54
|
},
|
|
53
55
|
"engines": {
|
|
54
56
|
"node": ">=18.0.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"ssh2": "^1.17.0"
|
|
55
60
|
}
|
|
56
61
|
}
|
package/src/ide-guidance.js
CHANGED
|
@@ -72,9 +72,18 @@ import { renderTemplate } from './template.js';
|
|
|
72
72
|
/** The prompt document, next to this module so it ships with it. */
|
|
73
73
|
const TEMPLATE_PATH = new URL('ide-guidance.template.md', import.meta.url);
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* 远程执行的指引。`remote_*` 是**插件自己**的工具(不带 `<前缀>_`、IDE 关掉也在),
|
|
77
|
+
* 所以这份文本与 IDE 连接状态无关,单独渲染、单独注入。
|
|
78
|
+
*/
|
|
79
|
+
const REMOTE_TEMPLATE_PATH = new URL('plugin-tools-guidance.template.md', import.meta.url);
|
|
80
|
+
|
|
75
81
|
/** Read once: the file is fixed for the life of the process, and this runs per request. */
|
|
76
82
|
const TEMPLATE = readFileSync(TEMPLATE_PATH, 'utf8');
|
|
77
83
|
|
|
84
|
+
/** 同上,只为远程工具那份。 */
|
|
85
|
+
const REMOTE_TEMPLATE = readFileSync(REMOTE_TEMPLATE_PATH, 'utf8');
|
|
86
|
+
|
|
78
87
|
/**
|
|
79
88
|
* 「本会话有哪些项目、各自用哪个 MCP 工具前缀」的清单。
|
|
80
89
|
*
|
|
@@ -191,6 +200,29 @@ export function appendIdeGuidance(system, projects) {
|
|
|
191
200
|
system.push({ type: 'text', text });
|
|
192
201
|
}
|
|
193
202
|
|
|
203
|
+
/**
|
|
204
|
+
* 远程执行的指引,**与 IDE 连接状态无关**。
|
|
205
|
+
*
|
|
206
|
+
* `remote_*` 是插件常驻注册的工具:IDE 关掉时它们仍然能跑(服务器列表退到 hosts.json)。
|
|
207
|
+
* 所以这段不能跟着 `appendIdeGuidance` 一起在 `serving === false` 时被跳过 —— 否则模型手上有
|
|
208
|
+
* `remote_exec`,却没有任何说明告诉它该用、怎么传参。
|
|
209
|
+
*
|
|
210
|
+
* @returns {string}
|
|
211
|
+
*/
|
|
212
|
+
export function buildRemoteGuidanceText() {
|
|
213
|
+
return renderTemplate(REMOTE_TEMPLATE, {});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @param {unknown} system
|
|
218
|
+
*/
|
|
219
|
+
export function appendRemoteGuidance(system) {
|
|
220
|
+
if (!Array.isArray(system)) return;
|
|
221
|
+
const text = buildRemoteGuidanceText();
|
|
222
|
+
if (system.some((part) => part?.text === text)) return;
|
|
223
|
+
system.push({ type: 'text', text });
|
|
224
|
+
}
|
|
225
|
+
|
|
194
226
|
/**
|
|
195
227
|
* @param {unknown} system
|
|
196
228
|
* @param {{ marked?: boolean, serving?: boolean }} [options] `marked` picks the
|
|
@@ -7,11 +7,9 @@
|
|
|
7
7
|
2. **IDE 工具补自带工具做不到的**:符号与定义用 `<前缀>_search_symbol` + `<前缀>_read_file`;改名用
|
|
8
8
|
`<前缀>_rename_refactoring`;单文件检查用 `<前缀>_lint_files` / `<前缀>_get_file_problems`;跨模块构建用
|
|
9
9
|
`<前缀>_build_project`。
|
|
10
|
-
3.
|
|
11
|
-
仍走 `shell`。细则见「命令执行」与「终端」。
|
|
12
|
-
4. **校验节奏**:改一处只在收尾跑 `lint_files` (要细节再 `get_file_problems`);改一大片或动了接口 / 依赖就用
|
|
10
|
+
3. **校验节奏**:改一处只在收尾跑 `lint_files` (要细节再 `get_file_problems`);改一大片或动了接口 / 依赖就用
|
|
13
11
|
`build_project`;全仓检查只在收尾。
|
|
14
|
-
|
|
12
|
+
4. **批量修改**:逐文件用 `edit`,或一次用 `<前缀>_apply_patch`。
|
|
15
13
|
|
|
16
14
|
本会话接入了 {{projectCount}} 个 JetBrains 项目。**每个 IDE 功能在所有项目下都各有一份工具**,命令名是
|
|
17
15
|
`<项目前缀>_<工具名>`;下文写 `<前缀>` 就是指这一截。
|
|
@@ -35,9 +33,9 @@
|
|
|
35
33
|
项目外的路径(`/tmp`、别的仓库、用户主目录)不在 IDE 的解析范围内。
|
|
36
34
|
|
|
37
35
|
- **项目内** —— **OpenCode 自带的工具为主**,它们更快。**它们做不了、或者做得很差的,才用 IDE 工具**:
|
|
38
|
-
|
|
36
|
+
符号检索、依赖实现、重构、格式化、校验、运行配置、跨文件补丁。
|
|
39
37
|
**两边都不顺手时,你自己判断用 `shell` 里的命令** —— 那是第三选择,不是第一选择。
|
|
40
|
-
-
|
|
38
|
+
- **项目外、但还在本机** —— 用 OpenCode 自带的工具;IDE 工具只对项目内路径生效。
|
|
41
39
|
|
|
42
40
|
## 总则 (所有项目通用)
|
|
43
41
|
|
|
@@ -52,20 +50,14 @@
|
|
|
52
50
|
- **改文件**:改一个符号用 `<前缀>_rename_refactoring`;改一段文字用 `edit` / `write` 工具;
|
|
53
51
|
同一段文字铺到多个文件用 `<前缀>_apply_patch`。跑命令的路由见「命令执行」。
|
|
54
52
|
改代码始终落在具体的那几行上,其余内容保持原样。
|
|
55
|
-
- **改完代码自己先查一遍**:`lint_files` / `build_project
|
|
53
|
+
- **改完代码自己先查一遍**:`lint_files` / `build_project`,怎么挑见「改代码」的校验规则。
|
|
56
54
|
- **文档类文件 (Markdown / 文档) 改完之后,用 `<前缀>_reformat_file` 过一遍** —— 表格对齐、换行宽度、缩进交给 IDE
|
|
57
55
|
统一。它从磁盘重读,外部写完直接格式化是安全的。
|
|
58
56
|
- 某条功能下面没有你正要操作的那个项目的命令,说明那个项目没接上:提示用户执行 `/open-in-ide`。
|
|
59
57
|
|
|
60
|
-
##
|
|
58
|
+
## 本地命令
|
|
61
59
|
|
|
62
|
-
-
|
|
63
|
-
- **要**(IDE 里配好的 JDK / Node / Python / Maven / Go,以及依赖它们的构建、脚本、生成、迁移)→
|
|
64
|
-
{{cmd_project_sdk_terminal}}。它开的是 IDE 里真正的终端,自动带上项目 SDK 与 Maven / Node / Python 的路径;
|
|
65
|
-
`workingDirectory` 默认项目根,`env` 里的值最后覆盖。
|
|
66
|
-
- **不要**(`git` / `ls` / `cat` / `curl` / `docker` / `ps`)→ `shell` 工具。
|
|
67
|
-
- **常驻进程**(dev server / watch / 长时间跑的服务)有 run configuration 就用 `rerun` 起它;
|
|
68
|
-
没有就用项目终端传 `timeoutMs: 0` 起在 tab 里,之后用 {{cmd_terminal_io}} 跟日志、回答提示。
|
|
60
|
+
- **常驻进程**(dev server / watch / 长时间跑的服务)有 run configuration 就用 `rerun` 起它。
|
|
69
61
|
- **一次性的命令就地跑完,反复发生的流程才固化成 run configuration** —— 配置是**项目资产**,不是"跑命令"
|
|
70
62
|
的手段。判据只有一条: **这件事以后会不会以同样的形态再发生?** 会 (构建 / 生成 / 迁移 / 起服务 / 部署这类)
|
|
71
63
|
就 **主动提**一次要不要固化成配置,并把配置名 / 类型 / 命令 / 工作目录 / 为什么值得留摊开;
|
|
@@ -168,6 +160,8 @@
|
|
|
168
160
|
- {{cmd_rerun_run_configuration}} **启动 / 重启常驻进程**:在跑就重启、没在跑就启动 —— 改完代码要重启就直接用它,
|
|
169
161
|
有没有热重载都一样;读 `started` 判断新进程是否真的起来了。这次运行的输出在 `output`,完整日志文件路径在
|
|
170
162
|
`fullOutputPath`。启停都走这个工具,IDE 的 Run 窗口状态才会跟着更新。
|
|
163
|
+
**before-launch 很久**(要先构建 / 跑另一个配置)时,调用会一直等到它结束;等不起就传 `waitForStart: false`,
|
|
164
|
+
提交后自己查 {{cmd_list_running_configurations}}。
|
|
171
165
|
|
|
172
166
|
**配置名怎么读**:`[os@]base[@env]`,`@` 分割、最多三段 —— `os` = `win`(缺省 macOS / Linux)、`base` = 配置本体(必填)、
|
|
173
167
|
`env` = 环境名(缺省 = 默认环境)。`win@base`(换平台)与 `base@env`(换环境)都是 `base` 的 **变体**,跑哪一个:
|
|
@@ -177,52 +171,3 @@
|
|
|
177
171
|
变体、没有默认版的,就用它自己那个 —— "10 个服务"可以是"3 个 `@dev` + 7 个默认",不必逐条问。
|
|
178
172
|
- 一条配置同时有 `base` 与 `base@<env>`、又没说走哪个环境 → **问一次走哪个环境**,选定后本会话默认走这套。
|
|
179
173
|
- 一个环境怎么接上(启动参数 / 环境变量 / 配置文件)是项目自己的事:照现成的同类变体来,或问用户。
|
|
180
|
-
|
|
181
|
-
### 终端 (项目环境 · 交互 · 远程)
|
|
182
|
-
|
|
183
|
-
- {{cmd_project_sdk_terminal}} **在本项目 SDK 终端里跑一条命令,返回输出与 tab id**。`command` 必填;`timeoutMs` 默认
|
|
184
|
-
10000(`0` = 发出去就返回,最长 12000);`workingDirectory` 默认项目根;`env` 最后覆盖自动注入的环境;`input` 在命令后
|
|
185
|
-
写一次(自带换行);`maxChars` 只保留最新输出。 **每次调用都新开一个 tab** —— 同一件事的后续改用 `list_terminals` /
|
|
186
|
-
`terminal_io`,别再调它。
|
|
187
|
-
- {{cmd_list_terminals}} **本项目的终端 tab 清单**:`kind`(`mcp_project_sdk` / `mcp_remote` / `user`)、`status`、
|
|
188
|
-
`workingDirectory` / `currentDirectory`、`lastCommand`、`lastUsedAt`。 **只有 `tab` 非空且 `addressable: true` 的能交给
|
|
189
|
-
`terminal_io`**;用户自己开的 tab 只用于认位置,不可控制;`title` 不是 id。
|
|
190
|
-
- {{cmd_terminal_io}} **对已有 tab 读 / 写**:`target` 取 `local`(`project_sdk_terminal` 建的)或 `remote`。
|
|
191
|
-
- `action: "read"` —— 默认读**上次之后的新输出**;`tail: N` 读最近 N 个命令段(本地还带每段的 `exitCode`);`full: true`
|
|
192
|
-
读整屏;`timeoutMs > 0` 先等输出稳定再读(最长 12000)。
|
|
193
|
-
- `action: "write"` —— `text` **原样**送进当前命令(自带换行),不是新开一条命令。
|
|
194
|
-
- {{cmd_execute_in_remote_host}} **在远程主机的真实 SSH 终端里跑命令** —— 主机与凭据来自本项目的一台 Deployment
|
|
195
|
-
SFTP 服务器,名字传 `serverName`(那台**服务器**的名字,不是 deployment 名)。`loginShell` 默认 `true`(套
|
|
196
|
-
`zsh -lic` / `bash -lc`,PATH 与工具链才会加载);`strictHostKey` 默认 `false`(接受并记录新主机密钥)。
|
|
197
|
-
**远程命令一律走它,不要自己拼 `ssh` / `scp`** —— 凭据、代理、路径映射都在 IDE 这边。
|
|
198
|
-
远程没有"命令结束"信号:**`exitCode` 永远为 `null`,`running` 也可能为 `null`** —— 别等 `exitCode`,看输出,或接着用
|
|
199
|
-
`terminal_io` 读。
|
|
200
|
-
|
|
201
|
-
**tab 只在这次 IDE 会话里有效**:tab 一关(报 `terminal_closed` / 找不到 tab)才重新开一个;用完不必主动关 —— 用户能看见,
|
|
202
|
-
也能直接在 IDE 里接手。
|
|
203
|
-
|
|
204
|
-
### 服务器文件同步
|
|
205
|
-
|
|
206
|
-
往服务器传文件 / 从服务器取文件,走这一组 —— IDE 的连接、路径映射、排除规则都在它们后面,传的就是 IDE 在管的那一份。
|
|
207
|
-
|
|
208
|
-
- {{cmd_list_server_deployments}} **本项目能用的服务器,以及每台服务器上的 deployment**。先分清两层:
|
|
209
|
-
**Server 是连接目标,deployment 是挂在它上面的一条路径映射**;一台 Server 可以有 **0 个 deployment** —— 那样它仍能给
|
|
210
|
-
`execute_in_remote_host` 用,但**上传 / 下载没有目标**。
|
|
211
|
-
- `servers[].name` → **远程终端**的 `serverName`;`servers[].deployments[].name` → **上传 / 下载**的 `deploymentName`
|
|
212
|
-
(由本地路径最后一段派生,**全局唯一**)。
|
|
213
|
-
- 每台 Server 还带 `level`(项目级 / 应用级)、`accessType`、`host` / `port`、`rootFolder`、`hasCredentials`、
|
|
214
|
-
`validationIssue`、`isDefault`;返回里另有**整个项目共用**的 `options` 与 `warnings[]`(**只读 IDE 配置、不联网**)。
|
|
215
|
-
- **`hasCredentials: false` 时不要连接** —— IDE 会弹模态登录框,MCP 调用会卡死;动手前先看 `warnings[]`。
|
|
216
|
-
- {{cmd_upload_to_deployment}} **把一条 deployment(本地映射目录)传到服务器**(等同 IDE 的 Upload to …)。
|
|
217
|
-
`deploymentName` 传 `servers[].deployments[].name` —— **不是 `serverName`**。`mode` 默认 `mirror` ——
|
|
218
|
-
**会删掉服务器上多出来的文件**,使两边一致;要留住它们就传 `upload`。 **破坏性操作**。
|
|
219
|
-
- {{cmd_download_from_deployment}} **把一条 deployment 从服务器拉回本地**(等同 IDE 的 Download from …), **以服务器为准**:
|
|
220
|
-
两边都有的文件会被远端覆盖 (内容相同则不写)。同样传 `deploymentName`。 **没有"保留我的版本"这个选项**。
|
|
221
|
-
- {{cmd_get_deployment_task_status}} **查一次传输的状态**:`handle` 用上传 / 下载返回的那个 —— **它自己不启动任何传输**。
|
|
222
|
-
|
|
223
|
-
上传 / 下载都在 IDE 后台跑,单次调用只等到 `waitMs`(默认 10 秒,上限 12 秒)为止。返回 `state: "in_progress"` 是 **正常**的,
|
|
224
|
-
不是失败 (它可能以 error 结果回来) —— 拿 `handle` 调 `get_deployment_task_status` 接着看; **不要用重复调上传 / 下载来代替**
|
|
225
|
-
(那样会再起一次传输)。到 `completed` 之后 `counters` 才可信,而且它数的是**操作次数**,不是文件数(删掉整个目录也只算 1)。
|
|
226
|
-
|
|
227
|
-
**同步只是上半段**:传完之后在那台 Server 上继续操作(启动刚传上去的二进制、重启服务、看日志)—— 用
|
|
228
|
-
`execute_in_remote_host`,`serverName` 取 `servers[].name`。
|
package/src/mcp/idea.js
CHANGED
|
@@ -127,7 +127,7 @@ export async function callTool(port, projectPath, name, args = {}, options = {})
|
|
|
127
127
|
params: {
|
|
128
128
|
protocolVersion: PROTOCOL_VERSION,
|
|
129
129
|
capabilities: {},
|
|
130
|
-
clientInfo: { name: 'opencode-idea', version: '0.1.
|
|
130
|
+
clientInfo: { name: 'opencode-idea', version: '0.1.5' },
|
|
131
131
|
},
|
|
132
132
|
});
|
|
133
133
|
// The IDE's Streamable-HTTP endpoint binds follow-up requests to the session
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 远程机器
|
|
2
|
+
|
|
3
|
+
## 远程命令
|
|
4
|
+
|
|
5
|
+
- **部署机 / 开发机 / 用户点名的机器上的命令走 `remote_exec`**,不要自己拼 `ssh` / `scp`。名字不确定就先
|
|
6
|
+
`remote_list_servers`。
|
|
7
|
+
- **每条命令是独立连接**:`cd` / `export` 不跨命令保留,要目录就传 `workingDirectory`。
|
|
8
|
+
- **命令跑完才返回**
|
|
9
|
+
|
|
10
|
+
## 工具
|
|
11
|
+
|
|
12
|
+
- `remote_list_servers` 列出全部可用服务器(带 `[IDE]` / `[项目]` / `[全局]` 来源)与各自的 mapping。
|
|
13
|
+
- `remote_exec` 在服务器上跑一条命令:`server`、`command`、可选的 `workingDirectory`。
|
|
14
|
+
- `remote_upload` / `remote_download` 按 deployment 名同步目录。默认 `overwrite`;`mirror` 会删掉目标端多出来的
|
|
15
|
+
文件,只在明确要求镜像时用。
|
|
16
|
+
- **名字传错会返回全部候选**,按列表里的名字重新调用即可。
|
|
17
|
+
|
|
18
|
+
用户执行 `/persist-remote-config` 后,IDE 的服务器与 mapping 会写进项目里的 hosts.json,IDE 关掉也能用。
|