opencodekit 0.12.3 → 0.12.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.
Files changed (62) hide show
  1. package/dist/index.js +2 -2
  2. package/dist/template/.opencode/agent/build.md +66 -9
  3. package/dist/template/.opencode/agent/rush.md +43 -19
  4. package/dist/template/.opencode/command/accessibility-check.md +7 -10
  5. package/dist/template/.opencode/command/analyze-mockup.md +3 -16
  6. package/dist/template/.opencode/command/analyze-project.md +57 -69
  7. package/dist/template/.opencode/command/brainstorm.md +3 -11
  8. package/dist/template/.opencode/command/commit.md +10 -18
  9. package/dist/template/.opencode/command/create.md +4 -8
  10. package/dist/template/.opencode/command/design-audit.md +24 -51
  11. package/dist/template/.opencode/command/design.md +10 -17
  12. package/dist/template/.opencode/command/finish.md +9 -9
  13. package/dist/template/.opencode/command/fix-ci.md +7 -28
  14. package/dist/template/.opencode/command/fix-types.md +3 -7
  15. package/dist/template/.opencode/command/fix-ui.md +5 -11
  16. package/dist/template/.opencode/command/fix.md +4 -10
  17. package/dist/template/.opencode/command/handoff.md +8 -14
  18. package/dist/template/.opencode/command/implement.md +13 -16
  19. package/dist/template/.opencode/command/import-plan.md +20 -38
  20. package/dist/template/.opencode/command/init.md +9 -13
  21. package/dist/template/.opencode/command/integration-test.md +11 -13
  22. package/dist/template/.opencode/command/issue.md +4 -8
  23. package/dist/template/.opencode/command/new-feature.md +20 -40
  24. package/dist/template/.opencode/command/plan.md +8 -12
  25. package/dist/template/.opencode/command/pr.md +29 -38
  26. package/dist/template/.opencode/command/quick-build.md +3 -7
  27. package/dist/template/.opencode/command/research-and-implement.md +4 -6
  28. package/dist/template/.opencode/command/research.md +10 -7
  29. package/dist/template/.opencode/command/resume.md +12 -24
  30. package/dist/template/.opencode/command/revert-feature.md +21 -56
  31. package/dist/template/.opencode/command/review-codebase.md +21 -23
  32. package/dist/template/.opencode/command/skill-create.md +1 -5
  33. package/dist/template/.opencode/command/skill-optimize.md +3 -10
  34. package/dist/template/.opencode/command/status.md +28 -25
  35. package/dist/template/.opencode/command/triage.md +19 -31
  36. package/dist/template/.opencode/command/ui-review.md +6 -13
  37. package/dist/template/.opencode/command.backup/analyze-project.md +465 -0
  38. package/dist/template/.opencode/command.backup/finish.md +167 -0
  39. package/dist/template/.opencode/command.backup/implement.md +143 -0
  40. package/dist/template/.opencode/command.backup/pr.md +252 -0
  41. package/dist/template/.opencode/command.backup/status.md +376 -0
  42. package/dist/template/.opencode/memory/project/SHELL_OUTPUT_MIGRATION_PLAN.md +551 -0
  43. package/dist/template/.opencode/memory/project/gotchas.md +33 -28
  44. package/dist/template/.opencode/opencode.json +542 -510
  45. package/dist/template/.opencode/package.json +1 -3
  46. package/dist/template/.opencode/plugin/compaction.ts +51 -129
  47. package/dist/template/.opencode/plugin/handoff.ts +18 -163
  48. package/dist/template/.opencode/plugin/notification.ts +1 -1
  49. package/dist/template/.opencode/plugin/package.json +7 -0
  50. package/dist/template/.opencode/plugin/sessions.ts +185 -651
  51. package/dist/template/.opencode/plugin/skill-mcp.ts +2 -1
  52. package/dist/template/.opencode/plugin/truncator.ts +19 -41
  53. package/dist/template/.opencode/plugin/tsconfig.json +14 -13
  54. package/dist/template/.opencode/tool/bd-inbox.ts +109 -0
  55. package/dist/template/.opencode/tool/bd-msg.ts +62 -0
  56. package/dist/template/.opencode/tool/bd-release.ts +71 -0
  57. package/dist/template/.opencode/tool/bd-reserve.ts +120 -0
  58. package/package.json +2 -2
  59. package/dist/template/.opencode/plugin/beads.ts +0 -1419
  60. package/dist/template/.opencode/plugin/compactor.ts +0 -107
  61. package/dist/template/.opencode/plugin/enforcer.ts +0 -190
  62. package/dist/template/.opencode/plugin/injector.ts +0 -150
