@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/README.md +77 -104
- package/README.vi.md +80 -89
- package/dist/bin/cli.js +188 -80
- package/dist/index.js +81 -179
- package/docs/ARCHITECTURE.md +6 -9
- package/docs/ARCHITECTURE.vi.md +10 -14
- package/docs/RESEARCH.md +5 -12
- package/docs/RESEARCH.vi.md +5 -12
- package/package.json +6 -13
- package/templates/AGENTS.md +4 -3
- package/templates/MEMORY-DETAIL.md +16 -0
- package/templates/copilot-instructions.md +4 -3
- package/templates/cursorrules.md +44 -0
- package/templates/windsurfrules.md +44 -0
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,
|
|
41
|
+
function scaffold(projectDir, options = {}) {
|
|
40
42
|
const result = { created: [], skipped: [] };
|
|
41
|
-
const projectName =
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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 (
|
|
82
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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 (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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 (!
|
|
146
|
+
if (!existsSync2(filePath)) {
|
|
247
147
|
throw new Error(`${MEMORY_FILE} not found. Run 'agent-memory init' first.`);
|
|
248
148
|
}
|
|
249
|
-
const content =
|
|
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 (!
|
|
274
|
-
const content =
|
|
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
|
|
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 (!
|
|
303
|
-
const content =
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
375
|
-
const content =
|
|
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 (
|
|
393
|
-
const lines =
|
|
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 =
|
|
304
|
+
const opencodeExists = existsSync4(join4(projectDir, ".opencode"));
|
|
405
305
|
if (opencodeExists) {
|
|
406
|
-
if (!
|
|
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(
|
|
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 (!
|
|
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(
|
|
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,
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -253,18 +253,15 @@ This document proposes a generalizable architecture for AI memory across codebas
|
|
|
253
253
|
|
|
254
254
|
## Tooling Support
|
|
255
255
|
|
|
256
|
-
###
|
|
257
|
-
- ✓
|
|
258
|
-
- ✓
|
|
259
|
-
- ✓
|
|
260
|
-
-
|
|
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
|
|
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"
|
package/docs/ARCHITECTURE.vi.md
CHANGED
|
@@ -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
|
-
-
|
|
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 (
|
|
175
|
-
-
|
|
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:
|
|
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
|
-
###
|
|
257
|
-
- ✓
|
|
258
|
-
- ✓
|
|
259
|
-
- ✓
|
|
260
|
-
-
|
|
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
|
|
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 |
|
|
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
|
|
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
|
-
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
package/docs/RESEARCH.vi.md
CHANGED
|
@@ -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 |
|
|
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
|
|
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
|
-
-
|
|
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:
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "Structured AI memory for codebases — OpenCode
|
|
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
|
}
|
package/templates/AGENTS.md
CHANGED
|
@@ -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
|
|
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 `→
|
|
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`.
|