@xinleibird/bridge-opencode 0.2.6 → 0.2.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.
Binary file
Binary file
Binary file
package/bridge.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
+ import type { FilePart, Part } from "@opencode-ai/sdk";
2
3
  import crypto from "node:crypto";
3
4
  import { access } from "node:fs/promises";
4
5
  import { basename, isAbsolute, join } from "node:path";
@@ -104,12 +105,29 @@ export const BridgePlugin: Plugin = async ({ directory }) => {
104
105
  pendingByCallID.delete(input.callID);
105
106
 
106
107
  for (const filePath of pending.filePaths) {
107
- await refreshBuffer(filePath);
108
- await sendMessage("🔄 Reloaded by OpenCode.", "info");
108
+ const before = await checkBuffer(filePath);
109
+ let reloadSucceeded = false;
110
+ try {
111
+ await refreshBuffer(filePath);
112
+ reloadSucceeded = true;
113
+ } catch (err) {
114
+ console.error(`Failed to reload ${filePath}:`, err);
115
+ } finally {
116
+ if (before.isCurrent) {
117
+ try {
118
+ await sendMessage(
119
+ reloadSucceeded ? "🔄 Reloaded by OpenCode." : "⚠️ Reload failed.",
120
+ reloadSucceeded ? "info" : "warn",
121
+ );
122
+ } catch {
123
+ // non-fatal
124
+ }
125
+ }
126
+ }
109
127
  }
110
128
  },
111
129
 
112
- "chat.message": async (input, output) => {
130
+ "experimental.chat.messages.transform": async (_, output) => {
113
131
  let selections: Awaited<ReturnType<typeof getVisualSelections>>;
114
132
  try {
115
133
  selections = await getVisualSelections();
@@ -121,8 +139,17 @@ export const BridgePlugin: Plugin = async ({ directory }) => {
121
139
  const filteredSelections = selections.filter((s) => !s.cwd || s.cwd === cwd);
122
140
  if (filteredSelections.length === 0) return;
123
141
 
142
+ const userMessages = output.messages.filter((m) => m.info.role === "user");
143
+ if (userMessages.length === 0) return;
144
+
145
+ const latestUserMessage = userMessages[userMessages.length - 1];
146
+ const msgInfo = latestUserMessage.info;
147
+ if (!msgInfo || msgInfo.role !== "user") return;
148
+ if (!latestUserMessage.parts) {
149
+ latestUserMessage.parts = [];
150
+ }
151
+
124
152
  const refs: string[] = [];
125
- let attached = 0;
126
153
  for (const s of filteredSelections) {
127
154
  try {
128
155
  await access(s.filePath);
@@ -131,13 +158,59 @@ export const BridgePlugin: Plugin = async ({ directory }) => {
131
158
  }
132
159
  if (!s.startLine) continue;
133
160
 
134
- const filePath = s.filePath.startsWith(cwd + "/")
135
- ? "./" + s.filePath.slice(cwd.length + 1)
136
- : s.filePath;
161
+ const fileRef = `${s.filePath}:${s.startLine}-${s.endLine}`;
162
+ refs.push(fileRef);
137
163
 
138
- const fileRef = `${filePath}:${s.startLine}-${s.endLine}`;
164
+ const fileName = basename(s.filePath);
139
165
 
140
- refs.push(fileRef);
166
+ const filePart: FilePart = {
167
+ id: crypto.randomUUID(),
168
+ sessionID: msgInfo.sessionID,
169
+ messageID: msgInfo.id,
170
+ type: "file",
171
+ mime: "text/plain",
172
+ filename: fileName,
173
+ url: `file://${s.filePath}?start=${s.startLine}&end=${s.endLine}`,
174
+ source: {
175
+ text: {
176
+ start: 0,
177
+ value: fileRef,
178
+ end: fileRef.length,
179
+ },
180
+ type: "file",
181
+ path: s.filePath,
182
+ },
183
+ };
184
+ latestUserMessage.parts.push(filePart);
185
+ }
186
+
187
+ if (refs.length === 0) return;
188
+
189
+ const textPart = latestUserMessage.parts.find((p) => p.type === "text") as Part | undefined;
190
+ if (textPart && textPart.type === "text" && typeof textPart.text === "string") {
191
+ textPart.text = `${refs.join("\n")}\n\n${textPart.text}`;
192
+ }
193
+ },
194
+
195
+ "chat.message": async (input, output) => {
196
+ let selections: Awaited<ReturnType<typeof getVisualSelections>>;
197
+ try {
198
+ selections = await getVisualSelections();
199
+ } catch {
200
+ return;
201
+ }
202
+ if (!selections || selections.length === 0) return;
203
+
204
+ const filteredSelections = selections.filter((s) => !s.cwd || s.cwd === cwd);
205
+ if (filteredSelections.length === 0) return;
206
+
207
+ for (const s of filteredSelections) {
208
+ try {
209
+ await access(s.filePath);
210
+ } catch {
211
+ continue;
212
+ }
213
+ if (!s.startLine) continue;
141
214
 
142
215
  const fileName = basename(s.filePath);
143
216
 
@@ -147,20 +220,11 @@ export const BridgePlugin: Plugin = async ({ directory }) => {
147
220
  sessionID: input.sessionID,
148
221
  messageID: input.messageID ?? "",
149
222
  mime: "text/plain",
150
- filename: fileName,
223
+ filename: `${fileName}:${s.startLine}-${s.endLine}`,
151
224
  url: `file://${s.filePath}?start=${s.startLine}&end=${s.endLine}`,
152
225
  });
153
- attached++;
154
- }
155
-
156
- if (attached === 0) return;
157
-
158
- const textPart = output.parts.find((p: any) => p.type === "text") as any;
159
- if (textPart && typeof textPart.text === "string") {
160
- textPart.text = `${refs.join("\n")}\n\n${textPart.text}`;
161
226
  }
162
227
  },
163
228
  };
164
229
  };
165
-
166
230
  export default BridgePlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xinleibird/bridge-opencode",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,5 +38,8 @@
38
38
  "aarch64-unknown-linux-gnu"
39
39
  ]
40
40
  }
41
+ },
42
+ "dependencies": {
43
+ "@opencode-ai/sdk": "^1.15.12"
41
44
  }
42
45
  }