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