opencodekit 0.12.5 → 0.12.7

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 (44) hide show
  1. package/dist/index.js +2 -2
  2. package/dist/template/.opencode/agent/build.md +54 -29
  3. package/dist/template/.opencode/agent/explore.md +27 -16
  4. package/dist/template/.opencode/agent/planner.md +103 -63
  5. package/dist/template/.opencode/agent/review.md +31 -23
  6. package/dist/template/.opencode/agent/rush.md +33 -23
  7. package/dist/template/.opencode/agent/scout.md +27 -19
  8. package/dist/template/.opencode/agent/vision.md +29 -19
  9. package/dist/template/.opencode/command/accessibility-check.md +1 -0
  10. package/dist/template/.opencode/command/analyze-mockup.md +1 -0
  11. package/dist/template/.opencode/command/analyze-project.md +2 -1
  12. package/dist/template/.opencode/command/brainstorm.md +2 -1
  13. package/dist/template/.opencode/command/design-audit.md +1 -0
  14. package/dist/template/.opencode/command/finish.md +39 -4
  15. package/dist/template/.opencode/command/implement.md +26 -6
  16. package/dist/template/.opencode/command/init.md +1 -0
  17. package/dist/template/.opencode/command/pr.md +28 -1
  18. package/dist/template/.opencode/command/research-ui.md +1 -0
  19. package/dist/template/.opencode/command/research.md +1 -0
  20. package/dist/template/.opencode/command/review-codebase.md +1 -0
  21. package/dist/template/.opencode/command/status.md +3 -2
  22. package/dist/template/.opencode/command/summarize.md +2 -1
  23. package/dist/template/.opencode/command/ui-review.md +1 -0
  24. package/dist/template/.opencode/memory/project/architecture.md +59 -6
  25. package/dist/template/.opencode/memory/project/commands.md +20 -164
  26. package/dist/template/.opencode/memory/user.md +24 -7
  27. package/dist/template/.opencode/opencode.json +496 -542
  28. package/dist/template/.opencode/package.json +1 -1
  29. package/dist/template/.opencode/skill/condition-based-waiting/example.ts +71 -65
  30. package/dist/template/.opencode/tool/memory-read.ts +57 -57
  31. package/dist/template/.opencode/tool/memory-update.ts +53 -53
  32. package/dist/template/.opencode/tsconfig.json +19 -19
  33. package/package.json +1 -1
  34. package/dist/template/.opencode/command.backup/analyze-project.md +0 -465
  35. package/dist/template/.opencode/command.backup/finish.md +0 -167
  36. package/dist/template/.opencode/command.backup/implement.md +0 -143
  37. package/dist/template/.opencode/command.backup/pr.md +0 -252
  38. package/dist/template/.opencode/command.backup/status.md +0 -376
  39. package/dist/template/.opencode/lib/lsp/client.ts +0 -614
  40. package/dist/template/.opencode/lib/lsp/config.ts +0 -199
  41. package/dist/template/.opencode/lib/lsp/constants.ts +0 -339
  42. package/dist/template/.opencode/lib/lsp/types.ts +0 -138
  43. package/dist/template/.opencode/lib/lsp/utils.ts +0 -190
  44. package/dist/template/.opencode/memory/project/SHELL_OUTPUT_MIGRATION_PLAN.md +0 -551
@@ -11,7 +11,7 @@
11
11
  "author": "",
12
12
  "license": "ISC",
