@vuau/agent-memory 0.2.0 → 0.3.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/README.md +77 -104
- package/README.vi.md +80 -89
- package/dist/bin/cli.js +192 -89
- package/dist/index.js +84 -164
- 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/cursorrules.md +43 -0
- package/templates/windsurfrules.md +43 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// src/opencode/plugin.ts
|
|
2
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } 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";
|
|
@@ -14,22 +10,24 @@ var MEMORY_FILE = ".agents/MEMORY.md";
|
|
|
14
10
|
var TASKS_FILE = ".agents/TASKS.md";
|
|
15
11
|
var AGENTS_MD = "AGENTS.md";
|
|
16
12
|
var COPILOT_INSTRUCTIONS = ".github/copilot-instructions.md";
|
|
13
|
+
var CURSOR_RULES = ".cursorrules";
|
|
14
|
+
var WINDSURF_RULES = ".windsurfrules";
|
|
17
15
|
|
|
18
16
|
// src/core/scaffold.ts
|
|
19
17
|
function getTemplatesDir() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
const candidate2 = resolve(__dirname, "../../templates");
|
|
27
|
-
if (existsSync(candidate2)) return candidate2;
|
|
28
|
-
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})`);
|
|
29
24
|
}
|
|
30
25
|
var TEMPLATES_DIR = getTemplatesDir();
|
|
31
26
|
function readTemplate(name) {
|
|
32
27
|
const templatePath = join(TEMPLATES_DIR, name);
|
|
28
|
+
if (!existsSync(templatePath)) {
|
|
29
|
+
throw new Error(`Template not found: ${templatePath}`);
|
|
30
|
+
}
|
|
33
31
|
return readFileSync(templatePath, "utf-8");
|
|
34
32
|
}
|
|
35
33
|
function applyVars(content, vars) {
|
|
@@ -39,15 +37,21 @@ function applyVars(content, vars) {
|
|
|
39
37
|
}
|
|
40
38
|
return result;
|
|
41
39
|
}
|
|
42
|
-
function scaffold(projectDir,
|
|
40
|
+
function scaffold(projectDir, options = {}) {
|
|
43
41
|
const result = { created: [], skipped: [] };
|
|
44
|
-
const projectName =
|
|
42
|
+
const projectName = options.projectName || guessProjectName(projectDir);
|
|
45
43
|
const vars = { PROJECT_NAME: projectName };
|
|
44
|
+
const force = options.force || false;
|
|
45
|
+
const hasAnyIde = options.opencode || options.copilot || options.cursor || options.windsurf;
|
|
46
|
+
const useOpencode = hasAnyIde ? options.opencode : true;
|
|
47
|
+
const useCopilot = options.copilot || false;
|
|
48
|
+
const useCursor = options.cursor || false;
|
|
49
|
+
const useWindsurf = options.windsurf || false;
|
|
46
50
|
const dirs = [
|
|
47
51
|
join(projectDir, AGENTS_DIR),
|
|
48
52
|
join(projectDir, SPEC_DIR)
|
|
49
53
|
];
|
|
50
|
-
if (
|
|
54
|
+
if (useCopilot) {
|
|
51
55
|
dirs.push(join(projectDir, ".github"));
|
|
52
56
|
}
|
|
53
57
|
for (const dir of dirs) {
|
|
@@ -55,18 +59,11 @@ function scaffold(projectDir, config = {}, force = false) {
|
|
|
55
59
|
mkdirSync(dir, { recursive: true });
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
|
-
const
|
|
59
|
-
{ target: AGENTS_MD, template: "AGENTS.md" },
|
|
62
|
+
const coreFiles = [
|
|
60
63
|
{ target: MEMORY_FILE, template: "MEMORY.md" },
|
|
61
64
|
{ target: TASKS_FILE, template: "TASKS.md" }
|
|
62
65
|
];
|
|
63
|
-
|
|
64
|
-
files.push({
|
|
65
|
-
target: COPILOT_INSTRUCTIONS,
|
|
66
|
-
template: "copilot-instructions.md"
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
for (const { target, template } of files) {
|
|
66
|
+
for (const { target, template } of coreFiles) {
|
|
70
67
|
const targetPath = join(projectDir, target);
|
|
71
68
|
if (existsSync(targetPath) && !force) {
|
|
72
69
|
result.skipped.push(target);
|
|
@@ -81,56 +78,51 @@ function scaffold(projectDir, config = {}, force = false) {
|
|
|
81
78
|
writeFileSync(specKeep, "");
|
|
82
79
|
result.created.push(`${SPEC_DIR}/.gitkeep`);
|
|
83
80
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
81
|
+
if (useOpencode) {
|
|
82
|
+
writeFileIfNeeded(
|
|
83
|
+
join(projectDir, AGENTS_MD),
|
|
84
|
+
applyVars(readTemplate("AGENTS.md"), vars),
|
|
85
|
+
AGENTS_MD,
|
|
86
|
+
result,
|
|
87
|
+
force
|
|
88
|
+
);
|
|
86
89
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
if (useCopilot) {
|
|
91
|
+
writeFileIfNeeded(
|
|
92
|
+
join(projectDir, COPILOT_INSTRUCTIONS),
|
|
93
|
+
applyVars(readTemplate("copilot-instructions.md"), vars),
|
|
94
|
+
COPILOT_INSTRUCTIONS,
|
|
95
|
+
result,
|
|
96
|
+
force
|
|
97
|
+
);
|
|
95
98
|
}
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
if (useCursor) {
|
|
100
|
+
writeFileIfNeeded(
|
|
101
|
+
join(projectDir, CURSOR_RULES),
|
|
102
|
+
applyVars(readTemplate("cursorrules.md"), vars),
|
|
103
|
+
CURSOR_RULES,
|
|
104
|
+
result,
|
|
105
|
+
force
|
|
100
106
|
);
|
|
101
|
-
result.created.push(".opencode/package.json");
|
|
102
|
-
} else {
|
|
103
|
-
const pkg = JSON.parse(readFileSync(opencodePkgPath, "utf-8"));
|
|
104
|
-
const deps = pkg.dependencies || {};
|
|
105
|
-
if (!deps[PACKAGE_NAME] || force) {
|
|
106
|
-
deps[PACKAGE_NAME] = "latest";
|
|
107
|
-
pkg.dependencies = deps;
|
|
108
|
-
writeFileSync(opencodePkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
109
|
-
if (!deps[PACKAGE_NAME]) {
|
|
110
|
-
result.created.push(".opencode/package.json");
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
result.skipped.push(".opencode/package.json");
|
|
114
|
-
}
|
|
115
107
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
108
|
+
if (useWindsurf) {
|
|
109
|
+
writeFileIfNeeded(
|
|
110
|
+
join(projectDir, WINDSURF_RULES),
|
|
111
|
+
applyVars(readTemplate("windsurfrules.md"), vars),
|
|
112
|
+
WINDSURF_RULES,
|
|
113
|
+
result,
|
|
114
|
+
force
|
|
121
115
|
);
|
|
122
|
-
result.created.push("opencode.json");
|
|
123
|
-
} else {
|
|
124
|
-
const config = JSON.parse(readFileSync(opencodeJsonPath, "utf-8"));
|
|
125
|
-
const plugins = config.plugin || [];
|
|
126
|
-
if (!plugins.includes(PACKAGE_NAME)) {
|
|
127
|
-
config.plugin = [...plugins, PACKAGE_NAME];
|
|
128
|
-
writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
129
|
-
result.created.push("opencode.json (merged plugin)");
|
|
130
|
-
} else {
|
|
131
|
-
result.skipped.push("opencode.json");
|
|
132
|
-
}
|
|
133
116
|
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
function writeFileIfNeeded(targetPath, content, displayName, result, force) {
|
|
120
|
+
if (existsSync(targetPath) && !force) {
|
|
121
|
+
result.skipped.push(displayName);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
writeFileSync(targetPath, content);
|
|
125
|
+
result.created.push(displayName);
|
|
134
126
|
}
|
|
135
127
|
function guessProjectName(dir) {
|
|
136
128
|
const pkgPath = join(dir, "package.json");
|
|
@@ -144,88 +136,15 @@ function guessProjectName(dir) {
|
|
|
144
136
|
return dir.split("/").pop() || "Project";
|
|
145
137
|
}
|
|
146
138
|
|
|
147
|
-
// src/opencode/plugin.ts
|
|
148
|
-
var MemoryLifecyclePlugin = async ({ client, directory }) => {
|
|
149
|
-
const memoryFile = resolve2(directory, MEMORY_FILE);
|
|
150
|
-
const tasksFile = resolve2(directory, TASKS_FILE);
|
|
151
|
-
const agentsFile = resolve2(directory, AGENTS_MD);
|
|
152
|
-
let editCount = 0;
|
|
153
|
-
let specFilesEdited = [];
|
|
154
|
-
return {
|
|
155
|
-
event: async ({ event }) => {
|
|
156
|
-
if (event.type === "session.created") {
|
|
157
|
-
const hasAgentsMd = existsSync2(agentsFile);
|
|
158
|
-
const hasMemory = existsSync2(memoryFile);
|
|
159
|
-
if (!hasMemory && hasAgentsMd) {
|
|
160
|
-
try {
|
|
161
|
-
const result = scaffold(directory, { copilotInstructions: false });
|
|
162
|
-
if (result.created.length > 0) {
|
|
163
|
-
await client.tui.showToast({
|
|
164
|
-
body: {
|
|
165
|
-
message: `Agent memory initialized: ${result.created.join(", ")}`,
|
|
166
|
-
variant: "success"
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
} catch (err) {
|
|
171
|
-
await client.app.log({
|
|
172
|
-
body: {
|
|
173
|
-
service: "agent-memory",
|
|
174
|
-
level: "warn",
|
|
175
|
-
message: `Auto-scaffold failed: ${err}`
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
editCount = 0;
|
|
181
|
-
specFilesEdited = [];
|
|
182
|
-
await client.app.log({
|
|
183
|
-
body: {
|
|
184
|
-
service: "agent-memory",
|
|
185
|
-
level: "info",
|
|
186
|
-
message: `Memory: ${hasMemory}, Tasks: ${existsSync2(tasksFile)}`
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
if (event.type === "session.idle" && editCount >= 3) {
|
|
191
|
-
if (existsSync2(tasksFile)) {
|
|
192
|
-
const content = readFileSync2(tasksFile, "utf-8");
|
|
193
|
-
const inProgressSection = content.split("## In Progress")[1];
|
|
194
|
-
if (inProgressSection?.trim()) {
|
|
195
|
-
await client.tui.showToast({
|
|
196
|
-
body: {
|
|
197
|
-
message: `${editCount} edits this session. Update .agents/TASKS.md before ending.`,
|
|
198
|
-
variant: "info"
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
"tool.execute.after": async (input) => {
|
|
206
|
-
if (input.tool === "edit" || input.tool === "write") {
|
|
207
|
-
editCount++;
|
|
208
|
-
const filePath = input.args?.filePath || "";
|
|
209
|
-
if (filePath.includes(SPEC_DIR)) {
|
|
210
|
-
const shortPath = filePath.split(SPEC_DIR + "/").pop() || filePath;
|
|
211
|
-
if (!specFilesEdited.includes(shortPath)) {
|
|
212
|
-
specFilesEdited.push(shortPath);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
};
|
|
219
|
-
|
|
220
139
|
// src/core/memory.ts
|
|
221
|
-
import { existsSync as
|
|
140
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
222
141
|
import { join as join2 } from "path";
|
|
223
142
|
function appendMemory(projectDir, entry) {
|
|
224
143
|
const filePath = join2(projectDir, MEMORY_FILE);
|
|
225
|
-
if (!
|
|
144
|
+
if (!existsSync2(filePath)) {
|
|
226
145
|
throw new Error(`${MEMORY_FILE} not found. Run 'agent-memory init' first.`);
|
|
227
146
|
}
|
|
228
|
-
const content =
|
|
147
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
229
148
|
const category = entry.category || "Decisions";
|
|
230
149
|
const line = `- ${entry.date}: ${entry.content}`;
|
|
231
150
|
const categoryHeader = `## ${category}`;
|
|
@@ -249,8 +168,8 @@ ${line}
|
|
|
249
168
|
}
|
|
250
169
|
function readMemory(projectDir) {
|
|
251
170
|
const filePath = join2(projectDir, MEMORY_FILE);
|
|
252
|
-
if (!
|
|
253
|
-
const content =
|
|
171
|
+
if (!existsSync2(filePath)) return {};
|
|
172
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
254
173
|
const categories = {};
|
|
255
174
|
let currentCategory = "_uncategorized";
|
|
256
175
|
for (const line of content.split("\n")) {
|
|
@@ -269,7 +188,7 @@ function readMemory(projectDir) {
|
|
|
269
188
|
}
|
|
270
189
|
|
|
271
190
|
// src/core/tasks.ts
|
|
272
|
-
import { existsSync as
|
|
191
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
273
192
|
import { join as join3 } from "path";
|
|
274
193
|
function readTasks(projectDir) {
|
|
275
194
|
const filePath = join3(projectDir, TASKS_FILE);
|
|
@@ -278,8 +197,8 @@ function readTasks(projectDir) {
|
|
|
278
197
|
up_next: [],
|
|
279
198
|
completed: []
|
|
280
199
|
};
|
|
281
|
-
if (!
|
|
282
|
-
const content =
|
|
200
|
+
if (!existsSync3(filePath)) return result;
|
|
201
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
283
202
|
let currentSection = null;
|
|
284
203
|
for (const line of content.split("\n")) {
|
|
285
204
|
if (line.startsWith("## In Progress")) {
|
|
@@ -321,7 +240,7 @@ ${tasks.completed.map((t) => `- ${t}`).join("\n") || ""}
|
|
|
321
240
|
}
|
|
322
241
|
|
|
323
242
|
// src/core/doctor.ts
|
|
324
|
-
import { existsSync as
|
|
243
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
325
244
|
import { join as join4 } from "path";
|
|
326
245
|
function doctor(projectDir) {
|
|
327
246
|
const issues = [];
|
|
@@ -332,17 +251,17 @@ function doctor(projectDir) {
|
|
|
332
251
|
];
|
|
333
252
|
for (const { file, desc } of required) {
|
|
334
253
|
const filePath = join4(projectDir, file);
|
|
335
|
-
if (!
|
|
254
|
+
if (!existsSync4(filePath)) {
|
|
336
255
|
issues.push({ level: "error", file, message: `Missing ${desc}` });
|
|
337
256
|
}
|
|
338
257
|
}
|
|
339
258
|
for (const dir of [AGENTS_DIR, SPEC_DIR]) {
|
|
340
|
-
if (!
|
|
259
|
+
if (!existsSync4(join4(projectDir, dir))) {
|
|
341
260
|
issues.push({ level: "error", file: dir, message: "Directory missing" });
|
|
342
261
|
}
|
|
343
262
|
}
|
|
344
263
|
const copilotPath = join4(projectDir, COPILOT_INSTRUCTIONS);
|
|
345
|
-
if (!
|
|
264
|
+
if (!existsSync4(copilotPath)) {
|
|
346
265
|
issues.push({
|
|
347
266
|
level: "warning",
|
|
348
267
|
file: COPILOT_INSTRUCTIONS,
|
|
@@ -350,8 +269,8 @@ function doctor(projectDir) {
|
|
|
350
269
|
});
|
|
351
270
|
}
|
|
352
271
|
const agentsPath = join4(projectDir, AGENTS_MD);
|
|
353
|
-
if (
|
|
354
|
-
const content =
|
|
272
|
+
if (existsSync4(agentsPath)) {
|
|
273
|
+
const content = readFileSync4(agentsPath, "utf-8");
|
|
355
274
|
if (!content.includes(".agents/")) {
|
|
356
275
|
issues.push({
|
|
357
276
|
level: "warning",
|
|
@@ -368,8 +287,8 @@ function doctor(projectDir) {
|
|
|
368
287
|
}
|
|
369
288
|
}
|
|
370
289
|
const memoryPath = join4(projectDir, MEMORY_FILE);
|
|
371
|
-
if (
|
|
372
|
-
const lines =
|
|
290
|
+
if (existsSync4(memoryPath)) {
|
|
291
|
+
const lines = readFileSync4(memoryPath, "utf-8").split("\n").length;
|
|
373
292
|
if (lines > 150) {
|
|
374
293
|
issues.push({
|
|
375
294
|
level: "warning",
|
|
@@ -380,9 +299,9 @@ function doctor(projectDir) {
|
|
|
380
299
|
}
|
|
381
300
|
const opencodePkgPath = join4(projectDir, ".opencode", "package.json");
|
|
382
301
|
const opencodeJsonPath = join4(projectDir, "opencode.json");
|
|
383
|
-
const opencodeExists =
|
|
302
|
+
const opencodeExists = existsSync4(join4(projectDir, ".opencode"));
|
|
384
303
|
if (opencodeExists) {
|
|
385
|
-
if (!
|
|
304
|
+
if (!existsSync4(opencodePkgPath)) {
|
|
386
305
|
issues.push({
|
|
387
306
|
level: "warning",
|
|
388
307
|
file: ".opencode/package.json",
|
|
@@ -390,7 +309,7 @@ function doctor(projectDir) {
|
|
|
390
309
|
});
|
|
391
310
|
} else {
|
|
392
311
|
try {
|
|
393
|
-
const pkg = JSON.parse(
|
|
312
|
+
const pkg = JSON.parse(readFileSync4(opencodePkgPath, "utf-8"));
|
|
394
313
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
395
314
|
if (!deps["@vuau/agent-memory"]) {
|
|
396
315
|
issues.push({
|
|
@@ -403,7 +322,7 @@ function doctor(projectDir) {
|
|
|
403
322
|
issues.push({ level: "warning", file: ".opencode/package.json", message: "Invalid JSON" });
|
|
404
323
|
}
|
|
405
324
|
}
|
|
406
|
-
if (!
|
|
325
|
+
if (!existsSync4(opencodeJsonPath)) {
|
|
407
326
|
issues.push({
|
|
408
327
|
level: "warning",
|
|
409
328
|
file: "opencode.json",
|
|
@@ -411,7 +330,7 @@ function doctor(projectDir) {
|
|
|
411
330
|
});
|
|
412
331
|
} else {
|
|
413
332
|
try {
|
|
414
|
-
const config = JSON.parse(
|
|
333
|
+
const config = JSON.parse(readFileSync4(opencodeJsonPath, "utf-8"));
|
|
415
334
|
const plugins = config.plugin || [];
|
|
416
335
|
if (!plugins.includes("@vuau/agent-memory")) {
|
|
417
336
|
issues.push({
|
|
@@ -430,11 +349,12 @@ function doctor(projectDir) {
|
|
|
430
349
|
export {
|
|
431
350
|
AGENTS_DIR,
|
|
432
351
|
AGENTS_MD,
|
|
433
|
-
MemoryLifecyclePlugin as AgentMemoryPlugin,
|
|
434
352
|
COPILOT_INSTRUCTIONS,
|
|
353
|
+
CURSOR_RULES,
|
|
435
354
|
MEMORY_FILE,
|
|
436
355
|
SPEC_DIR,
|
|
437
356
|
TASKS_FILE,
|
|
357
|
+
WINDSURF_RULES,
|
|
438
358
|
appendMemory,
|
|
439
359
|
doctor,
|
|
440
360
|
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.0",
|
|
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
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} - Cursor Rules
|
|
2
|
+
|
|
3
|
+
Instructions for Cursor AI. Keep under 150 lines.
|
|
4
|
+
|
|
5
|
+
## Priority
|
|
6
|
+
1. User request first.
|
|
7
|
+
2. These rules.
|
|
8
|
+
3. Spec files in `.agents/spec/`.
|
|
9
|
+
4. If conflict remains, choose smallest safe change and state assumption.
|
|
10
|
+
|
|
11
|
+
## Documentation Map
|
|
12
|
+
|
|
13
|
+
| Task | Location |
|
|
14
|
+
|------|----------|
|
|
15
|
+
| Past decisions & patterns | `.agents/MEMORY.md` |
|
|
16
|
+
| Current work in progress | `.agents/TASKS.md` |
|
|
17
|
+
| Detailed specs | `.agents/spec/*.md` |
|
|
18
|
+
|
|
19
|
+
## Memory Protocol
|
|
20
|
+
|
|
21
|
+
### When to write
|
|
22
|
+
- User approves a decision or pattern → append to `.agents/MEMORY.md`
|
|
23
|
+
- Explore codebase/architecture → update relevant `.agents/spec/*.md`
|
|
24
|
+
- Start/finish a large task → update `.agents/TASKS.md`
|
|
25
|
+
|
|
26
|
+
### MEMORY.md entry format
|
|
27
|
+
```
|
|
28
|
+
- YYYY-MM-DD: <1-line decision or pattern>
|
|
29
|
+
```
|
|
30
|
+
Place under the appropriate category. Add `→ spec file` pointer if details belong in a spec.
|
|
31
|
+
|
|
32
|
+
### TASKS.md update
|
|
33
|
+
Before ending a session with unfinished work, move items to `## In Progress` or `## Up Next`.
|
|
34
|
+
|
|
35
|
+
### Rules
|
|
36
|
+
- Keep MEMORY.md entries to 1 line each. Details go in spec files.
|
|
37
|
+
- If MEMORY.md > 150 lines, archive old entries.
|
|
38
|
+
- Do not create additional memory files outside `.agents/`.
|
|
39
|
+
|
|
40
|
+
## Response Style
|
|
41
|
+
- Concise, concrete, implementation-focused.
|
|
42
|
+
- If uncertain, say `I don't know`, then give fastest verification step.
|
|
43
|
+
- Do not invent files, APIs, or command outputs.
|