@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/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, config = {}, force = false) {
47
+ function scaffold(projectDir, options = {}) {
38
48
  const result = { created: [], skipped: [] };
39
- const projectName = config.projectName || guessProjectName(projectDir);
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 (config.copilotInstructions !== false) {
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 files = [
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
- if (config.copilotInstructions !== false) {
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 (config.opencode) {
80
- 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
+ );
81
96
  }
82
- return result;
83
- }
84
- function scaffoldOpenCode(projectDir, result, force) {
85
- const PACKAGE_NAME = "@vuau/agent-memory";
86
- const opencodePkgPath = join(projectDir, ".opencode", "package.json");
87
- const opencodeDir = join(projectDir, ".opencode");
88
- if (!existsSync(opencodeDir)) {
89
- 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
+ );
90
105
  }
91
- if (!existsSync(opencodePkgPath)) {
92
- writeFileSync(
93
- opencodePkgPath,
94
- 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
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
- const opencodeJsonPath = join(projectDir, "opencode.json");
112
- if (!existsSync(opencodeJsonPath)) {
113
- writeFileSync(
114
- opencodeJsonPath,
115
- 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
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
- --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
264
299
  --name <name> Project name (default: from package.json)
265
- --no-copilot Skip .github/copilot-instructions.md
266
- --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
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 noCopilot = args.includes("--no-copilot");
272
- 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");
273
316
  const nameIdx = args.indexOf("--name");
274
317
  const projectName = nameIdx !== -1 ? args[nameIdx + 1] : void 0;
275
- const cwd = process.cwd();
276
- console.log(`Initializing agent memory in ${cwd}...`);
277
- 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 = {
278
340
  projectName,
279
- copilotInstructions: !noCopilot,
280
- opencode
281
- }, 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);
282
384
  if (result.created.length > 0) {
283
- console.log("\nCreated:");
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 AGENTS.md \u2014 add your project-specific rules");
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
- if (opencode) {
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();