From c7f8d98c190cea04fd5a5ea17deb937629335661 Mon Sep 17 00:00:00 2001 From: AndyMik90 Date: Sun, 14 Dec 2025 11:46:55 +0100 Subject: [PATCH] Improvement to let claude code decide parallell agents --- .claude/agents/subtask-worker.md | 63 -- CLAUDE.md | 10 +- auto-claude-ui/src/main/ipc-handlers.ts | 63 +- .../src/renderer/components/AppSettings.tsx | 539 ++++++++-------- .../src/renderer/components/Context.tsx | 1 + .../src/renderer/components/Sidebar.tsx | 46 +- .../src/renderer/components/Terminal.tsx | 91 ++- .../components/ui/full-screen-dialog.tsx | 144 +++++ .../src/renderer/styles/globals.css | 36 ++ auto-claude-ui/src/shared/types.ts | 2 +- auto-claude/agent.py | 44 +- auto-claude/implementation_plan.py | 32 +- auto-claude/prompts/coder.md | 29 + auto-claude/run.py | 66 +- auto-claude/task_tool.py | 412 ------------ tests/test_agent_architecture.py | 289 +++++++++ tests/test_critique_integration.py | 4 +- tests/test_followup.py | 42 +- tests/test_implementation_plan.py | 28 +- tests/test_parallel.py | 606 ------------------ tests/test_workspace.py | 68 +- 21 files changed, 1095 insertions(+), 1520 deletions(-) delete mode 100644 .claude/agents/subtask-worker.md create mode 100644 auto-claude-ui/src/renderer/components/ui/full-screen-dialog.tsx delete mode 100644 auto-claude/task_tool.py create mode 100644 tests/test_agent_architecture.py delete mode 100644 tests/test_parallel.py diff --git a/.claude/agents/subtask-worker.md b/.claude/agents/subtask-worker.md deleted file mode 100644 index d7869bef..00000000 --- a/.claude/agents/subtask-worker.md +++ /dev/null @@ -1,63 +0,0 @@ -# Subtask Worker Agent - -You are a focused implementation agent working on a single subtask within a larger feature build. Your job is to implement ONLY your assigned subtask, commit your changes, and report completion. - -## Core Responsibilities - -1. **Read** the subtask specification carefully -2. **Implement** ONLY the assigned subtask - nothing more, nothing less -3. **Verify** your implementation works (tests pass, build succeeds) -4. **Commit** your changes with a clear commit message -5. **Update** the implementation plan to mark the subtask as completed - -## Important Constraints - -### Stay Focused -- You are part of a **parallel build** - other agents may be working on other subtasks -- **DO NOT** modify files outside your subtask's scope -- **DO NOT** implement other subtasks, even if they seem related -- **DO NOT** refactor code unless explicitly required by your subtask - -### File Boundaries -- Your subtask specifies which files to modify/create -- Stick to those files unless absolutely necessary -- If you need to touch other files, they should be minimal changes (imports, exports) - -### Commit Strategy -- Make atomic commits for your subtask -- Use clear commit messages: `feat(subtask-id): description` -- Commit after verification passes - -## Workflow - -``` -1. Read subtask specification - └─> Understand what needs to be implemented - -2. Implement the subtask - └─> Write code following existing patterns - └─> Keep changes minimal and focused - -3. Verify the implementation - └─> Run relevant tests - └─> Check for lint errors - └─> Ensure build succeeds - -4. Commit changes - └─> git add - └─> git commit -m "feat(subtask-id): description" - -5. Update implementation plan - └─> Mark subtask status as "completed" - └─> Save the updated plan -``` - -## Communication - -- Report progress through the implementation plan JSON -- If you encounter blockers, update the subtask status to indicate the issue -- Keep your response focused - avoid lengthy explanations - -## Your Assigned Subtask - -[The orchestrator will inject the specific subtask details here at runtime] diff --git a/CLAUDE.md b/CLAUDE.md index 9c5e3ebe..ba831a27 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,9 +33,6 @@ python auto-claude/spec_runner.py --task "Fix button" --complexity simple # Run autonomous build python auto-claude/run.py --spec 001 -# Run with parallel workers -python auto-claude/run.py --spec 001 --parallel 2 - # List all specs python auto-claude/run.py --list ``` @@ -92,7 +89,7 @@ python auto-claude/validate_spec.py --spec-dir auto-claude/specs/001-feature --c **Implementation (run.py → agent.py)** - Multi-session build: 1. Planner Agent creates subtask-based implementation plan -2. Coder Agent implements subtasks (sequential or parallel via Task tool) +2. Coder Agent implements subtasks (can spawn subagents for parallel work) 3. QA Reviewer validates acceptance criteria 4. QA Fixer resolves issues in a loop @@ -101,7 +98,6 @@ python auto-claude/validate_spec.py --spec-dir auto-claude/specs/001-feature --c - **client.py** - Claude SDK client with security hooks and tool permissions - **security.py** + **project_analyzer.py** - Dynamic command allowlisting based on detected project stack - **worktree.py** - Git worktree isolation for safe feature development -- **task_tool.py** - Parallel subtask execution using Claude Code's Task tool - **memory.py** - File-based session memory (primary, always-available storage) - **graphiti_memory.py** - Optional graph-based cross-session memory with semantic search - **graphiti_providers.py** - Multi-provider factory for Graphiti (OpenAI, Anthropic, Azure, Ollama) @@ -144,14 +140,14 @@ main (user's branch) **Key principles:** - ONE branch per spec (`auto-claude/{spec-name}`) -- Parallel execution uses Claude Code's Task tool (not separate branches) +- Parallel work uses subagents (agent decides when to spawn) - NO automatic pushes to GitHub - user controls when to push - User reviews in spec worktree (`.worktrees/{spec-name}/`) - Final merge: spec branch → main (after user approval) **Workflow:** 1. Build runs in isolated worktree on spec branch -2. Parallel subtasks execute via Task tool (same branch) +2. Agent implements subtasks (can spawn subagents for parallel work) 3. User tests feature in `.worktrees/{spec-name}/` 4. User runs `--merge` to add to their project 5. User pushes to remote when ready diff --git a/auto-claude-ui/src/main/ipc-handlers.ts b/auto-claude-ui/src/main/ipc-handlers.ts index 929ada32..071d5ebd 100644 --- a/auto-claude-ui/src/main/ipc-handlers.ts +++ b/auto-claude-ui/src/main/ipc-handlers.ts @@ -2991,27 +2991,58 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' .slice(0, 10); // Last 10 specs for (const specDir of recentSpecDirs) { - // Look for session memory files const memoryDir = path.join(specsDir, specDir, 'memory'); if (existsSync(memoryDir)) { - const memoryFiles = readdirSync(memoryDir) - .filter((f: string) => f.endsWith('.json')) - .sort() - .reverse(); + // Load session insights from session_insights subdirectory + const sessionInsightsDir = path.join(memoryDir, 'session_insights'); + if (existsSync(sessionInsightsDir)) { + const sessionFiles = readdirSync(sessionInsightsDir) + .filter((f: string) => f.startsWith('session_') && f.endsWith('.json')) + .sort() + .reverse(); - for (const memFile of memoryFiles.slice(0, 3)) { + for (const sessionFile of sessionFiles.slice(0, 3)) { + try { + const sessionPath = path.join(sessionInsightsDir, sessionFile); + const sessionContent = readFileSync(sessionPath, 'utf-8'); + const sessionData = JSON.parse(sessionContent); + + // Session files have: session_number, timestamp, subtasks_completed, + // discoveries, what_worked, what_failed, recommendations_for_next_session + if (sessionData.session_number !== undefined) { + recentMemories.push({ + id: `${specDir}-${sessionFile}`, + type: 'session_insight', + timestamp: sessionData.timestamp || new Date().toISOString(), + content: JSON.stringify({ + discoveries: sessionData.discoveries, + what_worked: sessionData.what_worked, + what_failed: sessionData.what_failed, + recommendations: sessionData.recommendations_for_next_session, + subtasks_completed: sessionData.subtasks_completed + }, null, 2), + session_number: sessionData.session_number + }); + } + } catch { + // Skip invalid files + } + } + } + + // Also load codebase_map.json as a memory item + const codebaseMapPath = path.join(memoryDir, 'codebase_map.json'); + if (existsSync(codebaseMapPath)) { try { - const memPath = path.join(memoryDir, memFile); - const memContent = readFileSync(memPath, 'utf-8'); - const memData = JSON.parse(memContent); - - if (memData.insights) { + const mapContent = readFileSync(codebaseMapPath, 'utf-8'); + const mapData = JSON.parse(mapContent); + if (mapData.discovered_files && Object.keys(mapData.discovered_files).length > 0) { recentMemories.push({ - id: `${specDir}-${memFile}`, - type: 'session_insight', - timestamp: memData.timestamp || new Date().toISOString(), - content: JSON.stringify(memData.insights, null, 2), - session_number: memData.session_number + id: `${specDir}-codebase_map`, + type: 'codebase_map', + timestamp: mapData.last_updated || new Date().toISOString(), + content: JSON.stringify(mapData.discovered_files, null, 2), + session_number: undefined }); } } catch { diff --git a/auto-claude-ui/src/renderer/components/AppSettings.tsx b/auto-claude-ui/src/renderer/components/AppSettings.tsx index 5167de1b..1e2494c7 100644 --- a/auto-claude-ui/src/renderer/components/AppSettings.tsx +++ b/auto-claude-ui/src/renderer/components/AppSettings.tsx @@ -14,16 +14,22 @@ import { Key, Eye, EyeOff, - Info + Info, + Palette, + Bot, + FolderOpen, + Bell, + Package } from 'lucide-react'; import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle -} from './ui/dialog'; + FullScreenDialog, + FullScreenDialogContent, + FullScreenDialogHeader, + FullScreenDialogBody, + FullScreenDialogFooter, + FullScreenDialogTitle, + FullScreenDialogDescription +} from './ui/full-screen-dialog'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Label } from './ui/label'; @@ -37,6 +43,7 @@ import { SelectValue } from './ui/select'; import { Separator } from './ui/separator'; +import { cn } from '../lib/utils'; import { useSettingsStore, saveSettings, loadSettings } from '../stores/settings-store'; import { AVAILABLE_MODELS } from '../../shared/constants'; import type { @@ -51,12 +58,31 @@ interface AppSettingsDialogProps { onOpenChange: (open: boolean) => void; } +type SettingsSection = 'appearance' | 'agent' | 'paths' | 'api-keys' | 'framework' | 'notifications'; + +interface NavItem { + id: SettingsSection; + label: string; + icon: React.ElementType; + description: string; +} + +const navItems: NavItem[] = [ + { id: 'appearance', label: 'Appearance', icon: Palette, description: 'Theme and visual preferences' }, + { id: 'agent', label: 'Agent Settings', icon: Bot, description: 'Default model and parallelism' }, + { id: 'paths', label: 'Paths', icon: FolderOpen, description: 'Python and framework paths' }, + { id: 'api-keys', label: 'API Keys', icon: Key, description: 'Global API credentials' }, + { id: 'framework', label: 'Framework', icon: Package, description: 'Auto Claude updates' }, + { id: 'notifications', label: 'Notifications', icon: Bell, description: 'Alert preferences' } +]; + export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps) { const currentSettings = useSettingsStore((state) => state.settings); const [settings, setSettings] = useState(currentSettings); const [isSaving, setIsSaving] = useState(false); const [error, setError] = useState(null); const [version, setVersion] = useState(''); + const [activeSection, setActiveSection] = useState('appearance'); // Auto Claude source update state const [sourceUpdateCheck, setSourceUpdateCheck] = useState(null); @@ -164,82 +190,59 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps } }; - return ( - - - - - - Application Settings - - - Configure global application settings - - - - -
- {/* Appearance */} -
-

