rules-md 1.0.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.
Files changed (3) hide show
  1. package/README.md +73 -0
  2. package/index.js +574 -0
  3. package/package.json +28 -0
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Agent Rules 🤖
2
+
3
+ A command-line tool that allows you to manage a single **Source of Truth** markdown file for your AI coding instructions and links/syncs it to all your AI coding agents (Claude Code, Roo Code, Windsurf, Cline, Copilot, Aider, Codex, ZCode, OpenCode, Pi Agent, etc.).
4
+
5
+ ## Features
6
+
7
+ - **Centralized Source of Truth**: Define your persona, coding standards, preferred libraries, and workflow configurations in one place (defaults to `~/global-rules.md`).
8
+ - **Interactive Console UI**: An easy-to-use checklist terminal interface to select which agents to target.
9
+ - **Smart Merger**: If you already have existing rule files for specific agents, you can append their contents to your Source of Truth or back them up.
10
+ - **Robust Symlink/Hardlink Fallbacks**:
11
+ - Symlinks on Windows require Developer Mode or Administrator privileges.
12
+ - If a symlink fails, **Agent Rules** automatically falls back to a **Hard Link** (`fs.linkSync`), which behaves identically to a symlink for file edits but requires no special privileges!
13
+ - If both fail, it falls back to a copy.
14
+ - **Special Aider Support**: Automatically appends the Conventions file to your global `~/.aider.conf.yml` list so Aider loads it automatically.
15
+
16
+ ---
17
+
18
+ ## Installation & Running
19
+
20
+ Since it has zero external dependencies, you can run it directly using Node.js.
21
+
22
+ ### Run directly with Node
23
+
24
+ Navigate to this directory and run:
25
+
26
+ ```bash
27
+ node index.js
28
+ ```
29
+
30
+ ### Install globally (Optional)
31
+
32
+ To make the `rules-md` command available anywhere on your system:
33
+
34
+ ```bash
35
+ npm link
36
+ # or
37
+ npm install -g .
38
+ ```
39
+
40
+ Then simply run:
41
+
42
+ ```bash
43
+ rules-md
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Supported AI Coding Agents
49
+
50
+ | Agent | Global Instructions Path | Notes |
51
+ | ------------------------------ | ----------------------------------------------------------------------- | ------------------------------------------- |
52
+ | **Antigravity** | `~/.gemini/GEMINI.md` | Google Antigravity global agent rules file. |
53
+ | **Claude Code** | `~/.claude/CLAUDE.md` | Automatically loaded at startup. |
54
+ | **Roo Code / Roo Cline** | `~/.roo/rules/global_rules.md` | Reads all markdown files in rules folder. |
55
+ | **Windsurf (Cascade)** | `~/.codeium/windsurf/memories/global_rules.md` | Central memories configuration. |
56
+ | **Cline (Documents)** | `~/Documents/Cline/Rules/global_rules.md` | Cline's central rules directory. |
57
+ | **Cline / Common** | `~/.agents/AGENTS.md` | Shared standard agent instructions path. |
58
+ | **GitHub Copilot (JetBrains)** | `%LOCALAPPDATA%/github-copilot/intellij/global-copilot-instructions.md` | Local appdata path. |
59
+ | **GitHub Copilot (CLI)** | `~/.copilot/copilot-instructions.md` | Default CLI config. |
60
+ | **Aider** | `~/.aider.conventions.md` | Handled via `~/.aider.conf.yml` read block. |
61
+ | **Z.ai ZCode** | `~/.zcode/AGENTS.md` | Central ZCode instructions path. |
62
+ | **OpenCode** | `~/.config/opencode/AGENTS.md` | Standard OpenCode path. |
63
+ | **Pi Agent** | `~/.pi/agent/AGENTS.md` | Pi Agent instructions path. |
64
+ | **OpenAI Codex** | `~/.codex/AGENTS.md` | OpenAI Codex agent global rules file. |
65
+
66
+ ---
67
+
68
+ ## Editor Integration Tips
69
+
70
+ For tools that store rules in Settings UI or databases instead of the filesystem:
71
+
72
+ - **Cursor**: Open **Cursor Settings > General > Rules for AI** and copy-paste your rules there.
73
+ - **VS Code Copilot**: Add your source rules folder path to the `chat.instructionsFilesLocations` setting in VS Code.
package/index.js ADDED
@@ -0,0 +1,574 @@
1
+ #!/usr/bin/env node
2
+
3
+ const os = require("os");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const prompts = require("prompts");
7
+
8
+ // ANSI Color Codes
9
+ const colors = {
10
+ reset: "\x1b[0m",
11
+ bold: "\x1b[1m",
12
+ dim: "\x1b[2m",
13
+ green: "\x1b[32m",
14
+ cyan: "\x1b[36m",
15
+ yellow: "\x1b[33m",
16
+ red: "\x1b[31m",
17
+ blue: "\x1b[34m",
18
+ magenta: "\x1b[35m",
19
+ bgGreen: "\x1b[42m",
20
+ bgBlue: "\x1b[44m",
21
+ black: "\x1b[30m",
22
+ };
23
+
24
+ // Tool Configuration
25
+ const tools = [
26
+ {
27
+ id: "antigravity",
28
+ name: "Antigravity",
29
+ path: "~/.gemini/GEMINI.md",
30
+ desc: "Google Antigravity global agent rules file.",
31
+ },
32
+ {
33
+ id: "claude",
34
+ name: "Claude Code",
35
+ path: "~/.claude/CLAUDE.md",
36
+ desc: "Automatically read by Claude Code CLI at session start.",
37
+ },
38
+ {
39
+ id: "roo-code",
40
+ name: "Roo Code / Roo Cline",
41
+ path: "~/.roo/rules/global_rules.md",
42
+ desc: "Reads all markdown files in this rules directory.",
43
+ },
44
+ {
45
+ id: "windsurf",
46
+ name: "Windsurf (Cascade)",
47
+ path: "~/.codeium/windsurf/memories/global_rules.md",
48
+ desc: "Cascade's global rule memory file.",
49
+ },
50
+ {
51
+ id: "cline-docs",
52
+ name: "Cline (Documents Rules)",
53
+ path: "~/Documents/Cline/Rules/global_rules.md",
54
+ desc: "Cline global rules directory.",
55
+ },
56
+ {
57
+ id: "cline-agents",
58
+ name: "Common (AGENTS.md)",
59
+ path: "~/.agents/AGENTS.md",
60
+ desc: "Common path recognized by Cline and other agents.",
61
+ },
62
+ {
63
+ id: "copilot-jetbrains",
64
+ name: "GitHub Copilot (JetBrains)",
65
+ path:
66
+ process.platform === "win32"
67
+ ? "%LOCALAPPDATA%/github-copilot/intellij/global-copilot-instructions.md"
68
+ : process.platform === "darwin"
69
+ ? "~/Library/Application Support/github-copilot/intellij/global-copilot-instructions.md"
70
+ : "~/.config/github-copilot/intellij/global-copilot-instructions.md",
71
+ desc: "Global copilot instructions for JetBrains IDEs.",
72
+ },
73
+ {
74
+ id: "copilot-cli",
75
+ name: "GitHub Copilot (CLI)",
76
+ path: "~/.copilot/copilot-instructions.md",
77
+ desc: "Default Copilot CLI instructions file.",
78
+ },
79
+ {
80
+ id: "aider",
81
+ name: "Aider",
82
+ path: "~/.aider.conventions.md",
83
+ desc: "Global conventions file (will optionally add to ~/.aider.conf.yml).",
84
+ },
85
+ {
86
+ id: "zcode",
87
+ name: "Z.ai ZCode",
88
+ path: "~/.zcode/AGENTS.md",
89
+ desc: "ZCode Agentic IDE global instructions.",
90
+ },
91
+ {
92
+ id: "opencode",
93
+ name: "OpenCode",
94
+ path: "~/.config/opencode/AGENTS.md",
95
+ desc: "OpenCode central instructions path.",
96
+ },
97
+ {
98
+ id: "pi-agent",
99
+ name: "Pi Agent",
100
+ path: "~/.pi/agent/AGENTS.md",
101
+ desc: "Pi Coding Agent global rules.",
102
+ },
103
+ {
104
+ id: "openai-codex",
105
+ name: "OpenAI Codex",
106
+ path: "~/.codex/AGENTS.md",
107
+ desc: "Codex agent global instructions.",
108
+ },
109
+ ];
110
+
111
+ // Helper to resolve paths with env vars and tilde
112
+ function resolvePath(p) {
113
+ if (!p) return "";
114
+ let resolved = p;
115
+ // Replace Windows %LOCALAPPDATA% or similar env variables
116
+ resolved = resolved.replace(
117
+ /%([^%]+)%/g,
118
+ (_, name) => process.env[name] || "",
119
+ );
120
+ // Replace tilde with home directory
121
+ resolved = resolved.replace(/^\~/, os.homedir());
122
+ return path.resolve(resolved);
123
+ }
124
+
125
+ // Standard Markdown Seed Template
126
+ const defaultTemplate = `# Global AI Agent Rules & Preferences
127
+
128
+ This file serves as the single source of truth for all AI coding assistants and agents.
129
+
130
+ ## Coding Style & Rules
131
+ - Keep functions small, focused, and single-purpose.
132
+ - Always use descriptive variable and function names.
133
+ - Handle errors gracefully; log errors with appropriate context.
134
+ - Keep documentation up-to-date and maintain comment/docstring integrity.
135
+
136
+ ## Git & Workflow
137
+ - Use standard conventional commits (e.g., \`feat: add rules linker\`, \`fix: handle EPERM symlinks\`).
138
+ - Keep commits atomic; write clear, concise commit messages.
139
+ - Never commit secrets, credentials, or \`.env\` files.
140
+ `;
141
+
142
+ // Helper to back up files
143
+ function backupFile(filePath) {
144
+ if (!fs.existsSync(filePath)) return null;
145
+ const stats = fs.lstatSync(filePath);
146
+ if (stats.isSymbolicLink()) {
147
+ try {
148
+ fs.unlinkSync(filePath);
149
+ return "symlink_removed";
150
+ } catch (e) {
151
+ return null;
152
+ }
153
+ }
154
+
155
+ const dateStr = new Date().toISOString().replace(/[:.]/g, "-");
156
+ const backupPath = `${filePath}.bak_${dateStr}`;
157
+ fs.renameSync(filePath, backupPath);
158
+ return backupPath;
159
+ }
160
+
161
+ // Helper to create link/copy
162
+ function linkFile(sourceFile, targetFile, preserveOption) {
163
+ const targetDir = path.dirname(targetFile);
164
+ if (!fs.existsSync(targetDir)) {
165
+ fs.mkdirSync(targetDir, { recursive: true });
166
+ }
167
+
168
+ let stats = null;
169
+ try {
170
+ stats = fs.lstatSync(targetFile);
171
+ } catch (e) {}
172
+
173
+ if (stats) {
174
+ if (preserveOption === "append") {
175
+ try {
176
+ const existingContent = fs.readFileSync(targetFile, "utf8");
177
+ fs.appendFileSync(
178
+ sourceFile,
179
+ `\n\n## Imported from ${path.basename(targetFile)}\n\n${existingContent}`,
180
+ );
181
+ } catch (e) {
182
+ console.log(
183
+ ` ${colors.red}Error appending contents of ${path.basename(targetFile)}: ${e.message}${colors.reset}`,
184
+ );
185
+ }
186
+ backupFile(targetFile);
187
+ } else if (preserveOption === "backup") {
188
+ const backupPath = backupFile(targetFile);
189
+ if (backupPath && backupPath !== "symlink_removed") {
190
+ console.log(
191
+ ` ${colors.dim}Backed up existing file to ${path.basename(backupPath)}${colors.reset}`,
192
+ );
193
+ }
194
+ } else if (preserveOption === "overwrite") {
195
+ try {
196
+ fs.unlinkSync(targetFile);
197
+ } catch (e) {
198
+ try {
199
+ fs.rmSync(targetFile, { force: true });
200
+ } catch (err) {}
201
+ }
202
+ } else {
203
+ // Skip
204
+ return { success: false, skipped: true };
205
+ }
206
+ }
207
+
208
+ // Create link
209
+ try {
210
+ fs.symlinkSync(sourceFile, targetFile, "file");
211
+ return { success: true, type: "symlink" };
212
+ } catch (symlinkErr) {
213
+ // Fall back to hard link (requires no admin privileges on Windows)
214
+ try {
215
+ fs.linkSync(sourceFile, targetFile);
216
+ return { success: true, type: "hardlink" };
217
+ } catch (hardlinkErr) {
218
+ // Fall back to copy
219
+ try {
220
+ fs.copyFileSync(sourceFile, targetFile);
221
+ return { success: true, type: "copy" };
222
+ } catch (copyErr) {
223
+ return { success: false, error: copyErr };
224
+ }
225
+ }
226
+ }
227
+ }
228
+
229
+ // Check if a target file is linked to the source file (symlink or hardlink)
230
+ function isLinked(sourceFile, targetFile) {
231
+ try {
232
+ const lstats = fs.lstatSync(targetFile);
233
+
234
+ // 1. Check if it's a symbolic link
235
+ if (lstats.isSymbolicLink()) {
236
+ const linkTarget = fs.readlinkSync(targetFile);
237
+ const resolvedLinkTarget = path.resolve(
238
+ path.dirname(targetFile),
239
+ linkTarget,
240
+ );
241
+ return resolvedLinkTarget === path.resolve(sourceFile);
242
+ }
243
+
244
+ // 2. Check if it's a hard link (same inode and device)
245
+ if (fs.existsSync(sourceFile)) {
246
+ const sourceStats = fs.statSync(sourceFile);
247
+ const targetStats = fs.statSync(targetFile);
248
+ return (
249
+ sourceStats.dev === targetStats.dev &&
250
+ sourceStats.ino === targetStats.ino
251
+ );
252
+ }
253
+ } catch (e) {}
254
+ return false;
255
+ }
256
+
257
+ // Remove Aider conventions file config from ~/.aider.conf.yml
258
+ function removeAiderConfig() {
259
+ const configPath = resolvePath("~/.aider.conf.yml");
260
+ if (!fs.existsSync(configPath)) return;
261
+ let content = fs.readFileSync(configPath, "utf8");
262
+
263
+ const lines = content.split("\n");
264
+ const filteredLines = lines.filter(
265
+ (line) => !line.includes(".aider.conventions.md"),
266
+ );
267
+
268
+ fs.writeFileSync(configPath, filteredLines.join("\n"), "utf8");
269
+ }
270
+
271
+ // Update Aider config file
272
+ function updateAiderConfig() {
273
+ const configPath = resolvePath("~/.aider.conf.yml");
274
+ let content = "";
275
+ if (fs.existsSync(configPath)) {
276
+ content = fs.readFileSync(configPath, "utf8");
277
+ }
278
+
279
+ if (content.includes(".aider.conventions.md")) {
280
+ return "already_configured";
281
+ }
282
+
283
+ const relativePath = "~/.aider.conventions.md";
284
+ if (!content.trim()) {
285
+ content = `# Aider Configuration\nread:\n - ${relativePath}\n`;
286
+ } else if (content.includes("read:")) {
287
+ content = content.replace(/(read:\s*)/, `$1\n - ${relativePath}`);
288
+ } else {
289
+ content += `\n\n# Added by Agent Rules Linker\nread:\n - ${relativePath}\n`;
290
+ }
291
+
292
+ fs.writeFileSync(configPath, content, "utf8");
293
+ return "updated";
294
+ }
295
+
296
+ // Main flow
297
+ async function main() {
298
+ console.log(`
299
+ ┌────────────────────────────────────────────────────────┐
300
+ │ ${colors.bold}${colors.cyan}AGENT RULES${colors.reset} │
301
+ │ Sync your AI coding instructions centrally │
302
+ └────────────────────────────────────────────────────────┘
303
+ `);
304
+
305
+ // Handle Ctrl+C gracefully for prompts
306
+ const onCancel = (prompt) => {
307
+ console.log("\n" + colors.red + "Cancelled by user." + colors.reset);
308
+ process.exit(1);
309
+ };
310
+
311
+ // 1. Confirm Source of Truth file
312
+ const defaultSource = "~/global-rules.md";
313
+ const sourcePrompt = await prompts(
314
+ {
315
+ type: "text",
316
+ name: "source",
317
+ message: "Specify your global instructions file:",
318
+ initial: defaultSource,
319
+ },
320
+ { onCancel },
321
+ );
322
+
323
+ const sourceFile = resolvePath(sourcePrompt.source);
324
+ console.log(
325
+ `Global instructions path: ${colors.bold}${sourceFile}${colors.reset}\n`,
326
+ );
327
+
328
+ // 2. Initialize Source of Truth if missing
329
+ if (!fs.existsSync(sourceFile)) {
330
+ const confirmPrompt = await prompts(
331
+ {
332
+ type: "confirm",
333
+ name: "create",
334
+ message:
335
+ "Global instructions file does not exist. Create it with a standard template?",
336
+ initial: true,
337
+ },
338
+ { onCancel },
339
+ );
340
+
341
+ if (confirmPrompt.create) {
342
+ const sourceDir = path.dirname(sourceFile);
343
+ if (!fs.existsSync(sourceDir)) {
344
+ fs.mkdirSync(sourceDir, { recursive: true });
345
+ }
346
+ fs.writeFileSync(sourceFile, defaultTemplate, "utf8");
347
+ console.log(
348
+ `${colors.green}Created global instructions file at ${sourceFile}${colors.reset}\n`,
349
+ );
350
+ } else {
351
+ console.log(
352
+ `${colors.red}Abort: Global instructions file is required to proceed.${colors.reset}`,
353
+ );
354
+ process.exit(1);
355
+ }
356
+ }
357
+
358
+ // 3. Detect currently linked agents and map to choices
359
+ const choices = tools.map((t) => {
360
+ const targetFile = resolvePath(t.path);
361
+ const linked = isLinked(sourceFile, targetFile);
362
+ return {
363
+ title: t.name,
364
+ value: t,
365
+ description:
366
+ `${t.path} ${linked ? colors.cyan + "(linked)" + colors.reset : ""}`.trim(),
367
+ selected: linked,
368
+ };
369
+ });
370
+
371
+ // 4. Let user select tools
372
+ const agentsPrompt = await prompts(
373
+ {
374
+ type: "multiselect",
375
+ name: "agents",
376
+ message: "Select coding agents you want to link:",
377
+ choices,
378
+ hint: "- Space to toggle, Enter to confirm",
379
+ min: 0,
380
+ },
381
+ { onCancel },
382
+ );
383
+
384
+ const selectedTools = agentsPrompt.agents || [];
385
+ const previouslyLinked = tools.filter((t) =>
386
+ isLinked(sourceFile, resolvePath(t.path)),
387
+ );
388
+
389
+ // Tools to link: selected ones that are NOT currently linked
390
+ const newToolsToLink = selectedTools.filter(
391
+ (t) => !isLinked(sourceFile, resolvePath(t.path)),
392
+ );
393
+
394
+ // Tools to unlink: previously linked ones that are NOT selected anymore
395
+ const toolsToUnlink = previouslyLinked.filter(
396
+ (t) => !selectedTools.some((sel) => sel.id === t.id),
397
+ );
398
+
399
+ // If no changes needed
400
+ if (newToolsToLink.length === 0 && toolsToUnlink.length === 0) {
401
+ console.log(
402
+ `\n${colors.yellow}No changes needed. All active links are up to date.${colors.reset}`,
403
+ );
404
+ process.exit(0);
405
+ }
406
+
407
+ // 5. Unlink deselected tools
408
+ if (toolsToUnlink.length > 0) {
409
+ console.log(
410
+ `\n${colors.bold}Unlinking deselected agents:${colors.reset}`,
411
+ );
412
+ for (const tool of toolsToUnlink) {
413
+ const targetFile = resolvePath(tool.path);
414
+ console.log(
415
+ ` Unlinking ${colors.bold}${tool.name}${colors.reset}...`,
416
+ );
417
+ try {
418
+ const stats = fs.lstatSync(targetFile);
419
+ if (stats.isSymbolicLink()) {
420
+ fs.unlinkSync(targetFile);
421
+ } else {
422
+ fs.rmSync(targetFile, { force: true });
423
+ }
424
+ console.log(
425
+ ` ${colors.green}Unlinked successfully.${colors.reset}`,
426
+ );
427
+
428
+ if (tool.id === "aider") {
429
+ try {
430
+ removeAiderConfig();
431
+ console.log(
432
+ ` ${colors.dim}Cleaned Aider conventions from ~/.aider.conf.yml.${colors.reset}`,
433
+ );
434
+ } catch (e) {
435
+ console.log(
436
+ ` ${colors.yellow}Could not clean ~/.aider.conf.yml: ${e.message}${colors.reset}`,
437
+ );
438
+ }
439
+ }
440
+ } catch (e) {
441
+ console.log(
442
+ ` ${colors.red}Failed to unlink: ${e.message}${colors.reset}`,
443
+ );
444
+ }
445
+ }
446
+ }
447
+
448
+ // 6. Link new tools
449
+ if (newToolsToLink.length > 0) {
450
+ console.log(`\n${colors.bold}Linking new agents:${colors.reset}`);
451
+ const preservePrompt = await prompts(
452
+ {
453
+ type: "select",
454
+ name: "option",
455
+ message:
456
+ "If a target instructions file already exists for new agents, what should we do?",
457
+ choices: [
458
+ {
459
+ title: "Append & Link",
460
+ value: "append",
461
+ description:
462
+ "Append existing instructions to Source of Truth, back up the file, and link",
463
+ },
464
+ {
465
+ title: "Backup & Link",
466
+ value: "backup",
467
+ description: "Rename existing file to .bak and link",
468
+ },
469
+ {
470
+ title: "Overwrite & Link",
471
+ value: "overwrite",
472
+ description: "Directly delete existing file and link",
473
+ },
474
+ {
475
+ title: "Skip",
476
+ value: "skip",
477
+ description: "Do not modify the file for this agent",
478
+ },
479
+ ],
480
+ },
481
+ { onCancel },
482
+ );
483
+
484
+ const preserveOption = preservePrompt.option;
485
+ console.log();
486
+
487
+ let aiderSelected = false;
488
+ for (const tool of newToolsToLink) {
489
+ const targetFile = resolvePath(tool.path);
490
+ console.log(`Linking ${colors.bold}${tool.name}${colors.reset}...`);
491
+ console.log(` Target: ${colors.dim}${targetFile}${colors.reset}`);
492
+
493
+ const result = linkFile(sourceFile, targetFile, preserveOption);
494
+
495
+ if (result.skipped) {
496
+ console.log(` ${colors.yellow}Skipped.${colors.reset}`);
497
+ } else if (result.success) {
498
+ let typeLabel = "";
499
+ if (result.type === "symlink")
500
+ typeLabel = `${colors.green}Symlink created successfully.${colors.reset}`;
501
+ if (result.type === "hardlink")
502
+ typeLabel = `${colors.cyan}Hard link created successfully (Windows symlink fallback).${colors.reset}`;
503
+ if (result.type === "copy")
504
+ typeLabel = `${colors.yellow}Copy created successfully (linking not possible on this environment).${colors.reset}`;
505
+ console.log(` ${typeLabel}`);
506
+ } else {
507
+ console.log(
508
+ ` ${colors.red}Failed: ${result.error ? result.error.message : "Unknown error"}${colors.reset}`,
509
+ );
510
+ }
511
+
512
+ if (tool.id === "aider") {
513
+ aiderSelected = true;
514
+ }
515
+ }
516
+
517
+ // Aider Post-process
518
+ if (aiderSelected) {
519
+ console.log(`\n${colors.bold}Aider Post-Setup:${colors.reset}`);
520
+ const configureAiderPrompt = await prompts(
521
+ {
522
+ type: "confirm",
523
+ name: "configure",
524
+ message:
525
+ "Would you like to auto-update ~/.aider.conf.yml to load ~/.aider.conventions.md?",
526
+ initial: true,
527
+ },
528
+ { onCancel },
529
+ );
530
+
531
+ if (configureAiderPrompt.configure) {
532
+ try {
533
+ const aiderRes = updateAiderConfig();
534
+ if (aiderRes === "updated") {
535
+ console.log(
536
+ ` ${colors.green}Successfully updated ~/.aider.conf.yml.${colors.reset}`,
537
+ );
538
+ } else if (aiderRes === "already_configured") {
539
+ console.log(
540
+ ` ${colors.dim}Already configured in ~/.aider.conf.yml.${colors.reset}`,
541
+ );
542
+ }
543
+ } catch (e) {
544
+ console.log(
545
+ ` ${colors.red}Failed to update ~/.aider.conf.yml: ${e.message}${colors.reset}`,
546
+ );
547
+ }
548
+ }
549
+ }
550
+ }
551
+
552
+ // 7. Render tips for editors without file-based global rules
553
+ console.log(`
554
+ ┌────────────────────────────────────────────────────────┐
555
+ │ ✨ ${colors.bold}SUCCESSFULLY COMPLETED${colors.reset} │
556
+ │ │
557
+ │ Your central rule file is now linked. Any changes to │
558
+ │ your global instructions will automatically sync to │
559
+ │ the active agents! │
560
+ └────────────────────────────────────────────────────────┘
561
+
562
+ 💡 ${colors.bold}Editor integration tips:${colors.reset}
563
+ - ${colors.bold}Cursor:${colors.reset} Copy your rules to ${colors.cyan}Cursor Settings > General > Rules for AI${colors.reset}.
564
+ - ${colors.bold}VS Code Copilot Chat:${colors.reset} In settings, configure ${colors.cyan}chat.instructionsFilesLocations${colors.reset}
565
+ to point to the directory containing your source rules file.
566
+ `);
567
+ }
568
+
569
+ main().catch((err) => {
570
+ console.error(
571
+ `${colors.red}An error occurred: ${err.message}${colors.reset}`,
572
+ );
573
+ process.exit(1);
574
+ });
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "rules-md",
3
+ "version": "1.0.0",
4
+ "description": "Interactive CLI tool to symlink/hardlink coding agent rules to a central Source of Truth",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "rules-md": "index.js"
8
+ },
9
+ "type": "commonjs",
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "keywords": [
14
+ "ai",
15
+ "agent",
16
+ "rules",
17
+ "claude",
18
+ "codex",
19
+ "cursor",
20
+ "copilot",
21
+ "aider",
22
+ "windsurf"
23
+ ],
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "prompts": "^2.4.2"
27
+ }
28
+ }