@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/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,22 +17,24 @@ 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() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
const candidate2 = resolve(__dirname, "../../templates");
|
|
25
|
-
if (existsSync(candidate2)) return candidate2;
|
|
26
|
-
throw new Error("Cannot locate templates directory");
|
|
25
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const fromSource = resolve(thisDir, "../../templates");
|
|
27
|
+
if (existsSync(fromSource)) return fromSource;
|
|
28
|
+
const fromDist = resolve(thisDir, "../templates");
|
|
29
|
+
if (existsSync(fromDist)) return fromDist;
|
|
30
|
+
throw new Error(`Cannot locate templates directory (checked ${fromSource} and ${fromDist})`);
|
|
27
31
|
}
|
|
28
32
|
var TEMPLATES_DIR = getTemplatesDir();
|
|
29
33
|
function readTemplate(name) {
|
|
30
34
|
const templatePath = join(TEMPLATES_DIR, name);
|
|
35
|
+
if (!existsSync(templatePath)) {
|
|
36
|
+
throw new Error(`Template not found: ${templatePath}`);
|
|
37
|
+
}
|
|
31
38
|
return readFileSync(templatePath, "utf-8");
|
|
32
39
|
}
|
|
33
40
|
function applyVars(content, vars) {
|
|
@@ -37,15 +44,21 @@ function applyVars(content, vars) {
|
|
|
37
44
|
}
|
|
38
45
|
return result;
|
|
39
46
|
}
|
|
40
|
-
function scaffold(projectDir,
|
|
47
|
+
function scaffold(projectDir, options = {}) {
|
|
41
48
|
const result = { created: [], skipped: [] };
|
|
42
|
-
const projectName =
|
|
49
|
+
const projectName = options.projectName || guessProjectName(projectDir);
|
|
43
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;
|
|
44
57
|
const dirs = [
|
|
45
58
|
join(projectDir, AGENTS_DIR),
|
|
46
59
|
join(projectDir, SPEC_DIR)
|
|
47
60
|
];
|
|
48
|
-
if (
|
|
61
|
+
if (useCopilot) {
|
|
49
62
|
dirs.push(join(projectDir, ".github"));
|
|
50
63
|
}
|
|
51
64
|
for (const dir of dirs) {
|
|
@@ -53,18 +66,11 @@ function scaffold(projectDir, config = {}, force = false) {
|
|
|
53
66
|
mkdirSync(dir, { recursive: true });
|
|
54
67
|
}
|
|
55
68
|
}
|
|
56
|
-
const
|
|
57
|
-
{ target: AGENTS_MD, template: "AGENTS.md" },
|
|
69
|
+
const coreFiles = [
|
|
58
70
|
{ target: MEMORY_FILE, template: "MEMORY.md" },
|
|
59
71
|
{ target: TASKS_FILE, template: "TASKS.md" }
|
|
60
72
|
];
|
|
61
|
-
|
|
62
|
-
files.push({
|
|
63
|
-
target: COPILOT_INSTRUCTIONS,
|
|
64
|
-
template: "copilot-instructions.md"
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
for (const { target, template } of files) {
|
|
73
|
+
for (const { target, template } of coreFiles) {
|
|
68
74
|
const targetPath = join(projectDir, target);
|
|
69
75
|
if (existsSync(targetPath) && !force) {
|
|
70
76
|
result.skipped.push(target);
|
|
@@ -79,56 +85,51 @@ function scaffold(projectDir, config = {}, force = false) {
|
|
|
79
85
|
writeFileSync(specKeep, "");
|
|
80
86
|
result.created.push(`${SPEC_DIR}/.gitkeep`);
|
|
81
87
|
}
|
|
82
|
-
if (
|
|
83
|
-
|
|
88
|
+
if (useOpencode) {
|
|
89
|
+
writeFileIfNeeded(
|
|
90
|
+
join(projectDir, AGENTS_MD),
|
|
91
|
+
applyVars(readTemplate("AGENTS.md"), vars),
|
|
92
|
+
AGENTS_MD,
|
|
93
|
+
result,
|
|
94
|
+
force
|
|
95
|
+
);
|
|
84
96
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
+
);
|
|
93
105
|
}
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
if (useCursor) {
|
|
107
|
+
writeFileIfNeeded(
|
|
108
|
+
join(projectDir, CURSOR_RULES),
|
|
109
|
+
applyVars(readTemplate("cursorrules.md"), vars),
|
|
110
|
+
CURSOR_RULES,
|
|
111
|
+
result,
|
|
112
|
+
force
|
|
98
113
|
);
|
|
99
|
-
result.created.push(".opencode/package.json");
|
|
100
|
-
} else {
|
|
101
|
-
const pkg = JSON.parse(readFileSync(opencodePkgPath, "utf-8"));
|
|
102
|
-
const deps = pkg.dependencies || {};
|
|
103
|
-
if (!deps[PACKAGE_NAME] || force) {
|
|
104
|
-
deps[PACKAGE_NAME] = "latest";
|
|
105
|
-
pkg.dependencies = deps;
|
|
106
|
-
writeFileSync(opencodePkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
107
|
-
if (!deps[PACKAGE_NAME]) {
|
|
108
|
-
result.created.push(".opencode/package.json");
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
result.skipped.push(".opencode/package.json");
|
|
112
|
-
}
|
|
113
114
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
if (useWindsurf) {
|
|
116
|
+
writeFileIfNeeded(
|
|
117
|
+
join(projectDir, WINDSURF_RULES),
|
|
118
|
+
applyVars(readTemplate("windsurfrules.md"), vars),
|
|
119
|
+
WINDSURF_RULES,
|
|
120
|
+
result,
|
|
121
|
+
force
|
|
119
122
|
);
|
|
120
|
-
result.created.push("opencode.json");
|
|
121
|
-
} else {
|
|
122
|
-
const config = JSON.parse(readFileSync(opencodeJsonPath, "utf-8"));
|
|
123
|
-
const plugins = config.plugin || [];
|
|
124
|
-
if (!plugins.includes(PACKAGE_NAME)) {
|
|
125
|
-
config.plugin = [...plugins, PACKAGE_NAME];
|
|
126
|
-
writeFileSync(opencodeJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
127
|
-
result.created.push("opencode.json (merged plugin)");
|
|
128
|
-
} else {
|
|
129
|
-
result.skipped.push("opencode.json");
|
|
130
|
-
}
|
|
131
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);
|
|
132
133
|
}
|
|
133
134
|
function guessProjectName(dir) {
|
|
134
135
|
const pkgPath = join(dir, "package.json");
|
|
@@ -253,6 +254,32 @@ function doctor(projectDir) {
|
|
|
253
254
|
// bin/cli.ts
|
|
254
255
|
var args = process.argv.slice(2);
|
|
255
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
|
+
}
|
|
256
283
|
function printUsage() {
|
|
257
284
|
console.log(`
|
|
258
285
|
@vuau/agent-memory \u2014 Structured AI memory for codebases
|
|
@@ -263,27 +290,99 @@ Usage:
|
|
|
263
290
|
agent-memory help Show this help
|
|
264
291
|
|
|
265
292
|
Options (init):
|
|
266
|
-
--
|
|
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
|
|
267
299
|
--name <name> Project name (default: from package.json)
|
|
268
|
-
|
|
269
|
-
|
|
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
|
|
270
306
|
`);
|
|
271
307
|
}
|
|
272
|
-
function runInit() {
|
|
308
|
+
async function runInit() {
|
|
309
|
+
const cwd = process.cwd();
|
|
273
310
|
const force = args.includes("--force");
|
|
274
|
-
const
|
|
275
|
-
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");
|
|
276
316
|
const nameIdx = args.indexOf("--name");
|
|
277
317
|
const projectName = nameIdx !== -1 ? args[nameIdx + 1] : void 0;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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 = {
|
|
281
340
|
projectName,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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);
|
|
285
384
|
if (result.created.length > 0) {
|
|
286
|
-
console.log("
|
|
385
|
+
console.log("Created:");
|
|
287
386
|
for (const f of result.created) {
|
|
288
387
|
console.log(` \u2713 ${f}`);
|
|
289
388
|
}
|
|
@@ -294,18 +393,12 @@ function runInit() {
|
|
|
294
393
|
console.log(` - ${f}`);
|
|
295
394
|
}
|
|
296
395
|
}
|
|
297
|
-
if (result.created.length === 0 && result.skipped.length > 0) {
|
|
298
|
-
console.log("\nAll files already exist. Use --force to overwrite.");
|
|
299
|
-
}
|
|
300
396
|
console.log("\nNext steps:");
|
|
301
|
-
console.log(" 1. Edit
|
|
397
|
+
console.log(" 1. Edit your IDE config file \u2014 add project-specific rules");
|
|
302
398
|
console.log(" 2. Add spec files to .agents/spec/ for detailed documentation");
|
|
303
|
-
|
|
304
|
-
console.log(" 3. Restart OpenCode to activate the plugin");
|
|
305
|
-
} else {
|
|
306
|
-
console.log(" 3. For OpenCode: run with --opencode flag to wire up the plugin");
|
|
307
|
-
}
|
|
399
|
+
console.log(" 3. Agent will read rules and write to .agents/MEMORY.md automatically");
|
|
308
400
|
console.log("");
|
|
401
|
+
rl.close();
|
|
309
402
|
}
|
|
310
403
|
function runDoctor() {
|
|
311
404
|
const cwd = process.cwd();
|
|
@@ -328,7 +421,11 @@ function runDoctor() {
|
|
|
328
421
|
}
|
|
329
422
|
switch (command) {
|
|
330
423
|
case "init":
|
|
331
|
-
runInit()
|
|
424
|
+
runInit().catch((err) => {
|
|
425
|
+
console.error("Error:", err.message);
|
|
426
|
+
rl.close();
|
|
427
|
+
process.exit(1);
|
|
428
|
+
});
|
|
332
429
|
break;
|
|
333
430
|
case "doctor":
|
|
334
431
|
runDoctor();
|
|
@@ -336,9 +433,15 @@ switch (command) {
|
|
|
336
433
|
case "help":
|
|
337
434
|
case "--help":
|
|
338
435
|
case "-h":
|
|
339
|
-
case void 0:
|
|
340
436
|
printUsage();
|
|
341
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;
|
|
342
445
|
default:
|
|
343
446
|
console.error(`Unknown command: ${command}`);
|
|
344
447
|
printUsage();
|