Appearance

-
- - -
-
- + const renderSection = () => { + switch (activeSection) { + case 'appearance': + return ( +
+
+

Appearance

+

Customize how Auto Claude looks

+
+
+
+ +

Choose your preferred color scheme

+
+ {(['system', 'light', 'dark'] as const).map((theme) => ( + + ))} +
+
+
+
+ ); - {/* Default Agent Settings */} -
-

Default Agent Settings

-
+ case 'agent': + return ( +
+
+

Default Agent Settings

+

Configure defaults for new projects

+
+ +
+
+

The AI model used for agent tasks

-
+
+

Number of concurrent agent workers (1-8)

setSettings({ @@ -267,64 +272,70 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps } />
-
+
+ + ); + case 'paths': + return ( +
+
+

Paths

+

Configure executable and framework paths

+
- - {/* Paths */} -
-

Paths

-
+
+
+

Path to Python executable (leave empty for default)

- setSettings({ ...settings, pythonPath: e.target.value }) - } + onChange={(e) => setSettings({ ...settings, pythonPath: e.target.value })} /> -

- Path to Python executable (leave empty for default) -

-
+
+

Relative path to auto-claude directory in projects

- setSettings({ ...settings, autoBuildPath: e.target.value }) - } + onChange={(e) => setSettings({ ...settings, autoBuildPath: e.target.value })} /> -

- Relative path to auto-claude directory in projects +

+
+
+ ); + + case 'api-keys': + return ( +
+
+

Global API Keys

+

Set API keys to use across all projects

+
+ +
+
+ +

+ Keys set here will be used as defaults. Individual projects can override these in their settings.

-
- - - - {/* Global API Keys */} -
-
- -

Global API Keys

-
-
-
- -

- Set API keys here to use them across all projects. Individual projects can override these in their settings. -

-
-
-
+
+
+
-
+

+ Get your token by running claude setup-token +

+
: }
-

- Get your token by running claude setup-token -

-
+
-
+

+ Required for Graphiti memory backend (embeddings) +

+
: }
-