@@ -11,12 +11,10 @@
11
11
  "author": "",
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
- "@opencode-ai/plugin": "1.1.2"
14
+ "@opencode-ai/plugin": "1.1.3"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^25.0.3",
18
- "fs": "^0.0.1-security",
19
- "path": "^0.12.7",
20
18
  "typescript": "^5.9.3"
21
19
  }
22
20
  }
@@ -1,94 +1,39 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import type { Plugin } from "@opencode-ai/plugin";
4
-
5
1
  /**
6
2
  * Memory & Compaction Plugin
7
3
  *
8
- * Combines memory auto-loading with session compaction customization:
9
- * 1. Auto-load memory on session start (session.start hook)
10
- * 2. Inject memory + beads context into compaction
11
- * 3. Custom compaction prompt for context continuity
4
+ * Injects memory context and beads state into session compaction:
5
+ * 1. Load memory files for context
6
+ * 2. Inject beads in-progress state
7
+ * 3. Append workflow-specific compaction rules
12
8
  */
13
9
 
14
- // Memory paths
15
- const getGlobalUserMemoryPath = () =>
16
- path.join(process.env.HOME || "~", ".config/opencode/memory/user.md");
17
-
18
- const getProjectMemoryPath = (directory: string) =>
19
- path.join(directory, ".opencode/memory");
20
-
21
- // Load memory files for injection
22
- const loadMemoryContext = async (directory: string): Promise<string[]> => {
23
- const context: string[] = [];
24
-
25
- // 1. Global user memory (cross-project preferences)
26
- const globalUserPath = getGlobalUserMemoryPath();
27
- if (fs.existsSync(globalUserPath)) {
28
- try {
29
- const content = fs.readFileSync(globalUserPath, "utf-8");
30
- if (content.trim() && !content.includes("<!-- ")) {
31
- context.push(`## User Profile (Global)\n${content}`);
32
- }
33
- } catch {}
34
- }
35
-
36
- // 2. Project memory files
37
- const projectMemoryPath = getProjectMemoryPath(directory);
38
- if (fs.existsSync(projectMemoryPath)) {
39
- const files = [
40
- "project/commands.md",
41
- "project/conventions.md",
42
- "project/gotchas.md",
43
- "project/architecture.md",
44
- ];
45
- for (const file of files) {
46
- const filePath = path.join(projectMemoryPath, file);
47
- if (fs.existsSync(filePath)) {
48
- try {
49
- const content = fs.readFileSync(filePath, "utf-8");
50
- // Only include if has actual content (not just template)
51
- if (content.trim() && !content.includes("<!-- ")) {
52
- const name = path.basename(file, ".md");
53
- context.push(`## Project ${name}\n${content}`);
54
- }
55
- } catch {}
56
- }
57
- }
58
- }
59
-
60
- return context;
61
- };
10
+ import type { Plugin } from "@opencode-ai/plugin";
62
11
 