13
13
  "dependencies": {
14
- "@opencode-ai/plugin": "1.1.3"
14
+ "@opencode-ai/plugin": "1.1.4"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^25.0.3",
@@ -2,8 +2,8 @@
2
2
  // From: Lace test infrastructure improvements (2025-10-03)
3
3
  // Context: Fixed 15 flaky tests by replacing arbitrary timeouts
4
4
 
5
- import type { ThreadManager } from '~/threads/thread-manager';
6
- import type { LaceEvent, LaceEventType } from '~/threads/types';
5
+ import type { ThreadManager } from "~/threads/thread-manager";
6
+ import type { LaceEvent, LaceEventType } from "~/threads/types";
7
7
 
8
8
  /**
9
9
  * Wait for a specific event type to appear in thread
@@ -18,29 +18,33 @@ import type { LaceEvent, LaceEventType } from '~/threads/types';
18
18
  * await waitForEvent(threadManager, agentThreadId, 'TOOL_RESULT');
19
19
  */
20
20
  export function waitForEvent(
21
- threadManager: ThreadManager,
22
- threadId: string,
23
- eventType: LaceEventType,
24
- timeoutMs = 5000
21
+ threadManager: ThreadManager,
22
+ threadId: string,
23
+ eventType: LaceEventType,
24
+ timeoutMs = 5000,
25
25
  ): Promise<LaceEvent> {
26
- return new Promise((resolve, reject) => {
27
- const startTime = Date.now();
26
+ return new Promise((resolve, reject) => {
27
+ const startTime = Date.now();
28
28
 
29
- const check = () => {
30
- const events = threadManager.getEvents(threadId);
31
- const event = events.find((e) => e.type === eventType);
29
+ const check = () => {
30
+ const events = threadManager.getEvents(threadId);
31
+ const event = events.find((e) => e.type === eventType);
32
32
 
33
- if (event) {
34
- resolve(event);
35
- } else if (Date.now() - startTime > timeoutMs) {
36
- reject(new Error(`Timeout waiting for ${eventType} event after ${timeoutMs}ms`));
37
- } else {
38
- setTimeout(check, 10); // Poll every 10ms for efficiency
39
- }
40
- };
33
+ if (event) {
34
+ resolve(event);
35
+ } else if (Date.now() - startTime > timeoutMs) {
36
+ reject(
37
+ new Error(
38
+ `Timeout waiting for ${eventType} event after ${timeoutMs}ms`,
39
+ ),
40
+ );
41
+ } else {
42
+ setTimeout(check, 10); // Poll every 10ms for efficiency
43
+ }
44
+ };
41
45
 
42
- check();
43
- });
46
+ check();
47
+ });
44
48
  }
45
49
 
46
50
  /**
@@ -58,34 +62,34 @@ export function waitForEvent(
58
62
  * await waitForEventCount(threadManager, agentThreadId, 'AGENT_MESSAGE', 2);
59
63
  */
60
64
  export function waitForEventCount(
61
- threadManager: ThreadManager,
62
- threadId: string,
63
- eventType: LaceEventType,
64
- count: number,
65
- timeoutMs = 5000
65
+ threadManager: ThreadManager,
66
+ threadId: string,
67
+ eventType: LaceEventType,
68
+ count: number,
69
+ timeoutMs = 5000,
66
70
  ): Promise<LaceEvent[]> {
67
- return new Promise((resolve, reject) => {
68
- const startTime = Date.now();
71
+ return new Promise((resolve, reject) => {
72
+ const startTime = Date.now();
69
73
 
70
- const check = () => {
71
- const events = threadManager.getEvents(threadId);
72
- const matchingEvents = events.filter((e) => e.type === eventType);
74
+ const check = () => {
75
+ const events = threadManager.getEvents(threadId);
76
+ const matchingEvents = events.filter((e) => e.type === eventType);
73
77
 
74
- if (matchingEvents.length >= count) {
75
- resolve(matchingEvents);
76
- } else if (Date.now() - startTime > timeoutMs) {
77
- reject(
78
- new Error(
79
- `Timeout waiting for ${count} ${eventType} events after ${timeoutMs}ms (got ${matchingEvents.length})`
80
- )
81
- );
82
- } else {
83
- setTimeout(check, 10);
84
- }
85
- };
78
+ if (matchingEvents.length >= count) {
79
+ resolve(matchingEvents);
80
+ } else if (Date.now() - startTime > timeoutMs) {
81
+ reject(
82
+ new Error(
83
+ `Timeout waiting for ${count} ${eventType} events after ${timeoutMs}ms (got ${matchingEvents.length})`,
84
+ ),
85
+ );
86
+ } else {
87
+ setTimeout(check, 10);
88
+ }
89
+ };
86
90
 
87
- check();
88
- });
91
+ check();
92
+ });
89
93
  }
90
94
 
91
95
  /**
@@ -109,30 +113,32 @@ export function waitForEventCount(
109
113
  * );
110
114
  */
111
115
  export function waitForEventMatch(
112
- threadManager: ThreadManager,
113
- threadId: string,
114
- predicate: (event: LaceEvent) => boolean,
115
- description: string,
116
- timeoutMs = 5000
116
+ threadManager: ThreadManager,
117
+ threadId: string,
118
+ predicate: (event: LaceEvent) => boolean,
119
+ description: string,
120
+ timeoutMs = 5000,
117
121
  ): Promise<LaceEvent> {
118
- return new Promise((resolve, reject) => {
119
- const startTime = Date.now();
122
+ return new Promise((resolve, reject) => {
123
+ const startTime = Date.now();
120
124
 
121
- const check = () => {
122
- const events = threadManager.getEvents(threadId);
123
- const event = events.find(predicate);
125
+ const check = () => {
126
+ const events = threadManager.getEvents(threadId);
127
+ const event = events.find(predicate);
124
128
 
125
- if (event) {
126
- resolve(event);
127
- } else if (Date.now() - startTime > timeoutMs) {
128
- reject(new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`));
129
- } else {
130
- setTimeout(check, 10);
131
- }
132
- };
129
+ if (event) {
130
+ resolve(event);
131
+ } else if (Date.now() - startTime > timeoutMs) {
132
+ reject(
133
+ new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`),
134
+ );
135
+ } else {
136
+ setTimeout(check, 10);
137
+ }
138
+ };
133
139
 
134
- check();
135
- });
140
+ check();
141
+ });
136
142
  }
137
143
 
138
144
  // Usage example from actual debugging session:
@@ -1,66 +1,66 @@
1
+ import path from "path";
1
2
  import { tool } from "@opencode-ai/plugin";
2
3
  import fs from "fs/promises";
3
- import path from "path";
4
4
 
5
5
  export default tool({
6
- description:
7
- "Read memory files for persistent cross-session context. Returns current project state, learnings, and active tasks. Supports subdirectories (e.g., 'research/opencode-sessions').",
8
- args: {
9
- file: tool.schema
10
- .string()
11
- .optional()
12
- .describe(
13
- "Memory file to read: handoffs/YYYY-MM-DD-phase, research/YYYY-MM-DD-topic, _templates/task-prd, _templates/task-spec, _templates/task-review, _templates/research, _templates/handoff",
14
- ),
15
- },
16
- execute: async (args: { file?: string }) => {
17
- const fileName = args.file || "memory";
6
+ description:
7
+ "Read memory files for persistent cross-session context. Returns current project state, learnings, and active tasks. Supports subdirectories (e.g., 'research/opencode-sessions').",
8
+ args: {
9
+ file: tool.schema
10
+ .string()
11
+ .optional()
12
+ .describe(
13
+ "Memory file to read: handoffs/YYYY-MM-DD-phase, research/YYYY-MM-DD-topic, _templates/task-prd, _templates/task-spec, _templates/task-review, _templates/research, _templates/handoff",
14
+ ),
15
+ },
16
+ execute: async (args: { file?: string }) => {
17
+ const fileName = args.file || "memory";
18
18
 
19
- // Normalize: strip .md extension if present
20
- const normalizedFile = fileName.replace(/\.md$/i, "");
19
+ // Normalize: strip .md extension if present
20
+ const normalizedFile = fileName.replace(/\.md$/i, "");
21
21
 
22
- // Location priority: project > global > legacy
23
- const locations = [
24
- path.join(process.cwd(), ".opencode/memory", `${normalizedFile}.md`),
25
- path.join(
26
- process.env.HOME || "",
27
- ".config/opencode/memory",
28
- `${normalizedFile}.md`,
29
- ),
30
- path.join(
31
- process.cwd(),
32
- ".config/opencode/memory",
33
- `${normalizedFile}.md`,
34
- ),
35
- ];
22
+ // Location priority: project > global > legacy
23
+ const locations = [
24
+ path.join(process.cwd(), ".opencode/memory", `${normalizedFile}.md`),
25
+ path.join(
26
+ process.env.HOME || "",
27
+ ".config/opencode/memory",
28
+ `${normalizedFile}.md`,
29
+ ),
30
+ path.join(
31
+ process.cwd(),
32
+ ".config/opencode/memory",
33
+ `${normalizedFile}.md`,
34
+ ),
35
+ ];
36
36
 
37
- // Try each location in order
38
- for (const filePath of locations) {
39
- try {
40
- const content = await fs.readFile(filePath, "utf-8");
41
- const locationLabel = filePath.includes(".opencode/memory")
42
- ? "project"
43
- : filePath.includes(process.env.HOME || "")
44
- ? "global"
45
- : "legacy";
46
- return `[Read from ${locationLabel}: ${filePath}]\n\n${content}`;
47
- } catch (error) {
48
- // Continue to next location if file not found
49
- if (
50
- error instanceof Error &&
51
- "code" in error &&
52
- error.code === "ENOENT"
53
- ) {
54
- continue;
55
- }
56
- // Other errors should be reported
57
- if (error instanceof Error) {
58
- return `Error reading memory from ${filePath}: ${error.message}`;
59
- }
60
- }
61
- }
37
+ // Try each location in order
38
+ for (const filePath of locations) {
39
+ try {
40
+ const content = await fs.readFile(filePath, "utf-8");
41
+ const locationLabel = filePath.includes(".opencode/memory")
42
+ ? "project"
43
+ : filePath.includes(process.env.HOME || "")
44
+ ? "global"
45
+ : "legacy";
46
+ return `[Read from ${locationLabel}: ${filePath}]\n\n${content}`;
47
+ } catch (error) {
48
+ // Continue to next location if file not found
49
+ if (
50
+ error instanceof Error &&
51
+ "code" in error &&
52
+ error.code === "ENOENT"
53
+ ) {
54
+ continue;
55
+ }
56
+ // Other errors should be reported
57
+ if (error instanceof Error) {
58
+ return `Error reading memory from ${filePath}: ${error.message}`;
59
+ }
60
+ }
61
+ }
62
62
 
63
- // No file found in any location
64
- return `Memory file '${normalizedFile}.md' not found in any location.\nSearched:\n- ${locations.join("\n- ")}\n\nStructure:\n- handoffs/YYYY-MM-DD-phase (phase transitions)\n- research/YYYY-MM-DD-topic (research findings)\n- _templates/ (prd, spec, review, research, handoff)`;
65
- },
63
+ // No file found in any location
64
+ return `Memory file '${normalizedFile}.md' not found in any location.\nSearched:\n- ${locations.join("\n- ")}\n\nStructure:\n- handoffs/YYYY-MM-DD-phase (phase transitions)\n- research/YYYY-MM-DD-topic (research findings)\n- _templates/ (prd, spec, review, research, handoff)`;
65
+ },
66
66
  });
@@ -1,61 +1,61 @@
1
+ import path from "path";
1
2
  import { tool } from "@opencode-ai/plugin";
2
3
  import fs from "fs/promises";
3
- import path from "path";
4
4
 
5
5
  export default tool({
6
- description:
7
- "Update memory files with new learnings, progress, or context. Appends or replaces content based on mode. Supports subdirectories for organization (e.g., 'research/opencode-sessions' creates .opencode/memory/research/opencode-sessions.md).",
8
- args: {
9
- file: tool.schema
10
- .string()
11
- .describe(
12
- "Memory file to update: handoffs/YYYY-MM-DD-phase, research/YYYY-MM-DD-topic. Use _templates/ for reference only.",
13
- ),
14
- content: tool.schema
15
- .string()
16
- .describe("Content to write or append to the memory file"),
17
- mode: tool.schema
18
- .string()
19
- .optional()
20
- .default("replace")
21
- .describe(
22
- "Update mode: 'replace' (overwrite file) or 'append' (add to end).",
23
- ),
24
- },
25
- execute: async (args: { file: string; content: string; mode?: string }) => {
26
- // Always write to project memory (.opencode/memory/)
27
- const memoryDir = path.join(process.cwd(), ".opencode/memory");
6
+ description:
7
+ "Update memory files with new learnings, progress, or context. Appends or replaces content based on mode. Supports subdirectories for organization (e.g., 'research/opencode-sessions' creates .opencode/memory/research/opencode-sessions.md).",
8
+ args: {
9
+ file: tool.schema
10
+ .string()
11
+ .describe(
12
+ "Memory file to update: handoffs/YYYY-MM-DD-phase, research/YYYY-MM-DD-topic. Use _templates/ for reference only.",
13
+ ),
14
+ content: tool.schema
15
+ .string()
16
+ .describe("Content to write or append to the memory file"),
17
+ mode: tool.schema
18
+ .string()
19
+ .optional()
20
+ .default("replace")
21
+ .describe(
22
+ "Update mode: 'replace' (overwrite file) or 'append' (add to end).",
23
+ ),
24
+ },
25
+ execute: async (args: { file: string; content: string; mode?: string }) => {
26
+ // Always write to project memory (.opencode/memory/)
27
+ const memoryDir = path.join(process.cwd(), ".opencode/memory");
28
28
 
29
- // Normalize file path: strip existing .md extension, handle subdirectories
30
- let normalizedFile = args.file.replace(/\.md$/i, ""); // Remove .md if present
31
- const filePath = path.join(memoryDir, `${normalizedFile}.md`);
32
- const mode = args.mode || "replace";
29
+ // Normalize file path: strip existing .md extension, handle subdirectories
30
+ const normalizedFile = args.file.replace(/\.md$/i, ""); // Remove .md if present
31
+ const filePath = path.join(memoryDir, `${normalizedFile}.md`);
32
+ const mode = args.mode || "replace";
33
33
 
34
- try {
35
- // Ensure parent directory exists (handles subdirectories)
36
- const fileDir = path.dirname(filePath);
37
- await fs.mkdir(fileDir, { recursive: true });
34
+ try {
35
+ // Ensure parent directory exists (handles subdirectories)
36
+ const fileDir = path.dirname(filePath);
37
+ await fs.mkdir(fileDir, { recursive: true });
38
38
 
39
- if (mode === "append") {
40
- const timestamp = new Date().toISOString();
41
- const appendContent = `\n\n---\n**Updated:** ${timestamp}\n\n${args.content}`;
42
- await fs.appendFile(filePath, appendContent, "utf-8");
43
- return `Successfully appended to ${normalizedFile}.md\n[Written to: ${filePath}]`;
44
- } else {
45
- // Replace mode - update timestamp
46
- const timestamp = new Date().toISOString();
47
- const updatedContent = args.content.replace(
48
- /\*\*Last Updated:\*\* \[Timestamp\]/,
49
- `**Last Updated:** ${timestamp}`,
50
- );
51
- await fs.writeFile(filePath, updatedContent, "utf-8");
52
- return `Successfully updated ${normalizedFile}.md\n[Written to: ${filePath}]`;
53
- }
54
- } catch (error) {
55
- if (error instanceof Error) {
56
- return `Error updating memory: ${error.message}`;
57
- }
58
- return `Unknown error updating memory file`;
59
- }
60
- },
39
+ if (mode === "append") {
40
+ const timestamp = new Date().toISOString();
41
+ const appendContent = `\n\n---\n**Updated:** ${timestamp}\n\n${args.content}`;
42
+ await fs.appendFile(filePath, appendContent, "utf-8");
43
+ return `Successfully appended to ${normalizedFile}.md\n[Written to: ${filePath}]`;
44
+ } else {
45
+ // Replace mode - update timestamp
46
+ const timestamp = new Date().toISOString();
47
+ const updatedContent = args.content.replace(
48
+ /\*\*Last Updated:\*\* \[Timestamp\]/,
49
+ `**Last Updated:** ${timestamp}`,
50
+ );
51
+ await fs.writeFile(filePath, updatedContent, "utf-8");
52
+ return `Successfully updated ${normalizedFile}.md\n[Written to: ${filePath}]`;
53
+ }
54
+ } catch (error) {
55
+ if (error instanceof Error) {
56
+ return `Error updating memory: ${error.message}`;
57
+ }
58
+ return `Unknown error updating memory file`;
59
+ }
60
+ },
61
61
  });
@@ -1,21 +1,21 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "allowSyntheticDefaultImports": true,
7
- "esModuleInterop": true,
8
- "allowJs": true,
9
- "strict": false,
10
- "noEmit": true,
11
- "declaration": true,
12
- "outDir": "./dist",
13
- "rootDir": "./",
14
- "skipLibCheck": true,
15
- "forceConsistentCasingInFileNames": true,
16
- "resolveJsonModule": true,
17
- "types": ["node"]
18
- },
19
- "include": ["plugin/**/*.ts", "tool/**/*.ts", "agent/**/*.ts", "*.ts"],
20
- "exclude": ["node_modules", "dist", "**/*.js", "**/*.test.ts"]
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "allowSyntheticDefaultImports": true,
7
+ "esModuleInterop": true,
8
+ "allowJs": true,
9
+ "strict": false,
10
+ "noEmit": true,
11
+ "declaration": true,
12
+ "outDir": "./dist",
13
+ "rootDir": "./",
14
+ "skipLibCheck": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "resolveJsonModule": true,
17
+ "types": ["node"]
18
+ },
19
+ "include": ["plugin/**/*.ts", "tool/**/*.ts", "agent/**/*.ts", "*.ts"],
20
+ "exclude": ["node_modules", "dist", "**/*.js", "**/*.test.ts"]
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencodekit",
3
- "version": "0.12.5",
3
+ "version": "0.12.7",
4
4
  "description": "CLI tool for bootstrapping and managing OpenCodeKit projects",
5
5
  "type": "module",
6
6
  "repository": {