diff --git a/CLAUDE.md b/CLAUDE.md index a3d707f3..9de24392 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,7 +16,7 @@ When user says "COM": 1. Increment version in BOTH `package.json` AND `CLAUDE.md` 2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web` -**Version**: 0.1403 (must match `package.json`) +**Version**: 0.1404 (must match `package.json`) ## Project Overview diff --git a/package.json b/package.json index 13f04378..af05bbd9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1403", + "version": "0.1404", "description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/plan-orchestrator.ts b/src/plan-orchestrator.ts index 8119d99b..7a977332 100644 --- a/src/plan-orchestrator.ts +++ b/src/plan-orchestrator.ts @@ -15,8 +15,8 @@ import { Session } from './session.js'; import { ScreenManager } from './screen-manager.js'; -import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; -import { join } from 'node:path'; +import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs'; +import { join, dirname } from 'node:path'; // ============================================================================ // Types @@ -919,11 +919,91 @@ ${prompt} writeFileSync(summaryPath, summary, 'utf-8'); console.log(`[PlanOrchestrator] Saved final result to ${this.outputDir}`); + + // Update the case CLAUDE.md with research context links + this.updateCaseClaudeMd(); } catch (err) { console.error('[PlanOrchestrator] Failed to save final result:', err); } } + /** + * Update the case folder's CLAUDE.md with links to research and analysis files. + * This allows Ralph Loop to `/init` and understand the available knowledge base. + */ + private updateCaseClaudeMd(): void { + if (!this.outputDir) return; + + try { + // outputDir is like /path/to/case/ralph-wizard/ + // CLAUDE.md is in the parent (case folder) + const caseDir = dirname(this.outputDir); + const claudeMdPath = join(caseDir, 'CLAUDE.md'); + + // Get relative path from case folder to ralph-wizard + const wizardRelPath = 'ralph-wizard'; + + // Build the research context section + const contextSection = ` +## Ralph Wizard Knowledge Base + +The following research and analysis files were generated for this task. +Use \`/init\` to load this context, then read specific files as needed. + +### Research Phase +- \`${wizardRelPath}/research/result.json\` - External resources, codebase patterns, recommendations +- \`${wizardRelPath}/research/prompt.md\` - Research agent instructions + +### Analysis Phase +- \`${wizardRelPath}/requirements/result.json\` - Requirements analysis +- \`${wizardRelPath}/architecture/result.json\` - Architecture planning +- \`${wizardRelPath}/testing/result.json\` - Testing strategy +- \`${wizardRelPath}/risks/result.json\` - Risk analysis + +### Synthesis & Optimization +- \`${wizardRelPath}/verification/result.json\` - Plan verification and quality scoring +- \`${wizardRelPath}/execution-optimizer/result.json\` - Parallelization strategy +- \`${wizardRelPath}/final-review/result.json\` - Final review and completeness check + +### Summary +- \`${wizardRelPath}/summary.md\` - Human-readable plan summary +- \`${wizardRelPath}/final-result.json\` - Complete plan with all metadata + +**Key Research Insights**: Check \`${wizardRelPath}/research/result.json\` for: +- External GitHub repos and documentation links +- Existing codebase patterns to follow +- Technical recommendations from research +- Potential challenges to watch for +- Recommended tools and libraries +`; + + // Read existing CLAUDE.md or create new one + let existingContent = ''; + if (existsSync(claudeMdPath)) { + existingContent = readFileSync(claudeMdPath, 'utf-8'); + + // Remove any existing Ralph Wizard section to avoid duplicates + const sectionStart = existingContent.indexOf('## Ralph Wizard Knowledge Base'); + if (sectionStart !== -1) { + // Find the next ## heading or end of file + const nextSectionMatch = existingContent.slice(sectionStart + 1).match(/\n## /); + const sectionEnd = nextSectionMatch + ? sectionStart + 1 + nextSectionMatch.index! + : existingContent.length; + existingContent = existingContent.slice(0, sectionStart) + existingContent.slice(sectionEnd); + } + } + + // Append the new section + const newContent = existingContent.trimEnd() + '\n' + contextSection; + writeFileSync(claudeMdPath, newContent, 'utf-8'); + + console.log(`[PlanOrchestrator] Updated CLAUDE.md with research context at ${claudeMdPath}`); + } catch (err) { + console.error('[PlanOrchestrator] Failed to update CLAUDE.md:', err); + } + } + /** * Generate a human-readable summary of the plan. */ diff --git a/src/web/public/app.js b/src/web/public/app.js index d92b6078..b39d348a 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -4124,7 +4124,48 @@ class ClaudemanApp { throw new Error(errorData.error || `Failed to write prompt file: ${writeRes.status}`); } - console.log('[RalphWizard] Prompt file written, sending read command to Claude...'); + console.log('[RalphWizard] Prompt file written.'); + + // If plan was generated, send /init first to load CLAUDE.md with research context + // This gives Claude awareness of all the research files before starting tasks + if (hasPlan) { + console.log('[RalphWizard] Sending /init to load research context from CLAUDE.md...'); + const initRes = await fetch(`/api/sessions/${sessionId}/input`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ input: '/init\r', useScreen: true }) + }); + + if (!initRes.ok) { + console.warn('[RalphWizard] Failed to send /init, continuing anyway...'); + } else { + // Wait for /init to complete (look for ready indicator or timeout) + console.log('[RalphWizard] Waiting for /init to complete...'); + let initComplete = false; + for (let attempt = 0; attempt < 20; attempt++) { + await new Promise(r => setTimeout(r, 500)); + try { + const statusRes = await fetch(`/api/sessions/${sessionId}`); + const statusData = await statusRes.json(); + // /init is complete when we see the prompt indicator (❯) or status is ready + if (statusData.status === 'ready' || statusData.lastOutput?.includes('❯')) { + console.log('[RalphWizard] /init complete, Claude now has research context.'); + initComplete = true; + break; + } + } catch (e) { + console.warn('[RalphWizard] Status check error:', e); + } + } + if (!initComplete) { + console.log('[RalphWizard] /init may still be running, proceeding anyway...'); + } + // Small delay after /init + await new Promise(r => setTimeout(r, 1000)); + } + } + + console.log('[RalphWizard] Sending read command to Claude...'); // Send a simple command to Claude to read the prompt file // This avoids all the escaping issues with long multi-line prompts