ai-mind-map 1.6.1 → 1.7.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 (48) hide show
  1. package/dist/change-tracker/change-log.js +123 -123
  2. package/dist/change-tracker/watcher.d.ts.map +1 -1
  3. package/dist/change-tracker/watcher.js +1 -0
  4. package/dist/change-tracker/watcher.js.map +1 -1
  5. package/dist/cli.js +83 -83
  6. package/dist/cli.js.map +1 -1
  7. package/dist/context/compressor.js +3 -3
  8. package/dist/context/compressor.js.map +1 -1
  9. package/dist/context/progressive-disclosure.js +4 -4
  10. package/dist/context/progressive-disclosure.js.map +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +168 -143
  13. package/dist/index.js.map +1 -1
  14. package/dist/install.js +114 -114
  15. package/dist/install.js.map +1 -1
  16. package/dist/knowledge-graph/changelog.js +62 -62
  17. package/dist/knowledge-graph/dead-code.js +31 -31
  18. package/dist/knowledge-graph/graph.d.ts +5 -0
  19. package/dist/knowledge-graph/graph.d.ts.map +1 -1
  20. package/dist/knowledge-graph/graph.js +214 -202
  21. package/dist/knowledge-graph/graph.js.map +1 -1
  22. package/dist/knowledge-graph/indexer.d.ts +9 -0
  23. package/dist/knowledge-graph/indexer.d.ts.map +1 -1
  24. package/dist/knowledge-graph/indexer.js +339 -289
  25. package/dist/knowledge-graph/indexer.js.map +1 -1
  26. package/dist/knowledge-graph/semantic-search.js +50 -50
  27. package/dist/memory/decision-log.d.ts.map +1 -1
  28. package/dist/memory/decision-log.js +72 -61
  29. package/dist/memory/decision-log.js.map +1 -1
  30. package/dist/memory/persistent-memory.d.ts.map +1 -1
  31. package/dist/memory/persistent-memory.js +77 -70
  32. package/dist/memory/persistent-memory.js.map +1 -1
  33. package/dist/memory/session-memory.js +54 -54
  34. package/dist/memory/shared-sync.d.ts.map +1 -1
  35. package/dist/memory/shared-sync.js +6 -2
  36. package/dist/memory/shared-sync.js.map +1 -1
  37. package/dist/tools/context-tools.js +2 -2
  38. package/dist/tools/context-tools.js.map +1 -1
  39. package/dist/tools/debug-tools.js +9 -9
  40. package/dist/tools/debug-tools.js.map +1 -1
  41. package/dist/tools/evolving-tools.js +3 -3
  42. package/dist/tools/evolving-tools.js.map +1 -1
  43. package/dist/tools/flow-tools.js +29 -29
  44. package/dist/tools/flow-tools.js.map +1 -1
  45. package/dist/tools/session-tools.js +2 -2
  46. package/dist/tools/session-tools.js.map +1 -1
  47. package/dist/tools/snapshot-tools.js +24 -24
  48. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * AI Mind Map MCP Server Entry Point
3
+ * AI Mind Map — MCP Server Entry Point
4
4
  *
5
5
  * Creates the MCP server with stdio transport, registers all tools,
