UNPKG

context-forge

Version:

AI orchestration platform with autonomous teams, enhancement planning, migration tools, 25+ slash commands, checkpoints & hooks. Multi-IDE: Claude, Cursor, Windsurf, Cline, Copilot

236 lines (227 loc) • 9.61 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runPrpCommand = void 0; const commander_1 = require("commander"); const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const inquirer_1 = __importDefault(require("inquirer")); const child_process_1 = require("child_process"); exports.runPrpCommand = new commander_1.Command('run-prp') .description('Execute a PRP (Product Requirement Prompt) with Claude Code') .argument('[prp-name]', 'Name of the PRP file (without .md extension)') .option('-p, --prp-path <path>', 'Direct path to PRP file') .option('-i, --interactive', 'Run in interactive mode', true) .option('-o, --output-format <format>', 'Output format: text, json, stream-json', 'text') .option('-m, --model <model>', 'CLI executable for the LLM', 'claude') .action(async (prpName, options) => { console.log(chalk_1.default.blue.bold('\nšŸš€ Context Forge PRP Runner\n')); const spinner = (0, ora_1.default)(); try { // Determine PRP file path let prpPath; if (options.prpPath) { prpPath = path_1.default.resolve(options.prpPath); } else if (prpName) { // Look in PRPs directory prpPath = path_1.default.join(process.cwd(), 'PRPs', `${prpName}.md`); // Also check with -prp suffix if (!(await fs_extra_1.default.pathExists(prpPath))) { prpPath = path_1.default.join(process.cwd(), 'PRPs', `${prpName}-prp.md`); } } else { // Interactive selection const prpsDir = path_1.default.join(process.cwd(), 'PRPs'); if (!(await fs_extra_1.default.pathExists(prpsDir))) { console.error(chalk_1.default.red('No PRPs directory found. Run this command from a Context Forge project.')); process.exit(1); } const prpFiles = (await fs_extra_1.default.readdir(prpsDir)).filter((f) => f.endsWith('.md') && !f.includes('README') && !f.includes('template')); if (prpFiles.length === 0) { console.error(chalk_1.default.red('No PRP files found in PRPs directory.')); process.exit(1); } const { selectedPrp } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedPrp', message: 'Select a PRP to execute:', choices: prpFiles.map((f) => ({ name: f.replace('.md', ''), value: path_1.default.join(prpsDir, f), })), }, ]); prpPath = selectedPrp; } // Verify PRP exists if (!(await fs_extra_1.default.pathExists(prpPath))) { console.error(chalk_1.default.red(`PRP file not found: ${prpPath}`)); process.exit(1); } console.log(chalk_1.default.cyan(`\nšŸ“„ Loading PRP: ${path_1.default.basename(prpPath)}`)); // Read PRP content const prpContent = await fs_extra_1.default.readFile(prpPath, 'utf-8'); // Build meta prompt const metaPrompt = buildMetaPrompt(prpContent); // Check if claude is available spinner.start('Checking Claude Code availability...'); const claudeAvailable = await checkClaudeAvailable(); spinner.stop(); if (!claudeAvailable) { console.error(chalk_1.default.red('\nClaude Code not found. Please install it first:')); console.log(chalk_1.default.yellow('npm install -g @anthropic-ai/claude-code')); process.exit(1); } // Execute with Claude console.log(chalk_1.default.cyan('\nšŸ¤– Executing PRP with Claude Code...\n')); if (options.interactive) { // Interactive mode - user can continue conversation await runInteractive(metaPrompt); } else { // Headless mode - single execution await runHeadless(metaPrompt, options.outputFormat); } } catch (error) { spinner.fail('PRP execution failed'); console.error(chalk_1.default.red('Error:'), error); process.exit(1); } }); function buildMetaPrompt(prpContent) { return `Ingest and understand the Product Requirement Prompt (PRP) below in detail. # WORKFLOW GUIDANCE: ## Planning Phase - Think hard before you code. Create a comprehensive plan addressing all requirements. - Break down complex tasks into smaller, manageable steps. - Use the TodoWrite tool to create and track your implementation plan. - Identify implementation patterns from existing code to follow. ## Implementation Phase - Follow code conventions and patterns found in existing files. - Implement one component at a time and verify it works correctly. - Write clear, maintainable code with appropriate comments. - Consider error handling, edge cases, and potential security issues. - Use type hints to ensure type safety. ## Testing Phase - Test each component thoroughly as you build it. - Use the provided validation gates to verify your implementation. - Verify that all requirements have been satisfied. - Run all validation commands and fix any issues. ## Example Implementation Approach: 1. Analyze the PRP requirements in detail 2. Search for and understand existing patterns in the codebase 3. Search the Web and gather additional context and examples 4. Create a step-by-step implementation plan with TodoWrite 5. Implement core functionality first, then additional features 6. Test and validate each component 7. Ensure all validation gates pass When all validation gates pass and requirements are met, output "āœ… PRP COMPLETE" on a new line. --- # PRP CONTENT: ${prpContent}`; } async function checkClaudeAvailable() { return new Promise((resolve) => { const check = (0, child_process_1.spawn)('which', ['claude']); check.on('close', (code) => { resolve(code === 0); }); check.on('error', () => { resolve(false); }); }); } async function runInteractive(prompt) { return new Promise((resolve, reject) => { // Start claude in interactive mode with prompt via stdin const claude = (0, child_process_1.spawn)('claude', [], { stdio: ['pipe', 'inherit', 'inherit'], }); // Send the prompt claude.stdin.write(prompt); claude.stdin.end(); claude.on('close', (code) => { if (code === 0) { console.log(chalk_1.default.green('\nāœ… PRP execution completed successfully!')); resolve(); } else { reject(new Error(`Claude exited with code ${code}`)); } }); claude.on('error', (err) => { reject(err); }); }); } async function runHeadless(prompt, outputFormat) { return new Promise((resolve, reject) => { // Run claude with -p flag for single execution const args = ['-p', prompt]; if (outputFormat !== 'text') { args.push('--output-format', outputFormat); } const claude = (0, child_process_1.spawn)('claude', args, { stdio: ['ignore', 'pipe', 'pipe'], }); let output = ''; let errorOutput = ''; claude.stdout.on('data', (data) => { const chunk = data.toString(); output += chunk; if (outputFormat === 'stream-json') { // Stream JSON output line by line const lines = chunk.split('\n').filter((line) => line.trim()); for (const line of lines) { try { const parsed = JSON.parse(line); console.log(chalk_1.default.gray('[Stream]'), parsed); } catch { // Not valid JSON, skip } } } }); claude.stderr.on('data', (data) => { errorOutput += data.toString(); }); claude.on('close', (code) => { if (code === 0) { if (outputFormat === 'text') { console.log(output); } else if (outputFormat === 'json') { try { const parsed = JSON.parse(output); console.log(JSON.stringify(parsed, null, 2)); } catch { console.error(chalk_1.default.red('Failed to parse JSON output')); console.log(output); } } if (output.includes('āœ… PRP COMPLETE')) { console.log(chalk_1.default.green('\nāœ… PRP execution completed successfully!')); } resolve(); } else { console.error(chalk_1.default.red('Error output:'), errorOutput); reject(new Error(`Claude exited with code ${code}`)); } }); claude.on('error', (err) => { reject(err); }); }); } //# sourceMappingURL=run-prp.js.map