@vuau/agent-memory 0.1.1 → 0.2.1

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/README.md CHANGED
@@ -82,9 +82,18 @@ npx @vuau/agent-memory init
82
82
  npx @vuau/agent-memory init --force # Overwrite existing files
83
83
  npx @vuau/agent-memory init --name "My App" # Custom project name
84
84
  npx @vuau/agent-memory init --no-copilot # Skip copilot-instructions.md
85
+ npx @vuau/agent-memory init --opencode # Wire up OpenCode plugin
85
86
  npx @vuau/agent-memory doctor # Validate structure
86
87
  ```
87
88
 
89
+ #### `--opencode` flag
90
+
91
+ Wires up the OpenCode plugin automatically:
92
+ - Creates/updates `.opencode/package.json` with `@vuau/agent-memory` dependency
93
+ - Creates/updates `opencode.json` with `"plugin": ["@vuau/agent-memory"]`
94
+
95
+ After running, restart OpenCode to activate the plugin.
96
+
88
97
  ## How It Works
89
98
 
90
99
  ### For AI Agents
package/dist/bin/cli.js CHANGED
@@ -15,15 +15,12 @@ var COPILOT_INSTRUCTIONS = ".github/copilot-instructions.md";
15
15
 
16
16
  // src/core/scaffold.ts
17
17
  function getTemplatesDir() {
18
- try {
19
- const thisDir = dirname(fileURLToPath(import.meta.url));
20
- const candidate = resolve(thisDir, "../../templates");
21
- if (existsSync(candidate)) return candidate;
22
- } catch {
23
- }
24
- const candidate2 = resolve(__dirname, "../../templates");
25
- if (existsSync(candidate2)) return candidate2;
26
- throw new Error("Cannot locate templates directory");
18
+ const thisDir = dirname(fileURLToPath(import.meta.url));
19
+ const fromSource = resolve(thisDir, "../../templates");
20
+ if (existsSync(fromSource)) return fromSource;
21
+ const fromDist = resolve(thisDir, "../templates");
22
+ if (existsSync(fromDist)) return fromDist;
23
+ throw new Error(`Cannot locate templates directory (checked ${fromSource} and ${fromDist})`);
27
24
  }
28
25
  var TEMPLATES_DIR = getTemplatesDir();
29
26
  function readTemplate(name) {
@@ -79,8 +76,57 @@ function scaffold(projectDir, config = {}, force = false) {
79
76
  writeFileSync(specKeep, "");
80
77
  result.created.push(`${SPEC_DIR}/.gitkeep`);
81
78
  }
79
+ if (config.opencode) {
80
+ scaffoldOpenCode(projectDir, result, force);
81
+ }
82
82
  return result;
83
83
  }
84
+ function scaffoldOpenCode(projectDir, result, force) {
85
+ const PACKAGE_NAME = "@vuau/agent-memory";
86
+ const opencodePkgPath = join(projectDir, ".opencode", "package.json");
87
+ const opencodeDir = join(projectDir, ".opencode");
88
+ if (!existsSync(opencodeDir)) {
89
+ mkdirSync(opencodeDir, { recursive: true });
90
+ }
91
+ if (!existsSync(opencodePkgPath)) {
92
+ writeFileSync(
93
+ opencodePkgPath,
94
+ JSON.stringify({ dependencies: { [PACKAGE_NAME]: "latest" } }, null, 2) + "\n"
95
+ );
96
+ result.created.push(".opencode/package.json");
97
+ } else {
98
+ const pkg = JSON.parse(readFileSync(opencodePkgPath, "utf-8"));
99
+ const deps = pkg.dependencies || {};
100
+ if (!deps[PACKAGE_NAME] || force) {
101
+ deps[PACKAGE_NAME] = "latest";
102
+ pkg.dependencies = deps;
103
+ writeFileSync(opencodePkgPath, JSON.stringify(pkg, null, 2) + "\n");
104
+ if (!deps[PACKAGE_NAME]) {
105
+ result.created.push(".opencode/package.json");
106
+ }
107
+ } else {
108
+ result.skipped.push(".opencode/package.json");
109
+ }
110
+ }
111
+ const opencodeJsonPath = join(projectDir, "opencode.json");
112
+ if (!existsSync(opencodeJsonPath)) {
113
+ writeFileSync(
114
+ opencodeJsonPath,
115
+ JSON.stringify({ plugin: [PACKAGE_NAME] }, null, 2) + "\n"
116
+ );
117
+ result.created.push("opencode.json");
118
+ } else {
119
+ const config = JSON.parse(readFileSync(opencodeJsonPath, "utf-8"));
120
+ const plugins = config.plugin || [];
121
+ if (!plugins.includes(PACKAGE_NAME)) {
122
+ config.plugin = [...plugins, PACKAGE_NAME];
123
+ writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
124
+ result.created.push("opencode.json (merged plugin)");
125
+ } else {
126
+ result.skipped.push("opencode.json");
127
+ }
128
+ }
129
+ }
84
130
  function guessProjectName(dir) {
85
131
  const pkgPath = join(dir, "package.json");
86
132
  if (existsSync(pkgPath)) {
@@ -151,6 +197,53 @@ function doctor(projectDir) {
151
197
  });
152
198
  }
153
199
  }
200
+ const opencodePkgPath = join2(projectDir, ".opencode", "package.json");
201
+ const opencodeJsonPath = join2(projectDir, "opencode.json");
202
+ const opencodeExists = existsSync2(join2(projectDir, ".opencode"));
203
+ if (opencodeExists) {
204
+ if (!existsSync2(opencodePkgPath)) {
205
+ issues.push({
206
+ level: "warning",
207
+ file: ".opencode/package.json",
208
+ message: "Missing \u2014 run 'agent-memory init --opencode' to wire up the plugin"
209
+ });
210
+ } else {
211
+ try {
212
+ const pkg = JSON.parse(readFileSync2(opencodePkgPath, "utf-8"));
213
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
214
+ if (!deps["@vuau/agent-memory"]) {
215
+ issues.push({
216
+ level: "warning",
217
+ file: ".opencode/package.json",
218
+ message: "@vuau/agent-memory not in dependencies \u2014 run 'agent-memory init --opencode'"
219
+ });
220
+ }
221
+ } catch {
222
+ issues.push({ level: "warning", file: ".opencode/package.json", message: "Invalid JSON" });
223
+ }
224
+ }
225
+ if (!existsSync2(opencodeJsonPath)) {
226
+ issues.push({
227
+ level: "warning",
228
+ file: "opencode.json",
229
+ message: "Missing \u2014 run 'agent-memory init --opencode' to wire up the plugin"
230
+ });
231
+ } else {
232
+ try {
233
+ const config = JSON.parse(readFileSync2(opencodeJsonPath, "utf-8"));
234
+ const plugins = config.plugin || [];
235
+ if (!plugins.includes("@vuau/agent-memory")) {
236
+ issues.push({
237
+ level: "warning",
238
+ file: "opencode.json",
239
+ message: "@vuau/agent-memory not in plugin array \u2014 run 'agent-memory init --opencode'"
240
+ });
241
+ }
242
+ } catch {
243
+ issues.push({ level: "warning", file: "opencode.json", message: "Invalid JSON" });
244
+ }
245
+ }
246
+ }
154
247
  return { ok: issues.filter((i) => i.level === "error").length === 0, issues };
155
248
  }
156
249
 
@@ -170,18 +263,21 @@ Options (init):
170
263
  --force Overwrite existing files
171
264
  --name <name> Project name (default: from package.json)
172
265
  --no-copilot Skip .github/copilot-instructions.md
266
+ --opencode Wire up OpenCode plugin (.opencode/package.json + opencode.json)
173
267
  `);
174
268
  }
175
269
  function runInit() {
176
270
  const force = args.includes("--force");
177
271
  const noCopilot = args.includes("--no-copilot");
272
+ const opencode = args.includes("--opencode");
178
273
  const nameIdx = args.indexOf("--name");
179
274
  const projectName = nameIdx !== -1 ? args[nameIdx + 1] : void 0;
180
275
  const cwd = process.cwd();
181
276
  console.log(`Initializing agent memory in ${cwd}...`);
182
277
  const result = scaffold(cwd, {
183
278
  projectName,
184
- copilotInstructions: !noCopilot
279
+ copilotInstructions: !noCopilot,
280
+ opencode
185
281
  }, force);
186
282
  if (result.created.length > 0) {
187
283
  console.log("\nCreated:");
@@ -201,7 +297,11 @@ function runInit() {
201
297
  console.log("\nNext steps:");
202
298
  console.log(" 1. Edit AGENTS.md \u2014 add your project-specific rules");
203
299
  console.log(" 2. Add spec files to .agents/spec/ for detailed documentation");
204
- console.log(' 3. For OpenCode: add to opencode.json \u2192 { "plugin": ["@vuau/agent-memory"] }');
300
+ if (opencode) {
301
+ console.log(" 3. Restart OpenCode to activate the plugin");
302
+ } else {
303
+ console.log(" 3. For OpenCode: run with --opencode flag to wire up the plugin");
304
+ }
205
305
  console.log("");
206
306
  }
207
307
  function runDoctor() {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/opencode/plugin.ts
2
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2
+ import { existsSync as existsSync2 } from "fs";
3
3
  import { resolve as resolve2 } from "path";
4
4
 
5
5
  // src/core/scaffold.ts
@@ -17,15 +17,12 @@ var COPILOT_INSTRUCTIONS = ".github/copilot-instructions.md";
17
17
 
18
18
  // src/core/scaffold.ts
19
19
  function getTemplatesDir() {
20
- try {
21
- const thisDir = dirname(fileURLToPath(import.meta.url));
22
- const candidate = resolve(thisDir, "../../templates");
23
- if (existsSync(candidate)) return candidate;
24
- } catch {
25
- }
26
- const candidate2 = resolve(__dirname, "../../templates");
27
- if (existsSync(candidate2)) return candidate2;
28
- throw new Error("Cannot locate templates directory");
20
+ const thisDir = dirname(fileURLToPath(import.meta.url));
21
+ const fromSource = resolve(thisDir, "../../templates");
22
+ if (existsSync(fromSource)) return fromSource;
23
+ const fromDist = resolve(thisDir, "../templates");
24
+ if (existsSync(fromDist)) return fromDist;
25
+ throw new Error(`Cannot locate templates directory (checked ${fromSource} and ${fromDist})`);
29
26
  }
30
27
  var TEMPLATES_DIR = getTemplatesDir();
31
28
  function readTemplate(name) {
@@ -81,8 +78,57 @@ function scaffold(projectDir, config = {}, force = false) {
81
78
  writeFileSync(specKeep, "");
82
79
  result.created.push(`${SPEC_DIR}/.gitkeep`);
83
80
  }
81
+ if (config.opencode) {
82
+ scaffoldOpenCode(projectDir, result, force);
83
+ }
84
84
  return result;
85
85
  }
86
+ function scaffoldOpenCode(projectDir, result, force) {
87
+ const PACKAGE_NAME = "@vuau/agent-memory";
88
+ const opencodePkgPath = join(projectDir, ".opencode", "package.json");
89
+ const opencodeDir = join(projectDir, ".opencode");
90
+ if (!existsSync(opencodeDir)) {
91
+ mkdirSync(opencodeDir, { recursive: true });
92
+ }
93
+ if (!existsSync(opencodePkgPath)) {
94
+ writeFileSync(
95
+ opencodePkgPath,
96
+ JSON.stringify({ dependencies: { [PACKAGE_NAME]: "latest" } }, null, 2) + "\n"
97
+ );
98
+ result.created.push(".opencode/package.json");
99
+ } else {
100
+ const pkg = JSON.parse(readFileSync(opencodePkgPath, "utf-8"));
101
+ const deps = pkg.dependencies || {};
102
+ if (!deps[PACKAGE_NAME] || force) {
103
+ deps[PACKAGE_NAME] = "latest";
104
+ pkg.dependencies = deps;
105
+ writeFileSync(opencodePkgPath, JSON.stringify(pkg, null, 2) + "\n");
106
+ if (!deps[PACKAGE_NAME]) {
107
+ result.created.push(".opencode/package.json");
108
+ }
109
+ } else {
110
+ result.skipped.push(".opencode/package.json");
111
+ }
112
+ }
113
+ const opencodeJsonPath = join(projectDir, "opencode.json");
114
+ if (!existsSync(opencodeJsonPath)) {
115
+ writeFileSync(
116
+ opencodeJsonPath,
117
+ JSON.stringify({ plugin: [PACKAGE_NAME] }, null, 2) + "\n"
118
+ );
119
+ result.created.push("opencode.json");
120
+ } else {
121
+ const config = JSON.parse(readFileSync(opencodeJsonPath, "utf-8"));
122
+ const plugins = config.plugin || [];
123
+ if (!plugins.includes(PACKAGE_NAME)) {
124
+ config.plugin = [...plugins, PACKAGE_NAME];
125
+ writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
126
+ result.created.push("opencode.json (merged plugin)");
127
+ } else {
128
+ result.skipped.push("opencode.json");
129
+ }
130
+ }
131
+ }
86
132
  function guessProjectName(dir) {
87
133
  const pkgPath = join(dir, "package.json");
88
134
  if (existsSync(pkgPath)) {
@@ -102,67 +148,91 @@ var MemoryLifecyclePlugin = async ({ client, directory }) => {
102
148
  const agentsFile = resolve2(directory, AGENTS_MD);
103
149
  let editCount = 0;
104
150
  let specFilesEdited = [];
151
+ let sessionStartTime = null;
152
+ const showToast = async (message, variant = "info") => {
153
+ try {
154
+ await client.tui.showToast({
155
+ body: { message, variant }
156
+ });
157
+ } catch {
158
+ await log("info", message);
159
+ }
160
+ };
161
+ const log = async (level, message, extra) => {
162
+ try {
163
+ await client.app.log({
164
+ body: {
165
+ service: "agent-memory",
166
+ level,
167
+ message,
168
+ extra
169
+ }
170
+ });
171
+ } catch {
172
+ }
173
+ };
105
174
  return {
106
175
  event: async ({ event }) => {
107
176
  if (event.type === "session.created") {
177
+ sessionStartTime = Date.now();
178
+ editCount = 0;
179
+ specFilesEdited = [];
108
180
  const hasAgentsMd = existsSync2(agentsFile);
109
181
  const hasMemory = existsSync2(memoryFile);
182
+ const hasTasks = existsSync2(tasksFile);
110
183
  if (!hasMemory && hasAgentsMd) {
111
184
  try {
112
185
  const result = scaffold(directory, { copilotInstructions: false });
113
186
  if (result.created.length > 0) {
114
- await client.tui.showToast({
115
- body: {
116
- message: `Agent memory initialized: ${result.created.join(", ")}`,
117
- variant: "success"
118
- }
119
- });
187
+ await showToast(`Agent memory initialized: ${result.created.join(", ")}`, "success");
188
+ await log("info", "Auto-scaffolded .agents/ structure", { created: result.created });
120
189
  }
121
190
  } catch (err) {
122
- await client.app.log({
123
- body: {
124
- service: "agent-memory",
125
- level: "warn",
126
- message: `Auto-scaffold failed: ${err}`
127
- }
128
- });
191
+ await log("warn", `Auto-scaffold failed: ${err}`);
129
192
  }
130
193
  }
131
- editCount = 0;
132
- specFilesEdited = [];
133
- await client.app.log({
134
- body: {
135
- service: "agent-memory",
136
- level: "info",
137
- message: `Memory: ${hasMemory}, Tasks: ${existsSync2(tasksFile)}`
138
- }
194
+ await log("debug", "Session started", {
195
+ hasAgentsMd,
196
+ hasMemory,
197
+ hasTasks,
198
+ directory
139
199
  });
140
200
  }
141
- if (event.type === "session.idle" && editCount >= 3) {
142
- if (existsSync2(tasksFile)) {
143
- const content = readFileSync2(tasksFile, "utf-8");
144
- const inProgressSection = content.split("## In Progress")[1];
145
- if (inProgressSection?.trim()) {
146
- await client.tui.showToast({
147
- body: {
148
- message: `${editCount} edits this session. Update .agents/TASKS.md before ending.`,
149
- variant: "info"
150
- }
151
- });
152
- }
201
+ if (event.type === "session.idle") {
202
+ if (editCount >= 3 && existsSync2(tasksFile)) {
203
+ const sessionDuration = sessionStartTime ? Math.round((Date.now() - sessionStartTime) / 1e3 / 60) : 0;
204
+ await showToast(
205
+ `${editCount} file edits (${sessionDuration}m). Consider updating .agents/TASKS.md`,
206
+ "info"
207
+ );
208
+ await log("info", "Session idle with significant edits", {
209
+ editCount,
210
+ specFilesEdited,
211
+ sessionDurationMinutes: sessionDuration
212
+ });
153
213
  }
154
214
  }
155
215
  },
156
- "tool.execute.after": async (input) => {
157
- if (input.tool === "edit" || input.tool === "write") {
216
+ // ─────────────────────────────────────────────────────────────
217
+ // TOOL EXECUTE AFTER track edits
218
+ // ─────────────────────────────────────────────────────────────
219
+ "tool.execute.after": async (input, _output) => {
220
+ const toolName = input.tool;
221
+ const filePath = input.args?.filePath || "";
222
+ if (toolName === "edit" || toolName === "write") {
158
223
  editCount++;
159
- const filePath = input.args?.filePath || "";
160
224
  if (filePath.includes(SPEC_DIR)) {
161
225
  const shortPath = filePath.split(SPEC_DIR + "/").pop() || filePath;
162
226
  if (!specFilesEdited.includes(shortPath)) {
163
227
  specFilesEdited.push(shortPath);
164
228
  }
165
229
  }
230
+ if (editCount % 5 === 0) {
231
+ await log("debug", `Edit milestone: ${editCount} files modified`, {
232
+ latestFile: filePath,
233
+ specFilesEdited
234
+ });
235
+ }
166
236
  }
167
237
  }
168
238
  };
@@ -329,6 +399,53 @@ function doctor(projectDir) {
329
399
  });
330
400
  }
331
401
  }
402
+ const opencodePkgPath = join4(projectDir, ".opencode", "package.json");
403
+ const opencodeJsonPath = join4(projectDir, "opencode.json");
404
+ const opencodeExists = existsSync5(join4(projectDir, ".opencode"));
405
+ if (opencodeExists) {
406
+ if (!existsSync5(opencodePkgPath)) {
407
+ issues.push({
408
+ level: "warning",
409
+ file: ".opencode/package.json",
410
+ message: "Missing \u2014 run 'agent-memory init --opencode' to wire up the plugin"
411
+ });
412
+ } else {
413
+ try {
414
+ const pkg = JSON.parse(readFileSync5(opencodePkgPath, "utf-8"));
415
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
416
+ if (!deps["@vuau/agent-memory"]) {
417
+ issues.push({
418
+ level: "warning",
419
+ file: ".opencode/package.json",
420
+ message: "@vuau/agent-memory not in dependencies \u2014 run 'agent-memory init --opencode'"
421
+ });
422
+ }
423
+ } catch {
424
+ issues.push({ level: "warning", file: ".opencode/package.json", message: "Invalid JSON" });
425
+ }
426
+ }
427
+ if (!existsSync5(opencodeJsonPath)) {
428
+ issues.push({
429
+ level: "warning",
430
+ file: "opencode.json",
431
+ message: "Missing \u2014 run 'agent-memory init --opencode' to wire up the plugin"
432
+ });
433
+ } else {
434
+ try {
435
+ const config = JSON.parse(readFileSync5(opencodeJsonPath, "utf-8"));
436
+ const plugins = config.plugin || [];
437
+ if (!plugins.includes("@vuau/agent-memory")) {
438
+ issues.push({
439
+ level: "warning",
440
+ file: "opencode.json",
441
+ message: "@vuau/agent-memory not in plugin array \u2014 run 'agent-memory init --opencode'"
442
+ });
443
+ }
444
+ } catch {
445
+ issues.push({ level: "warning", file: "opencode.json", message: "Invalid JSON" });
446
+ }
447
+ }
448
+ }
332
449
  return { ok: issues.filter((i) => i.level === "error").length === 0, issues };
333
450
  }
334
451
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuau/agent-memory",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Structured AI memory for codebases — OpenCode plugin + scaffolding CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",