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/index.js ADDED
@@ -0,0 +1,944 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AI Mind Map β€” MCP Server Entry Point
4
+ *
5
+ * Creates the MCP server with stdio transport, registers all tools,
6
+ * initialises ALL real subsystems (knowledge graph, change tracker,
7
+ * persistent memory, context engine), and handles graceful shutdown.
8
+ *
9
+ * Usage:
10
+ * ai-mind-map [--project-root <path>] [--db-path <path>] [--log-level debug|info|warn|error]
11
+ */
12
+ import { existsSync, mkdirSync, statSync } from 'node:fs';
13
+ import path from 'node:path';
14
+ import process from 'node:process';
15
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
+ import Database from 'better-sqlite3';
18
+ import { loadConfig, parseCliArgs } from './config.js';
19
+ // ── Knowledge Graph ───────────────────────────────────────────
20
+ import { KnowledgeGraph } from './knowledge-graph/graph.js';
21
+ // Note: parser.ts exports functions (parseFile, parseFiles, etc.), not a class
22
+ import { Indexer } from './knowledge-graph/indexer.js';
23
+ import { PageRankEngine } from './knowledge-graph/pagerank.js';
24
+ // ── Change Tracker ────────────────────────────────────────────
25
+ import { FileWatcher } from './change-tracker/watcher.js';
26
+ import { DiffEngine } from './change-tracker/diff-engine.js';
27
+ import { ChangeLog } from './change-tracker/change-log.js';
28
+ // ── Memory ────────────────────────────────────────────────────
29
+ import { SessionMemory } from './memory/session-memory.js';
30
+ import { PersistentMemory } from './memory/persistent-memory.js';
31
+ import { DecisionLog } from './memory/decision-log.js';
32
+ // ── Context Engine ────────────────────────────────────────────
33
+ // compressor.ts exports functions: compress, detectContentType
34
+ import { compress as compressContent } from './context/compressor.js';
35
+ // progressive-disclosure.ts exports function: buildContextPackage
36
+ import { buildContextPackage } from './context/progressive-disclosure.js';
37
+ import { estimateTokens } from './utils/token-counter.js';
38
+ // ── Tools ─────────────────────────────────────────────────────
39
+ import { registerGraphTools } from './tools/graph-tools.js';
40
+ import { registerChangeTools } from './tools/change-tools.js';
41
+ import { registerMemoryTools } from './tools/memory-tools.js';
42
+ import { registerContextTools } from './tools/context-tools.js';
43
+ import { registerDebugTools } from './tools/debug-tools.js';
44
+ import { registerFlowTools } from './tools/flow-tools.js';
45
+ import { registerSnapshotTools } from './tools/snapshot-tools.js';
46
+ import { registerSmartTools } from './tools/smart-tools.js';
47
+ import { registerEvolvingTools } from './tools/evolving-tools.js';
48
+ // ============================================================
49
+ // Logger β€” writes to stderr so MCP stdio is uncontaminated
50
+ // ============================================================
51
+ const LOG_LEVELS = {
52
+ debug: 0,
53
+ info: 1,
54
+ warn: 2,
55
+ error: 3,
56
+ };
57
+ let currentLogLevel = LOG_LEVELS.info;
58
+ function setLogLevel(level) {
59
+ currentLogLevel = LOG_LEVELS[level];
60
+ }
61
+ function log(level, message, ...extra) {
62
+ if (LOG_LEVELS[level] < currentLogLevel)
63
+ return;
64
+ const ts = new Date().toISOString();
65
+ const prefix = `[${ts}] [${level.toUpperCase()}]`;
66
+ if (extra.length > 0) {
67
+ process.stderr.write(`${prefix} ${message} ${JSON.stringify(extra)}\n`);
68
+ }
69
+ else {
70
+ process.stderr.write(`${prefix} ${message}\n`);
71
+ }
72
+ }
73
+ // ============================================================
74
+ // Adapters β€” Bridge real implementations to tool interfaces
75
+ // ============================================================
76
+ /**
77
+ * Creates an adapter that satisfies IKnowledgeGraph from the real
78
+ * KnowledgeGraph and PageRankEngine classes.
79
+ *
80
+ * Key API mappings:
81
+ * - graph.search(query, limit) β€” FTS5 search, no type filter param
82
+ * - graph.getProjectOverview() β€” returns Map<string, GraphNode[]>, no args
83
+ * - graph.findCallers(nodeId) / graph.findCallees(nodeId) β€” single nodeId arg
84
+ * - graph.getNodesByName(name) β€” returns GraphNode[]
85
+ * - graph.getFileStructure(filePath) β€” returns GraphNode[]
86
+ */
87
+ function createGraphAdapter(graph, pagerank) {
88
+ return {
89
+ search: (query, type, limit) => {
90
+ const maxResults = limit ?? 20;
91
+ let results = graph.search(query, maxResults);
92
+ // Expand with learned search aliases
93
+ try {
94
+ const aliases = graph.getLearnedSearchAliases();
95
+ const lowerQuery = query.toLowerCase();
96
+ for (const alias of aliases) {
97
+ if (alias.term.toLowerCase() === lowerQuery) {
98
+ for (const alt of alias.aliases) {
99
+ if (results.length >= maxResults)
100
+ break;
101
+ const aliasResults = graph.search(alt, Math.max(3, maxResults - results.length));
102
+ const existingIds = new Set(results.map(r => r.id));
103
+ for (const r of aliasResults) {
104
+ if (!existingIds.has(r.id) && results.length < maxResults) {
105
+ results.push(r);
106
+ existingIds.add(r.id);
107
+ }
108
+ }
109
+ }
110
+ // Touch the alias to track usage
111
+ try {
112
+ graph.touchLearnedRule(alias.id);
113
+ }
114
+ catch { }
115
+ break;
116
+ }
117
+ }
118
+ }
119
+ catch {
120
+ // Learned rules table might not exist
121
+ }
122
+ if (type) {
123
+ return results.filter((n) => n.type === type);
124
+ }
125
+ return results;
126
+ },
127
+ getStructure: (depth) => {
128
+ // graph.getProjectOverview() returns Map<string, GraphNode[]>
129
+ // We convert it to the shape expected by IKnowledgeGraph.getStructure
130
+ const overview = graph.getProjectOverview();
131
+ const files = [];
132
+ for (const [filePath, nodes] of overview) {
133
+ files.push({
134
+ path: filePath,
135
+ symbols: nodes.map(n => ({
136
+ name: n.name,
137
+ type: n.type,
138
+ signature: n.signature,
139
+ })),
140
+ });
141
+ }
142
+ // depth is accepted but getProjectOverview doesn't take a depth arg;
143
+ // we can slice results if needed, but return all for now.
144
+ return { files };
145
+ },
146
+ traceDependencies: (symbolName, direction, depth) => {
147
+ const nodes = graph.getNodesByName(symbolName);
148
+ if (nodes.length === 0) {
149
+ return { root: symbolName, direction, depth, nodes: [], edges: [] };
150
+ }
151
+ const rootNode = nodes[0];
152
+ let traced = [];
153
+ // findCallers(nodeId) and findCallees(nodeId) each take a single string arg
154
+ if (direction === 'callers' || direction === 'both') {
155
+ traced = traced.concat(graph.findCallers(rootNode.id));
156
+ }
157
+ if (direction === 'callees' || direction === 'both') {
158
+ traced = traced.concat(graph.findCallees(rootNode.id));
159
+ }
160
+ // Deduplicate
161
+ const seen = new Set();
162
+ const unique = traced.filter(n => {
163
+ if (seen.has(n.id))
164
+ return false;
165
+ seen.add(n.id);
166
+ return true;
167
+ });
168
+ // Collect relevant edges
169
+ const edgeSet = [];
170
+ for (const n of unique) {
171
+ const outEdges = graph.getOutEdges(n.id);
172
+ const inEdges = graph.getInEdges(n.id);
173
+ edgeSet.push(...outEdges, ...inEdges);
174
+ }
175
+ return {
176
+ root: symbolName,
177
+ direction,
178
+ depth,
179
+ nodes: unique,
180
+ edges: edgeSet,
181
+ };
182
+ },
183
+ getSignature: (symbolName, filePath) => {
184
+ const nodes = graph.getNodesByName(symbolName);
185
+ let match;
186
+ if (filePath) {
187
+ match = nodes.find((n) => n.filePath === filePath);
188
+ }
189
+ else {
190
+ match = nodes[0];
191
+ }
192
+ if (!match)
193
+ return null;
194
+ // Return the shape expected by IKnowledgeGraph.getSignature
195
+ return {
196
+ node: match,
197
+ parameters: match.parameters,
198
+ returnType: match.returnType,
199
+ docComment: match.docComment,
200
+ };
201
+ },
202
+ findReferences: (symbolName) => {
203
+ const nodes = graph.getNodesByName(symbolName);
204
+ if (nodes.length === 0) {
205
+ return { symbol: symbolName, references: [] };
206
+ }
207
+ // Find all callers of the first matching node as "references"
208
+ const callers = graph.findCallers(nodes[0].id);
209
+ return {
210
+ symbol: symbolName,
211
+ references: callers.map((n) => ({
212
+ filePath: n.filePath,
213
+ line: n.startLine,
214
+ context: n.signature,
215
+ })),
216
+ };
217
+ },
218
+ getFileMap: (filePath) => {
219
+ const nodes = graph.getFileStructure(filePath);
220
+ if (!nodes || nodes.length === 0)
221
+ return null;
222
+ return {
223
+ filePath,
224
+ symbols: nodes
225
+ .filter(n => n.type !== 'file')
226
+ .map(n => ({
227
+ name: n.name,
228
+ type: n.type,
229
+ signature: n.signature,
230
+ startLine: n.startLine,
231
+ endLine: n.endLine,
232
+ visibility: n.visibility,
233
+ isExported: n.isExported,
234
+ })),
235
+ };
236
+ },
237
+ };
238
+ }
239
+ /**
240
+ * Creates an adapter that satisfies IChangeTracker from the real
241
+ * DiffEngine and ChangeLog classes.
242
+ *
243
+ * Key API mappings:
244
+ * - changeLog.getLatestSession() β€” returns ChangeSession | null
245
+ * - changeLog.queryChanges(options) β€” options has `since` (timestamp), not `afterTimestamp`
246
+ * - changeLog.generateSessionSummary(sessionId) β€” returns string
247
+ * - changeLog.recordChange(change) β€” records a FileChange
248
+ */
249
+ function createChangeAdapter(diffEngine, changeLog, graph) {
250
+ return {
251
+ getChanges: (since) => {
252
+ try {
253
+ const latestSession = changeLog.getLatestSession();
254
+ const sinceTimestamp = since === 'last_session'
255
+ ? (latestSession?.endedAt ?? Date.now() - 86400000)
256
+ : since === 'today'
257
+ ? new Date().setHours(0, 0, 0, 0)
258
+ : since === 'this_week'
259
+ ? Date.now() - 7 * 86400000
260
+ : new Date(since).getTime();
261
+ // ChangeLog.queryChanges uses ChangeQueryOptions with `since` field (timestamp)
262
+ const changes = changeLog.queryChanges({
263
+ since: sinceTimestamp,
264
+ });
265
+ return {
266
+ since,
267
+ resolvedTimestamp: sinceTimestamp,
268
+ changes,
269
+ totalFilesChanged: new Set(changes.map(c => c.filePath)).size,
270
+ totalLinesAdded: changes.reduce((sum, c) => sum + c.linesAdded, 0),
271
+ totalLinesRemoved: changes.reduce((sum, c) => sum + c.linesRemoved, 0),
272
+ summary: changes.length > 0
273
+ ? changes.map(c => `- ${c.filePath}: ${c.summary}`).join('\n')
274
+ : 'No changes found since the specified time.',
275
+ };
276
+ }
277
+ catch {
278
+ return {
279
+ since,
280
+ resolvedTimestamp: Date.now(),
281
+ changes: [],
282
+ totalFilesChanged: 0,
283
+ totalLinesAdded: 0,
284
+ totalLinesRemoved: 0,
285
+ summary: 'Unable to retrieve changes.',
286
+ };
287
+ }
288
+ },
289
+ getSessionDiff: () => {
290
+ try {
291
+ const latestSession = changeLog.getLatestSession();
292
+ if (!latestSession) {
293
+ return {
294
+ previousSession: null,
295
+ changes: [],
296
+ affectedSymbols: [],
297
+ summary: 'No previous session found.',
298
+ };
299
+ }
300
+ const changes = changeLog.queryChanges({
301
+ since: latestSession.endedAt ?? latestSession.startedAt,
302
+ });
303
+ const symbols = changes.flatMap(c => c.symbolsAffected);
304
+ return {
305
+ previousSession: latestSession,
306
+ changes,
307
+ affectedSymbols: [...new Set(symbols)],
308
+ summary: changeLog.generateSessionSummary(latestSession.sessionId),
309
+ };
310
+ }
311
+ catch {
312
+ return {
313
+ previousSession: null,
314
+ changes: [],
315
+ affectedSymbols: [],
316
+ summary: 'Unable to compute session diff.',
317
+ };
318
+ }
319
+ },
320
+ analyseImpact: (params) => {
321
+ const target = params.filePath ?? params.symbolName ?? 'unknown';
322
+ // Try to find the node and compute blast radius
323
+ let directlyAffected = [];
324
+ let transitivelyAffected = [];
325
+ let riskLevel = 'low';
326
+ try {
327
+ if (params.symbolName) {
328
+ const nodes = graph.getNodesByName(params.symbolName);
329
+ if (nodes.length > 0) {
330
+ const rootNode = nodes[0];
331
+ const callers = graph.findCallers(rootNode.id);
332
+ directlyAffected = callers.map(n => ({
333
+ node: n,
334
+ relationship: 'calls',
335
+ }));
336
+ const blastNodes = graph.blastRadius(rootNode.id, 3);
337
+ transitivelyAffected = blastNodes.map((n, idx) => ({
338
+ node: n,
339
+ depth: Math.min(idx + 1, 3),
340
+ }));
341
+ const total = directlyAffected.length + transitivelyAffected.length;
342
+ riskLevel = total > 20 ? 'high' : total > 5 ? 'medium' : 'low';
343
+ }
344
+ }
345
+ else if (params.filePath) {
346
+ const fileNodes = graph.getFileStructure(params.filePath);
347
+ for (const node of fileNodes) {
348
+ if (node.type === 'file')
349
+ continue;
350
+ const callers = graph.findCallers(node.id);
351
+ for (const caller of callers) {
352
+ directlyAffected.push({ node: caller, relationship: 'calls' });
353
+ }
354
+ }
355
+ riskLevel = directlyAffected.length > 10 ? 'high'
356
+ : directlyAffected.length > 3 ? 'medium' : 'low';
357
+ }
358
+ }
359
+ catch {
360
+ // Fall through with empty arrays
361
+ }
362
+ return {
363
+ target,
364
+ directlyAffected,
365
+ transitivelyAffected,
366
+ riskLevel,
367
+ summary: directlyAffected.length > 0
368
+ ? `${target}: ${directlyAffected.length} directly affected, ${transitivelyAffected.length} transitively affected. Risk: ${riskLevel}.`
369
+ : `${target}: No dependents found. Use mindmap_trace_dependencies for full dependency chain.`,
370
+ };
371
+ },
372
+ };
373
+ }
374
+ /**
375
+ * Creates an adapter that satisfies IMemoryStore from PersistentMemory,
376
+ * DecisionLog, and SessionMemory.
377
+ *
378
+ * Key API mappings:
379
+ * - persistentMemory.queryMemories(query) β€” query uses MemoryQuery shape
380
+ * - persistentMemory.createMemory(input) β€” input is CreateMemoryInput
381
+ * - persistentMemory.getStats() β€” returns MemoryStats
382
+ * - decisionLog.queryDecisions(query) β€” query uses DecisionQuery
383
+ * - decisionLog.createDecision(input) β€” returns { decision, conflicts }
384
+ * - decisionLog.getActiveDecisions() β€” returns Decision[]
385
+ * - sessionMemory.listRecentSessions(limit) β€” returns SessionListItem[]
386
+ */
387
+ function createMemoryAdapter(persistentMemory, decisionLog, sessionMemory) {
388
+ return {
389
+ recall: (query, category, limit) => {
390
+ return persistentMemory.queryMemories({
391
+ text: query,
392
+ categories: category ? [category] : undefined,
393
+ limit: limit ?? 10,
394
+ });
395
+ },
396
+ remember: (params) => {
397
+ return persistentMemory.createMemory({
398
+ category: params.category,
399
+ content: params.content,
400
+ importance: params.importance,
401
+ tags: params.tags,
402
+ relatedFiles: params.relatedFiles,
403
+ sessionId: params.sessionId,
404
+ source: params.source,
405
+ });
406
+ },
407
+ getDecisions: (params) => {
408
+ if (params.query) {
409
+ // Use DecisionLog.queryDecisions with text search
410
+ const results = decisionLog.queryDecisions({
411
+ text: params.query,
412
+ status: params.status === 'all' ? undefined : (params.status ?? 'active'),
413
+ });
414
+ return results;
415
+ }
416
+ if (!params.status || params.status === 'active') {
417
+ return decisionLog.getActiveDecisions();
418
+ }
419
+ // 'all' β€” query with no filters
420
+ return decisionLog.queryDecisions({});
421
+ },
422
+ decide: (params) => {
423
+ // DecisionLog.createDecision returns { decision, conflicts }
424
+ const result = decisionLog.createDecision({
425
+ title: params.title,
426
+ description: params.description,
427
+ rationale: params.rationale,
428
+ alternatives: params.alternatives,
429
+ consequences: params.consequences,
430
+ relatedFiles: params.relatedFiles,
431
+ tags: params.tags,
432
+ decidedBy: params.decidedBy,
433
+ });
434
+ return result.decision;
435
+ },
436
+ getSessionSummaries: (count) => {
437
+ // sessionMemory.listRecentSessions returns SessionListItem[]
438
+ // We need to return SessionSummary[], so we adapt
439
+ const sessions = sessionMemory.listRecentSessions(count);
440
+ return sessions.map(s => {
441
+ // Try to get full session details if available
442
+ const full = sessionMemory.getSession(s.sessionId);
443
+ if (full) {
444
+ return {
445
+ sessionId: full.sessionId,
446
+ startedAt: full.startedAt,
447
+ endedAt: full.endedAt,
448
+ tasksCompleted: full.tasksCompleted,
449
+ filesModified: full.filesModified,
450
+ decisionseMade: full.decisionseMade,
451
+ memoriesCreated: full.memoriesCreated,
452
+ tokensSaved: full.tokensSaved,
453
+ summary: full.summary,
454
+ };
455
+ }
456
+ // Fallback: return minimal SessionSummary from SessionListItem
457
+ return {
458
+ sessionId: s.sessionId,
459
+ startedAt: s.startedAt,
460
+ endedAt: s.endedAt ?? Date.now(),
461
+ tasksCompleted: [],
462
+ filesModified: [],
463
+ decisionseMade: [],
464
+ memoriesCreated: 0,
465
+ tokensSaved: 0,
466
+ summary: s.summary,
467
+ };
468
+ });
469
+ },
470
+ };
471
+ }
472
+ /**
473
+ * Creates an adapter that satisfies ISessionProvider.
474
+ * sessionMemory.getCurrentSessionId() returns string | null.
475
+ * The interface expects string, so we provide a fallback.
476
+ */
477
+ function createSessionAdapter(sessionMemory) {
478
+ return {
479
+ currentSessionId: () => {
480
+ return sessionMemory.getCurrentSessionId() ?? 'no-session';
481
+ },
482
+ };
483
+ }
484
+ /**
485
+ * Creates an adapter that satisfies IContextEngine.
486
+ *
487
+ * Key API mappings:
488
+ * - compress(text, level, contentType) β€” module-level function from compressor.ts
489
+ * - buildContextPackage(...) β€” module-level function from progressive-disclosure.ts
490
+ */
491
+ function createContextAdapter(graph, persistentMemory, decisionLog, changeLog, config) {
492
+ return {
493
+ getContext: (params) => {
494
+ try {
495
+ // Build ProjectInfo for Tier 1
496
+ const overview = graph.getProjectOverview();
497
+ const stats = graph.getStats();
498
+ const directoryTree = Array.from(overview.keys())
499
+ .slice(0, 20)
500
+ .map(f => ` ${f}`)
501
+ .join('\n');
502
+ const projectInfo = {
503
+ name: path.basename(config.projectRoot),
504
+ description: params.taskDescription,
505
+ techStack: Object.keys(stats.languageBreakdown),
506
+ directoryTree,
507
+ conventions: [],
508
+ currentTask: params.taskDescription,
509
+ };
510
+ // Build Tier 2 data
511
+ const tier2Data = {};
512
+ if (params.includeMemories) {
513
+ tier2Data.memories = persistentMemory.queryMemories({
514
+ text: params.taskDescription,
515
+ limit: 5,
516
+ });
517
+ tier2Data.decisions = decisionLog.getActiveDecisions();
518
+ }
519
+ if (params.includeChanges) {
520
+ tier2Data.recentChanges = changeLog.queryChanges({
521
+ limit: 10,
522
+ });
523
+ }
524
+ // Search graph for relevant nodes
525
+ const graphNodes = graph.search(params.taskDescription, 10);
526
+ if (graphNodes.length > 0) {
527
+ tier2Data.graphNodes = graphNodes;
528
+ }
529
+ // Build context package
530
+ const pkg = buildContextPackage(projectInfo, tier2Data, {}, // Tier 3 data β€” empty for initial load
531
+ config.tokenBudgets, params.taskDescription);
532
+ return pkg;
533
+ }
534
+ catch {
535
+ return {
536
+ tier1: 'Project context loading failed.',
537
+ tier2: '',
538
+ tier3: '',
539
+ totalTokens: 10,
540
+ tokensSaved: 0,
541
+ breakdown: [],
542
+ };
543
+ }
544
+ },
545
+ compress: (params) => {
546
+ // compress(text, level, contentType?) from compressor.ts
547
+ // Returns CompressionResult { compressed, originalTokens, compressedTokens, ratio, contentType, level }
548
+ const result = compressContent(params.content, params.level, params.contentType);
549
+ return {
550
+ original: params.content,
551
+ compressed: result.compressed,
552
+ originalTokens: result.originalTokens,
553
+ compressedTokens: result.compressedTokens,
554
+ ratio: result.ratio,
555
+ contentType: result.contentType,
556
+ };
557
+ },
558
+ };
559
+ }
560
+ /**
561
+ * Creates an adapter that satisfies IIndexer.
562
+ *
563
+ * Key API mappings:
564
+ * - indexer.fullIndex(onProgress?) β€” returns Promise<IndexStats>
565
+ * IndexStats has: filesScanned, filesParsed, filesSkipped, filesDeleted,
566
+ * nodesCreated, edgesCreated, parseErrors, durationMs, languages
567
+ * - graph.getStats() β€” returns { totalNodes, totalEdges, totalFiles, nodesByType, edgesByType, languageBreakdown }
568
+ * - persistentMemory.getStats() β€” returns MemoryStats
569
+ * - changeLog.getStats(topN?) β€” returns ChangeLogStats
570
+ */
571
+ function createIndexerAdapter(indexer, graph, persistentMemory, decisionLog, changeLog, config) {
572
+ return {
573
+ reindex: async () => {
574
+ const startTime = Date.now();
575
+ try {
576
+ const result = await indexer.fullIndex();
577
+ return {
578
+ filesScanned: result.filesScanned,
579
+ filesIndexed: result.filesParsed,
580
+ nodesCreated: result.nodesCreated,
581
+ edgesCreated: result.edgesCreated,
582
+ durationMs: result.durationMs,
583
+ errors: result.parseErrors > 0
584
+ ? [`${result.parseErrors} parse errors encountered`]
585
+ : [],
586
+ };
587
+ }
588
+ catch (err) {
589
+ return {
590
+ filesScanned: 0,
591
+ filesIndexed: 0,
592
+ nodesCreated: 0,
593
+ edgesCreated: 0,
594
+ durationMs: Date.now() - startTime,
595
+ errors: [err instanceof Error ? err.message : String(err)],
596
+ };
597
+ }
598
+ },
599
+ getStats: () => {
600
+ let dbSize = 0;
601
+ try {
602
+ if (existsSync(config.dbPath)) {
603
+ dbSize = statSync(config.dbPath).size;
604
+ }
605
+ }
606
+ catch {
607
+ // ignore
608
+ }
609
+ const graphStats = graph.getStats();
610
+ const memoryStats = persistentMemory.getStats();
611
+ const changeStats = changeLog.getStats();
612
+ // DecisionLog doesn't have a count() method; use queryDecisions
613
+ const allDecisions = decisionLog.queryDecisions({});
614
+ return {
615
+ indexedFiles: graphStats.totalFiles,
616
+ totalNodes: graphStats.totalNodes,
617
+ totalEdges: graphStats.totalEdges,
618
+ totalMemories: memoryStats.totalMemories,
619
+ totalDecisions: allDecisions.length,
620
+ totalChangesTracked: changeStats.totalChanges,
621
+ lastIndexedAt: null, // Graph doesn't track this directly
622
+ lastChangeAt: null,
623
+ dbSizeBytes: dbSize,
624
+ languageBreakdown: graphStats.languageBreakdown,
625
+ tokensSavedEstimate: graphStats.totalNodes * 500,
626
+ };
627
+ },
628
+ };
629
+ }
630
+ // ============================================================
631
+ // Ensure database directory exists
632
+ // ============================================================
633
+ function ensureDbDirectory(dbPath) {
634
+ const dir = path.dirname(dbPath);
635
+ if (!existsSync(dir)) {
636
+ mkdirSync(dir, { recursive: true });
637
+ log('info', `Created database directory: ${dir}`);
638
+ }
639
+ }
640
+ // ============================================================
641
+ // Main entry point
642
+ // ============================================================
643
+ async function main() {
644
+ // ── 1. Parse CLI & load config ──────────────────────────────
645
+ let cliArgs;
646
+ try {
647
+ cliArgs = parseCliArgs();
648
+ }
649
+ catch (err) {
650
+ const msg = err instanceof Error ? err.message : String(err);
651
+ process.stderr.write(`Error parsing CLI arguments: ${msg}\n`);
652
+ process.exit(1);
653
+ }
654
+ setLogLevel(cliArgs.logLevel);
655
+ log('info', '🧠 AI Mind Map MCP Server starting…');
656
+ let config;
657
+ try {
658
+ config = await loadConfig(cliArgs);
659
+ log('info', `Project root: ${config.projectRoot}`);
660
+ log('info', `Database path: ${config.dbPath}`);
661
+ log('debug', 'Loaded config', config);
662
+ }
663
+ catch (err) {
664
+ const msg = err instanceof Error ? err.message : String(err);
665
+ log('error', `Failed to load configuration: ${msg}`);
666
+ process.exit(1);
667
+ }
668
+ // ── 2. Initialise database directory ────────────────────────
669
+ try {
670
+ ensureDbDirectory(config.dbPath);
671
+ }
672
+ catch (err) {
673
+ const msg = err instanceof Error ? err.message : String(err);
674
+ log('error', `Failed to create DB directory: ${msg}`);
675
+ process.exit(1);
676
+ }
677
+ // ── 3. Initialise SQLite database ──────────────────────────
678
+ // KnowledgeGraph manages its own db connection, but ChangeLog,
679
+ // SessionMemory, PersistentMemory, and DecisionLog each need
680
+ // a shared Database instance for their tables.
681
+ log('info', 'Initialising database…');
682
+ let sharedDb;
683
+ try {
684
+ sharedDb = new Database(config.dbPath);
685
+ sharedDb.pragma('journal_mode = WAL');
686
+ sharedDb.pragma('foreign_keys = ON');
687
+ sharedDb.pragma('busy_timeout = 5000');
688
+ log('info', 'Database initialized with WAL mode');
689
+ }
690
+ catch (err) {
691
+ const msg = err instanceof Error ? err.message : String(err);
692
+ log('error', `Failed to initialize database: ${msg}`);
693
+ process.exit(1);
694
+ }
695
+ // ── 4. Initialise real subsystems ──────────────────────────
696
+ log('info', 'Initialising subsystems…');
697
+ // Knowledge Graph β€” constructor takes dbPath string
698
+ const graph = new KnowledgeGraph(config.dbPath);
699
+ // Indexer β€” constructor is Indexer(graph, config)
700
+ const indexer = new Indexer(graph, config);
701
+ // PageRankEngine β€” constructor is PageRankEngine(graph, config?)
702
+ const pagerank = new PageRankEngine(graph);
703
+ log('info', 'βœ… Knowledge Graph initialized');
704
+ // Change Tracker
705
+ // ChangeLog constructor takes ChangeLogConfig: { dbPath, retentionDays?, defaultSearchLimit? }
706
+ const changeLog = new ChangeLog({ dbPath: config.dbPath });
707
+ const diffEngine = new DiffEngine(config.projectRoot);
708
+ let watcher = null;
709
+ // SessionMemory β€” must be created before watcher so it's available in the handler
710
+ // SessionMemory constructor takes Database.Database instance
711
+ const sessionMemory = new SessionMemory(sharedDb);
712
+ const sessionId = sessionMemory.startSession();
713
+ log('info', `Session started: ${sessionId}`);
714
+ if (config.watchEnabled) {
715
+ // FileWatcher constructor: config with { projectRoot, watchDebounceMs?, maxFileSize?, ignore? }
716
+ watcher = new FileWatcher({
717
+ projectRoot: config.projectRoot,
718
+ ignore: config.ignore,
719
+ watchDebounceMs: config.watchDebounceMs,
720
+ maxFileSize: config.maxFileSize,
721
+ });
722
+ // FileWatcher emits 'changes' with WatcherEvent[]
723
+ watcher.on('changes', async (events) => {
724
+ log('debug', `File watcher detected ${events.length} changes`);
725
+ for (const event of events) {
726
+ try {
727
+ if (event.changeType === 'deleted') {
728
+ indexer.removeFile(event.filePath);
729
+ }
730
+ else {
731
+ await indexer.indexFile(event.filePath);
732
+ }
733
+ const currentSessionId = sessionMemory.getCurrentSessionId() ?? 'no-session';
734
+ changeLog.recordChange({
735
+ filePath: event.filePath,
736
+ changeType: event.changeType,
737
+ summary: `File ${event.changeType}: ${path.basename(event.filePath)}`,
738
+ symbolsAffected: [],
739
+ linesAdded: 0,
740
+ linesRemoved: 0,
741
+ timestamp: event.timestamp,
742
+ sessionId: currentSessionId,
743
+ });
744
+ }
745
+ catch (err) {
746
+ log('warn', `Failed to process file change: ${event.filePath}`, err);
747
+ }
748
+ }
749
+ // Invalidate PageRank cache when graph changes
750
+ pagerank.invalidateCache();
751
+ });
752
+ }
753
+ log('info', `βœ… Change Tracker initialized (watcher: ${config.watchEnabled ? 'enabled' : 'disabled'})`);
754
+ // Memory
755
+ // PersistentMemory constructor: (db, config?) where config is Pick<MindMapConfig['memory'], 'decayRate' | 'maxMemories' | 'importanceThreshold'>
756
+ const persistentMemory = new PersistentMemory(sharedDb, {
757
+ decayRate: config.memory.decayRate,
758
+ maxMemories: config.memory.maxMemories,
759
+ importanceThreshold: config.memory.importanceThreshold,
760
+ });
761
+ // DecisionLog constructor: (db, config?) where config is Pick<MindMapConfig['memory'], 'maxDecisions'>
762
+ const decisionLog = new DecisionLog(sharedDb, {
763
+ maxDecisions: config.memory.maxDecisions,
764
+ });
765
+ log('info', `βœ… Memory initialized (session: ${sessionMemory.getCurrentSessionId()})`);
766
+ // Context Engine β€” no class instances needed; uses module-level functions
767
+ log('info', 'βœ… Context Engine initialized');
768
+ // ── 5. Build adapters ──────────────────────────────────────
769
+ const graphAdapter = createGraphAdapter(graph, pagerank);
770
+ const changeAdapter = createChangeAdapter(diffEngine, changeLog, graph);
771
+ const memoryAdapter = createMemoryAdapter(persistentMemory, decisionLog, sessionMemory);
772
+ const sessionAdapter = createSessionAdapter(sessionMemory);
773
+ const contextAdapter = createContextAdapter(graph, persistentMemory, decisionLog, changeLog, config);
774
+ const indexerAdapter = createIndexerAdapter(indexer, graph, persistentMemory, decisionLog, changeLog, config);
775
+ // Token estimator using the exported estimateTokens function
776
+ const tokenEstimator = {
777
+ estimate: (text) => estimateTokens(text),
778
+ };
779
+ // ── 6. Create MCP server ──────────────────────────────────
780
+ const server = new McpServer({
781
+ name: 'ai-mind-map',
782
+ version: '1.0.0',
783
+ });
784
+ // ── 7. Register all tools ─────────────────────────────────
785
+ log('info', 'Registering MCP tools…');
786
+ registerGraphTools(server, graphAdapter, tokenEstimator);
787
+ log('debug', 'Registered graph tools (6)');
788
+ registerChangeTools(server, changeAdapter, tokenEstimator);
789
+ log('debug', 'Registered change tools (3)');
790
+ registerMemoryTools(server, memoryAdapter, sessionAdapter, tokenEstimator);
791
+ log('debug', 'Registered memory tools (5)');
792
+ registerContextTools(server, contextAdapter, indexerAdapter, tokenEstimator);
793
+ log('debug', 'Registered context tools (4)');
794
+ registerDebugTools(server, graph, config, tokenEstimator);
795
+ log('debug', 'Registered debug tools (3)');
796
+ registerFlowTools(server, graph, config, tokenEstimator);
797
+ log('debug', 'Registered flow tools (4)');
798
+ registerSnapshotTools(server, graph, config, tokenEstimator);
799
+ log('debug', 'Registered snapshot tools (3)');
800
+ registerSmartTools(server, graph, config, tokenEstimator);
801
+ log('debug', 'Registered smart tools (3)');
802
+ registerEvolvingTools(server, graph, config, tokenEstimator);
803
+ log('debug', 'Registered evolving tools (3)');
804
+ log('info', 'πŸ”§ All 41 MCP tools registered:');
805
+ log('info', ' Graph: mindmap_search, mindmap_get_structure, mindmap_trace_dependencies, mindmap_get_signature, mindmap_find_references, mindmap_get_file_map');
806
+ log('info', ' Changes: mindmap_what_changed, mindmap_session_diff, mindmap_impact_analysis');
807
+ log('info', ' Memory: mindmap_recall, mindmap_remember, mindmap_get_decisions, mindmap_decide, mindmap_session_summary');
808
+ log('info', ' Context: mindmap_get_context, mindmap_compress, mindmap_reindex, mindmap_status');
809
+ log('info', ' Debug: mindmap_debug_changes, mindmap_file_before, mindmap_file_history');
810
+ log('info', ' Flow: mindmap_trace_flow, mindmap_interaction_map, mindmap_classify_file, mindmap_layer_overview');
811
+ log('info', ' Snapshot: mindmap_project_map, mindmap_change_delta, mindmap_session_start ⭐');
812
+ log('info', ' Advanced: mindmap_query_graph, mindmap_dead_code, mindmap_architecture, mindmap_get_code_snippet, mindmap_search_code, mindmap_list_projects, mindmap_health');
813
+ log('info', ' Smart: mindmap_explain ⭐, mindmap_git_changes ⭐, mindmap_smart_search ⭐');
814
+ log('info', ' Evolving: mindmap_teach ⭐, mindmap_get_learned, mindmap_forget');
815
+ // ── 8. Auto-index on first run ─────────────────────────────
816
+ const stats = graph.getStats();
817
+ if (stats.totalNodes === 0) {
818
+ log('info', 'πŸ“‹ No existing index found. Running initial codebase indexing…');
819
+ try {
820
+ const result = await indexer.fullIndex();
821
+ log('info', `βœ… Initial index complete: ${result.filesParsed} files, ${result.nodesCreated} nodes, ${result.edgesCreated} edges`);
822
+ if (result.parseErrors > 0) {
823
+ log('warn', `⚠️ ${result.parseErrors} parse errors (non-fatal)`);
824
+ }
825
+ }
826
+ catch (err) {
827
+ log('warn', `⚠️ Initial indexing failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
828
+ }
829
+ }
830
+ else {
831
+ log('info', `πŸ“‹ Existing index found: ${stats.totalNodes} nodes. Running incremental update…`);
832
+ try {
833
+ const result = await indexer.incrementalIndex();
834
+ log('info', `βœ… Incremental update: ${result.filesParsed} files reindexed`);
835
+ }
836
+ catch (err) {
837
+ log('warn', `⚠️ Incremental update failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
838
+ }
839
+ }
840
+ // ── 9. Start file watcher ──────────────────────────────────
841
+ if (watcher) {
842
+ try {
843
+ await watcher.start();
844
+ log('info', 'πŸ‘οΈ File watcher started');
845
+ }
846
+ catch (err) {
847
+ log('warn', `⚠️ File watcher failed to start: ${err instanceof Error ? err.message : String(err)}`);
848
+ }
849
+ }
850
+ // ── 10. Graceful shutdown ──────────────────────────────────
851
+ let shuttingDown = false;
852
+ async function shutdown(signal) {
853
+ if (shuttingDown)
854
+ return;
855
+ shuttingDown = true;
856
+ log('info', `Received ${signal}, shutting down gracefully…`);
857
+ try {
858
+ // Stop file watcher
859
+ if (watcher) {
860
+ await watcher.stop();
861
+ log('debug', 'File watcher stopped');
862
+ }
863
+ // End current session
864
+ try {
865
+ sessionMemory.endSession();
866
+ log('debug', 'Session ended');
867
+ }
868
+ catch {
869
+ // Ignore session end errors
870
+ }
871
+ // Apply memory decay
872
+ try {
873
+ persistentMemory.applyDecay();
874
+ log('debug', 'Memory decay applied');
875
+ }
876
+ catch {
877
+ // Ignore decay errors
878
+ }
879
+ // Close change log database
880
+ try {
881
+ changeLog.close();
882
+ log('debug', 'Change log closed');
883
+ }
884
+ catch {
885
+ // Ignore close errors
886
+ }
887
+ // Close graph database
888
+ try {
889
+ graph.close();
890
+ log('debug', 'Knowledge graph closed');
891
+ }
892
+ catch {
893
+ // Ignore close errors
894
+ }
895
+ // Close shared database
896
+ try {
897
+ sharedDb.close();
898
+ log('debug', 'Shared database closed');
899
+ }
900
+ catch {
901
+ // Ignore close errors
902
+ }
903
+ log('info', 'βœ… Cleanup complete. Goodbye!');
904
+ }
905
+ catch (err) {
906
+ const msg = err instanceof Error ? err.message : String(err);
907
+ log('error', `Error during shutdown: ${msg}`);
908
+ }
909
+ process.exit(0);
910
+ }
911
+ process.on('SIGINT', () => void shutdown('SIGINT'));
912
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
913
+ // Handle uncaught errors gracefully
914
+ process.on('uncaughtException', (err) => {
915
+ log('error', `Uncaught exception: ${err.message}`, err.stack);
916
+ void shutdown('uncaughtException');
917
+ });
918
+ process.on('unhandledRejection', (reason) => {
919
+ const msg = reason instanceof Error ? reason.message : String(reason);
920
+ log('error', `Unhandled rejection: ${msg}`);
921
+ });
922
+ // ── 11. Connect transport and start serving ────────────────
923
+ log('info', 'Connecting stdio transport…');
924
+ try {
925
+ const transport = new StdioServerTransport();
926
+ await server.connect(transport);
927
+ log('info', '🧠 AI Mind Map MCP Server is LIVE. Waiting for requests…');
928
+ log('info', ` Project: ${config.projectRoot}`);
929
+ log('info', ` Database: ${config.dbPath}`);
930
+ log('info', ` Session: ${sessionMemory.getCurrentSessionId()}`);
931
+ }
932
+ catch (err) {
933
+ const msg = err instanceof Error ? err.message : String(err);
934
+ log('error', `Failed to start MCP server: ${msg}`);
935
+ process.exit(1);
936
+ }
937
+ }
938
+ // ── Kick off ────────────────────────────────────────────────
939
+ main().catch((err) => {
940
+ const msg = err instanceof Error ? err.message : String(err);
941
+ process.stderr.write(`Fatal: ${msg}\n`);
942
+ process.exit(1);
943
+ });
944
+ //# sourceMappingURL=index.js.map