wm-opencode 0.1.0

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/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "wm-opencode",
3
+ "version": "0.1.0",
4
+ "description": "wm working memory plugin for OpenCode",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "bun build src/index.ts --outdir dist --target bun",
10
+ "dev": "bun run src/index.ts",
11
+ "test": "bun test",
12
+ "typecheck": "tsc --noEmit"
13
+ },
14
+ "dependencies": {},
15
+ "devDependencies": {
16
+ "@opencode-ai/plugin": "^1.0.61",
17
+ "@types/bun": "^1.2.4",
18
+ "typescript": "^5.7.0"
19
+ },
20
+ "peerDependencies": {
21
+ "bun": ">=1.0.0"
22
+ }
23
+ }
package/src/index.ts ADDED
@@ -0,0 +1,151 @@
1
+ /**
2
+ * WM OpenCode Plugin
3
+ *
4
+ * Working memory for OpenCode. The LLM calls wm tools when it needs context.
5
+ * Similar to superego's "pull mode" - model-driven rather than automatic.
6
+ */
7
+
8
+ import type { Plugin } from "@opencode-ai/plugin";
9
+ import { tool } from "@opencode-ai/plugin";
10
+ import { existsSync, appendFileSync } from "fs";
11
+ import { join } from "path";
12
+ import { spawnSync } from "child_process";
13
+
14
+ // Log to file since OpenCode is a TUI
15
+ function log(wmDir: string, message: string): void {
16
+ const timestamp = new Date().toISOString();
17
+ const logFile = join(wmDir, "hook.log");
18
+ try {
19
+ appendFileSync(logFile, `${timestamp} ${message}\n`);
20
+ } catch {
21
+ // Ignore log failures
22
+ }
23
+ }
24
+
25
+ const WM_DIR = ".wm";
26
+ const WM_CONTRACT = `WM ACTIVE: This project uses wm (working memory) to accumulate tacit knowledge across sessions. When you need relevant context for your work, use the wm tool to retrieve it. If you don't know what/why something works or need background, encourage the user to prep a dive pack via wm. Key commands: wm show state (view knowledge), wm compile (get relevant context for current task).`;
27
+
28
+ // Check if wm binary is available
29
+ function checkWmBinary(): { available: boolean; version?: string } {
30
+ try {
31
+ const result = spawnSync("wm", ["--version"], { encoding: "utf-8" });
32
+ if (result.status === 0) {
33
+ return { available: true, version: result.stdout.trim() };
34
+ }
35
+ return { available: false };
36
+ } catch {
37
+ return { available: false };
38
+ }
39
+ }
40
+
41
+ // Execute wm command
42
+ function executeWmCommand(
43
+ directory: string,
44
+ args: string[]
45
+ ): { success: boolean; output: string; error?: string } {
46
+ try {
47
+ const result = spawnSync("wm", args, {
48
+ cwd: directory,
49
+ encoding: "utf-8",
50
+ timeout: 30000,
51
+ maxBuffer: 10 * 1024 * 1024,
52
+ });
53
+
54
+ if (result.status === 0) {
55
+ return { success: true, output: result.stdout || "Command completed successfully" };
56
+ } else {
57
+ return {
58
+ success: false,
59
+ output: result.stdout || "",
60
+ error: result.stderr || `Command failed with exit code ${result.status}`,
61
+ };
62
+ }
63
+ } catch (e) {
64
+ return { success: false, output: "", error: `Failed to execute command: ${e}` };
65
+ }
66
+ }
67
+
68
+ export const WM: Plugin = async ({ directory }) => {
69
+ const wmDir = join(directory, WM_DIR);
70
+ const initialized = existsSync(wmDir);
71
+ const wmCheck = checkWmBinary();
72
+
73
+ if (initialized && wmCheck.available) {
74
+ log(wmDir, `Plugin loaded, binary: ${wmCheck.version}`);
75
+ }
76
+
77
+ return {
78
+ // Inject contract into system prompt (soft hint for model to use tool)
79
+ "experimental.chat.system.transform": async (_input, output) => {
80
+ if (initialized && wmCheck.available) {
81
+ output.system.push(WM_CONTRACT);
82
+ }
83
+ },
84
+
85
+ tool: {
86
+ wm: tool({
87
+ description: "Manage working memory. Commands: init, status, show <state|working|sessions>, compile, distill, compress, pause, resume. Use 'compile' to get relevant context for current task.",
88
+ args: {
89
+ command: tool.schema
90
+ .enum(["init", "status", "show", "compile", "distill", "compress", "pause", "resume"])
91
+ .default("status"),
92
+ subcommand: tool.schema.string().optional(),
93
+ options: tool.schema.string().optional(),
94
+ },
95
+ async execute({ command, subcommand, options }) {
96
+ // Check if wm binary is available
97
+ if (!wmCheck.available) {
98
+ return `wm binary not found. Install with:\n\n` +
99
+ ` brew tap cloud-atlas-ai/wm\n` +
100
+ ` brew install wm\n\n` +
101
+ `Or:\n\n` +
102
+ ` cargo install wm\n\n` +
103
+ `Then restart OpenCode.`;
104
+ }
105
+
106
+ // Build command arguments
107
+ const args: string[] = [command];
108
+ if (subcommand) args.push(subcommand);
109
+ if (options) args.push(...options.split(/\s+/));
110
+
111
+ // Special handling for init
112
+ if (command === "init") {
113
+ if (existsSync(wmDir)) {
114
+ return "wm already initialized in this project.";
115
+ }
116
+ const result = executeWmCommand(directory, args);
117
+ if (result.success) {
118
+ return `wm initialized. Created ${WM_DIR}/ directory.\n\n` +
119
+ `Working memory will accumulate knowledge from your sessions.\n\n` +
120
+ `Key commands:\n` +
121
+ ` wm show state - View accumulated knowledge\n` +
122
+ ` wm compile - Get relevant context for current task\n` +
123
+ ` wm distill - Extract knowledge from recent work`;
124
+ }
125
+ return `Failed to initialize wm: ${result.error || result.output}`;
126
+ }
127
+
128
+ // Check if initialized for other commands
129
+ if (!existsSync(wmDir)) {
130
+ return "wm not initialized. Use 'wm init' first.";
131
+ }
132
+
133
+ // Execute command
134
+ const result = executeWmCommand(directory, args);
135
+
136
+ if (result.success) {
137
+ // Special message for compile to explain output
138
+ if (command === "compile") {
139
+ return result.output + "\n\n(This is the relevant context from working memory for your current task)";
140
+ }
141
+ return result.output;
142
+ } else {
143
+ return `Error: ${result.error}\n\n${result.output}`;
144
+ }
145
+ },
146
+ }),
147
+ },
148
+ };
149
+ };
150
+
151
+ export default WM;
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "declaration": true,
10
+ "outDir": "dist",
11
+ "rootDir": "src",
12
+ "types": ["bun-types"]
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }