opencode-diff-viewer 1.0.6 → 1.0.8
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 +54 -54
- package/dist/index.js +92 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,39 +3,38 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/opencode-diff-viewer)
|
|
4
4
|
[](https://www.npmjs.com/package/opencode-diff-viewer)
|
|
5
5
|
|
|
6
|
-
一个 OpenCode 插件,使用 [
|
|
6
|
+
一个 OpenCode 插件,使用 [lumen](https://github.com/jnsahaj/lumen) + [tmux](https://github.com/tmux/tmux) 提供美观的 TUI diff 查看功能。
|
|
7
7
|
|
|
8
8
|
## 功能特性
|
|
9
9
|
|
|
10
|
-
- ✨ **自动安装
|
|
10
|
+
- ✨ **自动安装 tmux 和 lumen** - 插件会自动检测并安装依赖
|
|
11
11
|
- 🚀 **一键查看 diff** - 使用 `/diff` 命令快速查看代码变更
|
|
12
|
-
-
|
|
12
|
+
- 🔧 **tmux 集成** - 在后台 tmux 会话中运行 lumen
|
|
13
13
|
- 🤖 **LLM 工具集成** - LLM 可自动调用 `view_diff` 工具
|
|
14
14
|
|
|
15
15
|
## 前置条件
|
|
16
16
|
|
|
17
|
-
###
|
|
17
|
+
### 自动安装
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
插件会在启动时自动检查并安装以下依赖:
|
|
20
20
|
|
|
21
|
-
**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
1. **tmux** - 终端复用器
|
|
22
|
+
2. **lumen** - TUI diff 查看器
|
|
23
|
+
|
|
24
|
+
### 手动安装(如果自动安装失败)
|
|
25
25
|
|
|
26
|
-
**
|
|
26
|
+
**tmux**:
|
|
27
27
|
```bash
|
|
28
|
-
|
|
28
|
+
brew install tmux
|
|
29
|
+
# or
|
|
30
|
+
apt install tmux
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
**
|
|
32
|
-
下载 [delta releases](https://github.com/dandavison/delta/releases) 并添加到 PATH
|
|
33
|
-
|
|
34
|
-
### 2. Git 仓库
|
|
35
|
-
|
|
36
|
-
确保项目是 git 仓库,并且有修改的文件:
|
|
33
|
+
**lumen**:
|
|
37
34
|
```bash
|
|
38
|
-
|
|
35
|
+
brew install jnsahaj/lumen/lumen
|
|
36
|
+
# or
|
|
37
|
+
cargo install lumen
|
|
39
38
|
```
|
|
40
39
|
|
|
41
40
|
## 安装(全局配置)
|
|
@@ -63,8 +62,8 @@ cat > ~/.config/opencode/opencode.json << 'EOF'
|
|
|
63
62
|
{
|
|
64
63
|
"command": {
|
|
65
64
|
"diff": {
|
|
66
|
-
"template": "View git diff
|
|
67
|
-
"description": "
|
|
65
|
+
"template": "View git diff using lumen in tmux.",
|
|
66
|
+
"description": "View diff of modified files using lumen TUI"
|
|
68
67
|
}
|
|
69
68
|
},
|
|
70
69
|
"plugin": ["opencode-diff-viewer"]
|
|
@@ -91,27 +90,46 @@ EOF
|
|
|
91
90
|
|
|
92
91
|
LLM 可以自动调用 `view_diff` 工具来展示代码变更。无需手动操作,LLM 会根据对话上下文智能判断何时需要展示 diff。
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
### 查看 lumen
|
|
94
|
+
|
|
95
|
+
执行 `/diff` 后,lumen 会在 tmux 会话中运行。要查看 lumen:
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
```bash
|
|
98
|
+
tmux attach -t opencode-diff-viewer
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**tmux 快捷键**:
|
|
102
|
+
- `Ctrl+B` 然后 `D` - 分离会话(回到 OpenCode)
|
|
103
|
+
- `Ctrl+B` 然后 `?` - 查看所有快捷键
|
|
97
104
|
|
|
105
|
+
**lumen 快捷键**:
|
|
98
106
|
| 快捷键 | 功能 |
|
|
99
107
|
|--------|------|
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
108
|
+
| `j` / `k` 或 `↑` / `↓` | 上/下移动 |
|
|
109
|
+
| `{` / `}` | 跳转到上/下一个变更块 |
|
|
110
|
+
| `Tab` | 切换侧边栏 |
|
|
111
|
+
| `e` | 在编辑器中打开文件 |
|
|
112
|
+
| `q` | 退出 |
|
|
103
113
|
|
|
104
114
|
## 故障排除
|
|
105
115
|
|
|
106
|
-
### 1.
|
|
116
|
+
### 1. tmux 未安装
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
❌ tmux is not installed
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**解决方案**: 手动安装 tmux(见上方手动安装)
|
|
123
|
+
|
|
124
|
+
### 2. lumen 未安装
|
|
107
125
|
|
|
108
126
|
```
|
|
109
|
-
❌
|
|
127
|
+
❌ lumen is not installed
|
|
110
128
|
```
|
|
111
129
|
|
|
112
|
-
**解决方案**: 手动安装
|
|
130
|
+
**解决方案**: 手动安装 lumen(见上方手动安装)
|
|
113
131
|
|
|
114
|
-
###
|
|
132
|
+
### 3. 没有修改的文件
|
|
115
133
|
|
|
116
134
|
```
|
|
117
135
|
📝 No modified files
|
|
@@ -122,31 +140,19 @@ LLM 可以自动调用 `view_diff` 工具来展示代码变更。无需手动操
|
|
|
122
140
|
git add .
|
|
123
141
|
```
|
|
124
142
|
|
|
125
|
-
###
|
|
143
|
+
### 4. 插件未加载
|
|
126
144
|
|
|
127
145
|
检查全局配置文件是否正确:
|
|
128
146
|
```bash
|
|
129
147
|
cat ~/.config/opencode/opencode.json
|
|
130
148
|
```
|
|
131
149
|
|
|
132
|
-
确保配置正确:
|
|
133
|
-
```json
|
|
134
|
-
{
|
|
135
|
-
"command": {
|
|
136
|
-
"diff": {
|
|
137
|
-
"template": "View git diff with syntax highlighting.",
|
|
138
|
-
"description": "Show git diff with syntax highlighting"
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
"plugin": ["opencode-diff-viewer"]
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
150
|
## 工作原理
|
|
146
151
|
|
|
147
|
-
1.
|
|
148
|
-
2.
|
|
149
|
-
3.
|
|
152
|
+
1. **检测依赖** - 插件启动时检查 tmux 和 lumen
|
|
153
|
+
2. **自动安装** - 如果未安装,自动通过 brew 或 cargo 安装
|
|
154
|
+
3. **创建 tmux 会话** - 执行 `/diff` 时创建后台 tmux 会话
|
|
155
|
+
4. **运行 lumen** - 在 tmux 会话中运行 lumen diff
|
|
150
156
|
|
|
151
157
|
## 项目结构
|
|
152
158
|
|
|
@@ -171,18 +177,11 @@ cd opencode-diff-viewer
|
|
|
171
177
|
|
|
172
178
|
# 安装依赖
|
|
173
179
|
npm install
|
|
174
|
-
# 或
|
|
175
|
-
pnpm install
|
|
176
|
-
# 或
|
|
177
|
-
bun install
|
|
178
180
|
|
|
179
181
|
# 构建
|
|
180
182
|
npm run build
|
|
181
183
|
|
|
182
184
|
# 链接本地包
|
|
183
|
-
npm link
|
|
184
|
-
|
|
185
|
-
# 在全局使用
|
|
186
185
|
npm link -g opencode-diff-viewer
|
|
187
186
|
```
|
|
188
187
|
|
|
@@ -200,7 +199,8 @@ npm publish
|
|
|
200
199
|
|
|
201
200
|
## 依赖
|
|
202
201
|
|
|
203
|
-
- [
|
|
202
|
+
- [tmux](https://github.com/tmux/tmux) - 终端复用器
|
|
203
|
+
- [lumen](https://github.com/jnsahaj/lumen) - TUI Diff 查看器
|
|
204
204
|
- [@opencode-ai/plugin](https://www.npmjs.com/package/@opencode-ai/plugin) - OpenCode 插件 SDK
|
|
205
205
|
|
|
206
206
|
## License
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
export const DiffViewerPlugin = async ({ project, client, $, directory, worktree }) => {
|
|
3
|
-
const
|
|
3
|
+
const isLumenInstalled = async () => {
|
|
4
4
|
try {
|
|
5
|
-
await $ `which
|
|
5
|
+
await $ `which lumen`;
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const isTmuxInstalled = async () => {
|
|
13
|
+
try {
|
|
14
|
+
await $ `which tmux`;
|
|
6
15
|
return true;
|
|
7
16
|
}
|
|
8
17
|
catch {
|
|
@@ -27,10 +36,10 @@ export const DiffViewerPlugin = async ({ project, client, $, directory, worktree
|
|
|
27
36
|
return false;
|
|
28
37
|
}
|
|
29
38
|
};
|
|
30
|
-
const
|
|
39
|
+
const installLumen = async () => {
|
|
31
40
|
if (await isBrewInstalled()) {
|
|
32
41
|
try {
|
|
33
|
-
await $ `brew install
|
|
42
|
+
await $ `brew install jnsahaj/lumen/lumen`;
|
|
34
43
|
return { success: true, method: "brew" };
|
|
35
44
|
}
|
|
36
45
|
catch (e) {
|
|
@@ -39,7 +48,7 @@ export const DiffViewerPlugin = async ({ project, client, $, directory, worktree
|
|
|
39
48
|
}
|
|
40
49
|
if (await isCargoInstalled()) {
|
|
41
50
|
try {
|
|
42
|
-
await $ `cargo install
|
|
51
|
+
await $ `cargo install lumen`;
|
|
43
52
|
return { success: true, method: "cargo" };
|
|
44
53
|
}
|
|
45
54
|
catch (e) {
|
|
@@ -48,20 +57,53 @@ export const DiffViewerPlugin = async ({ project, client, $, directory, worktree
|
|
|
48
57
|
}
|
|
49
58
|
return {
|
|
50
59
|
success: false,
|
|
51
|
-
error: "Neither brew nor cargo available. Please install
|
|
60
|
+
error: "Neither brew nor cargo available. Please install lumen manually:\n brew install jnsahaj/lumen/lumen\n # or\n cargo install lumen"
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const installTmux = async () => {
|
|
64
|
+
if (await isBrewInstalled()) {
|
|
65
|
+
try {
|
|
66
|
+
await $ `brew install tmux`;
|
|
67
|
+
return { success: true, method: "brew" };
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
console.warn(`brew install tmux failed: ${e}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
error: "Please install tmux manually:\n brew install tmux\n # or\n apt install tmux"
|
|
52
76
|
};
|
|
53
77
|
};
|
|
54
|
-
const
|
|
55
|
-
if (await
|
|
78
|
+
const ensureLumenInstalled = async () => {
|
|
79
|
+
if (await isLumenInstalled()) {
|
|
56
80
|
return { installed: true };
|
|
57
81
|
}
|
|
58
|
-
const result = await
|
|
82
|
+
const result = await installLumen();
|
|
59
83
|
if (result.success) {
|
|
60
|
-
return { installed: true, message: `✅
|
|
84
|
+
return { installed: true, message: `✅ lumen installed via ${result.method}` };
|
|
61
85
|
}
|
|
62
86
|
return { installed: false, message: `❌ ${result.error}` };
|
|
63
87
|
};
|
|
64
|
-
|
|
88
|
+
const ensureTmuxInstalled = async () => {
|
|
89
|
+
if (await isTmuxInstalled()) {
|
|
90
|
+
return { installed: true };
|
|
91
|
+
}
|
|
92
|
+
const result = await installTmux();
|
|
93
|
+
if (result.success) {
|
|
94
|
+
return { installed: true, message: `✅ tmux installed via ${result.method}` };
|
|
95
|
+
}
|
|
96
|
+
return { installed: false, message: `❌ ${result.error}` };
|
|
97
|
+
};
|
|
98
|
+
// Check and install dependencies on startup
|
|
99
|
+
const tmuxCheck = await ensureTmuxInstalled();
|
|
100
|
+
if (!tmuxCheck.installed) {
|
|
101
|
+
console.warn(tmuxCheck.message);
|
|
102
|
+
}
|
|
103
|
+
const lumenCheck = await ensureLumenInstalled();
|
|
104
|
+
if (!lumenCheck.installed) {
|
|
105
|
+
console.warn(lumenCheck.message);
|
|
106
|
+
}
|
|
65
107
|
const getModifiedFiles = async () => {
|
|
66
108
|
try {
|
|
67
109
|
const unstaged = await $ `git diff --name-only`.text();
|
|
@@ -76,14 +118,26 @@ export const DiffViewerPlugin = async ({ project, client, $, directory, worktree
|
|
|
76
118
|
return [];
|
|
77
119
|
}
|
|
78
120
|
};
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
121
|
+
const launchDiffViewer = async (files) => {
|
|
122
|
+
// Check tmux
|
|
123
|
+
if (!await isTmuxInstalled()) {
|
|
124
|
+
return `❌ tmux is not installed.
|
|
82
125
|
|
|
83
126
|
To install:
|
|
84
|
-
brew install
|
|
127
|
+
brew install tmux
|
|
85
128
|
# or
|
|
86
|
-
|
|
129
|
+
apt install tmux
|
|
130
|
+
|
|
131
|
+
Then restart OpenCode.`;
|
|
132
|
+
}
|
|
133
|
+
// Check lumen
|
|
134
|
+
if (!await isLumenInstalled()) {
|
|
135
|
+
return `❌ lumen is not installed.
|
|
136
|
+
|
|
137
|
+
To install:
|
|
138
|
+
brew install jnsahaj/lumen/lumen
|
|
139
|
+
# or
|
|
140
|
+
cargo install lumen
|
|
87
141
|
|
|
88
142
|
Then restart OpenCode.`;
|
|
89
143
|
}
|
|
@@ -91,30 +145,28 @@ Then restart OpenCode.`;
|
|
|
91
145
|
if (modifiedFiles.length === 0) {
|
|
92
146
|
return "📝 No modified files.\n\nRun \`git add .\` to stage changes first.";
|
|
93
147
|
}
|
|
148
|
+
const fileList = modifiedFiles.map(f => ` • ${f}`).join('\n');
|
|
149
|
+
const fileArgs = modifiedFiles.map(f => `"${f}"`).join(' ');
|
|
150
|
+
const sessionName = "opencode-diff-viewer";
|
|
151
|
+
const cmd = `cd "${directory}" && lumen diff ${fileArgs}`;
|
|
94
152
|
try {
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
diffOutput += await $ `cd "${directory}" && git diff | delta --pager=never`.text();
|
|
109
|
-
}
|
|
110
|
-
if (!diffOutput.trim()) {
|
|
111
|
-
return "📝 No changes to show.";
|
|
112
|
-
}
|
|
113
|
-
const fileList = modifiedFiles.map(f => ` • ${f}`).join('\n');
|
|
114
|
-
return `✅ Modified files (${modifiedFiles.length}):\n${fileList}\n\n${diffOutput}`;
|
|
153
|
+
// Kill existing session if it exists
|
|
154
|
+
await $ `tmux kill-session -t "${sessionName}" 2>/dev/null || true`;
|
|
155
|
+
// Create new tmux session
|
|
156
|
+
await $ `tmux new-session -d -s "${sessionName}" "${cmd}"`;
|
|
157
|
+
return `✅ Opened lumen in tmux session "${sessionName}":\n${fileList}\n\nTo use lumen:
|
|
158
|
+
1. Run: tmux attach -t "${sessionName}"
|
|
159
|
+
2. Navigate: j/k or arrows, {/} for hunks
|
|
160
|
+
3. Detach: Ctrl+B then D
|
|
161
|
+
|
|
162
|
+
Keybindings:
|
|
163
|
+
j/k or ↑/↓: Navigate {/}: Jump hunks
|
|
164
|
+
tab: Sidebar e: Edit file
|
|
165
|
+
q: Quit`;
|
|
115
166
|
}
|
|
116
167
|
catch (e) {
|
|
117
|
-
return `❌
|
|
168
|
+
return `❌ Failed to launch tmux session: ${e.message || e}\n\nTry manually:
|
|
169
|
+
tmux new-session -s "${sessionName}" "${cmd}"`;
|
|
118
170
|
}
|
|
119
171
|
};
|
|
120
172
|
return {
|
|
@@ -122,7 +174,7 @@ Then restart OpenCode.`;
|
|
|
122
174
|
if (input.command === "diff") {
|
|
123
175
|
const files = input.args?.trim() ? [input.args.trim()] : undefined;
|
|
124
176
|
output.handled = true;
|
|
125
|
-
output.result = await
|
|
177
|
+
output.result = await launchDiffViewer(files);
|
|
126
178
|
}
|
|
127
179
|
},
|
|
128
180
|
"file.edited": async ({ event }) => {
|
|
@@ -130,12 +182,12 @@ Then restart OpenCode.`;
|
|
|
130
182
|
},
|
|
131
183
|
tool: {
|
|
132
184
|
view_diff: tool({
|
|
133
|
-
description: "
|
|
185
|
+
description: "Open the lumen diff viewer in a tmux session.",
|
|
134
186
|
args: {
|
|
135
187
|
file: tool.schema.string().optional().describe("Optional: specific file path"),
|
|
136
188
|
},
|
|
137
189
|
async execute(args, ctx) {
|
|
138
|
-
return await
|
|
190
|
+
return await launchDiffViewer(args.file ? [args.file] : undefined);
|
|
139
191
|
},
|
|
140
192
|
}),
|
|
141
193
|
},
|