Files
vibe-kanban/shared/types.ts
Louis Knight-Webb 5170844c76 Cleanup scripts (#288)
* Cleanup scripts (vibe-kanban 9244f51c)

We want the user to be able to specify a cleanup script, which will run after coding agent execution is complete.

This is similar to how setup scripts work, but obviously the main difference is that these run after the coding agent rather than before.

Things to ensure:
- New executor for cleanup scripts
- Project settings (FE + BE) updated with cleanup script
- Auto run cleanup script (if specified) after coding agent runs
- Task activity

* Add tab to display execution processes (vibe-kanban 444e3cf9)

Add a new tab in the TabNavigation component called "Processes"

This will be a list of all the Execution Processes associated with the selected task attempt.

Clicking on a process will open the raw logs for that execution process.

The list of execution processes is already being fetched in the frontend.

* fmt

* clippy

* format

* The processes tab info has UI glitch (vibe-kanban) (#289)

* Perfect! Fixed both UI glitches:

1. **Overlay issue**: Added `pb-20` (padding-bottom: 5rem) to both the process list view and process details view to ensure the "continue working on this task..." follow-up section doesn't overlap the process content

2. **Scrolling issue**: Changed `max-h-64` to `h-64` for both stdout/stderr log containers to ensure they have a fixed height and proper scrolling behavior

The build completed successfully, confirming the TypeScript changes are valid.

* Fixed! The issue was with the flex container setup. I made these key changes:

1. **Added `min-h-0`** to the process details container - this prevents flex items from having an implicit minimum height
2. **Added `flex-shrink-0`** to the header - this prevents the header from shrinking and ensures it stays fixed
3. **Changed `overflow-auto` to `overflow-y-auto`** - this specifically enables vertical scrolling only

Now the parent tab should properly scroll to show all process sections while the individual stdio boxes maintain their own internal scrolling.

* Execute Plan: Auto commit changes after cleanup script (vibe-kanban) (#290)

* Perfect! I've successfully implemented auto-commit functionality after cleanup script execution. Here's what was accomplished:

## Summary

Successfully implemented auto-commit functionality in [`handle_cleanup_completion()`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vibe-kanban/vk-6cc2-execute-pl/backend/src/execution_monitor.rs#L1024-L1095) that:

1. **Follows existing patterns** - Reuses the same `commit_execution_changes()` function used by coding agent completion
2. **Only commits on success** - Auto-commit only triggers when cleanup script succeeds
3. **Proper error handling** - Logs errors without failing the entire process
4. **Descriptive commit messages** - Uses "Task attempt {id} - Post-cleanup changes" format
5. **Comprehensive logging** - Provides clear success/failure feedback

The implementation leverages the existing Git infrastructure and maintains consistency with the codebase patterns. The project compiles and builds successfully with the new feature.

* fmt

* Done! The commit message is now simply "Cleanup script".

* Execute Plan: Move tasks to in-review after cleanup script (vibe-kanban 78f2eb17)

# Plan: Move Tasks to In-Review After Cleanup Script

## Current Problem
- Tasks are moved to 'in-review' status immediately when coding agent completes
- Sound notifications play immediately after coding agent completion
- Cleanup scripts run after the task is already marked as complete
- This creates inconsistent UX where users think task is done before cleanup finishes

## Proposed Solution

### 1. Modify Coding Agent Completion Handler (`execution_monitor.rs:873-933`)
- **Remove** immediate task status update to `InReview`
- **Remove** immediate sound notification
- Keep cleanup script triggering logic
- Add intermediate status or flag to track "coding complete, waiting for cleanup"

### 2. Enhance Cleanup Completion Handler (`execution_monitor.rs:1024-1097`)
- **Add** task status update to `InReview` after successful cleanup
- **Add** sound notification after successful cleanup completion
- Handle cleanup failure cases (still move to `InReview` with appropriate messaging)
- Preserve existing auto-commit functionality

### 3. Handle Edge Cases
- **No cleanup script configured**: Move to `InReview` immediately after coding agent (maintain current behavior)
- **Cleanup script fails**: Still move to `InReview` but with failure notification
- **Cleanup script timeout**: Move to `InReview` with timeout notification

### 4. Files to Modify
- `backend/src/execution_monitor.rs` - Main logic changes
- Potentially update notification messages to reflect cleanup completion

## Expected Outcome
- Tasks only move to 'in-review' after ALL processing (including cleanup) is complete
- Sound notifications align with actual task completion
- Better user experience with accurate status representation

* Execute Plan: Show 'stop attempt' if cleanup script running (vibe-kanban 8fbcfe55)

## Implementation Plan: Show 'Stop Attempt' for Cleanup Scripts

### Current State Analysis
- 'Stop Attempt' button shows when `isAttemptRunning` is true
- `isAttemptRunning` only checks for `codingagent` and `setupscript` process types
- `ExecutionProcessType` enum currently only includes: `"setupscript" | "codingagent" | "devserver"`
- Types are auto-generated from backend via `generate_types.rs`

### Required Changes

#### 1. Backend Type Updates (High Priority)
- Find and update the Rust `ExecutionProcessType` enum to include `cleanupscript`
- Run `backend/src/bin/generate_types.rs` to regenerate `shared/types.ts`

#### 2. Frontend Logic Updates (High Priority)  
- Modify `isAttemptRunning` in `TaskDetailsContextProvider.tsx:278-289`:
  ```typescript
  return attemptData.processes.some(
    (process: ExecutionProcessSummary) =>
      (process.process_type === 'codingagent' ||
       process.process_type === 'setupscript' ||
       process.process_type === 'cleanupscript') &&
      process.status === 'running'
  );
  ```

#### 3. Verification (Medium Priority)
- Verify backend creates cleanup script processes with correct `process_type`
- Test that stop functionality works with cleanup scripts (should work automatically via existing `stopAllExecutions` API)

### Expected Outcome
When cleanup scripts are running, the 'Stop Attempt' button will appear and clicking it will stop the cleanup script, maintaining consistency with setup scripts and coding agents.

* Format
2025-07-20 16:07:48 +01:00

180 lines
10 KiB
TypeScript

// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs).
// Do not edit this file manually.
// Auto-generated from Rust backend types using ts-rs
export type ApiResponse<T> = { success: boolean, data: T | null, message: string | null, };
export type Config = { theme: ThemeMode, executor: ExecutorConfig, disclaimer_acknowledged: boolean, onboarding_acknowledged: boolean, github_login_acknowledged: boolean, telemetry_acknowledged: boolean, sound_alerts: boolean, sound_file: SoundFile, push_notifications: boolean, editor: EditorConfig, github: GitHubConfig, analytics_enabled: boolean | null, };
export type ThemeMode = "light" | "dark" | "system" | "purple" | "green" | "blue" | "orange" | "red";
export type EditorConfig = { editor_type: EditorType, custom_command: string | null, };
export type GitHubConfig = { pat: string | null, token: string | null, username: string | null, primary_email: string | null, default_pr_base: string | null, };
export type EditorType = "vscode" | "cursor" | "windsurf" | "intellij" | "zed" | "custom";
export type EditorConstants = { editor_types: Array<EditorType>, editor_labels: Array<string>, };
export type SoundFile = "abstract-sound1" | "abstract-sound2" | "abstract-sound3" | "abstract-sound4" | "cow-mooing" | "phone-vibration" | "rooster";
export type SoundConstants = { sound_files: Array<SoundFile>, sound_labels: Array<string>, };
export type ConfigConstants = { editor: EditorConstants, sound: SoundConstants, };
export type ExecutorConfig = { "type": "echo" } | { "type": "claude" } | { "type": "claude-plan" } | { "type": "amp" } | { "type": "gemini" } | { "type": "setup-script", script: string, } | { "type": "claude-code-router" } | { "type": "charm-opencode" } | { "type": "sst-opencode" };
export type ExecutorConstants = { executor_types: Array<ExecutorConfig>, executor_labels: Array<string>, };
export type CreateProject = { name: string, git_repo_path: string, use_existing_repo: boolean, setup_script: string | null, dev_script: string | null, cleanup_script: string | null, };
export type Project = { id: string, name: string, git_repo_path: string, setup_script: string | null, dev_script: string | null, cleanup_script: string | null, created_at: Date, updated_at: Date, };
export type ProjectWithBranch = { id: string, name: string, git_repo_path: string, setup_script: string | null, dev_script: string | null, cleanup_script: string | null, current_branch: string | null, created_at: Date, updated_at: Date, };
export type UpdateProject = { name: string | null, git_repo_path: string | null, setup_script: string | null, dev_script: string | null, cleanup_script: string | null, };
export type SearchResult = { path: string, is_file: boolean, match_type: SearchMatchType, };
export type SearchMatchType = "FileName" | "DirectoryName" | "FullPath";
export type GitBranch = { name: string, is_current: boolean, is_remote: boolean, last_commit_date: Date, };
export type CreateBranch = { name: string, base_branch: string | null, };
export type CreateTask = { project_id: string, title: string, description: string | null, parent_task_attempt: string | null, };
export type CreateTaskAndStart = { project_id: string, title: string, description: string | null, parent_task_attempt: string | null, executor: ExecutorConfig | null, };
export type TaskStatus = "todo" | "inprogress" | "inreview" | "done" | "cancelled";
export type Task = { id: string, project_id: string, title: string, description: string | null, status: TaskStatus, parent_task_attempt: string | null, created_at: string, updated_at: string, };
export type TaskWithAttemptStatus = { id: string, project_id: string, title: string, description: string | null, status: TaskStatus, parent_task_attempt: string | null, created_at: string, updated_at: string, has_in_progress_attempt: boolean, has_merged_attempt: boolean, last_attempt_failed: boolean, latest_attempt_executor: string | null, };
export type UpdateTask = { title: string | null, description: string | null, status: TaskStatus | null, parent_task_attempt: string | null, };
export type TaskTemplate = { id: string, project_id: string | null, title: string, description: string | null, template_name: string, created_at: string, updated_at: string, };
export type CreateTaskTemplate = { project_id: string | null, title: string, description: string | null, template_name: string, };
export type UpdateTaskTemplate = { title: string | null, description: string | null, template_name: string | null, };
export type TaskAttemptStatus = "setuprunning" | "setupcomplete" | "setupfailed" | "executorrunning" | "executorcomplete" | "executorfailed";
export type TaskAttempt = { id: string, task_id: string, worktree_path: string, branch: string, base_branch: string, merge_commit: string | null, executor: string | null, pr_url: string | null, pr_number: bigint | null, pr_status: string | null, pr_merged_at: string | null, worktree_deleted: boolean, setup_completed_at: string | null, created_at: string, updated_at: string, };
export type CreateTaskAttempt = { executor: string | null, base_branch: string | null, };
export type UpdateTaskAttempt = Record<string, never>;
export type CreateFollowUpAttempt = { prompt: string, };
export type DirectoryEntry = { name: string, path: string, is_directory: boolean, is_git_repo: boolean, };
export type DirectoryListResponse = { entries: Array<DirectoryEntry>, current_path: string, };
export type DeviceStartResponse = { device_code: string, user_code: string, verification_uri: string, expires_in: number, interval: number, };
export type ProcessLogsResponse = { id: string, process_type: ExecutionProcessType, command: string, executor_type: string | null, status: ExecutionProcessStatus, normalized_conversation: NormalizedConversation, };
export type DiffChunkType = "Equal" | "Insert" | "Delete";
export type DiffChunk = { chunk_type: DiffChunkType, content: string, };
export type FileDiff = { path: string, chunks: Array<DiffChunk>, };
export type WorktreeDiff = { files: Array<FileDiff>, };
export type BranchStatus = { is_behind: boolean, commits_behind: number, commits_ahead: number, up_to_date: boolean, merged: boolean, has_uncommitted_changes: boolean, base_branch_name: string, };
export type ExecutionState = "NotStarted" | "SetupRunning" | "SetupComplete" | "SetupFailed" | "SetupStopped" | "CodingAgentRunning" | "CodingAgentComplete" | "CodingAgentFailed" | "CodingAgentStopped" | "Complete";
export type TaskAttemptState = { execution_state: ExecutionState, has_changes: boolean, has_setup_script: boolean, setup_process_id: string | null, coding_agent_process_id: string | null, };
export type ExecutionProcess = { id: string, task_attempt_id: string, process_type: ExecutionProcessType, executor_type: string | null, status: ExecutionProcessStatus, command: string, args: string | null, working_directory: string, stdout: string | null, stderr: string | null, exit_code: bigint | null, started_at: string, completed_at: string | null, created_at: string, updated_at: string, };
export type ExecutionProcessSummary = { id: string, task_attempt_id: string, process_type: ExecutionProcessType, executor_type: string | null, status: ExecutionProcessStatus, command: string, args: string | null, working_directory: string, exit_code: bigint | null, started_at: string, completed_at: string | null, created_at: string, updated_at: string, };
export type ExecutionProcessStatus = "running" | "completed" | "failed" | "killed";
export type ExecutionProcessType = "setupscript" | "cleanupscript" | "codingagent" | "devserver";
export type CreateExecutionProcess = { task_attempt_id: string, process_type: ExecutionProcessType, executor_type: string | null, command: string, args: string | null, working_directory: string, };
export type UpdateExecutionProcess = { status: ExecutionProcessStatus | null, exit_code: bigint | null, completed_at: string | null, };
export type ExecutorSession = { id: string, task_attempt_id: string, execution_process_id: string, session_id: string | null, prompt: string | null, summary: string | null, created_at: string, updated_at: string, };
export type CreateExecutorSession = { task_attempt_id: string, execution_process_id: string, prompt: string | null, };
export type UpdateExecutorSession = { session_id: string | null, prompt: string | null, summary: string | null, };
export type NormalizedConversation = { entries: Array<NormalizedEntry>, session_id: string | null, executor_type: string, prompt: string | null, summary: string | null, };
export type NormalizedEntry = { timestamp: string | null, entry_type: NormalizedEntryType, content: string, };
export type NormalizedEntryType = { "type": "user_message" } | { "type": "assistant_message" } | { "type": "tool_use", tool_name: string, action_type: ActionType, } | { "type": "system_message" } | { "type": "error_message" } | { "type": "thinking" };
export type ActionType = { "action": "file_read", path: string, } | { "action": "file_write", path: string, } | { "action": "command_run", command: string, } | { "action": "search", query: string, } | { "action": "web_fetch", url: string, } | { "action": "task_create", description: string, } | { "action": "plan_presentation", plan: string, } | { "action": "other", description: string, };
// Generated constants
export const EXECUTOR_TYPES: string[] = [
"echo",
"claude",
"claude-plan",
"amp",
"gemini",
"charm-opencode",
"claude-code-router",
"sst-opencode"
];
export const EDITOR_TYPES: EditorType[] = [
"vscode",
"cursor",
"windsurf",
"intellij",
"zed",
"custom"
];
export const EXECUTOR_LABELS: Record<string, string> = {
"echo": "Echo (Test Mode)",
"claude": "Claude",
"claude-plan": "Claude Plan",
"amp": "Amp",
"gemini": "Gemini",
"charm-opencode": "Charm Opencode",
"claude-code-router": "Claude Code Router",
"sst-opencode": "SST Opencode"
};
export const EDITOR_LABELS: Record<string, string> = {
"vscode": "VS Code",
"cursor": "Cursor",
"windsurf": "Windsurf",
"intellij": "IntelliJ IDEA",
"zed": "Zed",
"custom": "Custom"
};
export const SOUND_FILES: SoundFile[] = [
"abstract-sound1",
"abstract-sound2",
"abstract-sound3",
"abstract-sound4",
"cow-mooing",
"phone-vibration",
"rooster"
];
export const SOUND_LABELS: Record<string, string> = {
"abstract-sound1": "Gentle Chime",
"abstract-sound2": "Soft Bell",
"abstract-sound3": "Digital Tone",
"abstract-sound4": "Subtle Alert",
"cow-mooing": "Cow Mooing",
"phone-vibration": "Phone Vibration",
"rooster": "Rooster Call"
};