chore: bump version to 0.1434

This commit is contained in:
arkon
2026-01-30 11:30:51 +01:00
parent da5d86a84f
commit 7dfd97df58
15 changed files with 1300 additions and 122 deletions
+164 -2
View File
@@ -3482,6 +3482,10 @@ class ClaudemanApp {
// Stop any existing generation first
this.stopPlanGeneration();
// Close old plan subagent windows and clear their state
// This ensures clicking on prompts shows fresh data, not cached from previous runs
this.closePlanSubagentWindows();
// Reset stopped flag to allow new SSE events
this.planGenerationStopped = false;
@@ -4795,23 +4799,181 @@ class ClaudemanApp {
return;
}
// Get agent info from planSubagents Map
const agentInfo = this.planSubagents.get(agentId);
const isRunning = agentInfo?.status === 'running';
const taskDescription = this.ralphWizardConfig?.taskDescription || 'No task description available';
try {
const filePath = `${agentType}/prompt.md`;
const res = await fetch(`/api/cases/${encodeURIComponent(caseName)}/ralph-wizard/file/${encodeURIComponent(filePath)}`);
const data = await res.json();
if (!data.success) {
this.showPlanFileWindow(agentType, 'prompt.md', `Error: ${data.error}`, false);
// File not found - show contextual message based on agent status
if (isRunning) {
const content = this.buildAgentRunningContent(agentType, agentInfo, taskDescription);
this.showPlanFileWindow(agentType, 'prompt.md', content, false);
} else {
// Agent completed but no prompt file - might have failed
const content = this.buildAgentNoPromptContent(agentType, taskDescription);
this.showPlanFileWindow(agentType, 'prompt.md', content, false);
}
return;
}
this.showPlanFileWindow(agentType, 'prompt.md', data.data.content, false);
} catch (err) {
console.error('Failed to fetch prompt:', err);
this.showPlanFileWindow(agentType, 'prompt.md', `Error fetching prompt: ${err.message}`, false);
if (isRunning) {
const content = this.buildAgentRunningContent(agentType, agentInfo, taskDescription);
this.showPlanFileWindow(agentType, 'prompt.md', content, false);
} else {
this.showPlanFileWindow(agentType, 'prompt.md', `Error fetching prompt: ${err.message}`, false);
}
}
}
buildAgentRunningContent(agentType, agentInfo, taskDescription) {
const typeDescriptions = {
research: {
title: 'Research Agent',
role: 'Gathering external resources, exploring the codebase, and identifying patterns',
activities: [
'Reading CLAUDE.md and project documentation',
'Exploring the codebase structure with Glob and Grep',
'Searching the web for relevant documentation and examples',
'Identifying existing patterns and conventions',
'Finding similar implementations to reference',
],
},
requirements: {
title: 'Requirements Analyst',
role: 'Breaking down the task into concrete, actionable requirements',
activities: [
'Analyzing the task description for functional requirements',
'Identifying non-functional requirements (performance, security)',
'Detecting edge cases and error scenarios',
'Defining acceptance criteria for each requirement',
],
},
architecture: {
title: 'Architecture Planner',
role: 'Designing the technical approach and file structure',
activities: [
'Identifying files to create or modify',
'Planning the module/component structure',
'Defining interfaces and data flow',
'Ensuring consistency with existing architecture',
],
},
testing: {
title: 'TDD Specialist',
role: 'Planning the test-first development strategy',
activities: [
'Identifying test cases for each requirement',
'Planning unit, integration, and E2E tests',
'Defining test fixtures and mocks needed',
'Ensuring edge cases have test coverage',
],
},
risks: {
title: 'Risk Analyst',
role: 'Identifying potential issues and mitigation strategies',
activities: [
'Analyzing potential failure points',
'Identifying security concerns',
'Flagging performance bottlenecks',
'Suggesting rollback strategies',
],
},
verification: {
title: 'Verification Expert',
role: 'Validating plan completeness and quality',
activities: [
'Checking all requirements have implementation steps',
'Verifying test coverage is adequate',
'Ensuring dependencies are correctly ordered',
'Scoring plan quality and completeness',
],
},
'execution-optimizer': {
title: 'Execution Optimizer',
role: 'Optimizing the plan for Claude Code execution',
activities: [
'Identifying tasks that can run in parallel',
'Grouping related tasks for efficiency',
'Marking fresh context requirements',
'Estimating token usage per task',
],
},
'final-review': {
title: 'Final Reviewer',
role: 'Performing holistic validation of the complete plan',
activities: [
'Checking for gaps or missing steps',
'Validating task ordering and dependencies',
'Ensuring the plan is implementable',
'Final quality assessment',
],
},
};
const info = typeDescriptions[agentType] || {
title: agentType,
role: 'Processing the task',
activities: ['Working on the task...'],
};
const elapsed = agentInfo?.startTime
? Math.floor((Date.now() - agentInfo.startTime) / 1000)
: 0;
const model = agentInfo?.model || 'unknown';
let content = `# ${info.title} - Still Running\n\n`;
content += `⏳ **Status:** Generating... (${elapsed}s elapsed)\n`;
content += `🤖 **Model:** ${model}\n\n`;
content += `---\n\n`;
content += `## Role\n${info.role}\n\n`;
content += `## Currently Working On\n`;
info.activities.forEach(activity => {
content += `- ${activity}\n`;
});
content += `\n---\n\n`;
content += `## Task Being Analyzed\n\n`;
content += `\`\`\`\n${taskDescription}\n\`\`\`\n\n`;
content += `---\n\n`;
content += `*The full prompt will be available once the agent completes.*\n`;
content += `*Click again after completion to see the actual prompt that was used.*`;
return content;
}
buildAgentNoPromptContent(agentType, taskDescription) {
const typeLabels = {
research: 'Research Agent',
requirements: 'Requirements Analyst',
architecture: 'Architecture Planner',
testing: 'TDD Specialist',
risks: 'Risk Analyst',
verification: 'Verification Expert',
'execution-optimizer': 'Execution Optimizer',
'final-review': 'Final Reviewer',
};
let content = `# ${typeLabels[agentType] || agentType} - No Prompt Available\n\n`;
content += `The prompt file for this agent was not saved.\n\n`;
content += `This can happen if:\n`;
content += `- The agent failed before completing\n`;
content += `- The plan generation was cancelled\n`;
content += `- There was an error saving the output\n\n`;
content += `---\n\n`;
content += `## Task Description\n\n`;
content += `\`\`\`\n${taskDescription}\n\`\`\``;
return content;
}
async showPlanAgentFiles(agentId, agentType) {
const caseName = this.ralphWizardConfig?.caseName;
if (!caseName) {
+69 -84
View File
@@ -426,15 +426,19 @@
<div class="modal-tab-content" id="respawn-tab">
<!-- Respawn Settings Section -->
<div class="session-respawn-section" id="sessionRespawnSection">
<div class="section-divider">
<div class="respawn-header">
<div class="session-respawn-status" id="sessionRespawnStatus">
<span class="respawn-status-indicator"></span>
<span class="respawn-status-text">Not active</span>
</div>
<div class="respawn-actions">
<button class="btn-toolbar btn-success btn-sm" onclick="app.enableRespawnFromModal()" id="modalEnableRespawnBtn">Enable</button>
<button class="btn-toolbar btn-danger btn-sm" onclick="app.stopRespawnFromModal()" id="modalStopRespawnBtn" style="display: none;">Stop</button>
</div>
</div>
<div class="form-row">
<label>Run Duration</label>
<label>Duration</label>
<div class="duration-presets">
<button type="button" class="duration-preset-btn" data-minutes="30" onclick="app.selectDurationPreset('30')">30m</button>
<button type="button" class="duration-preset-btn" data-minutes="60" onclick="app.selectDurationPreset('60')">1h</button>
@@ -450,92 +454,81 @@
</div>
</div>
</div>
<span class="form-hint">How long to run the respawn loop</span>
</div>
<div class="form-section-header">Prompts</div>
<div class="form-row">
<label>Update Prompt</label>
<input type="text" id="modalRespawnPrompt" value="update all the docs and CLAUDE.md" placeholder="Prompt to send when idle" onchange="app.autoSaveRespawnConfig()">
</div>
<div class="form-row">
<label>After Update Prompt</label>
<div class="checkbox-group-inline">
<label class="checkbox-inline">
<input type="checkbox" id="modalRespawnSendClear" checked onchange="app.autoSaveRespawnConfig()">
<span>Send /clear</span>
</label>
<label class="checkbox-inline">
<input type="checkbox" id="modalRespawnSendInit" checked onchange="app.autoSaveRespawnConfig()">
<span>Send /init</span>
</label>
</div>
</div>
<div class="form-row">
<label>Kickstart Prompt</label>
<input type="text" id="modalRespawnKickstart" value="" placeholder="Optional: prompt if /init doesn't trigger work" onchange="app.autoSaveRespawnConfig()">
<span class="form-hint">Sent only when /init completes but Claude stays idle</span>
</div>
<div class="form-row">
<label>Auto-Accept Plan Mode</label>
<div class="checkbox-group-inline">
<label class="checkbox-inline">
<input type="checkbox" id="modalRespawnAutoAccept" checked onchange="app.autoSaveRespawnConfig()">
<span>Accept plan mode approvals</span>
</label>
</div>
<span class="form-hint">Presses Enter to accept plan approvals and default question options when Claude is waiting</span>
</div>
<div class="respawn-actions">
<button class="btn-toolbar btn-success" onclick="app.enableRespawnFromModal()" id="modalEnableRespawnBtn">Enable Respawn</button>
<button class="btn-toolbar btn-danger" onclick="app.stopRespawnFromModal()" id="modalStopRespawnBtn" style="display: none;">Stop Respawn</button>
<div class="form-section-header">Behavior</div>
<div class="respawn-options-row">
<label class="checkbox-inline">
<input type="checkbox" id="modalRespawnSendClear" checked onchange="app.autoSaveRespawnConfig()">
<span>Send /clear</span>
</label>
<label class="checkbox-inline">
<input type="checkbox" id="modalRespawnSendInit" checked onchange="app.autoSaveRespawnConfig()">
<span>Send /init</span>
</label>
<label class="checkbox-inline">
<input type="checkbox" id="modalRespawnAutoAccept" checked onchange="app.autoSaveRespawnConfig()">
<span>Auto-accept prompts</span>
</label>
</div>
<span class="form-hint">Auto-accept presses Enter for plan approvals and default question options</span>
</div>
</div><!-- End respawn-tab -->
<!-- Context Management Tab Content -->
<div class="modal-tab-content hidden" id="context-tab">
<div class="form-row">
<label>Auto-Compact Context</label>
<div class="auto-clear-row">
<label class="checkbox-inline">
<input type="checkbox" id="modalAutoCompactEnabled" onchange="app.autoSaveAutoCompact()">
<span>Enable at</span>
</label>
<div class="input-suffix input-suffix-sm">
<input type="number" id="modalAutoCompactThreshold" value="110000" min="10000" max="500000" step="10000" onchange="app.autoSaveAutoCompact()">
<span>tokens</span>
<div class="form-section-header">Token Management</div>
<div class="context-settings-grid">
<div class="context-setting">
<div class="context-setting-header">
<label class="checkbox-inline">
<input type="checkbox" id="modalAutoCompactEnabled" onchange="app.autoSaveAutoCompact()">
<span>Auto-Compact</span>
</label>
<div class="input-suffix input-suffix-sm">
<input type="number" id="modalAutoCompactThreshold" value="110000" min="10000" max="500000" step="10000" onchange="app.autoSaveAutoCompact()">
<span>tokens</span>
</div>
</div>
<input type="text" id="modalAutoCompactPrompt" value="" placeholder="Focus prompt (optional)" onchange="app.autoSaveAutoCompact()">
<span class="form-hint">Summarizes context while preserving key information</span>
</div>
<input type="text" id="modalAutoCompactPrompt" value="" placeholder="Optional: focus prompt for compact" class="mt-sm" onchange="app.autoSaveAutoCompact()">
<span class="form-hint">Summarizes context while preserving key information</span>
</div>
<div class="form-row">
<label>Auto-Clear Context</label>
<div class="auto-clear-row">
<label class="checkbox-inline">
<input type="checkbox" id="modalAutoClearEnabled" onchange="app.autoSaveAutoClear()">
<span>Enable at</span>
</label>
<div class="input-suffix input-suffix-sm">
<input type="number" id="modalAutoClearThreshold" value="140000" min="10000" max="500000" step="10000" onchange="app.autoSaveAutoClear()">
<span>tokens</span>
<div class="context-setting">
<div class="context-setting-header">
<label class="checkbox-inline">
<input type="checkbox" id="modalAutoClearEnabled" onchange="app.autoSaveAutoClear()">
<span>Auto-Clear</span>
</label>
<div class="input-suffix input-suffix-sm">
<input type="number" id="modalAutoClearThreshold" value="140000" min="10000" max="500000" step="10000" onchange="app.autoSaveAutoClear()">
<span>tokens</span>
</div>
</div>
<span class="form-hint">Full context reset at threshold (use higher than compact)</span>
</div>
<span class="form-hint">Completely resets context (use higher threshold than compact)</span>
</div>
<div class="form-section-header">File Watching</div>
<div class="form-row form-row-switch">
<label>Image Watcher</label>
<label class="switch">
<input type="checkbox" id="modalImageWatcherEnabled" checked onchange="app.toggleSessionImageWatcher()">
<span class="slider"></span>
</label>
<span class="form-hint">Auto-popup new images (screenshots, etc.) in this session's directory</span>
<span class="form-hint">Auto-popup new images (screenshots, generated files) in this session's directory</span>
</div>
</div><!-- End context-tab -->
@@ -547,34 +540,34 @@
<input type="checkbox" id="modalRalphEnabled">
<span class="slider"></span>
</label>
<span class="form-hint">Enable Ralph / Todo tracker for this session (auto-detects loops and todos)</span>
<span class="form-hint">Auto-detects loops and todos in Claude output</span>
</div>
<div class="form-row">
<label>Completion Phrase</label>
<input type="text" id="modalRalphPhrase" placeholder="e.g., COMPLETE, DONE, FINISHED">
<span class="form-hint">The phrase Claude outputs when the loop is complete (without &lt;promise&gt; tags)</span>
<span class="form-hint">Phrase Claude outputs when loop is complete (without &lt;promise&gt; tags)</span>
</div>
<div class="form-row">
<label>Max Iterations</label>
<input type="number" id="modalRalphMaxIterations" value="0" min="0" max="1000">
<span class="form-hint">Maximum respawn cycles (0 = unlimited)</span>
</div>
<div class="form-row">
<label>Max Todos</label>
<input type="number" id="modalRalphMaxTodos" value="50" min="1" max="500">
<span class="form-hint">Maximum todos to track per session</span>
</div>
<div class="form-row">
<label>Todo Expiration</label>
<div class="input-suffix input-suffix-sm">
<input type="number" id="modalRalphTodoExpiration" value="60" min="1" max="1440">
<span>min</span>
<div class="form-section-header">Limits</div>
<div class="ralph-limits-grid">
<div class="form-col">
<label>Max Iterations</label>
<input type="number" id="modalRalphMaxIterations" value="0" min="0" max="1000">
<span class="form-hint">0 = unlimited</span>
</div>
<div class="form-col">
<label>Max Todos</label>
<input type="number" id="modalRalphMaxTodos" value="50" min="1" max="500">
<span class="form-hint">Per session</span>
</div>
<div class="form-col">
<label>Todo Expiration</label>
<div class="input-suffix input-suffix-sm">
<input type="number" id="modalRalphTodoExpiration" value="60" min="1" max="1440">
<span>min</span>
</div>
</div>
<span class="form-hint">Minutes before todos auto-expire</span>
</div>
<div class="ralph-config-actions">
@@ -1153,14 +1146,6 @@
<details class="advanced-options">
<summary>Advanced Options</summary>
<div class="advanced-options-content">
<div class="form-row form-row-switch">
<label>Auto-Start After Plan</label>
<label class="switch">
<input type="checkbox" id="ralphAutoStart">
<span class="slider"></span>
</label>
<span class="form-hint">Automatically start the loop when plan generation completes</span>
</div>
<div class="form-row form-row-switch">
<label>Enable Respawn</label>
<label class="switch">
+125 -4
View File
@@ -1754,6 +1754,8 @@ body {
.btn-toolbar.btn-claude svg {
flex-shrink: 0;
width: 12px;
height: 12px;
}
/* Shell Button - Special styling */
@@ -2492,6 +2494,21 @@ body {
overflow: hidden;
}
/* Fix CSS specificity: ID selector overrides class selector, so we need this explicit rule */
#summary-tab.hidden,
#respawn-tab.hidden,
#context-tab.hidden,
#ralph-tab.hidden {
display: none !important;
}
/* Also hide child elements when parent tab is hidden (belt and suspenders) */
#summary-tab.hidden .run-summary-filters,
#summary-tab.hidden .run-summary-timeline,
#summary-tab.hidden .run-summary-footer {
display: none !important;
}
#summary-tab .run-summary-filters {
flex-shrink: 0;
}
@@ -2958,13 +2975,35 @@ body {
width: 60px;
}
/* Respawn header with status and actions */
.respawn-header {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
margin-bottom: 0.75rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border);
}
.respawn-header .respawn-actions {
margin: 0;
padding: 0;
border: none;
}
/* Respawn options row - inline checkboxes */
.respawn-options-row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 0.25rem;
}
/* Respawn actions in modal */
.respawn-actions {
display: flex;
gap: 0.5rem;
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid var(--border);
}
.session-respawn-actions {
@@ -3003,7 +3042,89 @@ body {
}
.modal-tab-content.hidden {
display: none;
display: none !important;
}
/* Context Tab Settings Grid */
.context-settings-grid {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.context-setting {
padding: 0.875rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid var(--border);
border-radius: 6px;
}
.context-setting-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
margin-bottom: 0.625rem;
}
.context-setting-header .checkbox-inline span {
font-weight: 500;
}
.context-setting-header .input-suffix-sm {
flex-shrink: 0;
}
.context-setting-header .input-suffix-sm input {
width: 70px;
}
.context-setting input[type="text"] {
width: 100%;
padding: 0.375rem 0.5rem;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text);
font-size: 0.75rem;
}
.context-setting input[type="text"]:focus {
outline: none;
border-color: var(--accent);
}
.context-setting .form-hint {
margin-top: 0.5rem;
}
/* Ralph Limits Grid */
.ralph-limits-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem;
margin-bottom: 0.5rem;
}
@media (max-width: 500px) {
.ralph-limits-grid {
grid-template-columns: 1fr 1fr;
}
}
.ralph-limits-grid .form-col input[type="number"] {
width: 100%;
padding: 0.375rem 0.5rem;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text);
font-size: 0.8rem;
}
.ralph-limits-grid .form-col .form-hint {
font-size: 0.6rem;
}
/* Ralph Config Actions */
+19 -2
View File
@@ -14,7 +14,7 @@ import Fastify, { FastifyInstance, FastifyReply } from 'fastify';
import fastifyStatic from '@fastify/static';
import path, { join, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { existsSync, mkdirSync, writeFileSync, readdirSync, readFileSync } from 'node:fs';
import { existsSync, mkdirSync, writeFileSync, readdirSync, readFileSync, rmSync } from 'node:fs';
import fs from 'node:fs/promises';
import { execSync } from 'node:child_process';
import { homedir, totalmem, freemem, loadavg, cpus } from 'node:os';
@@ -2518,6 +2518,17 @@ NOW: Generate the implementation plan for the task above. Think step by step.`;
const resolvedBase = resolve(casesDir);
if (resolvedCase.startsWith(resolvedBase) && existsSync(casePath)) {
outputDir = join(casePath, 'ralph-wizard');
// Clear old ralph-wizard directory to ensure fresh prompts for each generation
// This prevents stale prompts from previous runs being shown when clicking on agents
if (existsSync(outputDir)) {
try {
rmSync(outputDir, { recursive: true, force: true });
console.log(`[API] Cleared old ralph-wizard directory: ${outputDir}`);
} catch (err) {
console.warn(`[API] Failed to clear ralph-wizard directory:`, err);
}
}
}
}
@@ -2665,11 +2676,17 @@ NOW: Generate the implementation plan for the task above. Think step by step.`;
});
// Read a specific ralph-wizard file
this.app.get('/api/cases/:caseName/ralph-wizard/file/:filePath', async (req) => {
// Cache disabled to ensure fresh prompts when starting new plan generations
this.app.get('/api/cases/:caseName/ralph-wizard/file/:filePath', async (req, reply) => {
const { caseName, filePath } = req.params as { caseName: string; filePath: string };
const casesDir = join(homedir(), 'claudeman-cases');
const casePath = join(casesDir, caseName);
// Prevent browser caching - prompts change between plan generations
reply.header('Cache-Control', 'no-store, no-cache, must-revalidate');
reply.header('Pragma', 'no-cache');
reply.header('Expires', '0');
// Security: Path traversal protection for case name
const resolvedCase = resolve(casePath);
const resolvedBase = resolve(casesDir);