Skip to main content
Glama
claude-setup.ts8.93 kB
/** * WP Navigator Claude Setup Command * * Adds Claude Code support to existing WP Navigator projects. * Creates CLAUDE.md and .mcp.json for Claude Code integration. * * @package WP_Navigator_MCP * @since 2.3.0 */ import * as fs from 'fs'; import * as path from 'path'; import { generateClaudeMd, generateMcpJson, getDefaultClaudeMdContext, type ClaudeMdContext, } from '../init/generators.js'; import { success, error as errorMessage, info, newline, colorize } from '../tui/components.js'; // ============================================================================= // Types // ============================================================================= export interface ClaudeSetupOptions { yes?: boolean; // Skip confirmation json?: boolean; // Output JSON instead of TUI } export interface ClaudeSetupResult { claudeMdCreated: boolean; claudeMdOverwritten: boolean; mcpJsonCreated: boolean; mcpJsonOverwritten: boolean; smokeTestPassed: boolean | null; // null = not run (no credentials) } // ============================================================================= // Setup Functions // ============================================================================= /** * Read site info from wpnav.config.json if available */ function readSiteInfo(cwd: string): Partial<ClaudeMdContext> { const configPath = path.join(cwd, 'wpnav.config.json'); try { const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const result: Partial<ClaudeMdContext> = {}; // Try to get site URL from environments.default or environments.local const envConfig = config.environments?.default || config.environments?.local; if (envConfig?.site_url) { result.site_url = envConfig.site_url; } // Try to get site name from manifest reference or environment if (envConfig?.site_name) { result.site_name = envConfig.site_name; } // Try to get environment if (config.default_environment) { result.environment = config.default_environment; } return result; } catch { return {}; } } /** * Run a simple smoke test to verify WordPress connection * Returns true if connection works, false if failed, null if can't test (no credentials) */ async function runSmokeTest(cwd: string): Promise<boolean | null> { try { // Try to load .wpnav.env or wpnav.config.json for connection info const envPath = path.join(cwd, '.wpnav.env'); const configPath = path.join(cwd, 'wpnav.config.json'); let siteUrl: string | undefined; let username: string | undefined; let password: string | undefined; // Check .wpnav.env first if (fs.existsSync(envPath)) { const envContent = fs.readFileSync(envPath, 'utf8'); const lines = envContent.split('\n'); for (const line of lines) { const [key, ...valueParts] = line.split('='); const value = valueParts.join('=').trim(); if (key === 'WP_BASE_URL') siteUrl = value; if (key === 'WP_APP_USER') username = value; if (key === 'WP_APP_PASS') password = value; } } // Fall back to config environments if (!siteUrl && fs.existsSync(configPath)) { const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const envConfig = config.environments?.default || config.environments?.local; if (envConfig?.site_url) siteUrl = envConfig.site_url; if (envConfig?.username) username = envConfig.username; if (envConfig?.app_password) password = envConfig.app_password; } // Can't test without credentials if (!siteUrl || !username || !password) { return null; } // Try a simple request to the site (just check if reachable) const restUrl = siteUrl.replace(/\/$/, '') + '/wp-json'; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); try { const response = await fetch(restUrl, { method: 'GET', signal: controller.signal, headers: { Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`, }, }); clearTimeout(timeout); return response.ok || response.status === 401; // 401 means auth is required but site is reachable } catch { clearTimeout(timeout); return false; } } catch { return null; } } /** * Perform the Claude setup */ export function performClaudeSetup(cwd: string): ClaudeSetupResult { const result: ClaudeSetupResult = { claudeMdCreated: false, claudeMdOverwritten: false, mcpJsonCreated: false, mcpJsonOverwritten: false, smokeTestPassed: null, }; // Build context for template const siteInfo = readSiteInfo(cwd); const context = getDefaultClaudeMdContext(siteInfo); // Generate CLAUDE.md const claudeMdPath = path.join(cwd, 'CLAUDE.md'); const claudeMdExists = fs.existsSync(claudeMdPath); const claudeMdContent = generateClaudeMd(context); fs.writeFileSync(claudeMdPath, claudeMdContent, 'utf8'); result.claudeMdCreated = true; result.claudeMdOverwritten = claudeMdExists; // Generate .mcp.json const mcpJsonPath = path.join(cwd, '.mcp.json'); const mcpJsonExists = fs.existsSync(mcpJsonPath); const mcpJsonContent = generateMcpJson({ configPath: './wpnav.config.json', enableWrites: false, // Safe by default }); fs.writeFileSync(mcpJsonPath, mcpJsonContent, 'utf8'); result.mcpJsonCreated = true; result.mcpJsonOverwritten = mcpJsonExists; return result; } // ============================================================================= // Display Functions // ============================================================================= /** * Display setup results */ function displayResults(result: ClaudeSetupResult): void { if (result.claudeMdCreated) { if (result.claudeMdOverwritten) { success('Updated CLAUDE.md'); } else { success('Created CLAUDE.md'); } } if (result.mcpJsonCreated) { if (result.mcpJsonOverwritten) { success('Updated .mcp.json'); } else { success('Created .mcp.json'); } } } /** * Display smoke test result */ function displaySmokeTestResult(passed: boolean | null): void { if (passed === true) { success('Connection verified!'); } else if (passed === false) { info('Could not verify connection (site may be unreachable)'); } // null = credentials not available, don't display anything } // ============================================================================= // Main Handler // ============================================================================= /** * Output JSON result to stdout */ function outputJSON(data: unknown): void { console.log(JSON.stringify(data, null, 2)); } /** * Handle the claude-setup command * @returns Exit code: 0 for success, 1 for errors */ export async function handleClaudeSetup(options: ClaudeSetupOptions = {}): Promise<number> { const cwd = process.cwd(); const isJson = options.json === true; // Check for existing wpnav.config.json (require prior init) const configPath = path.join(cwd, 'wpnav.config.json'); if (!fs.existsSync(configPath)) { if (isJson) { outputJSON({ success: false, command: 'claude-setup', error: { code: 'CONFIG_NOT_FOUND', message: 'No wpnav.config.json found. Run "wpnav init" first.', }, }); } else { errorMessage( 'No wpnav.config.json found.', 'Run "wpnav init" first to initialize your project.' ); } return 1; } // Perform setup let result: ClaudeSetupResult; try { result = performClaudeSetup(cwd); } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); if (isJson) { outputJSON({ success: false, command: 'claude-setup', error: { code: 'SETUP_FAILED', message: errorMsg, }, }); } else { errorMessage('Setup failed', errorMsg); } return 1; } // Run smoke test result.smokeTestPassed = await runSmokeTest(cwd); // Output results if (isJson) { outputJSON({ success: true, command: 'claude-setup', data: { claude_md_created: result.claudeMdCreated, claude_md_overwritten: result.claudeMdOverwritten, mcp_json_created: result.mcpJsonCreated, mcp_json_overwritten: result.mcpJsonOverwritten, smoke_test_passed: result.smokeTestPassed, }, }); } else { // TUI mode newline(); console.error(`${colorize('Claude Setup', 'bold')}`); newline(); displayResults(result); displaySmokeTestResult(result.smokeTestPassed); newline(); success('Claude Code is ready!'); info('Start a new Claude Code session in this directory.'); } return 0; } export default handleClaudeSetup;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/littlebearapps/wp-navigator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server