- Required for Graphiti memory backend (embeddings) -

-
+
+ + ); + case 'framework': + return ( +
+
+

Auto Claude Framework

+

Manage framework updates and settings

+
- - {/* Auto Claude Framework Updates */} -
-

Auto Claude Framework

-
+
+
{isCheckingSourceUpdate ? ( -
- +
+ Checking for updates...
) : sourceUpdateCheck ? ( <>
-

- Current Version: {sourceUpdateCheck.currentVersion} +

+ Version {sourceUpdateCheck.currentVersion}

{sourceUpdateCheck.latestVersion && sourceUpdateCheck.updateAvailable && ( -

+

New version available: {sourceUpdateCheck.latestVersion}

)}
{sourceUpdateCheck.updateAvailable ? ( - + ) : ( - + )}
{sourceUpdateCheck.error && ( -

{sourceUpdateCheck.error}

+

{sourceUpdateCheck.error}

)} {!sourceUpdateCheck.updateAvailable && !sourceUpdateCheck.error && ( -

+

You're running the latest version of the Auto Claude framework.

)} {sourceUpdateCheck.updateAvailable && ( -
+
{sourceUpdateCheck.releaseNotes && ( -
+
                               {sourceUpdateCheck.releaseNotes}
                             
@@ -428,8 +442,8 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps )} {isDownloadingUpdate ? ( -
-
+
+
{downloadProgress?.message || 'Downloading...'}
@@ -438,20 +452,17 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps )}
) : downloadProgress?.stage === 'complete' ? ( -
- +
+ {downloadProgress.message}
) : downloadProgress?.stage === 'error' ? ( -
- +
+ {downloadProgress.message}
) : ( - @@ -460,33 +471,29 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps )} ) : ( -
- +
+ Unable to check for updates
)} -
+
- -

- Updates the bundled Auto Claude framework from GitHub. Individual projects can then be updated from Project Settings. -

-
-
- -

+

+
+ +

Automatically update Auto Claude in projects when a new version is available

@@ -497,94 +504,120 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps } />
-
- - - - {/* Notifications */} -
-

