UNPKG

@toolprint/hypertool-mcp

Version:

HyperTool MCP proxy server for routing requests between clients and multiple underlying MCP servers

232 lines • 10.3 kB
/** * Global installation setup script * Installs Hypertool for all detected applications * Usage: npx -y @toolprint/hypertool-mcp --install */ import { promises as fs } from "fs"; import { join } from "path"; import { homedir } from "os"; import inquirer from "inquirer"; import { displayBanner, output } from "../../utils/output.js"; import { theme } from "../../utils/theme.js"; import { fileExists, hasClaudeCodeGlobalHypertoolSlashCommands, } from "../shared/mcpSetupUtils.js"; import { ClaudeDesktopSetup } from "../claude-desktop/setup.js"; import { CursorSetup } from "../cursor/setup.js"; import { ClaudeCodeSetup } from "../claude-code/setup.js"; import { createCommandTemplates } from "../claude-code/utils.js"; export class GlobalSetup { dryRun = false; /** * Detect installed applications */ async detectInstalledApps() { const apps = []; // Check Claude Code first (if in a project directory) const currentDir = process.cwd(); const hasGit = await fileExists(join(currentDir, ".git")); const hasMcpJson = await fileExists(join(currentDir, ".mcp.json")); if (hasGit || hasMcpJson) { apps.push({ name: "claude-code", displayName: "🤖 Claude Code (current project)", configPath: join(currentDir, ".mcp.json"), setupClass: ClaudeCodeSetup, }); } // Check Claude Desktop (macOS) const claudeDesktopPath = join(homedir(), "Library/Application Support/Claude/claude_desktop_config.json"); if (await fileExists(claudeDesktopPath)) { apps.push({ name: "claude-desktop", displayName: "🖥️ Claude Desktop", configPath: claudeDesktopPath, setupClass: ClaudeDesktopSetup, }); } // Check Cursor const cursorPath = join(homedir(), ".cursor/mcp.json"); if (await fileExists(cursorPath)) { apps.push({ name: "cursor", displayName: "✏️ Cursor", configPath: cursorPath, setupClass: CursorSetup, }); } return apps; } async run(dryRun = false) { this.dryRun = dryRun; try { // Clear terminal at the beginning output.clearTerminal(); displayBanner(); // Welcome banner output.displayHeader("🚀 Hypertool Global Installation"); if (this.dryRun) { output.info(theme.info("🔍 [DRY RUN MODE] - No changes will be made")); output.displaySpaceBuffer(1); } // Step 1: Detect installed applications const detectedApps = await this.detectInstalledApps(); if (detectedApps.length === 0) { output.warn("⚠️ No supported applications detected"); output.displaySpaceBuffer(1); output.displaySubHeader("Supported applications:"); output.displayInstruction("• Claude Code (in project directory)"); output.displayInstruction("• Claude Desktop (macOS)"); output.displayInstruction("• Cursor IDE"); output.displaySpaceBuffer(1); output.info("💡 Install one of these applications and run the installer again"); return; } // Step 2: Show detected applications output.displaySubHeader("🔍 Detected Applications:"); output.displaySpaceBuffer(1); detectedApps.forEach((app) => { output.info(`✅ ${app.displayName}`); output.displayInstruction(` Config: ${app.configPath}`); }); output.displaySpaceBuffer(1); // Step 3: Check for global slash commands const hasGlobalCommands = await hasClaudeCodeGlobalHypertoolSlashCommands(); let shouldInstallGlobalCommands = false; if (hasGlobalCommands) { output.info("✅ Global CC slash commands already installed in ~/.claude/commands/ht/"); output.displaySpaceBuffer(1); } else { output.warn("⚠️ Global slash commands not found"); output.displaySpaceBuffer(1); const { installCommands } = await inquirer.prompt([ { type: "confirm", name: "installCommands", message: theme.warning("Install global slash commands? (recommended)"), default: true, }, ]); shouldInstallGlobalCommands = installCommands; output.displaySpaceBuffer(1); } // Step 4: Get user confirmation const { shouldProceed } = await inquirer.prompt([ { type: "confirm", name: "shouldProceed", message: theme.warning(`Install Hypertool for ${detectedApps.length} application(s)?`), default: true, }, ]); if (!shouldProceed) { output.info("Installation cancelled."); return; } output.displaySpaceBuffer(1); // Step 5: Install global slash commands if needed if (shouldInstallGlobalCommands) { if (this.dryRun) { output.info("[DRY RUN] Would install global slash commands to: ~/.claude/commands/ht/"); } else { output.info("📦 Installing global slash commands..."); const globalCommandsDir = join(homedir(), ".claude/commands/ht"); // Clean existing commands and install fresh ones try { await fs.rm(globalCommandsDir, { recursive: true, force: true }); } catch { // Directory doesn't exist, continue } // Create directory await fs.mkdir(globalCommandsDir, { recursive: true }); // Generate command templates const commandTemplates = await createCommandTemplates(); // Write all command files for (const [filename, content] of Object.entries(commandTemplates)) { const filePath = join(globalCommandsDir, filename); await fs.writeFile(filePath, content, "utf8"); } output.displaySpaceBuffer(1); } } // Step 6: Run individual setup scripts const results = []; for (const app of detectedApps) { // Add separator between apps output.displaySeparator(); output.displaySpaceBuffer(1); // // Display app header with typewriter effect // const headerEmoji = app.name === 'claude-code' ? '📝' : // app.name === 'claude-desktop' ? '🖥️' : // app.name === 'cursor' ? '✏️' : '📦'; // const headerText = `${headerEmoji} ${app.displayName}`; if (this.dryRun) { await output.displayHeader(`[DRY RUN] ${app.displayName}`); } else { await output.displayHeader(app.displayName); } output.displaySpaceBuffer(1); try { const setup = new app.setupClass(); await setup.run(this.dryRun); results.push({ app: app.displayName, success: true }); } catch (error) { results.push({ app: app.displayName, success: false, error: error instanceof Error ? error.message : String(error), }); } } // Success summary output.displaySpaceBuffer(1); output.displaySeparator(); output.displaySpaceBuffer(1); if (this.dryRun) { output.info(theme.warning("🔍 [DRY RUN] Installation simulation complete")); output.displaySpaceBuffer(1); output.info("No actual changes were made to your system."); } else { output.success("✨ All installations complete!"); output.displaySpaceBuffer(1); // Show concise results const successfulApps = results.filter((r) => r.success).length; const failedApps = results.filter((r) => !r.success).length; if (successfulApps > 0) { output.success(`✅ Configured ${successfulApps} application(s) successfully`); } if (failedApps > 0) { output.error(`❌ Failed to configure ${failedApps} application(s)`); } if (shouldInstallGlobalCommands) { output.success("✅ Installed global slash commands"); } output.displaySpaceBuffer(1); // Simple next step if (successfulApps > 0) { output.info("🎯 Restart your applications to use Hypertool!"); output.displaySpaceBuffer(1); } } } catch (error) { output.error("❌ Setup failed:"); output.error(error instanceof Error ? error.message : String(error)); process.exit(1); } } } // Run the setup if this script is executed directly if (import.meta.url === `file://${process.argv[1]}`) { const setup = new GlobalSetup(); setup.run().catch((error) => { output.error("Setup failed:"); output.error(error instanceof Error ? error.message : String(error)); process.exit(1); }); } //# sourceMappingURL=setup.js.map