opencodekit 0.6.4 → 0.6.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/dist/index.js +1 -1
- package/dist/template/.opencode/command/handoff.md +6 -6
- package/dist/template/.opencode/lib/lsp/config.ts +85 -3
- package/dist/template/.opencode/lib/lsp/constants.ts +216 -15
- package/dist/template/.opencode/memory/handoffs/2025-12-27T103000Z.md +76 -0
- package/dist/template/.opencode/opencode.json +56 -71
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/handoff.ts +182 -0
- package/dist/template/.opencode/tool/lsp.ts +13 -6
- package/package.json +1 -1
- package/dist/template/.opencode/pickle-thinker.jsonc +0 -11
package/dist/index.js
CHANGED
|
@@ -750,7 +750,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
750
750
|
// package.json
|
|
751
751
|
var package_default = {
|
|
752
752
|
name: "opencodekit",
|
|
753
|
-
version: "0.6.
|
|
753
|
+
version: "0.6.5",
|
|
754
754
|
description: "CLI tool for bootstrapping and managing OpenCodeKit projects",
|
|
755
755
|
type: "module",
|
|
756
756
|
repository: {
|
|
@@ -136,11 +136,11 @@ Branch: [branch]
|
|
|
136
136
|
|
|
137
137
|
Saved: .beads/artifacts/<bead-id>/handoffs/<timestamp>.md
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
━━━━━━━━━━━━━━━━━━━━━━━━
|
|
140
|
+
🔄 Press Ctrl+K to compact session with handoff context
|
|
141
|
+
━━━━━━━━━━━━━━━━━━━━━━━━
|
|
142
|
+
|
|
143
|
+
The handoff plugin will automatically inject this context into the new session.
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
- This handoff
|
|
143
|
-
- Previous session context (read_session)
|
|
144
|
-
- Git state
|
|
145
|
-
- Bead artifacts
|
|
145
|
+
Alternatively, resume manually: /resume <bead-id>
|
|
146
146
|
```
|
|
@@ -1,8 +1,71 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { extname, join } from "node:path";
|
|
3
4
|
import { BUILTIN_SERVERS, EXT_TO_LANG } from "./constants";
|
|
4
5
|
import type { LSPServerConfig } from "./types";
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Known paths where OpenCode installs bundled LSP servers
|
|
9
|
+
* These are checked when the command isn't found in PATH
|
|
10
|
+
*/
|
|
11
|
+
function getOpenCodeDataDir(): string {
|
|
12
|
+
const home = homedir();
|
|
13
|
+
switch (process.platform) {
|
|
14
|
+
case "darwin":
|
|
15
|
+
return join(home, ".local/share/opencode");
|
|
16
|
+
case "win32":
|
|
17
|
+
return join(
|
|
18
|
+
process.env.APPDATA ?? join(home, "AppData/Roaming"),
|
|
19
|
+
"opencode",
|
|
20
|
+
);
|
|
21
|
+
default: // linux
|
|
22
|
+
return join(
|
|
23
|
+
process.env.XDG_DATA_HOME ?? join(home, ".local/share"),
|
|
24
|
+
"opencode",
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Find OpenCode's bundled jdtls launcher jar
|
|
31
|
+
*/
|
|
32
|
+
function findOpenCodeJdtls(): string[] | null {
|
|
33
|
+
const jdtlsDir = join(getOpenCodeDataDir(), "bin/jdtls");
|
|
34
|
+
const pluginsDir = join(jdtlsDir, "plugins");
|
|
35
|
+
|
|
36
|
+
if (!existsSync(pluginsDir)) return null;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const files = readdirSync(pluginsDir);
|
|
40
|
+
const launcherJar = files.find(
|
|
41
|
+
(f) =>
|
|
42
|
+
f.startsWith("org.eclipse.equinox.launcher_") && f.endsWith(".jar"),
|
|
43
|
+
);
|
|
44
|
+
if (!launcherJar) return null;
|
|
45
|
+
|
|
46
|
+
const configDir = join(
|
|
47
|
+
jdtlsDir,
|
|
48
|
+
process.platform === "darwin"
|
|
49
|
+
? "config_mac"
|
|
50
|
+
: process.platform === "win32"
|
|
51
|
+
? "config_win"
|
|
52
|
+
: "config_linux",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!existsSync(configDir)) return null;
|
|
56
|
+
|
|
57
|
+
return [
|
|
58
|
+
"java",
|
|
59
|
+
"-jar",
|
|
60
|
+
join(pluginsDir, launcherJar),
|
|
61
|
+
"-configuration",
|
|
62
|
+
configDir,
|
|
63
|
+
];
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
6
69
|
export interface ResolvedServer extends LSPServerConfig {
|
|
7
70
|
id: string;
|
|
8
71
|
}
|
|
@@ -58,6 +121,18 @@ export function resolveServer(filePath: string): ResolvedServer | null {
|
|
|
58
121
|
}
|
|
59
122
|
}
|
|
60
123
|
|
|
124
|
+
// Special case: Try OpenCode's bundled jdtls for Java files
|
|
125
|
+
if (ext === ".java") {
|
|
126
|
+
const jdtlsCommand = findOpenCodeJdtls();
|
|
127
|
+
if (jdtlsCommand && commandExists("java")) {
|
|
128
|
+
return {
|
|
129
|
+
id: "jdtls",
|
|
130
|
+
command: jdtlsCommand,
|
|
131
|
+
extensions: [".java"],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
61
136
|
return null;
|
|
62
137
|
}
|
|
63
138
|
|
|
@@ -87,10 +162,17 @@ export function listAvailableServers(): Array<{
|
|
|
87
162
|
for (const [id, config] of Object.entries(BUILTIN_SERVERS)) {
|
|
88
163
|
if (configuredIds.has(id)) continue;
|
|
89
164
|
const [cmd] = config.command;
|
|
165
|
+
|
|
166
|
+
// Special case: jdtls can use OpenCode's bundled version
|
|
167
|
+
let available = !!cmd && commandExists(cmd);
|
|
168
|
+
if (!available && id === "jdtls") {
|
|
169
|
+
available = !!(findOpenCodeJdtls() && commandExists("java"));
|
|
170
|
+
}
|
|
171
|
+
|
|
90
172
|
results.push({
|
|
91
173
|
id,
|
|
92
174
|
extensions: config.extensions,
|
|
93
|
-
available
|
|
175
|
+
available,
|
|
94
176
|
});
|
|
95
177
|
}
|
|
96
178
|
|
|
@@ -40,20 +40,45 @@ export const DEFAULT_MAX_REFERENCES = 200;
|
|
|
40
40
|
export const DEFAULT_MAX_SYMBOLS = 200;
|
|
41
41
|
export const DEFAULT_MAX_DIAGNOSTICS = 200;
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Built-in LSP servers synced with OpenCode's LSP configuration
|
|
45
|
+
* @see https://opencode.ai/docs/lsp/
|
|
46
|
+
*
|
|
47
|
+
* Servers are matched in order - first match wins.
|
|
48
|
+
* Some servers (jdtls, pyright, etc.) may use OpenCode's bundled installation.
|
|
49
|
+
*/
|
|
44
50
|
export const BUILTIN_SERVERS: Record<string, Omit<LSPServerConfig, "id">> = {
|
|
51
|
+
// TypeScript/JavaScript
|
|
45
52
|
typescript: {
|
|
46
53
|
command: ["typescript-language-server", "--stdio"],
|
|
47
54
|
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
|
|
48
55
|
},
|
|
56
|
+
deno: {
|
|
57
|
+
command: ["deno", "lsp"],
|
|
58
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs"],
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Frontend frameworks
|
|
49
62
|
vue: {
|
|
50
63
|
command: ["vue-language-server", "--stdio"],
|
|
51
64
|
extensions: [".vue"],
|
|
52
65
|
},
|
|
66
|
+
svelte: {
|
|
67
|
+
command: ["svelteserver", "--stdio"],
|
|
68
|
+
extensions: [".svelte"],
|
|
69
|
+
},
|
|
70
|
+
astro: {
|
|
71
|
+
command: ["astro-ls", "--stdio"],
|
|
72
|
+
extensions: [".astro"],
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Go
|
|
53
76
|
gopls: {
|
|
54
77
|
command: ["gopls"],
|
|
55
78
|
extensions: [".go"],
|
|
56
79
|
},
|
|
80
|
+
|
|
81
|
+
// Python
|
|
57
82
|
basedpyright: {
|
|
58
83
|
command: ["basedpyright-langserver", "--stdio"],
|
|
59
84
|
extensions: [".py", ".pyi"],
|
|
@@ -62,10 +87,14 @@ export const BUILTIN_SERVERS: Record<string, Omit<LSPServerConfig, "id">> = {
|
|
|
62
87
|
command: ["pyright-langserver", "--stdio"],
|
|
63
88
|
extensions: [".py", ".pyi"],
|
|
64
89
|
},
|
|
90
|
+
|
|
91
|
+
// Rust
|
|
65
92
|
rust: {
|
|
66
93
|
command: ["rust-analyzer"],
|
|
67
94
|
extensions: [".rs"],
|
|
68
95
|
},
|
|
96
|
+
|
|
97
|
+
// C/C++
|
|
69
98
|
clangd: {
|
|
70
99
|
command: ["clangd", "--background-index", "--clang-tidy"],
|
|
71
100
|
extensions: [
|
|
@@ -81,26 +110,117 @@ export const BUILTIN_SERVERS: Record<string, Omit<LSPServerConfig, "id">> = {
|
|
|
81
110
|
".h++",
|
|
82
111
|
],
|
|
83
112
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
extensions: [".svelte"],
|
|
87
|
-
},
|
|
88
|
-
astro: {
|
|
89
|
-
command: ["astro-ls", "--stdio"],
|
|
90
|
-
extensions: [".astro"],
|
|
91
|
-
},
|
|
113
|
+
|
|
114
|
+
// Java - OpenCode auto-installs jdtls, requires Java 21+
|
|
92
115
|
jdtls: {
|
|
93
116
|
command: ["jdtls"],
|
|
94
117
|
extensions: [".java"],
|
|
95
118
|
},
|
|
96
|
-
|
|
119
|
+
|
|
120
|
+
// C#/F#
|
|
121
|
+
csharp: {
|
|
122
|
+
command: ["OmniSharp", "-lsp"],
|
|
123
|
+
extensions: [".cs"],
|
|
124
|
+
},
|
|
125
|
+
fsharp: {
|
|
126
|
+
command: ["fsautocomplete", "--adaptive-lsp-server-enabled"],
|
|
127
|
+
extensions: [".fs", ".fsi", ".fsx", ".fsscript"],
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// Ruby
|
|
131
|
+
"ruby-lsp": {
|
|
132
|
+
command: ["ruby-lsp"],
|
|
133
|
+
extensions: [".rb", ".rake", ".gemspec", ".ru"],
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// PHP
|
|
137
|
+
intelephense: {
|
|
138
|
+
command: ["intelephense", "--stdio"],
|
|
139
|
+
extensions: [".php"],
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// Lua
|
|
143
|
+
"lua-ls": {
|
|
97
144
|
command: ["lua-language-server"],
|
|
98
145
|
extensions: [".lua"],
|
|
99
146
|
},
|
|
147
|
+
|
|
148
|
+
// Shell
|
|
149
|
+
bash: {
|
|
150
|
+
command: ["bash-language-server", "start"],
|
|
151
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh"],
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Swift/Objective-C
|
|
155
|
+
"sourcekit-lsp": {
|
|
156
|
+
command: ["sourcekit-lsp"],
|
|
157
|
+
extensions: [".swift", ".objc", ".objcpp"],
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// Dart
|
|
161
|
+
dart: {
|
|
162
|
+
command: ["dart", "language-server", "--protocol=lsp"],
|
|
163
|
+
extensions: [".dart"],
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
// Elixir
|
|
167
|
+
"elixir-ls": {
|
|
168
|
+
command: ["elixir-ls"],
|
|
169
|
+
extensions: [".ex", ".exs"],
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// Gleam
|
|
173
|
+
gleam: {
|
|
174
|
+
command: ["gleam", "lsp"],
|
|
175
|
+
extensions: [".gleam"],
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
// Clojure
|
|
179
|
+
"clojure-lsp": {
|
|
180
|
+
command: ["clojure-lsp"],
|
|
181
|
+
extensions: [".clj", ".cljs", ".cljc", ".edn"],
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// OCaml
|
|
185
|
+
"ocaml-lsp": {
|
|
186
|
+
command: ["ocamllsp"],
|
|
187
|
+
extensions: [".ml", ".mli"],
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// Zig
|
|
191
|
+
zls: {
|
|
192
|
+
command: ["zls"],
|
|
193
|
+
extensions: [".zig", ".zon"],
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// Nix
|
|
197
|
+
nixd: {
|
|
198
|
+
command: ["nixd"],
|
|
199
|
+
extensions: [".nix"],
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
// Terraform
|
|
203
|
+
terraform: {
|
|
204
|
+
command: ["terraform-ls", "serve"],
|
|
205
|
+
extensions: [".tf", ".tfvars"],
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// Typst
|
|
209
|
+
tinymist: {
|
|
210
|
+
command: ["tinymist", "lsp"],
|
|
211
|
+
extensions: [".typ", ".typc"],
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// YAML
|
|
215
|
+
"yaml-ls": {
|
|
216
|
+
command: ["yaml-language-server", "--stdio"],
|
|
217
|
+
extensions: [".yaml", ".yml"],
|
|
218
|
+
},
|
|
100
219
|
};
|
|
101
220
|
|
|
102
|
-
// Extension to language ID mapping
|
|
221
|
+
// Extension to language ID mapping (used for LSP textDocument/didOpen)
|
|
103
222
|
export const EXT_TO_LANG: Record<string, string> = {
|
|
223
|
+
// TypeScript/JavaScript
|
|
104
224
|
".ts": "typescript",
|
|
105
225
|
".tsx": "typescriptreact",
|
|
106
226
|
".mts": "typescript",
|
|
@@ -111,28 +231,109 @@ export const EXT_TO_LANG: Record<string, string> = {
|
|
|
111
231
|
".cjs": "javascript",
|
|
112
232
|
".json": "json",
|
|
113
233
|
".jsonc": "jsonc",
|
|
234
|
+
|
|
235
|
+
// Python
|
|
114
236
|
".py": "python",
|
|
115
237
|
".pyi": "python",
|
|
238
|
+
|
|
239
|
+
// Go
|
|
116
240
|
".go": "go",
|
|
241
|
+
|
|
242
|
+
// Rust
|
|
117
243
|
".rs": "rust",
|
|
244
|
+
|
|
245
|
+
// C/C++
|
|
118
246
|
".c": "c",
|
|
119
247
|
".cpp": "cpp",
|
|
120
248
|
".cc": "cpp",
|
|
121
249
|
".cxx": "cpp",
|
|
250
|
+
".c++": "cpp",
|
|
122
251
|
".h": "c",
|
|
123
252
|
".hpp": "cpp",
|
|
253
|
+
".hh": "cpp",
|
|
254
|
+
".hxx": "cpp",
|
|
255
|
+
".h++": "cpp",
|
|
256
|
+
|
|
257
|
+
// Java
|
|
124
258
|
".java": "java",
|
|
259
|
+
|
|
260
|
+
// C#/F#
|
|
261
|
+
".cs": "csharp",
|
|
262
|
+
".fs": "fsharp",
|
|
263
|
+
".fsi": "fsharp",
|
|
264
|
+
".fsx": "fsharp",
|
|
265
|
+
".fsscript": "fsharp",
|
|
266
|
+
|
|
267
|
+
// Ruby
|
|
268
|
+
".rb": "ruby",
|
|
269
|
+
".rake": "ruby",
|
|
270
|
+
".gemspec": "ruby",
|
|
271
|
+
".ru": "ruby",
|
|
272
|
+
|
|
273
|
+
// PHP
|
|
274
|
+
".php": "php",
|
|
275
|
+
|
|
276
|
+
// Lua
|
|
125
277
|
".lua": "lua",
|
|
278
|
+
|
|
279
|
+
// Shell
|
|
280
|
+
".sh": "shellscript",
|
|
281
|
+
".bash": "shellscript",
|
|
282
|
+
".zsh": "shellscript",
|
|
283
|
+
".ksh": "shellscript",
|
|
284
|
+
|
|
285
|
+
// Swift/Objective-C
|
|
286
|
+
".swift": "swift",
|
|
287
|
+
".objc": "objective-c",
|
|
288
|
+
".objcpp": "objective-cpp",
|
|
289
|
+
|
|
290
|
+
// Dart
|
|
291
|
+
".dart": "dart",
|
|
292
|
+
|
|
293
|
+
// Elixir
|
|
294
|
+
".ex": "elixir",
|
|
295
|
+
".exs": "elixir",
|
|
296
|
+
|
|
297
|
+
// Gleam
|
|
298
|
+
".gleam": "gleam",
|
|
299
|
+
|
|
300
|
+
// Clojure
|
|
301
|
+
".clj": "clojure",
|
|
302
|
+
".cljs": "clojurescript",
|
|
303
|
+
".cljc": "clojure",
|
|
304
|
+
".edn": "edn",
|
|
305
|
+
|
|
306
|
+
// OCaml
|
|
307
|
+
".ml": "ocaml",
|
|
308
|
+
".mli": "ocaml",
|
|
309
|
+
|
|
310
|
+
// Zig
|
|
311
|
+
".zig": "zig",
|
|
312
|
+
".zon": "zig",
|
|
313
|
+
|
|
314
|
+
// Nix
|
|
315
|
+
".nix": "nix",
|
|
316
|
+
|
|
317
|
+
// Terraform
|
|
318
|
+
".tf": "terraform",
|
|
319
|
+
".tfvars": "terraform",
|
|
320
|
+
|
|
321
|
+
// Typst
|
|
322
|
+
".typ": "typst",
|
|
323
|
+
".typc": "typst",
|
|
324
|
+
|
|
325
|
+
// Frontend frameworks
|
|
126
326
|
".vue": "vue",
|
|
127
327
|
".svelte": "svelte",
|
|
128
328
|
".astro": "astro",
|
|
329
|
+
|
|
330
|
+
// Web
|
|
129
331
|
".html": "html",
|
|
130
332
|
".css": "css",
|
|
131
333
|
".scss": "scss",
|
|
132
|
-
|
|
334
|
+
|
|
335
|
+
// Config/Data
|
|
133
336
|
".yaml": "yaml",
|
|
134
337
|
".yml": "yaml",
|
|
135
|
-
".
|
|
136
|
-
".bash": "shellscript",
|
|
137
|
-
".zsh": "shellscript",
|
|
338
|
+
".md": "markdown",
|
|
138
339
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Handoff: test-handoff-plugin
|
|
2
|
+
|
|
3
|
+
**Created:** 2025-12-27T10:30:00Z
|
|
4
|
+
**Agent:** build
|
|
5
|
+
|
|
6
|
+
## Provenance
|
|
7
|
+
|
|
8
|
+
| Key | Value |
|
|
9
|
+
| ------ | --------------------------------------------------- |
|
|
10
|
+
| Repo | git@github.com:opencodekit/opencodekit-template.git |
|
|
11
|
+
| Branch | main |
|
|
12
|
+
| Commit | ae38a95f899300419e9b17030268cedc60ebb44f |
|
|
13
|
+
| Clean | no |
|
|
14
|
+
|
|
15
|
+
## Session Context
|
|
16
|
+
|
|
17
|
+
**Current Session:** testing handoff plugin
|
|
18
|
+
**Token Usage:** ~50k estimated
|
|
19
|
+
|
|
20
|
+
Next session can load context with:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
read_session("last", project="current")
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Progress
|
|
27
|
+
|
|
28
|
+
### Completed
|
|
29
|
+
|
|
30
|
+
- [x] Released v0.6.4 (version bump, changelog, tag, GitHub release)
|
|
31
|
+
- [x] Reviewed Agent-Skills-for-Context-Engineering repo (verdict: mostly theory, not worth stealing)
|
|
32
|
+
- [x] Reviewed Karpathy's hn-time-capsule (verdict: useful patterns)
|
|
33
|
+
- [x] Created handoff plugin (`plugin/handoff.ts`)
|
|
34
|
+
- [x] Updated `/handoff` command to instruct Ctrl+K
|
|
35
|
+
|
|
36
|
+
### In Progress
|
|
37
|
+
|
|
38
|
+
- [ ] Testing handoff plugin - verifying it injects context on compaction
|
|
39
|
+
|
|
40
|
+
### Remaining
|
|
41
|
+
|
|
42
|
+
- [ ] Commit new handoff plugin and updated command
|
|
43
|
+
- [ ] Test full flow: handoff → Ctrl+K → new session with context
|
|
44
|
+
|
|
45
|
+
## Context
|
|
46
|
+
|
|
47
|
+
### Key Files
|
|
48
|
+
|
|
49
|
+
- `.opencode/plugin/handoff.ts` - New plugin that injects handoff on compaction
|
|
50
|
+
- `.opencode/command/handoff.md` - Updated to tell user to press Ctrl+K
|
|
51
|
+
- `.opencode/plugin/compactor.ts` - Reference for plugin structure
|
|
52
|
+
|
|
53
|
+
### Decisions Made
|
|
54
|
+
|
|
55
|
+
- Use `experimental.session.compacting` hook to inject handoff context
|
|
56
|
+
- Only inject handoffs < 1 hour old to prevent stale context
|
|
57
|
+
- Search both `.opencode/memory/handoffs/` and `.beads/artifacts/` for handoffs
|
|
58
|
+
|
|
59
|
+
### Blockers/Issues
|
|
60
|
+
|
|
61
|
+
- OpenCode lacks `session.start` hook (Issue #5409) - can't auto-navigate to new session
|
|
62
|
+
- Workaround: User must manually press Ctrl+K after /handoff
|
|
63
|
+
|
|
64
|
+
## Resume Instructions
|
|
65
|
+
|
|
66
|
+
1. Commit the new handoff plugin and updated command
|
|
67
|
+
2. Test the full flow: run /handoff, press Ctrl+K, verify context injected
|
|
68
|
+
3. If working, consider adding to v0.6.5 release
|
|
69
|
+
|
|
70
|
+
**Recommended:** Start next session with `/resume test-handoff-plugin` to auto-load this handoff + previous session context.
|
|
71
|
+
|
|
72
|
+
## Artifacts
|
|
73
|
+
|
|
74
|
+
- spec.md: missing
|
|
75
|
+
- research.md: missing
|
|
76
|
+
- plan.md: missing
|
|
@@ -109,7 +109,8 @@
|
|
|
109
109
|
"command_list": ";",
|
|
110
110
|
"leader": "`",
|
|
111
111
|
"session_child_cycle": "ctrl+alt+right",
|
|
112
|
-
"session_child_cycle_reverse": "ctrl+alt+left"
|
|
112
|
+
"session_child_cycle_reverse": "ctrl+alt+left",
|
|
113
|
+
"session_compact": "ctrl+k"
|
|
113
114
|
},
|
|
114
115
|
"mcp": {
|
|
115
116
|
"Framelink MCP for Figma": {
|
|
@@ -169,7 +170,8 @@
|
|
|
169
170
|
"opencode-gemini-auth",
|
|
170
171
|
"@tarquinen/opencode-dcp@latest",
|
|
171
172
|
"@franlol/opencode-md-table-formatter@0.0.3",
|
|
172
|
-
"./plugin/skill.ts"
|
|
173
|
+
"./plugin/skill.ts",
|
|
174
|
+
"./plugin/handoff.ts"
|
|
173
175
|
],
|
|
174
176
|
"provider": {
|
|
175
177
|
"github-copilot": {
|
|
@@ -252,65 +254,62 @@
|
|
|
252
254
|
"models": {
|
|
253
255
|
"gemini-2.5-computer-use-preview-10-2025": {
|
|
254
256
|
"limit": {
|
|
255
|
-
"context":
|
|
257
|
+
"context": 880964,
|
|
256
258
|
"output": 65536
|
|
257
259
|
},
|
|
258
260
|
"name": "Gemini 2 5 Computer Use Preview 10 2025"
|
|
259
261
|
},
|
|
260
262
|
"gemini-2.5-flash": {
|
|
261
263
|
"limit": {
|
|
262
|
-
"context":
|
|
264
|
+
"context": 880964,
|
|
263
265
|
"output": 65536
|
|
264
266
|
},
|
|
265
267
|
"name": "Gemini 2 5 Flash"
|
|
266
268
|
},
|
|
267
269
|
"gemini-2.5-flash-lite": {
|
|
268
270
|
"limit": {
|
|
269
|
-
"context":
|
|
271
|
+
"context": 880964,
|
|
270
272
|
"output": 65536
|
|
271
273
|
},
|
|
272
274
|
"name": "Gemini 2 5 Flash Lite"
|
|
273
275
|
},
|
|
274
276
|
"gemini-2.5-pro": {
|
|
275
277
|
"limit": {
|
|
276
|
-
"context":
|
|
278
|
+
"context": 880964,
|
|
277
279
|
"output": 65536
|
|
278
280
|
},
|
|
279
281
|
"name": "Gemini 2 5 Pro"
|
|
280
282
|
},
|
|
281
283
|
"gemini-3-flash-preview": {
|
|
282
284
|
"limit": {
|
|
283
|
-
"context":
|
|
285
|
+
"context": 880964,
|
|
284
286
|
"output": 65536
|
|
285
287
|
},
|
|
286
288
|
"name": "Gemini 3 Flash Preview"
|
|
287
289
|
},
|
|
288
290
|
"gemini-3-pro-image-preview": {
|
|
289
291
|
"limit": {
|
|
290
|
-
"context":
|
|
292
|
+
"context": 880964,
|
|
291
293
|
"output": 65536
|
|
292
294
|
},
|
|
293
295
|
"name": "Gemini 3 Pro Image Preview"
|
|
294
296
|
},
|
|
295
297
|
"gemini-3-pro-preview": {
|
|
296
298
|
"limit": {
|
|
297
|
-
"context":
|
|
299
|
+
"context": 880964,
|
|
298
300
|
"output": 65536
|
|
299
301
|
},
|
|
300
302
|
"name": "Gemini 3 Pro Preview"
|
|
301
303
|
},
|
|
302
304
|
"gemini-claude-opus-4-5-thinking": {
|
|
303
|
-
"interleaved": {
|
|
304
|
-
"field": "reasoning_content"
|
|
305
|
-
},
|
|
306
305
|
"limit": {
|
|
307
|
-
"context":
|
|
306
|
+
"context": 168000,
|
|
308
307
|
"output": 64000
|
|
309
308
|
},
|
|
310
309
|
"name": "Gemini Claude Opus 4 5 Thinking",
|
|
311
310
|
"options": {
|
|
312
311
|
"thinking": {
|
|
313
|
-
"
|
|
312
|
+
"budgetTokens": 32768,
|
|
314
313
|
"type": "enabled"
|
|
315
314
|
}
|
|
316
315
|
},
|
|
@@ -318,23 +317,20 @@
|
|
|
318
317
|
},
|
|
319
318
|
"gemini-claude-sonnet-4-5": {
|
|
320
319
|
"limit": {
|
|
321
|
-
"context":
|
|
320
|
+
"context": 168000,
|
|
322
321
|
"output": 64000
|
|
323
322
|
},
|
|
324
323
|
"name": "Gemini Claude Sonnet 4 5"
|
|
325
324
|
},
|
|
326
325
|
"gemini-claude-sonnet-4-5-thinking": {
|
|
327
|
-
"interleaved": {
|
|
328
|
-
"field": "reasoning_content"
|
|
329
|
-
},
|
|
330
326
|
"limit": {
|
|
331
|
-
"context":
|
|
327
|
+
"context": 168000,
|
|
332
328
|
"output": 64000
|
|
333
329
|
},
|
|
334
330
|
"name": "Gemini Claude Sonnet 4 5 Thinking",
|
|
335
331
|
"options": {
|
|
336
332
|
"thinking": {
|
|
337
|
-
"
|
|
333
|
+
"budgetTokens": 32768,
|
|
338
334
|
"type": "enabled"
|
|
339
335
|
}
|
|
340
336
|
},
|
|
@@ -342,162 +338,146 @@
|
|
|
342
338
|
},
|
|
343
339
|
"glm-4.6": {
|
|
344
340
|
"limit": {
|
|
345
|
-
"context":
|
|
341
|
+
"context": 107520,
|
|
346
342
|
"output": 16384
|
|
347
343
|
},
|
|
348
344
|
"name": "Glm 4 6"
|
|
349
345
|
},
|
|
350
346
|
"glm-4.7": {
|
|
351
347
|
"limit": {
|
|
352
|
-
"context":
|
|
348
|
+
"context": 107520,
|
|
353
349
|
"output": 16384
|
|
354
350
|
},
|
|
355
351
|
"name": "Glm 4 7"
|
|
356
352
|
},
|
|
357
353
|
"gpt-5": {
|
|
358
354
|
"limit": {
|
|
359
|
-
"context":
|
|
355
|
+
"context": 336000,
|
|
360
356
|
"output": 32768
|
|
361
357
|
},
|
|
362
358
|
"name": "Gpt 5",
|
|
363
359
|
"options": {
|
|
364
|
-
"
|
|
365
|
-
"effort": "medium"
|
|
366
|
-
}
|
|
360
|
+
"reasoningEffort": "medium"
|
|
367
361
|
},
|
|
368
362
|
"reasoning": true
|
|
369
363
|
},
|
|
370
364
|
"gpt-5-codex": {
|
|
371
365
|
"limit": {
|
|
372
|
-
"context":
|
|
366
|
+
"context": 336000,
|
|
373
367
|
"output": 32768
|
|
374
368
|
},
|
|
375
369
|
"name": "Gpt 5 Codex",
|
|
376
370
|
"options": {
|
|
377
|
-
"
|
|
378
|
-
"effort": "medium"
|
|
379
|
-
}
|
|
371
|
+
"reasoningEffort": "medium"
|
|
380
372
|
},
|
|
381
373
|
"reasoning": true
|
|
382
374
|
},
|
|
383
375
|
"gpt-5-codex-mini": {
|
|
384
376
|
"limit": {
|
|
385
|
-
"context":
|
|
377
|
+
"context": 336000,
|
|
386
378
|
"output": 32768
|
|
387
379
|
},
|
|
388
380
|
"name": "Gpt 5 Codex Mini",
|
|
389
381
|
"options": {
|
|
390
|
-
"
|
|
391
|
-
"effort": "medium"
|
|
392
|
-
}
|
|
382
|
+
"reasoningEffort": "medium"
|
|
393
383
|
},
|
|
394
384
|
"reasoning": true
|
|
395
385
|
},
|
|
396
386
|
"gpt-5.1": {
|
|
397
387
|
"limit": {
|
|
398
|
-
"context":
|
|
388
|
+
"context": 336000,
|
|
399
389
|
"output": 32768
|
|
400
390
|
},
|
|
401
391
|
"name": "Gpt 5 1",
|
|
402
392
|
"options": {
|
|
403
|
-
"
|
|
404
|
-
"effort": "medium"
|
|
405
|
-
}
|
|
393
|
+
"reasoningEffort": "medium"
|
|
406
394
|
},
|
|
407
395
|
"reasoning": true
|
|
408
396
|
},
|
|
409
397
|
"gpt-5.1-codex": {
|
|
410
398
|
"limit": {
|
|
411
|
-
"context":
|
|
399
|
+
"context": 336000,
|
|
412
400
|
"output": 32768
|
|
413
401
|
},
|
|
414
402
|
"name": "Gpt 5 1 Codex",
|
|
415
403
|
"options": {
|
|
416
|
-
"
|
|
417
|
-
"effort": "medium"
|
|
418
|
-
}
|
|
404
|
+
"reasoningEffort": "medium"
|
|
419
405
|
},
|
|
420
406
|
"reasoning": true
|
|
421
407
|
},
|
|
422
408
|
"gpt-5.1-codex-max": {
|
|
423
409
|
"limit": {
|
|
424
|
-
"context":
|
|
410
|
+
"context": 336000,
|
|
425
411
|
"output": 32768
|
|
426
412
|
},
|
|
427
413
|
"name": "Gpt 5 1 Codex Max",
|
|
428
414
|
"options": {
|
|
429
|
-
"
|
|
430
|
-
"effort": "medium"
|
|
431
|
-
}
|
|
415
|
+
"reasoningEffort": "medium"
|
|
432
416
|
},
|
|
433
417
|
"reasoning": true
|
|
434
418
|
},
|
|
435
419
|
"gpt-5.1-codex-mini": {
|
|
436
420
|
"limit": {
|
|
437
|
-
"context":
|
|
421
|
+
"context": 336000,
|
|
438
422
|
"output": 32768
|
|
439
423
|
},
|
|
440
424
|
"name": "Gpt 5 1 Codex Mini",
|
|
441
425
|
"options": {
|
|
442
|
-
"
|
|
443
|
-
"effort": "medium"
|
|
444
|
-
}
|
|
426
|
+
"reasoningEffort": "medium"
|
|
445
427
|
},
|
|
446
428
|
"reasoning": true
|
|
447
429
|
},
|
|
448
430
|
"gpt-5.2": {
|
|
449
431
|
"limit": {
|
|
450
|
-
"context":
|
|
432
|
+
"context": 336000,
|
|
451
433
|
"output": 32768
|
|
452
434
|
},
|
|
453
435
|
"name": "Gpt 5 2",
|
|
454
436
|
"options": {
|
|
455
|
-
"
|
|
456
|
-
"effort": "medium"
|
|
457
|
-
}
|
|
437
|
+
"reasoningEffort": "medium"
|
|
458
438
|
},
|
|
459
439
|
"reasoning": true
|
|
460
440
|
},
|
|
461
441
|
"gpt-5.2-codex": {
|
|
462
442
|
"limit": {
|
|
463
|
-
"context":
|
|
443
|
+
"context": 336000,
|
|
464
444
|
"output": 32768
|
|
465
445
|
},
|
|
466
446
|
"name": "Gpt 5 2 Codex",
|
|
467
447
|
"options": {
|
|
468
|
-
"
|
|
469
|
-
"effort": "medium"
|
|
470
|
-
}
|
|
448
|
+
"reasoningEffort": "medium"
|
|
471
449
|
},
|
|
472
450
|
"reasoning": true
|
|
473
451
|
},
|
|
474
452
|
"gpt-oss-120b-medium": {
|
|
475
453
|
"limit": {
|
|
476
|
-
"context":
|
|
454
|
+
"context": 107520,
|
|
477
455
|
"output": 16384
|
|
478
456
|
},
|
|
479
457
|
"name": "Gpt Oss 120b Medium"
|
|
480
458
|
}
|
|
481
459
|
},
|
|
482
460
|
"name": "ProxyPal",
|
|
483
|
-
"npm": "@ai-sdk/
|
|
461
|
+
"npm": "@ai-sdk/anthropic",
|
|
484
462
|
"options": {
|
|
485
463
|
"apiKey": "proxypal-local",
|
|
486
|
-
"baseURL": "http://127.0.0.1:8317/v1"
|
|
464
|
+
"baseURL": "http://127.0.0.1:8317/v1",
|
|
465
|
+
"includeUsage": true
|
|
487
466
|
}
|
|
488
467
|
},
|
|
489
468
|
"zai-coding-plan": {
|
|
490
469
|
"models": {
|
|
491
470
|
"glm-4.6": {
|
|
492
471
|
"attachment": true,
|
|
472
|
+
"limit": {
|
|
473
|
+
"context": 128000,
|
|
474
|
+
"output": 16384
|
|
475
|
+
},
|
|
493
476
|
"options": {
|
|
494
|
-
"reasoningEffort": "high",
|
|
495
477
|
"temperature": 1,
|
|
496
478
|
"thinking": {
|
|
497
479
|
"type": "enabled"
|
|
498
|
-
}
|
|
499
|
-
"top_k": 40,
|
|
500
|
-
"top_p": 0.95
|
|
480
|
+
}
|
|
501
481
|
},
|
|
502
482
|
"reasoning": true,
|
|
503
483
|
"temperature": true,
|
|
@@ -506,20 +486,25 @@
|
|
|
506
486
|
"glm-4.7": {
|
|
507
487
|
"id": "glm-4.7",
|
|
508
488
|
"interleaved": true,
|
|
489
|
+
"limit": {
|
|
490
|
+
"context": 200000,
|
|
491
|
+
"output": 128000
|
|
492
|
+
},
|
|
509
493
|
"name": "GLM-4.7",
|
|
510
494
|
"options": {
|
|
511
|
-
"maxOutputTokens": 131072,
|
|
512
|
-
"reasoningEffort": "high",
|
|
513
|
-
"reasoningSummary": "true",
|
|
514
495
|
"temperature": 1,
|
|
515
496
|
"thinking": {
|
|
516
497
|
"type": "enabled"
|
|
517
|
-
}
|
|
518
|
-
"top_k": 40,
|
|
519
|
-
"top_p": 0.95
|
|
498
|
+
}
|
|
520
499
|
},
|
|
521
500
|
"reasoning": true
|
|
522
501
|
}
|
|
502
|
+
},
|
|
503
|
+
"name": "Z.AI Coding Plan",
|
|
504
|
+
"npm": "@ai-sdk/openai-compatible",
|
|
505
|
+
"options": {
|
|
506
|
+
"apiKey": "{env:ZAI_API_KEY}",
|
|
507
|
+
"baseURL": "https://api.z.ai/api/paas/v4"
|
|
523
508
|
}
|
|
524
509
|
}
|
|
525
510
|
},
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Handoff Plugin
|
|
3
|
+
* Injects handoff context into session compaction for seamless session transitions
|
|
4
|
+
*
|
|
5
|
+
* Workflow:
|
|
6
|
+
* 1. User runs /handoff command → creates handoff markdown file
|
|
7
|
+
* 2. User presses Ctrl+K to compact session
|
|
8
|
+
* 3. This plugin injects the handoff context into the compaction prompt
|
|
9
|
+
* 4. New session starts with full context from previous session
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
15
|
+
|
|
16
|
+
const HANDOFF_DIRS = [
|
|
17
|
+
".opencode/memory/handoffs",
|
|
18
|
+
".beads/artifacts", // Check for bead-specific handoffs
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
interface HandoffFile {
|
|
22
|
+
path: string;
|
|
23
|
+
mtime: number;
|
|
24
|
+
content: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findLatestHandoff(baseDir: string): HandoffFile | null {
|
|
28
|
+
const handoffs: HandoffFile[] = [];
|
|
29
|
+
|
|
30
|
+
for (const dir of HANDOFF_DIRS) {
|
|
31
|
+
const fullPath = join(baseDir, dir);
|
|
32
|
+
if (!existsSync(fullPath)) continue;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const files = findMarkdownFiles(fullPath);
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
try {
|
|
38
|
+
const stat = require("fs").statSync(file);
|
|
39
|
+
const content = readFileSync(file, "utf-8");
|
|
40
|
+
|
|
41
|
+
// Only include files that look like handoffs
|
|
42
|
+
if (
|
|
43
|
+
content.includes("## Resume Instructions") ||
|
|
44
|
+
content.includes("## Progress") ||
|
|
45
|
+
content.includes("# Handoff:")
|
|
46
|
+
) {
|
|
47
|
+
handoffs.push({
|
|
48
|
+
path: file,
|
|
49
|
+
mtime: stat.mtimeMs,
|
|
50
|
+
content,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Skip unreadable files
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// Skip inaccessible directories
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (handoffs.length === 0) return null;
|
|
63
|
+
|
|
64
|
+
// Return most recent
|
|
65
|
+
return handoffs.sort((a, b) => b.mtime - a.mtime)[0];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function findMarkdownFiles(dir: string, depth = 3): string[] {
|
|
69
|
+
if (depth <= 0) return [];
|
|
70
|
+
|
|
71
|
+
const results: string[] = [];
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
75
|
+
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
const fullPath = join(dir, entry.name);
|
|
78
|
+
|
|
79
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
80
|
+
results.push(...findMarkdownFiles(fullPath, depth - 1));
|
|
81
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
82
|
+
results.push(fullPath);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// Skip inaccessible directories
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatHandoffForCompaction(handoff: HandoffFile): string {
|
|
93
|
+
// Extract key sections from handoff
|
|
94
|
+
const content = handoff.content;
|
|
95
|
+
|
|
96
|
+
return `## Previous Session Handoff
|
|
97
|
+
|
|
98
|
+
<handoff-context>
|
|
99
|
+
${content}
|
|
100
|
+
</handoff-context>
|
|
101
|
+
|
|
102
|
+
**IMPORTANT**: Resume work from where the previous session left off. Check the "Resume Instructions" section above for specific next steps.`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const HandoffPlugin: Plugin = async ({ client, directory }) => {
|
|
106
|
+
client.app
|
|
107
|
+
.log({
|
|
108
|
+
body: {
|
|
109
|
+
service: "handoff-plugin",
|
|
110
|
+
level: "info",
|
|
111
|
+
message: "🔄 Handoff Plugin loaded - session continuity enabled",
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
.catch(() => {});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
// Inject handoff context when session is being compacted
|
|
118
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
119
|
+
const handoff = findLatestHandoff(directory);
|
|
120
|
+
|
|
121
|
+
if (!handoff) {
|
|
122
|
+
client.app
|
|
123
|
+
.log({
|
|
124
|
+
body: {
|
|
125
|
+
service: "handoff-plugin",
|
|
126
|
+
level: "debug",
|
|
127
|
+
message: "No handoff found - compacting without handoff context",
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
.catch(() => {});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Check if handoff is recent (within last hour)
|
|
135
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
136
|
+
const isRecent = handoff.mtime > oneHourAgo;
|
|
137
|
+
|
|
138
|
+
if (!isRecent) {
|
|
139
|
+
client.app
|
|
140
|
+
.log({
|
|
141
|
+
body: {
|
|
142
|
+
service: "handoff-plugin",
|
|
143
|
+
level: "debug",
|
|
144
|
+
message: `Handoff found but stale (${new Date(handoff.mtime).toISOString()}) - skipping injection`,
|
|
145
|
+
},
|
|
146
|
+
})
|
|
147
|
+
.catch(() => {});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Inject handoff context
|
|
152
|
+
const formattedHandoff = formatHandoffForCompaction(handoff);
|
|
153
|
+
output.context.push(formattedHandoff);
|
|
154
|
+
|
|
155
|
+
client.app
|
|
156
|
+
.log({
|
|
157
|
+
body: {
|
|
158
|
+
service: "handoff-plugin",
|
|
159
|
+
level: "info",
|
|
160
|
+
message: `📋 Injected handoff context from: ${handoff.path}`,
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
.catch(() => {});
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
// Log when compaction completes
|
|
167
|
+
event: async ({ event }) => {
|
|
168
|
+
if (event.type === "session.compacted") {
|
|
169
|
+
client.app
|
|
170
|
+
.log({
|
|
171
|
+
body: {
|
|
172
|
+
service: "handoff-plugin",
|
|
173
|
+
level: "info",
|
|
174
|
+
message:
|
|
175
|
+
"✅ Session compacted with handoff context - ready to continue",
|
|
176
|
+
},
|
|
177
|
+
})
|
|
178
|
+
.catch(() => {});
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* Semantic code refactoring using Language Server Protocol
|
|
4
4
|
*
|
|
5
5
|
* Provides: lsp_rename, lsp_code_actions, lsp_code_action_apply, lsp_organize_imports
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* Uses OpenCode's bundled LSP servers when available, falls back to system-installed servers.
|
|
8
|
+
* @see https://opencode.ai/docs/lsp/
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import { resolve } from "node:path";
|
|
@@ -55,11 +57,16 @@ IMPORTANT: This tool directly modifies files. Review the changes shown in output
|
|
|
55
57
|
if (!server) {
|
|
56
58
|
return `Error: No LSP server available for file type: ${absPath}
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
-
|
|
60
|
+
OpenCode supports 30+ languages with built-in LSP servers.
|
|
61
|
+
See https://opencode.ai/docs/lsp/ for the full list.
|
|
62
|
+
|
|
63
|
+
Common servers:
|
|
64
|
+
- TypeScript: Requires 'typescript' in project dependencies
|
|
65
|
+
- Python: Requires 'pyright' dependency installed
|
|
66
|
+
- Go: Requires 'go' command available
|
|
67
|
+
- Rust: Requires 'rust-analyzer' command available
|
|
68
|
+
- Java: Requires Java SDK 21+ installed (OpenCode auto-installs jdtls)
|
|
69
|
+
- C/C++: Requires 'clangd' available (OpenCode auto-installs for C/C++ projects)`;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
try {
|
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Ultrathink config for pickle-thinker
|
|
3
|
-
// mode: "lite" keeps the original behavior (prefix user prompts only).
|
|
4
|
-
// mode: "tool" adds an extra user turn after each tool result to force deeper analysis.
|
|
5
|
-
// Note: tool mode increases turns/tokens and may impact subscription limits.
|
|
6
|
-
"enabled": true,
|
|
7
|
-
// "lite" | "tool"
|
|
8
|
-
"mode": "tool",
|
|
9
|
-
// Change the thinking keyword if you like
|
|
10
|
-
"prefix": "Ultrathink: "
|
|
11
|
-
}
|