@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/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, config = {}, force = false) {
48
+ function scaffold(projectDir, options = {}) {
38
49
  const result = { created: [], skipped: [] };
39
- const projectName = config.projectName || guessProjectName(projectDir);
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 (config.copilotInstructions !== false) {
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 files = [
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
- 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) {
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 (config.opencode) {
80
- scaffoldOpenCode(projectDir, result, force);
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
- 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 });
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 (!existsSync(opencodePkgPath)) {
92
- writeFileSync(
93
- opencodePkgPath,
94
- JSON.stringify({ dependencies: { [PACKAGE_NAME]: "latest" } }, null, 2) + "\n"
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
- const opencodeJsonPath = join(projectDir, "opencode.json");
112
- if (!existsSync(opencodeJsonPath)) {
113
- writeFileSync(
114
- opencodeJsonPath,
115
- JSON.stringify({ plugin: [PACKAGE_NAME] }, null, 2) + "\n"
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
- --force Overwrite existing files
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
- --no-copilot Skip .github/copilot-instructions.md
266
- --opencode Wire up OpenCode plugin (.opencode/package.json + opencode.json)
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 noCopilot = args.includes("--no-copilot");
272
- const opencode = args.includes("--opencode");
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
- const cwd = process.cwd();
276
- console.log(`Initializing agent memory in ${cwd}...`);
277
- const result = scaffold(cwd, {
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
- copilotInstructions: !noCopilot,
280
- opencode
281
- }, force);
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("\nCreated:");
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 AGENTS.md \u2014 add your project-specific rules");
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
- 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
- }
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();