@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/bin/cli.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// bin/cli.ts
|
|
4
|
+
import * as readline from "readline";
|
|
5
|
+
import { existsSync as existsSync3 } from "fs";
|
|
6
|
+
import { join as join3 } from "path";
|
|
7
|
+
|
|
3
8
|
// src/core/scaffold.ts
|
|
4
9
|
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
5
10
|
import { join, resolve, dirname } from "path";
|
|
@@ -9,9 +14,12 @@ import { fileURLToPath } from "url";
|
|
|
9
14
|
var AGENTS_DIR = ".agents";
|
|
10
15
|
var SPEC_DIR = ".agents/spec";
|
|
11
16
|
var MEMORY_FILE = ".agents/MEMORY.md";
|
|
17
|
+
var MEMORY_DETAIL_FILE = ".agents/MEMORY-DETAIL.md";
|
|
12
18
|
var TASKS_FILE = ".agents/TASKS.md";
|
|
13
19
|
var AGENTS_MD = "AGENTS.md";
|
|
14
20
|
var COPILOT_INSTRUCTIONS = ".github/copilot-instructions.md";
|
|
21
|
+
var CURSOR_RULES = ".cursorrules";
|
|
22
|
+
var WINDSURF_RULES = ".windsurfrules";
|
|
15
23
|
|
|
16
24
|
// src/core/scaffold.ts
|
|
17
25
|
function getTemplatesDir() {
|
|
@@ -25,6 +33,9 @@ function getTemplatesDir() {
|
|
|
25
33
|
var TEMPLATES_DIR = getTemplatesDir();
|
|
26
34
|
function readTemplate(name) {
|
|
27
35
|
const templatePath = join(TEMPLATES_DIR, name);
|
|
36
|
+
if (!existsSync(templatePath)) {
|
|
37
|
+
throw new Error(`Template not found: ${templatePath}`);
|
|
38
|
+
}
|
|
28
39
|
return readFileSync(templatePath, "utf-8");
|
|
29
40
|
}
|
|
30
41
|
function applyVars(content, vars) {
|
|
@@ -34,15 +45,21 @@ function applyVars(content, vars) {
|
|
|
34
45
|
}
|
|
35
46
|
return result;
|
|
36
47
|
}
|
|
37
|
-
function scaffold(projectDir,
|
|
48
|
+
function scaffold(projectDir, options = {}) {
|
|
38
49
|
const result = { created: [], skipped: [] };
|
|
39
|
-
const projectName =
|
|
50
|
+
const projectName = options.projectName || guessProjectName(projectDir);
|
|
40
51
|
const vars = { PROJECT_NAME: projectName };
|
|
52
|
+
const force = options.force || false;
|
|
53
|
+
const hasAnyIde = options.opencode || options.copilot || options.cursor || options.windsurf;
|
|
54
|
+
const useOpencode = hasAnyIde ? options.opencode : true;
|
|
55
|
+
const useCopilot = options.copilot || false;
|
|
56
|
+
const useCursor = options.cursor || false;
|
|
57
|
+
const useWindsurf = options.windsurf || false;
|
|
41
58
|
const dirs = [
|
|
42
59
|
join(projectDir, AGENTS_DIR),
|
|
43
60
|
join(projectDir, SPEC_DIR)
|
|
44
61
|
];
|
|
45
|
-
if (
|
|
62
|
+
if (useCopilot) {
|
|
46
63
|
dirs.push(join(projectDir, ".github"));
|
|
47
64
|
}
|
|
48
65
|
for (const dir of dirs) {
|
|
@@ -50,18 +67,12 @@ function scaffold(projectDir, config = {}, force = false) {
|
|
|
50
67
|
mkdirSync(dir, { recursive: true });
|
|
51
68
|
}
|
|
52
69
|
}
|
|
53
|
-
const
|
|
54
|
-
{ target: AGENTS_MD, template: "AGENTS.md" },
|
|
70
|
+
const coreFiles = [
|
|
55
71
|
{ target: MEMORY_FILE, template: "MEMORY.md" },
|
|
72
|
+
{ target: MEMORY_DETAIL_FILE, template: "MEMORY-DETAIL.md" },
|
|
56
73
|
{ target: TASKS_FILE, template: "TASKS.md" }
|
|
57
74
|
];
|
|
58
|
-
|
|
59
|
-
files.push({
|
|
60
|
-
target: COPILOT_INSTRUCTIONS,
|
|
61
|
-
template: "copilot-instructions.md"
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
for (const { target, template } of files) {
|
|
75
|
+
for (const { target, template } of coreFiles) {
|
|
65
76
|
const targetPath = join(projectDir, target);
|
|
66
77
|
if (existsSync(targetPath) && !force) {
|
|
67
78
|
result.skipped.push(target);
|
|
@@ -76,56 +87,51 @@ function scaffold(projectDir, config = {}, force = false) {
|
|
|
76
87
|
writeFileSync(specKeep, "");
|
|
77
88
|
result.created.push(`${SPEC_DIR}/.gitkeep`);
|
|
78
89
|
}
|
|
79
|
-
if (
|
|
80
|
-
|
|
90
|
+
if (useOpencode) {
|
|
91
|
+
writeFileIfNeeded(
|
|
92
|
+
join(projectDir, AGENTS_MD),
|
|
93
|
+
applyVars(readTemplate("AGENTS.md"), vars),
|
|
94
|
+
AGENTS_MD,
|
|
95
|
+
result,
|
|
96
|
+
force
|
|
97
|
+
);
|
|
81
98
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
if (useCopilot) {
|
|
100
|
+
writeFileIfNeeded(
|
|
101
|
+
join(projectDir, COPILOT_INSTRUCTIONS),
|
|
102
|
+
applyVars(readTemplate("copilot-instructions.md"), vars),
|
|
103
|
+
COPILOT_INSTRUCTIONS,
|
|
104
|
+
result,
|
|
105
|
+
force
|
|
106
|
+
);
|
|
90
107
|
}
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
if (useCursor) {
|
|
109
|
+
writeFileIfNeeded(
|
|
110
|
+
join(projectDir, CURSOR_RULES),
|
|
111
|
+
applyVars(readTemplate("cursorrules.md"), vars),
|
|
112
|
+
CURSOR_RULES,
|
|
113
|
+
result,
|
|
114
|
+
force
|
|
95
115
|
);
|
|
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
116
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
if (useWindsurf) {
|
|
118
|
+
writeFileIfNeeded(
|
|
119
|
+
join(projectDir, WINDSURF_RULES),
|
|
120
|
+
applyVars(readTemplate("windsurfrules.md"), vars),
|
|
121
|
+
WINDSURF_RULES,
|
|
122
|
+
result,
|
|
123
|
+
force
|
|
116
124
|
);
|
|
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
125
|
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
function writeFileIfNeeded(targetPath, content, displayName, result, force) {
|
|
129
|
+
if (existsSync(targetPath) && !force) {
|
|
130
|
+
result.skipped.push(displayName);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
writeFileSync(targetPath, content);
|
|
134
|
+
result.created.push(displayName);
|
|
129
135
|
}
|
|
130
136
|
function guessProjectName(dir) {
|
|
131
137
|
const pkgPath = join(dir, "package.json");
|
|
@@ -250,6 +256,32 @@ function doctor(projectDir) {
|
|
|
250
256
|
// bin/cli.ts
|
|
251
257
|
var args = process.argv.slice(2);
|
|
252
258
|
var command = args[0];
|
|
259
|
+
var rl = readline.createInterface({
|
|
260
|
+
input: process.stdin,
|
|
261
|
+
output: process.stdout
|
|
262
|
+
});
|
|
263
|
+
function ask(question) {
|
|
264
|
+
return new Promise((resolve2) => {
|
|
265
|
+
rl.question(question, (answer) => resolve2(answer.trim()));
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function askYesNo(question, defaultYes = true) {
|
|
269
|
+
const hint = defaultYes ? "(Y/n)" : "(y/N)";
|
|
270
|
+
return ask(`${question} ${hint} `).then((answer) => {
|
|
271
|
+
if (!answer) return defaultYes;
|
|
272
|
+
return answer.toLowerCase().startsWith("y");
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
async function askMultiSelect(question, options) {
|
|
276
|
+
console.log(`
|
|
277
|
+
${question}`);
|
|
278
|
+
for (const opt of options) {
|
|
279
|
+
console.log(` [${opt.key}] ${opt.label}`);
|
|
280
|
+
}
|
|
281
|
+
const answer = await ask("Enter choices (e.g., 1,2 or 1 2): ");
|
|
282
|
+
const selected = answer.split(/[,\s]+/).filter(Boolean);
|
|
283
|
+
return selected;
|
|
284
|
+
}
|
|
253
285
|
function printUsage() {
|
|
254
286
|
console.log(`
|
|
255
287
|
@vuau/agent-memory \u2014 Structured AI memory for codebases
|
|
@@ -260,27 +292,99 @@ Usage:
|
|
|
260
292
|
agent-memory help Show this help
|
|
261
293
|
|
|
262
294
|
Options (init):
|
|
263
|
-
--
|
|
295
|
+
--opencode Create AGENTS.md for OpenCode
|
|
296
|
+
--copilot Create .github/copilot-instructions.md for GitHub Copilot
|
|
297
|
+
--cursor Create .cursorrules for Cursor
|
|
298
|
+
--windsurf Create .windsurfrules for Windsurf
|
|
299
|
+
--all Create config for all IDEs
|
|
300
|
+
--force Overwrite existing files without asking
|
|
264
301
|
--name <name> Project name (default: from package.json)
|
|
265
|
-
|
|
266
|
-
|
|
302
|
+
|
|
303
|
+
Examples:
|
|
304
|
+
npx @vuau/agent-memory init # Interactive mode
|
|
305
|
+
npx @vuau/agent-memory init --opencode # OpenCode only
|
|
306
|
+
npx @vuau/agent-memory init --copilot --cursor # Copilot + Cursor
|
|
307
|
+
npx @vuau/agent-memory init --all # All IDEs
|
|
267
308
|
`);
|
|
268
309
|
}
|
|
269
|
-
function runInit() {
|
|
310
|
+
async function runInit() {
|
|
311
|
+
const cwd = process.cwd();
|
|
270
312
|
const force = args.includes("--force");
|
|
271
|
-
const
|
|
272
|
-
const
|
|
313
|
+
const hasOpencode = args.includes("--opencode");
|
|
314
|
+
const hasCopilot = args.includes("--copilot");
|
|
315
|
+
const hasCursor = args.includes("--cursor");
|
|
316
|
+
const hasWindsurf = args.includes("--windsurf");
|
|
317
|
+
const hasAll = args.includes("--all");
|
|
273
318
|
const nameIdx = args.indexOf("--name");
|
|
274
319
|
const projectName = nameIdx !== -1 ? args[nameIdx + 1] : void 0;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
320
|
+
let selectedIdes = [];
|
|
321
|
+
if (hasAll) {
|
|
322
|
+
selectedIdes = ["1", "2", "3", "4"];
|
|
323
|
+
} else if (hasOpencode || hasCopilot || hasCursor || hasWindsurf) {
|
|
324
|
+
if (hasOpencode) selectedIdes.push("1");
|
|
325
|
+
if (hasCopilot) selectedIdes.push("2");
|
|
326
|
+
if (hasCursor) selectedIdes.push("3");
|
|
327
|
+
if (hasWindsurf) selectedIdes.push("4");
|
|
328
|
+
} else {
|
|
329
|
+
console.log("\n@vuau/agent-memory \u2014 Structured AI memory for codebases\n");
|
|
330
|
+
selectedIdes = await askMultiSelect("What are your coding tools?", [
|
|
331
|
+
{ key: "1", label: "OpenCode (AGENTS.md)" },
|
|
332
|
+
{ key: "2", label: "GitHub Copilot (.github/copilot-instructions.md)" },
|
|
333
|
+
{ key: "3", label: "Cursor (.cursorrules)" },
|
|
334
|
+
{ key: "4", label: "Windsurf (.windsurfrules)" }
|
|
335
|
+
]);
|
|
336
|
+
if (selectedIdes.length === 0) {
|
|
337
|
+
console.log("\nNo tools selected. Defaulting to OpenCode.\n");
|
|
338
|
+
selectedIdes = ["1"];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const options = {
|
|
278
342
|
projectName,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
343
|
+
opencode: selectedIdes.includes("1"),
|
|
344
|
+
copilot: selectedIdes.includes("2"),
|
|
345
|
+
cursor: selectedIdes.includes("3"),
|
|
346
|
+
windsurf: selectedIdes.includes("4")
|
|
347
|
+
};
|
|
348
|
+
if (!force) {
|
|
349
|
+
const filesToCheck = [
|
|
350
|
+
{ path: MEMORY_FILE, name: ".agents/MEMORY.md" },
|
|
351
|
+
{ path: TASKS_FILE, name: ".agents/TASKS.md" }
|
|
352
|
+
];
|
|
353
|
+
if (options.opencode) {
|
|
354
|
+
filesToCheck.push({ path: AGENTS_MD, name: "AGENTS.md" });
|
|
355
|
+
}
|
|
356
|
+
if (options.copilot) {
|
|
357
|
+
filesToCheck.push({ path: COPILOT_INSTRUCTIONS, name: ".github/copilot-instructions.md" });
|
|
358
|
+
}
|
|
359
|
+
if (options.cursor) {
|
|
360
|
+
filesToCheck.push({ path: ".cursorrules", name: ".cursorrules" });
|
|
361
|
+
}
|
|
362
|
+
if (options.windsurf) {
|
|
363
|
+
filesToCheck.push({ path: ".windsurfrules", name: ".windsurfrules" });
|
|
364
|
+
}
|
|
365
|
+
const existingFiles = filesToCheck.filter((f) => existsSync3(join3(cwd, f.path)));
|
|
366
|
+
if (existingFiles.length > 0) {
|
|
367
|
+
console.log("\nExisting files found:");
|
|
368
|
+
for (const f of existingFiles) {
|
|
369
|
+
console.log(` - ${f.name}`);
|
|
370
|
+
}
|
|
371
|
+
const overwrite = await askYesNo("\nOverwrite these files?", false);
|
|
372
|
+
if (!overwrite) {
|
|
373
|
+
console.log("\nAborted. Use --force to overwrite without asking.\n");
|
|
374
|
+
rl.close();
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
options.force = true;
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
options.force = true;
|
|
381
|
+
}
|
|
382
|
+
console.log(`
|
|
383
|
+
Initializing agent memory in ${cwd}...
|
|
384
|
+
`);
|
|
385
|
+
const result = scaffold(cwd, options);
|
|
282
386
|
if (result.created.length > 0) {
|
|
283
|
-
console.log("
|
|
387
|
+
console.log("Created:");
|
|
284
388
|
for (const f of result.created) {
|
|
285
389
|
console.log(` \u2713 ${f}`);
|
|
286
390
|
}
|
|
@@ -291,18 +395,12 @@ function runInit() {
|
|
|
291
395
|
console.log(` - ${f}`);
|
|
292
396
|
}
|
|
293
397
|
}
|
|
294
|
-
if (result.created.length === 0 && result.skipped.length > 0) {
|
|
295
|
-
console.log("\nAll files already exist. Use --force to overwrite.");
|
|
296
|
-
}
|
|
297
398
|
console.log("\nNext steps:");
|
|
298
|
-
console.log(" 1. Edit
|
|
399
|
+
console.log(" 1. Edit your IDE config file \u2014 add project-specific rules");
|
|
299
400
|
console.log(" 2. Add spec files to .agents/spec/ for detailed documentation");
|
|
300
|
-
|
|
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
|
-
}
|
|
401
|
+
console.log(" 3. Agent will read rules and write to .agents/MEMORY.md automatically");
|
|
305
402
|
console.log("");
|
|
403
|
+
rl.close();
|
|
306
404
|
}
|
|
307
405
|
function runDoctor() {
|
|
308
406
|
const cwd = process.cwd();
|
|
@@ -325,7 +423,11 @@ function runDoctor() {
|
|
|
325
423
|
}
|
|
326
424
|
switch (command) {
|
|
327
425
|
case "init":
|
|
328
|
-
runInit()
|
|
426
|
+
runInit().catch((err) => {
|
|
427
|
+
console.error("Error:", err.message);
|
|
428
|
+
rl.close();
|
|
429
|
+
process.exit(1);
|
|
430
|
+
});
|
|
329
431
|
break;
|
|
330
432
|
case "doctor":
|
|
331
433
|
runDoctor();
|
|
@@ -333,9 +435,15 @@ switch (command) {
|
|
|
333
435
|
case "help":
|
|
334
436
|
case "--help":
|
|
335
437
|
case "-h":
|
|
336
|
-
case void 0:
|
|
337
438
|
printUsage();
|
|
338
439
|
break;
|
|
440
|
+
case void 0:
|
|
441
|
+
runInit().catch((err) => {
|
|
442
|
+
console.error("Error:", err.message);
|
|
443
|
+
rl.close();
|
|
444
|
+
process.exit(1);
|
|
445
|
+
});
|
|
446
|
+
break;
|
|
339
447
|
default:
|
|
340
448
|
console.error(`Unknown command: ${command}`);
|
|
341
449
|
printUsage();
|