@vuau/agent-memory 0.2.1 → 0.3.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/dist/index.js CHANGED
@@ -1,7 +1,3 @@
1
- // src/opencode/plugin.ts
2
- import { existsSync as existsSync2 } from "fs";
3
- import { resolve as resolve2 } from "path";
4
-
5
1
  // src/core/scaffold.ts
6
2
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
7
3
  import { join, resolve, dirname } from "path";
@@ -11,9 +7,12 @@ import { fileURLToPath } from "url";
11
7
  var AGENTS_DIR = ".agents";
12
8
  var SPEC_DIR = ".agents/spec";
13
9
  var MEMORY_FILE = ".agents/MEMORY.md";
10
+ var MEMORY_DETAIL_FILE = ".agents/MEMORY-DETAIL.md";
14
11
  var TASKS_FILE = ".agents/TASKS.md";
15
12
  var AGENTS_MD = "AGENTS.md";
16
13
  var COPILOT_INSTRUCTIONS = ".github/copilot-instructions.md";
14
+ var CURSOR_RULES = ".cursorrules";
15
+ var WINDSURF_RULES = ".windsurfrules";
17
16
 
18
17
  // src/core/scaffold.ts
19
18
  function getTemplatesDir() {
@@ -27,6 +26,9 @@ function getTemplatesDir() {
27
26
  var TEMPLATES_DIR = getTemplatesDir();
28
27
  function readTemplate(name) {
29
28
  const templatePath = join(TEMPLATES_DIR, name);
29
+ if (!existsSync(templatePath)) {
30
+ throw new Error(`Template not found: ${templatePath}`);
31
+ }
30
32
  return readFileSync(templatePath, "utf-8");
31
33
  }
32
34
  function applyVars(content, vars) {
@@ -36,15 +38,21 @@ function applyVars(content, vars) {
36
38
  }
37
39
  return result;
38
40
  }
39
- function scaffold(projectDir, config = {}, force = false) {
41
+ function scaffold(projectDir, options = {}) {
40
42
  const result = { created: [], skipped: [] };
41
- const projectName = config.projectName || guessProjectName(projectDir);
43
+ const projectName = options.projectName || guessProjectName(projectDir);
42
44
  const vars = { PROJECT_NAME: projectName };
45
+ const force = options.force || false;
46
+ const hasAnyIde = options.opencode || options.copilot || options.cursor || options.windsurf;
47
+ const useOpencode = hasAnyIde ? options.opencode : true;
48
+ const useCopilot = options.copilot || false;
49
+ const useCursor = options.cursor || false;
50
+ const useWindsurf = options.windsurf || false;
43
51
  const dirs = [
44
52
  join(projectDir, AGENTS_DIR),
45
53
  join(projectDir, SPEC_DIR)
46
54
  ];
47
- if (config.copilotInstructions !== false) {
55
+ if (useCopilot) {
48
56
  dirs.push(join(projectDir, ".github"));
49
57
  }
50
58
  for (const dir of dirs) {
@@ -52,18 +60,12 @@ function scaffold(projectDir, config = {}, force = false) {
52
60
  mkdirSync(dir, { recursive: true });
53
61
  }
54
62
  }
55
- const files = [
56
- { target: AGENTS_MD, template: "AGENTS.md" },
63
+ const coreFiles = [
57
64
  { target: MEMORY_FILE, template: "MEMORY.md" },
65
+ { target: MEMORY_DETAIL_FILE, template: "MEMORY-DETAIL.md" },
58
66
  { target: TASKS_FILE, template: "TASKS.md" }
59
67
  ];
60
- if (config.copilotInstructions !== false) {
61
- files.push({
62
- target: COPILOT_INSTRUCTIONS,
63
- template: "copilot-instructions.md"
64
- });
65
- }
66
- for (const { target, template } of files) {
68
+ for (const { target, template } of coreFiles) {
67
69
  const targetPath = join(projectDir, target);
68
70
  if (existsSync(targetPath) && !force) {
69
71
  result.skipped.push(target);
@@ -78,56 +80,51 @@ function scaffold(projectDir, config = {}, force = false) {
78
80
  writeFileSync(specKeep, "");
79
81
  result.created.push(`${SPEC_DIR}/.gitkeep`);
80
82
  }
81
- if (config.opencode) {
82
- scaffoldOpenCode(projectDir, result, force);
83
+ if (useOpencode) {
84
+ writeFileIfNeeded(
85
+ join(projectDir, AGENTS_MD),
86
+ applyVars(readTemplate("AGENTS.md"), vars),
87
+ AGENTS_MD,
88
+ result,
89
+ force
90
+ );
83
91
  }
84
- return result;
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
+ if (useCopilot) {
93
+ writeFileIfNeeded(
94
+ join(projectDir, COPILOT_INSTRUCTIONS),
95
+ applyVars(readTemplate("copilot-instructions.md"), vars),
96
+ COPILOT_INSTRUCTIONS,
97
+ result,
98
+ force
99
+ );
92
100
  }
93
- if (!existsSync(opencodePkgPath)) {
94
- writeFileSync(
95
- opencodePkgPath,
96
- JSON.stringify({ dependencies: { [PACKAGE_NAME]: "latest" } }, null, 2) + "\n"
101
+ if (useCursor) {
102
+ writeFileIfNeeded(
103
+ join(projectDir, CURSOR_RULES),
104
+ applyVars(readTemplate("cursorrules.md"), vars),
105
+ CURSOR_RULES,
106
+ result,
107
+ force
97
108
  );
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
109
  }
113
- const opencodeJsonPath = join(projectDir, "opencode.json");
114
- if (!existsSync(opencodeJsonPath)) {
115
- writeFileSync(
116
- opencodeJsonPath,
117
- JSON.stringify({ plugin: [PACKAGE_NAME] }, null, 2) + "\n"
110
+ if (useWindsurf) {
111
+ writeFileIfNeeded(
112
+ join(projectDir, WINDSURF_RULES),
113
+ applyVars(readTemplate("windsurfrules.md"), vars),
114
+ WINDSURF_RULES,
115
+ result,
116
+ force
118
117
  );
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
118
  }
119
+ return result;
120
+ }
121
+ function writeFileIfNeeded(targetPath, content, displayName, result, force) {
122
+ if (existsSync(targetPath) && !force) {
123
+ result.skipped.push(displayName);
124
+ return;
125
+ }
126
+ writeFileSync(targetPath, content);
127
+ result.created.push(displayName);
131
128
  }
132
129
  function guessProjectName(dir) {
133
130
  const pkgPath = join(dir, "package.json");
@@ -141,112 +138,15 @@ function guessProjectName(dir) {
141
138
  return dir.split("/").pop() || "Project";
142
139
  }
143
140
 
144
- // src/opencode/plugin.ts
145
- var MemoryLifecyclePlugin = async ({ client, directory }) => {
146
- const memoryFile = resolve2(directory, MEMORY_FILE);
147
- const tasksFile = resolve2(directory, TASKS_FILE);
148
- const agentsFile = resolve2(directory, AGENTS_MD);
149
- let editCount = 0;
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
- };
174
- return {
175
- event: async ({ event }) => {
176
- if (event.type === "session.created") {
177
- sessionStartTime = Date.now();
178
- editCount = 0;
179
- specFilesEdited = [];
180
- const hasAgentsMd = existsSync2(agentsFile);
181
- const hasMemory = existsSync2(memoryFile);
182
- const hasTasks = existsSync2(tasksFile);
183
- if (!hasMemory && hasAgentsMd) {
184
- try {
185
- const result = scaffold(directory, { copilotInstructions: false });
186
- if (result.created.length > 0) {
187
- await showToast(`Agent memory initialized: ${result.created.join(", ")}`, "success");
188
- await log("info", "Auto-scaffolded .agents/ structure", { created: result.created });
189
- }
190
- } catch (err) {
191
- await log("warn", `Auto-scaffold failed: ${err}`);
192
- }
193
- }
194
- await log("debug", "Session started", {
195
- hasAgentsMd,
196
- hasMemory,
197
- hasTasks,
198
- directory
199
- });
200
- }
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
- });
213
- }
214
- }
215
- },
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") {
223
- editCount++;
224
- if (filePath.includes(SPEC_DIR)) {
225
- const shortPath = filePath.split(SPEC_DIR + "/").pop() || filePath;
226
- if (!specFilesEdited.includes(shortPath)) {
227
- specFilesEdited.push(shortPath);
228
- }
229
- }
230
- if (editCount % 5 === 0) {
231
- await log("debug", `Edit milestone: ${editCount} files modified`, {
232
- latestFile: filePath,
233
- specFilesEdited
234
- });
235
- }
236
- }
237
- }
238
- };
239
- };
240
-
241
141
  // src/core/memory.ts
242
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
142
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
243
143
  import { join as join2 } from "path";
244
144
  function appendMemory(projectDir, entry) {
245
145
  const filePath = join2(projectDir, MEMORY_FILE);
246
- if (!existsSync3(filePath)) {
146
+ if (!existsSync2(filePath)) {
247
147
  throw new Error(`${MEMORY_FILE} not found. Run 'agent-memory init' first.`);
248
148
  }
249
- const content = readFileSync3(filePath, "utf-8");
149
+ const content = readFileSync2(filePath, "utf-8");
250
150
  const category = entry.category || "Decisions";
251
151
  const line = `- ${entry.date}: ${entry.content}`;
252
152
  const categoryHeader = `## ${category}`;
@@ -270,8 +170,8 @@ ${line}
270
170
  }
271
171
  function readMemory(projectDir) {
272
172
  const filePath = join2(projectDir, MEMORY_FILE);
273
- if (!existsSync3(filePath)) return {};
274
- const content = readFileSync3(filePath, "utf-8");
173
+ if (!existsSync2(filePath)) return {};
174
+ const content = readFileSync2(filePath, "utf-8");
275
175
  const categories = {};
276
176
  let currentCategory = "_uncategorized";
277
177
  for (const line of content.split("\n")) {
@@ -290,7 +190,7 @@ function readMemory(projectDir) {
290
190
  }
291
191
 
292
192
  // src/core/tasks.ts
293
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
193
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
294
194
  import { join as join3 } from "path";
295
195
  function readTasks(projectDir) {
296
196
  const filePath = join3(projectDir, TASKS_FILE);
@@ -299,8 +199,8 @@ function readTasks(projectDir) {
299
199
  up_next: [],
300
200
  completed: []
301
201
  };
302
- if (!existsSync4(filePath)) return result;
303
- const content = readFileSync4(filePath, "utf-8");
202
+ if (!existsSync3(filePath)) return result;
203
+ const content = readFileSync3(filePath, "utf-8");
304
204
  let currentSection = null;
305
205
  for (const line of content.split("\n")) {
306
206
  if (line.startsWith("## In Progress")) {
@@ -342,7 +242,7 @@ ${tasks.completed.map((t) => `- ${t}`).join("\n") || ""}
342
242
  }
343
243
 
344
244
  // src/core/doctor.ts
345
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
245
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
346
246
  import { join as join4 } from "path";
347
247
  function doctor(projectDir) {
348
248
  const issues = [];
@@ -353,17 +253,17 @@ function doctor(projectDir) {
353
253
  ];
354
254
  for (const { file, desc } of required) {
355
255
  const filePath = join4(projectDir, file);
356
- if (!existsSync5(filePath)) {
256
+ if (!existsSync4(filePath)) {
357
257
  issues.push({ level: "error", file, message: `Missing ${desc}` });
358
258
  }
359
259
  }
360
260
  for (const dir of [AGENTS_DIR, SPEC_DIR]) {
361
- if (!existsSync5(join4(projectDir, dir))) {
261
+ if (!existsSync4(join4(projectDir, dir))) {
362
262
  issues.push({ level: "error", file: dir, message: "Directory missing" });
363
263
  }
364
264
  }
365
265
  const copilotPath = join4(projectDir, COPILOT_INSTRUCTIONS);
366
- if (!existsSync5(copilotPath)) {
266
+ if (!existsSync4(copilotPath)) {
367
267
  issues.push({
368
268
  level: "warning",
369
269
  file: COPILOT_INSTRUCTIONS,
@@ -371,8 +271,8 @@ function doctor(projectDir) {
371
271
  });
372
272
  }
373
273
  const agentsPath = join4(projectDir, AGENTS_MD);
374
- if (existsSync5(agentsPath)) {
375
- const content = readFileSync5(agentsPath, "utf-8");
274
+ if (existsSync4(agentsPath)) {
275
+ const content = readFileSync4(agentsPath, "utf-8");
376
276
  if (!content.includes(".agents/")) {
377
277
  issues.push({
378
278
  level: "warning",
@@ -389,8 +289,8 @@ function doctor(projectDir) {
389
289
  }
390
290
  }
391
291
  const memoryPath = join4(projectDir, MEMORY_FILE);
392
- if (existsSync5(memoryPath)) {
393
- const lines = readFileSync5(memoryPath, "utf-8").split("\n").length;
292
+ if (existsSync4(memoryPath)) {
293
+ const lines = readFileSync4(memoryPath, "utf-8").split("\n").length;
394
294
  if (lines > 150) {
395
295
  issues.push({
396
296
  level: "warning",
@@ -401,9 +301,9 @@ function doctor(projectDir) {
401
301
  }
402
302
  const opencodePkgPath = join4(projectDir, ".opencode", "package.json");
403
303
  const opencodeJsonPath = join4(projectDir, "opencode.json");
404
- const opencodeExists = existsSync5(join4(projectDir, ".opencode"));
304
+ const opencodeExists = existsSync4(join4(projectDir, ".opencode"));
405
305
  if (opencodeExists) {
406
- if (!existsSync5(opencodePkgPath)) {
306
+ if (!existsSync4(opencodePkgPath)) {
407
307
  issues.push({
408
308
  level: "warning",
409
309
  file: ".opencode/package.json",
@@ -411,7 +311,7 @@ function doctor(projectDir) {
411
311
  });
412
312
  } else {
413
313
  try {
414
- const pkg = JSON.parse(readFileSync5(opencodePkgPath, "utf-8"));
314
+ const pkg = JSON.parse(readFileSync4(opencodePkgPath, "utf-8"));
415
315
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
416
316
  if (!deps["@vuau/agent-memory"]) {
417
317
  issues.push({
@@ -424,7 +324,7 @@ function doctor(projectDir) {
424
324
  issues.push({ level: "warning", file: ".opencode/package.json", message: "Invalid JSON" });
425
325
  }
426
326
  }
427
- if (!existsSync5(opencodeJsonPath)) {
327
+ if (!existsSync4(opencodeJsonPath)) {
428
328
  issues.push({
429
329
  level: "warning",
430
330
  file: "opencode.json",
@@ -432,7 +332,7 @@ function doctor(projectDir) {
432
332
  });
433
333
  } else {
434
334
  try {
435
- const config = JSON.parse(readFileSync5(opencodeJsonPath, "utf-8"));
335
+ const config = JSON.parse(readFileSync4(opencodeJsonPath, "utf-8"));
436
336
  const plugins = config.plugin || [];
437
337
  if (!plugins.includes("@vuau/agent-memory")) {
438
338
  issues.push({
@@ -451,11 +351,13 @@ function doctor(projectDir) {
451
351
  export {
452
352
  AGENTS_DIR,
453
353
  AGENTS_MD,
454
- MemoryLifecyclePlugin as AgentMemoryPlugin,
455
354
  COPILOT_INSTRUCTIONS,
355
+ CURSOR_RULES,
356
+ MEMORY_DETAIL_FILE,
456
357
  MEMORY_FILE,
457
358
  SPEC_DIR,
458
359
  TASKS_FILE,
360
+ WINDSURF_RULES,
459
361
  appendMemory,
460
362
  doctor,
461
363
  readMemory,
@@ -253,18 +253,15 @@ This document proposes a generalizable architecture for AI memory across codebas
253
253
 
254
254
  ## Tooling Support
255
255
 
256
- ### OpenCode Plugin (Current)
257
- - ✓ session.created log memory file status
258
- - ✓ tool.execute.after track spec file edits
259
- - ✓ session.idle remind update TASKS.md
260
- - Planned: Auto-inject MEMORY.md into next agent session
261
-
262
- ### CLI (Current)
263
- - ✓ `npx @vuau/agent-memory init` — scaffold structure
256
+ ### CLI
257
+ - ✓ `npx @vuau/agent-memory init` scaffold structure (interactive or with flags)
258
+ - ✓ `npx @vuau/agent-memory init --opencode` OpenCode only
259
+ - ✓ `npx @vuau/agent-memory init --copilot --cursor` — multiple IDEs
260
+ - `npx @vuau/agent-memory init --all` all IDEs
264
261
  - ✓ `npx @vuau/agent-memory doctor` — validate structure
265
262
  - Planned: ✗ `report` — generate memory stats, archival suggestions
266
263
 
267
- ### VSCode Extension (Planned Phase 3)
264
+ ### VSCode Extension (Planned)
268
265
  - Sidebar showing MEMORY.md categories
269
266
  - Copilot Chat integration → inject relevant spec file on user request
270
267
  - Quick commands: "Add decision", "Update task"
@@ -111,7 +111,7 @@ Tài liệu này đề xuất kiến trúc khả năng skalabiliti cho AI memory
111
111
  **Agent behavior**:
112
112
  - Read khi session start → hiểu context, resume work
113
113
  - Update trước session end → gì done, gì next
114
- - Plugin reminds nếu tasks exist nhưng không update
114
+ - Rules trong AGENTS.md nhắc agent update tasks
115
115
 
116
116
  ---
117
117
 
@@ -171,16 +171,15 @@ Tài liệu này đề xuất kiến trúc khả năng skalabiliti cho AI memory
171
171
  ## IDE Integration Points
172
172
 
173
173
  ### OpenCode
174
- - Reads: AGENTS.md (via plugin hook)
175
- - Plugin auto-injects: `.agents/MEMORY.md` context khi session start
176
- - Plugin reminds: Update TASKS.md khi session idle
174
+ - Reads: `AGENTS.md` (native)
175
+ - Agents follow rules trong AGENTS.md
177
176
  - Writes: Agent appends đến MEMORY.md/TASKS.md/spec/
178
177
 
179
178
  ### GitHub Copilot (VSCode)
180
179
  - Reads: `.github/copilot-instructions.md` (GitHub convention)
181
180
  - copilot-instructions.md = cùng router format với AGENTS.md
182
181
  - Points đến `.agents/MEMORY.md` + spec files
183
- - Writes: User includes `@save memory: <decision>` trong chat → agent appends
182
+ - Writes: Agent follows rules appends khi appropriate
184
183
 
185
184
  ### Cursor / Windsurf
186
185
  - Reads: `.cursorrules` / `.windsurfrules` (IDE convention)
@@ -253,18 +252,15 @@ Tài liệu này đề xuất kiến trúc khả năng skalabiliti cho AI memory
253
252
 
254
253
  ## Công cụ Support
255
254
 
256
- ### OpenCode Plugin (Hiện tại)
257
- - ✓ session.created log memory file status
258
- - ✓ tool.execute.after track spec file edits
259
- - ✓ session.idle remind update TASKS.md
260
- - Planned: Auto-inject MEMORY.md vào next agent session
261
-
262
- ### CLI (Hiện tại)
263
- - ✓ `npx @vuau/agent-memory init` — scaffold structure
255
+ ### CLI
256
+ - ✓ `npx @vuau/agent-memory init` scaffold structure (interactive hoặc với flags)
257
+ - ✓ `npx @vuau/agent-memory init --opencode` chỉ OpenCode
258
+ - ✓ `npx @vuau/agent-memory init --copilot --cursor` — nhiều IDEs
259
+ - `npx @vuau/agent-memory init --all` tất cả IDEs
264
260
  - ✓ `npx @vuau/agent-memory doctor` — validate structure
265
261
  - Planned: ✗ `report` — generate memory stats, archival suggestions
266
262
 
267
- ### VSCode Extension (Planned Phase 3)
263
+ ### VSCode Extension (Planned)
268
264
  - Sidebar showing MEMORY.md categories
269
265
  - Copilot Chat integration → inject relevant spec file trên user request
270
266
  - Quick commands: "Add decision", "Update task"
package/docs/RESEARCH.md CHANGED
@@ -19,8 +19,6 @@ AI assistants (OpenCode, Copilot, Cursor, Windsurf) lose context between session
19
19
  | **mem0** | ✅ (hooks) | ❌ | Requires OpenAI API key or HuggingFace models | ❌ Failed |
20
20
  | **memories.sh** | ✅ (MCP) | ✅ | Auto-generates 10+ IDE config files (bloats repo) | ⚠️ Rejected |
21
21
  | **codemem** | ❌ | ❌ | Flaky (unreliable save/recall) | ⚠️ Rejected |
22
- | **OpenCode plugin** | ✅ (lifecycle hooks) | ✅ | None | ✅ **Viable** |
23
- | **GitHub Copilot native** | Partial (skills) | ✅ | No lifecycle hooks; no direct session integration | ⚠️ Limited |
24
22
  | **File-based + rules** | Manual (via rules) | ✅ | None | ✅ **CHOSEN** |
25
23
 
26
24
  ---
@@ -84,13 +82,12 @@ AI assistants (OpenCode, Copilot, Cursor, Windsurf) lose context between session
84
82
  ### 4. **IDE Portability**
85
83
  | IDE | Integration | Works Now |
86
84
  |-----|-------------|-----------|
87
- | OpenCode | Plugin hooks | ✅ Yes |
85
+ | OpenCode | Reads `AGENTS.md` | ✅ Yes |
88
86
  | GitHub Copilot | Reads `.github/copilot-instructions.md` | ✅ Yes |
89
87
  | Cursor | Reads `.cursorrules` | ✅ Yes |
90
88
  | Windsurf | Reads `.windsurfrules` | ✅ Yes |
91
- | VS Code | Reads markdown in workspace | ✅ Yes |
92
89
 
93
- No custom driver needed per IDE. Markdown is portable.
90
+ No custom plugin needed per IDE. Markdown is portable.
94
91
 
95
92
  ### 5. **Sharing & Sync**
96
93
  - Lives in git repo → automatically shared via Git/Rsync/Dropbox
@@ -175,7 +172,7 @@ TOTAL: 700 tokens
175
172
  - ✅ Context aware: Agent writes when they understand
176
173
  - ✅ Curated: Only important decisions survive
177
174
  - ✅ Portable: Works everywhere (no dependencies)
178
- - Needs agent discipline (mitigated by plugin reminders)
175
+ - No plugin maintenance burden
179
176
 
180
177
  **Verdict**: Quality + Portability > Automation for teams of 1-10.
181
178
 
@@ -184,16 +181,12 @@ TOTAL: 700 tokens
184
181
  ## Cross-IDE Reality Check
185
182
 
186
183
  ### ✅ What Works Now
187
- - OpenCode: Plugin hooks + session lifecycle
184
+ - OpenCode: Reads `AGENTS.md` natively
188
185
  - Copilot: Reads `.github/copilot-instructions.md` natively
189
186
  - Cursor: Reads `.cursorrules`
190
187
  - Windsurf: Reads `.windsurfrules`
191
- - VS Code: Reads markdown files
192
188
 
193
- ### ⚠️ Limitations
194
- - Copilot doesn't have lifecycle hooks (can't auto-remind to update tasks)
195
- - Cursor/Windsurf limited to reading rules, not injecting context
196
- - **Solution**: VSCode extension (Phase 3) to provide unified sidebar
189
+ All IDEs follow rules in their config file → agent writes to `.agents/MEMORY.md` when appropriate.
197
190
 
198
191
  ---
199
192
 
@@ -19,8 +19,6 @@ AI assistants (OpenCode, Copilot, Cursor, Windsurf) mất context giữa session
19
19
  | **mem0** | ✅ (hooks) | ❌ | Cần OpenAI API key hoặc HuggingFace models | ❌ Failed |
20
20
  | **memories.sh** | ✅ (MCP) | ✅ | Auto-generate 10+ IDE config files (bloat repo) | ⚠️ Rejected |
21
21
  | **codemem** | ❌ | ❌ | Flaky (unreliable save/recall) | ⚠️ Rejected |
22
- | **OpenCode plugin** | ✅ (lifecycle hooks) | ✅ | None | ✅ **Viable** |
23
- | **GitHub Copilot native** | Partial (skills) | ✅ | Không có lifecycle hooks | ⚠️ Limited |
24
22
  | **File-based + rules** | Manual (via rules) | ✅ | None | ✅ **CHOSEN** |
25
23
 
26
24
  ---
@@ -84,13 +82,12 @@ AI assistants (OpenCode, Copilot, Cursor, Windsurf) mất context giữa session
84
82
  ### 4. **IDE Portability**
85
83
  | IDE | Integration | Works Now |
86
84
  |-----|-------------|-----------|
87
- | OpenCode | Plugin hooks | ✅ Yes |
85
+ | OpenCode | Reads `AGENTS.md` | ✅ Yes |
88
86
  | GitHub Copilot | Reads `.github/copilot-instructions.md` | ✅ Yes |
89
87
  | Cursor | Reads `.cursorrules` | ✅ Yes |
90
88
  | Windsurf | Reads `.windsurfrules` | ✅ Yes |
91
- | VS Code | Reads markdown | ✅ Yes |
92
89
 
93
- Không cần custom driver per IDE. Markdown portable.
90
+ Không cần custom plugin per IDE. Markdown portable.
94
91
 
95
92
  ### 5. **Sharing & Sync**
96
93
  - Live trong git repo → auto-shared via Git/Rsync/Dropbox
@@ -175,7 +172,7 @@ TOTAL: 700 tokens
175
172
  - ✅ Context aware: Agent ghi khi họ hiểu
176
173
  - ✅ Curated: Chỉ important decisions sống sót
177
174
  - ✅ Portable: Hoạt động everywhere (no dependencies)
178
- - Cần agent discipline (mitigated bởi plugin reminders)
175
+ - Không cần maintain plugin
179
176
 
180
177
  **Verdict**: Quality + Portability > Automation cho teams 1-10.
181
178
 
@@ -184,16 +181,12 @@ TOTAL: 700 tokens
184
181
  ## Cross-IDE Reality Check
185
182
 
186
183
  ### ✅ Gì hoạt động ngay
187
- - OpenCode: Plugin hooks + session lifecycle
184
+ - OpenCode: Read `AGENTS.md` natively
188
185
  - Copilot: Read `.github/copilot-instructions.md` natively
189
186
  - Cursor: Read `.cursorrules`
190
187
  - Windsurf: Read `.windsurfrules`
191
- - VS Code: Read markdown files
192
188
 
193
- ### ⚠️ Limitations
194
- - Copilot không có lifecycle hooks (cannot auto-remind update tasks)
195
- - Cursor/Windsurf limited tới read rules, không inject context
196
- - **Solution**: VSCode extension (Phase 3) để provide unified sidebar
189
+ Tất cả IDEs follow rules trong config file → agent ghi vào `.agents/MEMORY.md` khi appropriate.
197
190
 
198
191
  ---
199
192
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vuau/agent-memory",
3
- "version": "0.2.1",
4
- "description": "Structured AI memory for codebases — OpenCode plugin + scaffolding CLI",
3
+ "version": "0.3.1",
4
+ "description": "Structured AI memory for codebases — scaffolding CLI for OpenCode, Copilot, Cursor, Windsurf",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "exports": {
@@ -25,27 +25,20 @@
25
25
  "typescript": "^6.0.3"
26
26
  },
27
27
  "keywords": [
28
- "opencode",
29
- "opencode-plugin",
30
28
  "agent-memory",
31
29
  "ai-memory",
32
30
  "agents",
31
+ "opencode",
33
32
  "copilot",
34
33
  "cursor",
35
- "windsurf"
34
+ "windsurf",
35
+ "claude",
36
+ "cli"
36
37
  ],
37
38
  "author": "Au Pham <phamvuau@gmail.com>",
38
39
  "license": "MIT",
39
40
  "repository": {
40
41
  "type": "git",
41
42
  "url": "git+https://github.com/vuau/agent-memory.git"
42
- },
43
- "peerDependencies": {
44
- "@opencode-ai/plugin": ">=1.0.0"
45
- },
46
- "peerDependenciesMeta": {
47
- "@opencode-ai/plugin": {
48
- "optional": true
49
- }
50
43
  }
51
44
  }
@@ -12,7 +12,8 @@ Router file for AI agents. Keep under 150 lines.
12
12
 
13
13
  | Task | Spec File |
14
14
  |------|-----------|
15
- | Past decisions & patterns | `.agents/MEMORY.md` |
15
+ | Past decisions (1-line) | `.agents/MEMORY.md` |
16
+ | Past decisions (full context) | `.agents/MEMORY-DETAIL.md` |
16
17
  | Current work in progress | `.agents/TASKS.md` |
17
18
 
18
19
  > Add your own spec files to `.agents/spec/` and reference them here.
@@ -26,9 +27,9 @@ Router file for AI agents. Keep under 150 lines.
26
27
 
27
28
  ### MEMORY.md entry format
28
29
  ```
29
- - YYYY-MM-DD: <1-line decision or pattern>
30
+ - YYYY-MM-DD: <1-line decision or pattern> → detail
30
31
  ```
31
- Place under the appropriate category. Add `→ spec file` pointer if details belong in a spec.
32
+ Place under the appropriate category. Add `→ detail` pointer when full context exists in MEMORY-DETAIL.md.
32
33
 
33
34
  ### TASKS.md update
34
35
  Before ending a session with unfinished work, move items to `## In Progress` or `## Up Next`.