@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/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
- try {
19
- const thisDir = dirname(fileURLToPath(import.meta.url));
20
- const candidate = resolve(thisDir, "../../templates");
21
- if (existsSync(candidate)) return candidate;
22
- } catch {
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, config = {}, force = false) {
47
+ function scaffold(projectDir, options = {}) {
41
48
  const result = { created: [], skipped: [] };
42
- const projectName = config.projectName || guessProjectName(projectDir);
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 (config.copilotInstructions !== false) {
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 files = [
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
- if (config.copilotInstructions !== false) {
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 (config.opencode) {
83
- scaffoldOpenCode(projectDir, result, force);
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
- return result;
86
- }
87
- function scaffoldOpenCode(projectDir, result, force) {
88
- const PACKAGE_NAME = "@vuau/agent-memory";
89
- const opencodePkgPath = join(projectDir, ".opencode", "package.json");
90
- const opencodeDir = join(projectDir, ".opencode");
91
- if (!existsSync(opencodeDir)) {
92
- mkdirSync(opencodeDir, { recursive: true });
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 (!existsSync(opencodePkgPath)) {
95
- writeFileSync(
96
- opencodePkgPath,
97
- JSON.stringify({ dependencies: { [PACKAGE_NAME]: "latest" } }, null, 2) + "\n"
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
- const opencodeJsonPath = join(projectDir, "opencode.json");
115
- if (!existsSync(opencodeJsonPath)) {
116
- writeFileSync(
117
- opencodeJsonPath,
118
- JSON.stringify({ plugin: [PACKAGE_NAME] }, null, 2) + "\n"
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
- --force Overwrite existing files
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
- --no-copilot Skip .github/copilot-instructions.md
269
- --opencode Wire up OpenCode plugin (.opencode/package.json + opencode.json)
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 noCopilot = args.includes("--no-copilot");
275
- const opencode = args.includes("--opencode");
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
- const cwd = process.cwd();
279
- console.log(`Initializing agent memory in ${cwd}...`);
280
- const result = scaffold(cwd, {
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
- copilotInstructions: !noCopilot,
283
- opencode
284
- }, force);
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("\nCreated:");
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 AGENTS.md \u2014 add your project-specific rules");
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
- if (opencode) {
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();