@mayurdluffy/cbm-cli 1.0.7

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.
@@ -0,0 +1,93 @@
1
+ const path = require("path");
2
+ const os = require("os");
3
+
4
+ const SCRIPT_VERSION = require("../package.json").version;
5
+
6
+ const AGENTS = [
7
+ {
8
+ name: "Claude Code",
9
+ config: ".mcp.json",
10
+ format: "json",
11
+ globalDir: () => path.join(os.homedir(), ".claude"),
12
+ supportsMcpConfig: true,
13
+ },
14
+ {
15
+ name: "Codex CLI",
16
+ config: ".codex/config.toml",
17
+ format: "toml",
18
+ globalDir: () => path.join(os.homedir(), ".codex"),
19
+ supportsMcpConfig: true,
20
+ },
21
+ {
22
+ name: "Gemini CLI",
23
+ config: ".gemini/settings.json",
24
+ format: "json",
25
+ globalDir: () => path.join(os.homedir(), ".gemini"),
26
+ supportsMcpConfig: true,
27
+ },
28
+ {
29
+ name: "Zed",
30
+ config: ".zed/settings.json",
31
+ format: "jsonc",
32
+ globalDir: () => path.join(os.homedir(), ".config", "zed"),
33
+ supportsMcpConfig: true,
34
+ },
35
+ {
36
+ name: "OpenCode",
37
+ config: ".opencode/opencode.json",
38
+ format: "opencode-json",
39
+ globalDir: () => path.join(os.homedir(), ".opencode"),
40
+ supportsMcpConfig: true,
41
+ },
42
+ {
43
+ name: "Antigravity",
44
+ config: ".gemini/settings.json",
45
+ format: "json",
46
+ globalDir: () => path.join(os.homedir(), ".gemini"),
47
+ supportsMcpConfig: true,
48
+ sharesConfigWith: "Gemini CLI",
49
+ },
50
+ {
51
+ name: "Aider",
52
+ config: ".aider.conf.yml",
53
+ format: "yml",
54
+ globalDir: () => os.homedir(),
55
+ supportsMcpConfig: false,
56
+ },
57
+ {
58
+ name: "KiloCode",
59
+ config: ".kilo/kilo.jsonc",
60
+ format: "kilo-jsonc",
61
+ globalDir: () => path.join(os.homedir(), ".config", "kilo"),
62
+ supportsMcpConfig: true,
63
+ },
64
+ {
65
+ name: "VS Code",
66
+ config: ".vscode/mcp.json",
67
+ format: "vscode-json",
68
+ globalDir: () => path.join(os.homedir(), ".vscode"),
69
+ supportsMcpConfig: true,
70
+ },
71
+ {
72
+ name: "OpenClaw",
73
+ config: ".openclaw/openclaw.json",
74
+ format: "openclaw-json",
75
+ globalDir: () => path.join(os.homedir(), ".openclaw"),
76
+ supportsMcpConfig: true,
77
+ },
78
+ {
79
+ name: "Kiro",
80
+ config: ".kiro/settings/mcp.json",
81
+ format: "json",
82
+ globalDir: () => path.join(os.homedir(), ".kiro"),
83
+ supportsMcpConfig: true,
84
+ },
85
+ ];
86
+
87
+ const NUM_AGENTS = AGENTS.length;
88
+
89
+ module.exports = {
90
+ SCRIPT_VERSION,
91
+ AGENTS,
92
+ NUM_AGENTS,
93
+ };
package/lib/git.js ADDED
@@ -0,0 +1,67 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { C, log } = require("./logger");
4
+
5
+ const GITIGNORE_ENTRY = `\n# CBM\n.codebase-memory/*\n!.codebase-memory/graph.db.zst\n!.codebase-memory/cbm-server.sh\n`;
6
+
7
+ const GITATTRIBUTES_ENTRY = `\n# CBM\n.codebase-memory/graph.db.zst merge=ours\n`;
8
+
9
+ function updateGitignore(projectDir) {
10
+ const gitignorePath = path.join(projectDir, ".gitignore");
11
+
12
+ if (fs.existsSync(gitignorePath)) {
13
+ const content = fs.readFileSync(gitignorePath, "utf8");
14
+ if (!content.includes(".codebase-memory/")) {
15
+ fs.appendFileSync(gitignorePath, GITIGNORE_ENTRY);
16
+ log.ok(`Updated ${C.cyan}.gitignore${C.reset}`);
17
+ } else {
18
+ log.ok(`${C.cyan}.gitignore${C.reset} already configured`);
19
+ }
20
+ } else {
21
+ fs.writeFileSync(gitignorePath, GITIGNORE_ENTRY.trimStart());
22
+ log.ok(`Created ${C.cyan}.gitignore${C.reset}`);
23
+ }
24
+ }
25
+
26
+ function updateGitattributes(projectDir) {
27
+ const gitattributesPath = path.join(projectDir, ".gitattributes");
28
+
29
+ if (fs.existsSync(gitattributesPath)) {
30
+ const content = fs.readFileSync(gitattributesPath, "utf8");
31
+ if (!content.includes("graph.db.zst")) {
32
+ fs.appendFileSync(gitattributesPath, GITATTRIBUTES_ENTRY);
33
+ log.ok(`Updated ${C.cyan}.gitattributes${C.reset}`);
34
+ }
35
+ } else {
36
+ fs.writeFileSync(gitattributesPath, GITATTRIBUTES_ENTRY.trimStart());
37
+ log.ok(`Created ${C.cyan}.gitattributes${C.reset}`);
38
+ }
39
+ }
40
+
41
+ function cleanupTempFiles(cbmDir) {
42
+ const tempPatterns = [".db", ".db-shm", ".db-wal", "_config.db"];
43
+
44
+ for (const file of fs.readdirSync(cbmDir)) {
45
+ if (tempPatterns.some((pattern) => file.endsWith(pattern))) {
46
+ fs.unlinkSync(path.join(cbmDir, file));
47
+ }
48
+ }
49
+ }
50
+
51
+ function getArtifactInfo(projectDir) {
52
+ const zstPath = path.join(projectDir, ".codebase-memory", "graph.db.zst");
53
+
54
+ if (fs.existsSync(zstPath)) {
55
+ const size = (fs.statSync(zstPath).size / 1024 / 1024).toFixed(2);
56
+ return { exists: true, path: zstPath, size };
57
+ }
58
+
59
+ return { exists: false, path: zstPath, size: 0 };
60
+ }
61
+
62
+ module.exports = {
63
+ updateGitignore,
64
+ updateGitattributes,
65
+ cleanupTempFiles,
66
+ getArtifactInfo,
67
+ };
package/lib/index.js ADDED
@@ -0,0 +1,111 @@
1
+ const { log } = require("./logger");
2
+ const {
3
+ cmdInstall,
4
+ cmdBuild,
5
+ cmdUse,
6
+ cmdReindex,
7
+ cmdStatus,
8
+ cmdUpdate,
9
+ cmdVersion,
10
+ cmdKill,
11
+ cmdUninstall,
12
+ cmdHelp,
13
+ passThrough,
14
+ } = require("./commands");
15
+ const { parseAgentOption } = require("./ui");
16
+
17
+ const COMMANDS = {
18
+ install: cmdInstall,
19
+ i: cmdInstall,
20
+ build: cmdBuild,
21
+ b: cmdBuild,
22
+ use: cmdUse,
23
+ u: cmdUse,
24
+ "re-index": cmdReindex,
25
+ reindex: cmdReindex,
26
+ ri: cmdReindex,
27
+ status: cmdStatus,
28
+ s: cmdStatus,
29
+ update: cmdUpdate,
30
+ up: cmdUpdate,
31
+ version: cmdVersion,
32
+ v: cmdVersion,
33
+ "--version": cmdVersion,
34
+ "-V": cmdVersion,
35
+ "-v": cmdVersion,
36
+ uninstall: cmdUninstall,
37
+ remove: cmdUninstall,
38
+ rm: cmdUninstall,
39
+ kill: cmdKill,
40
+ k: cmdKill,
41
+ help: cmdHelp,
42
+ "--help": cmdHelp,
43
+ "-h": cmdHelp,
44
+ "": cmdHelp,
45
+ };
46
+
47
+ function parseOptions(argv) {
48
+ const options = {
49
+ yes: false,
50
+ agents: null,
51
+ };
52
+
53
+ for (let i = 0; i < argv.length; i++) {
54
+ const arg = argv[i];
55
+ if (arg === "--yes" || arg === "-y") {
56
+ options.yes = true;
57
+ } else if (arg === "--agents" || arg === "-a") {
58
+ options.agents = argv[i + 1];
59
+ i++;
60
+ } else if (arg.startsWith("--agents=")) {
61
+ options.agents = arg.slice("--agents=".length);
62
+ }
63
+ }
64
+
65
+ // Validate and normalize agents option
66
+ if (options.agents) {
67
+ const parsed = parseAgentOption(options.agents);
68
+ if (parsed.length === 0) {
69
+ log.error(`Invalid --agents value: ${options.agents}`);
70
+ process.exit(1);
71
+ }
72
+ options.agents = parsed.join(",");
73
+ }
74
+
75
+ return options;
76
+ }
77
+
78
+ function stripFlags(args) {
79
+ const result = [];
80
+ for (let i = 0; i < args.length; i++) {
81
+ const arg = args[i];
82
+ if (arg === "--yes" || arg === "-y") {
83
+ continue;
84
+ }
85
+ if (arg === "--agents" || arg === "-a") {
86
+ i++;
87
+ continue;
88
+ }
89
+ if (arg.startsWith("--agents=")) {
90
+ continue;
91
+ }
92
+ result.push(arg);
93
+ }
94
+ return result;
95
+ }
96
+
97
+ async function main() {
98
+ const rawArgs = process.argv.slice(2);
99
+ const options = parseOptions(rawArgs);
100
+ const args = stripFlags(rawArgs);
101
+ const command = args[0] || "help";
102
+ const handler = COMMANDS[command];
103
+
104
+ if (handler) {
105
+ await handler(options);
106
+ } else {
107
+ await passThrough(args, options);
108
+ }
109
+ }
110
+
111
+ module.exports = { main, COMMANDS, parseOptions, stripFlags };
package/lib/logger.js ADDED
@@ -0,0 +1,21 @@
1
+ const C = {
2
+ green: "\x1b[32m",
3
+ red: "\x1b[31m",
4
+ yellow: "\x1b[33m",
5
+ cyan: "\x1b[36m",
6
+ magenta: "\x1b[35m",
7
+ blue: "\x1b[34m",
8
+ bold: "\x1b[1m",
9
+ dim: "\x1b[2m",
10
+ reset: "\x1b[0m",
11
+ };
12
+
13
+ const log = {
14
+ info: (msg) => console.log(`${C.blue}ℹ ${C.reset} ${msg}`),
15
+ ok: (msg) => console.log(`${C.green}✔${C.reset} ${msg}`),
16
+ warn: (msg) => console.log(`${C.yellow}⚠${C.reset} ${msg}`),
17
+ error: (msg) => console.error(`${C.red}✖${C.reset} ${msg}`),
18
+ step: (msg) => console.log(`${C.cyan}▸${C.reset} ${msg}`),
19
+ };
20
+
21
+ module.exports = { C, log };
package/lib/system.js ADDED
@@ -0,0 +1,49 @@
1
+ const { execSync } = require("child_process");
2
+ const os = require("os");
3
+ const path = require("path");
4
+
5
+ function isWindows() {
6
+ return process.platform === "win32";
7
+ }
8
+
9
+ function getHomeDir() {
10
+ return os.homedir();
11
+ }
12
+
13
+ function findBinary(name) {
14
+ try {
15
+ const cmd = isWindows() ? "where" : "which";
16
+ const result = execSync(`${cmd} ${name}`, { stdio: "pipe", encoding: "utf8" })
17
+ .trim()
18
+ .split("\n")[0];
19
+ return result || null;
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+
25
+ function binaryExists(name = "codebase-memory-mcp") {
26
+ return findBinary(name) !== null;
27
+ }
28
+
29
+ function getLocalBinDir() {
30
+ return path.join(getHomeDir(), ".local", "bin");
31
+ }
32
+
33
+ function getCacheDir() {
34
+ return path.join(getHomeDir(), ".cache", "codebase-memory-mcp");
35
+ }
36
+
37
+ function getConfigDir() {
38
+ return path.join(getHomeDir(), ".config", "codebase-memory-mcp");
39
+ }
40
+
41
+ module.exports = {
42
+ isWindows,
43
+ getHomeDir,
44
+ findBinary,
45
+ binaryExists,
46
+ getLocalBinDir,
47
+ getCacheDir,
48
+ getConfigDir,
49
+ };
package/lib/ui.js ADDED
@@ -0,0 +1,190 @@
1
+ const { C, log } = require("./logger");
2
+ const { AGENTS, NUM_AGENTS } = require("./constants");
3
+
4
+ function showBanner() {
5
+ console.log(`${C.magenta}`);
6
+ console.log(" ██████╗ ██████╗ ███╗ ███╗");
7
+ console.log(" ██╔════╝ ██╔══██╗████╗ ████║");
8
+ console.log(" ██║ ██████╔╝██╔████╔██║");
9
+ console.log(" ██║ ██╔══██╗██║╚██╔╝██║");
10
+ console.log(" ╚██████╗ ██████╔╝██║ ╚═╝ ██║");
11
+ console.log(" ╚═════╝ ╚═════╝ ╚═╝ ╚═╝");
12
+ console.log(`${C.reset}`);
13
+ console.log(` ${C.bold}Codebase Memory Manager${C.reset}`);
14
+ console.log(
15
+ ` - ${C.cyan}Supercharge your agentic coding — without the token bleed${C.reset}`,
16
+ );
17
+ console.log("");
18
+ }
19
+
20
+ function formatBox(lines) {
21
+ const width = 60;
22
+ const border = `${C.green}╔${"═".repeat(width)}╗${C.reset}`;
23
+ const bottom = `${C.green}╚${"═".repeat(width)}╝${C.reset}`;
24
+
25
+ console.log(border);
26
+ for (const line of lines) {
27
+ const padding = width - line.replace(/\x1b\[[0-9;]*m/g, "").length;
28
+ console.log(`${C.green}║${C.reset} ${line}${" ".repeat(Math.max(0, padding - 1))}${C.green}║${C.reset}`);
29
+ }
30
+ console.log(bottom);
31
+ }
32
+
33
+ function parseAgentOption(value) {
34
+ if (!value) return [];
35
+ return value
36
+ .split(",")
37
+ .map((s) => parseInt(s.trim(), 10))
38
+ .filter((n) => Number.isInteger(n) && n >= 1 && n <= NUM_AGENTS);
39
+ }
40
+
41
+ async function selectAgents(prompt, options = {}) {
42
+ const explicitAgents = parseAgentOption(options.agents);
43
+
44
+ if (explicitAgents.length > 0) {
45
+ const names = explicitAgents.map((n) => AGENTS[n - 1].name).join(", ");
46
+ log.ok(`Selected: ${C.cyan}${names}${C.reset}`);
47
+ return explicitAgents;
48
+ }
49
+
50
+ if (options.yes || !process.stdin.isTTY) {
51
+ const all = Array.from({ length: NUM_AGENTS }, (_, i) => i + 1);
52
+ if (options.yes) {
53
+ log.ok(`Selected: ${C.cyan}all agents${C.reset}`);
54
+ }
55
+ return all;
56
+ }
57
+
58
+ console.log("");
59
+ console.log(` ${C.bold}${prompt}${C.reset}`);
60
+ console.log(
61
+ ` ${C.dim}↑↓ navigate · space select multiple · enter confirm${C.reset}`,
62
+ );
63
+ console.log("");
64
+
65
+ const selected = new Array(NUM_AGENTS).fill(false);
66
+ let current = 0;
67
+
68
+ process.stdout.write("\x1B[?25l");
69
+
70
+ function draw() {
71
+ for (let i = 0; i < NUM_AGENTS; i++) {
72
+ process.stdout.write("\x1B[K");
73
+ const mark = selected[i]
74
+ ? `${C.green}●${C.reset}`
75
+ : `${C.dim}○${C.reset}`;
76
+ const name = AGENTS[i].name;
77
+ const note = !AGENTS[i].supportsMcpConfig
78
+ ? ` ${C.yellow}[manual setup]${C.reset}`
79
+ : "";
80
+ if (i === current) {
81
+ console.log(
82
+ ` ${C.cyan}❯${C.reset} ${mark} ${C.bold}${name}${C.reset}${note}`,
83
+ );
84
+ } else {
85
+ console.log(` ${mark} ${C.dim}${name}${C.reset}${note}`);
86
+ }
87
+ }
88
+ }
89
+
90
+ draw();
91
+
92
+ return new Promise((resolve) => {
93
+ const stdin = process.stdin;
94
+ stdin.setRawMode(true);
95
+ stdin.resume();
96
+ stdin.setEncoding("utf8");
97
+
98
+ function cleanup() {
99
+ stdin.setRawMode(false);
100
+ stdin.pause();
101
+ process.stdout.write("\x1B[?25h");
102
+ }
103
+
104
+ function clearMenu() {
105
+ for (let i = 0; i < NUM_AGENTS + 3; i++) {
106
+ process.stdout.write("\x1B[1A\x1B[K");
107
+ }
108
+ }
109
+
110
+ function redraw() {
111
+ process.stdout.write(`\x1B[${NUM_AGENTS}A`);
112
+ draw();
113
+ }
114
+
115
+ let escapeBuffer = "";
116
+
117
+ stdin.on("data", (chunk) => {
118
+ for (const char of chunk) {
119
+ const code = char.charCodeAt(0);
120
+
121
+ if (escapeBuffer.length > 0) {
122
+ escapeBuffer += char;
123
+ if (escapeBuffer.length === 2 && char === "[") {
124
+ continue;
125
+ }
126
+ if (escapeBuffer.length === 3) {
127
+ const seq = escapeBuffer;
128
+ escapeBuffer = "";
129
+ if (seq === "\u001b[A" || seq === "\u001bOA") {
130
+ current = Math.max(0, current - 1);
131
+ redraw();
132
+ } else if (seq === "\u001b[B" || seq === "\u001bOB") {
133
+ current = Math.min(NUM_AGENTS - 1, current + 1);
134
+ redraw();
135
+ }
136
+ }
137
+ continue;
138
+ }
139
+
140
+ if (code === 27) {
141
+ escapeBuffer = char;
142
+ continue;
143
+ }
144
+
145
+ switch (code) {
146
+ case 3:
147
+ cleanup();
148
+ process.exit(0);
149
+ break;
150
+ case 13:
151
+ case 10:
152
+ cleanup();
153
+ clearMenu();
154
+
155
+ if (!selected.includes(true)) {
156
+ selected[current] = true;
157
+ }
158
+
159
+ const result = selected
160
+ .map((s, i) => (s ? i + 1 : null))
161
+ .filter(Boolean);
162
+ if (result.length > 0) {
163
+ const names = result.map((n) => AGENTS[n - 1].name).join(", ");
164
+ log.ok(`Selected: ${C.cyan}${names}${C.reset}`);
165
+ } else {
166
+ log.info("No agents selected");
167
+ }
168
+ resolve(result);
169
+ return;
170
+ case 32:
171
+ selected[current] = !selected[current];
172
+ redraw();
173
+ break;
174
+ case 97:
175
+ case 65:
176
+ for (let i = 0; i < NUM_AGENTS; i++) selected[i] = true;
177
+ redraw();
178
+ break;
179
+ case 110:
180
+ case 78:
181
+ for (let i = 0; i < NUM_AGENTS; i++) selected[i] = false;
182
+ redraw();
183
+ break;
184
+ }
185
+ }
186
+ });
187
+ });
188
+ }
189
+
190
+ module.exports = { showBanner, formatBox, selectAgents, parseAgentOption };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@mayurdluffy/cbm-cli",
3
+ "version": "1.0.7",
4
+ "description": "The ultimate manager for codebase-memory-mcp — install, build, share, and query codebase graphs with one command",
5
+ "main": "bin/cbm.js",
6
+ "bin": {
7
+ "cbm": "bin/cbm.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node --test",
11
+ "lint": "echo 'No linter configured'",
12
+ "prepare": "node -e \"require('fs').chmodSync('bin/cbm.js', 0o755)\""
13
+ },
14
+ "keywords": [
15
+ "codebase-memory-mcp",
16
+ "mcp",
17
+ "codebase-graph",
18
+ "ai-coding",
19
+ "agentic-coding",
20
+ "token-savings",
21
+ "cli",
22
+ "developer-tools",
23
+ "claude",
24
+ "opencode",
25
+ "aider",
26
+ "zed",
27
+ "vscode"
28
+ ],
29
+ "author": "mayurdluffy <mayurdluffy@gmail.com>",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/mayurdluffy/cbm-cli.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/mayurdluffy/cbm-cli/issues"
37
+ },
38
+ "homepage": "https://github.com/mayurdluffy/cbm-cli#readme",
39
+ "engines": {
40
+ "node": ">=18.0.0"
41
+ },
42
+ "files": [
43
+ "bin/",
44
+ "lib/",
45
+ "README.md",
46
+ "LICENSE"
47
+ ]
48
+ }