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
@@ -0,0 +1,946 @@
1
+ /**
2
+ * AI Mind Map — Agent Auto-Detection & Installation
3
+ *
4
+ * Detects installed AI coding agents by probing their known config locations
5
+ * and writes MCP server entries so each agent can discover AI Mind Map.
6
+ *
7
+ * Inspired by codebase-memory-mcp's install command supporting 11+ agents.
8
+ *
9
+ * Supported agents:
10
+ * 1. Claude Code ~/.claude/
11
+ * 2. Cursor .cursor/
12
+ * 3. VS Code (Copilot) VS Code settings dir
13
+ * 4. Windsurf .windsurf/ or Windsurf config
14
+ * 5. Antigravity (Gemini) .gemini/config/
15
+ * 6. Zed Zed config dir
16
+ * 7. Continue.dev .continue/
17
+ *
18
+ * @module install
19
+ */
20
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
21
+ import { execSync } from 'node:child_process';
22
+ import path from 'node:path';
23
+ import { homedir, platform } from 'node:os';
24
+ import process from 'node:process';
25
+ // ============================================================
26
+ // ANSI Color Helpers
27
+ // ============================================================
28
+ const supportsColor = process.stdout.isTTY !== false;
29
+ const c = {
30
+ reset: supportsColor ? '\x1b[0m' : '',
31
+ bold: supportsColor ? '\x1b[1m' : '',
32
+ dim: supportsColor ? '\x1b[2m' : '',
33
+ red: supportsColor ? '\x1b[31m' : '',
34
+ green: supportsColor ? '\x1b[32m' : '',
35
+ yellow: supportsColor ? '\x1b[33m' : '',
36
+ blue: supportsColor ? '\x1b[34m' : '',
37
+ magenta: supportsColor ? '\x1b[35m' : '',
38
+ cyan: supportsColor ? '\x1b[36m' : '',
39
+ gray: supportsColor ? '\x1b[90m' : '',
40
+ };
41
+ function logFound(name, location) {
42
+ console.log(` ${c.green}[FOUND]${c.reset} ${c.bold}${name}${c.reset} ${c.dim}at ${location}${c.reset}`);
43
+ }
44
+ function logNotFound(name) {
45
+ console.log(` ${c.gray}[NOT FOUND]${c.reset} ${name}`);
46
+ }
47
+ function logConfigured(name) {
48
+ console.log(` ${c.green}[CONFIGURED]${c.reset} ${c.bold}${name}${c.reset} MCP entry added`);
49
+ }
50
+ function logSkipped(name, reason) {
51
+ console.log(` ${c.yellow}[SKIPPED]${c.reset} ${name}: ${reason}`);
52
+ }
53
+ function logRemoved(name) {
54
+ console.log(` ${c.yellow}[REMOVED]${c.reset} ${name} MCP entry removed`);
55
+ }
56
+ function logError(name, msg) {
57
+ console.log(` ${c.red}[ERROR]${c.reset} ${name}: ${msg}`);
58
+ }
59
+ function logOk(msg) {
60
+ console.log(` ${c.green}✔${c.reset} ${msg}`);
61
+ }
62
+ function logFail(msg) {
63
+ console.log(` ${c.red}✖${c.reset} ${msg}`);
64
+ }
65
+ function logWarn(msg) {
66
+ console.log(` ${c.yellow}⚠${c.reset} ${msg}`);
67
+ }
68
+ function logInfo(msg) {
69
+ console.log(` ${c.blue}ℹ${c.reset} ${msg}`);
70
+ }
71
+ function heading(msg) {
72
+ console.log(`\n${c.bold}${c.cyan}${msg}${c.reset}`);
73
+ }
74
+ function divider() {
75
+ console.log(`${c.dim}${'─'.repeat(60)}${c.reset}`);
76
+ }
77
+ // ============================================================
78
+ // Path Resolution
79
+ // ============================================================
80
+ const HOME = homedir();
81
+ const IS_WIN = platform() === 'win32';
82
+ const IS_MAC = platform() === 'darwin';
83
+ /**
84
+ * Resolve the absolute path to the compiled dist/index.js entry point
85
+ * (or cli.js as fallback).
86
+ */
87
+ function getServerEntryPath() {
88
+ // Try to resolve from this file's location
89
+ const thisDir = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1'));
90
+ // Expect: src/install.ts → dist/index.js
91
+ const distIndex = path.resolve(thisDir, '..', 'dist', 'index.js');
92
+ if (existsSync(distIndex))
93
+ return distIndex;
94
+ // Fallback: check if we're already in dist
95
+ const distSelf = path.resolve(thisDir, 'index.js');
96
+ if (existsSync(distSelf))
97
+ return distSelf;
98
+ // Last resort: use the source path
99
+ return path.resolve(thisDir, '..', 'dist', 'index.js');
100
+ }
101
+ /** Get the VS Code settings directory based on platform */
102
+ function getVSCodeSettingsDir() {
103
+ if (IS_WIN) {
104
+ return path.join(process.env.APPDATA ?? path.join(HOME, 'AppData', 'Roaming'), 'Code', 'User');
105
+ }
106
+ else if (IS_MAC) {
107
+ return path.join(HOME, 'Library', 'Application Support', 'Code', 'User');
108
+ }
109
+ return path.join(HOME, '.config', 'Code', 'User');
110
+ }
111
+ /** Get the Zed settings directory based on platform */
112
+ function getZedSettingsDir() {
113
+ if (IS_MAC) {
114
+ return path.join(HOME, 'Library', 'Application Support', 'Zed');
115
+ }
116
+ return path.join(HOME, '.config', 'zed');
117
+ }
118
+ /** Get the Windsurf settings directory based on platform */
119
+ function getWindsurfSettingsDir() {
120
+ if (IS_WIN) {
121
+ return path.join(process.env.APPDATA ?? path.join(HOME, 'AppData', 'Roaming'), 'Windsurf', 'User');
122
+ }
123
+ else if (IS_MAC) {
124
+ return path.join(HOME, 'Library', 'Application Support', 'Windsurf', 'User');
125
+ }
126
+ return path.join(HOME, '.config', 'Windsurf', 'User');
127
+ }
128
+ // ============================================================
129
+ // MCP Config Snippet Generators
130
+ // ============================================================
131
+ /** Generate the standard MCP server config object */
132
+ function mcpServerEntry(serverEntry) {
133
+ return {
134
+ 'ai-mind-map': {
135
+ command: 'node',
136
+ args: [serverEntry],
137
+ env: {},
138
+ },
139
+ };
140
+ }
141
+ /** Read a JSON file safely, returning null on failure */
142
+ function readJsonFile(filePath) {
143
+ try {
144
+ if (!existsSync(filePath))
145
+ return null;
146
+ const raw = readFileSync(filePath, 'utf-8');
147
+ return JSON.parse(raw);
148
+ }
149
+ catch {
150
+ return null;
151
+ }
152
+ }
153
+ /** Write a JSON object to a file, creating parent dirs if needed */
154
+ function writeJsonFile(filePath, data) {
155
+ const dir = path.dirname(filePath);
156
+ if (!existsSync(dir)) {
157
+ mkdirSync(dir, { recursive: true });
158
+ }
159
+ writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
160
+ }
161
+ /**
162
+ * Merge an MCP server entry into an existing JSON config.
163
+ * Creates the file if it doesn't exist.
164
+ *
165
+ * @param configPath - Path to the config file
166
+ * @param mcpKey - The JSON key under which MCP servers live (e.g., "mcpServers")
167
+ * @param serverEntry - Path to the server executable
168
+ * @returns true if changes were made
169
+ */
170
+ function mergeMcpConfig(configPath, mcpKey, serverEntry) {
171
+ const existing = readJsonFile(configPath) ?? {};
172
+ const servers = existing[mcpKey] ?? {};
173
+ // Already configured?
174
+ if (servers['ai-mind-map']) {
175
+ return false;
176
+ }
177
+ servers['ai-mind-map'] = {
178
+ command: 'node',
179
+ args: [serverEntry],
180
+ env: {},
181
+ };
182
+ existing[mcpKey] = servers;
183
+ writeJsonFile(configPath, existing);
184
+ return true;
185
+ }
186
+ /**
187
+ * Remove the ai-mind-map entry from a JSON config file.
188
+ *
189
+ * @param configPath - Path to the config file
190
+ * @param mcpKey - The JSON key under which MCP servers live
191
+ * @returns true if the entry was found and removed
192
+ */
193
+ function removeMcpConfig(configPath, mcpKey) {
194
+ const existing = readJsonFile(configPath);
195
+ if (!existing)
196
+ return false;
197
+ const servers = existing[mcpKey];
198
+ if (!servers || !servers['ai-mind-map'])
199
+ return false;
200
+ delete servers['ai-mind-map'];
201
+ // If the mcpServers object is empty, remove the key entirely
202
+ if (Object.keys(servers).length === 0) {
203
+ delete existing[mcpKey];
204
+ }
205
+ else {
206
+ existing[mcpKey] = servers;
207
+ }
208
+ writeJsonFile(configPath, existing);
209
+ return true;
210
+ }
211
+ /** Check if ai-mind-map is already configured in a JSON file under a given key */
212
+ function hasMcpConfig(configPath, mcpKey) {
213
+ const existing = readJsonFile(configPath);
214
+ if (!existing)
215
+ return false;
216
+ const servers = existing[mcpKey];
217
+ return !!servers?.['ai-mind-map'];
218
+ }
219
+ // ============================================================
220
+ // Agent Definitions
221
+ // ============================================================
222
+ function getAgentDefinitions(serverEntry) {
223
+ return [
224
+ // 1. Claude Code
225
+ {
226
+ name: 'Claude Code',
227
+ id: 'claude-code',
228
+ probePaths: [
229
+ path.join(HOME, '.claude'),
230
+ ],
231
+ configPath: path.join(HOME, '.claude', 'claude_desktop_config.json'),
232
+ generateConfig: () => JSON.stringify({ mcpServers: mcpServerEntry(serverEntry) }, null, 2),
233
+ isConfigured: () => hasMcpConfig(path.join(HOME, '.claude', 'claude_desktop_config.json'), 'mcpServers'),
234
+ removeConfig: () => removeMcpConfig(path.join(HOME, '.claude', 'claude_desktop_config.json'), 'mcpServers'),
235
+ },
236
+ // 2. Cursor
237
+ {
238
+ name: 'Cursor',
239
+ id: 'cursor',
240
+ probePaths: [
241
+ path.join(HOME, '.cursor'),
242
+ path.join(process.cwd(), '.cursor'),
243
+ ],
244
+ configPath: path.join(HOME, '.cursor', 'mcp.json'),
245
+ generateConfig: () => JSON.stringify({ mcpServers: mcpServerEntry(serverEntry) }, null, 2),
246
+ isConfigured: () => hasMcpConfig(path.join(HOME, '.cursor', 'mcp.json'), 'mcpServers'),
247
+ removeConfig: () => removeMcpConfig(path.join(HOME, '.cursor', 'mcp.json'), 'mcpServers'),
248
+ },
249
+ // 3. VS Code
250
+ {
251
+ name: 'VS Code',
252
+ id: 'vscode',
253
+ probePaths: [
254
+ getVSCodeSettingsDir(),
255
+ path.join(getVSCodeSettingsDir(), 'settings.json'),
256
+ ],
257
+ configPath: path.join(getVSCodeSettingsDir(), 'settings.json'),
258
+ generateConfig: () => {
259
+ const existing = readJsonFile(path.join(getVSCodeSettingsDir(), 'settings.json')) ?? {};
260
+ const mcpServers = existing['mcp.servers'] ?? {};
261
+ mcpServers['ai-mind-map'] = {
262
+ command: 'node',
263
+ args: [serverEntry],
264
+ env: {},
265
+ };
266
+ existing['mcp.servers'] = mcpServers;
267
+ return JSON.stringify(existing, null, 2);
268
+ },
269
+ isConfigured: () => hasMcpConfig(path.join(getVSCodeSettingsDir(), 'settings.json'), 'mcp.servers'),
270
+ removeConfig: () => removeMcpConfig(path.join(getVSCodeSettingsDir(), 'settings.json'), 'mcp.servers'),
271
+ },
272
+ // 4. Windsurf
273
+ {
274
+ name: 'Windsurf',
275
+ id: 'windsurf',
276
+ probePaths: [
277
+ getWindsurfSettingsDir(),
278
+ path.join(HOME, '.windsurf'),
279
+ ],
280
+ configPath: path.join(getWindsurfSettingsDir(), 'settings.json'),
281
+ generateConfig: () => {
282
+ const existing = readJsonFile(path.join(getWindsurfSettingsDir(), 'settings.json')) ?? {};
283
+ const mcpServers = existing['mcp.servers'] ?? {};
284
+ mcpServers['ai-mind-map'] = {
285
+ command: 'node',
286
+ args: [serverEntry],
287
+ env: {},
288
+ };
289
+ existing['mcp.servers'] = mcpServers;
290
+ return JSON.stringify(existing, null, 2);
291
+ },
292
+ isConfigured: () => hasMcpConfig(path.join(getWindsurfSettingsDir(), 'settings.json'), 'mcp.servers'),
293
+ removeConfig: () => removeMcpConfig(path.join(getWindsurfSettingsDir(), 'settings.json'), 'mcp.servers'),
294
+ },
295
+ // 5. Antigravity (Gemini)
296
+ {
297
+ name: 'Antigravity (Gemini)',
298
+ id: 'antigravity',
299
+ probePaths: [
300
+ path.join(HOME, '.gemini', 'config'),
301
+ path.join(HOME, '.gemini'),
302
+ ],
303
+ configPath: path.join(HOME, '.gemini', 'config', 'mcp.json'),
304
+ generateConfig: () => JSON.stringify({ mcpServers: mcpServerEntry(serverEntry) }, null, 2),
305
+ isConfigured: () => hasMcpConfig(path.join(HOME, '.gemini', 'config', 'mcp.json'), 'mcpServers'),
306
+ removeConfig: () => removeMcpConfig(path.join(HOME, '.gemini', 'config', 'mcp.json'), 'mcpServers'),
307
+ },
308
+ // 6. Zed
309
+ {
310
+ name: 'Zed',
311
+ id: 'zed',
312
+ probePaths: [
313
+ getZedSettingsDir(),
314
+ ],
315
+ configPath: path.join(getZedSettingsDir(), 'settings.json'),
316
+ generateConfig: () => {
317
+ const existing = readJsonFile(path.join(getZedSettingsDir(), 'settings.json')) ?? {};
318
+ const contextServers = existing['context_servers'] ?? {};
319
+ contextServers['ai-mind-map'] = {
320
+ command: {
321
+ path: 'node',
322
+ args: [serverEntry],
323
+ env: {},
324
+ },
325
+ settings: {},
326
+ };
327
+ existing['context_servers'] = contextServers;
328
+ return JSON.stringify(existing, null, 2);
329
+ },
330
+ isConfigured: () => {
331
+ const existing = readJsonFile(path.join(getZedSettingsDir(), 'settings.json'));
332
+ if (!existing)
333
+ return false;
334
+ const ctx = existing['context_servers'];
335
+ return !!ctx?.['ai-mind-map'];
336
+ },
337
+ removeConfig: () => {
338
+ const configPath = path.join(getZedSettingsDir(), 'settings.json');
339
+ const existing = readJsonFile(configPath);
340
+ if (!existing)
341
+ return false;
342
+ const ctx = existing['context_servers'];
343
+ if (!ctx?.['ai-mind-map'])
344
+ return false;
345
+ delete ctx['ai-mind-map'];
346
+ if (Object.keys(ctx).length === 0) {
347
+ delete existing['context_servers'];
348
+ }
349
+ writeJsonFile(configPath, existing);
350
+ return true;
351
+ },
352
+ },
353
+ // 7. Continue.dev
354
+ {
355
+ name: 'Continue.dev',
356
+ id: 'continue',
357
+ probePaths: [
358
+ path.join(HOME, '.continue'),
359
+ ],
360
+ configPath: path.join(HOME, '.continue', 'config.json'),
361
+ generateConfig: () => {
362
+ const existing = readJsonFile(path.join(HOME, '.continue', 'config.json')) ?? {};
363
+ const experimental = existing['experimental'] ?? {};
364
+ const modelContextProtocolServers = experimental['modelContextProtocolServers'] ?? [];
365
+ // Check if already present
366
+ const exists = modelContextProtocolServers.some((s) => s['name'] === 'ai-mind-map');
367
+ if (!exists) {
368
+ modelContextProtocolServers.push({
369
+ name: 'ai-mind-map',
370
+ command: 'node',
371
+ args: [serverEntry],
372
+ env: {},
373
+ });
374
+ }
375
+ experimental['modelContextProtocolServers'] = modelContextProtocolServers;
376
+ existing['experimental'] = experimental;
377
+ return JSON.stringify(existing, null, 2);
378
+ },
379
+ isConfigured: () => {
380
+ const existing = readJsonFile(path.join(HOME, '.continue', 'config.json'));
381
+ if (!existing)
382
+ return false;
383
+ const exp = existing['experimental'];
384
+ if (!exp)
385
+ return false;
386
+ const servers = exp['modelContextProtocolServers'];
387
+ if (!servers)
388
+ return false;
389
+ return servers.some((s) => s['name'] === 'ai-mind-map');
390
+ },
391
+ removeConfig: () => {
392
+ const configPath = path.join(HOME, '.continue', 'config.json');
393
+ const existing = readJsonFile(configPath);
394
+ if (!existing)
395
+ return false;
396
+ const exp = existing['experimental'];
397
+ if (!exp)
398
+ return false;
399
+ const servers = exp['modelContextProtocolServers'];
400
+ if (!servers)
401
+ return false;
402
+ const filtered = servers.filter((s) => s['name'] !== 'ai-mind-map');
403
+ if (filtered.length === servers.length)
404
+ return false; // nothing removed
405
+ exp['modelContextProtocolServers'] = filtered;
406
+ writeJsonFile(configPath, existing);
407
+ return true;
408
+ },
409
+ },
410
+ ];
411
+ }
412
+ // ============================================================
413
+ // Public API: installAgents
414
+ // ============================================================
415
+ /**
416
+ * Auto-detect installed AI coding agents and configure their MCP settings
417
+ * to include the AI Mind Map server.
418
+ *
419
+ * Checks 7 agents and writes JSON config for each one found.
420
+ */
421
+ export async function installAgents() {
422
+ heading('🔌 AI Mind Map — Agent Installation');
423
+ divider();
424
+ const serverEntry = getServerEntryPath();
425
+ logInfo(`Server entry: ${serverEntry}`);
426
+ console.log('');
427
+ const agents = getAgentDefinitions(serverEntry);
428
+ let foundCount = 0;
429
+ let configuredCount = 0;
430
+ for (const agent of agents) {
431
+ const detected = agent.probePaths.some((p) => existsSync(p));
432
+ if (!detected) {
433
+ logNotFound(agent.name);
434
+ continue;
435
+ }
436
+ foundCount++;
437
+ const probedPath = agent.probePaths.find((p) => existsSync(p)) ?? agent.probePaths[0];
438
+ logFound(agent.name, probedPath);
439
+ // Check if already configured
440
+ if (agent.isConfigured()) {
441
+ logSkipped(agent.name, 'already configured');
442
+ continue;
443
+ }
444
+ // Write the configuration
445
+ try {
446
+ // Use the specific merge strategy for each agent
447
+ const configDir = path.dirname(agent.configPath);
448
+ if (!existsSync(configDir)) {
449
+ mkdirSync(configDir, { recursive: true });
450
+ }
451
+ // For agents that use mcpServers key directly
452
+ if (['claude-code', 'cursor', 'antigravity'].includes(agent.id)) {
453
+ mergeMcpConfig(agent.configPath, 'mcpServers', serverEntry);
454
+ }
455
+ else if (['vscode', 'windsurf'].includes(agent.id)) {
456
+ mergeMcpConfig(agent.configPath, 'mcp.servers', serverEntry);
457
+ }
458
+ else {
459
+ // For Zed and Continue.dev, generate full config and write
460
+ const configContent = agent.generateConfig(serverEntry);
461
+ writeFileSync(agent.configPath, configContent + '\n', 'utf-8');
462
+ }
463
+ logConfigured(agent.name);
464
+ configuredCount++;
465
+ }
466
+ catch (err) {
467
+ logError(agent.name, err instanceof Error ? err.message : String(err));
468
+ }
469
+ }
470
+ console.log('');
471
+ divider();
472
+ if (foundCount === 0) {
473
+ logWarn('No AI coding agents detected on this system.');
474
+ logInfo('Install an agent first, then re-run "ai-mind-map install".');
475
+ }
476
+ else {
477
+ logOk(`${foundCount} agent(s) detected, ${configuredCount} newly configured.`);
478
+ }
479
+ // Deploy rules files so agents know about our capabilities
480
+ console.log('');
481
+ deployRulesFiles();
482
+ console.log('');
483
+ }
484
+ // ============================================================
485
+ // Public API: uninstallAgents
486
+ // ============================================================
487
+ /**
488
+ * Remove AI Mind Map MCP configurations from all detected agents.
489
+ */
490
+ export async function uninstallAgents() {
491
+ heading('🔌 AI Mind Map — Agent Uninstall');
492
+ divider();
493
+ const serverEntry = getServerEntryPath();
494
+ const agents = getAgentDefinitions(serverEntry);
495
+ let removedCount = 0;
496
+ for (const agent of agents) {
497
+ if (!agent.isConfigured()) {
498
+ continue;
499
+ }
500
+ try {
501
+ const removed = agent.removeConfig();
502
+ if (removed) {
503
+ logRemoved(agent.name);
504
+ removedCount++;
505
+ }
506
+ }
507
+ catch (err) {
508
+ logError(agent.name, err instanceof Error ? err.message : String(err));
509
+ }
510
+ }
511
+ console.log('');
512
+ divider();
513
+ if (removedCount === 0) {
514
+ logInfo('No AI Mind Map configurations found to remove.');
515
+ }
516
+ else {
517
+ logOk(`Removed AI Mind Map from ${removedCount} agent(s).`);
518
+ }
519
+ console.log('');
520
+ }
521
+ /**
522
+ * List all supported agents and their detection/configuration status.
523
+ */
524
+ export function listAgents() {
525
+ const serverEntry = getServerEntryPath();
526
+ const agents = getAgentDefinitions(serverEntry);
527
+ return agents.map((agent) => {
528
+ const detected = agent.probePaths.some((p) => existsSync(p));
529
+ let configured = false;
530
+ try {
531
+ configured = agent.isConfigured();
532
+ }
533
+ catch {
534
+ // ignore
535
+ }
536
+ return {
537
+ name: agent.name,
538
+ id: agent.id,
539
+ detected,
540
+ configured,
541
+ configPath: agent.configPath,
542
+ };
543
+ });
544
+ }
545
+ /**
546
+ * Run a comprehensive diagnostics check covering:
547
+ * - Node.js version
548
+ * - npm version
549
+ * - TypeScript compilation status
550
+ * - SQLite working
551
+ * - Project indexed status
552
+ * - Memory database accessible
553
+ * - Detected & configured agents
554
+ */
555
+ export async function runDoctor() {
556
+ heading('🩺 AI Mind Map — Diagnostics');
557
+ divider();
558
+ const results = [];
559
+ // 1. Node.js version
560
+ const nodeVersion = process.version;
561
+ const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0], 10);
562
+ if (nodeMajor >= 18) {
563
+ results.push({
564
+ name: 'Node.js',
565
+ status: 'ok',
566
+ message: `${nodeVersion} (>= 18 required)`,
567
+ });
568
+ }
569
+ else {
570
+ results.push({
571
+ name: 'Node.js',
572
+ status: 'fail',
573
+ message: `${nodeVersion} — Node.js >= 18.0.0 is required`,
574
+ });
575
+ }
576
+ // 2. npm version
577
+ try {
578
+ const npmVersion = execSync('npm --version', { encoding: 'utf-8' }).trim();
579
+ results.push({
580
+ name: 'npm',
581
+ status: 'ok',
582
+ message: `v${npmVersion}`,
583
+ });
584
+ }
585
+ catch {
586
+ results.push({
587
+ name: 'npm',
588
+ status: 'warn',
589
+ message: 'npm not found in PATH',
590
+ });
591
+ }
592
+ // 3. TypeScript compilation
593
+ const distDir = path.resolve(path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, '$1')), '..', 'dist');
594
+ if (existsSync(path.join(distDir, 'index.js'))) {
595
+ results.push({
596
+ name: 'TypeScript Build',
597
+ status: 'ok',
598
+ message: `dist/index.js exists at ${distDir}`,
599
+ });
600
+ }
601
+ else {
602
+ results.push({
603
+ name: 'TypeScript Build',
604
+ status: 'warn',
605
+ message: 'dist/index.js not found — run "npm run build" first',
606
+ });
607
+ }
608
+ // 4. SQLite working
609
+ try {
610
+ const Database = (await import('better-sqlite3')).default;
611
+ const testDb = new Database(':memory:');
612
+ testDb.exec('CREATE TABLE test (id INTEGER PRIMARY KEY)');
613
+ testDb.exec('INSERT INTO test (id) VALUES (1)');
614
+ const row = testDb.prepare('SELECT id FROM test').get();
615
+ testDb.close();
616
+ if (row?.id === 1) {
617
+ results.push({
618
+ name: 'SQLite (better-sqlite3)',
619
+ status: 'ok',
620
+ message: 'In-memory test passed',
621
+ });
622
+ }
623
+ else {
624
+ results.push({
625
+ name: 'SQLite (better-sqlite3)',
626
+ status: 'fail',
627
+ message: 'Query returned unexpected result',
628
+ });
629
+ }
630
+ }
631
+ catch (err) {
632
+ results.push({
633
+ name: 'SQLite (better-sqlite3)',
634
+ status: 'fail',
635
+ message: `Import/test failed: ${err instanceof Error ? err.message : String(err)}`,
636
+ });
637
+ }
638
+ // 5. Project indexed?
639
+ const defaultDbPath = path.resolve(process.cwd(), '.mindmap', 'mindmap.db');
640
+ if (existsSync(defaultDbPath)) {
641
+ try {
642
+ const Database = (await import('better-sqlite3')).default;
643
+ const db = new Database(defaultDbPath, { readonly: true });
644
+ const nodeCount = db.prepare('SELECT COUNT(*) as cnt FROM nodes').get().cnt;
645
+ db.close();
646
+ results.push({
647
+ name: 'Project Indexed',
648
+ status: nodeCount > 0 ? 'ok' : 'warn',
649
+ message: nodeCount > 0
650
+ ? `${nodeCount.toLocaleString()} nodes in ${defaultDbPath}`
651
+ : `Database exists but is empty — run "ai-mind-map index"`,
652
+ });
653
+ }
654
+ catch {
655
+ results.push({
656
+ name: 'Project Indexed',
657
+ status: 'warn',
658
+ message: `Database exists at ${defaultDbPath} but could not be read`,
659
+ });
660
+ }
661
+ }
662
+ else {
663
+ results.push({
664
+ name: 'Project Indexed',
665
+ status: 'warn',
666
+ message: 'No .mindmap/mindmap.db found — run "ai-mind-map index <path>"',
667
+ });
668
+ }
669
+ // 6. Memory database accessible?
670
+ if (existsSync(defaultDbPath)) {
671
+ try {
672
+ const Database = (await import('better-sqlite3')).default;
673
+ const db = new Database(defaultDbPath, { readonly: true });
674
+ let memCount = 0;
675
+ try {
676
+ memCount = db.prepare('SELECT COUNT(*) as cnt FROM memories').get().cnt;
677
+ }
678
+ catch {
679
+ // memories table might not exist yet
680
+ }
681
+ db.close();
682
+ results.push({
683
+ name: 'Memory Store',
684
+ status: 'ok',
685
+ message: `${memCount} memories stored`,
686
+ });
687
+ }
688
+ catch {
689
+ results.push({
690
+ name: 'Memory Store',
691
+ status: 'warn',
692
+ message: 'Could not read memory database',
693
+ });
694
+ }
695
+ }
696
+ else {
697
+ results.push({
698
+ name: 'Memory Store',
699
+ status: 'warn',
700
+ message: 'Database not found (will be created on first use)',
701
+ });
702
+ }
703
+ // 7. Agent detection
704
+ const agentStatuses = listAgents();
705
+ const detectedAgents = agentStatuses.filter((a) => a.detected);
706
+ const configuredAgents = agentStatuses.filter((a) => a.configured);
707
+ if (detectedAgents.length > 0) {
708
+ results.push({
709
+ name: 'AI Agents Detected',
710
+ status: 'ok',
711
+ message: `${detectedAgents.length} agent(s): ${detectedAgents.map((a) => a.name).join(', ')}`,
712
+ });
713
+ }
714
+ else {
715
+ results.push({
716
+ name: 'AI Agents Detected',
717
+ status: 'warn',
718
+ message: 'No supported AI coding agents detected',
719
+ });
720
+ }
721
+ if (configuredAgents.length > 0) {
722
+ results.push({
723
+ name: 'AI Agents Configured',
724
+ status: 'ok',
725
+ message: `${configuredAgents.length} agent(s): ${configuredAgents.map((a) => a.name).join(', ')}`,
726
+ });
727
+ }
728
+ else if (detectedAgents.length > 0) {
729
+ results.push({
730
+ name: 'AI Agents Configured',
731
+ status: 'warn',
732
+ message: 'No agents configured — run "ai-mind-map install"',
733
+ });
734
+ }
735
+ // Print results
736
+ console.log('');
737
+ const maxNameLen = Math.max(...results.map((r) => r.name.length));
738
+ for (const result of results) {
739
+ const paddedName = result.name.padEnd(maxNameLen + 2);
740
+ if (result.status === 'ok') {
741
+ logOk(`${paddedName} ${result.message}`);
742
+ }
743
+ else if (result.status === 'warn') {
744
+ logWarn(`${paddedName} ${result.message}`);
745
+ }
746
+ else {
747
+ logFail(`${paddedName} ${result.message}`);
748
+ }
749
+ }
750
+ // Summary
751
+ const okCount = results.filter((r) => r.status === 'ok').length;
752
+ const warnCount = results.filter((r) => r.status === 'warn').length;
753
+ const failCount = results.filter((r) => r.status === 'fail').length;
754
+ console.log('');
755
+ divider();
756
+ if (failCount > 0) {
757
+ logFail(`${okCount} passed, ${warnCount} warnings, ${failCount} failures`);
758
+ logInfo('Fix the failures above before using AI Mind Map.');
759
+ }
760
+ else if (warnCount > 0) {
761
+ logWarn(`${okCount} passed, ${warnCount} warnings`);
762
+ logInfo('AI Mind Map should work, but address warnings for best experience.');
763
+ }
764
+ else {
765
+ logOk(`All ${okCount} checks passed!`);
766
+ logInfo('AI Mind Map is ready to use.');
767
+ }
768
+ // Agent detail table
769
+ if (agentStatuses.some((a) => a.detected)) {
770
+ console.log('');
771
+ heading('Agent Details');
772
+ divider();
773
+ for (const agent of agentStatuses) {
774
+ const statusIcon = !agent.detected
775
+ ? `${c.gray}○${c.reset}`
776
+ : agent.configured
777
+ ? `${c.green}●${c.reset}`
778
+ : `${c.yellow}◐${c.reset}`;
779
+ const statusText = !agent.detected
780
+ ? `${c.gray}not found${c.reset}`
781
+ : agent.configured
782
+ ? `${c.green}configured${c.reset}`
783
+ : `${c.yellow}detected, not configured${c.reset}`;
784
+ console.log(` ${statusIcon} ${c.bold}${agent.name.padEnd(22)}${c.reset} ${statusText}`);
785
+ if (agent.detected && agent.configured) {
786
+ console.log(` ${c.dim}${agent.configPath}${c.reset}`);
787
+ }
788
+ }
789
+ }
790
+ console.log('');
791
+ }
792
+ // ============================================================
793
+ // Rules File Deployment
794
+ // ============================================================
795
+ /** The core rules content that teaches AI agents about our tools */
796
+ function getToolAwarenessRules() {
797
+ return `# AI Mind Map MCP — Tool Awareness
798
+
799
+ You have the AI Mind Map MCP server connected with 35 tools. Use them INSTEAD of reading raw files.
800
+
801
+ ## ⭐ FIRST THING TO DO IN EVERY CONVERSATION
802
+ Call \`mindmap_session_start\` — it returns the entire project map + recent changes + memories in ONE call (~2000-4000 tokens instead of reading every file at ~50,000+ tokens).
803
+
804
+ ## Quick Lookup: "I need to..." → Use this tool
805
+
806
+ ### Start & Navigate
807
+ - Start any session → \`mindmap_session_start\` ⭐ (THE most important call)
808
+ - Get full project map → \`mindmap_project_map\`
809
+ - See what changed recently → \`mindmap_change_delta\`
810
+
811
+ ### Find & Read Code
812
+ - Find a function/class → \`mindmap_search\`, then \`mindmap_get_code_snippet\`
813
+ - Get function signature only → \`mindmap_get_signature\` (don't open the file)
814
+ - Find who calls a function → \`mindmap_find_references\`
815
+ - What's in this file? → \`mindmap_get_file_map\`
816
+
817
+ ### Understand Flow & Architecture
818
+ - What does this button/feature do? → \`mindmap_trace_flow\`
819
+ - Show all routes/events/components → \`mindmap_interaction_map\`
820
+ - What layer is this file? → \`mindmap_classify_file\`
821
+ - Architecture overview → \`mindmap_layer_overview\`
822
+
823
+ ### Debug & Investigate
824
+ - Something broke → \`mindmap_debug_changes\` (shows ACTUAL git diffs)
825
+ - What did the file look like before? → \`mindmap_file_before\`
826
+ - What's the blast radius? → \`mindmap_impact_analysis\`
827
+ - File commit history → \`mindmap_file_history\`
828
+
829
+ ### Memory (persists across sessions)
830
+ - Remember something → \`mindmap_remember\`
831
+ - Recall past knowledge → \`mindmap_recall\`
832
+ - Record a decision → \`mindmap_decide\`
833
+ - Past decisions → \`mindmap_get_decisions\`
834
+
835
+ ### Advanced
836
+ - Cypher graph query → \`mindmap_query_graph\`
837
+ - Find dead code → \`mindmap_dead_code\`
838
+ - Full architecture report → \`mindmap_architecture\`
839
+
840
+ ## Rules
841
+ 1. ALWAYS call \`mindmap_session_start\` at the beginning of every conversation
842
+ 2. PREFER Mind Map tools over reading raw files — but read directly when you need comments, edge cases, or complex algorithm logic
843
+ 3. START debugging with \`mindmap_debug_changes\` — then drill into specific files
844
+ 4. Use \`mindmap_remember\` for important learnings, \`mindmap_decide\` for architecture choices
845
+ 5. If Mind Map returns unexpected results, the index may be stale — read the file and run \`mindmap_reindex\`
846
+
847
+ ## When to READ FILES DIRECTLY
848
+ - Complex algorithm logic that signatures can't capture
849
+ - Reading comments, TODOs, inline docs
850
+ - Small config files (faster to just read them)
851
+ - Dynamic dispatch, DI, or event-driven code
852
+ - When Mind Map returns "not found" but you suspect the code exists (stale index)
853
+ `;
854
+ }
855
+ /**
856
+ * Deploy rules files into the user's project so AI agents automatically
857
+ * know about Mind Map's capabilities.
858
+ *
859
+ * Creates:
860
+ * - CLAUDE.md (for Claude Code)
861
+ * - .cursorrules (for Cursor)
862
+ * - .agents/AGENTS.md (for Antigravity/Gemini)
863
+ * - .github/copilot-instructions.md (for GitHub Copilot)
864
+ * - .windsurfrules (for Windsurf)
865
+ */
866
+ export function deployRulesFiles(projectRoot) {
867
+ const root = projectRoot ?? process.cwd();
868
+ const rules = getToolAwarenessRules();
869
+ const deployments = [
870
+ {
871
+ name: 'CLAUDE.md',
872
+ filePath: path.join(root, 'CLAUDE.md'),
873
+ content: rules,
874
+ agent: 'Claude Code',
875
+ },
876
+ {
877
+ name: '.cursorrules',
878
+ filePath: path.join(root, '.cursorrules'),
879
+ content: rules,
880
+ agent: 'Cursor',
881
+ },
882
+ {
883
+ name: '.agents/AGENTS.md',
884
+ filePath: path.join(root, '.agents', 'AGENTS.md'),
885
+ content: rules,
886
+ agent: 'Antigravity / Gemini',
887
+ },
888
+ {
889
+ name: '.github/copilot-instructions.md',
890
+ filePath: path.join(root, '.github', 'copilot-instructions.md'),
891
+ content: rules,
892
+ agent: 'GitHub Copilot',
893
+ },
894
+ {
895
+ name: '.windsurfrules',
896
+ filePath: path.join(root, '.windsurfrules'),
897
+ content: rules,
898
+ agent: 'Windsurf',
899
+ },
900
+ ];
901
+ heading('📋 Deploying AI Agent Rules Files');
902
+ console.log(` ${c.dim}These files teach each AI agent about Mind Map\'s 32 tools${c.reset}`);
903
+ console.log('');
904
+ let deployed = 0;
905
+ let skipped = 0;
906
+ for (const dep of deployments) {
907
+ // Skip if file already exists with our content
908
+ if (existsSync(dep.filePath)) {
909
+ try {
910
+ const existing = readFileSync(dep.filePath, 'utf-8');
911
+ if (existing.includes('AI Mind Map MCP')) {
912
+ console.log(` ${c.yellow}⊘${c.reset} ${dep.name} ${c.dim}(already has Mind Map rules)${c.reset}`);
913
+ skipped++;
914
+ continue;
915
+ }
916
+ // File exists but doesn't have our rules — append
917
+ const separator = '\n\n---\n\n';
918
+ writeFileSync(dep.filePath, existing + separator + dep.content, 'utf-8');
919
+ console.log(` ${c.green}✓${c.reset} ${dep.name} ${c.dim}(appended Mind Map rules)${c.reset} — for ${dep.agent}`);
920
+ deployed++;
921
+ }
922
+ catch {
923
+ console.log(` ${c.red}✗${c.reset} ${dep.name} ${c.dim}(failed to read/write)${c.reset}`);
924
+ }
925
+ }
926
+ else {
927
+ // Create new file
928
+ try {
929
+ const dir = path.dirname(dep.filePath);
930
+ if (!existsSync(dir)) {
931
+ mkdirSync(dir, { recursive: true });
932
+ }
933
+ writeFileSync(dep.filePath, dep.content, 'utf-8');
934
+ console.log(` ${c.green}✓${c.reset} ${dep.name} ${c.dim}(created)${c.reset} — for ${dep.agent}`);
935
+ deployed++;
936
+ }
937
+ catch {
938
+ console.log(` ${c.red}✗${c.reset} ${dep.name} ${c.dim}(failed to create)${c.reset}`);
939
+ }
940
+ }
941
+ }
942
+ console.log('');
943
+ logOk(`${deployed} rules file(s) deployed, ${skipped} already up-to-date.`);
944
+ console.log(` ${c.dim}Each AI agent will now know about all 32 Mind Map tools.${c.reset}`);
945
+ }
946
+ //# sourceMappingURL=install.js.map