63
- export const CompactionPlugin: Plugin = async (ctx) => {
64
- const { $, directory } = ctx;
12
+ export const CompactionPlugin: Plugin = async ({ $, directory }) => {
13
+ const MEMORY_DIR = `${directory}/.opencode/memory`;
65
14
 
66
15
  return {
67
- // 1. Auto-load memory context on session start
68
- "session.start": async (
69
- _input: unknown,
70
- output: { additionalContext?: string },
71
- ) => {
72
- const memoryContext = await loadMemoryContext(directory);
73
-
74
- if (memoryContext.length > 0) {
75
- output.additionalContext = `
76
- # Memory Context (Auto-loaded)
77
-
78
- ${memoryContext.join("\n\n")}
79
- `;
80
- console.log(
81
- `[compaction] Injected ${memoryContext.length} memory contexts into session`,
82
- );
83
- }
84
- },
85
-
86
- // 2. Inject context into compaction
87
16
  "experimental.session.compacting": async (input, output) => {
88
- // Load memory context for compaction
89
- const memoryContext = await loadMemoryContext(directory);
17
+ // Load memory context
18
+ let memoryContext = "";
19
+ try {
20
+ const memoryFiles =
21
+ await $`find ${MEMORY_DIR}/project -name '*.md' -type f 2>/dev/null | head -10`.quiet();
22
+ if (memoryFiles.stdout) {
23
+ const files = memoryFiles.stdout.toString().trim().split("\n");
24
+ for (const file of files) {
25
+ const content = await $`cat ${file}`.text();
26
+ if (content.trim() && !content.includes("<!-- ")) {
27
+ const name = file.split("/").pop()?.replace(".md", "");
28
+ memoryContext += `\n## Project ${name}\n${content}`;
29
+ }
30
+ }
31
+ }
32
+ } catch {
33
+ // Memory dir doesn't exist or other error
34
+ }
90
35
 
91
- // Inject beads state if available
36
+ // Inject beads in-progress state
92
37
  let beadsContext = "";
93
38
  try {
94
39
  const result =
@@ -96,68 +41,45 @@ ${memoryContext.join("\n\n")}
96
41
  if (result.stdout) {
97
42
  const inProgress = JSON.parse(result.stdout.toString());
98
43
  if (inProgress.length > 0) {
99
- beadsContext = `\n## Active Beads\n${inProgress.map((b: { id: string; title: string }) => `- ${b.id}: ${b.title}`).join("\n")}`;
44
+ beadsContext = `\n## Active Beads (In Progress)\n${inProgress.map((b: { id: string; title: string }) => `- ${b.id}: ${b.title}`).join("\n")}`;
100
45
  }
101
46
  }
102
47
  } catch {
103
48
  // Beads not available, skip
104
49
  }
105
50
 
106
- // Inject custom context with memory
107
- const memorySection =
108
- memoryContext.length > 0
109
- ? `\n## Persistent Memory\n${memoryContext.join("\n\n")}\n`
110
- : "";
111
-
112
- output.context.push(`## Session Context
51
+ // Inject memory and beads context into compaction context
52
+ if (memoryContext || beadsContext) {
53
+ output.context.push(`## Session Context
113
54
  ${beadsContext}
114
- ${memorySection}
115
- ## Memory Files to Check
116
- - .opencode/memory/project/gotchas.md - Non-obvious behaviors discovered
117
- - .opencode/memory/project/commands.md - Build/test commands learned
118
- - .opencode/memory/project/conventions.md - Code patterns observed
119
-
120
- ## Persistence Rules
121
- - PRESERVE: Bead IDs, todo items, file paths, line numbers, user constraints
122
- - DROP: Failed attempts, superseded info, verbose tool outputs, exploration dead-ends
123
- - Include enough context that a new session can continue seamlessly`);
124
-
125
- // Replace the entire compaction prompt for more control
126
- output.prompt = `You are summarizing a coding session for context continuity.
127
-
128
- ## Output Structure
129
-
130
- Use these sections:
131
-
132
- ### COMPLETED
133
- - What was done (with file paths)
134
- - Bead IDs closed and why
55
+ ${memoryContext}
56
+ `);
57
+ }
135
58
 
136
- ### IN PROGRESS
137
- - Current task and bead ID (if any)
138
- - Files being modified (exact paths)
139
- - Current todo state (preserve TodoWrite items)
59
+ // Append workflow-specific rules to OpenCode's default prompt
60
+ output.prompt = `${output.prompt}
140
61
 
141
- ### NEXT
142
- - What needs to be done next
143
- - Blockers or pending decisions
62
+ ## Additional Rules for This Workflow
144
63
 
145
- ### CONSTRAINTS
146
- - User preferences that must persist
147
- - Rules or requirements stated by user
148
- - Technical decisions and rationale
64
+ ### Beads Workflow
65
+ - PRESERVE: Bead IDs (bd-xxx format), bead states, in-progress task IDs
66
+ - DROP: Closed/completed beads (already tracked in git)
149
67
 
150
- ### PERSIST TO MEMORY
151
- - Gotchas discovered suggest for project/gotchas.md
152
- - Commands learned → suggest for project/commands.md
153
- - Patterns observed → suggest for project/conventions.md
68
+ ### TodoWrite Items
69
+ - PRESERVE: Todo items with exact IDs, statuses (pending/in_progress/completed), priorities
70
+ - DROP: Cancelled todos
154
71
 
155
- ## Rules
72
+ ### Memory System
73
+ If you discover:
74
+ - Gotchas/edge cases → Mention they should be saved to .opencode/memory/project/gotchas.md
75
+ - Build/test commands → Mention they should be saved to .opencode/memory/project/commands.md
76
+ - Code patterns/conventions → Mention they should be saved to .opencode/memory/project/conventions.md
77
+ - Architecture insights → Mention they should be saved to .opencode/memory/project/architecture.md
156
78
 
157
- - PRESERVE: Bead IDs, todo items, file paths, line numbers, user constraints
79
+ ### Preservation Priorities
80
+ - PRESERVE: File paths with exact locations (file:line_number), user constraints, technical decisions
158
81
  - DROP: Failed attempts, superseded info, verbose tool outputs, exploration dead-ends
159
- - Be concise but complete - this summary replaces the full conversation
160
- - Include enough context that a new session can continue seamlessly`;
82
+ `;
161
83
  },
162
84
  };
163
85
  };
@@ -1,182 +1,37 @@
1
1
  /**
2
2
  * OpenCode Handoff Plugin
3
- * Injects handoff context into session compaction for seamless session transitions
3
+ * Injects the most recent handoff file into session compaction
4
4
  *
5
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
6
+ * 1. User creates handoff markdown file in .opencode/memory/handoffs/
7
+ * 2. Session compaction (Ctrl+K) includes handoff context
8
+ * 3. New session resumes from previous state
10
9
  */
11
10
 
12
- import { existsSync, readFileSync, readdirSync } from "fs";
13
- import { join } from "path";
14
11
  import type { Plugin } from "@opencode-ai/plugin";
15
12
 
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(() => {});
13
+ export const HandoffPlugin: Plugin = async ({ $, directory }) => {
14
+ const HANDOFF_DIR = `${directory}/.opencode/memory/handoffs`;
115
15
 
116
16
  return {
117
- // Inject handoff context when session is being compacted
118
17
  "experimental.session.compacting": async (_input, output) => {
119
- const handoff = findLatestHandoff(directory);
18
+ // Find most recent handoff file
19
+ const result =
20
+ await $`ls -t ${HANDOFF_DIR}/*.md 2>/dev/null | head -1`.quiet();
120
21
 
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
- }
22
+ if (!result.stdout) return;
133
23
 
134
- // Check if handoff is recent (within last hour)
135
- const oneHourAgo = Date.now() - 60 * 60 * 1000;
136
- const isRecent = handoff.mtime > oneHourAgo;
24
+ const handoffPath = result.stdout.toString().trim();
25
+ const handoffContent = await $`cat ${handoffPath}`.text();
137
26
 
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
- }
27
+ // Inject into compaction context
28
+ output.context.push(`
29
+ ## Previous Session Handoff
150
30
 
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
- },
31
+ ${handoffContent}
165
32
 
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
- }
33
+ **IMPORTANT**: Resume work from where previous session left off.
34
+ `);
180
35
  },
181
36
  };
182
37
  };
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { Plugin } from "@opencode-ai/plugin";
7
- import { notify } from "./lib/notify";
7
+ import { notify } from "./lib/notify.js";
8
8
 
9
9
  export const NotificationPlugin: Plugin = async ({ client, $ }) => {
10
10
  const notifiedSessions = new Set<string>();
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "opencode-plugins",
3
+ "type": "module",
4
+ "dependencies": {
5
+ "@opencode-ai/plugin": "^1.1.2"
6
+ }
7
+ }