ai-mind-map 1.4.1 → 1.4.3

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.
package/dist/install.js CHANGED
@@ -164,7 +164,7 @@ function mcpServerEntry(serverEntry) {
164
164
  return {
165
165
  'ai-mind-map': {
166
166
  command: 'npx',
167
- args: ['-y', 'ai-mind-map'],
167
+ args: ['-y', NPM_PACKAGE_NAME],
168
168
  env: {},
169
169
  },
170
170
  };
@@ -198,6 +198,337 @@ function writeJsonFile(filePath, data) {
198
198
  }
199
199
  writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
200
200
  }
201
+ // ============================================================
202
+ // Robustness: Dynamic Config Path Detection
203
+ // ============================================================
204
+ /** The npm package name — single source of truth to prevent wrong-name bugs */
205
+ const NPM_PACKAGE_NAME = 'ai-mind-map';
206
+ /**
207
+ * Dynamically find the correct MCP config file for Gemini/Antigravity.
208
+ * Scans candidate files and uses whichever already has mcpServers defined.
209
+ * Falls back to mcp_config.json (Gemini's primary config file).
210
+ */
211
+ function findGeminiMcpConfigPath() {
212
+ const configDir = path.join(HOME, '.gemini', 'config');
213
+ // Order matters: prefer mcp_config.json (Gemini's primary), then mcp.json
214
+ const candidates = ['mcp_config.json', 'mcp.json'];
215
+ for (const file of candidates) {
216
+ const filePath = path.join(configDir, file);
217
+ const content = readJsonFile(filePath);
218
+ if (content?.mcpServers && typeof content.mcpServers === 'object') {
219
+ return filePath;
220
+ }
221
+ }
222
+ // Default to mcp_config.json (confirmed working with Firebase/SpriteAI)
223
+ return path.join(configDir, 'mcp_config.json');
224
+ }
225
+ /**
226
+ * Dynamically find the MCP config file for VS Code / Copilot.
227
+ * VS Code may store MCP config in settings.json or a dedicated mcp.json.
228
+ */
229
+ function findVSCodeMcpConfigPath() {
230
+ const settingsDir = getVSCodeSettingsDir();
231
+ // Check for dedicated mcp.json first, then settings.json
232
+ const candidates = [
233
+ path.join(settingsDir, 'mcp.json'),
234
+ path.join(settingsDir, 'settings.json'),
235
+ ];
236
+ for (const filePath of candidates) {
237
+ const content = readJsonFile(filePath);
238
+ if (content?.['mcp.servers'] && typeof content['mcp.servers'] === 'object') {
239
+ return filePath;
240
+ }
241
+ }
242
+ return path.join(settingsDir, 'settings.json');
243
+ }
244
+ /**
245
+ * Verify that a config file was written correctly after install.
246
+ * Checks: file exists, entry exists under correct key, package name is correct.
247
+ */
248
+ function verifyConfigEntry(configPath, mcpKey) {
249
+ // 1. Can we read the file?
250
+ const config = readJsonFile(configPath);
251
+ if (!config) {
252
+ return { ok: false, error: `Config file not readable: ${configPath}` };
253
+ }
254
+ // 2. Does our key exist?
255
+ const servers = config[mcpKey];
256
+ if (!servers || !servers['ai-mind-map']) {
257
+ return { ok: false, error: `Entry 'ai-mind-map' not found under '${mcpKey}' in ${configPath}` };
258
+ }
259
+ // 3. Is the package name correct?
260
+ const entry = servers['ai-mind-map'];
261
+ const args = entry.args;
262
+ if (args && args.includes('ai-mind-map-server')) {
263
+ return { ok: false, error: `Wrong package name 'ai-mind-map-server' in args (should be '${NPM_PACKAGE_NAME}')` };
264
+ }
265
+ // 4. Is the command valid?
266
+ const command = entry.command;
267
+ if (!command || (command !== 'npx' && command !== 'node')) {
268
+ return { ok: false, error: `Invalid command '${command}' (expected 'npx' or 'node')` };
269
+ }
270
+ return { ok: true };
271
+ }
272
+ /**
273
+ * Auto-fix common config problems in a config file.
274
+ * Returns true if any fixes were applied.
275
+ */
276
+ function autoFixConfig(configPath, mcpKey) {
277
+ const config = readJsonFile(configPath);
278
+ if (!config)
279
+ return { ok: false, error: 'Cannot read config file' };
280
+ const servers = config[mcpKey];
281
+ if (!servers || !servers['ai-mind-map'])
282
+ return { ok: true }; // nothing to fix
283
+ const entry = servers['ai-mind-map'];
284
+ let fixed = false;
285
+ // Fix 1: Wrong package name
286
+ const args = entry.args;
287
+ if (args) {
288
+ const idx = args.indexOf('ai-mind-map-server');
289
+ if (idx !== -1) {
290
+ args[idx] = NPM_PACKAGE_NAME;
291
+ entry.args = args;
292
+ fixed = true;
293
+ }
294
+ }
295
+ // Fix 2: Add version metadata
296
+ if (!entry._version) {
297
+ entry._version = getPackageVersion();
298
+ entry._installedAt = new Date().toISOString();
299
+ fixed = true;
300
+ }
301
+ if (fixed) {
302
+ servers['ai-mind-map'] = entry;
303
+ config[mcpKey] = servers;
304
+ writeJsonFile(configPath, config);
305
+ }
306
+ return { ok: true, autoFixed: fixed };
307
+ }
308
+ /** Get the current package version */
309
+ function getPackageVersion() {
310
+ try {
311
+ const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
312
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
313
+ return pkg.version ?? '0.0.0';
314
+ }
315
+ catch {
316
+ return '0.0.0';
317
+ }
318
+ }
319
+ // ============================================================
320
+ // Robustness: Server Smoke Test
321
+ // ============================================================
322
+ /**
323
+ * Spawn the MCP server as a child process and verify it responds correctly.
324
+ * Returns diagnostic info about the server's health.
325
+ */
326
+ async function smokeTestServer() {
327
+ const result = { ok: false, serverStarts: false, toolCount: 0, hasInstructions: false, error: '' };
328
+ try {
329
+ const runConfig = getServerRunConfig();
330
+ let cmd;
331
+ let args;
332
+ if (runConfig.mode === 'npx') {
333
+ cmd = IS_WIN ? 'npx.cmd' : 'npx';
334
+ args = ['-y', NPM_PACKAGE_NAME];
335
+ }
336
+ else {
337
+ cmd = 'node';
338
+ args = [runConfig.path];
339
+ }
340
+ // We'll try to spawn the server with a timeout
341
+ const { spawn } = await import('node:child_process');
342
+ return new Promise((resolve) => {
343
+ const timeout = setTimeout(() => {
344
+ child.kill();
345
+ result.error = 'Server did not respond within 10 seconds';
346
+ resolve(result);
347
+ }, 10_000);
348
+ const child = spawn(cmd, args, {
349
+ stdio: ['pipe', 'pipe', 'pipe'],
350
+ env: { ...process.env, MINDMAP_PROJECT_ROOT: process.cwd() },
351
+ });
352
+ let stdout = '';
353
+ child.stdout.on('data', (data) => {
354
+ stdout += data.toString();
355
+ });
356
+ child.stderr.on('data', (data) => {
357
+ const line = data.toString();
358
+ // Server logs to stderr via MCP protocol
359
+ if (line.includes('MCP tools registered') || line.includes('tools registered')) {
360
+ result.serverStarts = true;
361
+ }
362
+ if (line.includes('instructions')) {
363
+ result.hasInstructions = true;
364
+ }
365
+ });
366
+ // Send initialize request via stdin (MCP JSON-RPC over stdio)
367
+ const initRequest = JSON.stringify({
368
+ jsonrpc: '2.0',
369
+ id: 1,
370
+ method: 'initialize',
371
+ params: {
372
+ protocolVersion: '2024-11-05',
373
+ capabilities: {},
374
+ clientInfo: { name: 'smoke-test', version: '1.0.0' },
375
+ },
376
+ });
377
+ child.stdin.write(initRequest + '\n');
378
+ // Give it a moment then send tools/list
379
+ setTimeout(() => {
380
+ const toolsRequest = JSON.stringify({
381
+ jsonrpc: '2.0',
382
+ id: 2,
383
+ method: 'tools/list',
384
+ params: {},
385
+ });
386
+ child.stdin.write(toolsRequest + '\n');
387
+ }, 2000);
388
+ // After 5 seconds, check what we got
389
+ setTimeout(() => {
390
+ clearTimeout(timeout);
391
+ child.kill();
392
+ // Try to parse responses from stdout
393
+ try {
394
+ const lines = stdout.split('\n').filter(l => l.trim());
395
+ for (const line of lines) {
396
+ try {
397
+ const msg = JSON.parse(line);
398
+ if (msg.id === 1 && msg.result) {
399
+ result.serverStarts = true;
400
+ if (msg.result.instructions) {
401
+ result.hasInstructions = true;
402
+ }
403
+ }
404
+ if (msg.id === 2 && msg.result?.tools) {
405
+ result.toolCount = msg.result.tools.length;
406
+ }
407
+ }
408
+ catch {
409
+ // skip unparseable lines
410
+ }
411
+ }
412
+ }
413
+ catch {
414
+ // parsing failed
415
+ }
416
+ result.ok = result.serverStarts;
417
+ resolve(result);
418
+ }, 5000);
419
+ child.on('error', (err) => {
420
+ clearTimeout(timeout);
421
+ result.error = `Failed to start: ${err.message}`;
422
+ resolve(result);
423
+ });
424
+ });
425
+ }
426
+ catch (err) {
427
+ result.error = `Smoke test error: ${err instanceof Error ? err.message : String(err)}`;
428
+ return result;
429
+ }
430
+ }
431
+ // ============================================================
432
+ // Robustness: Misconfig Detection & Migration
433
+ // ============================================================
434
+ /**
435
+ * Check for common misconfigurations and return diagnostics.
436
+ * Called by `doctor` to detect problems.
437
+ */
438
+ function detectMisconfigs() {
439
+ const issues = [];
440
+ // Check 1: Gemini config in wrong file (mcp.json instead of mcp_config.json)
441
+ const geminiWrongFile = path.join(HOME, '.gemini', 'config', 'mcp.json');
442
+ const geminiRightFile = path.join(HOME, '.gemini', 'config', 'mcp_config.json');
443
+ const wrongConfig = readJsonFile(geminiWrongFile);
444
+ if (wrongConfig?.mcpServers) {
445
+ const servers = wrongConfig.mcpServers;
446
+ if (servers['ai-mind-map']) {
447
+ const rightConfig = readJsonFile(geminiRightFile);
448
+ const rightServers = rightConfig?.mcpServers ?? {};
449
+ if (!rightServers['ai-mind-map']) {
450
+ issues.push({
451
+ agent: 'Antigravity (Gemini)',
452
+ problem: `Config in mcp.json but Gemini reads mcp_config.json`,
453
+ fix: 'Move entry to mcp_config.json',
454
+ fixFn: () => {
455
+ // Copy entry to correct file
456
+ const config = readJsonFile(geminiRightFile) ?? { mcpServers: {} };
457
+ const mcpServers = config.mcpServers ?? {};
458
+ mcpServers['ai-mind-map'] = servers['ai-mind-map'];
459
+ config.mcpServers = mcpServers;
460
+ writeJsonFile(geminiRightFile, config);
461
+ // Remove from wrong file
462
+ delete servers['ai-mind-map'];
463
+ if (Object.keys(servers).length === 0)
464
+ delete wrongConfig.mcpServers;
465
+ writeJsonFile(geminiWrongFile, wrongConfig);
466
+ },
467
+ });
468
+ }
469
+ }
470
+ }
471
+ // Check 2: Wrong package name in any config
472
+ const configsToCheck = [
473
+ { agent: 'Claude Code', path: path.join(HOME, '.claude', 'claude_desktop_config.json'), key: 'mcpServers' },
474
+ { agent: 'Cursor', path: path.join(HOME, '.cursor', 'mcp.json'), key: 'mcpServers' },
475
+ { agent: 'Antigravity', path: geminiRightFile, key: 'mcpServers' },
476
+ { agent: 'VS Code', path: path.join(getVSCodeSettingsDir(), 'settings.json'), key: 'mcp.servers' },
477
+ ];
478
+ for (const check of configsToCheck) {
479
+ const content = readJsonFile(check.path);
480
+ if (!content)
481
+ continue;
482
+ const servers = content[check.key];
483
+ if (!servers?.['ai-mind-map'])
484
+ continue;
485
+ const entry = servers['ai-mind-map'];
486
+ const args = entry.args;
487
+ if (args?.includes('ai-mind-map-server')) {
488
+ issues.push({
489
+ agent: check.agent,
490
+ problem: `Wrong package name 'ai-mind-map-server' in args`,
491
+ fix: `Change to '${NPM_PACKAGE_NAME}'`,
492
+ fixFn: () => {
493
+ const idx = args.indexOf('ai-mind-map-server');
494
+ if (idx !== -1)
495
+ args[idx] = NPM_PACKAGE_NAME;
496
+ entry.args = args;
497
+ servers['ai-mind-map'] = entry;
498
+ content[check.key] = servers;
499
+ writeJsonFile(check.path, content);
500
+ },
501
+ });
502
+ }
503
+ }
504
+ // Check 3: Outdated version metadata
505
+ for (const check of configsToCheck) {
506
+ const content = readJsonFile(check.path);
507
+ if (!content)
508
+ continue;
509
+ const servers = content[check.key];
510
+ if (!servers?.['ai-mind-map'])
511
+ continue;
512
+ const entry = servers['ai-mind-map'];
513
+ const configVersion = entry._version;
514
+ const currentVersion = getPackageVersion();
515
+ if (configVersion && configVersion !== currentVersion && currentVersion !== '0.0.0') {
516
+ issues.push({
517
+ agent: check.agent,
518
+ problem: `Config version ${configVersion} is outdated (current: ${currentVersion})`,
519
+ fix: 'Update version metadata',
520
+ fixFn: () => {
521
+ entry._version = currentVersion;
522
+ entry._installedAt = new Date().toISOString();
523
+ servers['ai-mind-map'] = entry;
524
+ content[check.key] = servers;
525
+ writeJsonFile(check.path, content);
526
+ },
527
+ });
528
+ }
529
+ }
530
+ return issues;
531
+ }
201
532
  /**
202
533
  * Merge an MCP server entry into an existing JSON config.
203
534
  * Creates the file if it doesn't exist.
@@ -218,7 +549,7 @@ function mergeMcpConfig(configPath, mcpKey, serverEntry) {
218
549
  if (runConfig.mode === 'npx') {
219
550
  servers['ai-mind-map'] = {
220
551
  command: 'npx',
221
- args: ['-y', 'ai-mind-map'],
552
+ args: ['-y', NPM_PACKAGE_NAME],
222
553
  env: {},
223
554
  };
224
555
  }
@@ -310,7 +641,7 @@ function getAgentDefinitions(serverEntry) {
310
641
  const mcpServers = existing['mcp.servers'] ?? {};
311
642
  const runConfig = getServerRunConfig();
312
643
  mcpServers['ai-mind-map'] = runConfig.mode === 'npx'
313
- ? { command: 'npx', args: ['-y', 'ai-mind-map'], env: {} }
644
+ ? { command: 'npx', args: ['-y', NPM_PACKAGE_NAME], env: {} }
314
645
  : { command: 'node', args: [serverEntry], env: {} };
315
646
  existing['mcp.servers'] = mcpServers;
316
647
  return JSON.stringify(existing, null, 2);
@@ -332,7 +663,7 @@ function getAgentDefinitions(serverEntry) {
332
663
  const mcpServers = existing['mcp.servers'] ?? {};
333
664
  const runConfig = getServerRunConfig();
334
665
  mcpServers['ai-mind-map'] = runConfig.mode === 'npx'
335
- ? { command: 'npx', args: ['-y', 'ai-mind-map'], env: {} }
666
+ ? { command: 'npx', args: ['-y', NPM_PACKAGE_NAME], env: {} }
336
667
  : { command: 'node', args: [serverEntry], env: {} };
337
668
  existing['mcp.servers'] = mcpServers;
338
669
  return JSON.stringify(existing, null, 2);
@@ -349,10 +680,10 @@ function getAgentDefinitions(serverEntry) {
349
680
  path.join(HOME, '.gemini', 'config'),
350
681
  path.join(HOME, '.gemini'),
351
682
  ],
352
- configPath: path.join(HOME, '.gemini', 'config', 'mcp_config.json'),
683
+ configPath: findGeminiMcpConfigPath(),
353
684
  generateConfig: () => JSON.stringify({ mcpServers: mcpServerEntry(serverEntry) }, null, 2),
354
- isConfigured: () => hasMcpConfig(path.join(HOME, '.gemini', 'config', 'mcp_config.json'), 'mcpServers'),
355
- removeConfig: () => removeMcpConfig(path.join(HOME, '.gemini', 'config', 'mcp_config.json'), 'mcpServers'),
685
+ isConfigured: () => hasMcpConfig(findGeminiMcpConfigPath(), 'mcpServers'),
686
+ removeConfig: () => removeMcpConfig(findGeminiMcpConfigPath(), 'mcpServers'),
356
687
  },
357
688
  // 6. Zed
358
689
  {
@@ -367,7 +698,7 @@ function getAgentDefinitions(serverEntry) {
367
698
  const contextServers = existing['context_servers'] ?? {};
368
699
  const runConfig = getServerRunConfig();
369
700
  const serverConfig = runConfig.mode === 'npx'
370
- ? { path: 'npx', args: ['-y', 'ai-mind-map'], env: {} }
701
+ ? { path: 'npx', args: ['-y', NPM_PACKAGE_NAME], env: {} }
371
702
  : { path: 'node', args: [serverEntry], env: {} };
372
703
  contextServers['ai-mind-map'] = {
373
704
  command: serverConfig,
@@ -416,7 +747,7 @@ function getAgentDefinitions(serverEntry) {
416
747
  if (!exists) {
417
748
  const runConfig = getServerRunConfig();
418
749
  const serverEntryConfig = runConfig.mode === 'npx'
419
- ? { command: 'npx', args: ['-y', 'ai-mind-map'], env: {} }
750
+ ? { command: 'npx', args: ['-y', NPM_PACKAGE_NAME], env: {} }
420
751
  : { command: 'node', args: [serverEntry], env: {} };
421
752
  modelContextProtocolServers.push({
422
753
  name: 'ai-mind-map',
@@ -478,6 +809,7 @@ export async function installAgents() {
478
809
  const agents = getAgentDefinitions(serverEntry);
479
810
  let foundCount = 0;
480
811
  let configuredCount = 0;
812
+ let verifyFailCount = 0;
481
813
  for (const agent of agents) {
482
814
  const detected = agent.probePaths.some((p) => existsSync(p));
483
815
  if (!detected) {
@@ -490,6 +822,16 @@ export async function installAgents() {
490
822
  // Check if already configured
491
823
  if (agent.isConfigured()) {
492
824
  logSkipped(agent.name, 'already configured');
825
+ // Still verify existing config is correct
826
+ const mcpKey = ['claude-code', 'cursor', 'antigravity'].includes(agent.id) ? 'mcpServers' : 'mcp.servers';
827
+ const verifyResult = verifyConfigEntry(agent.configPath, mcpKey);
828
+ if (!verifyResult.ok) {
829
+ console.log(` ${c.yellow}⚠${c.reset} ${c.dim}Verification issue: ${verifyResult.error}${c.reset}`);
830
+ const fixResult = autoFixConfig(agent.configPath, mcpKey);
831
+ if (fixResult.autoFixed) {
832
+ console.log(` ${c.green}✔${c.reset} ${c.dim}Auto-fixed!${c.reset}`);
833
+ }
834
+ }
493
835
  continue;
494
836
  }
495
837
  // Write the configuration
@@ -511,7 +853,25 @@ export async function installAgents() {
511
853
  const configContent = agent.generateConfig(serverEntry);
512
854
  writeFileSync(agent.configPath, configContent + '\n', 'utf-8');
513
855
  }
514
- logConfigured(agent.name);
856
+ // ── Post-write verification ──
857
+ const mcpKey = ['claude-code', 'cursor', 'antigravity'].includes(agent.id) ? 'mcpServers' : 'mcp.servers';
858
+ const verifyResult = verifyConfigEntry(agent.configPath, mcpKey);
859
+ if (verifyResult.ok) {
860
+ logConfigured(agent.name);
861
+ console.log(` ${c.green}✔${c.reset} ${c.dim}Verified: config entry is correct${c.reset}`);
862
+ }
863
+ else {
864
+ logConfigured(agent.name);
865
+ console.log(` ${c.yellow}⚠${c.reset} ${c.dim}Verify warning: ${verifyResult.error}${c.reset}`);
866
+ // Attempt auto-fix
867
+ const fixResult = autoFixConfig(agent.configPath, mcpKey);
868
+ if (fixResult.autoFixed) {
869
+ console.log(` ${c.green}✔${c.reset} ${c.dim}Auto-fixed!${c.reset}`);
870
+ }
871
+ else {
872
+ verifyFailCount++;
873
+ }
874
+ }
515
875
  configuredCount++;
516
876
  }
517
877
  catch (err) {
@@ -526,6 +886,26 @@ export async function installAgents() {
526
886
  }
527
887
  else {
528
888
  logOk(`${foundCount} agent(s) detected, ${configuredCount} newly configured.`);
889
+ if (verifyFailCount > 0) {
890
+ logWarn(`${verifyFailCount} config(s) have verification warnings — run "ai-mind-map doctor --fix" to repair.`);
891
+ }
892
+ }
893
+ // Check for misconfigurations from older versions
894
+ const misconfigs = detectMisconfigs();
895
+ if (misconfigs.length > 0) {
896
+ console.log('');
897
+ heading('⚠️ Misconfigurations Detected');
898
+ divider();
899
+ for (const issue of misconfigs) {
900
+ console.log(` ${c.yellow}⚠${c.reset} ${c.bold}${issue.agent}${c.reset}: ${issue.problem}`);
901
+ if (issue.fixFn) {
902
+ issue.fixFn();
903
+ console.log(` ${c.green}✔${c.reset} AUTO-FIXED: ${issue.fix}`);
904
+ }
905
+ else {
906
+ console.log(` ${c.dim}Fix: ${issue.fix}${c.reset}`);
907
+ }
908
+ }
529
909
  }
530
910
  // Deploy rules files so agents know about our capabilities
531
911
  console.log('');
@@ -798,24 +1178,6 @@ export async function runDoctor() {
798
1178
  logFail(`${paddedName} ${result.message}`);
799
1179
  }
800
1180
  }
801
- // Summary
802
- const okCount = results.filter((r) => r.status === 'ok').length;
803
- const warnCount = results.filter((r) => r.status === 'warn').length;
804
- const failCount = results.filter((r) => r.status === 'fail').length;
805
- console.log('');
806
- divider();
807
- if (failCount > 0) {
808
- logFail(`${okCount} passed, ${warnCount} warnings, ${failCount} failures`);
809
- logInfo('Fix the failures above before using AI Mind Map.');
810
- }
811
- else if (warnCount > 0) {
812
- logWarn(`${okCount} passed, ${warnCount} warnings`);
813
- logInfo('AI Mind Map should work, but address warnings for best experience.');
814
- }
815
- else {
816
- logOk(`All ${okCount} checks passed!`);
817
- logInfo('AI Mind Map is ready to use.');
818
- }
819
1181
  // Agent detail table
820
1182
  if (agentStatuses.some((a) => a.detected)) {
821
1183
  console.log('');
@@ -838,6 +1200,59 @@ export async function runDoctor() {
838
1200
  }
839
1201
  }
840
1202
  }
1203
+ // 8. Misconfig detection
1204
+ const misconfigs = detectMisconfigs();
1205
+ if (misconfigs.length > 0) {
1206
+ console.log('');
1207
+ heading('⚠️ Configuration Issues Found');
1208
+ divider();
1209
+ const shouldFix = process.argv.includes('--fix');
1210
+ for (const issue of misconfigs) {
1211
+ results.push({
1212
+ name: `Config: ${issue.agent}`,
1213
+ status: 'warn',
1214
+ message: issue.problem,
1215
+ });
1216
+ console.log(` ${c.yellow}⚠${c.reset} ${c.bold}${issue.agent}${c.reset}: ${issue.problem}`);
1217
+ if (shouldFix && issue.fixFn) {
1218
+ issue.fixFn();
1219
+ console.log(` ${c.green}✔${c.reset} AUTO-FIXED: ${issue.fix}`);
1220
+ }
1221
+ else if (issue.fixFn) {
1222
+ console.log(` ${c.dim}Run with --fix to auto-repair: ${issue.fix}${c.reset}`);
1223
+ }
1224
+ else {
1225
+ console.log(` ${c.dim}Manual fix required: ${issue.fix}${c.reset}`);
1226
+ }
1227
+ }
1228
+ }
1229
+ else {
1230
+ results.push({
1231
+ name: 'Config Integrity',
1232
+ status: 'ok',
1233
+ message: 'No misconfigurations detected',
1234
+ });
1235
+ }
1236
+ // Re-print summary with updated results
1237
+ const finalOk = results.filter((r) => r.status === 'ok').length;
1238
+ const finalWarn = results.filter((r) => r.status === 'warn').length;
1239
+ const finalFail = results.filter((r) => r.status === 'fail').length;
1240
+ console.log('');
1241
+ divider();
1242
+ if (finalFail > 0) {
1243
+ logFail(`${finalOk} passed, ${finalWarn} warnings, ${finalFail} failures`);
1244
+ logInfo('Fix the failures above before using AI Mind Map.');
1245
+ }
1246
+ else if (finalWarn > 0) {
1247
+ logWarn(`${finalOk} passed, ${finalWarn} warnings`);
1248
+ if (!process.argv.includes('--fix')) {
1249
+ logInfo('Run "ai-mind-map doctor --fix" to auto-repair warnings.');
1250
+ }
1251
+ }
1252
+ else {
1253
+ logOk(`All ${finalOk} checks passed!`);
1254
+ logInfo('AI Mind Map is ready to use.');
1255
+ }
841
1256
  console.log('');
842
1257
  }
843
1258
  // ============================================================