Skip to main content
Glama
graduation.ts7.5 kB
/** * Graduation Prompt * * Displays copy-paste ready AI prompts after successful smoke test. * Includes cross-platform clipboard support using native commands. * * @package WP_Navigator_MCP * @since 2.1.0 */ import { spawn } from 'child_process'; import { box, success, info, newline, colorize, symbols } from '../tui/components.js'; // ============================================================================= // Types // ============================================================================= export interface GraduationContext { siteUrl: string; siteName?: string; pluginEdition?: 'free' | 'pro'; } export interface GraduationPrompt { primary: string; alternatives: string[]; } // ============================================================================= // Prompt Generation // ============================================================================= /** * Extract display-friendly site name from URL or siteName */ function getSiteDisplayName(context: GraduationContext): string { if (context.siteName) { return context.siteName; } try { const url = new URL(context.siteUrl); return url.hostname; } catch { return context.siteUrl; } } /** * Generate context-aware AI prompts */ export function generateGraduationPrompts(context: GraduationContext): GraduationPrompt { const siteName = getSiteDisplayName(context); const primary = `Show me an overview of my WordPress site at ${siteName} and list the 5 most recent posts`; const alternatives = [ `Audit the SEO metadata for all pages on ${siteName}`, `List all plugins on ${siteName} and check for available updates`, `Show me pages on ${siteName} that haven't been updated in 30 days`, ]; // Add Pro-only prompts if edition is pro if (context.pluginEdition === 'pro') { alternatives.push(`Analyze content performance across ${siteName}`); } return { primary, alternatives }; } /** * Format prompt text for terminal display * Wraps long lines at word boundaries */ export function formatPromptForTerminal(prompt: string, maxWidth: number = 60): string { const words = prompt.split(' '); const lines: string[] = []; let currentLine = ''; for (const word of words) { if (currentLine.length + word.length + 1 <= maxWidth) { currentLine += (currentLine ? ' ' : '') + word; } else { if (currentLine) lines.push(currentLine); currentLine = word; } } if (currentLine) lines.push(currentLine); return lines.join('\n'); } // ============================================================================= // Clipboard Support // ============================================================================= /** * Check if clipboard is available on this platform */ export function isClipboardAvailable(): boolean { const platform = process.platform; return platform === 'darwin' || platform === 'linux' || platform === 'win32'; } /** * Copy text to clipboard using native commands via spawn (no shell) * - macOS: pbcopy * - Linux: xclip or xsel * - Windows: clip.exe * * Uses spawn instead of exec to prevent shell injection vulnerabilities. * Text is piped to stdin, not passed as command arguments. * * @returns true if copy succeeded, false otherwise */ export async function copyToClipboard(text: string): Promise<boolean> { const platform = process.platform; const runClipboardCommand = (cmd: string, args: string[]): Promise<boolean> => { return new Promise((resolve) => { try { const proc = spawn(cmd, args, { stdio: ['pipe', 'ignore', 'ignore'] }); proc.on('error', () => resolve(false)); proc.on('close', (code) => resolve(code === 0)); if (proc.stdin) { proc.stdin.write(text); proc.stdin.end(); } else { resolve(false); } } catch { resolve(false); } }); }; try { if (platform === 'darwin') { // macOS - use pbcopy return await runClipboardCommand('pbcopy', []); } else if (platform === 'linux') { // Linux - try xclip first, then xsel const xclipResult = await runClipboardCommand('xclip', ['-selection', 'clipboard']); if (xclipResult) return true; // Try xsel as fallback return await runClipboardCommand('xsel', ['--clipboard', '--input']); } else if (platform === 'win32') { // Windows - use clip.exe return await runClipboardCommand('clip', []); } return false; } catch { return false; } } // ============================================================================= // Interactive Display // ============================================================================= /** * Wait for keypress with timeout * Returns the key pressed or null on timeout */ async function waitForKeypress(timeoutMs: number = 10000): Promise<string | null> { return new Promise((resolve) => { const stdin = process.stdin; const wasRaw = stdin.isRaw; // Set up timeout const timer = setTimeout(() => { cleanup(); resolve(null); }, timeoutMs); // Set up raw mode for single keypress if (stdin.isTTY) { stdin.setRawMode(true); } stdin.resume(); const cleanup = () => { clearTimeout(timer); stdin.removeListener('data', onData); if (stdin.isTTY) { stdin.setRawMode(wasRaw ?? false); } stdin.pause(); }; const onData = (data: Buffer) => { cleanup(); const key = data.toString(); // Handle Ctrl+C if (key === '\x03') { process.exit(0); } resolve(key); }; stdin.once('data', onData); }); } /** * Display graduation prompt with interactive copy option */ export async function displayGraduationPrompt( context: GraduationContext, options?: { showAlternatives?: boolean; timeout?: number } ): Promise<void> { const { showAlternatives = true, timeout = 10000 } = options ?? {}; const prompts = generateGraduationPrompts(context); const clipboardAvailable = isClipboardAvailable(); // Display header newline(); console.error(colorize('Setup Complete!', 'bold') + ' Try this in Claude:'); newline(); // Display primary prompt in a box const formattedPrompt = formatPromptForTerminal(prompts.primary, 56); box(`"${formattedPrompt}"`, { title: 'Copy this prompt' }); newline(); // Display interactive options if (clipboardAvailable && process.stdin.isTTY) { console.error( ` Press ${colorize('[c]', 'cyan')} to copy ${colorize(symbols.bullet, 'dim')} Press ${colorize('[Enter]', 'cyan')} to continue` ); newline(); // Wait for keypress const key = await waitForKeypress(timeout); if (key?.toLowerCase() === 'c') { const copied = await copyToClipboard(prompts.primary); if (copied) { success('Copied to clipboard!'); } else { console.error(colorize('Could not copy to clipboard', 'yellow')); } newline(); } } else { // Non-TTY or clipboard unavailable - just show the prompt info('Copy the prompt above to try it in Claude'); newline(); } // Display alternative prompts if (showAlternatives && prompts.alternatives.length > 0) { console.error(colorize('Alternative prompts:', 'dim')); for (const alt of prompts.alternatives) { console.error(` ${colorize(symbols.bullet, 'dim')} "${alt}"`); } newline(); } } export default displayGraduationPrompt;

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