All files / cli main-stdio.ts

21.21% Statements 7/33
6.25% Branches 1/16
0% Functions 0/8
21.21% Lines 7/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91    1x 1x   1x 1x 1x 1x                                                                                                                                       1x                            
#!/usr/bin/env node
 
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { LoggerService } from '@nestjs/common';
import { AppModule } from '../nest/app.module';
import { McpCliCommand } from './mcp-cli.command';
import { spawn } from 'child_process';
import * as path from 'path';
 
/**
 * Custom logger that writes all logs to stderr instead of stdout.
 * This is required for stdio-based MCP communication where only
 * JSON-RPC messages should go to stdout.
 */
class StderrLogger implements LoggerService {
  log(message: string): void {
    process.stderr.write(`[LOG] ${message}\n`);
  }
 
  error(message: string, trace?: string): void {
    process.stderr.write(`[ERROR] ${message}\n`);
    if (trace) {
      process.stderr.write(`[TRACE] ${trace}\n`);
    }
  }
 
  warn(message: string): void {
    process.stderr.write(`[WARN] ${message}\n`);
  }
 
  debug(message: string): void {
    process.stderr.write(`[DEBUG] ${message}\n`);
  }
 
  verbose(message: string): void {
    process.stderr.write(`[VERBOSE] ${message}\n`);
  }
}
 
async function bootstrap(): Promise<void> {
  const app = await NestFactory.createApplicationContext(AppModule, {
    logger: new StderrLogger()
  });
 
  // If invoked as "gs-squad-mcp dashboard" (or with --dashboard),
  // initialize DB via Nest + TypeORM,
  // then spawn the dashboard TUI process and exit when it exits.
  const argv = process.argv.slice(2);
  const wantsDashboard =
    argv.includes('dashboard') || argv.includes('--dashboard');
  if (wantsDashboard) {
    const projectRoot = path.resolve(__dirname, '../../');
    const dashboardEntry = path.join(
      projectRoot,
      'packages/gs-squad-dashboard/dist/cli.js'
    );
    const child = spawn(process.execPath, [ dashboardEntry ], {
      stdio: 'inherit',
      env: { ...process.env }
    });
    child.on('exit', async (code) => {
      try {
        await app.close();
      } finally {
        process.exit(code ?? 0);
      }
    });
    return;
  }
 
  const cliCommand = app.get(McpCliCommand);
  await cliCommand.run();
}
 
// Only execute bootstrap if this file is run directly (not imported)
Iif (require.main === module) {
  bootstrap().catch((error) => {
    process.stderr.write(
      `[FATAL] Failed to start MCP server: ${
        error instanceof Error ? error.message : String(error)
      }\n`
    );
    if (error instanceof Error && error.stack) {
      process.stderr.write(`[STACK] ${error.stack}\n`);
    }
    process.exit(1);
  });
}