6
6
  * initialises ALL real subsystems (knowledge graph, change tracker,
@@ -16,27 +16,27 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
16
16
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
17
17
  import Database from 'better-sqlite3';
18
18
  import { loadConfig, parseCliArgs } from './config.js';
19
- // ── Knowledge Graph ───────────────────────────────────────────
19
+ // ── Knowledge Graph ───────────────────────────────────────────
20
20
  import { KnowledgeGraph } from './knowledge-graph/graph.js';
21
21
  // Note: parser.ts exports functions (parseFile, parseFiles, etc.), not a class
22
22
  import { Indexer } from './knowledge-graph/indexer.js';
23
23
  import { PageRankEngine } from './knowledge-graph/pagerank.js';
24
- // ── Change Tracker ────────────────────────────────────────────
24
+ // ── Change Tracker ────────────────────────────────────────────
25
25
  import { FileWatcher } from './change-tracker/watcher.js';
26
26
  import { DiffEngine } from './change-tracker/diff-engine.js';
27
27
  import { ChangeLog } from './change-tracker/change-log.js';
28
- // ── Memory ────────────────────────────────────────────────────
28
+ // ── Memory ────────────────────────────────────────────────────
29
29
  import { SessionMemory } from './memory/session-memory.js';
30
30
  import { PersistentMemory } from './memory/persistent-memory.js';
31
31
  import { DecisionLog } from './memory/decision-log.js';
32
32
  import { syncSharedContext } from './memory/shared-sync.js';
33
- // ── Context Engine ────────────────────────────────────────────
33
+ // ── Context Engine ────────────────────────────────────────────
34
34
  // compressor.ts exports functions: compress, detectContentType
35
35
  import { compress as compressContent } from './context/compressor.js';
36
36
  // progressive-disclosure.ts exports function: buildContextPackage
37
37
  import { buildContextPackage } from './context/progressive-disclosure.js';
38
38
  import { estimateTokens } from './utils/token-counter.js';
39
- // ── Tools ─────────────────────────────────────────────────────
39
+ // ── Tools ─────────────────────────────────────────────────────
40
40
  import { registerGraphTools } from './tools/graph-tools.js';
41
41
  import { registerChangeTools } from './tools/change-tools.js';
42
42
  import { registerMemoryTools } from './tools/memory-tools.js';
@@ -53,7 +53,7 @@ import { ChangelogEngine } from './knowledge-graph/changelog.js';
53
53
  import { registerSessionTools } from './tools/session-tools.js';
54
54
  import { registerDigestTools } from './tools/digest-tools.js';
55
55
  // ============================================================
56
- // Logger writes to stderr so MCP stdio is uncontaminated
56
+ // Logger — writes to stderr so MCP stdio is uncontaminated
57
57
  // ============================================================
58
58
  const LOG_LEVELS = {
59
59
  debug: 0,
@@ -78,18 +78,18 @@ function log(level, message, ...extra) {
78
78
  }
79
79
  }
80
80
  // ============================================================
81
- // Adapters Bridge real implementations to tool interfaces
81
+ // Adapters — Bridge real implementations to tool interfaces
82
82
  // ============================================================
83
83
  /**
84
84
  * Creates an adapter that satisfies IKnowledgeGraph from the real
85
85
  * KnowledgeGraph and PageRankEngine classes.
86
86
  *
87
87
  * Key API mappings:
88
- * - graph.search(query, limit) FTS5 search, no type filter param
89
- * - graph.getProjectOverview() returns Map<string, GraphNode[]>, no args
90
- * - graph.findCallers(nodeId) / graph.findCallees(nodeId) single nodeId arg
91
- * - graph.getNodesByName(name) returns GraphNode[]
92
- * - graph.getFileStructure(filePath) returns GraphNode[]
88
+ * - graph.search(query, limit) — FTS5 search, no type filter param
89
+ * - graph.getProjectOverview() — returns Map<string, GraphNode[]>, no args
90
+ * - graph.findCallers(nodeId) / graph.findCallees(nodeId) — single nodeId arg
91
+ * - graph.getNodesByName(name) — returns GraphNode[]
92
+ * - graph.getFileStructure(filePath) — returns GraphNode[]
93
93
  */
94
94
  function createGraphAdapter(graph, pagerank) {
95
95
  return {
@@ -248,10 +248,10 @@ function createGraphAdapter(graph, pagerank) {
248
248
  * DiffEngine and ChangeLog classes.
249
249
  *
250
250
  * Key API mappings:
251
- * - changeLog.getLatestSession() returns ChangeSession | null
252
- * - changeLog.queryChanges(options) options has `since` (timestamp), not `afterTimestamp`
253
- * - changeLog.generateSessionSummary(sessionId) returns string
254
- * - changeLog.recordChange(change) records a FileChange
251
+ * - changeLog.getLatestSession() — returns ChangeSession | null
252
+ * - changeLog.queryChanges(options) — options has `since` (timestamp), not `afterTimestamp`
253
+ * - changeLog.generateSessionSummary(sessionId) — returns string
254
+ * - changeLog.recordChange(change) — records a FileChange
255
255
  */
256
256
  function createChangeAdapter(diffEngine, changeLog, graph) {
257
257
  return {
@@ -383,13 +383,13 @@ function createChangeAdapter(diffEngine, changeLog, graph) {
383
383
  * DecisionLog, and SessionMemory.
384
384
  *
385
385
  * Key API mappings:
386
- * - persistentMemory.queryMemories(query) query uses MemoryQuery shape
387
- * - persistentMemory.createMemory(input) input is CreateMemoryInput
388
- * - persistentMemory.getStats() returns MemoryStats
389
- * - decisionLog.queryDecisions(query) query uses DecisionQuery
390
- * - decisionLog.createDecision(input) returns { decision, conflicts }
391
- * - decisionLog.getActiveDecisions() returns Decision[]
392
- * - sessionMemory.listRecentSessions(limit) returns SessionListItem[]
386
+ * - persistentMemory.queryMemories(query) — query uses MemoryQuery shape
387
+ * - persistentMemory.createMemory(input) — input is CreateMemoryInput
388
+ * - persistentMemory.getStats() — returns MemoryStats
389
+ * - decisionLog.queryDecisions(query) — query uses DecisionQuery
390
+ * - decisionLog.createDecision(input) — returns { decision, conflicts }
391
+ * - decisionLog.getActiveDecisions() — returns Decision[]
392
+ * - sessionMemory.listRecentSessions(limit) — returns SessionListItem[]
393
393
  */
394
394
  function createMemoryAdapter(persistentMemory, decisionLog, sessionMemory) {
395
395
  return {
@@ -423,7 +423,7 @@ function createMemoryAdapter(persistentMemory, decisionLog, sessionMemory) {
423
423
  if (!params.status || params.status === 'active') {
424
424
  return decisionLog.getActiveDecisions();
425
425
  }
426
- // 'all' query with no filters
426
+ // 'all' — query with no filters
427
427
  return decisionLog.queryDecisions({});
428
428
  },
429
429
  decide: (params) => {
@@ -492,8 +492,8 @@ function createSessionAdapter(sessionMemory) {
492
492
  * Creates an adapter that satisfies IContextEngine.
493
493
  *
494
494
  * Key API mappings:
495
- * - compress(text, level, contentType) module-level function from compressor.ts
496
- * - buildContextPackage(...) module-level function from progressive-disclosure.ts
495
+ * - compress(text, level, contentType) — module-level function from compressor.ts
496
+ * - buildContextPackage(...) — module-level function from progressive-disclosure.ts
497
497
  */
498
498
  function createContextAdapter(graph, persistentMemory, decisionLog, changeLog, config) {
499
499
  return {
@@ -534,7 +534,7 @@ function createContextAdapter(graph, persistentMemory, decisionLog, changeLog, c
534
534
  tier2Data.graphNodes = graphNodes;
535
535
  }
536
536
  // Build context package
537
- const pkg = buildContextPackage(projectInfo, tier2Data, {}, // Tier 3 data empty for initial load
537
+ const pkg = buildContextPackage(projectInfo, tier2Data, {}, // Tier 3 data — empty for initial load
538
538
  config.tokenBudgets, params.taskDescription);
539
539
  return pkg;
540
540
  }
@@ -568,12 +568,12 @@ function createContextAdapter(graph, persistentMemory, decisionLog, changeLog, c
568
568
  * Creates an adapter that satisfies IIndexer.
569
569
  *
570
570
  * Key API mappings:
571
- * - indexer.fullIndex(onProgress?) returns Promise<IndexStats>
571
+ * - indexer.fullIndex(onProgress?) — returns Promise<IndexStats>
572
572
  * IndexStats has: filesScanned, filesParsed, filesSkipped, filesDeleted,
573
573
  * nodesCreated, edgesCreated, parseErrors, durationMs, languages
574
- * - graph.getStats() returns { totalNodes, totalEdges, totalFiles, nodesByType, edgesByType, languageBreakdown }
575
- * - persistentMemory.getStats() returns MemoryStats
576
- * - changeLog.getStats(topN?) returns ChangeLogStats
574
+ * - graph.getStats() — returns { totalNodes, totalEdges, totalFiles, nodesByType, edgesByType, languageBreakdown }
575
+ * - persistentMemory.getStats() — returns MemoryStats
576
+ * - changeLog.getStats(topN?) — returns ChangeLogStats
577
577
  */
578
578
  function createIndexerAdapter(indexer, graph, persistentMemory, decisionLog, changeLog, config, watcher) {
579
579
  return {
@@ -609,13 +609,13 @@ function createIndexerAdapter(indexer, graph, persistentMemory, decisionLog, cha
609
609
  try {
610
610
  // Re-target the indexer to the new project
611
611
  indexer.setProjectRoot(resolvedPath);
612
- log('info', `📁 Re-targeted to project: ${resolvedPath}`);
612
+ log('info', `📁 Re-targeted to project: ${resolvedPath}`);
613
613
  // Also watch the new project directory for changes
614
614
  if (watcher) {
615
615
  watcher.addRoot(resolvedPath);
616
- log('info', `👁️ File watcher now also watching: ${resolvedPath}`);
616
+ log('info', `👁️ File watcher now also watching: ${resolvedPath}`);
617
617
  }
618
- // Run full index on the new project (don't clear multi-project)
618
+ // Run full index on the new project (don't clear — multi-project)
619
619
  const result = await indexer.fullIndex();
620
620
  return {
621
621
  filesScanned: result.filesScanned,
@@ -715,6 +715,20 @@ class SessionTokenTracker {
715
715
  };
716
716
  }
717
717
  }
718
+ /**
719
+ * Directories that should NEVER be indexed — IDE install dirs, tool dirs, etc.
720
+ */
721
+ const BLOCKED_DIRECTORY_PATTERNS = [
722
+ 'antigravity', '.gemini', '.cursor', '.vscode-server',
723
+ 'program files', 'programdata', 'appdata',
724
+ 'node_modules', '.npm', '.yarn',
725
+ 'system32', 'windows',
726
+ ];
727
+ /** Check if a path is a known IDE/tool directory that should never be indexed */
728
+ function isBlockedDirectory(dirPath) {
729
+ const lower = dirPath.toLowerCase().replace(/\\/g, '/');
730
+ return BLOCKED_DIRECTORY_PATTERNS.some(pattern => lower.includes(pattern));
731
+ }
718
732
  /**
719
733
  * Intercept an MCP tool response to inject session token metadata.
720
734
  *
@@ -732,23 +746,20 @@ function enrichToolResponse(response, tracker, estimator, graphNodeCount, getPro
732
746
  const tokensSaved = result.tokensSaved ?? 0;
733
747
  // Record in tracker
734
748
  tracker.record(0, outputTokens, tokensSaved);
735
- // Always add session token metadata
736
- result._sessionTokens = tracker.getSummary();
737
749
  // Always add project metadata
738
- result._project = getProjectInfo();
739
- // If result is empty/failed AND graph has no nodes, add auto-index hint
750
+ const projectInfo = getProjectInfo();
751
+ result._project = projectInfo;
752
+ // ALWAYS tell the agent to provide project path if no real project is indexed
740
753
  const nodeCount = graphNodeCount();
741
- if (nodeCount === 0) {
742
- const isEmpty = result.success === true && (result.data === null ||
743
- (Array.isArray(result.data) && result.data.length === 0) ||
744
- (typeof result.data === 'object' && result.data !== null && Object.keys(result.data).length === 0));
745
- const isFail = result.success === false;
746
- if (isEmpty || isFail) {
747
- result._hint = '⚠️ No codebase index found. Call mindmap_reindex with the projectPath parameter ' +
748
- 'set to the root directory of the user\'s project (e.g. "E:\\\\myproject"). ' +
749
- 'This is a one-time operation that takes ~10-30 seconds.';
750
- }
754
+ const rootIsBlocked = isBlockedDirectory(projectInfo.root);
755
+ if (nodeCount === 0 || rootIsBlocked) {
756
+ result._indexRequired = true;
757
+ result._action = 'REQUIRED: Call mindmap_reindex({ projectPath: "<USER_WORKSPACE_PATH>" }). ' +
758
+ 'The current root "' + projectInfo.root + '" is NOT a user project. ' +
759
+ 'Use the workspace/project directory that the user has open in their editor.';
751
760
  }
761
+ // Always add session token metadata
762
+ result._sessionTokens = tracker.getSummary();
752
763
  response.content[0].text = JSON.stringify(result);
753
764
  }
754
765
  catch {
@@ -760,7 +771,7 @@ function enrichToolResponse(response, tracker, estimator, graphNodeCount, getPro
760
771
  // Main entry point
761
772
  // ============================================================
762
773
  async function main() {
763
- // ── 1. Parse CLI & load config ──────────────────────────────
774
+ // ── 1. Parse CLI & load config ──────────────────────────────
764
775
  let cliArgs;
765
776
  try {
766
777
  cliArgs = parseCliArgs();
@@ -771,7 +782,7 @@ async function main() {
771
782
  process.exit(1);
772
783
  }
773
784
  setLogLevel(cliArgs.logLevel);
774
- log('info', '🧠 AI Mind Map MCP Server starting');
785
+ log('info', '🧠 AI Mind Map MCP Server starting…');
775
786
  let config;
776
787
  try {
777
788
  config = await loadConfig(cliArgs);
@@ -784,7 +795,7 @@ async function main() {
784
795
  log('error', `Failed to load configuration: ${msg}`);
785
796
  process.exit(1);
786
797
  }
787
- // ── 2. Initialise database directory ────────────────────────
798
+ // ── 2. Initialise database directory ────────────────────────
788
799
  try {
789
800
  ensureDbDirectory(config.dbPath);
790
801
  }
@@ -793,11 +804,11 @@ async function main() {
793
804
  log('error', `Failed to create DB directory: ${msg}`);
794
805
  process.exit(1);
795
806
  }
796
- // ── 3. Initialise SQLite database ──────────────────────────
807
+ // ── 3. Initialise SQLite database ──────────────────────────
797
808
  // KnowledgeGraph manages its own db connection, but ChangeLog,
798
809
  // SessionMemory, PersistentMemory, and DecisionLog each need
799
810
  // a shared Database instance for their tables.
800
- log('info', 'Initialising database');
811
+ log('info', 'Initialising database…');
801
812
  let sharedDb;
802
813
  try {
803
814
  sharedDb = new Database(config.dbPath);
@@ -811,24 +822,24 @@ async function main() {
811
822
  log('error', `Failed to initialize database: ${msg}`);
812
823
  process.exit(1);
813
824
  }
814
- // ── 4. Initialise real subsystems ──────────────────────────
815
- log('info', 'Initialising subsystems');
816
- // Knowledge Graph constructor takes dbPath string
825
+ // ── 4. Initialise real subsystems ──────────────────────────
826
+ log('info', 'Initialising subsystems…');
827
+ // Knowledge Graph — constructor takes dbPath string
817
828
  const graph = new KnowledgeGraph(config.dbPath);
818
- // Indexer constructor is Indexer(graph, config)
829
+ // Indexer — constructor is Indexer(graph, config)
819
830
  const indexer = new Indexer(graph, config);
820
- // PageRankEngine constructor is PageRankEngine(graph, config?)
831
+ // PageRankEngine — constructor is PageRankEngine(graph, config?)
821
832
  const pagerank = new PageRankEngine(graph);
822
- // Changelog Engine node-level change tracking (v1.4.0)
833
+ // Changelog Engine — node-level change tracking (v1.4.0)
823
834
  const changelogEngine = new ChangelogEngine(graph.getDb());
824
835
  indexer.setChangelog(changelogEngine);
825
- log('info', ' Knowledge Graph initialized (with changelog engine)');
836
+ log('info', '✅ Knowledge Graph initialized (with changelog engine)');
826
837
  // Change Tracker
827
838
  // ChangeLog constructor takes ChangeLogConfig: { dbPath, retentionDays?, defaultSearchLimit? }
828
839
  const changeLog = new ChangeLog({ dbPath: config.dbPath });
829
840
  const diffEngine = new DiffEngine(config.projectRoot);
830
841
  let watcher = null;
831
- // SessionMemory must be created before watcher so it's available in the handler
842
+ // SessionMemory — must be created before watcher so it's available in the handler
832
843
  // SessionMemory constructor takes Database.Database instance
833
844
  const sessionMemory = new SessionMemory(sharedDb);
834
845
  const sessionId = sessionMemory.startSession();
@@ -841,6 +852,8 @@ async function main() {
841
852
  watchDebounceMs: config.watchDebounceMs,
842
853
  maxFileSize: config.maxFileSize,
843
854
  });
855
+ // Wire watcher to indexer so fullIndex() can pause/resume it
856
+ indexer.setWatcher(watcher);
844
857
  // FileWatcher emits 'changes' with WatcherEvent[]
845
858
  watcher.on('changes', async (events) => {
846
859
  log('debug', `File watcher detected ${events.length} changes`);
@@ -872,7 +885,7 @@ async function main() {
872
885
  pagerank.invalidateCache();
873
886
  });
874
887
  }
875
- log('info', `✅ Change Tracker initialized (watcher: ${config.watchEnabled ? 'enabled' : 'disabled'})`);
888
+ log('info', `✅ Change Tracker initialized (watcher: ${config.watchEnabled ? 'enabled' : 'disabled'})`);
876
889
  // Memory
877
890
  // PersistentMemory constructor: (db, config?) where config is Pick<MindMapConfig['memory'], 'decayRate' | 'maxMemories' | 'importanceThreshold'>
878
891
  const persistentMemory = new PersistentMemory(sharedDb, {
@@ -884,7 +897,7 @@ async function main() {
884
897
  const decisionLog = new DecisionLog(sharedDb, {
885
898
  maxDecisions: config.memory.maxDecisions,
886
899
  });
887
- log('info', `✅ Memory initialized (session: ${sessionMemory.getCurrentSessionId()})`);
900
+ log('info', `✅ Memory initialized (session: ${sessionMemory.getCurrentSessionId()})`);
888
901
  // Apply memory decay on startup
889
902
  try {
890
903
  const decayed = persistentMemory.applyDecay();
@@ -893,9 +906,9 @@ async function main() {
893
906
  catch (err) {
894
907
  log('warn', 'Failed to apply memory decay at startup', err);
895
908
  }
896
- // Context Engine no class instances needed; uses module-level functions
897
- log('info', ' Context Engine initialized');
898
- // ── 5. Build adapters ──────────────────────────────────────
909
+ // Context Engine — no class instances needed; uses module-level functions
910
+ log('info', '✅ Context Engine initialized');
911
+ // ── 5. Build adapters ──────────────────────────────────────
899
912
  const graphAdapter = createGraphAdapter(graph, pagerank);
900
913
  const changeAdapter = createChangeAdapter(diffEngine, changeLog, graph);
901
914
  const memoryAdapter = createMemoryAdapter(persistentMemory, decisionLog, sessionMemory);
@@ -906,25 +919,38 @@ async function main() {
906
919
  const tokenEstimator = {
907
920
  estimate: (text) => estimateTokens(text),
908
921
  };
909
- // ── 6. Create MCP server ──────────────────────────────────
922
+ // ── 6. Create MCP server ──────────────────────────────────
910
923
  const server = new McpServer({
911
924
  name: 'ai-mind-map',
912
- version: '1.6.1',
925
+ version: '1.7.0',
913
926
  }, {
914
927
  instructions: [
915
928
  '# AI Mind Map — Code Memory Engine',
916
929
  '',
917
- 'You have access to AI Mind Map, a persistent code memory system that saves you from re-reading files and losing context between sessions.',
930
+ '## CRITICAL: FIRST STEP IN EVERY CONVERSATION',
931
+ 'Before calling ANY other tool, you MUST ensure a project is indexed:',
932
+ '1. Check if `_indexRequired: true` appears in any response — if so, the index is empty or pointing at an IDE directory.',
933
+ '2. Call `mindmap_reindex({ projectPath: "<WORKSPACE>" })` where <WORKSPACE> is the user project directory from their open editor.',
934
+ '3. NEVER use paths containing: .gemini, antigravity, .cursor, .vscode, AppData, Program Files — these are IDE directories, NOT projects.',
935
+ '4. Example: `mindmap_reindex({ projectPath: "E:\\myproject" })`',
918
936
  '',
919
- '## 🚀 FIRST CALL (every new conversation):',
920
- 'Call `mindmap_session_resume` FIRST. It returns:',
937
+ '## After Indexing - Resume Session:',
938
+ 'Call `mindmap_session_resume`. It returns:',
921
939
  '- What the previous AI worked on',
922
940
  '- What code changed since then (function-level diffs)',
923
941
  '- Project structure + tech stack',
924
942
  '- Hot files (most frequently changed)',
925
- 'This ONE call replaces reading 10+ files.',
926
943
  '',
927
- '## 📋 Tool Selection Guide:',
944
+ '## Project Indexing:',
945
+ 'Every response includes `_project.root` and `_project.totalNodes`.',
946
+ 'If `_indexRequired: true` appears, you MUST call `mindmap_reindex` with the correct project path.',
947
+ 'NEVER index directories containing: antigravity, .gemini, .cursor, .vscode, AppData, Program Files.',
948
+ 'The graph supports multiple projects — call `mindmap_reindex` with different paths to switch.',
949
+ '',
950
+ '## Token Tracking:',
951
+ 'Every response includes `_sessionTokens` with cumulative usage.',
952
+ '',
953
+ '## Tool Selection Guide:',
928
954
  '',
929
955
  '### When you need to UNDERSTAND the project:',
930
956
  '- `mindmap_digest` → Full project summary in <2000 tokens',
@@ -938,48 +964,47 @@ async function main() {
938
964
  '- `mindmap_trace_dependencies` → Who calls X? What does X call?',
939
965
  '',
940
966
  '### When you need to READ code:',
941
- '- `mindmap_explain` Get EVERYTHING about a symbol: signature, callers, callees, doc',
942
- '- `mindmap_get_code_snippet` Read actual source code for a function/class',
943
- '- `mindmap_get_file_map` → All symbols in a file with signatures + line ranges',
967
+ '- `mindmap_get_code_snippet` → Read actual source code for a function/class',
968
+ '- `mindmap_get_file_map` → All symbols in a file with signatures + line ranges',
944
969
  '',
945
970
  '### When you need to know WHAT CHANGED:',
946
- '- `mindmap_changelog` Symbol-level diffs (added/modified/deleted functions)',
947
- '- `mindmap_git_changes` Git-aware changes with symbol mapping',
948
- '- `mindmap_verify` Check if your cached knowledge is still valid',
949
- '- `mindmap_hotspots` Most frequently changed files + symbols',
971
+ '- `mindmap_changelog` → Symbol-level diffs (added/modified/deleted functions)',
972
+ '- `mindmap_git_changes` → Git-aware changes with symbol mapping',
973
+ '- `mindmap_verify` → Check if your cached knowledge is still valid',
974
+ '- `mindmap_hotspots` → Most frequently changed files + symbols',
950
975
  '',
951
976
  '### When you need to REMEMBER:',
952
- '- `mindmap_remember` Save a fact/convention for future sessions',
953
- '- `mindmap_recall` Retrieve relevant memories for current task',
954
- '- `mindmap_decide` Record architectural decisions with rationale',
977
+ '- `mindmap_remember` → Save a fact/convention for future sessions',
978
+ '- `mindmap_recall` → Retrieve relevant memories for current task',
979
+ '- `mindmap_decide` → Record architectural decisions with rationale',
955
980
  '',
956
981
  '### When you finish work:',
957
- '- `mindmap_session_end` Save summary so next AI can resume instantly',
982
+ '- `mindmap_session_end` → Save summary so next AI can resume instantly',
958
983
  '',
959
984
  '### After editing code:',
960
- '- `mindmap_verify_changes` Verify your edits at the symbol level WITHOUT re-reading files',
985
+ '- `mindmap_verify_changes` → Verify your edits at the symbol level WITHOUT re-reading files',
961
986
  '',
962
- '## Token-Saving Rules:',
963
- '1. ALWAYS call `mindmap_session_resume` first never start blind',
964
- '2. Use `mindmap_file_digest` BEFORE reading a full file you may not need the full file',
965
- '3. Use `mindmap_verify_changes` after editing to verify changes do NOT re-read whole files',
987
+ '## âš¡ Token-Saving Rules:',
988
+ '1. ALWAYS call `mindmap_session_resume` first — never start blind',
989
+ '2. Use `mindmap_file_digest` BEFORE reading a full file — you may not need the full file',
990
+ '3. Use `mindmap_verify_changes` after editing to verify changes — do NOT re-read whole files',
966
991
  '4. Use `mindmap_changelog` instead of re-reading files to see what changed',
967
- '5. Call `mindmap_session_end` when done save context for next session',
992
+ '5. Call `mindmap_session_end` when done — save context for next session',
968
993
  '',
969
- '## 🔄 Project Indexing:',
994
+ '## 🔄 Project Indexing:',
970
995
  'Every response includes `_project.root` showing which directory is currently indexed.',
971
996
  'If `_project.totalNodes` is 0, you MUST call `mindmap_reindex` with `projectPath` set to',
972
997
  'the user\'s project directory (e.g. their workspace root). This creates the knowledge graph.',
973
998
  'Example: `mindmap_reindex({ projectPath: "/home/user/my-project" })`',
974
- 'You can also reindex to switch between projects the graph supports multiple projects.',
999
+ 'You can also reindex to switch between projects — the graph supports multiple projects.',
975
1000
  '',
976
- '## 📊 Token Tracking:',
1001
+ '## 📊 Token Tracking:',
977
1002
  'Every response includes `_sessionTokens` with cumulative usage.',
978
1003
  ].join('\n'),
979
1004
  });
980
- // ── 7. Register all tools ─────────────────────────────────
981
- log('info', 'Registering MCP tools');
982
- // ── 7.0 Token tracking middleware ──────────────────────────
1005
+ // ── 7. Register all tools ─────────────────────────────────
1006
+ log('info', 'Registering MCP tools…');
1007
+ // ── 7.0 Token tracking middleware ──────────────────────────
983
1008
  // Wrap every tool handler to inject session token metadata
984
1009
  const tokenTracker = new SessionTokenTracker();
985
1010
  const originalToolFn = server.tool.bind(server);
@@ -1036,7 +1061,7 @@ async function main() {
1036
1061
  log('debug', 'Registered session tools (5)');
1037
1062
  registerDigestTools(server, graph, changelogEngine, config, tokenEstimator);
1038
1063
  log('debug', 'Registered digest tools (3)');
1039
- // ── mindmap_sync_shared_context ─────────────────────────────
1064
+ // ── mindmap_sync_shared_context ─────────────────────────────
1040
1065
  server.tool('mindmap_sync_shared_context', 'Synchronise local memories, decisions, and learned rules with the team-shared `.mindmap-shared.json` file. ' +
1041
1066
  'Performs a bidirectional sync to import new conventions/decisions and export local updates.', {}, async () => {
1042
1067
  try {
@@ -1070,21 +1095,21 @@ async function main() {
1070
1095
  }
1071
1096
  });
1072
1097
  log('debug', 'Registered shared context sync tool');
1073
- log('info', '🔧 All MCP tools registered:');
1098
+ log('info', '🔧 All MCP tools registered:');
1074
1099
  log('info', ' Graph: mindmap_search, mindmap_get_structure, mindmap_trace_dependencies, mindmap_get_signature, mindmap_find_references, mindmap_get_file_map');
1075
1100
  log('info', ' Changes: mindmap_what_changed, mindmap_session_diff, mindmap_impact_analysis');
1076
1101
  log('info', ' Memory: mindmap_recall, mindmap_remember, mindmap_get_decisions, mindmap_decide, mindmap_session_summary, mindmap_sync_shared_context');
1077
1102
  log('info', ' Context: mindmap_get_context, mindmap_compress, mindmap_reindex, mindmap_status');
1078
1103
  log('info', ' Debug: mindmap_debug_changes, mindmap_file_before, mindmap_file_history');
1079
1104
  log('info', ' Flow: mindmap_trace_flow, mindmap_interaction_map, mindmap_classify_file, mindmap_layer_overview');
1080
- log('info', ' Snapshot: mindmap_project_map, mindmap_change_delta, mindmap_session_kickoff ');
1105
+ log('info', ' Snapshot: mindmap_project_map, mindmap_change_delta, mindmap_session_kickoff ⭐');
1081
1106
  log('info', ' Advanced: mindmap_query_graph, mindmap_dead_code, mindmap_architecture, mindmap_get_code_snippet, mindmap_search_code, mindmap_list_projects, mindmap_health');
1082
- log('info', ' Smart: mindmap_explain ⭐, mindmap_git_changes ⭐, mindmap_smart_search ');
1083
- log('info', ' Evolving: mindmap_teach ⭐, mindmap_get_learned, mindmap_forget');
1084
- log('info', ' Semantic: mindmap_semantic_search ⭐, mindmap_semantic_stats, mindmap_synonyms');
1085
- log('info', ' Session: mindmap_session_start 🆕, mindmap_session_resume 🔥🆕, mindmap_session_end, mindmap_changelog 🆕, mindmap_hotspots, mindmap_verify_changes 🆕');
1086
- log('info', ' Digest: mindmap_digest ⭐, mindmap_file_digest ⭐, mindmap_verify');
1087
- // ── 7.3 Register MCP Prompts ──────────────────────────────
1107
+ log('info', ' Smart: mindmap_explain ⭐, mindmap_git_changes ⭐, mindmap_smart_search ⭐');
1108
+ log('info', ' Evolving: mindmap_teach ⭐, mindmap_get_learned, mindmap_forget');
1109
+ log('info', ' Semantic: mindmap_semantic_search ⭐, mindmap_semantic_stats, mindmap_synonyms');
1110
+ log('info', ' Session: mindmap_session_start 🆕, mindmap_session_resume 🔥🆕, mindmap_session_end, mindmap_changelog 🆕, mindmap_hotspots, mindmap_verify_changes 🆕');
1111
+ log('info', ' Digest: mindmap_digest ⭐, mindmap_file_digest ⭐, mindmap_verify');
1112
+ // ── 7.3 Register MCP Prompts ──────────────────────────────
1088
1113
  // These are interactive workflow templates that AI agents can request
1089
1114
  server.prompt('start_session', 'Recommended first prompt for any AI coding session. Calls mindmap_session_resume and returns a complete project briefing.', async () => {
1090
1115
  // Auto-start session
@@ -1127,32 +1152,32 @@ async function main() {
1127
1152
  }],
1128
1153
  };
1129
1154
  });
1130
- server.prompt('tool_guide', 'Complete guide to all AI Mind Map tools when to use each one, organized by task.', async () => ({
1155
+ server.prompt('tool_guide', 'Complete guide to all AI Mind Map tools — when to use each one, organized by task.', async () => ({
1131
1156
  messages: [{
1132
1157
  role: 'user',
1133
1158
  content: {
1134
1159
  type: 'text',
1135
1160
  text: [
1136
- '# AI Mind Map Complete Tool Guide',
1161
+ '# AI Mind Map — Complete Tool Guide',
1137
1162
  '',
1138
- '## 🚀 Session Lifecycle (use these to avoid re-reading code)',
1163
+ '## 🚀 Session Lifecycle (use these to avoid re-reading code)',
1139
1164
  '| Tool | When to Use |',
1140
1165
  '|------|------------|',
1141
- '| `mindmap_session_resume` | **FIRST call every conversation** returns project context + changes |',
1166
+ '| `mindmap_session_resume` | **FIRST call every conversation** — returns project context + changes |',
1142
1167
  '| `mindmap_session_kickoff` | Full preamble: project map + change delta + memories in ONE call |',
1143
1168
  '| `mindmap_session_start` | Start tracking a new task (records agent name + task) |',
1144
1169
  '| `mindmap_session_end` | Save summary for next AI session |',
1145
1170
  '',
1146
- '## 🔍 Finding Code (instead of grep/reading files)',
1171
+ '## 🔍 Finding Code (instead of grep/reading files)',
1147
1172
  '| Tool | When to Use |',
1148
1173
  '|------|------------|',
1149
- '| `mindmap_smart_search` | Search by function/class name returns full context |',
1174
+ '| `mindmap_smart_search` | Search by function/class name — returns full context |',
1150
1175
  '| `mindmap_semantic_search` | Search by concept ("error handling", "auth") |',
1151
1176
  '| `mindmap_search_code` | Grep-like text search in code bodies |',
1152
1177
  '| `mindmap_find_references` | Find all usages of a symbol |',
1153
1178
  '| `mindmap_trace_dependencies` | Who calls X? What does X call? |',
1154
1179
  '',
1155
- '## 📖 Reading Code (without reading full files)',
1180
+ '## 📖 Reading Code (without reading full files)',
1156
1181
  '| Tool | When to Use |',
1157
1182
  '|------|------------|',
1158
1183
  '| `mindmap_explain` | Get EVERYTHING about a symbol in one call |',
@@ -1161,14 +1186,14 @@ async function main() {
1161
1186
  '| `mindmap_get_file_map` | All symbols in a file with signatures |',
1162
1187
  '| `mindmap_get_signature` | Just the signature (cheapest read) |',
1163
1188
  '',
1164
- '## 📊 Understanding the Project',
1189
+ '## 📊 Understanding the Project',
1165
1190
  '| Tool | When to Use |',
1166
1191
  '|------|------------|',
1167
1192
  '| `mindmap_digest` | Full project summary in <2000 tokens |',
1168
1193
  '| `mindmap_architecture` | Architecture layers + patterns |',
1169
1194
  '| `mindmap_project_map` | Complete project map |',
1170
1195
  '',
1171
- '## 🔄 Change Tracking',
1196
+ '## 🔄 Change Tracking',
1172
1197
  '| Tool | When to Use |',
1173
1198
  '|------|------------|',
1174
1199
  '| `mindmap_changelog` | Symbol-level diffs since a time |',
@@ -1176,7 +1201,7 @@ async function main() {
1176
1201
  '| `mindmap_verify` | Check if cached code is still valid |',
1177
1202
  '| `mindmap_hotspots` | Most frequently changed files |',
1178
1203
  '',
1179
- '## 🧠 Memory & Decisions',
1204
+ '## 🧠 Memory & Decisions',
1180
1205
  '| Tool | When to Use |',
1181
1206
  '|------|------------|',
1182
1207
  '| `mindmap_remember` | Save important facts for future |',
@@ -1188,22 +1213,22 @@ async function main() {
1188
1213
  }],
1189
1214
  }));
1190
1215
  log('debug', 'Registered 2 MCP prompts (start_session, tool_guide)');
1191
- // ── 7.5 Auto-sync shared context on startup ────────────────
1216
+ // ── 7.5 Auto-sync shared context on startup ────────────────
1192
1217
  if (config.autoSyncSharedContext) {
1193
- log('info', '🔄 Auto-syncing shared context');
1218
+ log('info', '🔄 Auto-syncing shared context…');
1194
1219
  try {
1195
1220
  const syncStats = await syncSharedContext(config, graph, persistentMemory, decisionLog);
1196
- log('info', `✅ Shared context sync complete: ` +
1221
+ log('info', `✅ Shared context sync complete: ` +
1197
1222
  `Imported: ${syncStats.memoriesImported} memories, ${syncStats.decisionsImported} decisions, ${syncStats.rulesImported} rules. ` +
1198
1223
  `Exported: ${syncStats.memoriesExported} memories, ${syncStats.decisionsExported} decisions, ${syncStats.rulesExported} rules.`);
1199
1224
  }
1200
1225
  catch (err) {
1201
- log('warn', `⚠️ Auto-sync of shared context failed: ${err instanceof Error ? err.message : String(err)}`);
1226
+ log('warn', `⚠️ Auto-sync of shared context failed: ${err instanceof Error ? err.message : String(err)}`);
1202
1227
  }
1203
1228
  }
1204
- // ── 8. Smart auto-index (only if projectRoot looks like a real project) ──
1229
+ // ── 8. Smart auto-index (only if projectRoot looks like a real project) ──
1205
1230
  if (config.memoryOnly) {
1206
- log('info', '🧠 Running in memoryOnly mode. Bypassing codebase parsing and indexing.');
1231
+ log('info', '🧠 Running in memoryOnly mode. Bypassing codebase parsing and indexing.');
1207
1232
  }
1208
1233
  else {
1209
1234
  // Check if projectRoot is a real project (not an IDE install directory)
@@ -1214,33 +1239,33 @@ async function main() {
1214
1239
  'pubspec.yaml', 'Gemfile', '*.sln', '*.csproj',
1215
1240
  ];
1216
1241
  const isRealProject = projectMarkers.some(marker => existsSync(path.join(config.projectRoot, marker)));
1217
- if (!isRealProject) {
1218
- log('info', `⚠️ Project root "${config.projectRoot}" does not look like a code project (no .git, package.json, etc.).`);
1242
+ if (!isRealProject || isBlockedDirectory(config.projectRoot)) {
1243
+ log('info', `⚠️ Project root "${config.projectRoot}" does not look like a code project (no .git, package.json, etc.).`);
1219
1244
  log('info', ' Skipping auto-index. The AI agent will be prompted to call mindmap_reindex with the correct project path.');
1220
1245
  }
1221
1246
  else {
1222
1247
  const stats = graph.getStats();
1223
1248
  if (stats.totalNodes === 0) {
1224
- log('info', `📋 Real project detected at: ${config.projectRoot}. Running initial indexing…`);
1249
+ log('info', `📋 Real project detected at: ${config.projectRoot}. Running initial indexing…`);
1225
1250
  try {
1226
1251
  const result = await indexer.fullIndex();
1227
- log('info', `✅ Initial index complete: ${result.filesParsed} files, ${result.nodesCreated} nodes, ${result.edgesCreated} edges`);
1252
+ log('info', `✅ Initial index complete: ${result.filesParsed} files, ${result.nodesCreated} nodes, ${result.edgesCreated} edges`);
1228
1253
  if (result.parseErrors > 0) {
1229
- log('warn', `⚠️ ${result.parseErrors} parse errors (non-fatal)`);
1254
+ log('warn', `⚠️ ${result.parseErrors} parse errors (non-fatal)`);
1230
1255
  }
1231
1256
  }
1232
1257
  catch (err) {
1233
- log('warn', `⚠️ Initial indexing failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
1258
+ log('warn', `⚠️ Initial indexing failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
1234
1259
  }
1235
1260
  }
1236
1261
  else {
1237
- log('info', `📋 Existing index found: ${stats.totalNodes} nodes. Running incremental update…`);
1262
+ log('info', `📋 Existing index found: ${stats.totalNodes} nodes. Running incremental update…`);
1238
1263
  try {
1239
1264
  const result = await indexer.incrementalIndex();
1240
- log('info', `✅ Incremental update: ${result.filesParsed} files reindexed`);
1265
+ log('info', `✅ Incremental update: ${result.filesParsed} files reindexed`);
1241
1266
  }
1242
1267
  catch (err) {
1243
- log('warn', `⚠️ Incremental update failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
1268
+ log('warn', `⚠️ Incremental update failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
1244
1269
  }
1245
1270
  }
1246
1271
  }
@@ -1258,30 +1283,30 @@ async function main() {
1258
1283
  filePath: n.filePath,
1259
1284
  })));
1260
1285
  semanticEngine.rebuildIDF();
1261
- log('info', `🧠 Semantic index built: ${nonFileNodes.length} symbols indexed`);
1286
+ log('info', `🧠 Semantic index built: ${nonFileNodes.length} symbols indexed`);
1262
1287
  }
1263
1288
  }
1264
1289
  catch (err) {
1265
- log('warn', `⚠️ Semantic index build failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
1290
+ log('warn', `⚠️ Semantic index build failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
1266
1291
  }
1267
1292
  }
1268
- // ── 9. Start file watcher ──────────────────────────────────
1293
+ // ── 9. Start file watcher ──────────────────────────────────
1269
1294
  if (watcher && !config.memoryOnly) {
1270
1295
  try {
1271
1296
  await watcher.start();
1272
- log('info', '👁️ File watcher started');
1297
+ log('info', '👁️ File watcher started');
1273
1298
  }
1274
1299
  catch (err) {
1275
- log('warn', `⚠️ File watcher failed to start: ${err instanceof Error ? err.message : String(err)}`);
1300
+ log('warn', `⚠️ File watcher failed to start: ${err instanceof Error ? err.message : String(err)}`);
1276
1301
  }
1277
1302
  }
1278
- // ── 10. Graceful shutdown ──────────────────────────────────
1303
+ // ── 10. Graceful shutdown ──────────────────────────────────
1279
1304
  let shuttingDown = false;
1280
1305
  async function shutdown(signal) {
1281
1306
  if (shuttingDown)
1282
1307
  return;
1283
1308
  shuttingDown = true;
1284
- log('info', `Received ${signal}, shutting down gracefully…`);
1309
+ log('info', `Received ${signal}, shutting down gracefully…`);
1285
1310
  try {
1286
1311
  // Stop file watcher
1287
1312
  if (watcher) {
@@ -1328,7 +1353,7 @@ async function main() {
1328
1353
  catch {
1329
1354
  // Ignore close errors
1330
1355
  }
1331
- log('info', ' Cleanup complete. Goodbye!');
1356
+ log('info', '✅ Cleanup complete. Goodbye!');
1332
1357
  }
1333
1358
  catch (err) {
1334
1359
  const msg = err instanceof Error ? err.message : String(err);
@@ -1347,12 +1372,12 @@ async function main() {
1347
1372
  const msg = reason instanceof Error ? reason.message : String(reason);
1348
1373
  log('error', `Unhandled rejection: ${msg}`);
1349
1374
  });
1350
- // ── 11. Connect transport and start serving ────────────────
1351
- log('info', 'Connecting stdio transport');
1375
+ // ── 11. Connect transport and start serving ────────────────
1376
+ log('info', 'Connecting stdio transport…');
1352
1377
  try {
1353
1378
  const transport = new StdioServerTransport();
1354
1379
  await server.connect(transport);
1355
- log('info', '🧠 AI Mind Map MCP Server is LIVE. Waiting for requests');
1380
+ log('info', '🧠 AI Mind Map MCP Server is LIVE. Waiting for requests…');
1356
1381
  log('info', ` Project: ${config.projectRoot}`);
1357
1382
  log('info', ` Database: ${config.dbPath}`);
1358
1383
  log('info', ` Session: ${sessionMemory.getCurrentSessionId()}`);
@@ -1363,7 +1388,7 @@ async function main() {
1363
1388
  process.exit(1);
1364
1389
  }
1365
1390
  }
1366
- // ── Kick off ────────────────────────────────────────────────
1391
+ // ── Kick off ────────────────────────────────────────────────
1367
1392
  main().catch((err) => {
1368
1393
  const msg = err instanceof Error ? err.message : String(err);
1369
1394
  process.stderr.write(`Fatal: ${msg}\n`);