2025-06-25 09:27:29 +01:00
use std ::path ::Path ;
2025-06-16 16:16:42 -04:00
use chrono ::{ DateTime , Utc } ;
2025-07-01 15:11:51 +01:00
use git2 ::{ BranchType , Error as GitError , RebaseOptions , Repository , WorktreeAddOptions } ;
2025-06-16 16:16:42 -04:00
use serde ::{ Deserialize , Serialize } ;
2025-06-17 15:22:47 -04:00
use sqlx ::{ FromRow , SqlitePool , Type } ;
2025-07-01 15:11:51 +01:00
use tracing ::{ debug , info } ;
2025-06-16 16:16:42 -04:00
use ts_rs ::TS ;
use uuid ::Uuid ;
2025-06-16 17:15:51 -04:00
2025-06-25 09:27:29 +01:00
use super ::{ project ::Project , task ::Task } ;
2025-06-27 16:18:35 +01:00
use crate ::{ executor ::Executor , utils ::shell ::get_shell_command } ;
2025-06-16 17:15:51 -04:00
#[ derive(Debug) ]
pub enum TaskAttemptError {
Database ( sqlx ::Error ) ,
Git ( GitError ) ,
TaskNotFound ,
ProjectNotFound ,
2025-06-24 16:50:58 +01:00
ValidationError ( String ) ,
2025-06-30 14:30:32 +01:00
BranchNotFound ( String ) ,
2025-06-16 17:15:51 -04:00
}
impl std ::fmt ::Display for TaskAttemptError {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
match self {
TaskAttemptError ::Database ( e ) = > write! ( f , " Database error: {} " , e ) ,
TaskAttemptError ::Git ( e ) = > write! ( f , " Git error: {} " , e ) ,
TaskAttemptError ::TaskNotFound = > write! ( f , " Task not found " ) ,
TaskAttemptError ::ProjectNotFound = > write! ( f , " Project not found " ) ,
2025-06-24 16:50:58 +01:00
TaskAttemptError ::ValidationError ( e ) = > write! ( f , " Validation error: {} " , e ) ,
2025-06-30 14:30:32 +01:00
TaskAttemptError ::BranchNotFound ( e ) = > write! ( f , " Branch not found: {} " , e ) ,
2025-06-16 17:15:51 -04:00
}
}
}
impl std ::error ::Error for TaskAttemptError { }
impl From < sqlx ::Error > for TaskAttemptError {
fn from ( err : sqlx ::Error ) -> Self {
TaskAttemptError ::Database ( err )
}
}
impl From < GitError > for TaskAttemptError {
fn from ( err : GitError ) -> Self {
TaskAttemptError ::Git ( err )
}
}
2025-06-16 16:16:42 -04:00
#[ derive(Debug, Clone, Type, Serialize, Deserialize, PartialEq, TS) ]
#[ sqlx(type_name = " task_attempt_status " , rename_all = " lowercase " ) ]
#[ serde(rename_all = " lowercase " ) ]
#[ ts(export) ]
pub enum TaskAttemptStatus {
2025-06-19 11:09:32 -04:00
SetupRunning ,
SetupComplete ,
SetupFailed ,
ExecutorRunning ,
ExecutorComplete ,
ExecutorFailed ,
2025-06-16 16:16:42 -04:00
}
#[ derive(Debug, Clone, FromRow, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub struct TaskAttempt {
pub id : Uuid ,
pub task_id : Uuid , // Foreign key to Task
pub worktree_path : String ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
pub branch : String , // Git branch name for this task attempt
pub base_branch : String , // Base branch this attempt is based on
2025-06-16 16:16:42 -04:00
pub merge_commit : Option < String > ,
2025-07-01 17:45:00 +01:00
pub executor : Option < String > , // Name of the executor to use
pub pr_url : Option < String > , // GitHub PR URL
pub pr_number : Option < i64 > , // GitHub PR number
pub pr_status : Option < String > , // open, closed, merged
pub pr_merged_at : Option < DateTime < Utc > > , // When PR was merged
2025-06-16 16:16:42 -04:00
pub created_at : DateTime < Utc > ,
pub updated_at : DateTime < Utc > ,
}
#[ derive(Debug, Deserialize, TS) ]
#[ ts(export) ]
pub struct CreateTaskAttempt {
2025-06-21 19:45:47 +01:00
pub executor : Option < String > , // Optional executor name (defaults to "echo")
2025-06-30 14:30:32 +01:00
pub base_branch : Option < String > , // Optional base branch to checkout (defaults to current HEAD)
2025-06-16 16:16:42 -04:00
}
#[ derive(Debug, Deserialize, TS) ]
#[ ts(export) ]
pub struct UpdateTaskAttempt {
2025-06-20 22:06:01 +01:00
// Currently no updateable fields, but keeping struct for API compatibility
2025-06-16 16:16:42 -04:00
}
2025-06-16 17:07:11 -04:00
2025-07-01 16:28:15 +01:00
/// GitHub PR creation parameters
pub struct CreatePrParams < ' a > {
pub attempt_id : Uuid ,
pub task_id : Uuid ,
pub project_id : Uuid ,
pub github_token : & ' a str ,
pub title : & ' a str ,
pub body : Option < & ' a str > ,
pub base_branch : Option < & ' a str > ,
}
2025-06-24 01:05:55 +01:00
#[ derive(Debug, Deserialize, TS) ]
#[ ts(export) ]
pub struct CreateFollowUpAttempt {
pub prompt : String ,
}
2025-06-16 23:47:16 -04:00
#[ derive(Debug, Clone, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub enum DiffChunkType {
Equal ,
Insert ,
Delete ,
}
#[ derive(Debug, Clone, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub struct DiffChunk {
pub chunk_type : DiffChunkType ,
pub content : String ,
}
#[ derive(Debug, Clone, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub struct FileDiff {
pub path : String ,
pub chunks : Vec < DiffChunk > ,
}
#[ derive(Debug, Clone, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub struct WorktreeDiff {
pub files : Vec < FileDiff > ,
}
2025-06-19 18:59:47 -04:00
#[ derive(Debug, Clone, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub struct BranchStatus {
pub is_behind : bool ,
pub commits_behind : usize ,
pub commits_ahead : usize ,
pub up_to_date : bool ,
2025-06-19 21:41:18 -04:00
pub merged : bool ,
2025-06-24 17:39:51 +01:00
pub has_uncommitted_changes : bool ,
2025-06-24 23:40:48 +01:00
pub base_branch_name : String ,
2025-06-19 18:59:47 -04:00
}
Redesign sidebar (#63)
* Agent logs
* ## Summary
I have successfully implemented a comprehensive log normalization system for vibe-kanban with the following features:
### ✅ **Completed Tasks:**
1. **Defined normalized log format types** - Created standard TypeScript-exportable types:
- `NormalizedConversation` - Top-level container with entries, session ID, and executor type
- `NormalizedEntry` - Individual conversation entries with timestamp, type, and content
- `NormalizedEntryType` - Union type for different entry types (user, assistant, tool use, system, thinking)
- `ActionType` - Specific action types for tool operations (file read/write, commands, search, etc.)
2. **Implemented normalize method for AMP executor** - Parses AMP's JSONL format to extract:
- User and assistant messages
- Thinking content
- Tool usage with intelligent action type detection
- Session ID (threadID)
- Timestamps from message metadata
3. **Implemented normalize method for Claude executor** - Parses Claude's stream-json format to extract:
- User and assistant messages
- Tool usage with action type detection
- System initialization messages
- Session ID
- Comprehensive tool mapping for Claude-specific tools
4. **Added normalize method to executor trait** - Created a default implementation that returns empty results for unsupported executors, allowing easy extension to other executor types
5. **Created new API endpoint for normalized logs** - Added `GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint that:
- Validates execution process belongs to the correct project
- Determines executor type automatically
- Applies appropriate normalization logic
- Returns standardized conversation format
6. **Tested the implementation** - Created comprehensive unit tests for both AMP and Claude normalization, verifying correct parsing of real log examples
### 🔧 **Technical Implementation Details:**
- **Intelligent Action Type Detection**: The system maps tool names to specific action types (file operations, commands, searches, etc.) based on tool names and input parameters
- **Cross-platform Compatibility**: Handles different parameter naming conventions between executors
- **Error Handling**: Robust error handling with detailed error messages for debugging
- **TypeScript Integration**: Full TypeScript type generation for frontend consumption
- **Extensible Design**: Easy to add new executors by implementing the `normalize_logs` method
### 🚀 **Usage:**
The new endpoint can be called to get a normalized view of any execution process logs:
```bash
GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs
```
This returns a standardized conversation format that the frontend can easily consume to display:
- User inputs and assistant responses
- Tool usage with specific action details (file paths, commands, etc.)
- Internal reasoning (thinking)
- System status messages
- Session tracking information
The normalized format provides a high-level, consistent representation of the conversation regardless of which executor (AMP, Claude, etc.) generated the original logs.
* Perfect! I have successfully implemented the normalized logs feature. Here's a summary of what was accomplished:
## Summary
✅ **Replaced manual log rendering with normalized logs API**
### Key Changes Made:
1. **Created a new `NormalizedConversationViewer` component** that:
- Fetches normalized logs from the backend API endpoint
- Displays conversation entries with appropriate icons for each type:
- 👤 User messages (blue)
- 🤖 Assistant messages (green)
- ⚙️ System messages (gray)
- 🧠 Thinking (purple)
- 👁️ File reads (orange)
- ✏️ File writes (red)
- 💻 Command execution (yellow)
- 🔍 Search operations (indigo)
- 🌐 Web fetches (cyan)
- ➕ Task creation (teal)
- Shows timestamps and entry type labels
- Handles loading and error states
2. **Updated `TaskActivityHistory` component** to:
- Use the new `NormalizedConversationViewer` instead of `ExecutionOutputViewer`
- Pass the required `projectId` prop
- Maintain the same expansion/collapse functionality
3. **Updated `TaskDetailsPanel`** to pass the `projectId` to the activity history
4. **Removed legacy components**:
- The complex `ExecutionOutputViewer` that manually parsed JSONL
- The `ConversationViewer` that handled format-specific parsing
- All the manual stdout/stderr rendering logic
### Benefits:
- **Cleaner codebase**: Eliminated complex frontend parsing logic
- **Consistent display**: All executors now show logs in the same format
- **Better UX**: Clear icons and labels make it easy to understand conversation flow
- **Maintainable**: Backend handles format normalization, frontend just displays
- **Extensible**: New executor types automatically work with the existing UI
The implementation leverages the existing `/api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint and properly handles loading states, errors, and displays the conversation history with meaningful icons for each entry type.
* Excellent! The build is successful and the bundle size is slightly smaller after removing the unused code. The task is now complete:
✅ **Legacy log rendering components successfully removed**
- Deleted `ExecutionOutputViewer.tsx` (224 lines)
- Deleted `ConversationViewer.tsx` (complex JSONL parsing logic)
- Build passes and bundle size reduced
- All functionality now uses the clean normalized logs API
The codebase is now cleaner and fully transitioned to using the normalized logs system.
* Perfect! I've successfully refactored the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) component with the new layout:
## Key Changes Made:
1. **Top 2/3 - Code Changes Section**:
- Extracted all the diff processing logic from [`task-attempt-compare.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/pages/task-attempt-compare.tsx)
- Shows file diffs with syntax highlighting, expandable sections, and line numbers
- Includes file deletion functionality
- "Show/Hide All Unchanged" toggle for better navigation
2. **Bottom 1/3 - Agent Logs Section**:
- Preserved the existing [`TaskActivityHistory`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskActivityHistory.tsx) component
- Auto-scrolling behavior for new log entries
- Clear separation with border and background styling
3. **Maintained Existing Functionality**:
- Header, toolbar, and follow-up sections remain unchanged
- Editor selection dialog and ESC key handling preserved
- All existing hooks and state management intact
The component now provides a much better developer experience by showing code changes prominently in the top section while keeping the agent logs easily accessible in the bottom third.
* Perfect! The build is successful. I've implemented all the requested style changes to the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
**Code Changes Section (Top 2/3):**
- ✅ Removed "Code Changes" header
- ✅ Removed description text
- ✅ Removed "Show All Unchanged" button and related functionality
- ✅ Removed parent Card wrapper - each file diff is now directly displayed in its own card
**Agent Logs Section (Bottom 1/3):**
- ✅ Removed "Agent Logs" header
- ✅ Removed description text
- ✅ Now displays only the `NormalizedConversationViewer` component
- ✅ Removed card/container wrapper
The component now has a cleaner, more streamlined interface with the code changes taking up most of the space in the top section, and the agent conversation displayed directly in the bottom third without extra headers or containers.
* Perfect! The build is successful. I've updated the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) to properly find and display the coding agent conversation.
## Changes Made:
The component now:
1. **First looks** for any running coding agent process in `runningProcessDetails`
2. **If not found**, searches through all processes in the `processes` array to find any coding agent process (completed or otherwise)
3. **Creates a minimal ExecutionProcess object** from the summary data if needed, since the `NormalizedConversationViewer` only requires the process ID to fetch the conversation data
4. **Displays the conversation** for any coding agent process found, whether running or completed
This should resolve the "Unsupported executor type: unknown" error and properly show the normalized conversation logs from the coding agent in the bottom section.
* Perfect! I've successfully updated the [`NormalizedConversationViewer.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/NormalizedConversationViewer.tsx) component with the requested style changes:
## Changes Made:
1. **Removed metadata header** - No longer shows executor type, session ID, or process type badges
2. **Removed Card wrapper** - The conversation entries are now displayed directly without any card container or special background
3. **Removed timestamps** - Each entry no longer shows the date/time information
4. **Cleaned up imports** - Removed unused imports for Card, CardContent, and Badge components
The normalized logs now display as a clean, minimal conversation with just the entry type icon, entry type label, and content for each message in the agent conversation.
* Perfect! I've made the requested style improvements to the diffs in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Removed max height limit** - Each file diff card no longer has a `max-h-[400px] overflow-y-auto` constraint, so the entire diff content is visible without individual scrolling per file
2. **Enhanced diff line colors** - Updated the background colors for added and removed lines:
- **Added lines** (Insert): Now use `bg-green-100` (light mode) and `bg-green-900/40` (dark mode) with stronger green text colors
- **Removed lines** (Delete): Now use `bg-red-100` (light mode) and `bg-red-900/40` (dark mode) with stronger red text colors
- Made the border colors more prominent with `border-green-500` and `border-red-500`
The diffs now display with clearer visual distinction between added and removed lines, and each file shows its complete content without requiring individual scrolling.
* Perfect! I've fixed the line number background colors in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added `getLineNumberClassName` function** - This creates appropriate styling for line numbers based on the chunk type:
- **Added lines**: Green background (`bg-green-50`/`bg-green-900/20`) with green text and borders
- **Removed lines**: Red background (`bg-red-50`/`bg-red-900/20`) with red text and borders
- **Unchanged lines**: Gray background (original styling)
2. **Updated line number div** - Replaced the hardcoded gray styling with a call to `getLineNumberClassName(line.chunkType)`
Now the line numbers will have matching background colors with their corresponding lines - green for additions, red for deletions, and gray for unchanged context lines. This creates a more cohesive visual experience for the diff display.
* Perfect! I've updated the line number background colors to match the main line content exactly:
## Changes Made:
- **Added lines**: Line numbers now use `bg-green-100 dark:bg-green-900/40` (same as the main content)
- **Removed lines**: Line numbers now use `bg-red-100 dark:bg-red-900/40` (same as the main content)
Now the line numbers have the same background intensity as the main line content, creating a consistent visual appearance across the entire diff line without the lighter/more transparent look.
* Perfect! I've made all the requested improvements to the diff styling in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added horizontal scrolling** - The diff container now has `overflow-x-auto` and each line has `min-w-max` to ensure long lines can be scrolled horizontally
2. **Removed thick border** - Removed the `border-l-2 border-green-500` and `border-l-2 border-red-500` thick left borders from added/removed lines
3. **GitHub-style background intensity** - Now follows GitHub's pattern:
- **Line content**: Lighter background (`bg-green-50`/`bg-red-50` for light mode, `bg-green-900/20`/`bg-red-900/20` for dark mode)
- **Line numbers**: Darker background (`bg-green-100`/`bg-red-100` for light mode, `bg-green-900/40`/`bg-red-900/40` for dark mode)
The diff now has a cleaner appearance without the heavy borders, proper horizontal scrolling for long lines, and follows GitHub's visual hierarchy where line numbers have more prominent background colors than the content.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Added `py-1` to the `getLineNumberClassName` function so the line number background now has the same vertical padding as the content lines, ensuring full height coverage
2. **Fixed horizontal scrolling layout** - Wrapped the diff content in a proper container structure:
- Added `min-w-full` wrapper inside the `overflow-x-auto` container
- This ensures that elements like horizontal lines and borders aren't cut off during horizontal scrolling
- The scrolling now works properly without affecting other page elements
The diff now has proper horizontal scrolling that doesn't interfere with other page elements, and the line number backgrounds extend to the full height of each line.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Both the line numbers and content now use `py-1` consistently, ensuring the backgrounds match in height perfectly
2. **Fixed horizontal scrolling** - Changed the approach:
- Used `whitespace-nowrap` instead of `whitespace-pre` to prevent wrapping
- Used `min-w-max` on each line to ensure they extend naturally
- The overflow scrolling now works at the container level without cutting off other elements
- Removed the problematic nested wrapper structure
The line number backgrounds now extend to the full height of each line, and horizontal scrolling works properly without affecting other page elements like borders and buttons.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height gaps**:
- Used `min-h-[1.75rem] flex items-center` for both line numbers and content divs
- This ensures both backgrounds have exactly the same height with no gaps above or below
- Removed separate `py-1` padding that was causing misalignment
2. **Fixed horizontal scroll border cutoff**:
- Added `min-w-max` to the "show X more lines" buttons
- This ensures the buttons extend to the full width of the scrollable content
- The borders now remain visible even when scrolling horizontally
Now the line number backgrounds perfectly match the line content height with no color gaps, and the expand/collapse buttons maintain their borders during horizontal scrolling.
* Perfect! I've fixed both remaining horizontal scroll issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed highlighted line backgrounds during scroll**:
- Changed from `min-w-max` to `w-full` for the base class
- Added `style={{ minWidth: 'max-content' }}` to ensure lines extend to natural content width
- Used `inline-block min-w-full` container to create proper scrollable width
- Now all highlighted lines (green/red) maintain their background color across the full scrollable width
2. **Fixed "show more lines" button borders**:
- Wrapped buttons in `div className="w-full"` containers
- Removed `min-w-max` from button classes and used `w-full`
- The buttons now properly extend across the full scrollable width
- Borders remain visible during horizontal scrolling
Now the diff view has proper horizontal scrolling where:
- All highlighted lines maintain their background colors across the entire scroll width
- The "show X more lines" buttons maintain their borders when scrolling
- The entire diff content scrolls cohesively
* Perfect! I've fixed the indentation issue in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Fixed indentation preservation**: Changed from `whitespace-nowrap` to `whitespace-pre` in the `getChunkClassName` function
Now the diff properly preserves:
- Code indentation (spaces and tabs)
- All whitespace formatting
- Line spacing as it appears in the original code
The horizontal scrolling functionality remains intact while properly displaying the code formatting and indentation structure.
* Perfect! I've right-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Added `justify-end`** to both "Show X more lines" and "Hide expanded lines" buttons
Now the button text ("Show X more lines" and "Hide expanded lines") is right-aligned, which means:
- The text stays visible and consistent during horizontal scrolling
- Users can always see the action text regardless of scroll position
- The interface feels more stable when navigating wide diffs
* Perfect! I've left-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Changed `justify-end` to `justify-start`** for both "Show X more lines" and "Hide expanded lines" buttons
Now the button text is left-aligned, which means:
- The text appears at the left edge of the button and stays visible during horizontal scrolling
- Users can always see the action text at a consistent position
- The interface aligns with the left-aligned line numbers below it
* ## Summary
I've successfully improved the normalized logs to show more concise content. Here's what was changed:
### Key Improvements
1. **File Operations**: Now show just the file path (e.g., `"src/components/TaskDialog.tsx"`) instead of verbose tool descriptions
2. **Commands**: Now show just the bash command (e.g., `"npm run build"`) instead of full tool invocation details
3. **Search Operations**: Now show just the search query (e.g., `"authentication headers"`) instead of tool metadata
4. **Web Requests**: Now show just the URL (e.g., `"https://api.example.com/data"`) instead of verbose descriptions
5. **Other Tools**: Now show concise, meaningful descriptions (e.g., `"Managing TODO list"` for todo tools)
### Changes Made
1. **Updated AMP Executor** (`backend/src/executors/amp.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
2. **Updated Claude Executor** (`backend/src/executors/claude.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
3. **Updated Documentation** (`test_normalized_logs_api.md`):
- Updated example response to show the new concise format
- Added detailed explanation of the new content format with examples
4. **Added Tests**:
- Enhanced existing tests to verify the new concise content format
- Tests confirm that `todo_write` shows "Managing TODO list" instead of verbose JSON
- Tests confirm that `Task` tool shows the task description directly
### Before and After
**Before:**
```json
{
"content": "Tool: todo_write with input: {\"todos\":[{\"id\":\"1\",\"content\":\"Explore task creation dialog component\",\"status\":\"todo\",\"priority\":\"high\"}]}"
}
```
**After:**
```json
{
"content": "Managing TODO list"
}
```
The implementation now provides much cleaner, more actionable content in the normalized conversation logs while maintaining all the rich metadata in the `action_type` field for programmatic access.
* Perfect! The frontend builds successfully. Now the normalized conversation logs will display much cleaner - just showing the appropriate icon next to the concise content without any redundant headers.
## Summary
I've removed the headers from the normalized conversation log display. Now instead of showing:
**Before:**
```
[file icon] Read: src/components/TaskDialog.tsx
src/components/TaskDialog.tsx
[command icon] Command: npm run build
npm run build
```
**After:**
```
[file icon] src/components/TaskDialog.tsx
[command icon] npm run build
[search icon] authentication headers
[todo icon] Managing TODO list
```
### Changes Made
1. **Removed redundant header display** - The `getEntryTypeDisplay` function and its usage has been removed
2. **Simplified layout** - Each log entry now shows just the icon and concise content directly
3. **Cleaner UI** - No more redundant information since the content is already concise and the icon provides the context
The display is now much cleaner and more efficient, with the icon providing the visual context for the action type and the concise content providing the essential information.
* Perfect! The frontend builds successfully.
## Summary
I've updated the NormalizedConversationViewer to render terminal commands with a monospace font. Now:
### Changes Made
1. **Added `getContentClassName` function** - This function conditionally applies styling based on the entry type
2. **Applied monospace font for commands** - When the entry type is a tool_use with action_type of "command_run", it applies the `font-mono` Tailwind class
3. **Maintained existing styling** - All other entry types keep the same styling as before
### Result
Now terminal commands like:
- `npm run build`
- `cargo check`
- `git status`
- `ls -la`
Will be displayed in a monospace font next to the terminal icon, making them more readable and clearly distinguishable as code/commands, while other content like file paths, search queries, and regular text continue to use the default font.
The monospace font makes commands much easier to read and gives them the appropriate technical appearance that users expect for terminal output.
* Excellent! Everything builds successfully.
## Summary
I've implemented real-time conversation updates with smart auto-scrolling functionality. Here's what was added:
### Key Features
1. **Auto-refresh**: The conversation automatically polls for updates every 2 seconds when the execution process is running
2. **Smart auto-scrolling**: Automatically scrolls to the bottom when new content arrives
3. **User scroll detection**: Stops auto-scrolling when the user manually scrolls up
4. **Auto-scroll resumption**: Resumes auto-scrolling when the user scrolls back to the bottom
5. **Efficient updates**: Only updates the conversation state when content actually changes
### Implementation Details
**Auto-refresh Logic:**
- Polls every 2 seconds when `executionProcess.status === 'running'`
- Uses efficient content comparison to avoid unnecessary re-renders
- Gracefully handles polling errors without disrupting the UI
**Smart Scrolling:**
- Tracks user scroll behavior vs automatic scrolling
- Uses a 5px threshold to detect "at bottom" state
- Provides smooth auto-scrolling with a small delay to ensure DOM updates
**User Experience:**
- Container has `max-h-96` with `overflow-y-auto` for scrollable content
- Terminal commands display in monospace font
- Clean display with just icons and concise content
- No disruption when user wants to review previous logs
### Behavior
- ✅ **Auto-updates**: Content refreshes every 2 seconds during execution
- ✅ **Auto-scroll**: New content automatically scrolls into view
- ✅ **Manual control**: User can scroll up to review previous logs
- ✅ **Resume auto-scroll**: When user scrolls back to bottom, auto-scrolling resumes
- ✅ **Efficient**: Only re-renders when content actually changes
- ✅ **Clean UI**: Terminal commands in monospace, no redundant headers
The conversation viewer now provides a seamless, real-time experience that follows the execution while respecting user control over scrolling behavior.
* ## Summary
I've successfully fixed the scroll-within-scroll issue in the TaskDetailsPanel. Here's what was changed:
**Problem**: The normalized logs section had nested scrolling containers:
- Parent container (TaskDetailsPanel) with `h-full overflow-y-auto`
- Child container (NormalizedConversationViewer) with `max-h-96 overflow-y-auto`
**Solution**: I removed the scrolling functionality from the `NormalizedConversationViewer` component by:
1. **Removed scroll container**: Changed from `<div ref={scrollContainerRef} className="space-y-2 max-h-96 overflow-y-auto" onScroll={handleScroll}>` to just `<div className="space-y-2">`
2. **Cleaned up scroll-related code**: Removed:
- State variables: `isUserScrolling`, `scrollContainerRef`, `scrollTimeoutRef`, `lastScrollTop`
- Functions: `isAtBottom`, `scrollToBottom`, `handleScroll`
- Effects: Auto-scroll on conversation updates and scroll timeout cleanup
- Unused imports: `useRef`
3. **Verified functionality**: The parent container in TaskDetailsPanel now handles all scrolling for the logs section, eliminating the annoying nested scroll behavior.
The build and lint checks pass successfully, and the user experience should now be much smoother without the competing scroll areas.
* ## Summary
I've successfully fixed the scroll-within-scroll issue while preserving the auto-scroll functionality. Here's what I implemented:
**Problem**: Nested scroll containers created annoying scroll-within-scroll behavior:
- Parent: TaskDetailsPanel with `h-full overflow-y-auto`
- Child: NormalizedConversationViewer with `max-h-96 overflow-y-auto`
**Solution**: Moved all scroll logic to the parent container while maintaining auto-scroll behavior:
### Changes Made:
1. **Removed inner scroll container** from NormalizedConversationViewer:
- Removed `max-h-96 overflow-y-auto` classes
- Removed all scroll-related state and functions
- Simplified to just `<div className="space-y-2">`
2. **Added callback interface** to NormalizedConversationViewer:
- Added `onConversationUpdate?: () => void` prop
- Called this callback when conversation data changes
- Used `setTimeout` to ensure state updates happen before auto-scroll
3. **Enhanced TaskDetailsPanel scroll logic**:
- Added `conversationUpdateTrigger` state to track conversation changes
- Added `handleConversationUpdate` callback function
- Extended existing auto-scroll effect to include conversation updates
- Maintained existing manual scroll detection logic
### Key Benefits:
✅ **No more nested scrolling** - Only the parent container scrolls
✅ **Auto-scroll preserved** - New messages still auto-scroll to bottom
✅ **Manual scroll respected** - Auto-scroll stops when user manually scrolls up
✅ **Smart resumption** - Auto-scroll resumes when user scrolls back to bottom
✅ **Seamless integration** - Works with existing activity and process log auto-scroll
The solution maintains all existing functionality while providing a much better user experience by eliminating the competing scroll areas.
* ## Summary
I've successfully updated the task attempts diff endpoint to include unstaged changes in addition to the existing committed changes. Here's what was implemented:
### Key Changes Made:
1. **Enhanced `get_diff` method** in [`backend/src/models/task_attempt.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-4fbf-update-dif/backend/src/models/task_attempt.rs#L1042-L1379):
- Added logic to capture unstaged changes using `diff_tree_to_workdir_with_index`
- For files with both committed and unstaged changes, creates a combined diff from the base branch to the current working directory
- For files with only unstaged changes, generates a standard diff
2. **New helper function** `process_unstaged_file`:
- Handles the processing of individual unstaged files
- Manages the complex logic of combining committed and unstaged changes
- Uses Git's native patch generation for accurate diff representation
### How It Works:
**For merged task attempts:** Shows the original merge commit diff (unchanged behavior)
**For active task attempts:**
1. **First pass:** Generates diff from base branch to current HEAD commit (committed changes)
2. **Second pass:** Finds files with unstaged changes in the working directory
3. **Combining logic:**
- **Files with both committed & unstaged changes:** Creates a combined diff from base branch directly to working directory content
- **Files with only unstaged changes:** Adds them as separate diff entries
- **Files with only committed changes:** Keeps original diff (unchanged)
### Benefits:
- ✅ **Complete visibility:** Users can now see both committed and uncommitted work
- ✅ **Unified view:** All changes are presented in a single coherent diff
- ✅ **Backward compatible:** Existing functionality for merged attempts remains unchanged
- ✅ **Accurate representation:** Uses Git's native diff algorithms for consistent formatting
The implementation maintains the existing API structure while extending functionality to provide a more comprehensive view of all changes in a task attempt.
* Perfect! I have successfully implemented the dynamic task details section that updates based on execution state. Here's a summary of what was accomplished:
## Summary
I implemented a comprehensive solution to make the details section dynamically update based on the execution state:
### Backend Changes:
1. **Added new execution state types** in `backend/src/models/task_attempt.rs`:
- `ExecutionState` enum with states: NotStarted, SetupRunning, SetupComplete, SetupFailed, CodingAgentRunning, CodingAgentComplete, CodingAgentFailed, Complete
- `TaskAttemptState` struct containing execution state, change detection, and process IDs
2. **Created `get_execution_state` method** that analyzes:
- Project setup script configuration
- Running execution processes
- Process status and completion
- Whether changes have been made to the worktree
3. **Added new API endpoint** `GET /projects/:project_id/tasks/:task_id/attempts/:attempt_id` to fetch execution state
4. **Updated type generation** to include the new types in the shared TypeScript definitions
### Frontend Changes:
1. **Enhanced useTaskDetails hook** to:
- Poll the execution state endpoint every 2 seconds
- Fetch execution state when switching attempts
- Provide execution state data to components
2. **Completely redesigned TaskDetailsPanel** with dynamic content rendering:
- **Setup Script Running**: Shows loading spinner with "Setup Script Running" message
- **Setup Failed**: Shows error message
- **Coding Agent Running (no changes)**: Shows full-height agent conversation only
- **Changes Available**: Shows 2/3 changes + 1/3 conversation split view
- **Default**: Shows "execution not started" message
### Key Features:
- **Real-time Updates**: Polls execution state every 2 seconds during execution
- **Progressive Disclosure**: Shows appropriate content based on execution phase
- **State-Aware UI**: Different layouts for different execution states
- **Error Handling**: Proper error states for failed setup scripts
- **Type Safety**: Full TypeScript support for execution states
The implementation ensures that users see relevant information at each stage of task execution, providing better visibility into what's happening and reducing confusion about the current state.
* Perfect! The fix is now in place. Here's what I've done to solve the loading spinner issue:
## Fix Summary
The problem was that the execution state was only being fetched during:
1. Polling (when `isAttemptRunning` is true)
2. Manual attempt switching via `handleAttemptChange`
But it was **not** being fetched during the initial page load when an attempt was automatically selected.
## Changes Made
1. **Added useEffect for selectedAttempt changes**: This ensures that whenever `selectedAttempt` changes (including during initial load), both `fetchAttemptData` and `fetchExecutionState` are called.
2. **Updated fetchTaskAttempts**: Added `fetchExecutionState` call when setting the latest attempt during initial load, and included it in the dependency array.
## How the Fix Works
Now when the page reloads:
1. `fetchTaskAttempts` runs and finds the latest attempt
2. It sets `selectedAttempt` to the latest attempt
3. The new useEffect detects the `selectedAttempt` change and immediately calls both:
- `fetchAttemptData(selectedAttempt.id)`
- `fetchExecutionState(selectedAttempt.id)`
4. The execution state is loaded, so the appropriate content is shown instead of the loading spinner
This ensures that the execution state is always available when displaying attempt details, whether it's the initial load, manual switching between attempts, or during polling updates.
* Perfect! The changes have been successfully implemented. Here's a summary of what I've done:
## Changes Made
### 1. Setup Script Stdio Display
- **Modified the setup running state** to show the actual stdout/stderr of the setup script instead of just a spinner
- **Enhanced process lookup** to find the setup script execution process from either the execution state's `setup_process_id` or by searching for processes with type `setupscript`
- **Added terminal-style output display** with:
- Black background and green text (terminal aesthetic)
- Monospace font for proper formatting
- Scrollable area with max height
- Fallback to "No output yet..." when no output is available
### 2. Diff Refresh During Coding Agent Execution
- **Added automatic diff polling** when the coding agent is running
- **Polls every 3 seconds** during `CodingAgentRunning` state to catch file changes as they happen
- **Uses existing fetchDiff function** to maintain consistency with manual refresh behavior
### 3. Enhanced Process Data Fetching
- **Modified fetchAttemptData** to always fetch setup script process details when available
- **Ensures setup script output is available** even when the script has completed but we want to show its output
- **Maintains existing logic** for other running processes while adding setup script support
## Key Features
1. **Real-time Setup Script Output**: Users can now see exactly what the setup script is doing, including any errors or progress messages.
2. **Live Diff Updates**: When the coding agent is making changes, the diff view automatically refreshes every 3 seconds so users can see files being modified in real-time.
3. **Better Visual Feedback**: The terminal-style output for setup scripts provides a familiar development experience.
4. **Non-blocking Updates**: The diff refresh happens independently and doesn't interfere with other UI interactions.
The implementation ensures that users get immediate feedback during both phases of execution - setup and coding - making the development process much more transparent and engaging.
* Perfect! Both issues have been fixed:
## Summary of Fixes
### 1. Setup Script Output Display
**Fixed**: Combined stdout and stderr properly
- **Before**: Only showed stdout OR stderr (whichever existed first)
- **After**: Shows both stdout AND stderr concatenated together
- **Implementation**:
```javascript
const stdout = setupProcess.stdout || '';
const stderr = setupProcess.stderr || '';
const combined = [stdout, stderr].filter(Boolean).join('\n');
return combined || 'No output yet...';
```
### 2. Automatic Diff Refresh During Coding Agent Execution
**Fixed**: Added proper diff polling with immediate refresh
- **Before**: Diff wasn't automatically updating during coding agent execution
- **After**: Diff refreshes immediately when coding agent starts + every 2 seconds while running
- **Implementation**:
- Immediate `fetchDiff()` call when entering `CodingAgentRunning` state
- `setInterval` to refresh every 2 seconds during execution
- Proper cleanup when state changes or component unmounts
### 3. Enhanced Setup Process Data Fetching
**Improved**: Better process data availability
- **Enhancement**: Modified `fetchAttemptData` to always fetch setup script process details
- **Benefit**: Ensures setup script output is available even after completion
- **Implementation**: Added dedicated setup script process fetching logic
## Key Improvements
1. **Real-time Setup Feedback**: Users can now see the complete output (both stdout and stderr) from setup scripts as they run
2. **Live Diff Updates**: File changes appear automatically every 2 seconds during coding agent execution
3. **Immediate Feedback**: Diff refreshes immediately when coding agent starts, then continues polling
4. **Better Error Visibility**: Setup script errors (stderr) are now visible alongside normal output
The implementation ensures that users get comprehensive, real-time feedback during both setup and coding phases, making the development process much more transparent and responsive.
* The styles have been cleaned up:
1. **Fixed the "No logs available" message**: Changed "No output yet..." to "Waiting for setup script output..." to indicate this is a loading state rather than an error.
2. **Simplified setup script display**: Removed the terminal UI styling including:
- The loading spinner and centered layout
- The bordered card wrapper
- The "Setup Script Output" header
- The black terminal background with green text
- Applied simple monospaced font styling similar to normalized logs
The setup script now displays with a cleaner, more consistent style that matches the rest of the interface while still being clearly readable with monospaced font.
* Added auto-scroll functionality to the setup script section:
1. **Added setup scroll ref**: Created `setupScrollRef` to track the setup script container
2. **Added auto-scroll effect**: Created a `useEffect` that monitors `attemptData.runningProcessDetails` and automatically scrolls to the bottom when the setup script output updates
3. **Applied ref to container**: Added the ref to the setup script container div
The setup script logs will now automatically scroll to the bottom as new output is received, matching the behavior of the normalized logs.
* Perfect! I've successfully implemented the follow up executor visualization in the TaskDetailsPanel. Here's what I did:
## Summary of Changes
1. **Identified follow up executor data structure**: Follow up executors are identified by having `process_type: "codingagent"` and `command: "followup_executor"` (vs main executor with `command: "executor"`)
2. **Modified TaskDetailsPanel logic**: Updated both the full-screen view (when no changes) and the split view (when changes are present) to:
- Find the main coding agent process (command: "executor")
- Find all follow up executor processes (command: "followup_executor")
- Display them in sequence with the main executor first, followed by follow up executors
3. **Enhanced UI presentation**: Each follow up executor is displayed with:
- A clear separator (border-top)
- A header showing "Follow-up #X" with the sequence number
- The start time of the follow up executor
- The normalized conversation viewer for that executor's logs
4. **Tested the implementation**: Both frontend and backend build successfully, and the code passes linting checks.
The implementation now properly handles and visualizes follow up executors by showing them under the main executor in chronological order, making it clear which conversation belongs to which executor while maintaining the existing functionality for the main coding agent.
* ## Issue Fixed!
I found and fixed the bug. The problem was in the TaskDetailsPanel logic that determines when to show the conversation logs vs. the fallback "Task execution not started yet" message.
### Root Cause
The condition `if (isCodingAgentRunning && !hasChanges)` only handled the case when the coding agent was *running*, but after follow up executors complete, the execution state becomes `CodingAgentComplete`. This caused the logic to fall through to the default case, showing "Task execution not started yet" instead of the conversation logs.
### Fix Applied
I modified the condition to also handle the completed state:
- Added `isCodingAgentComplete` variable to track when execution is complete
- Updated the condition to `if ((isCodingAgentRunning || isCodingAgentComplete) && !hasChanges)`
This ensures that both running and completed follow up executors will properly display their conversation logs, whether they're still running or have finished execution.
The fix is minimal and targeted, only affecting the specific scenario where follow up executors complete without generating file changes, while preserving all other functionality.
* ## Perfect! All styling changes implemented successfully!
Here's a summary of what I've accomplished:
### ✅ Backend Changes
1. **Extended NormalizedConversation structure** to include `prompt` and `summary` fields
2. **Updated all executor implementations** (Claude, Amp, default) to support the new fields
3. **Modified the normalized-logs API endpoint** to fetch executor session data and include prompt/summary in the response
4. **Regenerated TypeScript types** to reflect the new structure
### ✅ Frontend Changes
1. **Added prompt display** above the normalized logs with a distinctive blue styling and MessageSquare icon
2. **Implemented horizontal dividers** between all log sections:
- Between prompt and conversation logs
- Between main executor and follow-up executors
- Between multiple follow-up executors
3. **Removed the "Follow-up #1 Started:" headers** for a cleaner interface
4. **Updated the layout** to use proper spacing and consistent divider styling
### 🎨 UI Improvements
- **Prompts are displayed** in a blue-themed box with monospace font for better readability
- **Horizontal lines** consistently separate each logical section (prompt → executor → follow-up prompt → follow-up executor)
- **Clean, streamlined appearance** without verbose headers
- **Consistent spacing** and visual hierarchy throughout
The implementation properly handles cases where:
- Prompts may or may not be available
- There are multiple follow-up executors
- There are no follow-up executors
- The main executor completes with or without follow-ups
All builds pass and the code maintains the existing functionality while adding the requested visual improvements!
* ## ✅ Styling Updates Complete!
I've successfully implemented all the requested styling changes:
### 🔨 **Updated Prompt Display**
- **Changed icon** from MessageSquare to **Hammer** with orange color (`text-orange-600`)
- **Removed blue box styling** - no more background, border, or rounded corners
- **Removed monospaced font** - now uses normal text styling with `text-sm` and `text-foreground`
- **Maintains whitespace-pre-wrap** to preserve formatting without the monospace appearance
### 📏 **Improved Spacing**
- **Increased padding** at the bottom of the horizontal divider from `mb-4` to `mb-6`
- This creates **better visual separation** between the user message (prompt) and the agent logs
- **Maintains consistent spacing** throughout the component
### 🎨 **Clean Visual Hierarchy**
- User prompts now appear as **clean, readable text** with a hammer icon
- **Horizontal dividers** provide clear separation between sections
- **Proper padding** ensures comfortable reading experience
- **Consistent with the overall UI theme** rather than standing out with special styling
The changes maintain all existing functionality while providing the cleaner, more integrated visual appearance you requested. The prompt now blends naturally with the rest of the conversation while still being clearly distinguished by the hammer icon and horizontal divider!
* ## ✅ Styling Fixes Applied!
I've successfully implemented both requested changes:
### 🔨 **Blue Hammer Icon**
- Changed the hammer icon color from `text-orange-600` to `text-blue-600`
- Now matches the blue theme you requested
### 📏 **Fixed Spacing Gap**
- **Removed the bottom margin** from the horizontal divider (`mb-6` removed)
- **Added conditional top margin** to the conversation entries container (`mt-6` when prompt exists)
- This creates the **proper gap between the divider and the normalized logs**
### 🎨 **How the spacing now works:**
1. **User prompt** with hammer icon
2. **Horizontal divider** (no bottom margin)
3. **6-unit gap** (via `mt-6` on conversation container)
4. **Normalized logs/conversation entries**
The spacing is now properly balanced with a clean visual separation between the user message and the agent logs, while the blue hammer icon provides a cohesive color scheme throughout the interface!
* ## ✅ Spacing Fixed!
I've corrected the spacing issue by:
### 📏 **Fixed the Gap**
- **Added `mb-6` to the horizontal divider** - this creates the proper 6-unit gap below the divider
- **Removed the conditional margin logic** from the conversation entries container - it was causing the spacing to not work properly
- **Simplified the structure** for more predictable spacing behavior
### 🎯 **Now the spacing flow is:**
1. **User message** (with hammer icon)
2. **Gap** (via `mb-4` on user message container)
3. **Horizontal divider**
4. **Gap** (via `mb-6` on horizontal divider) ← **This was missing before!**
5. **Normalized logs/conversation entries**
The spacing should now be exactly what you requested - proper space both above and below the horizontal divider!
* ## ✅ Spacing Finally Fixed!
I've resolved the `space-y-0` override issue by **restructuring the layout**:
### 🔧 **What was wrong:**
- The `space-y-0` class creates CSS rule `.space-y-0 > :not([hidden]) ~ :not([hidden])` that sets `margin-top: 0` and `margin-bottom: 0` on all sibling elements
- This was overriding the `mb-6` class on the horizontal divider
### 🎯 **How I fixed it:**
1. **Removed `space-y-0` class** from the outer container
2. **Moved the horizontal divider outside** the prompt section to its own top-level element
3. **Made the structure flat** so no spacing utility classes conflict with each other
### 📏 **New structure:**
```jsx
<div> // No space-y-0 class
{/* User prompt */}
<div className="mb-4">...</div> // Space below prompt
{/* Horizontal divider */}
<div className="mb-6">...</div> // Space below divider - no override!
{/* Conversation entries */}
<div className="space-y-2">...</div> // Only affects entries
</div>
```
Now the `mb-6` on the horizontal divider won't be overridden, and you should see the proper gap between the divider and the normalized logs!
* ## ✅ Loading Message Fixed!
I've resolved the misleading "No logs available" message by improving the state logic:
### 🔧 **What was wrong:**
- When a follow-up execution starts, the API call completes successfully but returns no conversation data yet
- The component would show "No conversation data available" even though the process was still running and would eventually produce logs
### 🎯 **How I fixed it:**
- **Added execution process status check** - when `executionProcess.status === 'running'` and there's no conversation data
- **Shows "Waiting for logs..."** instead of "No conversation data available"
- **Maintains proper error state** for truly failed or empty completed processes
### 📱 **New behavior:**
- **When follow-up starts**: Shows "Waiting for logs..." ✅
- **During execution**: Shows "Waiting for logs..." ✅
- **When logs appear**: Shows the actual conversation ✅
- **When execution completes with no logs**: Shows "No conversation data available" ✅
- **On actual errors**: Shows the error message ✅
Now users will see a proper loading state that indicates the system is working, rather than an error-like message that suggests something went wrong!
* ## ✅ "No logs available" Message Fixed!
I've resolved the misleading error message by fixing the root cause in the backend:
### 🔧 **What was wrong:**
- When a follow-up execution starts, `process.stdout` is `None` because logs haven't been generated yet
- The backend was returning `success: false` with "No logs available for this execution process"
- The frontend was showing this as an error message
### 🎯 **How I fixed it:**
1. **Added status check in the backend** - when `process.status == ExecutionProcessStatus::Running` and `stdout` is `None`
2. **Return success with empty conversation** instead of an error
3. **Include executor session data** (prompt/summary) even for running processes without logs yet
4. **Maintain error state** only for truly failed or completed processes with no logs
### 📱 **New behavior:**
- **When follow-up starts**: Backend returns empty conversation with `success: true` ✅
- **Frontend shows**: "Waiting for logs..." (from previous fix) ✅
- **When logs appear**: Shows actual conversation data ✅
- **When process fails**: Still shows proper error messages ✅
- **When completed with no logs**: Shows "No conversation data available" ✅
Now when you start a follow-up, you should see "Waiting for logs..." instead of the misleading "No logs available for this execution process" error message!
* ## ✅ Spacing System Refactored!
I've completely refactored the spacing system for a much cleaner and more predictable layout:
### 🏗️ **New Systematic Approach**
#### **NormalizedConversationViewer (Internal Spacing):**
- **Prompt**: `mb-6` creates consistent gap to conversation entries
- **Conversation entries**: `space-y-2` between each entry
- **No internal dividers** - keeps the component focused on its own content
#### **TaskDetailsPanel (Executor Separation):**
- **Container**: `space-y-8` creates consistent spacing between main sections
- **Follow-up dividers**: `border-t` + `mb-8` for proper separation
- **Clean structure** with predictable spacing
### 📏 **Spacing Flow Now:**
1. **Main executor prompt**
2. **6-unit gap** (prompt `mb-6`)
3. **Main executor conversation**
4. **8-unit gap** (container `space-y-8`)
5. **Horizontal divider**
6. **8-unit gap** (divider `mb-8`)
7. **Follow-up prompt**
8. **6-unit gap** (prompt `mb-6`)
9. **Follow-up conversation**
10. **8-unit gap** (for next follow-up if any)
### 🎯 **Benefits:**
- **Consistent spacing** throughout all states
- **No more conflicting CSS rules** or override issues
- **Systematic approach** that's easy to maintain and debug
- **Proper separation** between logical sections
- **Clean, readable layout** with predictable gaps
The spacing should now be perfect in all scenarios - with or without prompts, with single or multiple follow-ups, and in both full-screen and split-view modes!
* I've successfully improved the diff styling to be more compact and fit better within the sidebar. Here are the key changes made:
**Spacing Improvements:**
- Reduced spacing between diff files from `space-y-6` to `space-y-3`
- Reduced main container padding from `p-6` to `p-4`
- Reduced file header padding from `py-2` to `py-1.5`
**Line Compactness:**
- Reduced diff line height from `min-h-[1.75rem]` to `min-h-[1.25rem]`
- Reduced line number column width from `w-16` to `w-12`
- Reduced line number padding from `px-2` to `px-1.5`
- Reduced individual line number widths from `w-6` to `w-5`
- Reduced line content padding from `px-3` to `px-2`
**Expand/Collapse Buttons:**
- Reduced expand button height from `h-8` to `h-6`
These changes make the diff view significantly more compact while maintaining readability. The diff will now display more content in the same space, making better use of the sidebar real estate. The frontend builds successfully with these changes.
* Perfect! The migration is complete. Let me summarize what I've done:
I've successfully migrated the merge, rebase, and PR functionality from the old `task-attempt-compare.tsx` page to the `TaskDetailsToolbar.tsx` component and deleted the old page.
1. **Added to TaskDetailsToolbar.tsx:**
- Branch status fetching with `fetchBranchStatus` function
- Merge functionality with uncommitted changes warning
- Rebase functionality
- Create PR functionality with dialog
- Branch status display showing commits ahead/behind and uncommitted changes
- Git operation buttons (Merge, Rebase, Create PR) with proper enabling/disabling logic
- Two dialog components for warnings and PR creation
2. **Deleted:**
- `frontend/src/pages/task-attempt-compare.tsx` - The entire compare page
3. **Updated routing:**
- Removed the compare page route from `App.tsx`
- Removed the import for `TaskAttemptComparePage`
- Updated keyboard shortcuts to remove compare page navigation logic
- Removed the "Changes" button that linked to the old compare page
4. **Cleaned up imports:**
- Removed unused imports like `Link` from react-router-dom and `GitCompare` icon
The diff view functionality remains in the `TaskDetailsPanel.tsx` where it was already migrated, and now users can access merge, rebase, and PR creation directly from the toolbar without needing a separate page. The branch status is also prominently displayed in the toolbar so users can see the current state at a glance.
* Perfect! Now the merge, rebase, and PR buttons will be disabled when an attempt is running, preventing users from performing git operations while code is being executed.
* Cargo fmt
* Clippy
* Prettier
* Cleanup
* Clippy
2025-07-03 22:00:44 +01:00
#[ derive(Debug, Clone, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub enum ExecutionState {
NotStarted ,
SetupRunning ,
SetupComplete ,
SetupFailed ,
CodingAgentRunning ,
CodingAgentComplete ,
CodingAgentFailed ,
Complete ,
}
#[ derive(Debug, Clone, Serialize, Deserialize, TS) ]
#[ ts(export) ]
pub struct TaskAttemptState {
pub execution_state : ExecutionState ,
pub has_changes : bool ,
pub has_setup_script : bool ,
pub setup_process_id : Option < String > ,
pub coding_agent_process_id : Option < String > ,
}
2025-06-16 17:07:11 -04:00
impl TaskAttempt {
2025-06-17 15:22:47 -04:00
pub async fn find_by_id ( pool : & SqlitePool , id : Uuid ) -> Result < Option < Self > , sqlx ::Error > {
2025-06-16 18:37:19 -04:00
sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT id as " id ! : Uuid " , task_id as " task_id ! : Uuid " , worktree_path, branch, base_branch, merge_commit, executor, pr_url, pr_number, pr_status, pr_merged_at as " pr_merged_at : DateTime < Utc > " , created_at as " created_at ! : DateTime < Utc > " , updated_at as " updated_at ! : DateTime < Utc > "
2025-06-16 18:37:19 -04:00
FROM task_attempts
WHERE id = $ 1 " #,
id
)
. fetch_optional ( pool )
. await
}
2025-06-17 15:22:47 -04:00
pub async fn find_by_task_id (
pool : & SqlitePool ,
task_id : Uuid ,
) -> Result < Vec < Self > , sqlx ::Error > {
2025-06-16 17:07:11 -04:00
sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT id as " id ! : Uuid " , task_id as " task_id ! : Uuid " , worktree_path, branch, base_branch, merge_commit, executor, pr_url, pr_number, pr_status, pr_merged_at as " pr_merged_at : DateTime < Utc > " , created_at as " created_at ! : DateTime < Utc > " , updated_at as " updated_at ! : DateTime < Utc > "
2025-06-16 17:07:11 -04:00
FROM task_attempts
WHERE task_id = $ 1
ORDER BY created_at DESC " #,
task_id
)
. fetch_all ( pool )
. await
}
2025-06-16 23:08:09 -04:00
pub async fn create (
2025-06-17 15:22:47 -04:00
pool : & SqlitePool ,
2025-06-16 23:08:09 -04:00
data : & CreateTaskAttempt ,
2025-06-21 19:45:47 +01:00
task_id : Uuid ,
2025-06-16 23:08:09 -04:00
) -> Result < Self , TaskAttemptError > {
2025-06-30 14:30:32 +01:00
let attempt_id = Uuid ::new_v4 ( ) ;
2025-07-01 15:11:51 +01:00
// let prefixed_id = format!("vibe-kanban-{}", attempt_id);
2025-06-30 14:30:32 +01:00
2025-06-16 17:15:51 -04:00
// First, get the task to get the project_id
2025-06-21 19:45:47 +01:00
let task = Task ::find_by_id ( pool , task_id )
2025-06-16 17:15:51 -04:00
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
2025-07-01 15:11:51 +01:00
// Create a unique and helpful branch name
let task_title_id = crate ::utils ::text ::git_branch_id ( & task . title ) ;
let task_attempt_branch = format! (
" vk-{}-{} " ,
crate ::utils ::text ::short_uuid ( & attempt_id ) ,
task_title_id
) ;
2025-06-16 17:15:51 -04:00
2025-06-27 16:18:35 +01:00
// Generate worktree path automatically using cross-platform temporary directory
let temp_dir = std ::env ::temp_dir ( ) ;
2025-07-01 15:11:51 +01:00
let worktree_path = temp_dir . join ( & task_attempt_branch ) ;
2025-06-27 16:18:35 +01:00
let worktree_path_str = worktree_path . to_string_lossy ( ) . to_string ( ) ;
2025-06-21 19:45:47 +01:00
2025-07-01 15:11:51 +01:00
// Then get the project using the project_id
let project = Project ::find_by_id ( pool , task . project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
// Determine the resolved base branch name first
let resolved_base_branch = if let Some ( ref base_branch ) = data . base_branch {
base_branch . clone ( )
} else {
// Default to current HEAD branch name or "main"
let repo = Repository ::open ( & project . git_repo_path ) ? ;
let default_branch = match repo . head ( ) {
Ok ( head_ref ) = > head_ref . shorthand ( ) . unwrap_or ( " main " ) . to_string ( ) ,
Err ( e )
if e . class ( ) = = git2 ::ErrorClass ::Reference
& & e . code ( ) = = git2 ::ErrorCode ::UnbornBranch = >
{
" main " . to_string ( ) // Repository has no commits yet
}
Err ( _ ) = > " main " . to_string ( ) , // Fallback
} ;
default_branch
} ;
2025-06-30 14:30:32 +01:00
// Solve scoping issues
{
// Create the worktree using git2
let repo = Repository ::open ( & project . git_repo_path ) ? ;
2025-07-01 15:11:51 +01:00
// Choose base reference, based on whether user specified base branch
let base_reference = if let Some ( base_branch ) = data . base_branch . clone ( ) {
let branch = repo . find_branch ( base_branch . as_str ( ) , BranchType ::Local ) ? ;
branch . into_reference ( )
} else {
2025-07-04 14:27:25 +01:00
// Handle new repositories without any commits
match repo . head ( ) {
Ok ( head_ref ) = > head_ref ,
Err ( e )
if e . class ( ) = = git2 ::ErrorClass ::Reference
& & e . code ( ) = = git2 ::ErrorCode ::UnbornBranch = >
{
// Repository has no commits yet, create an initial commit
let signature = repo . signature ( ) . unwrap_or_else ( | _ | {
// Fallback if no Git config is set
git2 ::Signature ::now ( " Vibe Kanban " , " noreply@vibekanban.com " )
. expect ( " Failed to create fallback signature " )
} ) ;
let tree_id = {
let tree_builder = repo . treebuilder ( None ) ? ;
tree_builder . write ( ) ?
} ;
let tree = repo . find_tree ( tree_id ) ? ;
// Create initial commit on main branch
let _commit_id = repo . commit (
Some ( " refs/heads/main " ) ,
& signature ,
& signature ,
" Initial commit " ,
& tree ,
& [ ] ,
) ? ;
// Set HEAD to point to main branch
repo . set_head ( " refs/heads/main " ) ? ;
// Return reference to main branch
repo . find_reference ( " refs/heads/main " ) ?
}
Err ( e ) = > return Err ( e . into ( ) ) ,
}
2025-07-01 15:11:51 +01:00
} ;
2025-06-30 14:30:32 +01:00
2025-07-01 15:11:51 +01:00
// Create branch
repo . branch (
& task_attempt_branch ,
& base_reference . peel_to_commit ( ) ? ,
false ,
) ? ;
2025-06-30 14:30:32 +01:00
2025-07-01 15:11:51 +01:00
let branch = repo . find_branch ( & task_attempt_branch , BranchType ::Local ) ? ;
let branch_ref = branch . into_reference ( ) ;
let mut worktree_opts = WorktreeAddOptions ::new ( ) ;
worktree_opts . reference ( Some ( & branch_ref ) ) ;
2025-06-30 14:30:32 +01:00
// Create the worktree directory if it doesn't exist
if let Some ( parent ) = worktree_path . parent ( ) {
std ::fs ::create_dir_all ( parent )
. map_err ( | e | TaskAttemptError ::Git ( GitError ::from_str ( & e . to_string ( ) ) ) ) ? ;
}
// Create the worktree at the specified path
2025-07-01 15:11:51 +01:00
repo . worktree ( & task_attempt_branch , & worktree_path , Some ( & worktree_opts ) ) ? ;
2025-06-16 17:15:51 -04:00
}
// Insert the record into the database
2025-06-17 15:22:47 -04:00
Ok ( sqlx ::query_as! (
2025-06-16 17:07:11 -04:00
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" INSERT INTO task_attempts (id, task_id, worktree_path, branch, base_branch, merge_commit, executor, pr_url, pr_number, pr_status, pr_merged_at)
VALUES ( $ 1 , $ 2 , $ 3 , $ 4 , $ 5 , $ 6 , $ 7 , $ 8 , $ 9 , $ 10 , $ 11 )
RETURNING id as " id!: Uuid " , task_id as " task_id!: Uuid " , worktree_path , branch , base_branch , merge_commit , executor , pr_url , pr_number , pr_status , pr_merged_at as " pr_merged_at: DateTime<Utc> " , created_at as " created_at!: DateTime<Utc> " , updated_at as " updated_at!: DateTime<Utc> " " #,
2025-06-16 17:07:11 -04:00
attempt_id ,
2025-06-21 19:45:47 +01:00
task_id ,
worktree_path_str ,
2025-07-01 15:11:51 +01:00
task_attempt_branch ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
resolved_base_branch ,
2025-06-21 19:45:47 +01:00
Option ::< String > ::None , // merge_commit is always None during creation
2025-07-01 17:45:00 +01:00
data . executor ,
Option ::< String > ::None , // pr_url is None during creation
Option ::< i64 > ::None , // pr_number is None during creation
Option ::< String > ::None , // pr_status is None during creation
Option ::< DateTime < Utc > > ::None // pr_merged_at is None during creation
2025-06-16 17:07:11 -04:00
)
. fetch_one ( pool )
2025-06-17 15:22:47 -04:00
. await ? )
2025-06-16 17:07:11 -04:00
}
2025-06-16 23:08:09 -04:00
pub async fn exists_for_task (
2025-06-17 15:22:47 -04:00
pool : & SqlitePool ,
2025-06-16 23:08:09 -04:00
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
) -> Result < bool , sqlx ::Error > {
2025-06-16 17:07:11 -04:00
let result = sqlx ::query! (
2025-06-17 22:09:10 -04:00
" SELECT ta.id as \" id!: Uuid \" FROM task_attempts ta
2025-06-16 17:07:11 -04:00
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1 AND t . id = $ 2 AND t . project_id = $ 3 " ,
attempt_id ,
task_id ,
project_id
)
. fetch_optional ( pool )
. await ? ;
Ok ( result . is_some ( ) )
}
2025-06-16 23:08:09 -04:00
2025-07-01 15:11:51 +01:00
/// Perform the actual merge operation (synchronous)
2025-06-17 00:12:34 -04:00
fn perform_merge_operation (
worktree_path : & str ,
main_repo_path : & str ,
2025-07-01 15:11:51 +01:00
branch_name : & str ,
2025-06-20 18:31:14 +01:00
task_title : & str ,
2025-06-17 00:12:34 -04:00
) -> Result < String , TaskAttemptError > {
// Open the main repository
let main_repo = Repository ::open ( main_repo_path ) ? ;
2025-07-01 15:11:51 +01:00
// Open the worktree repository to get the latest commit
let worktree_repo = Repository ::open ( worktree_path ) ? ;
let worktree_head = worktree_repo . head ( ) ? ;
let worktree_commit = worktree_head . peel_to_commit ( ) ? ;
// Verify the branch exists in the main repo
main_repo
. find_branch ( branch_name , BranchType ::Local )
. map_err ( | _ | TaskAttemptError ::BranchNotFound ( branch_name . to_string ( ) ) ) ? ;
// Get the current HEAD of the main repo (usually main/master)
let main_head = main_repo . head ( ) ? ;
let main_commit = main_head . peel_to_commit ( ) ? ;
// Get the signature for the merge commit
2025-06-17 00:12:34 -04:00
let signature = main_repo . signature ( ) ? ;
2025-07-01 15:11:51 +01:00
// Get the tree from the worktree commit and find it in the main repo
let worktree_tree_id = worktree_commit . tree_id ( ) ;
let main_tree = main_repo . find_tree ( worktree_tree_id ) ? ;
// Find the worktree commit in the main repo
let main_worktree_commit = main_repo . find_commit ( worktree_commit . id ( ) ) ? ;
// Create a merge commit
let merge_commit_id = main_repo . commit (
Some ( " HEAD " ) , // Update HEAD
& signature , // Author
& signature , // Committer
& format! ( " Merge: {} (vibe-kanban) " , task_title ) , // Message using task title
& main_tree , // Use the tree from main repo
& [ & main_commit , & main_worktree_commit ] , // Parents: main HEAD and worktree commit
) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
info! ( " Created merge commit: {} " , merge_commit_id ) ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
Ok ( merge_commit_id . to_string ( ) )
}
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
/// Perform the actual git rebase operations (synchronous)
fn perform_rebase_operation (
worktree_path : & str ,
main_repo_path : & str ,
new_base_branch : Option < String > ,
) -> Result < String , TaskAttemptError > {
// Open the worktree repository
let worktree_repo = Repository ::open ( worktree_path ) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Open the main repository to get the target base commit
let main_repo = Repository ::open ( main_repo_path ) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Get the target base branch reference
let base_branch_name = new_base_branch . unwrap_or_else ( | | {
main_repo
. head ( )
. ok ( )
. and_then ( | head | head . shorthand ( ) . map ( | s | s . to_string ( ) ) )
. unwrap_or_else ( | | " main " . to_string ( ) )
} ) ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Check if the specified base branch exists in the main repo
let base_branch = main_repo
. find_branch ( & base_branch_name , BranchType ::Local )
. map_err ( | _ | TaskAttemptError ::BranchNotFound ( base_branch_name . clone ( ) ) ) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
let base_commit_id = base_branch . get ( ) . peel_to_commit ( ) ? . id ( ) ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Get the HEAD commit of the worktree (the changes to rebase)
let head = worktree_repo . head ( ) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Set up rebase
let mut rebase_opts = RebaseOptions ::new ( ) ;
let signature = worktree_repo . signature ( ) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Start the rebase
let head_annotated = worktree_repo . reference_to_annotated_commit ( & head ) ? ;
let base_annotated = worktree_repo . find_annotated_commit ( base_commit_id ) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
let mut rebase = worktree_repo . rebase (
Some ( & head_annotated ) ,
Some ( & base_annotated ) ,
None , // onto (use upstream if None)
Some ( & mut rebase_opts ) ,
) ? ;
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Process each rebase operation
while let Some ( operation ) = rebase . next ( ) {
let _operation = operation ? ;
// Check for conflicts
let index = worktree_repo . index ( ) ? ;
if index . has_conflicts ( ) {
// For now, abort the rebase on conflicts
rebase . abort ( ) ? ;
return Err ( TaskAttemptError ::Git ( GitError ::from_str (
" Rebase failed due to conflicts. Please resolve conflicts manually. " ,
) ) ) ;
}
2025-06-17 00:12:34 -04:00
2025-07-01 15:11:51 +01:00
// Commit the rebased operation
rebase . commit ( None , & signature , None ) ? ;
2025-06-17 00:12:34 -04:00
}
2025-07-01 15:11:51 +01:00
// Finish the rebase
rebase . finish ( None ) ? ;
// Get the final commit ID after rebase
let final_head = worktree_repo . head ( ) ? ;
let final_commit = final_head . peel_to_commit ( ) ? ;
info! ( " Rebase completed. New HEAD: {} " , final_commit . id ( ) ) ;
Ok ( final_commit . id ( ) . to_string ( ) )
2025-06-17 00:12:34 -04:00
}
/// Merge the worktree changes back to the main repository
pub async fn merge_changes (
2025-06-17 15:22:47 -04:00
pool : & SqlitePool ,
2025-06-17 00:12:34 -04:00
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
) -> Result < String , TaskAttemptError > {
// Get the task attempt with validation
let attempt = sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT ta.id as " id ! : Uuid " , ta.task_id as " task_id ! : Uuid " , ta.worktree_path, ta.branch, ta.base_branch, ta.merge_commit, ta.executor, ta.pr_url, ta.pr_number, ta.pr_status, ta.pr_merged_at as " pr_merged_at : DateTime < Utc > " , ta.created_at as " created_at ! : DateTime < Utc > " , ta.updated_at as " updated_at ! : DateTime < Utc > "
2025-06-17 00:12:34 -04:00
FROM task_attempts ta
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1 AND t . id = $ 2 AND t . project_id = $ 3 " #,
2025-07-01 16:28:15 +01:00
attempt_id ,
task_id ,
project_id
2025-06-17 00:12:34 -04:00
)
. fetch_optional ( pool )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
// Get the task and project
2025-06-20 18:31:14 +01:00
let task = Task ::find_by_id ( pool , task_id )
2025-06-17 00:12:34 -04:00
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
let project = Project ::find_by_id ( pool , project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
2025-07-01 15:11:51 +01:00
// Perform the actual merge operation
2025-06-17 00:12:34 -04:00
let merge_commit_id = Self ::perform_merge_operation (
& attempt . worktree_path ,
& project . git_repo_path ,
2025-07-01 15:11:51 +01:00
& attempt . branch ,
2025-06-20 18:31:14 +01:00
& task . title ,
2025-06-17 00:12:34 -04:00
) ? ;
// Update the task attempt with the merge commit
sqlx ::query! (
2025-06-17 15:22:47 -04:00
" UPDATE task_attempts SET merge_commit = $1, updated_at = datetime('now') WHERE id = $2 " ,
2025-06-17 00:12:34 -04:00
merge_commit_id ,
attempt_id
)
. execute ( pool )
. await ? ;
Ok ( merge_commit_id )
}
2025-06-19 11:09:32 -04:00
/// Start the execution flow for a task attempt (setup script + executor)
pub async fn start_execution (
pool : & SqlitePool ,
2025-06-20 22:14:31 +01:00
app_state : & crate ::app_state ::AppState ,
2025-06-19 11:09:32 -04:00
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
) -> Result < ( ) , TaskAttemptError > {
use crate ::models ::task ::{ Task , TaskStatus } ;
2025-06-24 01:05:55 +01:00
// Load required entities
let ( task_attempt , project ) =
Self ::load_execution_context ( pool , attempt_id , project_id ) . await ? ;
// Update task status to indicate execution has started
Task ::update_status ( pool , task_id , project_id , TaskStatus ::InProgress ) . await ? ;
// Determine execution sequence based on project configuration
if Self ::should_run_setup_script ( & project ) {
Self ::start_setup_script (
pool ,
app_state ,
attempt_id ,
task_id ,
& project ,
& task_attempt . worktree_path ,
)
. await
} else {
Self ::start_coding_agent ( pool , app_state , attempt_id , task_id , project_id ) . await
}
}
/// Load the execution context (task attempt and project) with validation
async fn load_execution_context (
pool : & SqlitePool ,
attempt_id : Uuid ,
project_id : Uuid ,
) -> Result < ( TaskAttempt , Project ) , TaskAttemptError > {
2025-06-19 11:09:32 -04:00
let task_attempt = TaskAttempt ::find_by_id ( pool , attempt_id )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
let project = Project ::find_by_id ( pool , project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
2025-06-24 01:05:55 +01:00
Ok ( ( task_attempt , project ) )
}
/// Check if setup script should be executed
fn should_run_setup_script ( project : & Project ) -> bool {
project
. setup_script
. as_ref ( )
. map ( | script | ! script . trim ( ) . is_empty ( ) )
. unwrap_or ( false )
}
/// Start the setup script execution
async fn start_setup_script (
pool : & SqlitePool ,
app_state : & crate ::app_state ::AppState ,
attempt_id : Uuid ,
task_id : Uuid ,
project : & Project ,
worktree_path : & str ,
) -> Result < ( ) , TaskAttemptError > {
let setup_script = project . setup_script . as_ref ( ) . unwrap ( ) ;
2025-06-20 16:52:52 +01:00
2025-06-24 01:05:55 +01:00
Self ::start_process_execution (
pool ,
app_state ,
attempt_id ,
task_id ,
crate ::executor ::ExecutorType ::SetupScript ( setup_script . clone ( ) ) ,
" Starting setup script " . to_string ( ) ,
TaskAttemptStatus ::SetupRunning ,
crate ::models ::execution_process ::ExecutionProcessType ::SetupScript ,
worktree_path ,
)
. await
}
/// Start the coding agent after setup is complete or if no setup is needed
pub async fn start_coding_agent (
pool : & SqlitePool ,
app_state : & crate ::app_state ::AppState ,
attempt_id : Uuid ,
task_id : Uuid ,
_project_id : Uuid ,
) -> Result < ( ) , TaskAttemptError > {
let task_attempt = TaskAttempt ::find_by_id ( pool , attempt_id )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
let executor_config = Self ::resolve_executor_config ( & task_attempt . executor ) ;
Self ::start_process_execution (
pool ,
app_state ,
attempt_id ,
task_id ,
crate ::executor ::ExecutorType ::CodingAgent ( executor_config ) ,
" Starting executor " . to_string ( ) ,
TaskAttemptStatus ::ExecutorRunning ,
crate ::models ::execution_process ::ExecutionProcessType ::CodingAgent ,
& task_attempt . worktree_path ,
)
. await
}
2025-06-24 16:50:58 +01:00
/// Start a dev server for this task attempt
pub async fn start_dev_server (
pool : & SqlitePool ,
app_state : & crate ::app_state ::AppState ,
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
) -> Result < ( ) , TaskAttemptError > {
let task_attempt = TaskAttempt ::find_by_id ( pool , attempt_id )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
// Get the project to access the dev_script
let project = crate ::models ::project ::Project ::find_by_id ( pool , project_id )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
let dev_script = project . dev_script . ok_or_else ( | | {
TaskAttemptError ::ValidationError (
" No dev script configured for this project " . to_string ( ) ,
)
} ) ? ;
if dev_script . trim ( ) . is_empty ( ) {
return Err ( TaskAttemptError ::ValidationError (
" Dev script is empty " . to_string ( ) ,
) ) ;
}
2025-07-04 16:24:19 +01:00
let result = Self ::start_process_execution (
2025-06-24 16:50:58 +01:00
pool ,
app_state ,
attempt_id ,
task_id ,
crate ::executor ::ExecutorType ::DevServer ( dev_script ) ,
" Starting dev server " . to_string ( ) ,
TaskAttemptStatus ::ExecutorRunning , // Dev servers don't create activities, just use generic status
crate ::models ::execution_process ::ExecutionProcessType ::DevServer ,
& task_attempt . worktree_path ,
)
2025-07-04 16:24:19 +01:00
. await ;
if result . is_ok ( ) {
app_state
. track_analytics_event (
" dev_server_started " ,
Some ( serde_json ::json! ( {
" task_id " : task_id . to_string ( ) ,
" project_id " : project_id . to_string ( ) ,
" attempt_id " : attempt_id . to_string ( )
} ) ) ,
)
. await ;
}
result
2025-06-24 16:50:58 +01:00
}
2025-06-24 01:05:55 +01:00
/// Start a follow-up execution using the same executor type as the first process
pub async fn start_followup_execution (
pool : & SqlitePool ,
app_state : & crate ::app_state ::AppState ,
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
prompt : & str ,
) -> Result < ( ) , TaskAttemptError > {
2025-06-25 09:27:29 +01:00
use crate ::models ::{
executor_session ::ExecutorSession ,
task ::{ Task , TaskStatus } ,
} ;
2025-06-24 10:48:15 +01:00
// Update task status to indicate follow-up execution has started
Task ::update_status ( pool , task_id , project_id , TaskStatus ::InProgress ) . await ? ;
2025-06-24 01:05:55 +01:00
// Get task attempt
let task_attempt = TaskAttempt ::find_by_id ( pool , attempt_id )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
// Find the most recent coding agent execution process to get the executor type
let execution_processes =
crate ::models ::execution_process ::ExecutionProcess ::find_by_task_attempt_id (
pool , attempt_id ,
)
. await ? ;
let most_recent_coding_agent = execution_processes
. iter ( )
. rev ( ) // Reverse to get most recent first (since they're ordered by created_at ASC)
. find ( | p | {
matches! (
p . process_type ,
crate ::models ::execution_process ::ExecutionProcessType ::CodingAgent
2025-06-19 11:09:32 -04:00
)
2025-06-24 01:05:55 +01:00
} )
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ; // No previous coding agent found
// Get the executor session to find the session ID
let executor_session =
ExecutorSession ::find_by_execution_process_id ( pool , most_recent_coding_agent . id )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ; // No session found
// Determine the executor config from the stored executor_type
let executor_config = match most_recent_coding_agent . executor_type . as_deref ( ) {
Some ( " claude " ) = > crate ::executor ::ExecutorConfig ::Claude ,
Some ( " amp " ) = > crate ::executor ::ExecutorConfig ::Amp ,
2025-06-25 18:23:50 +01:00
Some ( " gemini " ) = > crate ::executor ::ExecutorConfig ::Gemini ,
2025-06-24 01:05:55 +01:00
Some ( " echo " ) = > crate ::executor ::ExecutorConfig ::Echo ,
_ = > return Err ( TaskAttemptError ::TaskNotFound ) , // Invalid executor type
} ;
2025-06-19 11:09:32 -04:00
2025-06-24 01:05:55 +01:00
// Create the follow-up executor type
let followup_executor = crate ::executor ::ExecutorType ::FollowUpCodingAgent {
config : executor_config ,
session_id : executor_session . session_id . clone ( ) ,
prompt : prompt . to_string ( ) ,
} ;
Self ::start_process_execution (
pool ,
app_state ,
attempt_id ,
task_id ,
followup_executor ,
" Starting follow-up executor " . to_string ( ) ,
TaskAttemptStatus ::ExecutorRunning ,
crate ::models ::execution_process ::ExecutionProcessType ::CodingAgent ,
& task_attempt . worktree_path ,
)
. await
}
2025-06-19 11:09:32 -04:00
2025-06-24 01:05:55 +01:00
/// Resolve executor configuration from string name
fn resolve_executor_config ( executor_name : & Option < String > ) -> crate ::executor ::ExecutorConfig {
match executor_name . as_ref ( ) . map ( | s | s . as_str ( ) ) {
Some ( " claude " ) = > crate ::executor ::ExecutorConfig ::Claude ,
Some ( " amp " ) = > crate ::executor ::ExecutorConfig ::Amp ,
2025-06-25 18:23:50 +01:00
Some ( " gemini " ) = > crate ::executor ::ExecutorConfig ::Gemini ,
2025-06-27 22:30:45 +01:00
Some ( " opencode " ) = > crate ::executor ::ExecutorConfig ::Opencode ,
2025-06-24 01:05:55 +01:00
_ = > crate ::executor ::ExecutorConfig ::Echo , // Default for "echo" or None
}
2025-06-20 22:39:06 +01:00
}
2025-06-24 01:05:55 +01:00
/// Unified function to start any type of process execution
2025-06-27 13:32:32 +01:00
#[ allow(clippy::too_many_arguments) ]
2025-06-20 23:20:38 +01:00
async fn start_process_execution (
2025-06-20 22:39:06 +01:00
pool : & SqlitePool ,
app_state : & crate ::app_state ::AppState ,
attempt_id : Uuid ,
task_id : Uuid ,
2025-06-20 23:20:38 +01:00
executor_type : crate ::executor ::ExecutorType ,
activity_note : String ,
activity_status : TaskAttemptStatus ,
process_type : crate ::models ::execution_process ::ExecutionProcessType ,
worktree_path : & str ,
2025-06-20 22:39:06 +01:00
) -> Result < ( ) , TaskAttemptError > {
2025-06-24 01:05:55 +01:00
let process_id = Uuid ::new_v4 ( ) ;
// Create execution process record
let _execution_process = Self ::create_execution_process_record (
pool ,
attempt_id ,
process_id ,
& executor_type ,
process_type . clone ( ) ,
worktree_path ,
)
. await ? ;
// Create executor session for coding agents
if matches! (
process_type ,
crate ::models ::execution_process ::ExecutionProcessType ::CodingAgent
) {
2025-06-25 00:56:13 +01:00
// Extract follow-up prompt if this is a follow-up execution
let followup_prompt = match & executor_type {
2025-06-25 09:27:29 +01:00
crate ::executor ::ExecutorType ::FollowUpCodingAgent { prompt , .. } = > {
Some ( prompt . clone ( ) )
}
2025-06-25 00:56:13 +01:00
_ = > None ,
} ;
2025-06-25 09:27:29 +01:00
Self ::create_executor_session_record (
pool ,
attempt_id ,
task_id ,
process_id ,
followup_prompt ,
)
. await ? ;
2025-06-24 01:05:55 +01:00
}
2025-06-24 16:50:58 +01:00
// Create activity record (skip for dev servers as they run in parallel)
if ! matches! (
process_type ,
crate ::models ::execution_process ::ExecutionProcessType ::DevServer
) {
Self ::create_activity_record ( pool , process_id , activity_status . clone ( ) , & activity_note )
. await ? ;
}
2025-06-24 01:05:55 +01:00
tracing ::info! ( " Starting {} for task attempt {} " , activity_note , attempt_id ) ;
// Execute the process
let child = Self ::execute_process (
& executor_type ,
pool ,
task_id ,
attempt_id ,
process_id ,
worktree_path ,
)
. await ? ;
// Register for monitoring
Self ::register_for_monitoring ( app_state , process_id , attempt_id , & process_type , child )
. await ;
tracing ::info! (
" Started execution {} for task attempt {} " ,
process_id ,
attempt_id
) ;
Ok ( ( ) )
}
/// Create execution process database record
async fn create_execution_process_record (
pool : & SqlitePool ,
attempt_id : Uuid ,
process_id : Uuid ,
executor_type : & crate ::executor ::ExecutorType ,
process_type : crate ::models ::execution_process ::ExecutionProcessType ,
worktree_path : & str ,
) -> Result < crate ::models ::execution_process ::ExecutionProcess , TaskAttemptError > {
2025-06-20 23:20:38 +01:00
use crate ::models ::execution_process ::{ CreateExecutionProcess , ExecutionProcess } ;
2025-06-20 22:39:06 +01:00
2025-06-27 16:18:35 +01:00
let ( shell_cmd , shell_arg ) = get_shell_command ( ) ;
2025-06-24 01:05:55 +01:00
let ( command , args , executor_type_string ) = match executor_type {
2025-06-20 23:20:38 +01:00
crate ::executor ::ExecutorType ::SetupScript ( _ ) = > (
2025-06-27 16:18:35 +01:00
shell_cmd . to_string ( ) ,
Some ( serde_json ::to_string ( & [ shell_arg , " setup_script " ] ) . unwrap ( ) ) ,
2025-06-24 01:05:55 +01:00
None , // Setup scripts don't have an executor type
2025-06-20 23:20:38 +01:00
) ,
2025-06-24 16:50:58 +01:00
crate ::executor ::ExecutorType ::DevServer ( _ ) = > (
2025-06-27 16:18:35 +01:00
shell_cmd . to_string ( ) ,
Some ( serde_json ::to_string ( & [ shell_arg , " dev_server " ] ) . unwrap ( ) ) ,
2025-06-24 16:50:58 +01:00
None , // Dev servers don't have an executor type
) ,
2025-06-24 01:05:55 +01:00
crate ::executor ::ExecutorType ::CodingAgent ( config ) = > {
let executor_type_str = match config {
crate ::executor ::ExecutorConfig ::Echo = > " echo " ,
crate ::executor ::ExecutorConfig ::Claude = > " claude " ,
crate ::executor ::ExecutorConfig ::Amp = > " amp " ,
2025-06-25 18:23:50 +01:00
crate ::executor ::ExecutorConfig ::Gemini = > " gemini " ,
2025-06-27 21:45:00 +01:00
crate ::executor ::ExecutorConfig ::Opencode = > " opencode " ,
2025-06-24 01:05:55 +01:00
} ;
(
" executor " . to_string ( ) ,
None ,
Some ( executor_type_str . to_string ( ) ) ,
)
}
crate ::executor ::ExecutorType ::FollowUpCodingAgent { config , .. } = > {
let executor_type_str = match config {
crate ::executor ::ExecutorConfig ::Echo = > " echo " ,
crate ::executor ::ExecutorConfig ::Claude = > " claude " ,
crate ::executor ::ExecutorConfig ::Amp = > " amp " ,
2025-06-25 18:23:50 +01:00
crate ::executor ::ExecutorConfig ::Gemini = > " gemini " ,
2025-06-27 21:45:00 +01:00
crate ::executor ::ExecutorConfig ::Opencode = > " opencode " ,
2025-06-24 01:05:55 +01:00
} ;
(
" followup_executor " . to_string ( ) ,
None ,
Some ( executor_type_str . to_string ( ) ) ,
)
}
2025-06-20 23:20:38 +01:00
} ;
2025-06-19 11:09:32 -04:00
2025-06-20 23:20:38 +01:00
let create_process = CreateExecutionProcess {
2025-06-20 22:39:06 +01:00
task_attempt_id : attempt_id ,
2025-06-24 01:05:55 +01:00
process_type ,
executor_type : executor_type_string ,
2025-06-20 23:20:38 +01:00
command ,
args ,
working_directory : worktree_path . to_string ( ) ,
2025-06-20 22:39:06 +01:00
} ;
2025-06-24 01:05:55 +01:00
ExecutionProcess ::create ( pool , & create_process , process_id )
. await
. map_err ( TaskAttemptError ::from )
}
/// Create executor session record for coding agents
async fn create_executor_session_record (
pool : & SqlitePool ,
attempt_id : Uuid ,
task_id : Uuid ,
process_id : Uuid ,
2025-06-25 00:56:13 +01:00
followup_prompt : Option < String > ,
2025-06-24 01:05:55 +01:00
) -> Result < ( ) , TaskAttemptError > {
use crate ::models ::executor_session ::{ CreateExecutorSession , ExecutorSession } ;
2025-06-25 00:56:13 +01:00
// Use follow-up prompt if provided, otherwise get the task to create prompt
let prompt = if let Some ( followup_prompt ) = followup_prompt {
followup_prompt
} else {
let task = Task ::find_by_id ( pool , task_id )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
format! ( " {} \n \n {} " , task . title , task . description . unwrap_or_default ( ) )
} ;
2025-06-24 01:05:55 +01:00
let session_id = Uuid ::new_v4 ( ) ;
let create_session = CreateExecutorSession {
task_attempt_id : attempt_id ,
execution_process_id : process_id ,
prompt : Some ( prompt ) ,
} ;
ExecutorSession ::create ( pool , & create_session , session_id )
. await
. map ( | _ | ( ) )
. map_err ( TaskAttemptError ::from )
}
/// Create activity record for process start
async fn create_activity_record (
pool : & SqlitePool ,
process_id : Uuid ,
activity_status : TaskAttemptStatus ,
activity_note : & str ,
) -> Result < ( ) , TaskAttemptError > {
use crate ::models ::task_attempt_activity ::{
CreateTaskAttemptActivity , TaskAttemptActivity ,
} ;
2025-06-20 22:39:06 +01:00
2025-06-21 19:31:41 +01:00
let activity_id = Uuid ::new_v4 ( ) ;
let create_activity = CreateTaskAttemptActivity {
execution_process_id : process_id ,
status : Some ( activity_status . clone ( ) ) ,
2025-06-24 01:05:55 +01:00
note : Some ( activity_note . to_string ( ) ) ,
2025-06-21 19:31:41 +01:00
} ;
2025-06-24 01:05:55 +01:00
TaskAttemptActivity ::create ( pool , & create_activity , activity_id , activity_status )
. await
. map ( | _ | ( ) )
. map_err ( TaskAttemptError ::from )
}
2025-06-21 19:31:41 +01:00
2025-06-24 01:05:55 +01:00
/// Execute the process based on type
async fn execute_process (
executor_type : & crate ::executor ::ExecutorType ,
pool : & SqlitePool ,
task_id : Uuid ,
attempt_id : Uuid ,
process_id : Uuid ,
worktree_path : & str ,
2025-06-27 16:18:35 +01:00
) -> Result < command_group ::AsyncGroupChild , TaskAttemptError > {
2025-06-24 16:50:58 +01:00
use crate ::executors ::{ DevServerExecutor , SetupScriptExecutor } ;
2025-06-20 23:20:38 +01:00
2025-06-24 01:05:55 +01:00
let result = match executor_type {
2025-06-20 23:20:38 +01:00
crate ::executor ::ExecutorType ::SetupScript ( script ) = > {
2025-06-24 01:05:55 +01:00
let executor = SetupScriptExecutor {
script : script . clone ( ) ,
} ;
2025-06-20 23:20:38 +01:00
executor
. execute_streaming ( pool , task_id , attempt_id , process_id , worktree_path )
. await
}
2025-06-24 16:50:58 +01:00
crate ::executor ::ExecutorType ::DevServer ( script ) = > {
let executor = DevServerExecutor {
script : script . clone ( ) ,
} ;
executor
. execute_streaming ( pool , task_id , attempt_id , process_id , worktree_path )
. await
}
2025-06-20 23:20:38 +01:00
crate ::executor ::ExecutorType ::CodingAgent ( config ) = > {
let executor = config . create_executor ( ) ;
executor
. execute_streaming ( pool , task_id , attempt_id , process_id , worktree_path )
. await
}
2025-06-24 01:05:55 +01:00
crate ::executor ::ExecutorType ::FollowUpCodingAgent {
config ,
session_id ,
prompt ,
} = > {
2025-06-25 18:23:50 +01:00
use crate ::executors ::{
2025-06-30 11:55:55 +01:00
AmpFollowupExecutor , ClaudeFollowupExecutor , GeminiFollowupExecutor ,
OpencodeFollowupExecutor ,
2025-06-25 18:23:50 +01:00
} ;
2025-06-24 01:05:55 +01:00
let executor : Box < dyn crate ::executor ::Executor > = match config {
crate ::executor ::ExecutorConfig ::Claude = > {
if let Some ( sid ) = session_id {
Box ::new ( ClaudeFollowupExecutor {
session_id : sid . clone ( ) ,
prompt : prompt . clone ( ) ,
} )
} else {
return Err ( TaskAttemptError ::TaskNotFound ) ; // No session ID for followup
}
}
crate ::executor ::ExecutorConfig ::Amp = > {
if let Some ( tid ) = session_id {
Box ::new ( AmpFollowupExecutor {
thread_id : tid . clone ( ) ,
prompt : prompt . clone ( ) ,
} )
} else {
return Err ( TaskAttemptError ::TaskNotFound ) ; // No thread ID for followup
}
}
2025-06-25 18:23:50 +01:00
crate ::executor ::ExecutorConfig ::Gemini = > {
if let Some ( sid ) = session_id {
Box ::new ( GeminiFollowupExecutor {
session_id : sid . clone ( ) ,
prompt : prompt . clone ( ) ,
} )
} else {
return Err ( TaskAttemptError ::TaskNotFound ) ; // No session ID for followup
}
}
2025-06-24 01:05:55 +01:00
crate ::executor ::ExecutorConfig ::Echo = > {
// Echo doesn't support followup, use regular echo
config . create_executor ( )
}
2025-06-27 21:45:00 +01:00
crate ::executor ::ExecutorConfig ::Opencode = > {
if let Some ( sid ) = session_id {
Box ::new ( OpencodeFollowupExecutor {
session_id : sid . clone ( ) ,
prompt : prompt . clone ( ) ,
} )
} else {
return Err ( TaskAttemptError ::TaskNotFound ) ; // No session ID for followup
}
}
2025-06-24 01:05:55 +01:00
} ;
executor
. execute_streaming ( pool , task_id , attempt_id , process_id , worktree_path )
. await
}
} ;
result . map_err ( | e | TaskAttemptError ::Git ( git2 ::Error ::from_str ( & e . to_string ( ) ) ) )
}
2025-06-20 23:20:38 +01:00
2025-06-24 01:05:55 +01:00
/// Register process for monitoring
async fn register_for_monitoring (
app_state : & crate ::app_state ::AppState ,
process_id : Uuid ,
attempt_id : Uuid ,
process_type : & crate ::models ::execution_process ::ExecutionProcessType ,
2025-06-27 16:18:35 +01:00
child : command_group ::AsyncGroupChild ,
2025-06-24 01:05:55 +01:00
) {
2025-06-20 23:20:38 +01:00
let execution_type = match process_type {
crate ::models ::execution_process ::ExecutionProcessType ::SetupScript = > {
crate ::app_state ::ExecutionType ::SetupScript
}
crate ::models ::execution_process ::ExecutionProcessType ::CodingAgent = > {
crate ::app_state ::ExecutionType ::CodingAgent
}
crate ::models ::execution_process ::ExecutionProcessType ::DevServer = > {
crate ::app_state ::ExecutionType ::DevServer
}
} ;
2025-06-19 11:09:32 -04:00
2025-06-20 21:46:28 +01:00
app_state
. add_running_execution (
2025-06-20 23:20:38 +01:00
process_id ,
2025-06-20 22:14:31 +01:00
crate ::app_state ::RunningExecution {
2025-06-19 11:09:32 -04:00
task_attempt_id : attempt_id ,
2025-06-27 13:32:32 +01:00
_execution_type : execution_type ,
2025-06-19 11:09:32 -04:00
child ,
} ,
2025-06-20 21:46:28 +01:00
)
. await ;
2025-06-20 23:20:38 +01:00
}
2025-06-19 18:59:47 -04:00
/// Get the git diff between the base commit and the current committed worktree state
2025-06-16 23:47:16 -04:00
pub async fn get_diff (
2025-06-17 15:22:47 -04:00
pool : & SqlitePool ,
2025-06-16 23:47:16 -04:00
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
) -> Result < WorktreeDiff , TaskAttemptError > {
// Get the task attempt with validation
let attempt = sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT ta.id as " id ! : Uuid " , ta.task_id as " task_id ! : Uuid " , ta.worktree_path, ta.branch, ta.base_branch, ta.merge_commit, ta.executor, ta.pr_url, ta.pr_number, ta.pr_status, ta.pr_merged_at as " pr_merged_at : DateTime < Utc > " , ta.created_at as " created_at ! : DateTime < Utc > " , ta.updated_at as " updated_at ! : DateTime < Utc > "
2025-06-16 23:47:16 -04:00
FROM task_attempts ta
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1 AND t . id = $ 2 AND t . project_id = $ 3 " #,
attempt_id ,
task_id ,
project_id
)
. fetch_optional ( pool )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
2025-06-22 23:43:13 +01:00
// Get the project to access the main repository
2025-06-19 21:28:36 -04:00
let project = Project ::find_by_id ( pool , project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
2025-06-16 23:47:16 -04:00
let mut files = Vec ::new ( ) ;
2025-06-22 23:43:13 +01:00
if let Some ( merge_commit_id ) = & attempt . merge_commit {
// Task attempt has been merged - show the diff from the merge commit
let main_repo = Repository ::open ( & project . git_repo_path ) ? ;
let merge_commit = main_repo . find_commit ( git2 ::Oid ::from_str ( merge_commit_id ) ? ) ? ;
// A merge commit has multiple parents - first parent is the main branch before merge,
// second parent is the branch that was merged
let parents : Vec < _ > = merge_commit . parents ( ) . collect ( ) ;
2025-06-24 17:22:00 +01:00
// Create diff options with more context
let mut diff_opts = git2 ::DiffOptions ::new ( ) ;
diff_opts . context_lines ( 10 ) ; // Include 10 lines of context around changes
diff_opts . interhunk_lines ( 0 ) ; // Don't merge hunks
2025-06-22 23:43:13 +01:00
let diff = if parents . len ( ) > = 2 {
let base_tree = parents [ 0 ] . tree ( ) ? ; // Main branch before merge
let merged_tree = parents [ 1 ] . tree ( ) ? ; // The branch that was merged
2025-06-25 09:27:29 +01:00
main_repo . diff_tree_to_tree (
Some ( & base_tree ) ,
Some ( & merged_tree ) ,
Some ( & mut diff_opts ) ,
) ?
2025-06-22 23:43:13 +01:00
} else {
// Fast-forward merge or single parent - compare merge commit with its parent
let base_tree = if ! parents . is_empty ( ) {
parents [ 0 ] . tree ( ) ?
} else {
// No parents (shouldn't happen), use empty tree
main_repo . find_tree ( git2 ::Oid ::zero ( ) ) ?
} ;
let merged_tree = merge_commit . tree ( ) ? ;
2025-06-25 09:27:29 +01:00
main_repo . diff_tree_to_tree (
Some ( & base_tree ) ,
Some ( & merged_tree ) ,
Some ( & mut diff_opts ) ,
) ?
2025-06-22 23:43:13 +01:00
} ;
// Process each diff delta (file change)
diff . foreach (
& mut | delta , _progress | {
if let Some ( path_str ) = delta . new_file ( ) . path ( ) . and_then ( | p | p . to_str ( ) ) {
let old_file = delta . old_file ( ) ;
let new_file = delta . new_file ( ) ;
// Get old content
let old_content = if ! old_file . id ( ) . is_zero ( ) {
match main_repo . find_blob ( old_file . id ( ) ) {
Ok ( blob ) = > String ::from_utf8_lossy ( blob . content ( ) ) . to_string ( ) ,
Err ( _ ) = > String ::new ( ) ,
}
} else {
String ::new ( ) // File didn't exist in base commit
} ;
// Get new content
let new_content = if ! new_file . id ( ) . is_zero ( ) {
match main_repo . find_blob ( new_file . id ( ) ) {
Ok ( blob ) = > String ::from_utf8_lossy ( blob . content ( ) ) . to_string ( ) ,
Err ( _ ) = > String ::new ( ) ,
}
} else {
String ::new ( ) // File was deleted
} ;
// Generate Git-native diff chunks
if old_content ! = new_content {
match Self ::generate_git_diff_chunks (
& main_repo , & old_file , & new_file , path_str ,
) {
Ok ( diff_chunks ) if ! diff_chunks . is_empty ( ) = > {
files . push ( FileDiff {
path : path_str . to_string ( ) ,
chunks : diff_chunks ,
} ) ;
}
Err ( e ) = > {
eprintln! ( " Error generating diff for {} : {:?} " , path_str , e ) ;
}
_ = > { }
}
2025-06-19 18:59:47 -04:00
}
2025-06-22 23:43:13 +01:00
}
true // Continue processing
} ,
None ,
None ,
None ,
) ? ;
} else {
// Task attempt not yet merged - use the original logic with fork point
let worktree_repo = Repository ::open ( & attempt . worktree_path ) ? ;
let main_repo = Repository ::open ( & project . git_repo_path ) ? ;
let main_head_oid = main_repo . head ( ) ? . peel_to_commit ( ) ? . id ( ) ;
// Get the current worktree HEAD commit
let worktree_head = worktree_repo . head ( ) ? ;
let worktree_head_oid = worktree_head . peel_to_commit ( ) ? . id ( ) ;
// Find the merge base (common ancestor) between main and the worktree branch
// This represents the point where the worktree branch forked off from main
let base_oid = worktree_repo . merge_base ( main_head_oid , worktree_head_oid ) ? ;
let base_commit = worktree_repo . find_commit ( base_oid ) ? ;
let base_tree = base_commit . tree ( ) ? ;
// Get the current tree from the worktree HEAD commit we already retrieved
let current_commit = worktree_repo . find_commit ( worktree_head_oid ) ? ;
let current_tree = current_commit . tree ( ) ? ;
2025-06-24 17:22:00 +01:00
// Create a diff between the base tree and current tree with more context
let mut diff_opts = git2 ::DiffOptions ::new ( ) ;
diff_opts . context_lines ( 10 ) ; // Include 10 lines of context around changes
diff_opts . interhunk_lines ( 0 ) ; // Don't merge hunks
2025-06-25 09:27:29 +01:00
let diff = worktree_repo . diff_tree_to_tree (
Some ( & base_tree ) ,
Some ( & current_tree ) ,
Some ( & mut diff_opts ) ,
) ? ;
2025-06-22 23:43:13 +01:00
// Process each diff delta (file change)
diff . foreach (
& mut | delta , _progress | {
if let Some ( path_str ) = delta . new_file ( ) . path ( ) . and_then ( | p | p . to_str ( ) ) {
let old_file = delta . old_file ( ) ;
let new_file = delta . new_file ( ) ;
// Get old content
let old_content = if ! old_file . id ( ) . is_zero ( ) {
match worktree_repo . find_blob ( old_file . id ( ) ) {
Ok ( blob ) = > String ::from_utf8_lossy ( blob . content ( ) ) . to_string ( ) ,
Err ( _ ) = > String ::new ( ) ,
2025-06-22 23:09:01 +01:00
}
2025-06-22 23:43:13 +01:00
} else {
String ::new ( ) // File didn't exist in base commit
} ;
// Get new content
let new_content = if ! new_file . id ( ) . is_zero ( ) {
match worktree_repo . find_blob ( new_file . id ( ) ) {
Ok ( blob ) = > String ::from_utf8_lossy ( blob . content ( ) ) . to_string ( ) ,
Err ( _ ) = > String ::new ( ) ,
}
} else {
String ::new ( ) // File was deleted
} ;
// Generate Git-native diff chunks
if old_content ! = new_content {
match Self ::generate_git_diff_chunks (
& worktree_repo ,
& old_file ,
& new_file ,
path_str ,
) {
Ok ( diff_chunks ) if ! diff_chunks . is_empty ( ) = > {
files . push ( FileDiff {
path : path_str . to_string ( ) ,
chunks : diff_chunks ,
} ) ;
}
Err ( e ) = > {
eprintln! ( " Error generating diff for {} : {:?} " , path_str , e ) ;
}
_ = > { }
2025-06-22 23:09:01 +01:00
}
2025-06-20 16:35:17 +01:00
}
2025-06-16 23:47:16 -04:00
}
2025-06-22 23:43:13 +01:00
true // Continue processing
} ,
None ,
None ,
None ,
) ? ;
Redesign sidebar (#63)
* Agent logs
* ## Summary
I have successfully implemented a comprehensive log normalization system for vibe-kanban with the following features:
### ✅ **Completed Tasks:**
1. **Defined normalized log format types** - Created standard TypeScript-exportable types:
- `NormalizedConversation` - Top-level container with entries, session ID, and executor type
- `NormalizedEntry` - Individual conversation entries with timestamp, type, and content
- `NormalizedEntryType` - Union type for different entry types (user, assistant, tool use, system, thinking)
- `ActionType` - Specific action types for tool operations (file read/write, commands, search, etc.)
2. **Implemented normalize method for AMP executor** - Parses AMP's JSONL format to extract:
- User and assistant messages
- Thinking content
- Tool usage with intelligent action type detection
- Session ID (threadID)
- Timestamps from message metadata
3. **Implemented normalize method for Claude executor** - Parses Claude's stream-json format to extract:
- User and assistant messages
- Tool usage with action type detection
- System initialization messages
- Session ID
- Comprehensive tool mapping for Claude-specific tools
4. **Added normalize method to executor trait** - Created a default implementation that returns empty results for unsupported executors, allowing easy extension to other executor types
5. **Created new API endpoint for normalized logs** - Added `GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint that:
- Validates execution process belongs to the correct project
- Determines executor type automatically
- Applies appropriate normalization logic
- Returns standardized conversation format
6. **Tested the implementation** - Created comprehensive unit tests for both AMP and Claude normalization, verifying correct parsing of real log examples
### 🔧 **Technical Implementation Details:**
- **Intelligent Action Type Detection**: The system maps tool names to specific action types (file operations, commands, searches, etc.) based on tool names and input parameters
- **Cross-platform Compatibility**: Handles different parameter naming conventions between executors
- **Error Handling**: Robust error handling with detailed error messages for debugging
- **TypeScript Integration**: Full TypeScript type generation for frontend consumption
- **Extensible Design**: Easy to add new executors by implementing the `normalize_logs` method
### 🚀 **Usage:**
The new endpoint can be called to get a normalized view of any execution process logs:
```bash
GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs
```
This returns a standardized conversation format that the frontend can easily consume to display:
- User inputs and assistant responses
- Tool usage with specific action details (file paths, commands, etc.)
- Internal reasoning (thinking)
- System status messages
- Session tracking information
The normalized format provides a high-level, consistent representation of the conversation regardless of which executor (AMP, Claude, etc.) generated the original logs.
* Perfect! I have successfully implemented the normalized logs feature. Here's a summary of what was accomplished:
## Summary
✅ **Replaced manual log rendering with normalized logs API**
### Key Changes Made:
1. **Created a new `NormalizedConversationViewer` component** that:
- Fetches normalized logs from the backend API endpoint
- Displays conversation entries with appropriate icons for each type:
- 👤 User messages (blue)
- 🤖 Assistant messages (green)
- ⚙️ System messages (gray)
- 🧠 Thinking (purple)
- 👁️ File reads (orange)
- ✏️ File writes (red)
- 💻 Command execution (yellow)
- 🔍 Search operations (indigo)
- 🌐 Web fetches (cyan)
- ➕ Task creation (teal)
- Shows timestamps and entry type labels
- Handles loading and error states
2. **Updated `TaskActivityHistory` component** to:
- Use the new `NormalizedConversationViewer` instead of `ExecutionOutputViewer`
- Pass the required `projectId` prop
- Maintain the same expansion/collapse functionality
3. **Updated `TaskDetailsPanel`** to pass the `projectId` to the activity history
4. **Removed legacy components**:
- The complex `ExecutionOutputViewer` that manually parsed JSONL
- The `ConversationViewer` that handled format-specific parsing
- All the manual stdout/stderr rendering logic
### Benefits:
- **Cleaner codebase**: Eliminated complex frontend parsing logic
- **Consistent display**: All executors now show logs in the same format
- **Better UX**: Clear icons and labels make it easy to understand conversation flow
- **Maintainable**: Backend handles format normalization, frontend just displays
- **Extensible**: New executor types automatically work with the existing UI
The implementation leverages the existing `/api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint and properly handles loading states, errors, and displays the conversation history with meaningful icons for each entry type.
* Excellent! The build is successful and the bundle size is slightly smaller after removing the unused code. The task is now complete:
✅ **Legacy log rendering components successfully removed**
- Deleted `ExecutionOutputViewer.tsx` (224 lines)
- Deleted `ConversationViewer.tsx` (complex JSONL parsing logic)
- Build passes and bundle size reduced
- All functionality now uses the clean normalized logs API
The codebase is now cleaner and fully transitioned to using the normalized logs system.
* Perfect! I've successfully refactored the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) component with the new layout:
## Key Changes Made:
1. **Top 2/3 - Code Changes Section**:
- Extracted all the diff processing logic from [`task-attempt-compare.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/pages/task-attempt-compare.tsx)
- Shows file diffs with syntax highlighting, expandable sections, and line numbers
- Includes file deletion functionality
- "Show/Hide All Unchanged" toggle for better navigation
2. **Bottom 1/3 - Agent Logs Section**:
- Preserved the existing [`TaskActivityHistory`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskActivityHistory.tsx) component
- Auto-scrolling behavior for new log entries
- Clear separation with border and background styling
3. **Maintained Existing Functionality**:
- Header, toolbar, and follow-up sections remain unchanged
- Editor selection dialog and ESC key handling preserved
- All existing hooks and state management intact
The component now provides a much better developer experience by showing code changes prominently in the top section while keeping the agent logs easily accessible in the bottom third.
* Perfect! The build is successful. I've implemented all the requested style changes to the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
**Code Changes Section (Top 2/3):**
- ✅ Removed "Code Changes" header
- ✅ Removed description text
- ✅ Removed "Show All Unchanged" button and related functionality
- ✅ Removed parent Card wrapper - each file diff is now directly displayed in its own card
**Agent Logs Section (Bottom 1/3):**
- ✅ Removed "Agent Logs" header
- ✅ Removed description text
- ✅ Now displays only the `NormalizedConversationViewer` component
- ✅ Removed card/container wrapper
The component now has a cleaner, more streamlined interface with the code changes taking up most of the space in the top section, and the agent conversation displayed directly in the bottom third without extra headers or containers.
* Perfect! The build is successful. I've updated the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) to properly find and display the coding agent conversation.
## Changes Made:
The component now:
1. **First looks** for any running coding agent process in `runningProcessDetails`
2. **If not found**, searches through all processes in the `processes` array to find any coding agent process (completed or otherwise)
3. **Creates a minimal ExecutionProcess object** from the summary data if needed, since the `NormalizedConversationViewer` only requires the process ID to fetch the conversation data
4. **Displays the conversation** for any coding agent process found, whether running or completed
This should resolve the "Unsupported executor type: unknown" error and properly show the normalized conversation logs from the coding agent in the bottom section.
* Perfect! I've successfully updated the [`NormalizedConversationViewer.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/NormalizedConversationViewer.tsx) component with the requested style changes:
## Changes Made:
1. **Removed metadata header** - No longer shows executor type, session ID, or process type badges
2. **Removed Card wrapper** - The conversation entries are now displayed directly without any card container or special background
3. **Removed timestamps** - Each entry no longer shows the date/time information
4. **Cleaned up imports** - Removed unused imports for Card, CardContent, and Badge components
The normalized logs now display as a clean, minimal conversation with just the entry type icon, entry type label, and content for each message in the agent conversation.
* Perfect! I've made the requested style improvements to the diffs in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Removed max height limit** - Each file diff card no longer has a `max-h-[400px] overflow-y-auto` constraint, so the entire diff content is visible without individual scrolling per file
2. **Enhanced diff line colors** - Updated the background colors for added and removed lines:
- **Added lines** (Insert): Now use `bg-green-100` (light mode) and `bg-green-900/40` (dark mode) with stronger green text colors
- **Removed lines** (Delete): Now use `bg-red-100` (light mode) and `bg-red-900/40` (dark mode) with stronger red text colors
- Made the border colors more prominent with `border-green-500` and `border-red-500`
The diffs now display with clearer visual distinction between added and removed lines, and each file shows its complete content without requiring individual scrolling.
* Perfect! I've fixed the line number background colors in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added `getLineNumberClassName` function** - This creates appropriate styling for line numbers based on the chunk type:
- **Added lines**: Green background (`bg-green-50`/`bg-green-900/20`) with green text and borders
- **Removed lines**: Red background (`bg-red-50`/`bg-red-900/20`) with red text and borders
- **Unchanged lines**: Gray background (original styling)
2. **Updated line number div** - Replaced the hardcoded gray styling with a call to `getLineNumberClassName(line.chunkType)`
Now the line numbers will have matching background colors with their corresponding lines - green for additions, red for deletions, and gray for unchanged context lines. This creates a more cohesive visual experience for the diff display.
* Perfect! I've updated the line number background colors to match the main line content exactly:
## Changes Made:
- **Added lines**: Line numbers now use `bg-green-100 dark:bg-green-900/40` (same as the main content)
- **Removed lines**: Line numbers now use `bg-red-100 dark:bg-red-900/40` (same as the main content)
Now the line numbers have the same background intensity as the main line content, creating a consistent visual appearance across the entire diff line without the lighter/more transparent look.
* Perfect! I've made all the requested improvements to the diff styling in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added horizontal scrolling** - The diff container now has `overflow-x-auto` and each line has `min-w-max` to ensure long lines can be scrolled horizontally
2. **Removed thick border** - Removed the `border-l-2 border-green-500` and `border-l-2 border-red-500` thick left borders from added/removed lines
3. **GitHub-style background intensity** - Now follows GitHub's pattern:
- **Line content**: Lighter background (`bg-green-50`/`bg-red-50` for light mode, `bg-green-900/20`/`bg-red-900/20` for dark mode)
- **Line numbers**: Darker background (`bg-green-100`/`bg-red-100` for light mode, `bg-green-900/40`/`bg-red-900/40` for dark mode)
The diff now has a cleaner appearance without the heavy borders, proper horizontal scrolling for long lines, and follows GitHub's visual hierarchy where line numbers have more prominent background colors than the content.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Added `py-1` to the `getLineNumberClassName` function so the line number background now has the same vertical padding as the content lines, ensuring full height coverage
2. **Fixed horizontal scrolling layout** - Wrapped the diff content in a proper container structure:
- Added `min-w-full` wrapper inside the `overflow-x-auto` container
- This ensures that elements like horizontal lines and borders aren't cut off during horizontal scrolling
- The scrolling now works properly without affecting other page elements
The diff now has proper horizontal scrolling that doesn't interfere with other page elements, and the line number backgrounds extend to the full height of each line.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Both the line numbers and content now use `py-1` consistently, ensuring the backgrounds match in height perfectly
2. **Fixed horizontal scrolling** - Changed the approach:
- Used `whitespace-nowrap` instead of `whitespace-pre` to prevent wrapping
- Used `min-w-max` on each line to ensure they extend naturally
- The overflow scrolling now works at the container level without cutting off other elements
- Removed the problematic nested wrapper structure
The line number backgrounds now extend to the full height of each line, and horizontal scrolling works properly without affecting other page elements like borders and buttons.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height gaps**:
- Used `min-h-[1.75rem] flex items-center` for both line numbers and content divs
- This ensures both backgrounds have exactly the same height with no gaps above or below
- Removed separate `py-1` padding that was causing misalignment
2. **Fixed horizontal scroll border cutoff**:
- Added `min-w-max` to the "show X more lines" buttons
- This ensures the buttons extend to the full width of the scrollable content
- The borders now remain visible even when scrolling horizontally
Now the line number backgrounds perfectly match the line content height with no color gaps, and the expand/collapse buttons maintain their borders during horizontal scrolling.
* Perfect! I've fixed both remaining horizontal scroll issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed highlighted line backgrounds during scroll**:
- Changed from `min-w-max` to `w-full` for the base class
- Added `style={{ minWidth: 'max-content' }}` to ensure lines extend to natural content width
- Used `inline-block min-w-full` container to create proper scrollable width
- Now all highlighted lines (green/red) maintain their background color across the full scrollable width
2. **Fixed "show more lines" button borders**:
- Wrapped buttons in `div className="w-full"` containers
- Removed `min-w-max` from button classes and used `w-full`
- The buttons now properly extend across the full scrollable width
- Borders remain visible during horizontal scrolling
Now the diff view has proper horizontal scrolling where:
- All highlighted lines maintain their background colors across the entire scroll width
- The "show X more lines" buttons maintain their borders when scrolling
- The entire diff content scrolls cohesively
* Perfect! I've fixed the indentation issue in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Fixed indentation preservation**: Changed from `whitespace-nowrap` to `whitespace-pre` in the `getChunkClassName` function
Now the diff properly preserves:
- Code indentation (spaces and tabs)
- All whitespace formatting
- Line spacing as it appears in the original code
The horizontal scrolling functionality remains intact while properly displaying the code formatting and indentation structure.
* Perfect! I've right-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Added `justify-end`** to both "Show X more lines" and "Hide expanded lines" buttons
Now the button text ("Show X more lines" and "Hide expanded lines") is right-aligned, which means:
- The text stays visible and consistent during horizontal scrolling
- Users can always see the action text regardless of scroll position
- The interface feels more stable when navigating wide diffs
* Perfect! I've left-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Changed `justify-end` to `justify-start`** for both "Show X more lines" and "Hide expanded lines" buttons
Now the button text is left-aligned, which means:
- The text appears at the left edge of the button and stays visible during horizontal scrolling
- Users can always see the action text at a consistent position
- The interface aligns with the left-aligned line numbers below it
* ## Summary
I've successfully improved the normalized logs to show more concise content. Here's what was changed:
### Key Improvements
1. **File Operations**: Now show just the file path (e.g., `"src/components/TaskDialog.tsx"`) instead of verbose tool descriptions
2. **Commands**: Now show just the bash command (e.g., `"npm run build"`) instead of full tool invocation details
3. **Search Operations**: Now show just the search query (e.g., `"authentication headers"`) instead of tool metadata
4. **Web Requests**: Now show just the URL (e.g., `"https://api.example.com/data"`) instead of verbose descriptions
5. **Other Tools**: Now show concise, meaningful descriptions (e.g., `"Managing TODO list"` for todo tools)
### Changes Made
1. **Updated AMP Executor** (`backend/src/executors/amp.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
2. **Updated Claude Executor** (`backend/src/executors/claude.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
3. **Updated Documentation** (`test_normalized_logs_api.md`):
- Updated example response to show the new concise format
- Added detailed explanation of the new content format with examples
4. **Added Tests**:
- Enhanced existing tests to verify the new concise content format
- Tests confirm that `todo_write` shows "Managing TODO list" instead of verbose JSON
- Tests confirm that `Task` tool shows the task description directly
### Before and After
**Before:**
```json
{
"content": "Tool: todo_write with input: {\"todos\":[{\"id\":\"1\",\"content\":\"Explore task creation dialog component\",\"status\":\"todo\",\"priority\":\"high\"}]}"
}
```
**After:**
```json
{
"content": "Managing TODO list"
}
```
The implementation now provides much cleaner, more actionable content in the normalized conversation logs while maintaining all the rich metadata in the `action_type` field for programmatic access.
* Perfect! The frontend builds successfully. Now the normalized conversation logs will display much cleaner - just showing the appropriate icon next to the concise content without any redundant headers.
## Summary
I've removed the headers from the normalized conversation log display. Now instead of showing:
**Before:**
```
[file icon] Read: src/components/TaskDialog.tsx
src/components/TaskDialog.tsx
[command icon] Command: npm run build
npm run build
```
**After:**
```
[file icon] src/components/TaskDialog.tsx
[command icon] npm run build
[search icon] authentication headers
[todo icon] Managing TODO list
```
### Changes Made
1. **Removed redundant header display** - The `getEntryTypeDisplay` function and its usage has been removed
2. **Simplified layout** - Each log entry now shows just the icon and concise content directly
3. **Cleaner UI** - No more redundant information since the content is already concise and the icon provides the context
The display is now much cleaner and more efficient, with the icon providing the visual context for the action type and the concise content providing the essential information.
* Perfect! The frontend builds successfully.
## Summary
I've updated the NormalizedConversationViewer to render terminal commands with a monospace font. Now:
### Changes Made
1. **Added `getContentClassName` function** - This function conditionally applies styling based on the entry type
2. **Applied monospace font for commands** - When the entry type is a tool_use with action_type of "command_run", it applies the `font-mono` Tailwind class
3. **Maintained existing styling** - All other entry types keep the same styling as before
### Result
Now terminal commands like:
- `npm run build`
- `cargo check`
- `git status`
- `ls -la`
Will be displayed in a monospace font next to the terminal icon, making them more readable and clearly distinguishable as code/commands, while other content like file paths, search queries, and regular text continue to use the default font.
The monospace font makes commands much easier to read and gives them the appropriate technical appearance that users expect for terminal output.
* Excellent! Everything builds successfully.
## Summary
I've implemented real-time conversation updates with smart auto-scrolling functionality. Here's what was added:
### Key Features
1. **Auto-refresh**: The conversation automatically polls for updates every 2 seconds when the execution process is running
2. **Smart auto-scrolling**: Automatically scrolls to the bottom when new content arrives
3. **User scroll detection**: Stops auto-scrolling when the user manually scrolls up
4. **Auto-scroll resumption**: Resumes auto-scrolling when the user scrolls back to the bottom
5. **Efficient updates**: Only updates the conversation state when content actually changes
### Implementation Details
**Auto-refresh Logic:**
- Polls every 2 seconds when `executionProcess.status === 'running'`
- Uses efficient content comparison to avoid unnecessary re-renders
- Gracefully handles polling errors without disrupting the UI
**Smart Scrolling:**
- Tracks user scroll behavior vs automatic scrolling
- Uses a 5px threshold to detect "at bottom" state
- Provides smooth auto-scrolling with a small delay to ensure DOM updates
**User Experience:**
- Container has `max-h-96` with `overflow-y-auto` for scrollable content
- Terminal commands display in monospace font
- Clean display with just icons and concise content
- No disruption when user wants to review previous logs
### Behavior
- ✅ **Auto-updates**: Content refreshes every 2 seconds during execution
- ✅ **Auto-scroll**: New content automatically scrolls into view
- ✅ **Manual control**: User can scroll up to review previous logs
- ✅ **Resume auto-scroll**: When user scrolls back to bottom, auto-scrolling resumes
- ✅ **Efficient**: Only re-renders when content actually changes
- ✅ **Clean UI**: Terminal commands in monospace, no redundant headers
The conversation viewer now provides a seamless, real-time experience that follows the execution while respecting user control over scrolling behavior.
* ## Summary
I've successfully fixed the scroll-within-scroll issue in the TaskDetailsPanel. Here's what was changed:
**Problem**: The normalized logs section had nested scrolling containers:
- Parent container (TaskDetailsPanel) with `h-full overflow-y-auto`
- Child container (NormalizedConversationViewer) with `max-h-96 overflow-y-auto`
**Solution**: I removed the scrolling functionality from the `NormalizedConversationViewer` component by:
1. **Removed scroll container**: Changed from `<div ref={scrollContainerRef} className="space-y-2 max-h-96 overflow-y-auto" onScroll={handleScroll}>` to just `<div className="space-y-2">`
2. **Cleaned up scroll-related code**: Removed:
- State variables: `isUserScrolling`, `scrollContainerRef`, `scrollTimeoutRef`, `lastScrollTop`
- Functions: `isAtBottom`, `scrollToBottom`, `handleScroll`
- Effects: Auto-scroll on conversation updates and scroll timeout cleanup
- Unused imports: `useRef`
3. **Verified functionality**: The parent container in TaskDetailsPanel now handles all scrolling for the logs section, eliminating the annoying nested scroll behavior.
The build and lint checks pass successfully, and the user experience should now be much smoother without the competing scroll areas.
* ## Summary
I've successfully fixed the scroll-within-scroll issue while preserving the auto-scroll functionality. Here's what I implemented:
**Problem**: Nested scroll containers created annoying scroll-within-scroll behavior:
- Parent: TaskDetailsPanel with `h-full overflow-y-auto`
- Child: NormalizedConversationViewer with `max-h-96 overflow-y-auto`
**Solution**: Moved all scroll logic to the parent container while maintaining auto-scroll behavior:
### Changes Made:
1. **Removed inner scroll container** from NormalizedConversationViewer:
- Removed `max-h-96 overflow-y-auto` classes
- Removed all scroll-related state and functions
- Simplified to just `<div className="space-y-2">`
2. **Added callback interface** to NormalizedConversationViewer:
- Added `onConversationUpdate?: () => void` prop
- Called this callback when conversation data changes
- Used `setTimeout` to ensure state updates happen before auto-scroll
3. **Enhanced TaskDetailsPanel scroll logic**:
- Added `conversationUpdateTrigger` state to track conversation changes
- Added `handleConversationUpdate` callback function
- Extended existing auto-scroll effect to include conversation updates
- Maintained existing manual scroll detection logic
### Key Benefits:
✅ **No more nested scrolling** - Only the parent container scrolls
✅ **Auto-scroll preserved** - New messages still auto-scroll to bottom
✅ **Manual scroll respected** - Auto-scroll stops when user manually scrolls up
✅ **Smart resumption** - Auto-scroll resumes when user scrolls back to bottom
✅ **Seamless integration** - Works with existing activity and process log auto-scroll
The solution maintains all existing functionality while providing a much better user experience by eliminating the competing scroll areas.
* ## Summary
I've successfully updated the task attempts diff endpoint to include unstaged changes in addition to the existing committed changes. Here's what was implemented:
### Key Changes Made:
1. **Enhanced `get_diff` method** in [`backend/src/models/task_attempt.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-4fbf-update-dif/backend/src/models/task_attempt.rs#L1042-L1379):
- Added logic to capture unstaged changes using `diff_tree_to_workdir_with_index`
- For files with both committed and unstaged changes, creates a combined diff from the base branch to the current working directory
- For files with only unstaged changes, generates a standard diff
2. **New helper function** `process_unstaged_file`:
- Handles the processing of individual unstaged files
- Manages the complex logic of combining committed and unstaged changes
- Uses Git's native patch generation for accurate diff representation
### How It Works:
**For merged task attempts:** Shows the original merge commit diff (unchanged behavior)
**For active task attempts:**
1. **First pass:** Generates diff from base branch to current HEAD commit (committed changes)
2. **Second pass:** Finds files with unstaged changes in the working directory
3. **Combining logic:**
- **Files with both committed & unstaged changes:** Creates a combined diff from base branch directly to working directory content
- **Files with only unstaged changes:** Adds them as separate diff entries
- **Files with only committed changes:** Keeps original diff (unchanged)
### Benefits:
- ✅ **Complete visibility:** Users can now see both committed and uncommitted work
- ✅ **Unified view:** All changes are presented in a single coherent diff
- ✅ **Backward compatible:** Existing functionality for merged attempts remains unchanged
- ✅ **Accurate representation:** Uses Git's native diff algorithms for consistent formatting
The implementation maintains the existing API structure while extending functionality to provide a more comprehensive view of all changes in a task attempt.
* Perfect! I have successfully implemented the dynamic task details section that updates based on execution state. Here's a summary of what was accomplished:
## Summary
I implemented a comprehensive solution to make the details section dynamically update based on the execution state:
### Backend Changes:
1. **Added new execution state types** in `backend/src/models/task_attempt.rs`:
- `ExecutionState` enum with states: NotStarted, SetupRunning, SetupComplete, SetupFailed, CodingAgentRunning, CodingAgentComplete, CodingAgentFailed, Complete
- `TaskAttemptState` struct containing execution state, change detection, and process IDs
2. **Created `get_execution_state` method** that analyzes:
- Project setup script configuration
- Running execution processes
- Process status and completion
- Whether changes have been made to the worktree
3. **Added new API endpoint** `GET /projects/:project_id/tasks/:task_id/attempts/:attempt_id` to fetch execution state
4. **Updated type generation** to include the new types in the shared TypeScript definitions
### Frontend Changes:
1. **Enhanced useTaskDetails hook** to:
- Poll the execution state endpoint every 2 seconds
- Fetch execution state when switching attempts
- Provide execution state data to components
2. **Completely redesigned TaskDetailsPanel** with dynamic content rendering:
- **Setup Script Running**: Shows loading spinner with "Setup Script Running" message
- **Setup Failed**: Shows error message
- **Coding Agent Running (no changes)**: Shows full-height agent conversation only
- **Changes Available**: Shows 2/3 changes + 1/3 conversation split view
- **Default**: Shows "execution not started" message
### Key Features:
- **Real-time Updates**: Polls execution state every 2 seconds during execution
- **Progressive Disclosure**: Shows appropriate content based on execution phase
- **State-Aware UI**: Different layouts for different execution states
- **Error Handling**: Proper error states for failed setup scripts
- **Type Safety**: Full TypeScript support for execution states
The implementation ensures that users see relevant information at each stage of task execution, providing better visibility into what's happening and reducing confusion about the current state.
* Perfect! The fix is now in place. Here's what I've done to solve the loading spinner issue:
## Fix Summary
The problem was that the execution state was only being fetched during:
1. Polling (when `isAttemptRunning` is true)
2. Manual attempt switching via `handleAttemptChange`
But it was **not** being fetched during the initial page load when an attempt was automatically selected.
## Changes Made
1. **Added useEffect for selectedAttempt changes**: This ensures that whenever `selectedAttempt` changes (including during initial load), both `fetchAttemptData` and `fetchExecutionState` are called.
2. **Updated fetchTaskAttempts**: Added `fetchExecutionState` call when setting the latest attempt during initial load, and included it in the dependency array.
## How the Fix Works
Now when the page reloads:
1. `fetchTaskAttempts` runs and finds the latest attempt
2. It sets `selectedAttempt` to the latest attempt
3. The new useEffect detects the `selectedAttempt` change and immediately calls both:
- `fetchAttemptData(selectedAttempt.id)`
- `fetchExecutionState(selectedAttempt.id)`
4. The execution state is loaded, so the appropriate content is shown instead of the loading spinner
This ensures that the execution state is always available when displaying attempt details, whether it's the initial load, manual switching between attempts, or during polling updates.
* Perfect! The changes have been successfully implemented. Here's a summary of what I've done:
## Changes Made
### 1. Setup Script Stdio Display
- **Modified the setup running state** to show the actual stdout/stderr of the setup script instead of just a spinner
- **Enhanced process lookup** to find the setup script execution process from either the execution state's `setup_process_id` or by searching for processes with type `setupscript`
- **Added terminal-style output display** with:
- Black background and green text (terminal aesthetic)
- Monospace font for proper formatting
- Scrollable area with max height
- Fallback to "No output yet..." when no output is available
### 2. Diff Refresh During Coding Agent Execution
- **Added automatic diff polling** when the coding agent is running
- **Polls every 3 seconds** during `CodingAgentRunning` state to catch file changes as they happen
- **Uses existing fetchDiff function** to maintain consistency with manual refresh behavior
### 3. Enhanced Process Data Fetching
- **Modified fetchAttemptData** to always fetch setup script process details when available
- **Ensures setup script output is available** even when the script has completed but we want to show its output
- **Maintains existing logic** for other running processes while adding setup script support
## Key Features
1. **Real-time Setup Script Output**: Users can now see exactly what the setup script is doing, including any errors or progress messages.
2. **Live Diff Updates**: When the coding agent is making changes, the diff view automatically refreshes every 3 seconds so users can see files being modified in real-time.
3. **Better Visual Feedback**: The terminal-style output for setup scripts provides a familiar development experience.
4. **Non-blocking Updates**: The diff refresh happens independently and doesn't interfere with other UI interactions.
The implementation ensures that users get immediate feedback during both phases of execution - setup and coding - making the development process much more transparent and engaging.
* Perfect! Both issues have been fixed:
## Summary of Fixes
### 1. Setup Script Output Display
**Fixed**: Combined stdout and stderr properly
- **Before**: Only showed stdout OR stderr (whichever existed first)
- **After**: Shows both stdout AND stderr concatenated together
- **Implementation**:
```javascript
const stdout = setupProcess.stdout || '';
const stderr = setupProcess.stderr || '';
const combined = [stdout, stderr].filter(Boolean).join('\n');
return combined || 'No output yet...';
```
### 2. Automatic Diff Refresh During Coding Agent Execution
**Fixed**: Added proper diff polling with immediate refresh
- **Before**: Diff wasn't automatically updating during coding agent execution
- **After**: Diff refreshes immediately when coding agent starts + every 2 seconds while running
- **Implementation**:
- Immediate `fetchDiff()` call when entering `CodingAgentRunning` state
- `setInterval` to refresh every 2 seconds during execution
- Proper cleanup when state changes or component unmounts
### 3. Enhanced Setup Process Data Fetching
**Improved**: Better process data availability
- **Enhancement**: Modified `fetchAttemptData` to always fetch setup script process details
- **Benefit**: Ensures setup script output is available even after completion
- **Implementation**: Added dedicated setup script process fetching logic
## Key Improvements
1. **Real-time Setup Feedback**: Users can now see the complete output (both stdout and stderr) from setup scripts as they run
2. **Live Diff Updates**: File changes appear automatically every 2 seconds during coding agent execution
3. **Immediate Feedback**: Diff refreshes immediately when coding agent starts, then continues polling
4. **Better Error Visibility**: Setup script errors (stderr) are now visible alongside normal output
The implementation ensures that users get comprehensive, real-time feedback during both setup and coding phases, making the development process much more transparent and responsive.
* The styles have been cleaned up:
1. **Fixed the "No logs available" message**: Changed "No output yet..." to "Waiting for setup script output..." to indicate this is a loading state rather than an error.
2. **Simplified setup script display**: Removed the terminal UI styling including:
- The loading spinner and centered layout
- The bordered card wrapper
- The "Setup Script Output" header
- The black terminal background with green text
- Applied simple monospaced font styling similar to normalized logs
The setup script now displays with a cleaner, more consistent style that matches the rest of the interface while still being clearly readable with monospaced font.
* Added auto-scroll functionality to the setup script section:
1. **Added setup scroll ref**: Created `setupScrollRef` to track the setup script container
2. **Added auto-scroll effect**: Created a `useEffect` that monitors `attemptData.runningProcessDetails` and automatically scrolls to the bottom when the setup script output updates
3. **Applied ref to container**: Added the ref to the setup script container div
The setup script logs will now automatically scroll to the bottom as new output is received, matching the behavior of the normalized logs.
* Perfect! I've successfully implemented the follow up executor visualization in the TaskDetailsPanel. Here's what I did:
## Summary of Changes
1. **Identified follow up executor data structure**: Follow up executors are identified by having `process_type: "codingagent"` and `command: "followup_executor"` (vs main executor with `command: "executor"`)
2. **Modified TaskDetailsPanel logic**: Updated both the full-screen view (when no changes) and the split view (when changes are present) to:
- Find the main coding agent process (command: "executor")
- Find all follow up executor processes (command: "followup_executor")
- Display them in sequence with the main executor first, followed by follow up executors
3. **Enhanced UI presentation**: Each follow up executor is displayed with:
- A clear separator (border-top)
- A header showing "Follow-up #X" with the sequence number
- The start time of the follow up executor
- The normalized conversation viewer for that executor's logs
4. **Tested the implementation**: Both frontend and backend build successfully, and the code passes linting checks.
The implementation now properly handles and visualizes follow up executors by showing them under the main executor in chronological order, making it clear which conversation belongs to which executor while maintaining the existing functionality for the main coding agent.
* ## Issue Fixed!
I found and fixed the bug. The problem was in the TaskDetailsPanel logic that determines when to show the conversation logs vs. the fallback "Task execution not started yet" message.
### Root Cause
The condition `if (isCodingAgentRunning && !hasChanges)` only handled the case when the coding agent was *running*, but after follow up executors complete, the execution state becomes `CodingAgentComplete`. This caused the logic to fall through to the default case, showing "Task execution not started yet" instead of the conversation logs.
### Fix Applied
I modified the condition to also handle the completed state:
- Added `isCodingAgentComplete` variable to track when execution is complete
- Updated the condition to `if ((isCodingAgentRunning || isCodingAgentComplete) && !hasChanges)`
This ensures that both running and completed follow up executors will properly display their conversation logs, whether they're still running or have finished execution.
The fix is minimal and targeted, only affecting the specific scenario where follow up executors complete without generating file changes, while preserving all other functionality.
* ## Perfect! All styling changes implemented successfully!
Here's a summary of what I've accomplished:
### ✅ Backend Changes
1. **Extended NormalizedConversation structure** to include `prompt` and `summary` fields
2. **Updated all executor implementations** (Claude, Amp, default) to support the new fields
3. **Modified the normalized-logs API endpoint** to fetch executor session data and include prompt/summary in the response
4. **Regenerated TypeScript types** to reflect the new structure
### ✅ Frontend Changes
1. **Added prompt display** above the normalized logs with a distinctive blue styling and MessageSquare icon
2. **Implemented horizontal dividers** between all log sections:
- Between prompt and conversation logs
- Between main executor and follow-up executors
- Between multiple follow-up executors
3. **Removed the "Follow-up #1 Started:" headers** for a cleaner interface
4. **Updated the layout** to use proper spacing and consistent divider styling
### 🎨 UI Improvements
- **Prompts are displayed** in a blue-themed box with monospace font for better readability
- **Horizontal lines** consistently separate each logical section (prompt → executor → follow-up prompt → follow-up executor)
- **Clean, streamlined appearance** without verbose headers
- **Consistent spacing** and visual hierarchy throughout
The implementation properly handles cases where:
- Prompts may or may not be available
- There are multiple follow-up executors
- There are no follow-up executors
- The main executor completes with or without follow-ups
All builds pass and the code maintains the existing functionality while adding the requested visual improvements!
* ## ✅ Styling Updates Complete!
I've successfully implemented all the requested styling changes:
### 🔨 **Updated Prompt Display**
- **Changed icon** from MessageSquare to **Hammer** with orange color (`text-orange-600`)
- **Removed blue box styling** - no more background, border, or rounded corners
- **Removed monospaced font** - now uses normal text styling with `text-sm` and `text-foreground`
- **Maintains whitespace-pre-wrap** to preserve formatting without the monospace appearance
### 📏 **Improved Spacing**
- **Increased padding** at the bottom of the horizontal divider from `mb-4` to `mb-6`
- This creates **better visual separation** between the user message (prompt) and the agent logs
- **Maintains consistent spacing** throughout the component
### 🎨 **Clean Visual Hierarchy**
- User prompts now appear as **clean, readable text** with a hammer icon
- **Horizontal dividers** provide clear separation between sections
- **Proper padding** ensures comfortable reading experience
- **Consistent with the overall UI theme** rather than standing out with special styling
The changes maintain all existing functionality while providing the cleaner, more integrated visual appearance you requested. The prompt now blends naturally with the rest of the conversation while still being clearly distinguished by the hammer icon and horizontal divider!
* ## ✅ Styling Fixes Applied!
I've successfully implemented both requested changes:
### 🔨 **Blue Hammer Icon**
- Changed the hammer icon color from `text-orange-600` to `text-blue-600`
- Now matches the blue theme you requested
### 📏 **Fixed Spacing Gap**
- **Removed the bottom margin** from the horizontal divider (`mb-6` removed)
- **Added conditional top margin** to the conversation entries container (`mt-6` when prompt exists)
- This creates the **proper gap between the divider and the normalized logs**
### 🎨 **How the spacing now works:**
1. **User prompt** with hammer icon
2. **Horizontal divider** (no bottom margin)
3. **6-unit gap** (via `mt-6` on conversation container)
4. **Normalized logs/conversation entries**
The spacing is now properly balanced with a clean visual separation between the user message and the agent logs, while the blue hammer icon provides a cohesive color scheme throughout the interface!
* ## ✅ Spacing Fixed!
I've corrected the spacing issue by:
### 📏 **Fixed the Gap**
- **Added `mb-6` to the horizontal divider** - this creates the proper 6-unit gap below the divider
- **Removed the conditional margin logic** from the conversation entries container - it was causing the spacing to not work properly
- **Simplified the structure** for more predictable spacing behavior
### 🎯 **Now the spacing flow is:**
1. **User message** (with hammer icon)
2. **Gap** (via `mb-4` on user message container)
3. **Horizontal divider**
4. **Gap** (via `mb-6` on horizontal divider) ← **This was missing before!**
5. **Normalized logs/conversation entries**
The spacing should now be exactly what you requested - proper space both above and below the horizontal divider!
* ## ✅ Spacing Finally Fixed!
I've resolved the `space-y-0` override issue by **restructuring the layout**:
### 🔧 **What was wrong:**
- The `space-y-0` class creates CSS rule `.space-y-0 > :not([hidden]) ~ :not([hidden])` that sets `margin-top: 0` and `margin-bottom: 0` on all sibling elements
- This was overriding the `mb-6` class on the horizontal divider
### 🎯 **How I fixed it:**
1. **Removed `space-y-0` class** from the outer container
2. **Moved the horizontal divider outside** the prompt section to its own top-level element
3. **Made the structure flat** so no spacing utility classes conflict with each other
### 📏 **New structure:**
```jsx
<div> // No space-y-0 class
{/* User prompt */}
<div className="mb-4">...</div> // Space below prompt
{/* Horizontal divider */}
<div className="mb-6">...</div> // Space below divider - no override!
{/* Conversation entries */}
<div className="space-y-2">...</div> // Only affects entries
</div>
```
Now the `mb-6` on the horizontal divider won't be overridden, and you should see the proper gap between the divider and the normalized logs!
* ## ✅ Loading Message Fixed!
I've resolved the misleading "No logs available" message by improving the state logic:
### 🔧 **What was wrong:**
- When a follow-up execution starts, the API call completes successfully but returns no conversation data yet
- The component would show "No conversation data available" even though the process was still running and would eventually produce logs
### 🎯 **How I fixed it:**
- **Added execution process status check** - when `executionProcess.status === 'running'` and there's no conversation data
- **Shows "Waiting for logs..."** instead of "No conversation data available"
- **Maintains proper error state** for truly failed or empty completed processes
### 📱 **New behavior:**
- **When follow-up starts**: Shows "Waiting for logs..." ✅
- **During execution**: Shows "Waiting for logs..." ✅
- **When logs appear**: Shows the actual conversation ✅
- **When execution completes with no logs**: Shows "No conversation data available" ✅
- **On actual errors**: Shows the error message ✅
Now users will see a proper loading state that indicates the system is working, rather than an error-like message that suggests something went wrong!
* ## ✅ "No logs available" Message Fixed!
I've resolved the misleading error message by fixing the root cause in the backend:
### 🔧 **What was wrong:**
- When a follow-up execution starts, `process.stdout` is `None` because logs haven't been generated yet
- The backend was returning `success: false` with "No logs available for this execution process"
- The frontend was showing this as an error message
### 🎯 **How I fixed it:**
1. **Added status check in the backend** - when `process.status == ExecutionProcessStatus::Running` and `stdout` is `None`
2. **Return success with empty conversation** instead of an error
3. **Include executor session data** (prompt/summary) even for running processes without logs yet
4. **Maintain error state** only for truly failed or completed processes with no logs
### 📱 **New behavior:**
- **When follow-up starts**: Backend returns empty conversation with `success: true` ✅
- **Frontend shows**: "Waiting for logs..." (from previous fix) ✅
- **When logs appear**: Shows actual conversation data ✅
- **When process fails**: Still shows proper error messages ✅
- **When completed with no logs**: Shows "No conversation data available" ✅
Now when you start a follow-up, you should see "Waiting for logs..." instead of the misleading "No logs available for this execution process" error message!
* ## ✅ Spacing System Refactored!
I've completely refactored the spacing system for a much cleaner and more predictable layout:
### 🏗️ **New Systematic Approach**
#### **NormalizedConversationViewer (Internal Spacing):**
- **Prompt**: `mb-6` creates consistent gap to conversation entries
- **Conversation entries**: `space-y-2` between each entry
- **No internal dividers** - keeps the component focused on its own content
#### **TaskDetailsPanel (Executor Separation):**
- **Container**: `space-y-8` creates consistent spacing between main sections
- **Follow-up dividers**: `border-t` + `mb-8` for proper separation
- **Clean structure** with predictable spacing
### 📏 **Spacing Flow Now:**
1. **Main executor prompt**
2. **6-unit gap** (prompt `mb-6`)
3. **Main executor conversation**
4. **8-unit gap** (container `space-y-8`)
5. **Horizontal divider**
6. **8-unit gap** (divider `mb-8`)
7. **Follow-up prompt**
8. **6-unit gap** (prompt `mb-6`)
9. **Follow-up conversation**
10. **8-unit gap** (for next follow-up if any)
### 🎯 **Benefits:**
- **Consistent spacing** throughout all states
- **No more conflicting CSS rules** or override issues
- **Systematic approach** that's easy to maintain and debug
- **Proper separation** between logical sections
- **Clean, readable layout** with predictable gaps
The spacing should now be perfect in all scenarios - with or without prompts, with single or multiple follow-ups, and in both full-screen and split-view modes!
* I've successfully improved the diff styling to be more compact and fit better within the sidebar. Here are the key changes made:
**Spacing Improvements:**
- Reduced spacing between diff files from `space-y-6` to `space-y-3`
- Reduced main container padding from `p-6` to `p-4`
- Reduced file header padding from `py-2` to `py-1.5`
**Line Compactness:**
- Reduced diff line height from `min-h-[1.75rem]` to `min-h-[1.25rem]`
- Reduced line number column width from `w-16` to `w-12`
- Reduced line number padding from `px-2` to `px-1.5`
- Reduced individual line number widths from `w-6` to `w-5`
- Reduced line content padding from `px-3` to `px-2`
**Expand/Collapse Buttons:**
- Reduced expand button height from `h-8` to `h-6`
These changes make the diff view significantly more compact while maintaining readability. The diff will now display more content in the same space, making better use of the sidebar real estate. The frontend builds successfully with these changes.
* Perfect! The migration is complete. Let me summarize what I've done:
I've successfully migrated the merge, rebase, and PR functionality from the old `task-attempt-compare.tsx` page to the `TaskDetailsToolbar.tsx` component and deleted the old page.
1. **Added to TaskDetailsToolbar.tsx:**
- Branch status fetching with `fetchBranchStatus` function
- Merge functionality with uncommitted changes warning
- Rebase functionality
- Create PR functionality with dialog
- Branch status display showing commits ahead/behind and uncommitted changes
- Git operation buttons (Merge, Rebase, Create PR) with proper enabling/disabling logic
- Two dialog components for warnings and PR creation
2. **Deleted:**
- `frontend/src/pages/task-attempt-compare.tsx` - The entire compare page
3. **Updated routing:**
- Removed the compare page route from `App.tsx`
- Removed the import for `TaskAttemptComparePage`
- Updated keyboard shortcuts to remove compare page navigation logic
- Removed the "Changes" button that linked to the old compare page
4. **Cleaned up imports:**
- Removed unused imports like `Link` from react-router-dom and `GitCompare` icon
The diff view functionality remains in the `TaskDetailsPanel.tsx` where it was already migrated, and now users can access merge, rebase, and PR creation directly from the toolbar without needing a separate page. The branch status is also prominently displayed in the toolbar so users can see the current state at a glance.
* Perfect! Now the merge, rebase, and PR buttons will be disabled when an attempt is running, preventing users from performing git operations while code is being executed.
* Cargo fmt
* Clippy
* Prettier
* Cleanup
* Clippy
2025-07-03 22:00:44 +01:00
// Now also get unstaged changes (working directory changes)
let current_tree = worktree_repo . head ( ) ? . peel_to_tree ( ) ? ;
// Create diff from HEAD to working directory for unstaged changes
let mut unstaged_diff_opts = git2 ::DiffOptions ::new ( ) ;
unstaged_diff_opts . context_lines ( 10 ) ;
unstaged_diff_opts . interhunk_lines ( 0 ) ;
unstaged_diff_opts . include_untracked ( true ) ; // Include untracked files
let unstaged_diff = worktree_repo . diff_tree_to_workdir_with_index (
Some ( & current_tree ) ,
Some ( & mut unstaged_diff_opts ) ,
) ? ;
// Process unstaged changes
unstaged_diff . foreach (
& mut | delta , _progress | {
if let Some ( path_str ) = delta . new_file ( ) . path ( ) . and_then ( | p | p . to_str ( ) ) {
if let Err ( e ) = Self ::process_unstaged_file (
& mut files ,
& worktree_repo ,
base_oid ,
& attempt . worktree_path ,
path_str ,
& delta ,
) {
eprintln! ( " Error processing unstaged file {} : {:?} " , path_str , e ) ;
}
}
true
} ,
None ,
None ,
None ,
) ? ;
2025-06-22 23:43:13 +01:00
}
2025-06-19 18:59:47 -04:00
Ok ( WorktreeDiff { files } )
}
Redesign sidebar (#63)
* Agent logs
* ## Summary
I have successfully implemented a comprehensive log normalization system for vibe-kanban with the following features:
### ✅ **Completed Tasks:**
1. **Defined normalized log format types** - Created standard TypeScript-exportable types:
- `NormalizedConversation` - Top-level container with entries, session ID, and executor type
- `NormalizedEntry` - Individual conversation entries with timestamp, type, and content
- `NormalizedEntryType` - Union type for different entry types (user, assistant, tool use, system, thinking)
- `ActionType` - Specific action types for tool operations (file read/write, commands, search, etc.)
2. **Implemented normalize method for AMP executor** - Parses AMP's JSONL format to extract:
- User and assistant messages
- Thinking content
- Tool usage with intelligent action type detection
- Session ID (threadID)
- Timestamps from message metadata
3. **Implemented normalize method for Claude executor** - Parses Claude's stream-json format to extract:
- User and assistant messages
- Tool usage with action type detection
- System initialization messages
- Session ID
- Comprehensive tool mapping for Claude-specific tools
4. **Added normalize method to executor trait** - Created a default implementation that returns empty results for unsupported executors, allowing easy extension to other executor types
5. **Created new API endpoint for normalized logs** - Added `GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint that:
- Validates execution process belongs to the correct project
- Determines executor type automatically
- Applies appropriate normalization logic
- Returns standardized conversation format
6. **Tested the implementation** - Created comprehensive unit tests for both AMP and Claude normalization, verifying correct parsing of real log examples
### 🔧 **Technical Implementation Details:**
- **Intelligent Action Type Detection**: The system maps tool names to specific action types (file operations, commands, searches, etc.) based on tool names and input parameters
- **Cross-platform Compatibility**: Handles different parameter naming conventions between executors
- **Error Handling**: Robust error handling with detailed error messages for debugging
- **TypeScript Integration**: Full TypeScript type generation for frontend consumption
- **Extensible Design**: Easy to add new executors by implementing the `normalize_logs` method
### 🚀 **Usage:**
The new endpoint can be called to get a normalized view of any execution process logs:
```bash
GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs
```
This returns a standardized conversation format that the frontend can easily consume to display:
- User inputs and assistant responses
- Tool usage with specific action details (file paths, commands, etc.)
- Internal reasoning (thinking)
- System status messages
- Session tracking information
The normalized format provides a high-level, consistent representation of the conversation regardless of which executor (AMP, Claude, etc.) generated the original logs.
* Perfect! I have successfully implemented the normalized logs feature. Here's a summary of what was accomplished:
## Summary
✅ **Replaced manual log rendering with normalized logs API**
### Key Changes Made:
1. **Created a new `NormalizedConversationViewer` component** that:
- Fetches normalized logs from the backend API endpoint
- Displays conversation entries with appropriate icons for each type:
- 👤 User messages (blue)
- 🤖 Assistant messages (green)
- ⚙️ System messages (gray)
- 🧠 Thinking (purple)
- 👁️ File reads (orange)
- ✏️ File writes (red)
- 💻 Command execution (yellow)
- 🔍 Search operations (indigo)
- 🌐 Web fetches (cyan)
- ➕ Task creation (teal)
- Shows timestamps and entry type labels
- Handles loading and error states
2. **Updated `TaskActivityHistory` component** to:
- Use the new `NormalizedConversationViewer` instead of `ExecutionOutputViewer`
- Pass the required `projectId` prop
- Maintain the same expansion/collapse functionality
3. **Updated `TaskDetailsPanel`** to pass the `projectId` to the activity history
4. **Removed legacy components**:
- The complex `ExecutionOutputViewer` that manually parsed JSONL
- The `ConversationViewer` that handled format-specific parsing
- All the manual stdout/stderr rendering logic
### Benefits:
- **Cleaner codebase**: Eliminated complex frontend parsing logic
- **Consistent display**: All executors now show logs in the same format
- **Better UX**: Clear icons and labels make it easy to understand conversation flow
- **Maintainable**: Backend handles format normalization, frontend just displays
- **Extensible**: New executor types automatically work with the existing UI
The implementation leverages the existing `/api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint and properly handles loading states, errors, and displays the conversation history with meaningful icons for each entry type.
* Excellent! The build is successful and the bundle size is slightly smaller after removing the unused code. The task is now complete:
✅ **Legacy log rendering components successfully removed**
- Deleted `ExecutionOutputViewer.tsx` (224 lines)
- Deleted `ConversationViewer.tsx` (complex JSONL parsing logic)
- Build passes and bundle size reduced
- All functionality now uses the clean normalized logs API
The codebase is now cleaner and fully transitioned to using the normalized logs system.
* Perfect! I've successfully refactored the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) component with the new layout:
## Key Changes Made:
1. **Top 2/3 - Code Changes Section**:
- Extracted all the diff processing logic from [`task-attempt-compare.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/pages/task-attempt-compare.tsx)
- Shows file diffs with syntax highlighting, expandable sections, and line numbers
- Includes file deletion functionality
- "Show/Hide All Unchanged" toggle for better navigation
2. **Bottom 1/3 - Agent Logs Section**:
- Preserved the existing [`TaskActivityHistory`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskActivityHistory.tsx) component
- Auto-scrolling behavior for new log entries
- Clear separation with border and background styling
3. **Maintained Existing Functionality**:
- Header, toolbar, and follow-up sections remain unchanged
- Editor selection dialog and ESC key handling preserved
- All existing hooks and state management intact
The component now provides a much better developer experience by showing code changes prominently in the top section while keeping the agent logs easily accessible in the bottom third.
* Perfect! The build is successful. I've implemented all the requested style changes to the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
**Code Changes Section (Top 2/3):**
- ✅ Removed "Code Changes" header
- ✅ Removed description text
- ✅ Removed "Show All Unchanged" button and related functionality
- ✅ Removed parent Card wrapper - each file diff is now directly displayed in its own card
**Agent Logs Section (Bottom 1/3):**
- ✅ Removed "Agent Logs" header
- ✅ Removed description text
- ✅ Now displays only the `NormalizedConversationViewer` component
- ✅ Removed card/container wrapper
The component now has a cleaner, more streamlined interface with the code changes taking up most of the space in the top section, and the agent conversation displayed directly in the bottom third without extra headers or containers.
* Perfect! The build is successful. I've updated the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) to properly find and display the coding agent conversation.
## Changes Made:
The component now:
1. **First looks** for any running coding agent process in `runningProcessDetails`
2. **If not found**, searches through all processes in the `processes` array to find any coding agent process (completed or otherwise)
3. **Creates a minimal ExecutionProcess object** from the summary data if needed, since the `NormalizedConversationViewer` only requires the process ID to fetch the conversation data
4. **Displays the conversation** for any coding agent process found, whether running or completed
This should resolve the "Unsupported executor type: unknown" error and properly show the normalized conversation logs from the coding agent in the bottom section.
* Perfect! I've successfully updated the [`NormalizedConversationViewer.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/NormalizedConversationViewer.tsx) component with the requested style changes:
## Changes Made:
1. **Removed metadata header** - No longer shows executor type, session ID, or process type badges
2. **Removed Card wrapper** - The conversation entries are now displayed directly without any card container or special background
3. **Removed timestamps** - Each entry no longer shows the date/time information
4. **Cleaned up imports** - Removed unused imports for Card, CardContent, and Badge components
The normalized logs now display as a clean, minimal conversation with just the entry type icon, entry type label, and content for each message in the agent conversation.
* Perfect! I've made the requested style improvements to the diffs in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Removed max height limit** - Each file diff card no longer has a `max-h-[400px] overflow-y-auto` constraint, so the entire diff content is visible without individual scrolling per file
2. **Enhanced diff line colors** - Updated the background colors for added and removed lines:
- **Added lines** (Insert): Now use `bg-green-100` (light mode) and `bg-green-900/40` (dark mode) with stronger green text colors
- **Removed lines** (Delete): Now use `bg-red-100` (light mode) and `bg-red-900/40` (dark mode) with stronger red text colors
- Made the border colors more prominent with `border-green-500` and `border-red-500`
The diffs now display with clearer visual distinction between added and removed lines, and each file shows its complete content without requiring individual scrolling.
* Perfect! I've fixed the line number background colors in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added `getLineNumberClassName` function** - This creates appropriate styling for line numbers based on the chunk type:
- **Added lines**: Green background (`bg-green-50`/`bg-green-900/20`) with green text and borders
- **Removed lines**: Red background (`bg-red-50`/`bg-red-900/20`) with red text and borders
- **Unchanged lines**: Gray background (original styling)
2. **Updated line number div** - Replaced the hardcoded gray styling with a call to `getLineNumberClassName(line.chunkType)`
Now the line numbers will have matching background colors with their corresponding lines - green for additions, red for deletions, and gray for unchanged context lines. This creates a more cohesive visual experience for the diff display.
* Perfect! I've updated the line number background colors to match the main line content exactly:
## Changes Made:
- **Added lines**: Line numbers now use `bg-green-100 dark:bg-green-900/40` (same as the main content)
- **Removed lines**: Line numbers now use `bg-red-100 dark:bg-red-900/40` (same as the main content)
Now the line numbers have the same background intensity as the main line content, creating a consistent visual appearance across the entire diff line without the lighter/more transparent look.
* Perfect! I've made all the requested improvements to the diff styling in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added horizontal scrolling** - The diff container now has `overflow-x-auto` and each line has `min-w-max` to ensure long lines can be scrolled horizontally
2. **Removed thick border** - Removed the `border-l-2 border-green-500` and `border-l-2 border-red-500` thick left borders from added/removed lines
3. **GitHub-style background intensity** - Now follows GitHub's pattern:
- **Line content**: Lighter background (`bg-green-50`/`bg-red-50` for light mode, `bg-green-900/20`/`bg-red-900/20` for dark mode)
- **Line numbers**: Darker background (`bg-green-100`/`bg-red-100` for light mode, `bg-green-900/40`/`bg-red-900/40` for dark mode)
The diff now has a cleaner appearance without the heavy borders, proper horizontal scrolling for long lines, and follows GitHub's visual hierarchy where line numbers have more prominent background colors than the content.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Added `py-1` to the `getLineNumberClassName` function so the line number background now has the same vertical padding as the content lines, ensuring full height coverage
2. **Fixed horizontal scrolling layout** - Wrapped the diff content in a proper container structure:
- Added `min-w-full` wrapper inside the `overflow-x-auto` container
- This ensures that elements like horizontal lines and borders aren't cut off during horizontal scrolling
- The scrolling now works properly without affecting other page elements
The diff now has proper horizontal scrolling that doesn't interfere with other page elements, and the line number backgrounds extend to the full height of each line.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Both the line numbers and content now use `py-1` consistently, ensuring the backgrounds match in height perfectly
2. **Fixed horizontal scrolling** - Changed the approach:
- Used `whitespace-nowrap` instead of `whitespace-pre` to prevent wrapping
- Used `min-w-max` on each line to ensure they extend naturally
- The overflow scrolling now works at the container level without cutting off other elements
- Removed the problematic nested wrapper structure
The line number backgrounds now extend to the full height of each line, and horizontal scrolling works properly without affecting other page elements like borders and buttons.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height gaps**:
- Used `min-h-[1.75rem] flex items-center` for both line numbers and content divs
- This ensures both backgrounds have exactly the same height with no gaps above or below
- Removed separate `py-1` padding that was causing misalignment
2. **Fixed horizontal scroll border cutoff**:
- Added `min-w-max` to the "show X more lines" buttons
- This ensures the buttons extend to the full width of the scrollable content
- The borders now remain visible even when scrolling horizontally
Now the line number backgrounds perfectly match the line content height with no color gaps, and the expand/collapse buttons maintain their borders during horizontal scrolling.
* Perfect! I've fixed both remaining horizontal scroll issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed highlighted line backgrounds during scroll**:
- Changed from `min-w-max` to `w-full` for the base class
- Added `style={{ minWidth: 'max-content' }}` to ensure lines extend to natural content width
- Used `inline-block min-w-full` container to create proper scrollable width
- Now all highlighted lines (green/red) maintain their background color across the full scrollable width
2. **Fixed "show more lines" button borders**:
- Wrapped buttons in `div className="w-full"` containers
- Removed `min-w-max` from button classes and used `w-full`
- The buttons now properly extend across the full scrollable width
- Borders remain visible during horizontal scrolling
Now the diff view has proper horizontal scrolling where:
- All highlighted lines maintain their background colors across the entire scroll width
- The "show X more lines" buttons maintain their borders when scrolling
- The entire diff content scrolls cohesively
* Perfect! I've fixed the indentation issue in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Fixed indentation preservation**: Changed from `whitespace-nowrap` to `whitespace-pre` in the `getChunkClassName` function
Now the diff properly preserves:
- Code indentation (spaces and tabs)
- All whitespace formatting
- Line spacing as it appears in the original code
The horizontal scrolling functionality remains intact while properly displaying the code formatting and indentation structure.
* Perfect! I've right-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Added `justify-end`** to both "Show X more lines" and "Hide expanded lines" buttons
Now the button text ("Show X more lines" and "Hide expanded lines") is right-aligned, which means:
- The text stays visible and consistent during horizontal scrolling
- Users can always see the action text regardless of scroll position
- The interface feels more stable when navigating wide diffs
* Perfect! I've left-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Changed `justify-end` to `justify-start`** for both "Show X more lines" and "Hide expanded lines" buttons
Now the button text is left-aligned, which means:
- The text appears at the left edge of the button and stays visible during horizontal scrolling
- Users can always see the action text at a consistent position
- The interface aligns with the left-aligned line numbers below it
* ## Summary
I've successfully improved the normalized logs to show more concise content. Here's what was changed:
### Key Improvements
1. **File Operations**: Now show just the file path (e.g., `"src/components/TaskDialog.tsx"`) instead of verbose tool descriptions
2. **Commands**: Now show just the bash command (e.g., `"npm run build"`) instead of full tool invocation details
3. **Search Operations**: Now show just the search query (e.g., `"authentication headers"`) instead of tool metadata
4. **Web Requests**: Now show just the URL (e.g., `"https://api.example.com/data"`) instead of verbose descriptions
5. **Other Tools**: Now show concise, meaningful descriptions (e.g., `"Managing TODO list"` for todo tools)
### Changes Made
1. **Updated AMP Executor** (`backend/src/executors/amp.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
2. **Updated Claude Executor** (`backend/src/executors/claude.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
3. **Updated Documentation** (`test_normalized_logs_api.md`):
- Updated example response to show the new concise format
- Added detailed explanation of the new content format with examples
4. **Added Tests**:
- Enhanced existing tests to verify the new concise content format
- Tests confirm that `todo_write` shows "Managing TODO list" instead of verbose JSON
- Tests confirm that `Task` tool shows the task description directly
### Before and After
**Before:**
```json
{
"content": "Tool: todo_write with input: {\"todos\":[{\"id\":\"1\",\"content\":\"Explore task creation dialog component\",\"status\":\"todo\",\"priority\":\"high\"}]}"
}
```
**After:**
```json
{
"content": "Managing TODO list"
}
```
The implementation now provides much cleaner, more actionable content in the normalized conversation logs while maintaining all the rich metadata in the `action_type` field for programmatic access.
* Perfect! The frontend builds successfully. Now the normalized conversation logs will display much cleaner - just showing the appropriate icon next to the concise content without any redundant headers.
## Summary
I've removed the headers from the normalized conversation log display. Now instead of showing:
**Before:**
```
[file icon] Read: src/components/TaskDialog.tsx
src/components/TaskDialog.tsx
[command icon] Command: npm run build
npm run build
```
**After:**
```
[file icon] src/components/TaskDialog.tsx
[command icon] npm run build
[search icon] authentication headers
[todo icon] Managing TODO list
```
### Changes Made
1. **Removed redundant header display** - The `getEntryTypeDisplay` function and its usage has been removed
2. **Simplified layout** - Each log entry now shows just the icon and concise content directly
3. **Cleaner UI** - No more redundant information since the content is already concise and the icon provides the context
The display is now much cleaner and more efficient, with the icon providing the visual context for the action type and the concise content providing the essential information.
* Perfect! The frontend builds successfully.
## Summary
I've updated the NormalizedConversationViewer to render terminal commands with a monospace font. Now:
### Changes Made
1. **Added `getContentClassName` function** - This function conditionally applies styling based on the entry type
2. **Applied monospace font for commands** - When the entry type is a tool_use with action_type of "command_run", it applies the `font-mono` Tailwind class
3. **Maintained existing styling** - All other entry types keep the same styling as before
### Result
Now terminal commands like:
- `npm run build`
- `cargo check`
- `git status`
- `ls -la`
Will be displayed in a monospace font next to the terminal icon, making them more readable and clearly distinguishable as code/commands, while other content like file paths, search queries, and regular text continue to use the default font.
The monospace font makes commands much easier to read and gives them the appropriate technical appearance that users expect for terminal output.
* Excellent! Everything builds successfully.
## Summary
I've implemented real-time conversation updates with smart auto-scrolling functionality. Here's what was added:
### Key Features
1. **Auto-refresh**: The conversation automatically polls for updates every 2 seconds when the execution process is running
2. **Smart auto-scrolling**: Automatically scrolls to the bottom when new content arrives
3. **User scroll detection**: Stops auto-scrolling when the user manually scrolls up
4. **Auto-scroll resumption**: Resumes auto-scrolling when the user scrolls back to the bottom
5. **Efficient updates**: Only updates the conversation state when content actually changes
### Implementation Details
**Auto-refresh Logic:**
- Polls every 2 seconds when `executionProcess.status === 'running'`
- Uses efficient content comparison to avoid unnecessary re-renders
- Gracefully handles polling errors without disrupting the UI
**Smart Scrolling:**
- Tracks user scroll behavior vs automatic scrolling
- Uses a 5px threshold to detect "at bottom" state
- Provides smooth auto-scrolling with a small delay to ensure DOM updates
**User Experience:**
- Container has `max-h-96` with `overflow-y-auto` for scrollable content
- Terminal commands display in monospace font
- Clean display with just icons and concise content
- No disruption when user wants to review previous logs
### Behavior
- ✅ **Auto-updates**: Content refreshes every 2 seconds during execution
- ✅ **Auto-scroll**: New content automatically scrolls into view
- ✅ **Manual control**: User can scroll up to review previous logs
- ✅ **Resume auto-scroll**: When user scrolls back to bottom, auto-scrolling resumes
- ✅ **Efficient**: Only re-renders when content actually changes
- ✅ **Clean UI**: Terminal commands in monospace, no redundant headers
The conversation viewer now provides a seamless, real-time experience that follows the execution while respecting user control over scrolling behavior.
* ## Summary
I've successfully fixed the scroll-within-scroll issue in the TaskDetailsPanel. Here's what was changed:
**Problem**: The normalized logs section had nested scrolling containers:
- Parent container (TaskDetailsPanel) with `h-full overflow-y-auto`
- Child container (NormalizedConversationViewer) with `max-h-96 overflow-y-auto`
**Solution**: I removed the scrolling functionality from the `NormalizedConversationViewer` component by:
1. **Removed scroll container**: Changed from `<div ref={scrollContainerRef} className="space-y-2 max-h-96 overflow-y-auto" onScroll={handleScroll}>` to just `<div className="space-y-2">`
2. **Cleaned up scroll-related code**: Removed:
- State variables: `isUserScrolling`, `scrollContainerRef`, `scrollTimeoutRef`, `lastScrollTop`
- Functions: `isAtBottom`, `scrollToBottom`, `handleScroll`
- Effects: Auto-scroll on conversation updates and scroll timeout cleanup
- Unused imports: `useRef`
3. **Verified functionality**: The parent container in TaskDetailsPanel now handles all scrolling for the logs section, eliminating the annoying nested scroll behavior.
The build and lint checks pass successfully, and the user experience should now be much smoother without the competing scroll areas.
* ## Summary
I've successfully fixed the scroll-within-scroll issue while preserving the auto-scroll functionality. Here's what I implemented:
**Problem**: Nested scroll containers created annoying scroll-within-scroll behavior:
- Parent: TaskDetailsPanel with `h-full overflow-y-auto`
- Child: NormalizedConversationViewer with `max-h-96 overflow-y-auto`
**Solution**: Moved all scroll logic to the parent container while maintaining auto-scroll behavior:
### Changes Made:
1. **Removed inner scroll container** from NormalizedConversationViewer:
- Removed `max-h-96 overflow-y-auto` classes
- Removed all scroll-related state and functions
- Simplified to just `<div className="space-y-2">`
2. **Added callback interface** to NormalizedConversationViewer:
- Added `onConversationUpdate?: () => void` prop
- Called this callback when conversation data changes
- Used `setTimeout` to ensure state updates happen before auto-scroll
3. **Enhanced TaskDetailsPanel scroll logic**:
- Added `conversationUpdateTrigger` state to track conversation changes
- Added `handleConversationUpdate` callback function
- Extended existing auto-scroll effect to include conversation updates
- Maintained existing manual scroll detection logic
### Key Benefits:
✅ **No more nested scrolling** - Only the parent container scrolls
✅ **Auto-scroll preserved** - New messages still auto-scroll to bottom
✅ **Manual scroll respected** - Auto-scroll stops when user manually scrolls up
✅ **Smart resumption** - Auto-scroll resumes when user scrolls back to bottom
✅ **Seamless integration** - Works with existing activity and process log auto-scroll
The solution maintains all existing functionality while providing a much better user experience by eliminating the competing scroll areas.
* ## Summary
I've successfully updated the task attempts diff endpoint to include unstaged changes in addition to the existing committed changes. Here's what was implemented:
### Key Changes Made:
1. **Enhanced `get_diff` method** in [`backend/src/models/task_attempt.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-4fbf-update-dif/backend/src/models/task_attempt.rs#L1042-L1379):
- Added logic to capture unstaged changes using `diff_tree_to_workdir_with_index`
- For files with both committed and unstaged changes, creates a combined diff from the base branch to the current working directory
- For files with only unstaged changes, generates a standard diff
2. **New helper function** `process_unstaged_file`:
- Handles the processing of individual unstaged files
- Manages the complex logic of combining committed and unstaged changes
- Uses Git's native patch generation for accurate diff representation
### How It Works:
**For merged task attempts:** Shows the original merge commit diff (unchanged behavior)
**For active task attempts:**
1. **First pass:** Generates diff from base branch to current HEAD commit (committed changes)
2. **Second pass:** Finds files with unstaged changes in the working directory
3. **Combining logic:**
- **Files with both committed & unstaged changes:** Creates a combined diff from base branch directly to working directory content
- **Files with only unstaged changes:** Adds them as separate diff entries
- **Files with only committed changes:** Keeps original diff (unchanged)
### Benefits:
- ✅ **Complete visibility:** Users can now see both committed and uncommitted work
- ✅ **Unified view:** All changes are presented in a single coherent diff
- ✅ **Backward compatible:** Existing functionality for merged attempts remains unchanged
- ✅ **Accurate representation:** Uses Git's native diff algorithms for consistent formatting
The implementation maintains the existing API structure while extending functionality to provide a more comprehensive view of all changes in a task attempt.
* Perfect! I have successfully implemented the dynamic task details section that updates based on execution state. Here's a summary of what was accomplished:
## Summary
I implemented a comprehensive solution to make the details section dynamically update based on the execution state:
### Backend Changes:
1. **Added new execution state types** in `backend/src/models/task_attempt.rs`:
- `ExecutionState` enum with states: NotStarted, SetupRunning, SetupComplete, SetupFailed, CodingAgentRunning, CodingAgentComplete, CodingAgentFailed, Complete
- `TaskAttemptState` struct containing execution state, change detection, and process IDs
2. **Created `get_execution_state` method** that analyzes:
- Project setup script configuration
- Running execution processes
- Process status and completion
- Whether changes have been made to the worktree
3. **Added new API endpoint** `GET /projects/:project_id/tasks/:task_id/attempts/:attempt_id` to fetch execution state
4. **Updated type generation** to include the new types in the shared TypeScript definitions
### Frontend Changes:
1. **Enhanced useTaskDetails hook** to:
- Poll the execution state endpoint every 2 seconds
- Fetch execution state when switching attempts
- Provide execution state data to components
2. **Completely redesigned TaskDetailsPanel** with dynamic content rendering:
- **Setup Script Running**: Shows loading spinner with "Setup Script Running" message
- **Setup Failed**: Shows error message
- **Coding Agent Running (no changes)**: Shows full-height agent conversation only
- **Changes Available**: Shows 2/3 changes + 1/3 conversation split view
- **Default**: Shows "execution not started" message
### Key Features:
- **Real-time Updates**: Polls execution state every 2 seconds during execution
- **Progressive Disclosure**: Shows appropriate content based on execution phase
- **State-Aware UI**: Different layouts for different execution states
- **Error Handling**: Proper error states for failed setup scripts
- **Type Safety**: Full TypeScript support for execution states
The implementation ensures that users see relevant information at each stage of task execution, providing better visibility into what's happening and reducing confusion about the current state.
* Perfect! The fix is now in place. Here's what I've done to solve the loading spinner issue:
## Fix Summary
The problem was that the execution state was only being fetched during:
1. Polling (when `isAttemptRunning` is true)
2. Manual attempt switching via `handleAttemptChange`
But it was **not** being fetched during the initial page load when an attempt was automatically selected.
## Changes Made
1. **Added useEffect for selectedAttempt changes**: This ensures that whenever `selectedAttempt` changes (including during initial load), both `fetchAttemptData` and `fetchExecutionState` are called.
2. **Updated fetchTaskAttempts**: Added `fetchExecutionState` call when setting the latest attempt during initial load, and included it in the dependency array.
## How the Fix Works
Now when the page reloads:
1. `fetchTaskAttempts` runs and finds the latest attempt
2. It sets `selectedAttempt` to the latest attempt
3. The new useEffect detects the `selectedAttempt` change and immediately calls both:
- `fetchAttemptData(selectedAttempt.id)`
- `fetchExecutionState(selectedAttempt.id)`
4. The execution state is loaded, so the appropriate content is shown instead of the loading spinner
This ensures that the execution state is always available when displaying attempt details, whether it's the initial load, manual switching between attempts, or during polling updates.
* Perfect! The changes have been successfully implemented. Here's a summary of what I've done:
## Changes Made
### 1. Setup Script Stdio Display
- **Modified the setup running state** to show the actual stdout/stderr of the setup script instead of just a spinner
- **Enhanced process lookup** to find the setup script execution process from either the execution state's `setup_process_id` or by searching for processes with type `setupscript`
- **Added terminal-style output display** with:
- Black background and green text (terminal aesthetic)
- Monospace font for proper formatting
- Scrollable area with max height
- Fallback to "No output yet..." when no output is available
### 2. Diff Refresh During Coding Agent Execution
- **Added automatic diff polling** when the coding agent is running
- **Polls every 3 seconds** during `CodingAgentRunning` state to catch file changes as they happen
- **Uses existing fetchDiff function** to maintain consistency with manual refresh behavior
### 3. Enhanced Process Data Fetching
- **Modified fetchAttemptData** to always fetch setup script process details when available
- **Ensures setup script output is available** even when the script has completed but we want to show its output
- **Maintains existing logic** for other running processes while adding setup script support
## Key Features
1. **Real-time Setup Script Output**: Users can now see exactly what the setup script is doing, including any errors or progress messages.
2. **Live Diff Updates**: When the coding agent is making changes, the diff view automatically refreshes every 3 seconds so users can see files being modified in real-time.
3. **Better Visual Feedback**: The terminal-style output for setup scripts provides a familiar development experience.
4. **Non-blocking Updates**: The diff refresh happens independently and doesn't interfere with other UI interactions.
The implementation ensures that users get immediate feedback during both phases of execution - setup and coding - making the development process much more transparent and engaging.
* Perfect! Both issues have been fixed:
## Summary of Fixes
### 1. Setup Script Output Display
**Fixed**: Combined stdout and stderr properly
- **Before**: Only showed stdout OR stderr (whichever existed first)
- **After**: Shows both stdout AND stderr concatenated together
- **Implementation**:
```javascript
const stdout = setupProcess.stdout || '';
const stderr = setupProcess.stderr || '';
const combined = [stdout, stderr].filter(Boolean).join('\n');
return combined || 'No output yet...';
```
### 2. Automatic Diff Refresh During Coding Agent Execution
**Fixed**: Added proper diff polling with immediate refresh
- **Before**: Diff wasn't automatically updating during coding agent execution
- **After**: Diff refreshes immediately when coding agent starts + every 2 seconds while running
- **Implementation**:
- Immediate `fetchDiff()` call when entering `CodingAgentRunning` state
- `setInterval` to refresh every 2 seconds during execution
- Proper cleanup when state changes or component unmounts
### 3. Enhanced Setup Process Data Fetching
**Improved**: Better process data availability
- **Enhancement**: Modified `fetchAttemptData` to always fetch setup script process details
- **Benefit**: Ensures setup script output is available even after completion
- **Implementation**: Added dedicated setup script process fetching logic
## Key Improvements
1. **Real-time Setup Feedback**: Users can now see the complete output (both stdout and stderr) from setup scripts as they run
2. **Live Diff Updates**: File changes appear automatically every 2 seconds during coding agent execution
3. **Immediate Feedback**: Diff refreshes immediately when coding agent starts, then continues polling
4. **Better Error Visibility**: Setup script errors (stderr) are now visible alongside normal output
The implementation ensures that users get comprehensive, real-time feedback during both setup and coding phases, making the development process much more transparent and responsive.
* The styles have been cleaned up:
1. **Fixed the "No logs available" message**: Changed "No output yet..." to "Waiting for setup script output..." to indicate this is a loading state rather than an error.
2. **Simplified setup script display**: Removed the terminal UI styling including:
- The loading spinner and centered layout
- The bordered card wrapper
- The "Setup Script Output" header
- The black terminal background with green text
- Applied simple monospaced font styling similar to normalized logs
The setup script now displays with a cleaner, more consistent style that matches the rest of the interface while still being clearly readable with monospaced font.
* Added auto-scroll functionality to the setup script section:
1. **Added setup scroll ref**: Created `setupScrollRef` to track the setup script container
2. **Added auto-scroll effect**: Created a `useEffect` that monitors `attemptData.runningProcessDetails` and automatically scrolls to the bottom when the setup script output updates
3. **Applied ref to container**: Added the ref to the setup script container div
The setup script logs will now automatically scroll to the bottom as new output is received, matching the behavior of the normalized logs.
* Perfect! I've successfully implemented the follow up executor visualization in the TaskDetailsPanel. Here's what I did:
## Summary of Changes
1. **Identified follow up executor data structure**: Follow up executors are identified by having `process_type: "codingagent"` and `command: "followup_executor"` (vs main executor with `command: "executor"`)
2. **Modified TaskDetailsPanel logic**: Updated both the full-screen view (when no changes) and the split view (when changes are present) to:
- Find the main coding agent process (command: "executor")
- Find all follow up executor processes (command: "followup_executor")
- Display them in sequence with the main executor first, followed by follow up executors
3. **Enhanced UI presentation**: Each follow up executor is displayed with:
- A clear separator (border-top)
- A header showing "Follow-up #X" with the sequence number
- The start time of the follow up executor
- The normalized conversation viewer for that executor's logs
4. **Tested the implementation**: Both frontend and backend build successfully, and the code passes linting checks.
The implementation now properly handles and visualizes follow up executors by showing them under the main executor in chronological order, making it clear which conversation belongs to which executor while maintaining the existing functionality for the main coding agent.
* ## Issue Fixed!
I found and fixed the bug. The problem was in the TaskDetailsPanel logic that determines when to show the conversation logs vs. the fallback "Task execution not started yet" message.
### Root Cause
The condition `if (isCodingAgentRunning && !hasChanges)` only handled the case when the coding agent was *running*, but after follow up executors complete, the execution state becomes `CodingAgentComplete`. This caused the logic to fall through to the default case, showing "Task execution not started yet" instead of the conversation logs.
### Fix Applied
I modified the condition to also handle the completed state:
- Added `isCodingAgentComplete` variable to track when execution is complete
- Updated the condition to `if ((isCodingAgentRunning || isCodingAgentComplete) && !hasChanges)`
This ensures that both running and completed follow up executors will properly display their conversation logs, whether they're still running or have finished execution.
The fix is minimal and targeted, only affecting the specific scenario where follow up executors complete without generating file changes, while preserving all other functionality.
* ## Perfect! All styling changes implemented successfully!
Here's a summary of what I've accomplished:
### ✅ Backend Changes
1. **Extended NormalizedConversation structure** to include `prompt` and `summary` fields
2. **Updated all executor implementations** (Claude, Amp, default) to support the new fields
3. **Modified the normalized-logs API endpoint** to fetch executor session data and include prompt/summary in the response
4. **Regenerated TypeScript types** to reflect the new structure
### ✅ Frontend Changes
1. **Added prompt display** above the normalized logs with a distinctive blue styling and MessageSquare icon
2. **Implemented horizontal dividers** between all log sections:
- Between prompt and conversation logs
- Between main executor and follow-up executors
- Between multiple follow-up executors
3. **Removed the "Follow-up #1 Started:" headers** for a cleaner interface
4. **Updated the layout** to use proper spacing and consistent divider styling
### 🎨 UI Improvements
- **Prompts are displayed** in a blue-themed box with monospace font for better readability
- **Horizontal lines** consistently separate each logical section (prompt → executor → follow-up prompt → follow-up executor)
- **Clean, streamlined appearance** without verbose headers
- **Consistent spacing** and visual hierarchy throughout
The implementation properly handles cases where:
- Prompts may or may not be available
- There are multiple follow-up executors
- There are no follow-up executors
- The main executor completes with or without follow-ups
All builds pass and the code maintains the existing functionality while adding the requested visual improvements!
* ## ✅ Styling Updates Complete!
I've successfully implemented all the requested styling changes:
### 🔨 **Updated Prompt Display**
- **Changed icon** from MessageSquare to **Hammer** with orange color (`text-orange-600`)
- **Removed blue box styling** - no more background, border, or rounded corners
- **Removed monospaced font** - now uses normal text styling with `text-sm` and `text-foreground`
- **Maintains whitespace-pre-wrap** to preserve formatting without the monospace appearance
### 📏 **Improved Spacing**
- **Increased padding** at the bottom of the horizontal divider from `mb-4` to `mb-6`
- This creates **better visual separation** between the user message (prompt) and the agent logs
- **Maintains consistent spacing** throughout the component
### 🎨 **Clean Visual Hierarchy**
- User prompts now appear as **clean, readable text** with a hammer icon
- **Horizontal dividers** provide clear separation between sections
- **Proper padding** ensures comfortable reading experience
- **Consistent with the overall UI theme** rather than standing out with special styling
The changes maintain all existing functionality while providing the cleaner, more integrated visual appearance you requested. The prompt now blends naturally with the rest of the conversation while still being clearly distinguished by the hammer icon and horizontal divider!
* ## ✅ Styling Fixes Applied!
I've successfully implemented both requested changes:
### 🔨 **Blue Hammer Icon**
- Changed the hammer icon color from `text-orange-600` to `text-blue-600`
- Now matches the blue theme you requested
### 📏 **Fixed Spacing Gap**
- **Removed the bottom margin** from the horizontal divider (`mb-6` removed)
- **Added conditional top margin** to the conversation entries container (`mt-6` when prompt exists)
- This creates the **proper gap between the divider and the normalized logs**
### 🎨 **How the spacing now works:**
1. **User prompt** with hammer icon
2. **Horizontal divider** (no bottom margin)
3. **6-unit gap** (via `mt-6` on conversation container)
4. **Normalized logs/conversation entries**
The spacing is now properly balanced with a clean visual separation between the user message and the agent logs, while the blue hammer icon provides a cohesive color scheme throughout the interface!
* ## ✅ Spacing Fixed!
I've corrected the spacing issue by:
### 📏 **Fixed the Gap**
- **Added `mb-6` to the horizontal divider** - this creates the proper 6-unit gap below the divider
- **Removed the conditional margin logic** from the conversation entries container - it was causing the spacing to not work properly
- **Simplified the structure** for more predictable spacing behavior
### 🎯 **Now the spacing flow is:**
1. **User message** (with hammer icon)
2. **Gap** (via `mb-4` on user message container)
3. **Horizontal divider**
4. **Gap** (via `mb-6` on horizontal divider) ← **This was missing before!**
5. **Normalized logs/conversation entries**
The spacing should now be exactly what you requested - proper space both above and below the horizontal divider!
* ## ✅ Spacing Finally Fixed!
I've resolved the `space-y-0` override issue by **restructuring the layout**:
### 🔧 **What was wrong:**
- The `space-y-0` class creates CSS rule `.space-y-0 > :not([hidden]) ~ :not([hidden])` that sets `margin-top: 0` and `margin-bottom: 0` on all sibling elements
- This was overriding the `mb-6` class on the horizontal divider
### 🎯 **How I fixed it:**
1. **Removed `space-y-0` class** from the outer container
2. **Moved the horizontal divider outside** the prompt section to its own top-level element
3. **Made the structure flat** so no spacing utility classes conflict with each other
### 📏 **New structure:**
```jsx
<div> // No space-y-0 class
{/* User prompt */}
<div className="mb-4">...</div> // Space below prompt
{/* Horizontal divider */}
<div className="mb-6">...</div> // Space below divider - no override!
{/* Conversation entries */}
<div className="space-y-2">...</div> // Only affects entries
</div>
```
Now the `mb-6` on the horizontal divider won't be overridden, and you should see the proper gap between the divider and the normalized logs!
* ## ✅ Loading Message Fixed!
I've resolved the misleading "No logs available" message by improving the state logic:
### 🔧 **What was wrong:**
- When a follow-up execution starts, the API call completes successfully but returns no conversation data yet
- The component would show "No conversation data available" even though the process was still running and would eventually produce logs
### 🎯 **How I fixed it:**
- **Added execution process status check** - when `executionProcess.status === 'running'` and there's no conversation data
- **Shows "Waiting for logs..."** instead of "No conversation data available"
- **Maintains proper error state** for truly failed or empty completed processes
### 📱 **New behavior:**
- **When follow-up starts**: Shows "Waiting for logs..." ✅
- **During execution**: Shows "Waiting for logs..." ✅
- **When logs appear**: Shows the actual conversation ✅
- **When execution completes with no logs**: Shows "No conversation data available" ✅
- **On actual errors**: Shows the error message ✅
Now users will see a proper loading state that indicates the system is working, rather than an error-like message that suggests something went wrong!
* ## ✅ "No logs available" Message Fixed!
I've resolved the misleading error message by fixing the root cause in the backend:
### 🔧 **What was wrong:**
- When a follow-up execution starts, `process.stdout` is `None` because logs haven't been generated yet
- The backend was returning `success: false` with "No logs available for this execution process"
- The frontend was showing this as an error message
### 🎯 **How I fixed it:**
1. **Added status check in the backend** - when `process.status == ExecutionProcessStatus::Running` and `stdout` is `None`
2. **Return success with empty conversation** instead of an error
3. **Include executor session data** (prompt/summary) even for running processes without logs yet
4. **Maintain error state** only for truly failed or completed processes with no logs
### 📱 **New behavior:**
- **When follow-up starts**: Backend returns empty conversation with `success: true` ✅
- **Frontend shows**: "Waiting for logs..." (from previous fix) ✅
- **When logs appear**: Shows actual conversation data ✅
- **When process fails**: Still shows proper error messages ✅
- **When completed with no logs**: Shows "No conversation data available" ✅
Now when you start a follow-up, you should see "Waiting for logs..." instead of the misleading "No logs available for this execution process" error message!
* ## ✅ Spacing System Refactored!
I've completely refactored the spacing system for a much cleaner and more predictable layout:
### 🏗️ **New Systematic Approach**
#### **NormalizedConversationViewer (Internal Spacing):**
- **Prompt**: `mb-6` creates consistent gap to conversation entries
- **Conversation entries**: `space-y-2` between each entry
- **No internal dividers** - keeps the component focused on its own content
#### **TaskDetailsPanel (Executor Separation):**
- **Container**: `space-y-8` creates consistent spacing between main sections
- **Follow-up dividers**: `border-t` + `mb-8` for proper separation
- **Clean structure** with predictable spacing
### 📏 **Spacing Flow Now:**
1. **Main executor prompt**
2. **6-unit gap** (prompt `mb-6`)
3. **Main executor conversation**
4. **8-unit gap** (container `space-y-8`)
5. **Horizontal divider**
6. **8-unit gap** (divider `mb-8`)
7. **Follow-up prompt**
8. **6-unit gap** (prompt `mb-6`)
9. **Follow-up conversation**
10. **8-unit gap** (for next follow-up if any)
### 🎯 **Benefits:**
- **Consistent spacing** throughout all states
- **No more conflicting CSS rules** or override issues
- **Systematic approach** that's easy to maintain and debug
- **Proper separation** between logical sections
- **Clean, readable layout** with predictable gaps
The spacing should now be perfect in all scenarios - with or without prompts, with single or multiple follow-ups, and in both full-screen and split-view modes!
* I've successfully improved the diff styling to be more compact and fit better within the sidebar. Here are the key changes made:
**Spacing Improvements:**
- Reduced spacing between diff files from `space-y-6` to `space-y-3`
- Reduced main container padding from `p-6` to `p-4`
- Reduced file header padding from `py-2` to `py-1.5`
**Line Compactness:**
- Reduced diff line height from `min-h-[1.75rem]` to `min-h-[1.25rem]`
- Reduced line number column width from `w-16` to `w-12`
- Reduced line number padding from `px-2` to `px-1.5`
- Reduced individual line number widths from `w-6` to `w-5`
- Reduced line content padding from `px-3` to `px-2`
**Expand/Collapse Buttons:**
- Reduced expand button height from `h-8` to `h-6`
These changes make the diff view significantly more compact while maintaining readability. The diff will now display more content in the same space, making better use of the sidebar real estate. The frontend builds successfully with these changes.
* Perfect! The migration is complete. Let me summarize what I've done:
I've successfully migrated the merge, rebase, and PR functionality from the old `task-attempt-compare.tsx` page to the `TaskDetailsToolbar.tsx` component and deleted the old page.
1. **Added to TaskDetailsToolbar.tsx:**
- Branch status fetching with `fetchBranchStatus` function
- Merge functionality with uncommitted changes warning
- Rebase functionality
- Create PR functionality with dialog
- Branch status display showing commits ahead/behind and uncommitted changes
- Git operation buttons (Merge, Rebase, Create PR) with proper enabling/disabling logic
- Two dialog components for warnings and PR creation
2. **Deleted:**
- `frontend/src/pages/task-attempt-compare.tsx` - The entire compare page
3. **Updated routing:**
- Removed the compare page route from `App.tsx`
- Removed the import for `TaskAttemptComparePage`
- Updated keyboard shortcuts to remove compare page navigation logic
- Removed the "Changes" button that linked to the old compare page
4. **Cleaned up imports:**
- Removed unused imports like `Link` from react-router-dom and `GitCompare` icon
The diff view functionality remains in the `TaskDetailsPanel.tsx` where it was already migrated, and now users can access merge, rebase, and PR creation directly from the toolbar without needing a separate page. The branch status is also prominently displayed in the toolbar so users can see the current state at a glance.
* Perfect! Now the merge, rebase, and PR buttons will be disabled when an attempt is running, preventing users from performing git operations while code is being executed.
* Cargo fmt
* Clippy
* Prettier
* Cleanup
* Clippy
2025-07-03 22:00:44 +01:00
fn process_unstaged_file (
files : & mut Vec < FileDiff > ,
worktree_repo : & Repository ,
base_oid : git2 ::Oid ,
worktree_path : & str ,
path_str : & str ,
delta : & git2 ::DiffDelta ,
) -> Result < ( ) , TaskAttemptError > {
let old_file = delta . old_file ( ) ;
let new_file = delta . new_file ( ) ;
// Check if we already have a diff for this file from committed changes
if let Some ( existing_file ) = files . iter_mut ( ) . find ( | f | f . path = = path_str ) {
// File already has committed changes, need to create a combined diff
// from the base branch to the current working directory (including unstaged changes)
// Get the base content (from the fork point)
let base_content = if let Ok ( base_commit ) = worktree_repo . find_commit ( base_oid ) {
if let Ok ( base_tree ) = base_commit . tree ( ) {
match base_tree . get_path ( std ::path ::Path ::new ( path_str ) ) {
Ok ( entry ) = > {
if let Ok ( blob ) = worktree_repo . find_blob ( entry . id ( ) ) {
String ::from_utf8_lossy ( blob . content ( ) ) . to_string ( )
} else {
String ::new ( )
}
}
Err ( _ ) = > String ::new ( ) ,
}
} else {
String ::new ( )
}
} else {
String ::new ( )
} ;
// Get the working directory content
let working_content = if delta . status ( ) ! = git2 ::Delta ::Deleted {
let file_path = std ::path ::Path ::new ( worktree_path ) . join ( path_str ) ;
std ::fs ::read_to_string ( & file_path ) . unwrap_or_default ( )
} else {
String ::new ( )
} ;
// Create a combined diff from base to working directory
if base_content ! = working_content {
// Use git's patch generation with the content directly
let mut diff_opts = git2 ::DiffOptions ::new ( ) ;
diff_opts . context_lines ( 10 ) ;
diff_opts . interhunk_lines ( 0 ) ;
if let Ok ( patch ) = git2 ::Patch ::from_buffers (
base_content . as_bytes ( ) ,
Some ( std ::path ::Path ::new ( path_str ) ) ,
working_content . as_bytes ( ) ,
Some ( std ::path ::Path ::new ( path_str ) ) ,
Some ( & mut diff_opts ) ,
) {
let mut combined_chunks = Vec ::new ( ) ;
// Process the patch hunks
for hunk_idx in 0 .. patch . num_hunks ( ) {
if let Ok ( ( _hunk , hunk_lines ) ) = patch . hunk ( hunk_idx ) {
// Process each line in the hunk
for line_idx in 0 .. hunk_lines {
if let Ok ( line ) = patch . line_in_hunk ( hunk_idx , line_idx ) {
let content =
String ::from_utf8_lossy ( line . content ( ) ) . to_string ( ) ;
let chunk_type = match line . origin ( ) {
' ' = > DiffChunkType ::Equal ,
'+' = > DiffChunkType ::Insert ,
'-' = > DiffChunkType ::Delete ,
_ = > continue , // Skip other line types
} ;
combined_chunks . push ( DiffChunk {
chunk_type ,
content ,
} ) ;
}
}
}
}
if ! combined_chunks . is_empty ( ) {
existing_file . chunks = combined_chunks ;
}
}
}
} else {
// File only has unstaged changes (new file or uncommitted changes only)
match Self ::generate_git_diff_chunks ( worktree_repo , & old_file , & new_file , path_str ) {
Ok ( diff_chunks ) if ! diff_chunks . is_empty ( ) = > {
files . push ( FileDiff {
path : path_str . to_string ( ) ,
chunks : diff_chunks ,
} ) ;
}
Err ( e ) = > {
eprintln! ( " Error generating unstaged diff for {} : {:?} " , path_str , e ) ;
}
_ = > { }
}
}
Ok ( ( ) )
}
2025-06-22 23:09:01 +01:00
/// Generate diff chunks using Git's native diff algorithm
pub fn generate_git_diff_chunks (
repo : & Repository ,
old_file : & git2 ::DiffFile ,
new_file : & git2 ::DiffFile ,
file_path : & str ,
) -> Result < Vec < DiffChunk > , TaskAttemptError > {
use std ::path ::Path ;
2025-06-20 16:35:17 +01:00
let mut chunks = Vec ::new ( ) ;
2025-06-20 17:10:49 +01:00
2025-06-22 23:09:01 +01:00
// Create a patch for the single file using Git's native diff
let old_blob = if ! old_file . id ( ) . is_zero ( ) {
Some ( repo . find_blob ( old_file . id ( ) ) ? )
} else {
None
} ;
2025-06-20 17:10:49 +01:00
2025-06-22 23:09:01 +01:00
let new_blob = if ! new_file . id ( ) . is_zero ( ) {
Some ( repo . find_blob ( new_file . id ( ) ) ? )
} else {
None
} ;
2025-06-20 17:49:46 +01:00
2025-06-24 17:22:00 +01:00
// Generate patch using Git's diff algorithm with context
let mut diff_opts = git2 ::DiffOptions ::new ( ) ;
diff_opts . context_lines ( 10 ) ; // Include 10 lines of context around changes
diff_opts . interhunk_lines ( 0 ) ; // Don't merge hunks
2025-06-22 23:09:01 +01:00
let patch = match ( old_blob . as_ref ( ) , new_blob . as_ref ( ) ) {
2025-06-22 23:43:13 +01:00
( Some ( old_b ) , Some ( new_b ) ) = > git2 ::Patch ::from_blobs (
old_b ,
Some ( Path ::new ( file_path ) ) ,
new_b ,
Some ( Path ::new ( file_path ) ) ,
2025-06-24 17:22:00 +01:00
Some ( & mut diff_opts ) ,
2025-06-22 23:43:13 +01:00
) ? ,
2025-06-22 23:09:01 +01:00
( None , Some ( new_b ) ) = > {
2025-06-23 00:00:40 +01:00
// File was added - diff from empty buffer to new blob content
git2 ::Patch ::from_buffers (
& [ ] , // empty buffer represents the "old" version (file didn't exist)
2025-06-22 23:09:01 +01:00
Some ( Path ::new ( file_path ) ) ,
2025-06-23 00:00:40 +01:00
new_b . content ( ) , // new blob content as buffer
2025-06-22 23:09:01 +01:00
Some ( Path ::new ( file_path ) ) ,
2025-06-24 17:22:00 +01:00
Some ( & mut diff_opts ) ,
2025-06-22 23:09:01 +01:00
) ?
}
( Some ( old_b ) , None ) = > {
2025-06-23 00:00:40 +01:00
// File was deleted - diff from old blob to empty buffer
2025-06-22 23:09:01 +01:00
git2 ::Patch ::from_blob_and_buffer (
old_b ,
Some ( Path ::new ( file_path ) ) ,
& [ ] ,
Some ( Path ::new ( file_path ) ) ,
2025-06-24 17:22:00 +01:00
Some ( & mut diff_opts ) ,
2025-06-22 23:09:01 +01:00
) ?
}
( None , None ) = > {
// No change, shouldn't happen
return Ok ( chunks ) ;
}
} ;
2025-06-20 17:49:46 +01:00
2025-06-22 23:09:01 +01:00
// Process the patch hunks
for hunk_idx in 0 .. patch . num_hunks ( ) {
let ( _hunk , hunk_lines ) = patch . hunk ( hunk_idx ) ? ;
2025-06-22 23:43:13 +01:00
2025-06-22 23:09:01 +01:00
// Process each line in the hunk
for line_idx in 0 .. hunk_lines {
let line = patch . line_in_hunk ( hunk_idx , line_idx ) ? ;
let content = String ::from_utf8_lossy ( line . content ( ) ) . to_string ( ) ;
2025-06-22 23:43:13 +01:00
2025-06-22 23:09:01 +01:00
let chunk_type = match line . origin ( ) {
' ' = > DiffChunkType ::Equal ,
'+' = > DiffChunkType ::Insert ,
'-' = > DiffChunkType ::Delete ,
_ = > continue , // Skip other line types (like context headers)
} ;
2025-06-20 17:49:46 +01:00
2025-06-20 16:35:17 +01:00
chunks . push ( DiffChunk {
2025-06-22 23:09:01 +01:00
chunk_type ,
content ,
2025-06-20 16:35:17 +01:00
} ) ;
}
}
2025-06-20 17:10:49 +01:00
2025-06-22 23:09:01 +01:00
Ok ( chunks )
2025-06-20 16:35:17 +01:00
}
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
/// Get the branch status for this task attempt
2025-06-19 18:59:47 -04:00
pub async fn get_branch_status (
pool : & SqlitePool ,
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
) -> Result < BranchStatus , TaskAttemptError > {
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
// ── fetch the task attempt ───────────────────────────────────────────────────
2025-06-19 18:59:47 -04:00
let attempt = sqlx ::query_as! (
TaskAttempt ,
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
r #"
SELECT ta . id AS " id!: Uuid " ,
ta . task_id AS " task_id!: Uuid " ,
ta . worktree_path ,
ta . branch ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
ta . base_branch ,
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
ta . merge_commit ,
ta . executor ,
ta . pr_url ,
ta . pr_number ,
ta . pr_status ,
ta . pr_merged_at AS " pr_merged_at: DateTime<Utc> " ,
ta . created_at AS " created_at!: DateTime<Utc> " ,
ta . updated_at AS " updated_at!: DateTime<Utc> "
FROM task_attempts ta
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1
AND t . id = $ 2
AND t . project_id = $ 3
" #,
2025-06-19 18:59:47 -04:00
attempt_id ,
task_id ,
project_id
)
. fetch_optional ( pool )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
// ── fetch the owning project & open its repository ───────────────────────────
2025-06-19 18:59:47 -04:00
let project = Project ::find_by_id ( pool , project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
use git2 ::{ BranchType , Repository , Status , StatusOptions } ;
2025-06-16 23:47:16 -04:00
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
let main_repo = Repository ::open ( & project . git_repo_path ) ? ;
let attempt_branch = attempt . branch . clone ( ) ;
// ── locate the commit pointed to by the attempt branch ───────────────────────
let attempt_ref = main_repo
// try "refs/heads/<name>" first, then raw name
. find_reference ( & format! ( " refs/heads/ {} " , attempt_branch ) )
. or_else ( | _ | main_repo . find_reference ( & attempt_branch ) ) ? ;
let attempt_oid = attempt_ref . target ( ) . unwrap ( ) ;
// ── determine the base branch & ahead/behind counts ─────────────────────────
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
let base_branch_name = attempt . base_branch . clone ( ) ;
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
// 1. prefer the branch’ s configured upstream, if any
if let Ok ( local_branch ) = main_repo . find_branch ( & attempt_branch , BranchType ::Local ) {
if let Ok ( upstream ) = local_branch . upstream ( ) {
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
if let Some ( _name ) = upstream . name ( ) ? {
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
if let Some ( base_oid ) = upstream . get ( ) . target ( ) {
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
let ( _ahead , _behind ) =
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
main_repo . graph_ahead_behind ( attempt_oid , base_oid ) ? ;
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
// Ignore upstream since we use stored base branch
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
}
}
}
}
2025-06-24 17:39:51 +01:00
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
// Calculate ahead/behind counts using the stored base branch
let ( commits_ahead , commits_behind ) =
if let Ok ( base_branch ) = main_repo . find_branch ( & base_branch_name , BranchType ::Local ) {
if let Some ( base_oid ) = base_branch . get ( ) . target ( ) {
main_repo . graph_ahead_behind ( attempt_oid , base_oid ) ?
} else {
( 0 , 0 ) // Base branch has no commits
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
}
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
} else {
// Base branch doesn't exist, assume no relationship
( 0 , 0 )
} ;
2025-06-19 18:59:47 -04:00
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
// ── detect any uncommitted / untracked changes ───────────────────────────────
let repo_for_status = Repository ::open ( & project . git_repo_path ) ? ;
2025-06-19 18:59:47 -04:00
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
let mut status_opts = StatusOptions ::new ( ) ;
status_opts
. include_untracked ( true )
. recurse_untracked_dirs ( true )
. include_ignored ( false ) ;
2025-06-19 18:59:47 -04:00
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
let has_uncommitted_changes = repo_for_status
. statuses ( Some ( & mut status_opts ) ) ?
. iter ( )
. any ( | e | e . status ( ) ! = Status ::CURRENT ) ;
2025-06-19 18:59:47 -04:00
Fix branch-status endpoint (#75)
* The fix has been successfully implemented! Here's what I changed:
**Problem**: The `get_branch_status` function was incorrectly using `main_repo.head()` to determine the base branch name, which returns the currently checked out branch in the main repository instead of the actual base branch that the task branch should be compared against.
**Solution**: I replaced the problematic logic with a smarter approach that:
1. **Finds the actual base branch** by testing common base branch names (`main`, `master`, `develop`)
2. **Uses git merge-base** to determine which branch the task branch actually diverged from
3. **Compares the task branch to the correct base branch** instead of whatever happens to be checked out
This ensures that the branch status always shows the task branch's status relative to its actual base branch, regardless of what branch is currently checked out in the main repository.
The fix is now compiled and ready to use. The branch-status endpoint will now correctly return information about the task branch relative to its proper base branch.
* Perfect! I've fixed the branch-status issue correctly. Here's what the solution does:
**The Problem**: The previous code was using `main_repo.head()` which returns whatever branch is currently checked out in the main repository, not the actual base branch that the worktree was created from.
**The Solution**: The new code:
1. **Examines all local branches** in the main repository
2. **Finds the merge base** between each branch and the worktree branch
3. **Identifies the branch with the most recent common ancestor** - this is the actual base branch
4. **Uses that branch for comparison** instead of the currently checked out branch
This approach correctly identifies the actual base branch that the worktree was created from by analyzing the git history, regardless of what branch is currently checked out in the main repository.
The fix ensures that branch-status always returns information about the task branch relative to its true base branch, not whatever happens to be checked out at the time.
* Perfect! You were absolutely right. The fix is now much more efficient and uses the proper Git approach:
**The Much Better Solution**:
1. **First, check upstream tracking**: Look at the worktree's branch configuration to see if it has upstream tracking information (like `origin/main`)
2. **Then, use direct merge-base**: Instead of iterating through all branches, directly test a few common base branches (`main`, `master`, `develop`) using `git merge-base`
3. **No iteration needed**: This is equivalent to running `git merge-base HEAD main` for each potential base branch
This approach is:
- **Much faster** - no iteration through all branches
- **More accurate** - uses Git's built-in merge-base functionality
- **More direct** - exactly like the `git merge-base HEAD master` command you mentioned
The key insight was that we don't need to check every branch - we just need to find which common base branch the worktree actually diverged from using Git's merge-base algorithm directly.
* Perfect! Much better. The fix is now:
- **8 lines** instead of 40+
- **Uses functional programming** with `find_map` instead of verbose loops
- **Same functionality** - finds the first common branch (`main`, `master`, or `develop`) that has a valid merge-base with the worktree
- **Clean fallback** - defaults to `"main"` if no merge-base is found
This concise solution directly addresses the original issue: instead of returning whatever branch happens to be checked out (`main_repo.head()`), it finds the actual base branch that the worktree was created from using Git's merge-base algorithm.
* manual fix
2025-07-07 20:31:40 +01:00
// ── assemble & return ────────────────────────────────────────────────────────
2025-06-19 18:59:47 -04:00
Ok ( BranchStatus {
is_behind : commits_behind > 0 ,
commits_behind ,
commits_ahead ,
up_to_date : commits_behind = = 0 & & commits_ahead = = 0 ,
2025-06-19 21:41:18 -04:00
merged : attempt . merge_commit . is_some ( ) ,
2025-06-24 17:39:51 +01:00
has_uncommitted_changes ,
2025-06-24 23:40:48 +01:00
base_branch_name ,
2025-06-19 18:59:47 -04:00
} )
}
2025-07-01 15:11:51 +01:00
/// Rebase the worktree branch onto specified base branch (or current HEAD if none specified)
pub async fn rebase_attempt (
2025-06-19 18:59:47 -04:00
pool : & SqlitePool ,
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
2025-07-01 15:11:51 +01:00
new_base_branch : Option < String > ,
2025-06-19 18:59:47 -04:00
) -> Result < String , TaskAttemptError > {
// Get the task attempt with validation
let attempt = sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT ta.id as " id ! : Uuid " , ta.task_id as " task_id ! : Uuid " , ta.worktree_path, ta.branch, ta.base_branch, ta.merge_commit, ta.executor, ta.pr_url, ta.pr_number, ta.pr_status, ta.pr_merged_at as " pr_merged_at : DateTime < Utc > " , ta.created_at as " created_at ! : DateTime < Utc > " , ta.updated_at as " updated_at ! : DateTime < Utc > "
2025-06-19 18:59:47 -04:00
FROM task_attempts ta
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1 AND t . id = $ 2 AND t . project_id = $ 3 " #,
attempt_id ,
task_id ,
project_id
)
. fetch_optional ( pool )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
// Get the project
let project = Project ::find_by_id ( pool , project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
// Use the stored base branch if no new base branch is provided
let effective_base_branch = new_base_branch . or_else ( | | Some ( attempt . base_branch . clone ( ) ) ) ;
2025-06-19 18:59:47 -04:00
// Perform the git rebase operations (synchronous)
2025-07-01 15:11:51 +01:00
let new_base_commit = Self ::perform_rebase_operation (
& attempt . worktree_path ,
& project . git_repo_path ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
effective_base_branch ,
2025-07-01 15:11:51 +01:00
) ? ;
2025-06-19 18:59:47 -04:00
2025-06-19 21:28:36 -04:00
// No need to update database as we now get base_commit live from git
2025-06-19 18:59:47 -04:00
Ok ( new_base_commit )
2025-06-16 23:47:16 -04:00
}
2025-06-20 16:56:57 +01:00
/// Delete a file from the worktree and commit the change
pub async fn delete_file (
pool : & SqlitePool ,
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
file_path : & str ,
) -> Result < String , TaskAttemptError > {
// Get the task attempt with validation
let attempt = sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT ta.id as " id ! : Uuid " , ta.task_id as " task_id ! : Uuid " , ta.worktree_path, ta.branch, ta.base_branch, ta.merge_commit, ta.executor, ta.pr_url, ta.pr_number, ta.pr_status, ta.pr_merged_at as " pr_merged_at : DateTime < Utc > " , ta.created_at as " created_at ! : DateTime < Utc > " , ta.updated_at as " updated_at ! : DateTime < Utc > "
2025-06-20 16:56:57 +01:00
FROM task_attempts ta
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1 AND t . id = $ 2 AND t . project_id = $ 3 " #,
attempt_id ,
task_id ,
project_id
)
. fetch_optional ( pool )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
// Open the worktree repository
let repo = Repository ::open ( & attempt . worktree_path ) ? ;
// Get the absolute path to the file within the worktree
let worktree_path = Path ::new ( & attempt . worktree_path ) ;
let file_full_path = worktree_path . join ( file_path ) ;
// Check if file exists and delete it
if file_full_path . exists ( ) {
2025-06-20 17:10:49 +01:00
std ::fs ::remove_file ( & file_full_path ) . map_err ( | e | {
TaskAttemptError ::Git ( GitError ::from_str ( & format! (
2025-06-20 16:56:57 +01:00
" Failed to delete file {}: {} " ,
2025-06-20 17:10:49 +01:00
file_path , e
) ) )
} ) ? ;
2025-06-20 16:56:57 +01:00
debug! ( " Deleted file: {} " , file_path ) ;
} else {
info! ( " File {} does not exist, skipping deletion " , file_path ) ;
}
// Stage the deletion
let mut index = repo . index ( ) ? ;
index . remove_path ( Path ::new ( file_path ) ) ? ;
index . write ( ) ? ;
// Create a commit for the file deletion
let signature = repo . signature ( ) ? ;
let tree_id = index . write_tree ( ) ? ;
let tree = repo . find_tree ( tree_id ) ? ;
// Get the current HEAD commit
let head = repo . head ( ) ? ;
let parent_commit = head . peel_to_commit ( ) ? ;
let commit_message = format! ( " Delete file: {} " , file_path ) ;
let commit_id = repo . commit (
Some ( " HEAD " ) ,
& signature ,
& signature ,
& commit_message ,
& tree ,
& [ & parent_commit ] ,
) ? ;
info! ( " File {} deleted and committed: {} " , file_path , commit_id ) ;
Ok ( commit_id . to_string ( ) )
}
2025-07-01 16:28:15 +01:00
/// Create a GitHub PR for this task attempt
pub async fn create_github_pr (
pool : & SqlitePool ,
params : CreatePrParams < '_ > ,
) -> Result < String , TaskAttemptError > {
// Get the task attempt with validation
let attempt = sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT ta.id as " id ! : Uuid " , ta.task_id as " task_id ! : Uuid " , ta.worktree_path, ta.branch, ta.base_branch, ta.merge_commit, ta.executor, ta.pr_url, ta.pr_number, ta.pr_status, ta.pr_merged_at as " pr_merged_at : DateTime < Utc > " , ta.created_at as " created_at ! : DateTime < Utc > " , ta.updated_at as " updated_at ! : DateTime < Utc > "
2025-07-01 16:28:15 +01:00
FROM task_attempts ta
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1 AND t . id = $ 2 AND t . project_id = $ 3 " #,
params . attempt_id ,
params . task_id ,
params . project_id
)
. fetch_optional ( pool )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
// Get the project to access the repository path
let project = Project ::find_by_id ( pool , params . project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
// Extract GitHub repository information from the project path
let ( owner , repo_name ) = Self ::extract_github_repo_info ( & project . git_repo_path ) ? ;
// Push the branch to GitHub first
Self ::push_branch_to_github ( & attempt . worktree_path , & attempt . branch , params . github_token ) ? ;
// Create the PR using Octocrab
2025-07-01 17:45:00 +01:00
let pr_url = Self ::create_pr_with_octocrab (
2025-07-01 16:28:15 +01:00
params . github_token ,
& owner ,
& repo_name ,
& attempt . branch ,
params . base_branch . unwrap_or ( " main " ) ,
params . title ,
params . body ,
)
2025-07-01 17:45:00 +01:00
. await ? ;
// Extract PR number from URL (GitHub URLs are in format: https://github.com/owner/repo/pull/123)
let pr_number = pr_url
. split ( '/' )
. next_back ( )
. and_then ( | n | n . parse ::< i64 > ( ) . ok ( ) ) ;
// Update the task attempt with PR information
sqlx ::query! (
" UPDATE task_attempts SET pr_url = $1, pr_number = $2, pr_status = $3, updated_at = datetime('now') WHERE id = $4 " ,
pr_url ,
pr_number ,
" open " ,
params . attempt_id
)
. execute ( pool )
. await ? ;
Ok ( pr_url )
2025-07-01 16:28:15 +01:00
}
/// Extract GitHub owner and repo name from git repo path
fn extract_github_repo_info ( git_repo_path : & str ) -> Result < ( String , String ) , TaskAttemptError > {
// Try to extract from remote origin URL
let repo = Repository ::open ( git_repo_path ) ? ;
let remote = repo . find_remote ( " origin " ) . map_err ( | _ | {
TaskAttemptError ::ValidationError ( " No 'origin' remote found " . to_string ( ) )
} ) ? ;
let url = remote . url ( ) . ok_or_else ( | | {
TaskAttemptError ::ValidationError ( " Remote origin has no URL " . to_string ( ) )
} ) ? ;
// Parse GitHub URL (supports both HTTPS and SSH formats)
let github_regex = regex ::Regex ::new ( r "github\.com[:/]([^/]+)/(.+?)(?:\.git)?/?$" )
. map_err ( | e | TaskAttemptError ::ValidationError ( format! ( " Regex error: {} " , e ) ) ) ? ;
if let Some ( captures ) = github_regex . captures ( url ) {
let owner = captures . get ( 1 ) . unwrap ( ) . as_str ( ) . to_string ( ) ;
let repo_name = captures . get ( 2 ) . unwrap ( ) . as_str ( ) . to_string ( ) ;
Ok ( ( owner , repo_name ) )
} else {
Err ( TaskAttemptError ::ValidationError ( format! (
" Not a GitHub repository: {} " ,
url
) ) )
}
}
/// Push the branch to GitHub remote
fn push_branch_to_github (
worktree_path : & str ,
branch_name : & str ,
github_token : & str ,
) -> Result < ( ) , TaskAttemptError > {
let repo = Repository ::open ( worktree_path ) ? ;
// Get the remote
let remote = repo . find_remote ( " origin " ) ? ;
let remote_url = remote . url ( ) . ok_or_else ( | | {
TaskAttemptError ::ValidationError ( " Remote origin has no URL " . to_string ( ) )
} ) ? ;
// Convert SSH URL to HTTPS URL if necessary
let https_url = if remote_url . starts_with ( " git@github.com: " ) {
// Convert git@github.com:owner/repo.git to https://github.com/owner/repo.git
remote_url . replace ( " git@github.com: " , " https://github.com/ " )
} else if remote_url . starts_with ( " ssh://git@github.com/ " ) {
// Convert ssh://git@github.com/owner/repo.git to https://github.com/owner/repo.git
remote_url . replace ( " ssh://git@github.com/ " , " https://github.com/ " )
} else {
remote_url . to_string ( )
} ;
// Create a temporary remote with HTTPS URL for pushing
let temp_remote_name = " temp_https_origin " ;
// Remove any existing temp remote
let _ = repo . remote_delete ( temp_remote_name ) ;
// Create temporary HTTPS remote
let mut temp_remote = repo . remote ( temp_remote_name , & https_url ) ? ;
// Create refspec for pushing the branch
let refspec = format! ( " refs/heads/ {} :refs/heads/ {} " , branch_name , branch_name ) ;
// Set up authentication callback using the GitHub token
let mut callbacks = git2 ::RemoteCallbacks ::new ( ) ;
callbacks . credentials ( | _url , username_from_url , _allowed_types | {
git2 ::Cred ::userpass_plaintext ( username_from_url . unwrap_or ( " git " ) , github_token )
} ) ;
// Configure push options
let mut push_options = git2 ::PushOptions ::new ( ) ;
push_options . remote_callbacks ( callbacks ) ;
// Push the branch
let push_result = temp_remote . push ( & [ & refspec ] , Some ( & mut push_options ) ) ;
// Clean up the temporary remote
let _ = repo . remote_delete ( temp_remote_name ) ;
// Check push result
push_result . map_err ( TaskAttemptError ::Git ) ? ;
info! ( " Pushed branch {} to GitHub using HTTPS " , branch_name ) ;
Ok ( ( ) )
}
/// Create a PR using Octocrab
async fn create_pr_with_octocrab (
github_token : & str ,
owner : & str ,
repo_name : & str ,
head_branch : & str ,
base_branch : & str ,
title : & str ,
body : Option < & str > ,
) -> Result < String , TaskAttemptError > {
let octocrab = octocrab ::OctocrabBuilder ::new ( )
. personal_token ( github_token . to_string ( ) )
. build ( )
. map_err ( | e | {
TaskAttemptError ::ValidationError ( format! ( " Failed to create GitHub client: {} " , e ) )
} ) ? ;
// Verify repository access
octocrab . repos ( owner , repo_name ) . get ( ) . await . map_err ( | e | {
TaskAttemptError ::ValidationError ( format! (
" Cannot access repository {}/{}: {} " ,
owner , repo_name , e
) )
} ) ? ;
// Check if the base branch exists
octocrab
. repos ( owner , repo_name )
. get_ref ( & octocrab ::params ::repos ::Reference ::Branch (
base_branch . to_string ( ) ,
) )
. await
. map_err ( | e | {
TaskAttemptError ::ValidationError ( format! (
" Base branch '{}' does not exist: {} " ,
base_branch , e
) )
} ) ? ;
// Check if the head branch exists
octocrab . repos ( owner , repo_name )
. get_ref ( & octocrab ::params ::repos ::Reference ::Branch ( head_branch . to_string ( ) ) ) . await
. map_err ( | e | TaskAttemptError ::ValidationError ( format! ( " Head branch ' {} ' does not exist. Make sure the branch was pushed successfully: {} " , head_branch , e ) ) ) ? ;
let pr = octocrab
. pulls ( owner , repo_name )
. create ( title , head_branch , base_branch )
. body ( body . unwrap_or ( " " ) )
. send ( )
. await
. map_err ( | e | match e {
octocrab ::Error ::GitHub { source , .. } = > {
TaskAttemptError ::ValidationError ( format! (
" GitHub API error: {} (status: {}) " ,
source . message ,
source . status_code . as_u16 ( )
) )
}
_ = > TaskAttemptError ::ValidationError ( format! ( " Failed to create PR: {} " , e ) ) ,
} ) ? ;
info! (
" Created GitHub PR #{} for branch {} " ,
pr . number , head_branch
) ;
Ok ( pr
. html_url
. map ( | url | url . to_string ( ) )
. unwrap_or_else ( | | " " . to_string ( ) ) )
}
2025-07-01 17:45:00 +01:00
/// Update PR status and merge commit
pub async fn update_pr_status (
pool : & SqlitePool ,
attempt_id : Uuid ,
status : & str ,
merged_at : Option < DateTime < Utc > > ,
merge_commit_sha : Option < & str > ,
) -> Result < ( ) , sqlx ::Error > {
sqlx ::query! (
" UPDATE task_attempts SET pr_status = $1, pr_merged_at = $2, merge_commit = $3, updated_at = datetime('now') WHERE id = $4 " ,
status ,
merged_at ,
merge_commit_sha ,
attempt_id
)
. execute ( pool )
. await ? ;
Ok ( ( ) )
}
Redesign sidebar (#63)
* Agent logs
* ## Summary
I have successfully implemented a comprehensive log normalization system for vibe-kanban with the following features:
### ✅ **Completed Tasks:**
1. **Defined normalized log format types** - Created standard TypeScript-exportable types:
- `NormalizedConversation` - Top-level container with entries, session ID, and executor type
- `NormalizedEntry` - Individual conversation entries with timestamp, type, and content
- `NormalizedEntryType` - Union type for different entry types (user, assistant, tool use, system, thinking)
- `ActionType` - Specific action types for tool operations (file read/write, commands, search, etc.)
2. **Implemented normalize method for AMP executor** - Parses AMP's JSONL format to extract:
- User and assistant messages
- Thinking content
- Tool usage with intelligent action type detection
- Session ID (threadID)
- Timestamps from message metadata
3. **Implemented normalize method for Claude executor** - Parses Claude's stream-json format to extract:
- User and assistant messages
- Tool usage with action type detection
- System initialization messages
- Session ID
- Comprehensive tool mapping for Claude-specific tools
4. **Added normalize method to executor trait** - Created a default implementation that returns empty results for unsupported executors, allowing easy extension to other executor types
5. **Created new API endpoint for normalized logs** - Added `GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint that:
- Validates execution process belongs to the correct project
- Determines executor type automatically
- Applies appropriate normalization logic
- Returns standardized conversation format
6. **Tested the implementation** - Created comprehensive unit tests for both AMP and Claude normalization, verifying correct parsing of real log examples
### 🔧 **Technical Implementation Details:**
- **Intelligent Action Type Detection**: The system maps tool names to specific action types (file operations, commands, searches, etc.) based on tool names and input parameters
- **Cross-platform Compatibility**: Handles different parameter naming conventions between executors
- **Error Handling**: Robust error handling with detailed error messages for debugging
- **TypeScript Integration**: Full TypeScript type generation for frontend consumption
- **Extensible Design**: Easy to add new executors by implementing the `normalize_logs` method
### 🚀 **Usage:**
The new endpoint can be called to get a normalized view of any execution process logs:
```bash
GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs
```
This returns a standardized conversation format that the frontend can easily consume to display:
- User inputs and assistant responses
- Tool usage with specific action details (file paths, commands, etc.)
- Internal reasoning (thinking)
- System status messages
- Session tracking information
The normalized format provides a high-level, consistent representation of the conversation regardless of which executor (AMP, Claude, etc.) generated the original logs.
* Perfect! I have successfully implemented the normalized logs feature. Here's a summary of what was accomplished:
## Summary
✅ **Replaced manual log rendering with normalized logs API**
### Key Changes Made:
1. **Created a new `NormalizedConversationViewer` component** that:
- Fetches normalized logs from the backend API endpoint
- Displays conversation entries with appropriate icons for each type:
- 👤 User messages (blue)
- 🤖 Assistant messages (green)
- ⚙️ System messages (gray)
- 🧠 Thinking (purple)
- 👁️ File reads (orange)
- ✏️ File writes (red)
- 💻 Command execution (yellow)
- 🔍 Search operations (indigo)
- 🌐 Web fetches (cyan)
- ➕ Task creation (teal)
- Shows timestamps and entry type labels
- Handles loading and error states
2. **Updated `TaskActivityHistory` component** to:
- Use the new `NormalizedConversationViewer` instead of `ExecutionOutputViewer`
- Pass the required `projectId` prop
- Maintain the same expansion/collapse functionality
3. **Updated `TaskDetailsPanel`** to pass the `projectId` to the activity history
4. **Removed legacy components**:
- The complex `ExecutionOutputViewer` that manually parsed JSONL
- The `ConversationViewer` that handled format-specific parsing
- All the manual stdout/stderr rendering logic
### Benefits:
- **Cleaner codebase**: Eliminated complex frontend parsing logic
- **Consistent display**: All executors now show logs in the same format
- **Better UX**: Clear icons and labels make it easy to understand conversation flow
- **Maintainable**: Backend handles format normalization, frontend just displays
- **Extensible**: New executor types automatically work with the existing UI
The implementation leverages the existing `/api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint and properly handles loading states, errors, and displays the conversation history with meaningful icons for each entry type.
* Excellent! The build is successful and the bundle size is slightly smaller after removing the unused code. The task is now complete:
✅ **Legacy log rendering components successfully removed**
- Deleted `ExecutionOutputViewer.tsx` (224 lines)
- Deleted `ConversationViewer.tsx` (complex JSONL parsing logic)
- Build passes and bundle size reduced
- All functionality now uses the clean normalized logs API
The codebase is now cleaner and fully transitioned to using the normalized logs system.
* Perfect! I've successfully refactored the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) component with the new layout:
## Key Changes Made:
1. **Top 2/3 - Code Changes Section**:
- Extracted all the diff processing logic from [`task-attempt-compare.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/pages/task-attempt-compare.tsx)
- Shows file diffs with syntax highlighting, expandable sections, and line numbers
- Includes file deletion functionality
- "Show/Hide All Unchanged" toggle for better navigation
2. **Bottom 1/3 - Agent Logs Section**:
- Preserved the existing [`TaskActivityHistory`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskActivityHistory.tsx) component
- Auto-scrolling behavior for new log entries
- Clear separation with border and background styling
3. **Maintained Existing Functionality**:
- Header, toolbar, and follow-up sections remain unchanged
- Editor selection dialog and ESC key handling preserved
- All existing hooks and state management intact
The component now provides a much better developer experience by showing code changes prominently in the top section while keeping the agent logs easily accessible in the bottom third.
* Perfect! The build is successful. I've implemented all the requested style changes to the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
**Code Changes Section (Top 2/3):**
- ✅ Removed "Code Changes" header
- ✅ Removed description text
- ✅ Removed "Show All Unchanged" button and related functionality
- ✅ Removed parent Card wrapper - each file diff is now directly displayed in its own card
**Agent Logs Section (Bottom 1/3):**
- ✅ Removed "Agent Logs" header
- ✅ Removed description text
- ✅ Now displays only the `NormalizedConversationViewer` component
- ✅ Removed card/container wrapper
The component now has a cleaner, more streamlined interface with the code changes taking up most of the space in the top section, and the agent conversation displayed directly in the bottom third without extra headers or containers.
* Perfect! The build is successful. I've updated the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) to properly find and display the coding agent conversation.
## Changes Made:
The component now:
1. **First looks** for any running coding agent process in `runningProcessDetails`
2. **If not found**, searches through all processes in the `processes` array to find any coding agent process (completed or otherwise)
3. **Creates a minimal ExecutionProcess object** from the summary data if needed, since the `NormalizedConversationViewer` only requires the process ID to fetch the conversation data
4. **Displays the conversation** for any coding agent process found, whether running or completed
This should resolve the "Unsupported executor type: unknown" error and properly show the normalized conversation logs from the coding agent in the bottom section.
* Perfect! I've successfully updated the [`NormalizedConversationViewer.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/NormalizedConversationViewer.tsx) component with the requested style changes:
## Changes Made:
1. **Removed metadata header** - No longer shows executor type, session ID, or process type badges
2. **Removed Card wrapper** - The conversation entries are now displayed directly without any card container or special background
3. **Removed timestamps** - Each entry no longer shows the date/time information
4. **Cleaned up imports** - Removed unused imports for Card, CardContent, and Badge components
The normalized logs now display as a clean, minimal conversation with just the entry type icon, entry type label, and content for each message in the agent conversation.
* Perfect! I've made the requested style improvements to the diffs in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Removed max height limit** - Each file diff card no longer has a `max-h-[400px] overflow-y-auto` constraint, so the entire diff content is visible without individual scrolling per file
2. **Enhanced diff line colors** - Updated the background colors for added and removed lines:
- **Added lines** (Insert): Now use `bg-green-100` (light mode) and `bg-green-900/40` (dark mode) with stronger green text colors
- **Removed lines** (Delete): Now use `bg-red-100` (light mode) and `bg-red-900/40` (dark mode) with stronger red text colors
- Made the border colors more prominent with `border-green-500` and `border-red-500`
The diffs now display with clearer visual distinction between added and removed lines, and each file shows its complete content without requiring individual scrolling.
* Perfect! I've fixed the line number background colors in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added `getLineNumberClassName` function** - This creates appropriate styling for line numbers based on the chunk type:
- **Added lines**: Green background (`bg-green-50`/`bg-green-900/20`) with green text and borders
- **Removed lines**: Red background (`bg-red-50`/`bg-red-900/20`) with red text and borders
- **Unchanged lines**: Gray background (original styling)
2. **Updated line number div** - Replaced the hardcoded gray styling with a call to `getLineNumberClassName(line.chunkType)`
Now the line numbers will have matching background colors with their corresponding lines - green for additions, red for deletions, and gray for unchanged context lines. This creates a more cohesive visual experience for the diff display.
* Perfect! I've updated the line number background colors to match the main line content exactly:
## Changes Made:
- **Added lines**: Line numbers now use `bg-green-100 dark:bg-green-900/40` (same as the main content)
- **Removed lines**: Line numbers now use `bg-red-100 dark:bg-red-900/40` (same as the main content)
Now the line numbers have the same background intensity as the main line content, creating a consistent visual appearance across the entire diff line without the lighter/more transparent look.
* Perfect! I've made all the requested improvements to the diff styling in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added horizontal scrolling** - The diff container now has `overflow-x-auto` and each line has `min-w-max` to ensure long lines can be scrolled horizontally
2. **Removed thick border** - Removed the `border-l-2 border-green-500` and `border-l-2 border-red-500` thick left borders from added/removed lines
3. **GitHub-style background intensity** - Now follows GitHub's pattern:
- **Line content**: Lighter background (`bg-green-50`/`bg-red-50` for light mode, `bg-green-900/20`/`bg-red-900/20` for dark mode)
- **Line numbers**: Darker background (`bg-green-100`/`bg-red-100` for light mode, `bg-green-900/40`/`bg-red-900/40` for dark mode)
The diff now has a cleaner appearance without the heavy borders, proper horizontal scrolling for long lines, and follows GitHub's visual hierarchy where line numbers have more prominent background colors than the content.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Added `py-1` to the `getLineNumberClassName` function so the line number background now has the same vertical padding as the content lines, ensuring full height coverage
2. **Fixed horizontal scrolling layout** - Wrapped the diff content in a proper container structure:
- Added `min-w-full` wrapper inside the `overflow-x-auto` container
- This ensures that elements like horizontal lines and borders aren't cut off during horizontal scrolling
- The scrolling now works properly without affecting other page elements
The diff now has proper horizontal scrolling that doesn't interfere with other page elements, and the line number backgrounds extend to the full height of each line.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Both the line numbers and content now use `py-1` consistently, ensuring the backgrounds match in height perfectly
2. **Fixed horizontal scrolling** - Changed the approach:
- Used `whitespace-nowrap` instead of `whitespace-pre` to prevent wrapping
- Used `min-w-max` on each line to ensure they extend naturally
- The overflow scrolling now works at the container level without cutting off other elements
- Removed the problematic nested wrapper structure
The line number backgrounds now extend to the full height of each line, and horizontal scrolling works properly without affecting other page elements like borders and buttons.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height gaps**:
- Used `min-h-[1.75rem] flex items-center` for both line numbers and content divs
- This ensures both backgrounds have exactly the same height with no gaps above or below
- Removed separate `py-1` padding that was causing misalignment
2. **Fixed horizontal scroll border cutoff**:
- Added `min-w-max` to the "show X more lines" buttons
- This ensures the buttons extend to the full width of the scrollable content
- The borders now remain visible even when scrolling horizontally
Now the line number backgrounds perfectly match the line content height with no color gaps, and the expand/collapse buttons maintain their borders during horizontal scrolling.
* Perfect! I've fixed both remaining horizontal scroll issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed highlighted line backgrounds during scroll**:
- Changed from `min-w-max` to `w-full` for the base class
- Added `style={{ minWidth: 'max-content' }}` to ensure lines extend to natural content width
- Used `inline-block min-w-full` container to create proper scrollable width
- Now all highlighted lines (green/red) maintain their background color across the full scrollable width
2. **Fixed "show more lines" button borders**:
- Wrapped buttons in `div className="w-full"` containers
- Removed `min-w-max` from button classes and used `w-full`
- The buttons now properly extend across the full scrollable width
- Borders remain visible during horizontal scrolling
Now the diff view has proper horizontal scrolling where:
- All highlighted lines maintain their background colors across the entire scroll width
- The "show X more lines" buttons maintain their borders when scrolling
- The entire diff content scrolls cohesively
* Perfect! I've fixed the indentation issue in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Fixed indentation preservation**: Changed from `whitespace-nowrap` to `whitespace-pre` in the `getChunkClassName` function
Now the diff properly preserves:
- Code indentation (spaces and tabs)
- All whitespace formatting
- Line spacing as it appears in the original code
The horizontal scrolling functionality remains intact while properly displaying the code formatting and indentation structure.
* Perfect! I've right-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Added `justify-end`** to both "Show X more lines" and "Hide expanded lines" buttons
Now the button text ("Show X more lines" and "Hide expanded lines") is right-aligned, which means:
- The text stays visible and consistent during horizontal scrolling
- Users can always see the action text regardless of scroll position
- The interface feels more stable when navigating wide diffs
* Perfect! I've left-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Changed `justify-end` to `justify-start`** for both "Show X more lines" and "Hide expanded lines" buttons
Now the button text is left-aligned, which means:
- The text appears at the left edge of the button and stays visible during horizontal scrolling
- Users can always see the action text at a consistent position
- The interface aligns with the left-aligned line numbers below it
* ## Summary
I've successfully improved the normalized logs to show more concise content. Here's what was changed:
### Key Improvements
1. **File Operations**: Now show just the file path (e.g., `"src/components/TaskDialog.tsx"`) instead of verbose tool descriptions
2. **Commands**: Now show just the bash command (e.g., `"npm run build"`) instead of full tool invocation details
3. **Search Operations**: Now show just the search query (e.g., `"authentication headers"`) instead of tool metadata
4. **Web Requests**: Now show just the URL (e.g., `"https://api.example.com/data"`) instead of verbose descriptions
5. **Other Tools**: Now show concise, meaningful descriptions (e.g., `"Managing TODO list"` for todo tools)
### Changes Made
1. **Updated AMP Executor** (`backend/src/executors/amp.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
2. **Updated Claude Executor** (`backend/src/executors/claude.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
3. **Updated Documentation** (`test_normalized_logs_api.md`):
- Updated example response to show the new concise format
- Added detailed explanation of the new content format with examples
4. **Added Tests**:
- Enhanced existing tests to verify the new concise content format
- Tests confirm that `todo_write` shows "Managing TODO list" instead of verbose JSON
- Tests confirm that `Task` tool shows the task description directly
### Before and After
**Before:**
```json
{
"content": "Tool: todo_write with input: {\"todos\":[{\"id\":\"1\",\"content\":\"Explore task creation dialog component\",\"status\":\"todo\",\"priority\":\"high\"}]}"
}
```
**After:**
```json
{
"content": "Managing TODO list"
}
```
The implementation now provides much cleaner, more actionable content in the normalized conversation logs while maintaining all the rich metadata in the `action_type` field for programmatic access.
* Perfect! The frontend builds successfully. Now the normalized conversation logs will display much cleaner - just showing the appropriate icon next to the concise content without any redundant headers.
## Summary
I've removed the headers from the normalized conversation log display. Now instead of showing:
**Before:**
```
[file icon] Read: src/components/TaskDialog.tsx
src/components/TaskDialog.tsx
[command icon] Command: npm run build
npm run build
```
**After:**
```
[file icon] src/components/TaskDialog.tsx
[command icon] npm run build
[search icon] authentication headers
[todo icon] Managing TODO list
```
### Changes Made
1. **Removed redundant header display** - The `getEntryTypeDisplay` function and its usage has been removed
2. **Simplified layout** - Each log entry now shows just the icon and concise content directly
3. **Cleaner UI** - No more redundant information since the content is already concise and the icon provides the context
The display is now much cleaner and more efficient, with the icon providing the visual context for the action type and the concise content providing the essential information.
* Perfect! The frontend builds successfully.
## Summary
I've updated the NormalizedConversationViewer to render terminal commands with a monospace font. Now:
### Changes Made
1. **Added `getContentClassName` function** - This function conditionally applies styling based on the entry type
2. **Applied monospace font for commands** - When the entry type is a tool_use with action_type of "command_run", it applies the `font-mono` Tailwind class
3. **Maintained existing styling** - All other entry types keep the same styling as before
### Result
Now terminal commands like:
- `npm run build`
- `cargo check`
- `git status`
- `ls -la`
Will be displayed in a monospace font next to the terminal icon, making them more readable and clearly distinguishable as code/commands, while other content like file paths, search queries, and regular text continue to use the default font.
The monospace font makes commands much easier to read and gives them the appropriate technical appearance that users expect for terminal output.
* Excellent! Everything builds successfully.
## Summary
I've implemented real-time conversation updates with smart auto-scrolling functionality. Here's what was added:
### Key Features
1. **Auto-refresh**: The conversation automatically polls for updates every 2 seconds when the execution process is running
2. **Smart auto-scrolling**: Automatically scrolls to the bottom when new content arrives
3. **User scroll detection**: Stops auto-scrolling when the user manually scrolls up
4. **Auto-scroll resumption**: Resumes auto-scrolling when the user scrolls back to the bottom
5. **Efficient updates**: Only updates the conversation state when content actually changes
### Implementation Details
**Auto-refresh Logic:**
- Polls every 2 seconds when `executionProcess.status === 'running'`
- Uses efficient content comparison to avoid unnecessary re-renders
- Gracefully handles polling errors without disrupting the UI
**Smart Scrolling:**
- Tracks user scroll behavior vs automatic scrolling
- Uses a 5px threshold to detect "at bottom" state
- Provides smooth auto-scrolling with a small delay to ensure DOM updates
**User Experience:**
- Container has `max-h-96` with `overflow-y-auto` for scrollable content
- Terminal commands display in monospace font
- Clean display with just icons and concise content
- No disruption when user wants to review previous logs
### Behavior
- ✅ **Auto-updates**: Content refreshes every 2 seconds during execution
- ✅ **Auto-scroll**: New content automatically scrolls into view
- ✅ **Manual control**: User can scroll up to review previous logs
- ✅ **Resume auto-scroll**: When user scrolls back to bottom, auto-scrolling resumes
- ✅ **Efficient**: Only re-renders when content actually changes
- ✅ **Clean UI**: Terminal commands in monospace, no redundant headers
The conversation viewer now provides a seamless, real-time experience that follows the execution while respecting user control over scrolling behavior.
* ## Summary
I've successfully fixed the scroll-within-scroll issue in the TaskDetailsPanel. Here's what was changed:
**Problem**: The normalized logs section had nested scrolling containers:
- Parent container (TaskDetailsPanel) with `h-full overflow-y-auto`
- Child container (NormalizedConversationViewer) with `max-h-96 overflow-y-auto`
**Solution**: I removed the scrolling functionality from the `NormalizedConversationViewer` component by:
1. **Removed scroll container**: Changed from `<div ref={scrollContainerRef} className="space-y-2 max-h-96 overflow-y-auto" onScroll={handleScroll}>` to just `<div className="space-y-2">`
2. **Cleaned up scroll-related code**: Removed:
- State variables: `isUserScrolling`, `scrollContainerRef`, `scrollTimeoutRef`, `lastScrollTop`
- Functions: `isAtBottom`, `scrollToBottom`, `handleScroll`
- Effects: Auto-scroll on conversation updates and scroll timeout cleanup
- Unused imports: `useRef`
3. **Verified functionality**: The parent container in TaskDetailsPanel now handles all scrolling for the logs section, eliminating the annoying nested scroll behavior.
The build and lint checks pass successfully, and the user experience should now be much smoother without the competing scroll areas.
* ## Summary
I've successfully fixed the scroll-within-scroll issue while preserving the auto-scroll functionality. Here's what I implemented:
**Problem**: Nested scroll containers created annoying scroll-within-scroll behavior:
- Parent: TaskDetailsPanel with `h-full overflow-y-auto`
- Child: NormalizedConversationViewer with `max-h-96 overflow-y-auto`
**Solution**: Moved all scroll logic to the parent container while maintaining auto-scroll behavior:
### Changes Made:
1. **Removed inner scroll container** from NormalizedConversationViewer:
- Removed `max-h-96 overflow-y-auto` classes
- Removed all scroll-related state and functions
- Simplified to just `<div className="space-y-2">`
2. **Added callback interface** to NormalizedConversationViewer:
- Added `onConversationUpdate?: () => void` prop
- Called this callback when conversation data changes
- Used `setTimeout` to ensure state updates happen before auto-scroll
3. **Enhanced TaskDetailsPanel scroll logic**:
- Added `conversationUpdateTrigger` state to track conversation changes
- Added `handleConversationUpdate` callback function
- Extended existing auto-scroll effect to include conversation updates
- Maintained existing manual scroll detection logic
### Key Benefits:
✅ **No more nested scrolling** - Only the parent container scrolls
✅ **Auto-scroll preserved** - New messages still auto-scroll to bottom
✅ **Manual scroll respected** - Auto-scroll stops when user manually scrolls up
✅ **Smart resumption** - Auto-scroll resumes when user scrolls back to bottom
✅ **Seamless integration** - Works with existing activity and process log auto-scroll
The solution maintains all existing functionality while providing a much better user experience by eliminating the competing scroll areas.
* ## Summary
I've successfully updated the task attempts diff endpoint to include unstaged changes in addition to the existing committed changes. Here's what was implemented:
### Key Changes Made:
1. **Enhanced `get_diff` method** in [`backend/src/models/task_attempt.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-4fbf-update-dif/backend/src/models/task_attempt.rs#L1042-L1379):
- Added logic to capture unstaged changes using `diff_tree_to_workdir_with_index`
- For files with both committed and unstaged changes, creates a combined diff from the base branch to the current working directory
- For files with only unstaged changes, generates a standard diff
2. **New helper function** `process_unstaged_file`:
- Handles the processing of individual unstaged files
- Manages the complex logic of combining committed and unstaged changes
- Uses Git's native patch generation for accurate diff representation
### How It Works:
**For merged task attempts:** Shows the original merge commit diff (unchanged behavior)
**For active task attempts:**
1. **First pass:** Generates diff from base branch to current HEAD commit (committed changes)
2. **Second pass:** Finds files with unstaged changes in the working directory
3. **Combining logic:**
- **Files with both committed & unstaged changes:** Creates a combined diff from base branch directly to working directory content
- **Files with only unstaged changes:** Adds them as separate diff entries
- **Files with only committed changes:** Keeps original diff (unchanged)
### Benefits:
- ✅ **Complete visibility:** Users can now see both committed and uncommitted work
- ✅ **Unified view:** All changes are presented in a single coherent diff
- ✅ **Backward compatible:** Existing functionality for merged attempts remains unchanged
- ✅ **Accurate representation:** Uses Git's native diff algorithms for consistent formatting
The implementation maintains the existing API structure while extending functionality to provide a more comprehensive view of all changes in a task attempt.
* Perfect! I have successfully implemented the dynamic task details section that updates based on execution state. Here's a summary of what was accomplished:
## Summary
I implemented a comprehensive solution to make the details section dynamically update based on the execution state:
### Backend Changes:
1. **Added new execution state types** in `backend/src/models/task_attempt.rs`:
- `ExecutionState` enum with states: NotStarted, SetupRunning, SetupComplete, SetupFailed, CodingAgentRunning, CodingAgentComplete, CodingAgentFailed, Complete
- `TaskAttemptState` struct containing execution state, change detection, and process IDs
2. **Created `get_execution_state` method** that analyzes:
- Project setup script configuration
- Running execution processes
- Process status and completion
- Whether changes have been made to the worktree
3. **Added new API endpoint** `GET /projects/:project_id/tasks/:task_id/attempts/:attempt_id` to fetch execution state
4. **Updated type generation** to include the new types in the shared TypeScript definitions
### Frontend Changes:
1. **Enhanced useTaskDetails hook** to:
- Poll the execution state endpoint every 2 seconds
- Fetch execution state when switching attempts
- Provide execution state data to components
2. **Completely redesigned TaskDetailsPanel** with dynamic content rendering:
- **Setup Script Running**: Shows loading spinner with "Setup Script Running" message
- **Setup Failed**: Shows error message
- **Coding Agent Running (no changes)**: Shows full-height agent conversation only
- **Changes Available**: Shows 2/3 changes + 1/3 conversation split view
- **Default**: Shows "execution not started" message
### Key Features:
- **Real-time Updates**: Polls execution state every 2 seconds during execution
- **Progressive Disclosure**: Shows appropriate content based on execution phase
- **State-Aware UI**: Different layouts for different execution states
- **Error Handling**: Proper error states for failed setup scripts
- **Type Safety**: Full TypeScript support for execution states
The implementation ensures that users see relevant information at each stage of task execution, providing better visibility into what's happening and reducing confusion about the current state.
* Perfect! The fix is now in place. Here's what I've done to solve the loading spinner issue:
## Fix Summary
The problem was that the execution state was only being fetched during:
1. Polling (when `isAttemptRunning` is true)
2. Manual attempt switching via `handleAttemptChange`
But it was **not** being fetched during the initial page load when an attempt was automatically selected.
## Changes Made
1. **Added useEffect for selectedAttempt changes**: This ensures that whenever `selectedAttempt` changes (including during initial load), both `fetchAttemptData` and `fetchExecutionState` are called.
2. **Updated fetchTaskAttempts**: Added `fetchExecutionState` call when setting the latest attempt during initial load, and included it in the dependency array.
## How the Fix Works
Now when the page reloads:
1. `fetchTaskAttempts` runs and finds the latest attempt
2. It sets `selectedAttempt` to the latest attempt
3. The new useEffect detects the `selectedAttempt` change and immediately calls both:
- `fetchAttemptData(selectedAttempt.id)`
- `fetchExecutionState(selectedAttempt.id)`
4. The execution state is loaded, so the appropriate content is shown instead of the loading spinner
This ensures that the execution state is always available when displaying attempt details, whether it's the initial load, manual switching between attempts, or during polling updates.
* Perfect! The changes have been successfully implemented. Here's a summary of what I've done:
## Changes Made
### 1. Setup Script Stdio Display
- **Modified the setup running state** to show the actual stdout/stderr of the setup script instead of just a spinner
- **Enhanced process lookup** to find the setup script execution process from either the execution state's `setup_process_id` or by searching for processes with type `setupscript`
- **Added terminal-style output display** with:
- Black background and green text (terminal aesthetic)
- Monospace font for proper formatting
- Scrollable area with max height
- Fallback to "No output yet..." when no output is available
### 2. Diff Refresh During Coding Agent Execution
- **Added automatic diff polling** when the coding agent is running
- **Polls every 3 seconds** during `CodingAgentRunning` state to catch file changes as they happen
- **Uses existing fetchDiff function** to maintain consistency with manual refresh behavior
### 3. Enhanced Process Data Fetching
- **Modified fetchAttemptData** to always fetch setup script process details when available
- **Ensures setup script output is available** even when the script has completed but we want to show its output
- **Maintains existing logic** for other running processes while adding setup script support
## Key Features
1. **Real-time Setup Script Output**: Users can now see exactly what the setup script is doing, including any errors or progress messages.
2. **Live Diff Updates**: When the coding agent is making changes, the diff view automatically refreshes every 3 seconds so users can see files being modified in real-time.
3. **Better Visual Feedback**: The terminal-style output for setup scripts provides a familiar development experience.
4. **Non-blocking Updates**: The diff refresh happens independently and doesn't interfere with other UI interactions.
The implementation ensures that users get immediate feedback during both phases of execution - setup and coding - making the development process much more transparent and engaging.
* Perfect! Both issues have been fixed:
## Summary of Fixes
### 1. Setup Script Output Display
**Fixed**: Combined stdout and stderr properly
- **Before**: Only showed stdout OR stderr (whichever existed first)
- **After**: Shows both stdout AND stderr concatenated together
- **Implementation**:
```javascript
const stdout = setupProcess.stdout || '';
const stderr = setupProcess.stderr || '';
const combined = [stdout, stderr].filter(Boolean).join('\n');
return combined || 'No output yet...';
```
### 2. Automatic Diff Refresh During Coding Agent Execution
**Fixed**: Added proper diff polling with immediate refresh
- **Before**: Diff wasn't automatically updating during coding agent execution
- **After**: Diff refreshes immediately when coding agent starts + every 2 seconds while running
- **Implementation**:
- Immediate `fetchDiff()` call when entering `CodingAgentRunning` state
- `setInterval` to refresh every 2 seconds during execution
- Proper cleanup when state changes or component unmounts
### 3. Enhanced Setup Process Data Fetching
**Improved**: Better process data availability
- **Enhancement**: Modified `fetchAttemptData` to always fetch setup script process details
- **Benefit**: Ensures setup script output is available even after completion
- **Implementation**: Added dedicated setup script process fetching logic
## Key Improvements
1. **Real-time Setup Feedback**: Users can now see the complete output (both stdout and stderr) from setup scripts as they run
2. **Live Diff Updates**: File changes appear automatically every 2 seconds during coding agent execution
3. **Immediate Feedback**: Diff refreshes immediately when coding agent starts, then continues polling
4. **Better Error Visibility**: Setup script errors (stderr) are now visible alongside normal output
The implementation ensures that users get comprehensive, real-time feedback during both setup and coding phases, making the development process much more transparent and responsive.
* The styles have been cleaned up:
1. **Fixed the "No logs available" message**: Changed "No output yet..." to "Waiting for setup script output..." to indicate this is a loading state rather than an error.
2. **Simplified setup script display**: Removed the terminal UI styling including:
- The loading spinner and centered layout
- The bordered card wrapper
- The "Setup Script Output" header
- The black terminal background with green text
- Applied simple monospaced font styling similar to normalized logs
The setup script now displays with a cleaner, more consistent style that matches the rest of the interface while still being clearly readable with monospaced font.
* Added auto-scroll functionality to the setup script section:
1. **Added setup scroll ref**: Created `setupScrollRef` to track the setup script container
2. **Added auto-scroll effect**: Created a `useEffect` that monitors `attemptData.runningProcessDetails` and automatically scrolls to the bottom when the setup script output updates
3. **Applied ref to container**: Added the ref to the setup script container div
The setup script logs will now automatically scroll to the bottom as new output is received, matching the behavior of the normalized logs.
* Perfect! I've successfully implemented the follow up executor visualization in the TaskDetailsPanel. Here's what I did:
## Summary of Changes
1. **Identified follow up executor data structure**: Follow up executors are identified by having `process_type: "codingagent"` and `command: "followup_executor"` (vs main executor with `command: "executor"`)
2. **Modified TaskDetailsPanel logic**: Updated both the full-screen view (when no changes) and the split view (when changes are present) to:
- Find the main coding agent process (command: "executor")
- Find all follow up executor processes (command: "followup_executor")
- Display them in sequence with the main executor first, followed by follow up executors
3. **Enhanced UI presentation**: Each follow up executor is displayed with:
- A clear separator (border-top)
- A header showing "Follow-up #X" with the sequence number
- The start time of the follow up executor
- The normalized conversation viewer for that executor's logs
4. **Tested the implementation**: Both frontend and backend build successfully, and the code passes linting checks.
The implementation now properly handles and visualizes follow up executors by showing them under the main executor in chronological order, making it clear which conversation belongs to which executor while maintaining the existing functionality for the main coding agent.
* ## Issue Fixed!
I found and fixed the bug. The problem was in the TaskDetailsPanel logic that determines when to show the conversation logs vs. the fallback "Task execution not started yet" message.
### Root Cause
The condition `if (isCodingAgentRunning && !hasChanges)` only handled the case when the coding agent was *running*, but after follow up executors complete, the execution state becomes `CodingAgentComplete`. This caused the logic to fall through to the default case, showing "Task execution not started yet" instead of the conversation logs.
### Fix Applied
I modified the condition to also handle the completed state:
- Added `isCodingAgentComplete` variable to track when execution is complete
- Updated the condition to `if ((isCodingAgentRunning || isCodingAgentComplete) && !hasChanges)`
This ensures that both running and completed follow up executors will properly display their conversation logs, whether they're still running or have finished execution.
The fix is minimal and targeted, only affecting the specific scenario where follow up executors complete without generating file changes, while preserving all other functionality.
* ## Perfect! All styling changes implemented successfully!
Here's a summary of what I've accomplished:
### ✅ Backend Changes
1. **Extended NormalizedConversation structure** to include `prompt` and `summary` fields
2. **Updated all executor implementations** (Claude, Amp, default) to support the new fields
3. **Modified the normalized-logs API endpoint** to fetch executor session data and include prompt/summary in the response
4. **Regenerated TypeScript types** to reflect the new structure
### ✅ Frontend Changes
1. **Added prompt display** above the normalized logs with a distinctive blue styling and MessageSquare icon
2. **Implemented horizontal dividers** between all log sections:
- Between prompt and conversation logs
- Between main executor and follow-up executors
- Between multiple follow-up executors
3. **Removed the "Follow-up #1 Started:" headers** for a cleaner interface
4. **Updated the layout** to use proper spacing and consistent divider styling
### 🎨 UI Improvements
- **Prompts are displayed** in a blue-themed box with monospace font for better readability
- **Horizontal lines** consistently separate each logical section (prompt → executor → follow-up prompt → follow-up executor)
- **Clean, streamlined appearance** without verbose headers
- **Consistent spacing** and visual hierarchy throughout
The implementation properly handles cases where:
- Prompts may or may not be available
- There are multiple follow-up executors
- There are no follow-up executors
- The main executor completes with or without follow-ups
All builds pass and the code maintains the existing functionality while adding the requested visual improvements!
* ## ✅ Styling Updates Complete!
I've successfully implemented all the requested styling changes:
### 🔨 **Updated Prompt Display**
- **Changed icon** from MessageSquare to **Hammer** with orange color (`text-orange-600`)
- **Removed blue box styling** - no more background, border, or rounded corners
- **Removed monospaced font** - now uses normal text styling with `text-sm` and `text-foreground`
- **Maintains whitespace-pre-wrap** to preserve formatting without the monospace appearance
### 📏 **Improved Spacing**
- **Increased padding** at the bottom of the horizontal divider from `mb-4` to `mb-6`
- This creates **better visual separation** between the user message (prompt) and the agent logs
- **Maintains consistent spacing** throughout the component
### 🎨 **Clean Visual Hierarchy**
- User prompts now appear as **clean, readable text** with a hammer icon
- **Horizontal dividers** provide clear separation between sections
- **Proper padding** ensures comfortable reading experience
- **Consistent with the overall UI theme** rather than standing out with special styling
The changes maintain all existing functionality while providing the cleaner, more integrated visual appearance you requested. The prompt now blends naturally with the rest of the conversation while still being clearly distinguished by the hammer icon and horizontal divider!
* ## ✅ Styling Fixes Applied!
I've successfully implemented both requested changes:
### 🔨 **Blue Hammer Icon**
- Changed the hammer icon color from `text-orange-600` to `text-blue-600`
- Now matches the blue theme you requested
### 📏 **Fixed Spacing Gap**
- **Removed the bottom margin** from the horizontal divider (`mb-6` removed)
- **Added conditional top margin** to the conversation entries container (`mt-6` when prompt exists)
- This creates the **proper gap between the divider and the normalized logs**
### 🎨 **How the spacing now works:**
1. **User prompt** with hammer icon
2. **Horizontal divider** (no bottom margin)
3. **6-unit gap** (via `mt-6` on conversation container)
4. **Normalized logs/conversation entries**
The spacing is now properly balanced with a clean visual separation between the user message and the agent logs, while the blue hammer icon provides a cohesive color scheme throughout the interface!
* ## ✅ Spacing Fixed!
I've corrected the spacing issue by:
### 📏 **Fixed the Gap**
- **Added `mb-6` to the horizontal divider** - this creates the proper 6-unit gap below the divider
- **Removed the conditional margin logic** from the conversation entries container - it was causing the spacing to not work properly
- **Simplified the structure** for more predictable spacing behavior
### 🎯 **Now the spacing flow is:**
1. **User message** (with hammer icon)
2. **Gap** (via `mb-4` on user message container)
3. **Horizontal divider**
4. **Gap** (via `mb-6` on horizontal divider) ← **This was missing before!**
5. **Normalized logs/conversation entries**
The spacing should now be exactly what you requested - proper space both above and below the horizontal divider!
* ## ✅ Spacing Finally Fixed!
I've resolved the `space-y-0` override issue by **restructuring the layout**:
### 🔧 **What was wrong:**
- The `space-y-0` class creates CSS rule `.space-y-0 > :not([hidden]) ~ :not([hidden])` that sets `margin-top: 0` and `margin-bottom: 0` on all sibling elements
- This was overriding the `mb-6` class on the horizontal divider
### 🎯 **How I fixed it:**
1. **Removed `space-y-0` class** from the outer container
2. **Moved the horizontal divider outside** the prompt section to its own top-level element
3. **Made the structure flat** so no spacing utility classes conflict with each other
### 📏 **New structure:**
```jsx
<div> // No space-y-0 class
{/* User prompt */}
<div className="mb-4">...</div> // Space below prompt
{/* Horizontal divider */}
<div className="mb-6">...</div> // Space below divider - no override!
{/* Conversation entries */}
<div className="space-y-2">...</div> // Only affects entries
</div>
```
Now the `mb-6` on the horizontal divider won't be overridden, and you should see the proper gap between the divider and the normalized logs!
* ## ✅ Loading Message Fixed!
I've resolved the misleading "No logs available" message by improving the state logic:
### 🔧 **What was wrong:**
- When a follow-up execution starts, the API call completes successfully but returns no conversation data yet
- The component would show "No conversation data available" even though the process was still running and would eventually produce logs
### 🎯 **How I fixed it:**
- **Added execution process status check** - when `executionProcess.status === 'running'` and there's no conversation data
- **Shows "Waiting for logs..."** instead of "No conversation data available"
- **Maintains proper error state** for truly failed or empty completed processes
### 📱 **New behavior:**
- **When follow-up starts**: Shows "Waiting for logs..." ✅
- **During execution**: Shows "Waiting for logs..." ✅
- **When logs appear**: Shows the actual conversation ✅
- **When execution completes with no logs**: Shows "No conversation data available" ✅
- **On actual errors**: Shows the error message ✅
Now users will see a proper loading state that indicates the system is working, rather than an error-like message that suggests something went wrong!
* ## ✅ "No logs available" Message Fixed!
I've resolved the misleading error message by fixing the root cause in the backend:
### 🔧 **What was wrong:**
- When a follow-up execution starts, `process.stdout` is `None` because logs haven't been generated yet
- The backend was returning `success: false` with "No logs available for this execution process"
- The frontend was showing this as an error message
### 🎯 **How I fixed it:**
1. **Added status check in the backend** - when `process.status == ExecutionProcessStatus::Running` and `stdout` is `None`
2. **Return success with empty conversation** instead of an error
3. **Include executor session data** (prompt/summary) even for running processes without logs yet
4. **Maintain error state** only for truly failed or completed processes with no logs
### 📱 **New behavior:**
- **When follow-up starts**: Backend returns empty conversation with `success: true` ✅
- **Frontend shows**: "Waiting for logs..." (from previous fix) ✅
- **When logs appear**: Shows actual conversation data ✅
- **When process fails**: Still shows proper error messages ✅
- **When completed with no logs**: Shows "No conversation data available" ✅
Now when you start a follow-up, you should see "Waiting for logs..." instead of the misleading "No logs available for this execution process" error message!
* ## ✅ Spacing System Refactored!
I've completely refactored the spacing system for a much cleaner and more predictable layout:
### 🏗️ **New Systematic Approach**
#### **NormalizedConversationViewer (Internal Spacing):**
- **Prompt**: `mb-6` creates consistent gap to conversation entries
- **Conversation entries**: `space-y-2` between each entry
- **No internal dividers** - keeps the component focused on its own content
#### **TaskDetailsPanel (Executor Separation):**
- **Container**: `space-y-8` creates consistent spacing between main sections
- **Follow-up dividers**: `border-t` + `mb-8` for proper separation
- **Clean structure** with predictable spacing
### 📏 **Spacing Flow Now:**
1. **Main executor prompt**
2. **6-unit gap** (prompt `mb-6`)
3. **Main executor conversation**
4. **8-unit gap** (container `space-y-8`)
5. **Horizontal divider**
6. **8-unit gap** (divider `mb-8`)
7. **Follow-up prompt**
8. **6-unit gap** (prompt `mb-6`)
9. **Follow-up conversation**
10. **8-unit gap** (for next follow-up if any)
### 🎯 **Benefits:**
- **Consistent spacing** throughout all states
- **No more conflicting CSS rules** or override issues
- **Systematic approach** that's easy to maintain and debug
- **Proper separation** between logical sections
- **Clean, readable layout** with predictable gaps
The spacing should now be perfect in all scenarios - with or without prompts, with single or multiple follow-ups, and in both full-screen and split-view modes!
* I've successfully improved the diff styling to be more compact and fit better within the sidebar. Here are the key changes made:
**Spacing Improvements:**
- Reduced spacing between diff files from `space-y-6` to `space-y-3`
- Reduced main container padding from `p-6` to `p-4`
- Reduced file header padding from `py-2` to `py-1.5`
**Line Compactness:**
- Reduced diff line height from `min-h-[1.75rem]` to `min-h-[1.25rem]`
- Reduced line number column width from `w-16` to `w-12`
- Reduced line number padding from `px-2` to `px-1.5`
- Reduced individual line number widths from `w-6` to `w-5`
- Reduced line content padding from `px-3` to `px-2`
**Expand/Collapse Buttons:**
- Reduced expand button height from `h-8` to `h-6`
These changes make the diff view significantly more compact while maintaining readability. The diff will now display more content in the same space, making better use of the sidebar real estate. The frontend builds successfully with these changes.
* Perfect! The migration is complete. Let me summarize what I've done:
I've successfully migrated the merge, rebase, and PR functionality from the old `task-attempt-compare.tsx` page to the `TaskDetailsToolbar.tsx` component and deleted the old page.
1. **Added to TaskDetailsToolbar.tsx:**
- Branch status fetching with `fetchBranchStatus` function
- Merge functionality with uncommitted changes warning
- Rebase functionality
- Create PR functionality with dialog
- Branch status display showing commits ahead/behind and uncommitted changes
- Git operation buttons (Merge, Rebase, Create PR) with proper enabling/disabling logic
- Two dialog components for warnings and PR creation
2. **Deleted:**
- `frontend/src/pages/task-attempt-compare.tsx` - The entire compare page
3. **Updated routing:**
- Removed the compare page route from `App.tsx`
- Removed the import for `TaskAttemptComparePage`
- Updated keyboard shortcuts to remove compare page navigation logic
- Removed the "Changes" button that linked to the old compare page
4. **Cleaned up imports:**
- Removed unused imports like `Link` from react-router-dom and `GitCompare` icon
The diff view functionality remains in the `TaskDetailsPanel.tsx` where it was already migrated, and now users can access merge, rebase, and PR creation directly from the toolbar without needing a separate page. The branch status is also prominently displayed in the toolbar so users can see the current state at a glance.
* Perfect! Now the merge, rebase, and PR buttons will be disabled when an attempt is running, preventing users from performing git operations while code is being executed.
* Cargo fmt
* Clippy
* Prettier
* Cleanup
* Clippy
2025-07-03 22:00:44 +01:00
/// Get the current execution state for a task attempt
pub async fn get_execution_state (
pool : & SqlitePool ,
attempt_id : Uuid ,
task_id : Uuid ,
project_id : Uuid ,
) -> Result < TaskAttemptState , TaskAttemptError > {
// Get the task attempt with validation
let _attempt = sqlx ::query_as! (
TaskAttempt ,
Add base branch (vibe-kanban) (#100)
* Excellent! Everything compiles successfully.
## Summary
I have successfully implemented the base branch feature as requested. Here's what was accomplished:
### ✅ Completed Tasks
1. **Database Migration**: Created `20250708000000_add_base_branch_to_task_attempts.sql` that adds a `base_branch` column with NOT NULL and default value 'main'
2. **Backend Model Updates**:
- Added `base_branch: String` field to the `TaskAttempt` struct
- Updated all SQL queries to include the new column
- Modified task attempt creation logic to determine and store the resolved base branch (using provided base_branch or current HEAD branch name with fallback to "main")
3. **Improved Branch Status Logic**:
- Replaced the complex branch discovery algorithm with simple use of the stored `base_branch`
- This gives accurate and consistent results since the base branch is now explicitly known
4. **Updated Operations**:
- **PR Creation**: Now uses stored base branch as fallback when no explicit base branch is provided
- **Rebase**: Now uses stored base branch as fallback when no new base branch is specified
- **Merge**: Already used task context, no changes needed
5. **TypeScript Types**: Regenerated to include the new `base_branch: string` field
### 🔧 Key Implementation Details
- **Base Branch Resolution**: When creating a task attempt, the system now:
1. Uses the explicitly provided `base_branch` if available
2. Falls back to the current HEAD branch name of the repository
3. Defaults to "main" if the repository has no commits or other issues
- **Backward Compatibility**: The migration ensures existing records get a sensible default ("main")
- **API Consistency**: All endpoints that previously calculated base branches now use the stored value, providing consistent behavior
### 🎯 Benefits
- **Accuracy**: No more guessing which branch is the "base" - it's explicitly stored when the task attempt is created
- **Performance**: Eliminates the need to traverse git history to find the "closest" branch
- **Consistency**: Operations like merge, rebase, and PR creation now all reference the same base branch
- **User Control**: Users can specify the base branch when creating task attempts
- **Reliability**: Reduces git-related errors from branch discovery algorithms
The implementation maintains full backward compatibility while providing the accuracy and consistency requested in the task description.
* Perfect! Now the PR creation logic follows the correct precedence:
1. **User's explicit choice**: If the user specifies a `base_branch` in the PR creation request, that takes priority
2. **Stored base branch**: Uses the `base_branch` stored in the task attempt as the default
3. **Config/fallback**: Only falls back to the GitHub config default or "main" if the stored base branch is somehow empty/invalid
This ensures that:
- ✅ The PR defaults to the same base branch that was used when creating the task attempt
- ✅ Users can still override this by explicitly specifying a different base branch when creating the PR
- ✅ There's still a sensible fallback chain in case of edge cases
The behavior now matches the requirement: "The saved base branch should be saved by default, but if the user wishes to change it when creating a PR they can."
* Great! The lint passes with only pre-existing warnings (none related to my changes).
## Summary
I have successfully updated the "Create GitHub Pull Request" dialog to:
### ✅ Changes Made
1. **Replaced text input with dropdown**: The base branch field is now a `Select` component instead of a text `Input`
2. **Default to task attempt's base branch**: The dropdown now defaults to the `selectedAttempt.base_branch` instead of hardcoded "main"
3. **Populated with available branches**: The dropdown shows all local branches from the `branches` prop, with:
- Current branch marked with "(current)"
- Common fallback branches ("main", "master") if not in the list
- Only local branches (filtered out remote branches)
4. **Proper state management**:
- Initial state uses the task attempt's base branch
- Updates when selected attempt changes via `useEffect`
- Resets to task attempt's base branch when form is cancelled or submitted
5. **User can still override**: Users can select any available branch from the dropdown, providing the flexibility to change the base branch when creating PRs
### 🎯 User Experience
- **Default behavior**: When opening the PR dialog, the base branch dropdown shows the task attempt's stored base branch as selected
- **Override capability**: Users can click the dropdown to select a different base branch if needed
- **Visual clarity**: The dropdown shows "(current)" next to the current working branch and lists all available local branches
- **Fallback safety**: Even if the stored base branch isn't in the list, common branches like "main" and "master" are available as options
The implementation maintains the principle that the stored base branch is the sensible default while giving users full control to override when necessary.
* prettier
* cargo fmt
2025-07-08 15:13:00 +01:00
r #" SELECT ta.id as " id ! : Uuid " , ta.task_id as " task_id ! : Uuid " , ta.worktree_path, ta.branch, ta.base_branch, ta.merge_commit, ta.executor, ta.pr_url, ta.pr_number, ta.pr_status, ta.pr_merged_at as " pr_merged_at : DateTime < Utc > " , ta.created_at as " created_at ! : DateTime < Utc > " , ta.updated_at as " updated_at ! : DateTime < Utc > "
Redesign sidebar (#63)
* Agent logs
* ## Summary
I have successfully implemented a comprehensive log normalization system for vibe-kanban with the following features:
### ✅ **Completed Tasks:**
1. **Defined normalized log format types** - Created standard TypeScript-exportable types:
- `NormalizedConversation` - Top-level container with entries, session ID, and executor type
- `NormalizedEntry` - Individual conversation entries with timestamp, type, and content
- `NormalizedEntryType` - Union type for different entry types (user, assistant, tool use, system, thinking)
- `ActionType` - Specific action types for tool operations (file read/write, commands, search, etc.)
2. **Implemented normalize method for AMP executor** - Parses AMP's JSONL format to extract:
- User and assistant messages
- Thinking content
- Tool usage with intelligent action type detection
- Session ID (threadID)
- Timestamps from message metadata
3. **Implemented normalize method for Claude executor** - Parses Claude's stream-json format to extract:
- User and assistant messages
- Tool usage with action type detection
- System initialization messages
- Session ID
- Comprehensive tool mapping for Claude-specific tools
4. **Added normalize method to executor trait** - Created a default implementation that returns empty results for unsupported executors, allowing easy extension to other executor types
5. **Created new API endpoint for normalized logs** - Added `GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint that:
- Validates execution process belongs to the correct project
- Determines executor type automatically
- Applies appropriate normalization logic
- Returns standardized conversation format
6. **Tested the implementation** - Created comprehensive unit tests for both AMP and Claude normalization, verifying correct parsing of real log examples
### 🔧 **Technical Implementation Details:**
- **Intelligent Action Type Detection**: The system maps tool names to specific action types (file operations, commands, searches, etc.) based on tool names and input parameters
- **Cross-platform Compatibility**: Handles different parameter naming conventions between executors
- **Error Handling**: Robust error handling with detailed error messages for debugging
- **TypeScript Integration**: Full TypeScript type generation for frontend consumption
- **Extensible Design**: Easy to add new executors by implementing the `normalize_logs` method
### 🚀 **Usage:**
The new endpoint can be called to get a normalized view of any execution process logs:
```bash
GET /api/projects/{project_id}/execution-processes/{process_id}/normalized-logs
```
This returns a standardized conversation format that the frontend can easily consume to display:
- User inputs and assistant responses
- Tool usage with specific action details (file paths, commands, etc.)
- Internal reasoning (thinking)
- System status messages
- Session tracking information
The normalized format provides a high-level, consistent representation of the conversation regardless of which executor (AMP, Claude, etc.) generated the original logs.
* Perfect! I have successfully implemented the normalized logs feature. Here's a summary of what was accomplished:
## Summary
✅ **Replaced manual log rendering with normalized logs API**
### Key Changes Made:
1. **Created a new `NormalizedConversationViewer` component** that:
- Fetches normalized logs from the backend API endpoint
- Displays conversation entries with appropriate icons for each type:
- 👤 User messages (blue)
- 🤖 Assistant messages (green)
- ⚙️ System messages (gray)
- 🧠 Thinking (purple)
- 👁️ File reads (orange)
- ✏️ File writes (red)
- 💻 Command execution (yellow)
- 🔍 Search operations (indigo)
- 🌐 Web fetches (cyan)
- ➕ Task creation (teal)
- Shows timestamps and entry type labels
- Handles loading and error states
2. **Updated `TaskActivityHistory` component** to:
- Use the new `NormalizedConversationViewer` instead of `ExecutionOutputViewer`
- Pass the required `projectId` prop
- Maintain the same expansion/collapse functionality
3. **Updated `TaskDetailsPanel`** to pass the `projectId` to the activity history
4. **Removed legacy components**:
- The complex `ExecutionOutputViewer` that manually parsed JSONL
- The `ConversationViewer` that handled format-specific parsing
- All the manual stdout/stderr rendering logic
### Benefits:
- **Cleaner codebase**: Eliminated complex frontend parsing logic
- **Consistent display**: All executors now show logs in the same format
- **Better UX**: Clear icons and labels make it easy to understand conversation flow
- **Maintainable**: Backend handles format normalization, frontend just displays
- **Extensible**: New executor types automatically work with the existing UI
The implementation leverages the existing `/api/projects/{project_id}/execution-processes/{process_id}/normalized-logs` endpoint and properly handles loading states, errors, and displays the conversation history with meaningful icons for each entry type.
* Excellent! The build is successful and the bundle size is slightly smaller after removing the unused code. The task is now complete:
✅ **Legacy log rendering components successfully removed**
- Deleted `ExecutionOutputViewer.tsx` (224 lines)
- Deleted `ConversationViewer.tsx` (complex JSONL parsing logic)
- Build passes and bundle size reduced
- All functionality now uses the clean normalized logs API
The codebase is now cleaner and fully transitioned to using the normalized logs system.
* Perfect! I've successfully refactored the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) component with the new layout:
## Key Changes Made:
1. **Top 2/3 - Code Changes Section**:
- Extracted all the diff processing logic from [`task-attempt-compare.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/pages/task-attempt-compare.tsx)
- Shows file diffs with syntax highlighting, expandable sections, and line numbers
- Includes file deletion functionality
- "Show/Hide All Unchanged" toggle for better navigation
2. **Bottom 1/3 - Agent Logs Section**:
- Preserved the existing [`TaskActivityHistory`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskActivityHistory.tsx) component
- Auto-scrolling behavior for new log entries
- Clear separation with border and background styling
3. **Maintained Existing Functionality**:
- Header, toolbar, and follow-up sections remain unchanged
- Editor selection dialog and ESC key handling preserved
- All existing hooks and state management intact
The component now provides a much better developer experience by showing code changes prominently in the top section while keeping the agent logs easily accessible in the bottom third.
* Perfect! The build is successful. I've implemented all the requested style changes to the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
**Code Changes Section (Top 2/3):**
- ✅ Removed "Code Changes" header
- ✅ Removed description text
- ✅ Removed "Show All Unchanged" button and related functionality
- ✅ Removed parent Card wrapper - each file diff is now directly displayed in its own card
**Agent Logs Section (Bottom 1/3):**
- ✅ Removed "Agent Logs" header
- ✅ Removed description text
- ✅ Now displays only the `NormalizedConversationViewer` component
- ✅ Removed card/container wrapper
The component now has a cleaner, more streamlined interface with the code changes taking up most of the space in the top section, and the agent conversation displayed directly in the bottom third without extra headers or containers.
* Perfect! The build is successful. I've updated the [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx) to properly find and display the coding agent conversation.
## Changes Made:
The component now:
1. **First looks** for any running coding agent process in `runningProcessDetails`
2. **If not found**, searches through all processes in the `processes` array to find any coding agent process (completed or otherwise)
3. **Creates a minimal ExecutionProcess object** from the summary data if needed, since the `NormalizedConversationViewer` only requires the process ID to fetch the conversation data
4. **Displays the conversation** for any coding agent process found, whether running or completed
This should resolve the "Unsupported executor type: unknown" error and properly show the normalized conversation logs from the coding agent in the bottom section.
* Perfect! I've successfully updated the [`NormalizedConversationViewer.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/NormalizedConversationViewer.tsx) component with the requested style changes:
## Changes Made:
1. **Removed metadata header** - No longer shows executor type, session ID, or process type badges
2. **Removed Card wrapper** - The conversation entries are now displayed directly without any card container or special background
3. **Removed timestamps** - Each entry no longer shows the date/time information
4. **Cleaned up imports** - Removed unused imports for Card, CardContent, and Badge components
The normalized logs now display as a clean, minimal conversation with just the entry type icon, entry type label, and content for each message in the agent conversation.
* Perfect! I've made the requested style improvements to the diffs in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Removed max height limit** - Each file diff card no longer has a `max-h-[400px] overflow-y-auto` constraint, so the entire diff content is visible without individual scrolling per file
2. **Enhanced diff line colors** - Updated the background colors for added and removed lines:
- **Added lines** (Insert): Now use `bg-green-100` (light mode) and `bg-green-900/40` (dark mode) with stronger green text colors
- **Removed lines** (Delete): Now use `bg-red-100` (light mode) and `bg-red-900/40` (dark mode) with stronger red text colors
- Made the border colors more prominent with `border-green-500` and `border-red-500`
The diffs now display with clearer visual distinction between added and removed lines, and each file shows its complete content without requiring individual scrolling.
* Perfect! I've fixed the line number background colors in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added `getLineNumberClassName` function** - This creates appropriate styling for line numbers based on the chunk type:
- **Added lines**: Green background (`bg-green-50`/`bg-green-900/20`) with green text and borders
- **Removed lines**: Red background (`bg-red-50`/`bg-red-900/20`) with red text and borders
- **Unchanged lines**: Gray background (original styling)
2. **Updated line number div** - Replaced the hardcoded gray styling with a call to `getLineNumberClassName(line.chunkType)`
Now the line numbers will have matching background colors with their corresponding lines - green for additions, red for deletions, and gray for unchanged context lines. This creates a more cohesive visual experience for the diff display.
* Perfect! I've updated the line number background colors to match the main line content exactly:
## Changes Made:
- **Added lines**: Line numbers now use `bg-green-100 dark:bg-green-900/40` (same as the main content)
- **Removed lines**: Line numbers now use `bg-red-100 dark:bg-red-900/40` (same as the main content)
Now the line numbers have the same background intensity as the main line content, creating a consistent visual appearance across the entire diff line without the lighter/more transparent look.
* Perfect! I've made all the requested improvements to the diff styling in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Added horizontal scrolling** - The diff container now has `overflow-x-auto` and each line has `min-w-max` to ensure long lines can be scrolled horizontally
2. **Removed thick border** - Removed the `border-l-2 border-green-500` and `border-l-2 border-red-500` thick left borders from added/removed lines
3. **GitHub-style background intensity** - Now follows GitHub's pattern:
- **Line content**: Lighter background (`bg-green-50`/`bg-red-50` for light mode, `bg-green-900/20`/`bg-red-900/20` for dark mode)
- **Line numbers**: Darker background (`bg-green-100`/`bg-red-100` for light mode, `bg-green-900/40`/`bg-red-900/40` for dark mode)
The diff now has a cleaner appearance without the heavy borders, proper horizontal scrolling for long lines, and follows GitHub's visual hierarchy where line numbers have more prominent background colors than the content.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Added `py-1` to the `getLineNumberClassName` function so the line number background now has the same vertical padding as the content lines, ensuring full height coverage
2. **Fixed horizontal scrolling layout** - Wrapped the diff content in a proper container structure:
- Added `min-w-full` wrapper inside the `overflow-x-auto` container
- This ensures that elements like horizontal lines and borders aren't cut off during horizontal scrolling
- The scrolling now works properly without affecting other page elements
The diff now has proper horizontal scrolling that doesn't interfere with other page elements, and the line number backgrounds extend to the full height of each line.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height** - Both the line numbers and content now use `py-1` consistently, ensuring the backgrounds match in height perfectly
2. **Fixed horizontal scrolling** - Changed the approach:
- Used `whitespace-nowrap` instead of `whitespace-pre` to prevent wrapping
- Used `min-w-max` on each line to ensure they extend naturally
- The overflow scrolling now works at the container level without cutting off other elements
- Removed the problematic nested wrapper structure
The line number backgrounds now extend to the full height of each line, and horizontal scrolling works properly without affecting other page elements like borders and buttons.
* Perfect! I've fixed both issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed line number background height gaps**:
- Used `min-h-[1.75rem] flex items-center` for both line numbers and content divs
- This ensures both backgrounds have exactly the same height with no gaps above or below
- Removed separate `py-1` padding that was causing misalignment
2. **Fixed horizontal scroll border cutoff**:
- Added `min-w-max` to the "show X more lines" buttons
- This ensures the buttons extend to the full width of the scrollable content
- The borders now remain visible even when scrolling horizontally
Now the line number backgrounds perfectly match the line content height with no color gaps, and the expand/collapse buttons maintain their borders during horizontal scrolling.
* Perfect! I've fixed both remaining horizontal scroll issues in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Changes Made:
1. **Fixed highlighted line backgrounds during scroll**:
- Changed from `min-w-max` to `w-full` for the base class
- Added `style={{ minWidth: 'max-content' }}` to ensure lines extend to natural content width
- Used `inline-block min-w-full` container to create proper scrollable width
- Now all highlighted lines (green/red) maintain their background color across the full scrollable width
2. **Fixed "show more lines" button borders**:
- Wrapped buttons in `div className="w-full"` containers
- Removed `min-w-max` from button classes and used `w-full`
- The buttons now properly extend across the full scrollable width
- Borders remain visible during horizontal scrolling
Now the diff view has proper horizontal scrolling where:
- All highlighted lines maintain their background colors across the entire scroll width
- The "show X more lines" buttons maintain their borders when scrolling
- The entire diff content scrolls cohesively
* Perfect! I've fixed the indentation issue in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Fixed indentation preservation**: Changed from `whitespace-nowrap` to `whitespace-pre` in the `getChunkClassName` function
Now the diff properly preserves:
- Code indentation (spaces and tabs)
- All whitespace formatting
- Line spacing as it appears in the original code
The horizontal scrolling functionality remains intact while properly displaying the code formatting and indentation structure.
* Perfect! I've right-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Added `justify-end`** to both "Show X more lines" and "Hide expanded lines" buttons
Now the button text ("Show X more lines" and "Hide expanded lines") is right-aligned, which means:
- The text stays visible and consistent during horizontal scrolling
- Users can always see the action text regardless of scroll position
- The interface feels more stable when navigating wide diffs
* Perfect! I've left-aligned the text in the expand/collapse buttons in [`TaskDetailsPanel.tsx`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7218-refactor-t/frontend/src/components/tasks/TaskDetailsPanel.tsx):
## Change Made:
- **Changed `justify-end` to `justify-start`** for both "Show X more lines" and "Hide expanded lines" buttons
Now the button text is left-aligned, which means:
- The text appears at the left edge of the button and stays visible during horizontal scrolling
- Users can always see the action text at a consistent position
- The interface aligns with the left-aligned line numbers below it
* ## Summary
I've successfully improved the normalized logs to show more concise content. Here's what was changed:
### Key Improvements
1. **File Operations**: Now show just the file path (e.g., `"src/components/TaskDialog.tsx"`) instead of verbose tool descriptions
2. **Commands**: Now show just the bash command (e.g., `"npm run build"`) instead of full tool invocation details
3. **Search Operations**: Now show just the search query (e.g., `"authentication headers"`) instead of tool metadata
4. **Web Requests**: Now show just the URL (e.g., `"https://api.example.com/data"`) instead of verbose descriptions
5. **Other Tools**: Now show concise, meaningful descriptions (e.g., `"Managing TODO list"` for todo tools)
### Changes Made
1. **Updated AMP Executor** (`backend/src/executors/amp.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
2. **Updated Claude Executor** (`backend/src/executors/claude.rs`):
- Added `generate_concise_content()` method
- Modified tool_use content generation to use the new concise format
3. **Updated Documentation** (`test_normalized_logs_api.md`):
- Updated example response to show the new concise format
- Added detailed explanation of the new content format with examples
4. **Added Tests**:
- Enhanced existing tests to verify the new concise content format
- Tests confirm that `todo_write` shows "Managing TODO list" instead of verbose JSON
- Tests confirm that `Task` tool shows the task description directly
### Before and After
**Before:**
```json
{
"content": "Tool: todo_write with input: {\"todos\":[{\"id\":\"1\",\"content\":\"Explore task creation dialog component\",\"status\":\"todo\",\"priority\":\"high\"}]}"
}
```
**After:**
```json
{
"content": "Managing TODO list"
}
```
The implementation now provides much cleaner, more actionable content in the normalized conversation logs while maintaining all the rich metadata in the `action_type` field for programmatic access.
* Perfect! The frontend builds successfully. Now the normalized conversation logs will display much cleaner - just showing the appropriate icon next to the concise content without any redundant headers.
## Summary
I've removed the headers from the normalized conversation log display. Now instead of showing:
**Before:**
```
[file icon] Read: src/components/TaskDialog.tsx
src/components/TaskDialog.tsx
[command icon] Command: npm run build
npm run build
```
**After:**
```
[file icon] src/components/TaskDialog.tsx
[command icon] npm run build
[search icon] authentication headers
[todo icon] Managing TODO list
```
### Changes Made
1. **Removed redundant header display** - The `getEntryTypeDisplay` function and its usage has been removed
2. **Simplified layout** - Each log entry now shows just the icon and concise content directly
3. **Cleaner UI** - No more redundant information since the content is already concise and the icon provides the context
The display is now much cleaner and more efficient, with the icon providing the visual context for the action type and the concise content providing the essential information.
* Perfect! The frontend builds successfully.
## Summary
I've updated the NormalizedConversationViewer to render terminal commands with a monospace font. Now:
### Changes Made
1. **Added `getContentClassName` function** - This function conditionally applies styling based on the entry type
2. **Applied monospace font for commands** - When the entry type is a tool_use with action_type of "command_run", it applies the `font-mono` Tailwind class
3. **Maintained existing styling** - All other entry types keep the same styling as before
### Result
Now terminal commands like:
- `npm run build`
- `cargo check`
- `git status`
- `ls -la`
Will be displayed in a monospace font next to the terminal icon, making them more readable and clearly distinguishable as code/commands, while other content like file paths, search queries, and regular text continue to use the default font.
The monospace font makes commands much easier to read and gives them the appropriate technical appearance that users expect for terminal output.
* Excellent! Everything builds successfully.
## Summary
I've implemented real-time conversation updates with smart auto-scrolling functionality. Here's what was added:
### Key Features
1. **Auto-refresh**: The conversation automatically polls for updates every 2 seconds when the execution process is running
2. **Smart auto-scrolling**: Automatically scrolls to the bottom when new content arrives
3. **User scroll detection**: Stops auto-scrolling when the user manually scrolls up
4. **Auto-scroll resumption**: Resumes auto-scrolling when the user scrolls back to the bottom
5. **Efficient updates**: Only updates the conversation state when content actually changes
### Implementation Details
**Auto-refresh Logic:**
- Polls every 2 seconds when `executionProcess.status === 'running'`
- Uses efficient content comparison to avoid unnecessary re-renders
- Gracefully handles polling errors without disrupting the UI
**Smart Scrolling:**
- Tracks user scroll behavior vs automatic scrolling
- Uses a 5px threshold to detect "at bottom" state
- Provides smooth auto-scrolling with a small delay to ensure DOM updates
**User Experience:**
- Container has `max-h-96` with `overflow-y-auto` for scrollable content
- Terminal commands display in monospace font
- Clean display with just icons and concise content
- No disruption when user wants to review previous logs
### Behavior
- ✅ **Auto-updates**: Content refreshes every 2 seconds during execution
- ✅ **Auto-scroll**: New content automatically scrolls into view
- ✅ **Manual control**: User can scroll up to review previous logs
- ✅ **Resume auto-scroll**: When user scrolls back to bottom, auto-scrolling resumes
- ✅ **Efficient**: Only re-renders when content actually changes
- ✅ **Clean UI**: Terminal commands in monospace, no redundant headers
The conversation viewer now provides a seamless, real-time experience that follows the execution while respecting user control over scrolling behavior.
* ## Summary
I've successfully fixed the scroll-within-scroll issue in the TaskDetailsPanel. Here's what was changed:
**Problem**: The normalized logs section had nested scrolling containers:
- Parent container (TaskDetailsPanel) with `h-full overflow-y-auto`
- Child container (NormalizedConversationViewer) with `max-h-96 overflow-y-auto`
**Solution**: I removed the scrolling functionality from the `NormalizedConversationViewer` component by:
1. **Removed scroll container**: Changed from `<div ref={scrollContainerRef} className="space-y-2 max-h-96 overflow-y-auto" onScroll={handleScroll}>` to just `<div className="space-y-2">`
2. **Cleaned up scroll-related code**: Removed:
- State variables: `isUserScrolling`, `scrollContainerRef`, `scrollTimeoutRef`, `lastScrollTop`
- Functions: `isAtBottom`, `scrollToBottom`, `handleScroll`
- Effects: Auto-scroll on conversation updates and scroll timeout cleanup
- Unused imports: `useRef`
3. **Verified functionality**: The parent container in TaskDetailsPanel now handles all scrolling for the logs section, eliminating the annoying nested scroll behavior.
The build and lint checks pass successfully, and the user experience should now be much smoother without the competing scroll areas.
* ## Summary
I've successfully fixed the scroll-within-scroll issue while preserving the auto-scroll functionality. Here's what I implemented:
**Problem**: Nested scroll containers created annoying scroll-within-scroll behavior:
- Parent: TaskDetailsPanel with `h-full overflow-y-auto`
- Child: NormalizedConversationViewer with `max-h-96 overflow-y-auto`
**Solution**: Moved all scroll logic to the parent container while maintaining auto-scroll behavior:
### Changes Made:
1. **Removed inner scroll container** from NormalizedConversationViewer:
- Removed `max-h-96 overflow-y-auto` classes
- Removed all scroll-related state and functions
- Simplified to just `<div className="space-y-2">`
2. **Added callback interface** to NormalizedConversationViewer:
- Added `onConversationUpdate?: () => void` prop
- Called this callback when conversation data changes
- Used `setTimeout` to ensure state updates happen before auto-scroll
3. **Enhanced TaskDetailsPanel scroll logic**:
- Added `conversationUpdateTrigger` state to track conversation changes
- Added `handleConversationUpdate` callback function
- Extended existing auto-scroll effect to include conversation updates
- Maintained existing manual scroll detection logic
### Key Benefits:
✅ **No more nested scrolling** - Only the parent container scrolls
✅ **Auto-scroll preserved** - New messages still auto-scroll to bottom
✅ **Manual scroll respected** - Auto-scroll stops when user manually scrolls up
✅ **Smart resumption** - Auto-scroll resumes when user scrolls back to bottom
✅ **Seamless integration** - Works with existing activity and process log auto-scroll
The solution maintains all existing functionality while providing a much better user experience by eliminating the competing scroll areas.
* ## Summary
I've successfully updated the task attempts diff endpoint to include unstaged changes in addition to the existing committed changes. Here's what was implemented:
### Key Changes Made:
1. **Enhanced `get_diff` method** in [`backend/src/models/task_attempt.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-4fbf-update-dif/backend/src/models/task_attempt.rs#L1042-L1379):
- Added logic to capture unstaged changes using `diff_tree_to_workdir_with_index`
- For files with both committed and unstaged changes, creates a combined diff from the base branch to the current working directory
- For files with only unstaged changes, generates a standard diff
2. **New helper function** `process_unstaged_file`:
- Handles the processing of individual unstaged files
- Manages the complex logic of combining committed and unstaged changes
- Uses Git's native patch generation for accurate diff representation
### How It Works:
**For merged task attempts:** Shows the original merge commit diff (unchanged behavior)
**For active task attempts:**
1. **First pass:** Generates diff from base branch to current HEAD commit (committed changes)
2. **Second pass:** Finds files with unstaged changes in the working directory
3. **Combining logic:**
- **Files with both committed & unstaged changes:** Creates a combined diff from base branch directly to working directory content
- **Files with only unstaged changes:** Adds them as separate diff entries
- **Files with only committed changes:** Keeps original diff (unchanged)
### Benefits:
- ✅ **Complete visibility:** Users can now see both committed and uncommitted work
- ✅ **Unified view:** All changes are presented in a single coherent diff
- ✅ **Backward compatible:** Existing functionality for merged attempts remains unchanged
- ✅ **Accurate representation:** Uses Git's native diff algorithms for consistent formatting
The implementation maintains the existing API structure while extending functionality to provide a more comprehensive view of all changes in a task attempt.
* Perfect! I have successfully implemented the dynamic task details section that updates based on execution state. Here's a summary of what was accomplished:
## Summary
I implemented a comprehensive solution to make the details section dynamically update based on the execution state:
### Backend Changes:
1. **Added new execution state types** in `backend/src/models/task_attempt.rs`:
- `ExecutionState` enum with states: NotStarted, SetupRunning, SetupComplete, SetupFailed, CodingAgentRunning, CodingAgentComplete, CodingAgentFailed, Complete
- `TaskAttemptState` struct containing execution state, change detection, and process IDs
2. **Created `get_execution_state` method** that analyzes:
- Project setup script configuration
- Running execution processes
- Process status and completion
- Whether changes have been made to the worktree
3. **Added new API endpoint** `GET /projects/:project_id/tasks/:task_id/attempts/:attempt_id` to fetch execution state
4. **Updated type generation** to include the new types in the shared TypeScript definitions
### Frontend Changes:
1. **Enhanced useTaskDetails hook** to:
- Poll the execution state endpoint every 2 seconds
- Fetch execution state when switching attempts
- Provide execution state data to components
2. **Completely redesigned TaskDetailsPanel** with dynamic content rendering:
- **Setup Script Running**: Shows loading spinner with "Setup Script Running" message
- **Setup Failed**: Shows error message
- **Coding Agent Running (no changes)**: Shows full-height agent conversation only
- **Changes Available**: Shows 2/3 changes + 1/3 conversation split view
- **Default**: Shows "execution not started" message
### Key Features:
- **Real-time Updates**: Polls execution state every 2 seconds during execution
- **Progressive Disclosure**: Shows appropriate content based on execution phase
- **State-Aware UI**: Different layouts for different execution states
- **Error Handling**: Proper error states for failed setup scripts
- **Type Safety**: Full TypeScript support for execution states
The implementation ensures that users see relevant information at each stage of task execution, providing better visibility into what's happening and reducing confusion about the current state.
* Perfect! The fix is now in place. Here's what I've done to solve the loading spinner issue:
## Fix Summary
The problem was that the execution state was only being fetched during:
1. Polling (when `isAttemptRunning` is true)
2. Manual attempt switching via `handleAttemptChange`
But it was **not** being fetched during the initial page load when an attempt was automatically selected.
## Changes Made
1. **Added useEffect for selectedAttempt changes**: This ensures that whenever `selectedAttempt` changes (including during initial load), both `fetchAttemptData` and `fetchExecutionState` are called.
2. **Updated fetchTaskAttempts**: Added `fetchExecutionState` call when setting the latest attempt during initial load, and included it in the dependency array.
## How the Fix Works
Now when the page reloads:
1. `fetchTaskAttempts` runs and finds the latest attempt
2. It sets `selectedAttempt` to the latest attempt
3. The new useEffect detects the `selectedAttempt` change and immediately calls both:
- `fetchAttemptData(selectedAttempt.id)`
- `fetchExecutionState(selectedAttempt.id)`
4. The execution state is loaded, so the appropriate content is shown instead of the loading spinner
This ensures that the execution state is always available when displaying attempt details, whether it's the initial load, manual switching between attempts, or during polling updates.
* Perfect! The changes have been successfully implemented. Here's a summary of what I've done:
## Changes Made
### 1. Setup Script Stdio Display
- **Modified the setup running state** to show the actual stdout/stderr of the setup script instead of just a spinner
- **Enhanced process lookup** to find the setup script execution process from either the execution state's `setup_process_id` or by searching for processes with type `setupscript`
- **Added terminal-style output display** with:
- Black background and green text (terminal aesthetic)
- Monospace font for proper formatting
- Scrollable area with max height
- Fallback to "No output yet..." when no output is available
### 2. Diff Refresh During Coding Agent Execution
- **Added automatic diff polling** when the coding agent is running
- **Polls every 3 seconds** during `CodingAgentRunning` state to catch file changes as they happen
- **Uses existing fetchDiff function** to maintain consistency with manual refresh behavior
### 3. Enhanced Process Data Fetching
- **Modified fetchAttemptData** to always fetch setup script process details when available
- **Ensures setup script output is available** even when the script has completed but we want to show its output
- **Maintains existing logic** for other running processes while adding setup script support
## Key Features
1. **Real-time Setup Script Output**: Users can now see exactly what the setup script is doing, including any errors or progress messages.
2. **Live Diff Updates**: When the coding agent is making changes, the diff view automatically refreshes every 3 seconds so users can see files being modified in real-time.
3. **Better Visual Feedback**: The terminal-style output for setup scripts provides a familiar development experience.
4. **Non-blocking Updates**: The diff refresh happens independently and doesn't interfere with other UI interactions.
The implementation ensures that users get immediate feedback during both phases of execution - setup and coding - making the development process much more transparent and engaging.
* Perfect! Both issues have been fixed:
## Summary of Fixes
### 1. Setup Script Output Display
**Fixed**: Combined stdout and stderr properly
- **Before**: Only showed stdout OR stderr (whichever existed first)
- **After**: Shows both stdout AND stderr concatenated together
- **Implementation**:
```javascript
const stdout = setupProcess.stdout || '';
const stderr = setupProcess.stderr || '';
const combined = [stdout, stderr].filter(Boolean).join('\n');
return combined || 'No output yet...';
```
### 2. Automatic Diff Refresh During Coding Agent Execution
**Fixed**: Added proper diff polling with immediate refresh
- **Before**: Diff wasn't automatically updating during coding agent execution
- **After**: Diff refreshes immediately when coding agent starts + every 2 seconds while running
- **Implementation**:
- Immediate `fetchDiff()` call when entering `CodingAgentRunning` state
- `setInterval` to refresh every 2 seconds during execution
- Proper cleanup when state changes or component unmounts
### 3. Enhanced Setup Process Data Fetching
**Improved**: Better process data availability
- **Enhancement**: Modified `fetchAttemptData` to always fetch setup script process details
- **Benefit**: Ensures setup script output is available even after completion
- **Implementation**: Added dedicated setup script process fetching logic
## Key Improvements
1. **Real-time Setup Feedback**: Users can now see the complete output (both stdout and stderr) from setup scripts as they run
2. **Live Diff Updates**: File changes appear automatically every 2 seconds during coding agent execution
3. **Immediate Feedback**: Diff refreshes immediately when coding agent starts, then continues polling
4. **Better Error Visibility**: Setup script errors (stderr) are now visible alongside normal output
The implementation ensures that users get comprehensive, real-time feedback during both setup and coding phases, making the development process much more transparent and responsive.
* The styles have been cleaned up:
1. **Fixed the "No logs available" message**: Changed "No output yet..." to "Waiting for setup script output..." to indicate this is a loading state rather than an error.
2. **Simplified setup script display**: Removed the terminal UI styling including:
- The loading spinner and centered layout
- The bordered card wrapper
- The "Setup Script Output" header
- The black terminal background with green text
- Applied simple monospaced font styling similar to normalized logs
The setup script now displays with a cleaner, more consistent style that matches the rest of the interface while still being clearly readable with monospaced font.
* Added auto-scroll functionality to the setup script section:
1. **Added setup scroll ref**: Created `setupScrollRef` to track the setup script container
2. **Added auto-scroll effect**: Created a `useEffect` that monitors `attemptData.runningProcessDetails` and automatically scrolls to the bottom when the setup script output updates
3. **Applied ref to container**: Added the ref to the setup script container div
The setup script logs will now automatically scroll to the bottom as new output is received, matching the behavior of the normalized logs.
* Perfect! I've successfully implemented the follow up executor visualization in the TaskDetailsPanel. Here's what I did:
## Summary of Changes
1. **Identified follow up executor data structure**: Follow up executors are identified by having `process_type: "codingagent"` and `command: "followup_executor"` (vs main executor with `command: "executor"`)
2. **Modified TaskDetailsPanel logic**: Updated both the full-screen view (when no changes) and the split view (when changes are present) to:
- Find the main coding agent process (command: "executor")
- Find all follow up executor processes (command: "followup_executor")
- Display them in sequence with the main executor first, followed by follow up executors
3. **Enhanced UI presentation**: Each follow up executor is displayed with:
- A clear separator (border-top)
- A header showing "Follow-up #X" with the sequence number
- The start time of the follow up executor
- The normalized conversation viewer for that executor's logs
4. **Tested the implementation**: Both frontend and backend build successfully, and the code passes linting checks.
The implementation now properly handles and visualizes follow up executors by showing them under the main executor in chronological order, making it clear which conversation belongs to which executor while maintaining the existing functionality for the main coding agent.
* ## Issue Fixed!
I found and fixed the bug. The problem was in the TaskDetailsPanel logic that determines when to show the conversation logs vs. the fallback "Task execution not started yet" message.
### Root Cause
The condition `if (isCodingAgentRunning && !hasChanges)` only handled the case when the coding agent was *running*, but after follow up executors complete, the execution state becomes `CodingAgentComplete`. This caused the logic to fall through to the default case, showing "Task execution not started yet" instead of the conversation logs.
### Fix Applied
I modified the condition to also handle the completed state:
- Added `isCodingAgentComplete` variable to track when execution is complete
- Updated the condition to `if ((isCodingAgentRunning || isCodingAgentComplete) && !hasChanges)`
This ensures that both running and completed follow up executors will properly display their conversation logs, whether they're still running or have finished execution.
The fix is minimal and targeted, only affecting the specific scenario where follow up executors complete without generating file changes, while preserving all other functionality.
* ## Perfect! All styling changes implemented successfully!
Here's a summary of what I've accomplished:
### ✅ Backend Changes
1. **Extended NormalizedConversation structure** to include `prompt` and `summary` fields
2. **Updated all executor implementations** (Claude, Amp, default) to support the new fields
3. **Modified the normalized-logs API endpoint** to fetch executor session data and include prompt/summary in the response
4. **Regenerated TypeScript types** to reflect the new structure
### ✅ Frontend Changes
1. **Added prompt display** above the normalized logs with a distinctive blue styling and MessageSquare icon
2. **Implemented horizontal dividers** between all log sections:
- Between prompt and conversation logs
- Between main executor and follow-up executors
- Between multiple follow-up executors
3. **Removed the "Follow-up #1 Started:" headers** for a cleaner interface
4. **Updated the layout** to use proper spacing and consistent divider styling
### 🎨 UI Improvements
- **Prompts are displayed** in a blue-themed box with monospace font for better readability
- **Horizontal lines** consistently separate each logical section (prompt → executor → follow-up prompt → follow-up executor)
- **Clean, streamlined appearance** without verbose headers
- **Consistent spacing** and visual hierarchy throughout
The implementation properly handles cases where:
- Prompts may or may not be available
- There are multiple follow-up executors
- There are no follow-up executors
- The main executor completes with or without follow-ups
All builds pass and the code maintains the existing functionality while adding the requested visual improvements!
* ## ✅ Styling Updates Complete!
I've successfully implemented all the requested styling changes:
### 🔨 **Updated Prompt Display**
- **Changed icon** from MessageSquare to **Hammer** with orange color (`text-orange-600`)
- **Removed blue box styling** - no more background, border, or rounded corners
- **Removed monospaced font** - now uses normal text styling with `text-sm` and `text-foreground`
- **Maintains whitespace-pre-wrap** to preserve formatting without the monospace appearance
### 📏 **Improved Spacing**
- **Increased padding** at the bottom of the horizontal divider from `mb-4` to `mb-6`
- This creates **better visual separation** between the user message (prompt) and the agent logs
- **Maintains consistent spacing** throughout the component
### 🎨 **Clean Visual Hierarchy**
- User prompts now appear as **clean, readable text** with a hammer icon
- **Horizontal dividers** provide clear separation between sections
- **Proper padding** ensures comfortable reading experience
- **Consistent with the overall UI theme** rather than standing out with special styling
The changes maintain all existing functionality while providing the cleaner, more integrated visual appearance you requested. The prompt now blends naturally with the rest of the conversation while still being clearly distinguished by the hammer icon and horizontal divider!
* ## ✅ Styling Fixes Applied!
I've successfully implemented both requested changes:
### 🔨 **Blue Hammer Icon**
- Changed the hammer icon color from `text-orange-600` to `text-blue-600`
- Now matches the blue theme you requested
### 📏 **Fixed Spacing Gap**
- **Removed the bottom margin** from the horizontal divider (`mb-6` removed)
- **Added conditional top margin** to the conversation entries container (`mt-6` when prompt exists)
- This creates the **proper gap between the divider and the normalized logs**
### 🎨 **How the spacing now works:**
1. **User prompt** with hammer icon
2. **Horizontal divider** (no bottom margin)
3. **6-unit gap** (via `mt-6` on conversation container)
4. **Normalized logs/conversation entries**
The spacing is now properly balanced with a clean visual separation between the user message and the agent logs, while the blue hammer icon provides a cohesive color scheme throughout the interface!
* ## ✅ Spacing Fixed!
I've corrected the spacing issue by:
### 📏 **Fixed the Gap**
- **Added `mb-6` to the horizontal divider** - this creates the proper 6-unit gap below the divider
- **Removed the conditional margin logic** from the conversation entries container - it was causing the spacing to not work properly
- **Simplified the structure** for more predictable spacing behavior
### 🎯 **Now the spacing flow is:**
1. **User message** (with hammer icon)
2. **Gap** (via `mb-4` on user message container)
3. **Horizontal divider**
4. **Gap** (via `mb-6` on horizontal divider) ← **This was missing before!**
5. **Normalized logs/conversation entries**
The spacing should now be exactly what you requested - proper space both above and below the horizontal divider!
* ## ✅ Spacing Finally Fixed!
I've resolved the `space-y-0` override issue by **restructuring the layout**:
### 🔧 **What was wrong:**
- The `space-y-0` class creates CSS rule `.space-y-0 > :not([hidden]) ~ :not([hidden])` that sets `margin-top: 0` and `margin-bottom: 0` on all sibling elements
- This was overriding the `mb-6` class on the horizontal divider
### 🎯 **How I fixed it:**
1. **Removed `space-y-0` class** from the outer container
2. **Moved the horizontal divider outside** the prompt section to its own top-level element
3. **Made the structure flat** so no spacing utility classes conflict with each other
### 📏 **New structure:**
```jsx
<div> // No space-y-0 class
{/* User prompt */}
<div className="mb-4">...</div> // Space below prompt
{/* Horizontal divider */}
<div className="mb-6">...</div> // Space below divider - no override!
{/* Conversation entries */}
<div className="space-y-2">...</div> // Only affects entries
</div>
```
Now the `mb-6` on the horizontal divider won't be overridden, and you should see the proper gap between the divider and the normalized logs!
* ## ✅ Loading Message Fixed!
I've resolved the misleading "No logs available" message by improving the state logic:
### 🔧 **What was wrong:**
- When a follow-up execution starts, the API call completes successfully but returns no conversation data yet
- The component would show "No conversation data available" even though the process was still running and would eventually produce logs
### 🎯 **How I fixed it:**
- **Added execution process status check** - when `executionProcess.status === 'running'` and there's no conversation data
- **Shows "Waiting for logs..."** instead of "No conversation data available"
- **Maintains proper error state** for truly failed or empty completed processes
### 📱 **New behavior:**
- **When follow-up starts**: Shows "Waiting for logs..." ✅
- **During execution**: Shows "Waiting for logs..." ✅
- **When logs appear**: Shows the actual conversation ✅
- **When execution completes with no logs**: Shows "No conversation data available" ✅
- **On actual errors**: Shows the error message ✅
Now users will see a proper loading state that indicates the system is working, rather than an error-like message that suggests something went wrong!
* ## ✅ "No logs available" Message Fixed!
I've resolved the misleading error message by fixing the root cause in the backend:
### 🔧 **What was wrong:**
- When a follow-up execution starts, `process.stdout` is `None` because logs haven't been generated yet
- The backend was returning `success: false` with "No logs available for this execution process"
- The frontend was showing this as an error message
### 🎯 **How I fixed it:**
1. **Added status check in the backend** - when `process.status == ExecutionProcessStatus::Running` and `stdout` is `None`
2. **Return success with empty conversation** instead of an error
3. **Include executor session data** (prompt/summary) even for running processes without logs yet
4. **Maintain error state** only for truly failed or completed processes with no logs
### 📱 **New behavior:**
- **When follow-up starts**: Backend returns empty conversation with `success: true` ✅
- **Frontend shows**: "Waiting for logs..." (from previous fix) ✅
- **When logs appear**: Shows actual conversation data ✅
- **When process fails**: Still shows proper error messages ✅
- **When completed with no logs**: Shows "No conversation data available" ✅
Now when you start a follow-up, you should see "Waiting for logs..." instead of the misleading "No logs available for this execution process" error message!
* ## ✅ Spacing System Refactored!
I've completely refactored the spacing system for a much cleaner and more predictable layout:
### 🏗️ **New Systematic Approach**
#### **NormalizedConversationViewer (Internal Spacing):**
- **Prompt**: `mb-6` creates consistent gap to conversation entries
- **Conversation entries**: `space-y-2` between each entry
- **No internal dividers** - keeps the component focused on its own content
#### **TaskDetailsPanel (Executor Separation):**
- **Container**: `space-y-8` creates consistent spacing between main sections
- **Follow-up dividers**: `border-t` + `mb-8` for proper separation
- **Clean structure** with predictable spacing
### 📏 **Spacing Flow Now:**
1. **Main executor prompt**
2. **6-unit gap** (prompt `mb-6`)
3. **Main executor conversation**
4. **8-unit gap** (container `space-y-8`)
5. **Horizontal divider**
6. **8-unit gap** (divider `mb-8`)
7. **Follow-up prompt**
8. **6-unit gap** (prompt `mb-6`)
9. **Follow-up conversation**
10. **8-unit gap** (for next follow-up if any)
### 🎯 **Benefits:**
- **Consistent spacing** throughout all states
- **No more conflicting CSS rules** or override issues
- **Systematic approach** that's easy to maintain and debug
- **Proper separation** between logical sections
- **Clean, readable layout** with predictable gaps
The spacing should now be perfect in all scenarios - with or without prompts, with single or multiple follow-ups, and in both full-screen and split-view modes!
* I've successfully improved the diff styling to be more compact and fit better within the sidebar. Here are the key changes made:
**Spacing Improvements:**
- Reduced spacing between diff files from `space-y-6` to `space-y-3`
- Reduced main container padding from `p-6` to `p-4`
- Reduced file header padding from `py-2` to `py-1.5`
**Line Compactness:**
- Reduced diff line height from `min-h-[1.75rem]` to `min-h-[1.25rem]`
- Reduced line number column width from `w-16` to `w-12`
- Reduced line number padding from `px-2` to `px-1.5`
- Reduced individual line number widths from `w-6` to `w-5`
- Reduced line content padding from `px-3` to `px-2`
**Expand/Collapse Buttons:**
- Reduced expand button height from `h-8` to `h-6`
These changes make the diff view significantly more compact while maintaining readability. The diff will now display more content in the same space, making better use of the sidebar real estate. The frontend builds successfully with these changes.
* Perfect! The migration is complete. Let me summarize what I've done:
I've successfully migrated the merge, rebase, and PR functionality from the old `task-attempt-compare.tsx` page to the `TaskDetailsToolbar.tsx` component and deleted the old page.
1. **Added to TaskDetailsToolbar.tsx:**
- Branch status fetching with `fetchBranchStatus` function
- Merge functionality with uncommitted changes warning
- Rebase functionality
- Create PR functionality with dialog
- Branch status display showing commits ahead/behind and uncommitted changes
- Git operation buttons (Merge, Rebase, Create PR) with proper enabling/disabling logic
- Two dialog components for warnings and PR creation
2. **Deleted:**
- `frontend/src/pages/task-attempt-compare.tsx` - The entire compare page
3. **Updated routing:**
- Removed the compare page route from `App.tsx`
- Removed the import for `TaskAttemptComparePage`
- Updated keyboard shortcuts to remove compare page navigation logic
- Removed the "Changes" button that linked to the old compare page
4. **Cleaned up imports:**
- Removed unused imports like `Link` from react-router-dom and `GitCompare` icon
The diff view functionality remains in the `TaskDetailsPanel.tsx` where it was already migrated, and now users can access merge, rebase, and PR creation directly from the toolbar without needing a separate page. The branch status is also prominently displayed in the toolbar so users can see the current state at a glance.
* Perfect! Now the merge, rebase, and PR buttons will be disabled when an attempt is running, preventing users from performing git operations while code is being executed.
* Cargo fmt
* Clippy
* Prettier
* Cleanup
* Clippy
2025-07-03 22:00:44 +01:00
FROM task_attempts ta
JOIN tasks t ON ta . task_id = t . id
WHERE ta . id = $ 1 AND t . id = $ 2 AND t . project_id = $ 3 " #,
attempt_id ,
task_id ,
project_id
)
. fetch_optional ( pool )
. await ?
. ok_or ( TaskAttemptError ::TaskNotFound ) ? ;
// Get the project to check if it has a setup script
let project = Project ::find_by_id ( pool , project_id )
. await ?
. ok_or ( TaskAttemptError ::ProjectNotFound ) ? ;
let has_setup_script = project
. setup_script
. as_ref ( )
. map ( | script | ! script . trim ( ) . is_empty ( ) )
. unwrap_or ( false ) ;
// Get all execution processes for this attempt, ordered by created_at
let processes =
crate ::models ::execution_process ::ExecutionProcess ::find_by_task_attempt_id (
pool , attempt_id ,
)
. await ? ;
// Find setup and coding agent processes
let setup_process = processes . iter ( ) . find ( | p | {
matches! (
p . process_type ,
crate ::models ::execution_process ::ExecutionProcessType ::SetupScript
)
} ) ;
let coding_agent_process = processes . iter ( ) . find ( | p | {
matches! (
p . process_type ,
crate ::models ::execution_process ::ExecutionProcessType ::CodingAgent
)
} ) ;
// Determine execution state based on processes
let execution_state = if let Some ( setup ) = setup_process {
match setup . status {
crate ::models ::execution_process ::ExecutionProcessStatus ::Running = > {
ExecutionState ::SetupRunning
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Completed = > {
if let Some ( agent ) = coding_agent_process {
match agent . status {
crate ::models ::execution_process ::ExecutionProcessStatus ::Running = > {
ExecutionState ::CodingAgentRunning
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Completed = > {
ExecutionState ::CodingAgentComplete
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Failed = > {
ExecutionState ::CodingAgentFailed
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Killed = > {
ExecutionState ::CodingAgentFailed
}
}
} else {
ExecutionState ::SetupComplete
}
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Failed = > {
ExecutionState ::SetupFailed
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Killed = > {
ExecutionState ::SetupFailed
}
}
} else if let Some ( agent ) = coding_agent_process {
// No setup script, only coding agent
match agent . status {
crate ::models ::execution_process ::ExecutionProcessStatus ::Running = > {
ExecutionState ::CodingAgentRunning
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Completed = > {
ExecutionState ::CodingAgentComplete
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Failed = > {
ExecutionState ::CodingAgentFailed
}
crate ::models ::execution_process ::ExecutionProcessStatus ::Killed = > {
ExecutionState ::CodingAgentFailed
}
}
} else {
// No processes started yet
ExecutionState ::NotStarted
} ;
// Check if there are any changes (quick diff check)
let has_changes = match Self ::get_diff ( pool , attempt_id , task_id , project_id ) . await {
Ok ( diff ) = > ! diff . files . is_empty ( ) ,
Err ( _ ) = > false , // If diff fails, assume no changes
} ;
Ok ( TaskAttemptState {
execution_state ,
has_changes ,
has_setup_script ,
setup_process_id : setup_process . map ( | p | p . id . to_string ( ) ) ,
coding_agent_process_id : coding_agent_process . map ( | p | p . id . to_string ( ) ) ,
} )
}
2025-06-16 17:07:11 -04:00
}