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.
Files changed (3) hide show
  1. package/README.md +54 -54
  2. package/dist/index.js +92 -40
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,39 +3,38 @@
3
3
  [![npm version](https://img.shields.io/npm/v/opencode-diff-viewer.svg)](https://www.npmjs.com/package/opencode-diff-viewer)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/opencode-diff-viewer.svg)](https://www.npmjs.com/package/opencode-diff-viewer)
5
5
 
6
- 一个 OpenCode 插件,使用 [delta](https://github.com/dandavison/delta) 提供语法高亮的 git diff 查看功能。
6
+ 一个 OpenCode 插件,使用 [lumen](https://github.com/jnsahaj/lumen) + [tmux](https://github.com/tmux/tmux) 提供美观的 TUI diff 查看功能。
7
7
 
8
8
  ## 功能特性
9
9
 
10
- - ✨ **自动安装 delta** - 插件会自动检测并安装 delta 依赖
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
- ### 1. 安装 delta
17
+ ### 自动安装
18
18
 
19
- 插件会自动尝试安装 delta,如果自动安装失败,需要手动安装:
19
+ 插件会在启动时自动检查并安装以下依赖:
20
20
 
21
- **macOS / Linux (Homebrew)**:
22
- ```bash
23
- brew install dandavison/delta/delta
24
- ```
21
+ 1. **tmux** - 终端复用器
22
+ 2. **lumen** - TUI diff 查看器
23
+
24
+ ### 手动安装(如果自动安装失败)
25
25
 
26
- **Cargo (Rust)**:
26
+ **tmux**:
27
27
  ```bash
28
- cargo install delta
28
+ brew install tmux
29
+ # or
30
+ apt install tmux
29
31
  ```
30
32
 
31
- **Windows**:
32
- 下载 [delta releases](https://github.com/dandavison/delta/releases) 并添加到 PATH
33
-
34
- ### 2. Git 仓库
35
-
36
- 确保项目是 git 仓库,并且有修改的文件:
33
+ **lumen**:
37
34
  ```bash
38
- git status # 查看修改的文件
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 with syntax highlighting.",
67
- "description": "Show git diff with syntax highlighting"
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
- ## delta 快捷键
93
+ ### 查看 lumen
94
+
95
+ 执行 `/diff` 后,lumen 会在 tmux 会话中运行。要查看 lumen:
95
96
 
96
- 在 diff 输出中:
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
- | `n` / `p` | 下/上一个变更 |
101
- | `N` / `P` | 下/上一个文件 |
102
- | `q` | 退出(如果启用 pager) |
108
+ | `j` / `k` 或 `↑` / `↓` | 上/下移动 |
109
+ | `{` / `}` | 跳转到上/下一个变更块 |
110
+ | `Tab` | 切换侧边栏 |
111
+ | `e` | 在编辑器中打开文件 |
112
+ | `q` | 退出 |
103
113
 
104
114
  ## 故障排除
105
115
 
106
- ### 1. delta 未安装
116
+ ### 1. tmux 未安装
117
+
118
+ ```
119
+ ❌ tmux is not installed
120
+ ```
121
+
122
+ **解决方案**: 手动安装 tmux(见上方手动安装)
123
+
124
+ ### 2. lumen 未安装
107
125
 
108
126
  ```
109
- delta is not installed
127
+ lumen is not installed
110
128
  ```
111
129
 
112
- **解决方案**: 手动安装 delta(见上方前置条件)
130
+ **解决方案**: 手动安装 lumen(见上方手动安装)
113
131
 
114
- ### 2. 没有修改的文件
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
- ### 3. 插件未加载
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. **检测修改文件** - 插件使用 `git diff` 获取已暂存和未暂存的修改
148
- 2. **格式化输出** - 通过 `delta` 管道输出,带语法高亮
149
- 3. **自动安装** - 插件启动时检查 delta,未安装则自动安装
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
- - [delta](https://github.com/dandavison/delta) - 语法高亮的 git diff 查看器
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 isDeltaInstalled = async () => {
3
+ const isLumenInstalled = async () => {
4
4
  try {
5
- await $ `which delta`;
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 installDelta = async () => {
39
+ const installLumen = async () => {
31
40
  if (await isBrewInstalled()) {
32
41
  try {
33
- await $ `brew install delta`;
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 delta`;
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 delta manually:\n brew install dandavison/delta/delta\n # or\n cargo install delta"
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 ensureDeltaInstalled = async () => {
55
- if (await isDeltaInstalled()) {
78
+ const ensureLumenInstalled = async () => {
79
+ if (await isLumenInstalled()) {
56
80
  return { installed: true };
57
81
  }
58
- const result = await installDelta();
82
+ const result = await installLumen();
59
83
  if (result.success) {
60
- return { installed: true, message: `✅ delta installed via ${result.method}` };
84
+ return { installed: true, message: `✅ lumen installed via ${result.method}` };
61
85
  }
62
86
  return { installed: false, message: `❌ ${result.error}` };
63
87
  };
64
- await ensureDeltaInstalled();
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 showDiff = async (files) => {
80
- if (!await isDeltaInstalled()) {
81
- return `❌ delta is not installed.
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 dandavison/delta/delta
127
+ brew install tmux
85
128
  # or
86
- cargo install delta
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
- // Get diff output with delta
96
- let diffOutput = "";
97
- // Show staged diff
98
- const stagedDiff = await $ `cd "${directory}" && git diff --staged`.text();
99
- if (stagedDiff.trim()) {
100
- diffOutput += "=== STAGED CHANGES ===\n\n";
101
- diffOutput += await $ `cd "${directory}" && git diff --staged | delta --pager=never`.text();
102
- }
103
- // Show unstaged diff
104
- const unstagedDiff = await $ `cd "${directory}" && git diff`.text();
105
- if (unstagedDiff.trim()) {
106
- if (diffOutput)
107
- diffOutput += "\n=== UNSTAGED CHANGES ===\n\n";
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 `❌ Error showing diff: ${e.message || e}`;
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 showDiff(files);
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: "Show git diff with syntax highlighting using delta.",
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 showDiff(args.file ? [args.file] : undefined);
190
+ return await launchDiffViewer(args.file ? [args.file] : undefined);
139
191
  },
140
192
  }),
141
193
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-diff-viewer",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "OpenCode plugin for viewing git diffs using lumen TUI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",