Default Notifications

-
-
- - - setSettings({ - ...settings, - notifications: { - ...settings.notifications, - onTaskComplete: checked - } - }) - } - /> -
-
- - - setSettings({ - ...settings, - notifications: { - ...settings.notifications, - onTaskFailed: checked - } - }) - } - /> -
-
- - - setSettings({ - ...settings, - notifications: { - ...settings.notifications, - onReviewNeeded: checked - } - }) - } - /> -
-
- - - setSettings({ - ...settings, - notifications: { - ...settings.notifications, - sound: checked - } - }) - } - /> -
-
-
- - {/* Error */} - {error && ( -
- {error} -
- )} - - {/* Version */} - {version && ( -
- Version {version} -
- )} +
-
+ ); - + case 'notifications': + return ( +
+
+

Notifications

+

Configure default notification preferences

+
+ +
+ {[ + { key: 'onTaskComplete', label: 'On Task Complete', description: 'Notify when a task finishes successfully' }, + { key: 'onTaskFailed', label: 'On Task Failed', description: 'Notify when a task encounters an error' }, + { key: 'onReviewNeeded', label: 'On Review Needed', description: 'Notify when QA requires your review' }, + { key: 'sound', label: 'Sound', description: 'Play sound with notifications' } + ].map((item) => ( +
+
+ +

{item.description}

+
+ + setSettings({ + ...settings, + notifications: { + ...settings.notifications, + [item.key]: checked + } + }) + } + /> +
+ ))} +
+
+ ); + } + }; + + return ( + + + + + + Settings + + + Configure application-wide settings and preferences + + + + +
+ {/* Navigation sidebar */} + + + {/* Main content */} +
+ +
+ {renderSection()} +
+
+
+
+
+ + + {error && ( +
+ {error} +
+ )} @@ -601,8 +634,8 @@ export function AppSettingsDialog({ open, onOpenChange }: AppSettingsDialogProps )} -
-
-
+ + + ); } diff --git a/auto-claude-ui/src/renderer/components/Context.tsx b/auto-claude-ui/src/renderer/components/Context.tsx index d5fc6c45..8e4bd94d 100644 --- a/auto-claude-ui/src/renderer/components/Context.tsx +++ b/auto-claude-ui/src/renderer/components/Context.tsx @@ -69,6 +69,7 @@ const serviceTypeColors: Record = { const memoryTypeIcons: Record = { session_insight: Lightbulb, codebase_discovery: FolderTree, + codebase_map: FolderTree, pattern: Code, gotcha: AlertTriangle }; diff --git a/auto-claude-ui/src/renderer/components/Sidebar.tsx b/auto-claude-ui/src/renderer/components/Sidebar.tsx index 3d34de09..cd9267b7 100644 --- a/auto-claude-ui/src/renderer/components/Sidebar.tsx +++ b/auto-claude-ui/src/renderer/components/Sidebar.tsx @@ -17,7 +17,8 @@ import { Github, FileText, Sparkles, - GitBranch + GitBranch, + HelpCircle } from 'lucide-react'; import { Button } from './ui/button'; import { ScrollArea } from './ui/scroll-area'; @@ -281,14 +282,6 @@ export function Sidebar({ Toggle theme - - - - - Settings - @@ -385,8 +378,39 @@ export function Sidebar({ - {/* New Task button */} -
+ {/* Bottom section with Settings, Help, and New Task */} +
+ {/* Settings and Help row */} +
+ + + + + Application Settings + + + + + + Help & Feedback + +
+ + {/* New Task button */}