opencodekit 0.6.3 → 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.
@@ -11,7 +11,7 @@
11
11
  "author": "",
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
- "@opencode-ai/plugin": "1.0.199"
14
+ "@opencode-ai/plugin": "1.0.207"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^25.0.3",
@@ -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
- * Requires: Language servers installed (e.g., typescript-language-server, pyright, gopls)
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
- Install a language server:
59
- - TypeScript: npm i -g typescript-language-server typescript
60
- - Python: npm i -g pyright
61
- - Go: go install golang.org/x/tools/gopls@latest
62
- - Rust: rustup component add rust-analyzer`;
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,6 +1,6 @@
1
1
  {
2
2
  "name": "opencodekit",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "CLI tool for bootstrapping and managing OpenCodeKit projects",
5
5
  "type": "module",
6
6
  "repository": {
@@ -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
- }