ai-mind-map 1.1.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 (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +554 -0
  3. package/dist/change-tracker/change-log.d.ts +160 -0
  4. package/dist/change-tracker/change-log.d.ts.map +1 -0
  5. package/dist/change-tracker/change-log.js +507 -0
  6. package/dist/change-tracker/change-log.js.map +1 -0
  7. package/dist/change-tracker/diff-engine.d.ts +149 -0
  8. package/dist/change-tracker/diff-engine.d.ts.map +1 -0
  9. package/dist/change-tracker/diff-engine.js +530 -0
  10. package/dist/change-tracker/diff-engine.js.map +1 -0
  11. package/dist/change-tracker/watcher.d.ts +137 -0
  12. package/dist/change-tracker/watcher.d.ts.map +1 -0
  13. package/dist/change-tracker/watcher.js +300 -0
  14. package/dist/change-tracker/watcher.js.map +1 -0
  15. package/dist/cli.d.ts +20 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +937 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/config.d.ts +38 -0
  20. package/dist/config.d.ts.map +1 -0
  21. package/dist/config.js +222 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/context/compressor.d.ts +49 -0
  24. package/dist/context/compressor.d.ts.map +1 -0
  25. package/dist/context/compressor.js +769 -0
  26. package/dist/context/compressor.js.map +1 -0
  27. package/dist/context/progressive-disclosure.d.ts +71 -0
  28. package/dist/context/progressive-disclosure.d.ts.map +1 -0
  29. package/dist/context/progressive-disclosure.js +470 -0
  30. package/dist/context/progressive-disclosure.js.map +1 -0
  31. package/dist/context/token-budget.d.ts +121 -0
  32. package/dist/context/token-budget.d.ts.map +1 -0
  33. package/dist/context/token-budget.js +282 -0
  34. package/dist/context/token-budget.js.map +1 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +944 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/install.d.ts +66 -0
  40. package/dist/install.d.ts.map +1 -0
  41. package/dist/install.js +946 -0
  42. package/dist/install.js.map +1 -0
  43. package/dist/knowledge-graph/architecture.d.ts +213 -0
  44. package/dist/knowledge-graph/architecture.d.ts.map +1 -0
  45. package/dist/knowledge-graph/architecture.js +585 -0
  46. package/dist/knowledge-graph/architecture.js.map +1 -0
  47. package/dist/knowledge-graph/cypher.d.ts +113 -0
  48. package/dist/knowledge-graph/cypher.d.ts.map +1 -0
  49. package/dist/knowledge-graph/cypher.js +1051 -0
  50. package/dist/knowledge-graph/cypher.js.map +1 -0
  51. package/dist/knowledge-graph/dead-code.d.ts +121 -0
  52. package/dist/knowledge-graph/dead-code.d.ts.map +1 -0
  53. package/dist/knowledge-graph/dead-code.js +331 -0
  54. package/dist/knowledge-graph/dead-code.js.map +1 -0
  55. package/dist/knowledge-graph/flow-analyzer.d.ts +167 -0
  56. package/dist/knowledge-graph/flow-analyzer.d.ts.map +1 -0
  57. package/dist/knowledge-graph/flow-analyzer.js +739 -0
  58. package/dist/knowledge-graph/flow-analyzer.js.map +1 -0
  59. package/dist/knowledge-graph/graph.d.ts +291 -0
  60. package/dist/knowledge-graph/graph.d.ts.map +1 -0
  61. package/dist/knowledge-graph/graph.js +978 -0
  62. package/dist/knowledge-graph/graph.js.map +1 -0
  63. package/dist/knowledge-graph/index.d.ts +17 -0
  64. package/dist/knowledge-graph/index.d.ts.map +1 -0
  65. package/dist/knowledge-graph/index.js +14 -0
  66. package/dist/knowledge-graph/index.js.map +1 -0
  67. package/dist/knowledge-graph/indexer.d.ts +112 -0
  68. package/dist/knowledge-graph/indexer.d.ts.map +1 -0
  69. package/dist/knowledge-graph/indexer.js +506 -0
  70. package/dist/knowledge-graph/indexer.js.map +1 -0
  71. package/dist/knowledge-graph/pagerank.d.ts +141 -0
  72. package/dist/knowledge-graph/pagerank.d.ts.map +1 -0
  73. package/dist/knowledge-graph/pagerank.js +493 -0
  74. package/dist/knowledge-graph/pagerank.js.map +1 -0
  75. package/dist/knowledge-graph/parser.d.ts +55 -0
  76. package/dist/knowledge-graph/parser.d.ts.map +1 -0
  77. package/dist/knowledge-graph/parser.js +1090 -0
  78. package/dist/knowledge-graph/parser.js.map +1 -0
  79. package/dist/knowledge-graph/snapshot.d.ts +107 -0
  80. package/dist/knowledge-graph/snapshot.d.ts.map +1 -0
  81. package/dist/knowledge-graph/snapshot.js +435 -0
  82. package/dist/knowledge-graph/snapshot.js.map +1 -0
  83. package/dist/memory/decision-log.d.ts +151 -0
  84. package/dist/memory/decision-log.d.ts.map +1 -0
  85. package/dist/memory/decision-log.js +482 -0
  86. package/dist/memory/decision-log.js.map +1 -0
  87. package/dist/memory/persistent-memory.d.ts +182 -0
  88. package/dist/memory/persistent-memory.d.ts.map +1 -0
  89. package/dist/memory/persistent-memory.js +579 -0
  90. package/dist/memory/persistent-memory.js.map +1 -0
  91. package/dist/memory/session-memory.d.ts +165 -0
  92. package/dist/memory/session-memory.d.ts.map +1 -0
  93. package/dist/memory/session-memory.js +382 -0
  94. package/dist/memory/session-memory.js.map +1 -0
  95. package/dist/stress-test.d.ts +10 -0
  96. package/dist/stress-test.d.ts.map +1 -0
  97. package/dist/stress-test.js +258 -0
  98. package/dist/stress-test.js.map +1 -0
  99. package/dist/tools/advanced-tools.d.ts +32 -0
  100. package/dist/tools/advanced-tools.d.ts.map +1 -0
  101. package/dist/tools/advanced-tools.js +480 -0
  102. package/dist/tools/advanced-tools.js.map +1 -0
  103. package/dist/tools/change-tools.d.ts +76 -0
  104. package/dist/tools/change-tools.d.ts.map +1 -0
  105. package/dist/tools/change-tools.js +93 -0
  106. package/dist/tools/change-tools.js.map +1 -0
  107. package/dist/tools/context-tools.d.ts +68 -0
  108. package/dist/tools/context-tools.d.ts.map +1 -0
  109. package/dist/tools/context-tools.js +141 -0
  110. package/dist/tools/context-tools.js.map +1 -0
  111. package/dist/tools/debug-tools.d.ts +25 -0
  112. package/dist/tools/debug-tools.d.ts.map +1 -0
  113. package/dist/tools/debug-tools.js +286 -0
  114. package/dist/tools/debug-tools.js.map +1 -0
  115. package/dist/tools/evolving-tools.d.ts +23 -0
  116. package/dist/tools/evolving-tools.d.ts.map +1 -0
  117. package/dist/tools/evolving-tools.js +207 -0
  118. package/dist/tools/evolving-tools.js.map +1 -0
  119. package/dist/tools/flow-tools.d.ts +24 -0
  120. package/dist/tools/flow-tools.d.ts.map +1 -0
  121. package/dist/tools/flow-tools.js +265 -0
  122. package/dist/tools/flow-tools.js.map +1 -0
  123. package/dist/tools/graph-tools.d.ts +71 -0
  124. package/dist/tools/graph-tools.d.ts.map +1 -0
  125. package/dist/tools/graph-tools.js +165 -0
  126. package/dist/tools/graph-tools.js.map +1 -0
  127. package/dist/tools/memory-tools.d.ts +62 -0
  128. package/dist/tools/memory-tools.d.ts.map +1 -0
  129. package/dist/tools/memory-tools.js +195 -0
  130. package/dist/tools/memory-tools.js.map +1 -0
  131. package/dist/tools/smart-tools.d.ts +23 -0
  132. package/dist/tools/smart-tools.d.ts.map +1 -0
  133. package/dist/tools/smart-tools.js +482 -0
  134. package/dist/tools/smart-tools.js.map +1 -0
  135. package/dist/tools/snapshot-tools.d.ts +19 -0
  136. package/dist/tools/snapshot-tools.d.ts.map +1 -0
  137. package/dist/tools/snapshot-tools.js +149 -0
  138. package/dist/tools/snapshot-tools.js.map +1 -0
  139. package/dist/types.d.ts +181 -0
  140. package/dist/types.d.ts.map +1 -0
  141. package/dist/types.js +45 -0
  142. package/dist/types.js.map +1 -0
  143. package/dist/utils/logger.d.ts +59 -0
  144. package/dist/utils/logger.d.ts.map +1 -0
  145. package/dist/utils/logger.js +142 -0
  146. package/dist/utils/logger.js.map +1 -0
  147. package/dist/utils/token-counter.d.ts +51 -0
  148. package/dist/utils/token-counter.d.ts.map +1 -0
  149. package/dist/utils/token-counter.js +181 -0
  150. package/dist/utils/token-counter.js.map +1 -0
  151. package/install.ps1 +321 -0
  152. package/install.sh +345 -0
  153. package/package.json +94 -0
  154. package/setup.bat +62 -0
package/dist/cli.js ADDED
@@ -0,0 +1,937 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AI Mind Map — CLI Interface
4
+ *
5
+ * Full command-line interface inspired by codebase-memory-mcp's CLI mode.
6
+ * Parses process.argv manually (no external CLI library). Each command
7
+ * initialises only the components it needs — no full MCP server boot for
8
+ * simple queries.
9
+ *
10
+ * Usage:
11
+ * ai-mind-map <command> [options]
12
+ *
13
+ * @module cli
14
+ */
15
+ import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
16
+ import path from 'node:path';
17
+ import process from 'node:process';
18
+ import Database from 'better-sqlite3';
19
+ import { loadConfig } from './config.js';
20
+ import { DEFAULT_CONFIG } from './types.js';
21
+ import { KnowledgeGraph } from './knowledge-graph/graph.js';
22
+ import { Indexer } from './knowledge-graph/indexer.js';
23
+ import { PersistentMemory } from './memory/persistent-memory.js';
24
+ import { DecisionLog } from './memory/decision-log.js';
25
+ import { ChangeLog } from './change-tracker/change-log.js';
26
+ import { installAgents, uninstallAgents, runDoctor, } from './install.js';
27
+ // ============================================================
28
+ // ANSI Color Helpers
29
+ // ============================================================
30
+ const supportsColor = process.stdout.isTTY !== false;
31
+ /** ANSI escape codes for terminal coloring */
32
+ const c = {
33
+ reset: supportsColor ? '\x1b[0m' : '',
34
+ bold: supportsColor ? '\x1b[1m' : '',
35
+ dim: supportsColor ? '\x1b[2m' : '',
36
+ underline: supportsColor ? '\x1b[4m' : '',
37
+ red: supportsColor ? '\x1b[31m' : '',
38
+ green: supportsColor ? '\x1b[32m' : '',
39
+ yellow: supportsColor ? '\x1b[33m' : '',
40
+ blue: supportsColor ? '\x1b[34m' : '',
41
+ magenta: supportsColor ? '\x1b[35m' : '',
42
+ cyan: supportsColor ? '\x1b[36m' : '',
43
+ white: supportsColor ? '\x1b[37m' : '',
44
+ bgRed: supportsColor ? '\x1b[41m' : '',
45
+ bgGreen: supportsColor ? '\x1b[42m' : '',
46
+ bgBlue: supportsColor ? '\x1b[44m' : '',
47
+ gray: supportsColor ? '\x1b[90m' : '',
48
+ };
49
+ function success(msg) {
50
+ console.log(`${c.green}✔${c.reset} ${msg}`);
51
+ }
52
+ function error(msg) {
53
+ console.log(`${c.red}✖${c.reset} ${msg}`);
54
+ }
55
+ function info(msg) {
56
+ console.log(`${c.blue}ℹ${c.reset} ${msg}`);
57
+ }
58
+ function warn(msg) {
59
+ console.log(`${c.yellow}⚠${c.reset} ${msg}`);
60
+ }
61
+ function heading(msg) {
62
+ console.log(`\n${c.bold}${c.cyan}${msg}${c.reset}`);
63
+ }
64
+ function divider() {
65
+ console.log(`${c.dim}${'─'.repeat(60)}${c.reset}`);
66
+ }
67
+ /** Pad a string to a fixed width */
68
+ function pad(str, width) {
69
+ return str.length >= width ? str : str + ' '.repeat(width - str.length);
70
+ }
71
+ /** Format a number with commas */
72
+ function formatNum(n) {
73
+ return n.toLocaleString();
74
+ }
75
+ /** Format bytes as human-readable */
76
+ function formatBytes(bytes) {
77
+ if (bytes < 1024)
78
+ return `${bytes} B`;
79
+ if (bytes < 1024 * 1024)
80
+ return `${(bytes / 1024).toFixed(1)} KB`;
81
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
82
+ }
83
+ /** Format epoch timestamp as readable date */
84
+ function formatDate(epoch) {
85
+ if (!epoch)
86
+ return 'Never';
87
+ return new Date(epoch).toLocaleString();
88
+ }
89
+ /** Truncate a string with ellipsis */
90
+ function truncate(str, maxLen) {
91
+ if (str.length <= maxLen)
92
+ return str;
93
+ return str.slice(0, maxLen - 1) + '…';
94
+ }
95
+ // ============================================================
96
+ // Package Version
97
+ // ============================================================
98
+ function getVersion() {
99
+ try {
100
+ const pkgPath = path.resolve(path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1')), '..', 'package.json');
101
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
102
+ return pkg.version ?? '0.0.0';
103
+ }
104
+ catch {
105
+ return '1.0.0';
106
+ }
107
+ }
108
+ /**
109
+ * Parse process.argv into structured command, positional args, and flags.
110
+ * Flags can be `--key value` or `--flag` (boolean).
111
+ */
112
+ function parseArgs(argv) {
113
+ const args = argv.slice(2); // strip node + script path
114
+ const command = (args[0] && !args[0].startsWith('-')) ? args[0] : '';
115
+ const positional = [];
116
+ const flags = {};
117
+ let i = command ? 1 : 0;
118
+ while (i < args.length) {
119
+ const arg = args[i];
120
+ if (arg.startsWith('--')) {
121
+ const key = arg.slice(2);
122
+ const next = args[i + 1];
123
+ if (next && !next.startsWith('-')) {
124
+ flags[key] = next;
125
+ i += 2;
126
+ }
127
+ else {
128
+ flags[key] = true;
129
+ i += 1;
130
+ }
131
+ }
132
+ else if (arg.startsWith('-') && arg.length === 2) {
133
+ const key = arg.slice(1);
134
+ const next = args[i + 1];
135
+ if (next && !next.startsWith('-')) {
136
+ flags[key] = next;
137
+ i += 2;
138
+ }
139
+ else {
140
+ flags[key] = true;
141
+ i += 1;
142
+ }
143
+ }
144
+ else {
145
+ positional.push(arg);
146
+ i += 1;
147
+ }
148
+ }
149
+ return { command, positional, flags };
150
+ }
151
+ // ============================================================
152
+ // Component Initialisation Helpers
153
+ // ============================================================
154
+ /**
155
+ * Load config for a given project path.
156
+ * Resolves path and ensures DB directory exists.
157
+ */
158
+ async function resolveConfig(projectPath) {
159
+ const root = projectPath
160
+ ? path.resolve(projectPath)
161
+ : process.cwd();
162
+ const config = await loadConfig({ projectRoot: root, logLevel: 'error' });
163
+ // Ensure DB directory exists
164
+ const dbDir = path.dirname(config.dbPath);
165
+ if (!existsSync(dbDir)) {
166
+ mkdirSync(dbDir, { recursive: true });
167
+ }
168
+ return config;
169
+ }
170
+ /** Open the knowledge graph database */
171
+ function openGraph(config) {
172
+ return new KnowledgeGraph(config.dbPath);
173
+ }
174
+ /** Open a shared SQLite database for memory/decisions */
175
+ function openMemoryDb(config) {
176
+ const db = new Database(config.dbPath);
177
+ db.pragma('journal_mode = WAL');
178
+ return db;
179
+ }
180
+ // ============================================================
181
+ // Command Implementations
182
+ // ============================================================
183
+ /** ai-mind-map serve — Start MCP server (default) */
184
+ async function cmdServe() {
185
+ info('Starting AI Mind Map MCP server...');
186
+ // Dynamic import triggers the MCP server startup as a side effect.
187
+ // index.ts self-executes when imported (registers tools + connects transport).
188
+ await import('./index.js');
189
+ }
190
+ /** ai-mind-map index <project-path> — Index a project */
191
+ async function cmdIndex(args) {
192
+ const projectPath = args.positional[0] || process.cwd();
193
+ heading('🗂️ Indexing Project');
194
+ info(`Project: ${path.resolve(projectPath)}`);
195
+ divider();
196
+ const config = await resolveConfig(projectPath);
197
+ const graph = openGraph(config);
198
+ try {
199
+ const indexer = new Indexer(graph, config);
200
+ const startTime = Date.now();
201
+ const stats = await indexer.fullIndex((progress) => {
202
+ if (progress.phase === 'scanning') {
203
+ process.stdout.write(`\r${c.dim}Scanning files...${c.reset}`);
204
+ }
205
+ else if (progress.phase === 'parsing') {
206
+ const pct = progress.total > 0
207
+ ? Math.round((progress.current / progress.total) * 100)
208
+ : 0;
209
+ process.stdout.write(`\r${c.dim}Parsing: ${progress.current}/${progress.total} (${pct}%)${c.reset} `);
210
+ }
211
+ else if (progress.phase === 'storing') {
212
+ process.stdout.write(`\r${c.dim}Storing: ${progress.current}/${progress.total}${c.reset} `);
213
+ }
214
+ else if (progress.phase === 'complete') {
215
+ process.stdout.write('\r' + ' '.repeat(60) + '\r');
216
+ }
217
+ });
218
+ // Clear the progress line
219
+ process.stdout.write('\r' + ' '.repeat(60) + '\r');
220
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
221
+ heading('📊 Index Results');
222
+ divider();
223
+ console.log(` ${pad('Files scanned:', 20)} ${c.bold}${formatNum(stats.filesScanned)}${c.reset}`);
224
+ console.log(` ${pad('Files parsed:', 20)} ${c.bold}${formatNum(stats.filesParsed)}${c.reset}`);
225
+ console.log(` ${pad('Files skipped:', 20)} ${formatNum(stats.filesSkipped)}`);
226
+ console.log(` ${pad('Nodes created:', 20)} ${c.green}${formatNum(stats.nodesCreated)}${c.reset}`);
227
+ console.log(` ${pad('Edges created:', 20)} ${c.green}${formatNum(stats.edgesCreated)}${c.reset}`);
228
+ console.log(` ${pad('Parse errors:', 20)} ${stats.parseErrors > 0 ? c.yellow : ''}${formatNum(stats.parseErrors)}${c.reset}`);
229
+ console.log(` ${pad('Duration:', 20)} ${elapsed}s`);
230
+ if (Object.keys(stats.languages).length > 0) {
231
+ console.log(` ${pad('Languages:', 20)}`);
232
+ for (const [lang, count] of Object.entries(stats.languages).sort((a, b) => b[1] - a[1])) {
233
+ console.log(` ${c.cyan}${pad(lang, 16)}${c.reset} ${formatNum(count)} files`);
234
+ }
235
+ }
236
+ divider();
237
+ success(`Indexing complete in ${elapsed}s`);
238
+ }
239
+ finally {
240
+ graph.close();
241
+ }
242
+ }
243
+ /** ai-mind-map search <query> [--type ...] [--limit N] — Search the knowledge graph */
244
+ async function cmdSearch(args) {
245
+ const query = args.positional.join(' ');
246
+ if (!query) {
247
+ error('Usage: ai-mind-map search <query> [--type func|class|...] [--limit N]');
248
+ process.exit(1);
249
+ }
250
+ const typeFilter = typeof args.flags['type'] === 'string' ? args.flags['type'] : undefined;
251
+ const limit = typeof args.flags['limit'] === 'string' ? parseInt(args.flags['limit'], 10) : 20;
252
+ const config = await resolveConfig();
253
+ const graph = openGraph(config);
254
+ try {
255
+ let results = graph.search(query, limit * 2); // Over-fetch for type filtering
256
+ if (typeFilter) {
257
+ results = results.filter((n) => n.type === typeFilter);
258
+ }
259
+ results = results.slice(0, limit);
260
+ heading(`🔍 Search Results for "${query}"`);
261
+ if (typeFilter)
262
+ info(`Type filter: ${typeFilter}`);
263
+ divider();
264
+ if (results.length === 0) {
265
+ warn('No results found.');
266
+ return;
267
+ }
268
+ for (const node of results) {
269
+ const typeColor = getTypeColor(node.type);
270
+ console.log(` ${typeColor}${pad(node.type, 12)}${c.reset} ` +
271
+ `${c.bold}${node.name}${c.reset}` +
272
+ `${c.dim} (${truncate(node.filePath, 40)}:${node.startLine})${c.reset}`);
273
+ if (node.signature) {
274
+ console.log(`${c.dim} ${truncate(node.signature, 60)}${c.reset}`);
275
+ }
276
+ }
277
+ divider();
278
+ info(`${results.length} result(s) found`);
279
+ }
280
+ finally {
281
+ graph.close();
282
+ }
283
+ }
284
+ /** Color a node type for display */
285
+ function getTypeColor(type) {
286
+ switch (type) {
287
+ case 'function': return c.yellow;
288
+ case 'class': return c.magenta;
289
+ case 'method': return c.cyan;
290
+ case 'interface': return c.blue;
291
+ case 'type_alias': return c.blue;
292
+ case 'variable': return c.green;
293
+ case 'constant': return c.green;
294
+ case 'file': return c.gray;
295
+ case 'module': return c.gray;
296
+ case 'component': return c.magenta;
297
+ case 'hook': return c.cyan;
298
+ case 'test': return c.yellow;
299
+ default: return c.white;
300
+ }
301
+ }
302
+ /** ai-mind-map trace <symbol> [--direction both|callers|callees] [--depth N] */
303
+ async function cmdTrace(args) {
304
+ const symbolName = args.positional[0];
305
+ if (!symbolName) {
306
+ error('Usage: ai-mind-map trace <symbol-name> [--direction both|callers|callees] [--depth N]');
307
+ process.exit(1);
308
+ }
309
+ const direction = (typeof args.flags['direction'] === 'string' ? args.flags['direction'] : 'both');
310
+ const depth = typeof args.flags['depth'] === 'string' ? parseInt(args.flags['depth'], 10) : 3;
311
+ const config = await resolveConfig();
312
+ const graph = openGraph(config);
313
+ try {
314
+ // Find the symbol node(s)
315
+ const nodes = graph.getNodesByName(symbolName);
316
+ if (nodes.length === 0) {
317
+ warn(`Symbol "${symbolName}" not found in the knowledge graph.`);
318
+ info('Try running "ai-mind-map index" first, or search with "ai-mind-map search".');
319
+ return;
320
+ }
321
+ heading(`🔗 Trace: ${symbolName}`);
322
+ info(`Direction: ${direction} | Depth: ${depth}`);
323
+ divider();
324
+ for (const node of nodes) {
325
+ console.log(`\n ${c.bold}${node.qualifiedName}${c.reset} ` +
326
+ `${c.dim}(${node.type} in ${truncate(node.filePath, 40)}:${node.startLine})${c.reset}`);
327
+ if (direction === 'callers' || direction === 'both') {
328
+ const callers = graph.findCallers(node.id);
329
+ console.log(`\n ${c.cyan}↑ Callers${c.reset} (${callers.length}):`);
330
+ if (callers.length === 0) {
331
+ console.log(` ${c.dim}(none)${c.reset}`);
332
+ }
333
+ else {
334
+ for (const caller of callers.slice(0, depth * 5)) {
335
+ console.log(` ${c.green}←${c.reset} ${caller.qualifiedName} ` +
336
+ `${c.dim}(${caller.type}, ${truncate(caller.filePath, 30)}:${caller.startLine})${c.reset}`);
337
+ }
338
+ if (callers.length > depth * 5) {
339
+ console.log(` ${c.dim}… and ${callers.length - depth * 5} more${c.reset}`);
340
+ }
341
+ }
342
+ }
343
+ if (direction === 'callees' || direction === 'both') {
344
+ const callees = graph.findCallees(node.id);
345
+ console.log(`\n ${c.cyan}↓ Callees${c.reset} (${callees.length}):`);
346
+ if (callees.length === 0) {
347
+ console.log(` ${c.dim}(none)${c.reset}`);
348
+ }
349
+ else {
350
+ for (const callee of callees.slice(0, depth * 5)) {
351
+ console.log(` ${c.green}→${c.reset} ${callee.qualifiedName} ` +
352
+ `${c.dim}(${callee.type}, ${truncate(callee.filePath, 30)}:${callee.startLine})${c.reset}`);
353
+ }
354
+ if (callees.length > depth * 5) {
355
+ console.log(` ${c.dim}… and ${callees.length - depth * 5} more${c.reset}`);
356
+ }
357
+ }
358
+ }
359
+ // Blast radius
360
+ const blast = graph.blastRadius(node.id, depth);
361
+ if (blast.length > 0) {
362
+ console.log(`\n ${c.red}💥 Blast Radius${c.reset} (${blast.length} affected nodes):`);
363
+ for (const affected of blast.slice(0, 10)) {
364
+ console.log(` ${c.yellow}⚡${c.reset} ${affected.qualifiedName} ` +
365
+ `${c.dim}(${affected.type})${c.reset}`);
366
+ }
367
+ if (blast.length > 10) {
368
+ console.log(` ${c.dim}… and ${blast.length - 10} more${c.reset}`);
369
+ }
370
+ }
371
+ }
372
+ divider();
373
+ }
374
+ finally {
375
+ graph.close();
376
+ }
377
+ }
378
+ /** ai-mind-map structure [<project-path>] — Show project structure */
379
+ async function cmdStructure(args) {
380
+ const projectPath = args.positional[0];
381
+ const config = await resolveConfig(projectPath);
382
+ const graph = openGraph(config);
383
+ try {
384
+ const overview = graph.getProjectOverview();
385
+ heading('🏗️ Project Structure');
386
+ info(`Project: ${config.projectRoot}`);
387
+ divider();
388
+ if (overview.size === 0) {
389
+ warn('No indexed files found. Run "ai-mind-map index" first.');
390
+ return;
391
+ }
392
+ for (const [filePath, symbols] of overview) {
393
+ const relPath = path.relative(config.projectRoot, filePath);
394
+ console.log(`\n ${c.bold}${relPath}${c.reset}`);
395
+ if (symbols.length === 0) {
396
+ console.log(` ${c.dim}(no exported symbols)${c.reset}`);
397
+ }
398
+ else {
399
+ for (const sym of symbols) {
400
+ const typeColor = getTypeColor(sym.type);
401
+ const asyncMark = sym.isAsync ? `${c.yellow}async ${c.reset}` : '';
402
+ const exportMark = sym.isExported ? `${c.green}↗${c.reset} ` : ' ';
403
+ console.log(` ${exportMark}${typeColor}${pad(sym.type, 10)}${c.reset} ` +
404
+ `${asyncMark}${c.bold}${sym.name}${c.reset}` +
405
+ (sym.signature ? ` ${c.dim}${truncate(sym.signature, 45)}${c.reset}` : ''));
406
+ }
407
+ }
408
+ }
409
+ divider();
410
+ info(`${overview.size} file(s) indexed`);
411
+ }
412
+ finally {
413
+ graph.close();
414
+ }
415
+ }
416
+ /** ai-mind-map status [<project-path>] — Show index stats */
417
+ async function cmdStatus(args) {
418
+ const projectPath = args.positional[0];
419
+ const config = await resolveConfig(projectPath);
420
+ heading('📈 AI Mind Map Status');
421
+ info(`Project: ${config.projectRoot}`);
422
+ info(`Database: ${config.dbPath}`);
423
+ divider();
424
+ if (!existsSync(config.dbPath)) {
425
+ warn('Database not found. Run "ai-mind-map index" first.');
426
+ return;
427
+ }
428
+ const graph = openGraph(config);
429
+ const memDb = openMemoryDb(config);
430
+ try {
431
+ // Graph stats
432
+ const graphStats = graph.getStats();
433
+ console.log(`\n ${c.bold}Knowledge Graph${c.reset}`);
434
+ console.log(` ${pad('Files:', 18)} ${c.bold}${formatNum(graphStats.totalFiles)}${c.reset}`);
435
+ console.log(` ${pad('Nodes:', 18)} ${c.bold}${formatNum(graphStats.totalNodes)}${c.reset}`);
436
+ console.log(` ${pad('Edges:', 18)} ${c.bold}${formatNum(graphStats.totalEdges)}${c.reset}`);
437
+ if (Object.keys(graphStats.nodesByType).length > 0) {
438
+ console.log(` ${pad('By type:', 18)}`);
439
+ for (const [type, count] of Object.entries(graphStats.nodesByType).sort((a, b) => b[1] - a[1])) {
440
+ const typeColor = getTypeColor(type);
441
+ console.log(` ${typeColor}${pad(type, 14)}${c.reset} ${formatNum(count)}`);
442
+ }
443
+ }
444
+ if (Object.keys(graphStats.languageBreakdown).length > 0) {
445
+ console.log(` ${pad('Languages:', 18)}`);
446
+ for (const [lang, count] of Object.entries(graphStats.languageBreakdown).sort((a, b) => b[1] - a[1])) {
447
+ console.log(` ${c.cyan}${pad(lang, 14)}${c.reset} ${formatNum(count)} files`);
448
+ }
449
+ }
450
+ // Memory stats
451
+ try {
452
+ const memory = new PersistentMemory(memDb, config.memory);
453
+ const memStats = memory.getStats();
454
+ console.log(`\n ${c.bold}Memories${c.reset}`);
455
+ console.log(` ${pad('Total:', 18)} ${formatNum(memStats.totalMemories)}`);
456
+ console.log(` ${pad('Avg importance:', 18)} ${memStats.averageImportance.toFixed(2)}`);
457
+ console.log(` ${pad('Total accesses:', 18)} ${formatNum(memStats.totalAccessCount)}`);
458
+ if (Object.keys(memStats.byCategory).length > 0) {
459
+ console.log(` ${pad('By category:', 18)}`);
460
+ for (const [cat, cnt] of Object.entries(memStats.byCategory).sort((a, b) => b[1] - a[1])) {
461
+ console.log(` ${c.magenta}${pad(cat, 16)}${c.reset} ${formatNum(cnt)}`);
462
+ }
463
+ }
464
+ }
465
+ catch {
466
+ console.log(`\n ${c.bold}Memories${c.reset} ${c.dim}(not initialised)${c.reset}`);
467
+ }
468
+ // Decision stats
469
+ try {
470
+ const decisions = new DecisionLog(memDb, config.memory);
471
+ const active = decisions.getActiveDecisions();
472
+ const all = decisions.queryDecisions({});
473
+ console.log(`\n ${c.bold}Decisions${c.reset}`);
474
+ console.log(` ${pad('Total:', 18)} ${formatNum(all.length)}`);
475
+ console.log(` ${pad('Active:', 18)} ${c.green}${formatNum(active.length)}${c.reset}`);
476
+ }
477
+ catch {
478
+ console.log(`\n ${c.bold}Decisions${c.reset} ${c.dim}(not initialised)${c.reset}`);
479
+ }
480
+ // DB file size
481
+ try {
482
+ const dbStat = statSync(config.dbPath);
483
+ console.log(`\n ${c.bold}Storage${c.reset}`);
484
+ console.log(` ${pad('DB size:', 18)} ${formatBytes(dbStat.size)}`);
485
+ }
486
+ catch {
487
+ // skip
488
+ }
489
+ divider();
490
+ }
491
+ finally {
492
+ graph.close();
493
+ memDb.close();
494
+ }
495
+ }
496
+ /** ai-mind-map recall <query> — Search memories */
497
+ async function cmdRecall(args) {
498
+ const query = args.positional.join(' ');
499
+ if (!query) {
500
+ error('Usage: ai-mind-map recall <query>');
501
+ process.exit(1);
502
+ }
503
+ const limit = typeof args.flags['limit'] === 'string' ? parseInt(args.flags['limit'], 10) : 10;
504
+ const config = await resolveConfig();
505
+ const db = openMemoryDb(config);
506
+ try {
507
+ const memory = new PersistentMemory(db, config.memory);
508
+ const results = memory.queryMemories({ text: query, limit });
509
+ heading(`🧠 Memory Recall: "${query}"`);
510
+ divider();
511
+ if (results.length === 0) {
512
+ warn('No memories found matching your query.');
513
+ return;
514
+ }
515
+ for (const mem of results) {
516
+ const catColor = getCategoryColor(mem.category);
517
+ console.log(`\n ${c.bold}#${mem.id}${c.reset} ` +
518
+ `${catColor}[${mem.category}]${c.reset} ` +
519
+ `${c.dim}importance: ${mem.importance.toFixed(2)} | ` +
520
+ `accessed: ${mem.accessCount}x${c.reset}`);
521
+ console.log(` ${truncate(mem.content, 120)}`);
522
+ if (mem.tags.length > 0) {
523
+ console.log(` ${c.dim}Tags: ${mem.tags.join(', ')}${c.reset}`);
524
+ }
525
+ if (mem.relatedFiles.length > 0) {
526
+ console.log(` ${c.dim}Files: ${mem.relatedFiles.map((f) => truncate(f, 30)).join(', ')}${c.reset}`);
527
+ }
528
+ }
529
+ divider();
530
+ info(`${results.length} memor${results.length === 1 ? 'y' : 'ies'} found`);
531
+ }
532
+ finally {
533
+ db.close();
534
+ }
535
+ }
536
+ /** Color a memory category for display */
537
+ function getCategoryColor(cat) {
538
+ switch (cat) {
539
+ case 'architecture': return c.magenta;
540
+ case 'convention': return c.cyan;
541
+ case 'decision': return c.blue;
542
+ case 'gotcha': return c.red;
543
+ case 'dependency': return c.yellow;
544
+ case 'workflow': return c.green;
545
+ case 'context': return c.white;
546
+ case 'preference': return c.gray;
547
+ case 'lesson_learned': return c.yellow;
548
+ case 'todo': return c.cyan;
549
+ default: return c.white;
550
+ }
551
+ }
552
+ /** ai-mind-map remember <content> --category <cat> — Store a memory */
553
+ async function cmdRemember(args) {
554
+ const content = args.positional.join(' ');
555
+ if (!content) {
556
+ error('Usage: ai-mind-map remember <content> --category <category>');
557
+ process.exit(1);
558
+ }
559
+ const category = (typeof args.flags['category'] === 'string'
560
+ ? args.flags['category']
561
+ : 'convention');
562
+ const validCategories = [
563
+ 'architecture', 'convention', 'decision', 'gotcha', 'dependency',
564
+ 'workflow', 'context', 'preference', 'lesson_learned', 'todo',
565
+ ];
566
+ if (!validCategories.includes(category)) {
567
+ error(`Invalid category: "${category}". Valid: ${validCategories.join(', ')}`);
568
+ process.exit(1);
569
+ }
570
+ const tagsRaw = typeof args.flags['tags'] === 'string' ? args.flags['tags'] : '';
571
+ const tags = tagsRaw ? tagsRaw.split(',').map((t) => t.trim()) : [];
572
+ const config = await resolveConfig();
573
+ const db = openMemoryDb(config);
574
+ try {
575
+ const memory = new PersistentMemory(db, config.memory);
576
+ const created = memory.createMemory({
577
+ category,
578
+ content,
579
+ tags,
580
+ source: 'user',
581
+ });
582
+ heading('💾 Memory Stored');
583
+ divider();
584
+ console.log(` ${pad('ID:', 14)} ${c.bold}#${created.id}${c.reset}`);
585
+ console.log(` ${pad('Category:', 14)} ${getCategoryColor(created.category)}${created.category}${c.reset}`);
586
+ console.log(` ${pad('Importance:', 14)} ${created.importance.toFixed(2)}`);
587
+ console.log(` ${pad('Content:', 14)} ${truncate(created.content, 80)}`);
588
+ if (created.tags.length > 0) {
589
+ console.log(` ${pad('Tags:', 14)} ${created.tags.join(', ')}`);
590
+ }
591
+ divider();
592
+ success('Memory saved successfully');
593
+ }
594
+ finally {
595
+ db.close();
596
+ }
597
+ }
598
+ /** ai-mind-map decisions [--status active] — List decisions */
599
+ async function cmdDecisions(args) {
600
+ const statusFilter = typeof args.flags['status'] === 'string'
601
+ ? args.flags['status']
602
+ : undefined;
603
+ const config = await resolveConfig();
604
+ const db = openMemoryDb(config);
605
+ try {
606
+ const decisionLog = new DecisionLog(db, config.memory);
607
+ const decisions = decisionLog.queryDecisions({
608
+ status: statusFilter,
609
+ limit: 50,
610
+ });
611
+ heading('📋 Decision Log');
612
+ if (statusFilter)
613
+ info(`Filter: status = ${statusFilter}`);
614
+ divider();
615
+ if (decisions.length === 0) {
616
+ warn('No decisions found.');
617
+ return;
618
+ }
619
+ for (const d of decisions) {
620
+ const statusColor = d.status === 'active' ? c.green
621
+ : d.status === 'superseded' ? c.yellow
622
+ : c.red;
623
+ console.log(`\n ${c.bold}#${d.id}${c.reset} ${statusColor}[${d.status}]${c.reset} ${c.bold}${d.title}${c.reset}`);
624
+ console.log(` ${c.dim}${truncate(d.description, 100)}${c.reset}`);
625
+ console.log(` ${c.dim}Rationale: ${truncate(d.rationale, 80)}${c.reset}`);
626
+ console.log(` ${c.dim}Decided: ${formatDate(d.decidedAt)} by ${d.decidedBy}${c.reset}`);
627
+ if (d.tags.length > 0) {
628
+ console.log(` ${c.dim}Tags: ${d.tags.join(', ')}${c.reset}`);
629
+ }
630
+ if (d.alternatives.length > 0) {
631
+ console.log(` ${c.dim}Alternatives: ${d.alternatives.join('; ')}${c.reset}`);
632
+ }
633
+ }
634
+ divider();
635
+ info(`${decisions.length} decision(s) total`);
636
+ }
637
+ finally {
638
+ db.close();
639
+ }
640
+ }
641
+ /** ai-mind-map changes [--since last_session|<ms>] — Show what changed */
642
+ async function cmdChanges(args) {
643
+ const sinceRaw = typeof args.flags['since'] === 'string' ? args.flags['since'] : undefined;
644
+ const limit = typeof args.flags['limit'] === 'string' ? parseInt(args.flags['limit'], 10) : 30;
645
+ const config = await resolveConfig();
646
+ heading('📝 Change History');
647
+ divider();
648
+ if (!existsSync(config.dbPath)) {
649
+ warn('Database not found. Run "ai-mind-map index" first.');
650
+ return;
651
+ }
652
+ try {
653
+ const changeLog = new ChangeLog({ dbPath: config.dbPath });
654
+ let since;
655
+ if (sinceRaw === 'last_session') {
656
+ const latestSession = changeLog.getLatestSession();
657
+ if (latestSession) {
658
+ since = latestSession.startedAt;
659
+ info(`Since last session: ${formatDate(since)}`);
660
+ }
661
+ }
662
+ else if (sinceRaw && !isNaN(Number(sinceRaw))) {
663
+ since = Number(sinceRaw);
664
+ }
665
+ const changes = changeLog.queryChanges({ since, limit });
666
+ if (changes.length === 0) {
667
+ warn('No changes recorded.');
668
+ changeLog.close();
669
+ return;
670
+ }
671
+ for (const change of changes) {
672
+ const typeIcon = change.changeType === 'created' ? `${c.green}+`
673
+ : change.changeType === 'modified' ? `${c.yellow}~`
674
+ : change.changeType === 'deleted' ? `${c.red}-`
675
+ : `${c.blue}→`;
676
+ console.log(` ${typeIcon}${c.reset} ${c.bold}${truncate(change.filePath, 50)}${c.reset} ` +
677
+ `${c.dim}${formatDate(change.timestamp)}${c.reset}`);
678
+ if (change.summary) {
679
+ console.log(` ${c.dim}${truncate(change.summary, 80)}${c.reset}`);
680
+ }
681
+ if (change.symbolsAffected.length > 0) {
682
+ console.log(` ${c.dim}Symbols: ${change.symbolsAffected.join(', ')}${c.reset}`);
683
+ }
684
+ if (change.linesAdded > 0 || change.linesRemoved > 0) {
685
+ console.log(` ${c.green}+${change.linesAdded}${c.reset} ` +
686
+ `${c.red}-${change.linesRemoved}${c.reset}`);
687
+ }
688
+ }
689
+ // Summary stats
690
+ const stats = changeLog.getStats();
691
+ divider();
692
+ console.log(` Total: ${formatNum(stats.totalChanges)} changes across ` +
693
+ `${formatNum(stats.totalSessions)} sessions`);
694
+ console.log(` All-time: ${c.green}+${formatNum(stats.linesAddedAllTime)}${c.reset} / ` +
695
+ `${c.red}-${formatNum(stats.linesRemovedAllTime)}${c.reset} lines`);
696
+ changeLog.close();
697
+ }
698
+ catch (err) {
699
+ error(`Failed to read change log: ${err instanceof Error ? err.message : String(err)}`);
700
+ }
701
+ }
702
+ /** ai-mind-map config list | set <key> <value> | reset <key> */
703
+ async function cmdConfig(args) {
704
+ const subCommand = args.positional[0] || 'list';
705
+ if (subCommand === 'list') {
706
+ const config = await resolveConfig();
707
+ heading('⚙️ Configuration');
708
+ divider();
709
+ printConfigRecursive(config, '');
710
+ divider();
711
+ return;
712
+ }
713
+ if (subCommand === 'set') {
714
+ const key = args.positional[1];
715
+ const value = args.positional[2];
716
+ if (!key || !value) {
717
+ error('Usage: ai-mind-map config set <key> <value>');
718
+ process.exit(1);
719
+ }
720
+ info(`Setting ${key} = ${value}`);
721
+ info('Note: Runtime config changes are not persisted. Edit .mindmap.json instead.');
722
+ warn(`To persist: add {"${key}": ${JSON.stringify(value)}} to .mindmap.json`);
723
+ return;
724
+ }
725
+ if (subCommand === 'reset') {
726
+ const key = args.positional[1];
727
+ if (!key) {
728
+ error('Usage: ai-mind-map config reset <key>');
729
+ process.exit(1);
730
+ }
731
+ const defaultVal = getNestedValue(DEFAULT_CONFIG, key);
732
+ info(`Default value for "${key}": ${JSON.stringify(defaultVal)}`);
733
+ info('Remove the key from .mindmap.json to reset to default.');
734
+ return;
735
+ }
736
+ error(`Unknown config subcommand: ${subCommand}. Use: list, set, reset`);
737
+ process.exit(1);
738
+ }
739
+ /** Recursively print config key-value pairs */
740
+ function printConfigRecursive(obj, prefix) {
741
+ for (const [key, value] of Object.entries(obj)) {
742
+ const fullKey = prefix ? `${prefix}.${key}` : key;
743
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
744
+ printConfigRecursive(value, fullKey);
745
+ }
746
+ else {
747
+ const displayValue = Array.isArray(value) && value.length > 5
748
+ ? `[${value.slice(0, 5).join(', ')}, … +${value.length - 5}]`
749
+ : JSON.stringify(value);
750
+ console.log(` ${c.cyan}${pad(fullKey, 30)}${c.reset} ${displayValue}`);
751
+ }
752
+ }
753
+ }
754
+ /** Get a nested value from an object using dot notation */
755
+ function getNestedValue(obj, path) {
756
+ const parts = path.split('.');
757
+ let current = obj;
758
+ for (const part of parts) {
759
+ if (current === null || typeof current !== 'object')
760
+ return undefined;
761
+ current = current[part];
762
+ }
763
+ return current;
764
+ }
765
+ /** ai-mind-map update — Check for updates */
766
+ async function cmdUpdate() {
767
+ heading('🔄 Update Check');
768
+ divider();
769
+ const currentVersion = getVersion();
770
+ info(`Current version: ${c.bold}${currentVersion}${c.reset}`);
771
+ info('To update, run:');
772
+ console.log(`\n ${c.bold}npm update -g ai-mind-map${c.reset}`);
773
+ console.log(` ${c.dim}or${c.reset}`);
774
+ console.log(` ${c.bold}npm install -g ai-mind-map@latest${c.reset}\n`);
775
+ divider();
776
+ }
777
+ /** ai-mind-map --help — Show help text */
778
+ function showHelp() {
779
+ const version = getVersion();
780
+ console.log(`
781
+ ${c.bold}${c.cyan}AI Mind Map${c.reset} ${c.dim}v${version}${c.reset}
782
+ ${c.dim}MCP server that reduces AI coding agent token usage by 80-99%.${c.reset}
783
+
784
+ ${c.bold}USAGE${c.reset}
785
+ ${c.cyan}ai-mind-map${c.reset} <command> [options]
786
+
787
+ ${c.bold}COMMANDS${c.reset}
788
+ ${c.cyan}serve${c.reset} Start MCP server (default)
789
+ ${c.cyan}index${c.reset} <project-path> Index a project's codebase
790
+ ${c.cyan}search${c.reset} <query> Search the knowledge graph
791
+ ${c.dim}--type <type> Filter: func, class, method, etc.${c.reset}
792
+ ${c.dim}--limit <N> Max results (default: 20)${c.reset}
793
+ ${c.cyan}trace${c.reset} <symbol-name> Trace symbol dependencies
794
+ ${c.dim}--direction <dir> both, callers, or callees (default: both)${c.reset}
795
+ ${c.dim}--depth <N> Traversal depth (default: 3)${c.reset}
796
+ ${c.cyan}structure${c.reset} [<project-path>] Show project structure overview
797
+ ${c.cyan}status${c.reset} [<project-path>] Show index and memory stats
798
+ ${c.cyan}recall${c.reset} <query> Search stored memories
799
+ ${c.cyan}remember${c.reset} <content> Store a new memory
800
+ ${c.dim}--category <cat> Category (default: convention)${c.reset}
801
+ ${c.dim}--tags <tag1,tag2> Comma-separated tags${c.reset}
802
+ ${c.cyan}decisions${c.reset} List architectural decisions
803
+ ${c.dim}--status <status> Filter: active, superseded, reversed${c.reset}
804
+ ${c.cyan}changes${c.reset} Show change history
805
+ ${c.dim}--since <last_session|epoch> Filter changes since timestamp${c.reset}
806
+ ${c.dim}--limit <N> Max results (default: 30)${c.reset}
807
+
808
+ ${c.bold}AGENT MANAGEMENT${c.reset}
809
+ ${c.cyan}install${c.reset} Auto-detect and configure AI agents
810
+ ${c.cyan}uninstall${c.reset} Remove agent configurations
811
+ ${c.cyan}doctor${c.reset} Run diagnostics check
812
+
813
+ ${c.bold}CONFIGURATION${c.reset}
814
+ ${c.cyan}config list${c.reset} Show current configuration
815
+ ${c.cyan}config set${c.reset} <key> <value> Set a config value
816
+ ${c.cyan}config reset${c.reset} <key> Reset a key to default
817
+
818
+ ${c.bold}OTHER${c.reset}
819
+ ${c.cyan}update${c.reset} Check for updates
820
+ ${c.dim}--help, -h${c.reset} Show this help message
821
+ ${c.dim}--version, -v${c.reset} Show version
822
+
823
+ ${c.bold}EXAMPLES${c.reset}
824
+ ${c.dim}# Index current directory${c.reset}
825
+ ${c.cyan}ai-mind-map index .${c.reset}
826
+
827
+ ${c.dim}# Search for a function${c.reset}
828
+ ${c.cyan}ai-mind-map search "handleAuth" --type function${c.reset}
829
+
830
+ ${c.dim}# Trace who calls a function${c.reset}
831
+ ${c.cyan}ai-mind-map trace parseConfig --direction callers${c.reset}
832
+
833
+ ${c.dim}# Store a convention${c.reset}
834
+ ${c.cyan}ai-mind-map remember "Always use snake_case for DB columns" --category convention${c.reset}
835
+
836
+ ${c.dim}# Install MCP config for all detected agents${c.reset}
837
+ ${c.cyan}ai-mind-map install${c.reset}
838
+
839
+ ${c.dim}# Run diagnostics${c.reset}
840
+ ${c.cyan}ai-mind-map doctor${c.reset}
841
+ `);
842
+ }
843
+ // ============================================================
844
+ // Main Router
845
+ // ============================================================
846
+ /**
847
+ * Main CLI entry point. Parses arguments and dispatches to the
848
+ * appropriate command handler.
849
+ */
850
+ export async function main(argv = process.argv) {
851
+ const args = parseArgs(argv);
852
+ // Version flag
853
+ if (args.flags['version'] || args.flags['v']) {
854
+ console.log(getVersion());
855
+ return;
856
+ }
857
+ // Help flag
858
+ if (args.flags['help'] || args.flags['h'] || args.command === 'help') {
859
+ showHelp();
860
+ return;
861
+ }
862
+ try {
863
+ switch (args.command) {
864
+ case '':
865
+ case 'serve':
866
+ await cmdServe();
867
+ break;
868
+ case 'index':
869
+ await cmdIndex(args);
870
+ break;
871
+ case 'search':
872
+ await cmdSearch(args);
873
+ break;
874
+ case 'trace':
875
+ await cmdTrace(args);
876
+ break;
877
+ case 'structure':
878
+ await cmdStructure(args);
879
+ break;
880
+ case 'status':
881
+ await cmdStatus(args);
882
+ break;
883
+ case 'recall':
884
+ await cmdRecall(args);
885
+ break;
886
+ case 'remember':
887
+ await cmdRemember(args);
888
+ break;
889
+ case 'decisions':
890
+ await cmdDecisions(args);
891
+ break;
892
+ case 'changes':
893
+ await cmdChanges(args);
894
+ break;
895
+ case 'install':
896
+ await installAgents();
897
+ break;
898
+ case 'uninstall':
899
+ await uninstallAgents();
900
+ break;
901
+ case 'update':
902
+ await cmdUpdate();
903
+ break;
904
+ case 'config':
905
+ await cmdConfig(args);
906
+ break;
907
+ case 'doctor':
908
+ await runDoctor();
909
+ break;
910
+ default:
911
+ error(`Unknown command: "${args.command}"`);
912
+ info('Run "ai-mind-map --help" to see available commands.');
913
+ process.exit(1);
914
+ }
915
+ }
916
+ catch (err) {
917
+ const message = err instanceof Error ? err.message : String(err);
918
+ error(`Command failed: ${message}`);
919
+ if (err instanceof Error && err.stack && process.env.DEBUG) {
920
+ console.log(`${c.dim}${err.stack}${c.reset}`);
921
+ }
922
+ process.exit(1);
923
+ }
924
+ }
925
+ // ============================================================
926
+ // Auto-run when executed directly
927
+ // ============================================================
928
+ // Detect if this file is being run as the main entry point
929
+ const isMainModule = process.argv[1] &&
930
+ (process.argv[1].endsWith('cli.js') || process.argv[1].endsWith('cli.ts'));
931
+ if (isMainModule) {
932
+ main().catch((err) => {
933
+ console.error('Fatal error:', err);
934
+ process.exit(1);
935
+ });
936
+ }
937
+ //# sourceMappingURL=cli.js.map