Commit Graph

606 Commits

Author SHA1 Message Date
GitHub Action
b37672a475 chore: bump version to 0.0.133 2025-12-11 11:28:42 +00:00
Louis Knight-Webb
07e1b34a21 add border around task form elements (#1492) 2025-12-11 11:26:54 +00:00
Louis Knight-Webb
b818f32b1e fix: preserve preceding space when inserting tag/file from typeahead (Vibe Kanban) (#1495)
* All type checks pass. The fix is complete.

**Summary:** Fixed the issue in `frontend/src/components/ui/wysiwyg/plugins/file-tag-typeahead-plugin.tsx:117-122`. When selecting a tag or file from the typeahead menu, the code now checks if the replaced text started with whitespace and preserves it by prepending a space to the inserted content.

* Done. The more elegant fix is now in place - a single line change in the `triggerFn`:

```typescript
replaceableString: match[0].slice(match[0].indexOf('@')),
```

This excludes the preceding space from `replaceableString` at the source, so the `onSelectOption` handler remains simple with no conditional logic.
2025-12-11 11:24:37 +00:00
Solomon
72c952626a Allow changing target branch when agent is running (#1474)
Changing the target branch is a no-op.
2025-12-09 16:28:54 +00:00
Gabriel Gordon-Hall
84d80659b3 normalize tool display; remove backticks (#1482) 2025-12-09 15:50:17 +00:00
GitHub Action
d0392e6d5e chore: bump version to 0.0.132 2025-12-09 12:32:34 +00:00
Louis Knight-Webb
55ca4d3655 Add auto-generate PR description feature (Vibe Kanban) (#1479)
* I've completed the implementation. Here's a summary of the changes made:

## Summary

### Backend (Rust)

1. **`crates/services/src/services/config/versions/v8.rs`**
   - Added two new config fields:
     - `pr_auto_description_enabled: bool` - defaults to `false`
     - `pr_auto_description_prompt: Option<String>` - defaults to `None`

2. **`crates/server/src/routes/task_attempts.rs`**
   - Added `auto_generate_description: bool` field to `CreateGitHubPrRequest`
   - Added `DEFAULT_PR_DESCRIPTION_PROMPT` constant with the default prompt
   - Added `trigger_pr_description_follow_up()` helper function that:
     - Gets the custom prompt from config or uses default
     - Replaces `{pr_number}` and `{pr_url}` placeholders
     - Triggers a follow-up (or initial if no session) coding agent execution
   - Modified `create_github_pr()` to call the follow-up trigger after successful PR creation

### Frontend (TypeScript/React)

3. **`frontend/src/components/dialogs/tasks/CreatePRDialog.tsx`**
   - Added `autoGenerateDescription` state, initialized from `config.pr_auto_description_enabled`
   - Added checkbox to toggle auto-generate PR description
   - Included `auto_generate_description` in the API call payload

4. **`frontend/src/pages/settings/GeneralSettings.tsx`**
   - Added "Pull Requests" Card section with:
     - Checkbox to enable/disable auto-generate by default
     - Textarea for custom prompt override

5. **Translation files**
   - Added `autoGenerateLabel` to `tasks.json`
   - Added `pullRequests` section to `settings.json`

6. **`shared/types.ts`** - Auto-generated with new types

### Key Features
- Toggle in PR creation dialog, defaulting to the global config setting
- Settings page for configuring default behavior and custom prompt
- Placeholders `{pr_number}` and `{pr_url}` for custom prompts
- Uses the same executor profile as the original task attempt
- Graceful error handling (warns but doesn't fail PR creation if follow-up fails)

* Cleanup script changes for task attempt 46e66dfc-f608-47a4-baca-e38399d2da15

* Done! I've updated the placeholder text in `settings.json` to show the actual default prompt. Now users can see exactly what prompt the AI agent will use by default, and they know what they're customizing when they enter a custom prompt.

The placeholder now shows:
```
Update the GitHub PR that was just created with a better title and description.
The PR number is #{pr_number} and the URL is {pr_url}.

Analyze the changes in this branch and write:
1. A concise, descriptive title that summarizes the changes
2. A detailed description that explains:
   - What changes were made
   - Why they were made (based on the task context)
   - Any important implementation details

Use `gh pr edit` to update the PR.
```

I also updated the helper text to mention the placeholder variables (`{pr_number}` and `{pr_url}`) so users know what variables they can use in their custom prompts.

* Cleanup script changes for task attempt 46e66dfc-f608-47a4-baca-e38399d2da15

* Done! Updated the label to "Ask AI agent to generate a better PR description" as requested.

* All done! The refactoring is complete:

**Summary of changes:**

1. **Created `crates/server/src/routes/task_attempts/pr.rs`** - New submodule containing all PR-related code:
   - Types: `CreateGitHubPrRequest`, `CreatePrError`, `AttachPrResponse`, `PrCommentsResponse`, `GetPrCommentsError`
   - Constants: `DEFAULT_PR_DESCRIPTION_PROMPT`
   - Functions: `trigger_pr_description_follow_up`, `create_github_pr`, `attach_existing_pr`, `get_pr_comments`

2. **Updated `crates/server/src/routes/task_attempts.rs`** - Added `pub mod pr;` and updated router to use `pr::` prefix for PR routes

3. **Updated `crates/server/src/bin/generate_types.rs`** - Fixed all paths to reference `pr::` submodule for the PR-related types

Both backend (cargo check) and frontend (TypeScript) compile successfully, and TypeScript types have been regenerated.

* Cleanup script changes for task attempt 46e66dfc-f608-47a4-baca-e38399d2da15

* Done! I've updated the default PR description prompt in both locations:

1. **`crates/server/src/routes/task_attempts/pr.rs`** - Updated `DEFAULT_PR_DESCRIPTION_PROMPT` constant
2. **`frontend/src/i18n/locales/en/settings.json`** - Updated `pullRequests.customPrompt.placeholder`

The changes are:
- Title should now be postfixed with "(Vibe Kanban)"
- Description should include a note at the end: "This PR was written using [Vibe Kanban](https://vibekanban.com)"

* Done! I've updated `pr_auto_description_enabled` to default to `true` in three places:

1. Added `default_pr_auto_description_enabled()` function that returns `true`
2. Updated the `#[serde(default)]` attribute to use this function
3. Updated the `from_v7_config` migration to set it to `true`
4. Updated the `Default` impl to set it to `true`

* All done! I've added i18n translations for the PR auto-description feature to all supported languages:

**settings.json** - Added `pullRequests` section with:
- `title`, `description`
- `autoDescription.label`, `autoDescription.helper`
- `customPrompt.label`, `customPrompt.placeholder`, `customPrompt.helper`

**tasks.json** - Added `autoGenerateLabel` key

Languages updated:
- Spanish (es)
- Japanese (ja)
- Korean (ko)
- Simplified Chinese (zh-Hans)

* Update frontend/src/i18n/locales/en/tasks.json

Co-authored-by: Gabriel Gordon-Hall <gabriel@bloop.ai>

* Done! I've implemented the UX improvement from the PR review:

1. **Moved the auto-generate checkbox** to the top of the form (above title and description)
2. **Added `disabled` prop** to both the title Input and description Textarea when `autoGenerateDescription` is true
3. **Added greyed-out styling** (`opacity-50 cursor-not-allowed`) to visually indicate the fields are disabled

The new field order is:
1. Auto-generate checkbox
2. Title input (disabled when checked)
3. Description textarea (disabled when checked)
4. Base branch selector
5. Draft checkbox

* All done! I've:

1. **Made `DEFAULT_PR_DESCRIPTION_PROMPT` public** in `pr.rs`

2. **Exported the constant to `shared/types.ts`** via `generate_types.rs`:
   - Imported the constant from the pr module
   - Added code to append `export const DEFAULT_PR_DESCRIPTION_PROMPT = \`...\`;` to the generated types

3. **Updated `GeneralSettings.tsx`** to import and use the constant directly as the placeholder

4. **Removed the duplicate `placeholder` key** from all i18n `settings.json` files (en, es, ja, ko, zh-Hans)

The default prompt is now defined in a single place (the Rust backend) and exported to TypeScript, eliminating duplication across all i18n files.

* Cleanup script changes for task attempt 46e66dfc-f608-47a4-baca-e38399d2da15

* Done! I've implemented the two-step UX for the custom prompt:

1. **Updated `GeneralSettings.tsx`**:
   - Replaced the always-visible textarea with a "Use custom prompt" checkbox
   - When checked, pre-fills the textarea with `DEFAULT_PR_DESCRIPTION_PROMPT`
   - When unchecked, sets `pr_auto_description_prompt` to `null` (reverts to default)
   - Textarea only visible when custom prompt is enabled

2. **Updated i18n files** (all 5 languages):
   - Replaced `label` with `useCustom` key
   - Updated `helper` text to remove "Leave empty to use the default" since that's now handled by the checkbox
   - Added translations:
     - **en**: "Use custom prompt"
     - **es**: "Usar prompt personalizado"
     - **ja**: "カスタムプロンプトを使用"
     - **ko**: "사용자 정의 프롬프트 사용"
     - **zh-Hans**: "使用自定义提示"

* Done! The custom prompt UX has been updated:

- **Textarea is now always visible** showing the current prompt (default or custom)
- **When "Use custom prompt" is unchecked**: textarea shows `DEFAULT_PR_DESCRIPTION_PROMPT` but is disabled/read-only with greyed-out styling
- **When "Use custom prompt" is checked**: textarea becomes editable, pre-filled with the default prompt for customization

This gives users visibility into what prompt will be used even when using the default, while still allowing them to customize it by checking the checkbox.

* Cleanup script changes for task attempt 46e66dfc-f608-47a4-baca-e38399d2da15

* Done! The fix has been applied. Now when the "Use custom prompt" checkbox is checked, users can:
- Clear all the text in the textarea to start from scratch
- The text no longer automatically reverts to the default prompt when emptied

---------

Co-authored-by: Gabriel Gordon-Hall <gabriel@bloop.ai>
2025-12-09 12:31:22 +00:00
Solomon
1ee05ea862 normalize cursor todo items (#1473) 2025-12-09 10:54:21 +00:00
Louis Knight-Webb
8846e6621c Improve reset dialog (vibe-kanban) (#1466)
* Excellent! All checks pass. Let me provide a summary of the changes made:

## Summary

I've successfully implemented all the requested changes to improve the Reset Dialog:

### 1. Safety Improvements
- **Default `worktreeResetOn` to `false`** (`RestoreLogsDialog.tsx:45`) - The "Reset worktree" toggle is now off by default, preventing accidental destructive actions.

- **Added `acknowledgeUncommitted` toggle** - When uncommitted changes are detected, a new amber-colored section appears with a toggle that users must enable before proceeding. This ensures users explicitly acknowledge that their uncommitted changes may be affected.

- **Updated `isConfirmDisabled` logic** (`RestoreLogsDialog.tsx:130-133`) - The confirm button is now disabled when there are uncommitted changes until the user toggles the acknowledgment.

### 2. i18n Migration
All hardcoded strings in the dialog have been migrated to use i18n with full support for 5 languages:
- English (`en/tasks.json`)
- Japanese (`ja/tasks.json`)
- Spanish (`es/tasks.json`)
- Korean (`ko/tasks.json`)
- Chinese Simplified (`zh-Hans/tasks.json`)

The translations include proper pluralization support using i18next's `_one`/`_other` suffixes for counts.

### Files Modified
- `frontend/src/components/dialogs/tasks/RestoreLogsDialog.tsx` - Main component changes
- `frontend/src/i18n/locales/en/tasks.json` - English translations
- `frontend/src/i18n/locales/ja/tasks.json` - Japanese translations
- `frontend/src/i18n/locales/es/tasks.json` - Spanish translations
- `frontend/src/i18n/locales/ko/tasks.json` - Korean translations
- `frontend/src/i18n/locales/zh-Hans/tasks.json` - Chinese translations

* Cleanup script changes for task attempt 0279d8b9-dcc8-4274-8da5-14b27672d604
2025-12-09 09:35:29 +00:00
Louis Knight-Webb
a82c7e37e7 GitHub comment dialog overflow issue (vibe-kanban) (#1465)
* All changes are complete and tests pass. Here's a summary of what was done:

## Summary

Fixed the GitHub Comments Dialog overflow issue with two files modified:

### 1. `frontend/src/components/ui/github-comment-card.tsx`
- Made the layout classes (`inline-block`, `align-bottom`, `max-w-md`) conditional
- They only apply when no custom layout className is provided (checked for `flex-`, `block`, or `w-` in className)
- This preserves the WYSIWYG editor behavior while allowing the dialog to override

### 2. `frontend/src/components/dialogs/tasks/GitHubCommentsDialog.tsx`
- Added `min-w-0` to the scrollable content container (line 108)
- Added `min-w-0` to each comment row flex container (line 145)
- Simplified the GitHubCommentCard className to `"flex-1 min-w-0"` (line 175)

The `min-w-0` classes are essential for proper flex containment - they override the default `min-width: auto` behavior that prevents flex items from shrinking below their content size.

Both TypeScript type checking and ESLint pass successfully.

* All changes are complete and tests pass. Here's a summary of what was fixed:

## Summary

**Root cause**: The Dialog component's API was being misused. Width classes (`max-w-2xl`) were being passed to `DialogContent` instead of `Dialog`. Since `DialogContent` is nested inside the Dialog's inner container (which has default `max-w-lg`), the width override was ineffective.

**Changes made**:

1. **`GitHubCommentsDialog.tsx`**: Moved sizing classes from `DialogContent` to `Dialog`:
   - `Dialog` now has `className="max-w-2xl p-0 max-h-[80vh] overflow-hidden"`
   - `DialogContent` now has `className="flex flex-col h-full"`

2. **`github-comment-card.tsx`**: Added `overflow-hidden` to the FullCard container so inner elements (DiffHunk `<pre>`) can scroll properly within the card boundaries.

The dialog should now:
- Be properly sized at `max-w-2xl` (672px) instead of being constrained to `max-w-lg` (512px)
- Properly contain content without horizontal overflow
- Allow diff hunks to scroll horizontally within the cards

* All changes are complete and tests pass. Summary of the fix for vertical scrolling:

1. **`dialog.tsx`**: Changed `grid` to `flex flex-col` in the Dialog inner container - this enables proper flex-based height constraints for child elements

2. **`GitHubCommentsDialog.tsx`**: Added `min-h-0` to the scrollable content div - this overrides the default `min-height: auto` which was preventing the flex item from shrinking below its content size

The dialog should now scroll vertically when there are many comments.

* All changes are complete and tests pass. Here's a summary of what was done:

**Changes to fix vertical scrolling:**

The dialog now follows the same pattern as `ViewProcessesDialog`:

1. **Dialog**: `className="max-w-2xl p-0 overflow-hidden"` - removed height constraints
2. **DialogContent**: `className="p-0"` - removed `h-full` and flex classes
3. **Added wrapper div**: `className="max-h-[70vh] flex flex-col min-h-0"` - explicit height constraint on inner wrapper
4. **Scrollable content**: `className="p-4 overflow-auto flex-1 min-h-0"` - scrolls within the constrained wrapper

The key insight was that putting the height constraint (`max-h-[70vh]`) on an **inner wrapper** rather than on the Dialog itself makes the flex-based scrolling work correctly. The header and footer remain outside this wrapper so they stay fixed.

* Cleanup script changes for task attempt a7065bc4-aa62-4dab-90c7-d15524acc50f

* Done! The hacky string matching has been replaced with an explicit `layout` prop:

**Changes:**

1. **`github-comment-card.tsx`**:
   - Added `layout?: 'inline' | 'block'` prop to `GitHubCommentCardProps`
   - Replaced the string matching logic with a simple `layout === 'inline'` check
   - Default is `'inline'` for backwards compatibility (WYSIWYG editor)

2. **`GitHubCommentsDialog.tsx`**:
   - Added `layout="block"` prop to `GitHubCommentCard`

The WYSIWYG editor (`github-comment-node.tsx`) doesn't need changes since it uses the default `layout="inline"`.

* Done! The hacky string matching has been replaced with an explicit `layout` prop:

**Summary of changes:**

1. **`github-comment-card.tsx`**:
   - Consolidated `variant` and `layout` into a single required `variant` prop
   - Three values: `'compact'` (inline chip), `'full'` (inline card with max-width), `'list'` (block card)
   - Removed the `layout` prop entirely
   - Made `variant` required (no default)

2. **`GitHubCommentsDialog.tsx`**:
   - Changed from `variant="full" layout="block"` to just `variant="list"`

3. **`github-comment-node.tsx`**:
   - Already had `variant="full"` explicitly set, no change needed
2025-12-08 17:34:12 +00:00
Louis Knight-Webb
9c70858b21 Make it possible to create draft PRs (vibe-kanban) (#1460)
* All checks pass. Let me summarize what was implemented:

## Summary

I've successfully implemented the draft PR feature. Here's what was changed:

### Backend Changes

1. **`crates/server/src/routes/task_attempts.rs`** (lines 70-76, 724-731):
   - Added `draft: Option<bool>` field to `CreateGitHubPrRequest` struct
   - Pass the draft field to the `CreatePrRequest` when creating PRs

2. **`crates/services/src/services/github.rs`** (lines 146-153):
   - Added `draft: Option<bool>` field to `CreatePrRequest` struct

3. **`crates/services/src/services/github/cli.rs`** (lines 158-160):
   - Added logic to pass `--draft` flag to `gh pr create` when draft is true

### Frontend Changes

4. **`frontend/src/components/dialogs/tasks/CreatePRDialog.tsx`**:
   - Added `Checkbox` import
   - Added `isDraft` state (default: false)
   - Added checkbox UI with label from translations
   - Pass `draft: isDraft` in the API call
   - Reset `isDraft` on success and cancel

### Type Generation

5. **`shared/types.ts`** (auto-generated):
   - `CreateGitHubPrRequest` now includes `draft: boolean | null`

### Translations

6. Added `"draftLabel"` translation key to all locale files:
   - `frontend/src/i18n/locales/en/tasks.json`: "Create as draft"
   - `frontend/src/i18n/locales/es/tasks.json`: "Crear como borrador"
   - `frontend/src/i18n/locales/ja/tasks.json`: "下書きとして作成"
   - `frontend/src/i18n/locales/ko/tasks.json`: "초안으로 만들기"
   - `frontend/src/i18n/locales/zh-Hans/tasks.json`: "创建为草稿"

* Done. I've updated the checkbox to use `h-5 w-5` (20px) and added `text-sm` to the label for consistent sizing.
2025-12-08 16:53:37 +00:00
Louis Knight-Webb
047695e554 Exiting settings should navigate to previous location (#1467)
**Summary:** Updated `SettingsLayout.tsx` to simplify the `handleBack` function - now it always uses `navigate(-1)` to go back in browser history. This means pressing ESC (or clicking the X button) in Settings will return users to whatever page they came from. Removed the unused `useLocation` import as it was no longer needed.
2025-12-08 16:46:57 +00:00
Gabriel Gordon-Hall
45d0359118 feat: move default agent configuration to Agent Settings (vibe-kanban) (#1453) 2025-12-08 15:25:49 +00:00
Louis Knight-Webb
f045b3f935 All i18n issues are fixed. I added the missing parallelLabel and parallelHelper keys to the Chinese (Simplified) translation file (zh-Hans/settings.json). These keys were already present in English, Japanese, Korean, and Spanish but were missing from the Chinese translation. (#1455) 2025-12-08 14:06:28 +00:00
Louis Knight-Webb
76877ea631 Run setup script in parallel with coding agent (vibe-kanban) (#1446)
* The implementation is complete. Here's a summary of all the changes made:

## Summary

### Files Created
- `crates/db/migrations/20251206000000_add_parallel_setup_script_to_projects.sql` - Database migration

### Files Modified

**Backend (Rust):**
- `crates/db/src/models/project.rs` - Added `parallel_setup_script: bool` field to `Project`, `Option<bool>` to `CreateProject`/`UpdateProject`, updated all SQL queries
- `crates/services/src/services/container.rs` - Modified `start_attempt()` to support parallel execution when `parallel_setup_script` is true
- `crates/server/src/routes/projects.rs` - Updated create/update handlers to include new field
- `crates/deployment/src/lib.rs` - Added field to auto-created projects

**Frontend (TypeScript/React):**
- `frontend/src/pages/settings/ProjectSettings.tsx` - Added checkbox UI for parallel setup script setting
- `frontend/src/components/dialogs/projects/ProjectFormDialog.tsx` - Added field to create data
- `frontend/src/components/tasks/TaskDetails/preview/NoServerContent.tsx` - Added field to update data

**i18n:**
- `frontend/src/i18n/locales/en/settings.json` - Added translation strings
- `frontend/src/i18n/locales/es/settings.json` - Added Spanish translations
- `frontend/src/i18n/locales/ja/settings.json` - Added Japanese translations
- `frontend/src/i18n/locales/ko/settings.json` - Added Korean translations

**SQLx Cache:**
- `crates/db/.sqlx/` - Updated query cache files

### Behavior
- When `parallel_setup_script` is `false` (default): Sequential execution (setup → coding agent → cleanup)
- When `parallel_setup_script` is `true`: Setup script and coding agent start simultaneously, setup script failure does not stop the coding agent

* update useConversationHistory to display multiple running conversation threads at once

* The bug fix is complete. Here's a summary of the changes:

## Bug Fix Summary

**Problem:** When running in parallel mode, if the setup script finishes before the coding agent, the task incorrectly moves to "InReview" status while the coding agent is still running.

**Root Cause:** The `should_finalize()` method returned `true` when `next_action.is_none()`, which is the case for setup scripts in parallel mode. This caused premature task finalization.

**Solution:** Modified `should_finalize()` to check if there are other running non-DevServer processes for the same task attempt before finalizing.

### Files Modified

1. **`crates/db/src/models/execution_process.rs`**
   - Added `has_running_non_dev_server_processes_excluding()` method that accepts an optional `exclude_id` parameter
   - Refactored existing `has_running_non_dev_server_processes()` to delegate to the new method

2. **`crates/services/src/services/container.rs`**
   - Changed `should_finalize()` from sync to async
   - Added check for other running processes before allowing finalization

3. **`crates/local-deployment/src/container.rs`**
   - Updated call site to use `.await` on the now-async `should_finalize()`

* The bug fix is complete. Here's a summary of the changes:

**Fixed files:**

1. **`crates/services/src/services/container.rs`** - Simplified `should_finalize()`:
   - Changed from `async fn` back to `fn` (non-async)
   - Removed the complex database check for other running processes
   - Added a simple check: if the run_reason is `SetupScript` AND `next_action.is_none()` (parallel mode), return `false` (don't finalize)

2. **`crates/local-deployment/src/container.rs`** - Removed `.await` from the `should_finalize()` call

3. **`crates/db/src/models/execution_process.rs`** - Cleaned up by removing the unused `has_running_non_dev_server_processes_excluding()` function and simplifying `has_running_non_dev_server_processes()` back to its original form

**How the fix works:**

- In **parallel mode**: Setup scripts have `next_action: None`, so the new check catches this case and prevents finalization
- In **sequential mode**: Setup scripts have `next_action: Some(coding_agent)`, so they pass this check but won't finalize anyway because `next_action.is_none()` returns `false`
2025-12-07 15:25:13 +00:00
Stephan Fitzpatrick
7da884bc3a Add environment variables to executor profiles (#1444)
* The implementation is complete. Here's a summary of what was done:

Successfully added environment variable configuration support to executor profiles. Users can now configure env vars like this:

```json
{
  "CLAUDE_CODE": {
    "DEFAULT": {
      "CLAUDE_CODE": {
        "env": {
          "ANTHROPIC_AUTH_TOKEN": "...",
          "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
          "API_TIMEOUT_MS": "3000000",
          "ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-4.6"
        }
      }
    }
  }
}
```

1. **`crates/executors/src/command.rs`**:
   - Added `env: Option<HashMap<String, String>>` to `CmdOverrides` struct
   - Added `apply_env_vars()` helper function

2. **Updated all executors to apply env vars**:
   - `claude.rs` - `spawn_internal()`
   - `codex.rs` - `spawn()`
   - `amp.rs` - `spawn()` and `spawn_follow_up()`
   - `droid.rs` - module-level `spawn()` function
   - `copilot.rs` - both spawn methods
   - `cursor.rs` - both spawn methods
   - `opencode.rs` - both spawn methods
   - `acp/harness.rs` - `spawn_with_command()` and `spawn_follow_up_with_command()` (used by Gemini/Qwen)

3. **Type Generation**: All JSON schemas and TypeScript types now include the `env` field

This allows users to set environment variables for all supported coding agents (Claude Code, Codex, Gemini, Amp, Droid, Copilot, Cursor, OpenCode, Qwen) through profile configuration, eliminating the need for external tools like claude-code-router.

* The implementation is complete. Here's a summary of what was done:

## Summary

### Backend Changes
- Added `env: Option<HashMap<String, String>>` field to `CmdOverrides` struct in `crates/executors/src/command.rs`
- Created `apply_env_vars()` helper function to inject env vars into `tokio::process::Command`
- Updated all 9 executors to apply env vars during spawn:
  - claude.rs, codex.rs, amp.rs, droid.rs, copilot.rs, cursor.rs, opencode.rs
  - gemini.rs and qwen.rs (via ACP harness)
- Modified ACP harness signature to accept `Option<&CmdOverrides>`

### Frontend Changes
- Created `KeyValueField.tsx` - custom RJSF field for editing key-value pairs
- Registered the field in `theme.ts`
- Added `uiSchema` to `ExecutorConfigForm.tsx` to use the custom field for `env`

### Generated Files
- `shared/types.ts` - TypeScript types updated with `env` field
- `shared/schemas/*.json` - All 9 executor schemas include `env` property

The environment variables UI will now appear in Settings > Agent Settings as a key-value editor with "Environment Variables" label and description. Users can add/remove/edit env vars that will be passed to the CLI execution environment.

* cleanup env structs

* fix form

* fmt

* remove mise.toml

* fmt

* Seprate config form per selected variant

---------

Co-authored-by: Louis Knight-Webb <louis@bloop.ai>
Co-authored-by: Solomon <abcpro11051@disroot.org>
2025-12-07 15:01:12 +00:00
GitHub Action
e1c9c15f43 chore: bump version to 0.0.131 2025-12-07 14:13:05 +00:00
Gabriel Gordon-Hall
e6a5694bda fix i18n 2025-12-07 13:36:11 +00:00
Gabriel Gordon-Hall
77d4fcbf3f fmt 2025-12-07 13:23:39 +00:00
CG-man
52c84f0cce Add internationalization support for Simplified Chinese (vibe-kanban 1e5a14ae) 2025-12-07 13:23:39 +00:00
GitHub Action
9c434822d6 chore: bump version to 0.0.130 2025-12-06 19:27:31 +00:00
Louis Knight-Webb
9107b4b384 Add GitHub comments to WYSIWYG (vibe-kanban) (#1449)
* The implementation is complete. Here's a summary of what was added:

## Summary

I've implemented the endpoint to fetch all GitHub PR comments for a task attempt. The new endpoint is:

**`GET /api/task-attempts/{id}/pr/comments`**

### Files Modified

1. **`crates/services/src/services/github/cli.rs`**
   - Added `PrCommentAuthor` and `PrComment` types
   - Added `get_pr_comments()` method to `GhCli` that runs `gh pr view N --repo owner/repo --json comments`
   - Added `parse_pr_comments()` helper to parse the JSON response

2. **`crates/services/src/services/github.rs`**
   - Exported `PrComment` and `PrCommentAuthor` types
   - Added async `get_pr_comments()` method with retry logic (exponential backoff)

3. **`crates/server/src/routes/task_attempts.rs`**
   - Added `PrCommentsResponse` struct
   - Added `GetPrCommentsError` enum with variants: `NoPrAttached`, `GithubCliNotInstalled`, `GithubCliNotLoggedIn`
   - Added `get_pr_comments()` handler function
   - Registered the route at `/pr/comments`

4. **`crates/server/src/bin/generate_types.rs`**
   - Registered all new types for TypeScript generation

### Generated TypeScript Types

```typescript
type PrCommentsResponse = { comments: Array<PrComment> };

type GetPrCommentsError =
  | { "type": "no_pr_attached" }
  | { "type": "github_cli_not_installed" }
  | { "type": "github_cli_not_logged_in" };

type PrComment = {
  id: string,
  author: PrCommentAuthor,
  authorAssociation: string,
  body: string,
  createdAt: string,
  url: string
};

type PrCommentAuthor = { login: string };
```

* The enhancement is complete. Here's a summary of the changes:

## Summary

Enhanced the `GET /api/task-attempts/{id}/pr/comments` endpoint to fetch **both** general PR comments AND inline review comments, merged into a unified timeline.

### Response Format

```json
{
  "success": true,
  "data": {
    "comments": [
      {
        "comment_type": "general",
        "id": "IC_kwDOO7s21M7XzqRP",
        "author": "stunningpixels",
        "author_association": "MEMBER",
        "body": "Testing comments",
        "created_at": "2025-12-06T16:15:10Z",
        "url": "https://github.com/BloopAI/vibe-kanban/pull/1447#issuecomment-..."
      },
      {
        "comment_type": "review",
        "id": 2595085092,
        "author": "stunningpixels",
        "author_association": "MEMBER",
        "body": "undo this",
        "created_at": "2025-12-06T16:15:18Z",
        "url": "https://github.com/BloopAI/vibe-kanban/pull/1447#discussion_r...",
        "path": "frontend/src/components/layout/Navbar.tsx",
        "line": 23,
        "diff_hunk": "@@ -20,7 +20,6 @@ import {...}"
      }
    ]
  }
}
```

### TypeScript Types

```typescript
type UnifiedPrComment =
  | { comment_type: "general"; id: string; author: string; author_association: string; body: string; created_at: string; url: string; }
  | { comment_type: "review"; id: bigint; author: string; author_association: string; body: string; created_at: string; url: string; path: string; line: bigint | null; diff_hunk: string; };
```

* Add GitHub review comments (vibe-kanban b9ab9ab2)

frontend/src/components/tasks/TaskFollowUpSection.tsx

- New button to the right of attachment with github comment icon
- If user clicks, opens dialog
- Dialog will display list of comments, format like this:

```javascript

{

  "comments": [

    {

      "id": "IC_kwDOO7s21M7XAc3c",

      "author": {

        "login": "LSRCT"

      },

      "authorAssociation": "MEMBER",

      "body": "Hi! I tried to get claude to use the `AskUserQuestion` tool in vibe kanban but did not manage, it does not seem to have access to the tool at all. Could you give me a hint on how to best test this PR?",

      "createdAt": "2025-12-03T14:46:49Z",

      "includesCreatedEdit": false,

      "isMinimized": false,

      "minimizedReason": "",

      "reactionGroups": [],

      "url": "https://github.com/BloopAI/vibe-kanban/pull/1395#issuecomment-3607219676",

      "viewerDidAuthor": false

    },

    {

      "id": "IC_kwDOO7s21M7Xc1Pi",

      "author": {

        "login": "davidrudduck"

      },

      "authorAssociation": "NONE",

      "body": "> Hi! I tried to get claude to use the `AskUserQuestion` tool in vibe kanban but did not manage, it does not seem to have access to the tool at all. Could you give me a hint on how to best test this PR?\r\n\r\nI must have been having a daft night when I submitted this - am fixing this at the moment.",

      "createdAt": "2025-12-04T22:57:18Z",

      "includesCreatedEdit": false,

      "isMinimized": false,

      "minimizedReason": "",

      "reactionGroups": [],

      "url": "https://github.com/BloopAI/vibe-kanban/pull/1395#issuecomment-3614659554",

      "viewerDidAuthor": false

    }

  ]

}

```

The user will select a comment, this will then be added as a component to the chat.

The component should be fully serializable/deserializable as markdown <> WYSIWYG, just like we do for images atm.

The backend will be implemented separately so just hardcode a mock response in the frontend for now.

* PR 1449 failed because of i18n regressions (vibe-kanban 723e309c)

Please resolve the 7 hardcoded strings this PR introduces https://github.com/BloopAI/vibe-kanban/pull/1449

* PR 1449 failed because of i18n regressions (vibe-kanban 723e309c)

Please resolve the 7 hardcoded strings this PR introduces https://github.com/BloopAI/vibe-kanban/pull/1449
2025-12-06 19:26:29 +00:00
Louis Knight-Webb
86705f9c8e The fix is complete. Here's a summary of the changes: (#1441)
**Changes made:**

1. **`frontend/src/components/ui/wysiwyg.tsx`**:
   - Removed imports for `INLINE_CODE`, `InlineCodeNode`, and `INLINE_CODE_TRANSFORMER`
   - Removed `InlineCodeNode` from the registered nodes array
   - Simplified transformers to use Lexical's built-in `TRANSFORMERS` (which includes `INLINE_CODE`)

2. **Deleted files:**
   - `frontend/src/components/ui/wysiwyg/nodes/inline-code-node.tsx`
   - `frontend/src/components/ui/wysiwyg/transformers/inline-code-transformer.ts`

**What this fixes:**
1. **Escaping bug** - Backticks will no longer be incorrectly escaped when saving inline code
2. **Cannot edit inline code** - You can now edit characters within inline code blocks (TextNode is editable, unlike DecoratorNode which was atomic)

The TypeScript and lint checks pass. You can test by:
1. Creating a task with inline code like `` `console.log()` ``
2. Saving and reloading - no backslashes should appear
3. Clicking inside inline code and editing individual characters
2025-12-05 16:25:23 +00:00
GitHub Action
d81be475c5 chore: bump version to 0.0.129 2025-12-05 10:50:01 +00:00
Louis Knight-Webb
89c0f1b0ef revert (#1439) 2025-12-05 10:48:16 +00:00
Louis Knight-Webb
32c689dfc5 Run setup or cleanup scripts (vibe-kanban) (#1428)
* I've successfully implemented the two new API endpoints for running setup and cleanup scripts on task attempts. Here's a summary of the changes:

## Summary

### Files Modified

1. **`crates/server/src/routes/task_attempts.rs`**
   - Added `RunScriptError` enum type for error handling when no script is configured
   - Added `run_setup_script` handler - POST endpoint to run a project's setup script
   - Added `run_cleanup_script` handler - POST endpoint to run a project's cleanup script
   - Registered both routes: `/run-setup-script` and `/run-cleanup-script`

2. **`crates/server/src/bin/generate_types.rs`**
   - Added `RunScriptError` to the list of types to export to TypeScript

3. **`shared/types.ts`** (auto-generated)
   - Now includes the `RunScriptError` TypeScript type

### New Endpoints

- **`POST /api/task-attempts/{id}/run-setup-script`** - Runs the project's setup script in the task attempt's worktree
- **`POST /api/task-attempts/{id}/run-cleanup-script`** - Runs the project's cleanup script in the task attempt's worktree

Both endpoints:
- Return `ExecutionProcess` on success with the created execution process
- Return `RunScriptError::NoScriptConfigured` if the project doesn't have the respective script configured
- Track analytics events (`setup_script_executed` / `cleanup_script_executed`)
- Use the existing `ScriptContext::SetupScript` / `ScriptContext::CleanupScript` and `ExecutionProcessRunReason::SetupScript` / `ExecutionProcessRunReason::CleanupScript` patterns

* Done! I've added the running process check to both `run_setup_script` and `run_cleanup_script` endpoints. Here's a summary of the changes:

## Summary

### Files Modified

1. **`crates/db/src/models/execution_process.rs`**
   - Added `has_running_non_dev_server_processes` method that checks if any running processes (excluding dev servers) exist for a task attempt

2. **`crates/server/src/routes/task_attempts.rs`**
   - Added `ProcessAlreadyRunning` variant to `RunScriptError` enum
   - Added check at the start of both `run_setup_script` and `run_cleanup_script` handlers to return an error if non-dev-server processes are already running

3. **`shared/types.ts`** (auto-generated)
   - `RunScriptError` now includes both `no_script_configured` and `process_already_running` variants

### Behavior

Both endpoints now:
1. First check if any non-dev-server processes (coding agents, setup/cleanup scripts) are running on the task attempt
2. If running processes exist, return `RunScriptError::ProcessAlreadyRunning`
3. Otherwise proceed with script execution as before

* Cleanup script changes for task attempt cdb9cbd9-a6e7-4936-8e75-6dd837c634f3

* Done! I've implemented the frontend changes for the setup/cleanup script dropdown. Here's a summary:

## Summary of Frontend Changes

### Files Modified

1. **`frontend/src/lib/api.ts`**
   - Added `RunScriptError` to imports from shared types
   - Added `runSetupScript` and `runCleanupScript` methods to `attemptsApi`

2. **`frontend/src/components/tasks/TaskFollowUpSection.tsx`**
   - Added imports for `Terminal` icon, `DropdownMenu`, and `Tooltip` components
   - Added `attemptsApi` import
   - Updated `useProject()` to also get `project` data
   - Added computed values: `hasSetupScript`, `hasCleanupScript`, `hasAnyScript`
   - Added handlers: `handleRunSetupScript`, `handleRunCleanupScript`
   - Added dropdown button with Terminal icon after the Attach button
   - Dropdown only shows if project has at least one script configured
   - Menu items are disabled with tooltips when scripts are not configured
   - Button is disabled when `isAttemptRunning` is true

3. **`frontend/src/i18n/locales/en/tasks.json`**
   - Added translation strings for the dropdown menu items and tooltips

### Behavior

- **Dropdown visibility**: Only shown if project has `setup_script` or `cleanup_script` configured
- **Button disabled state**: When `isAttemptRunning` is true (tooltip explains why)
- **Menu item disabled states**:
  - "Run setup script" disabled if no `setup_script` (tooltip explains)
  - "Run cleanup script" disabled if no `cleanup_script` (tooltip explains)
- **After clicking**: Script starts executing and `isAttemptRunning` becomes true automatically, preventing further interactions

* Done! I've added translations for all languages:

- **Spanish (es)**:
  - "Ejecutar script de configuración" / "Ejecutar script de limpieza"
  - Tooltips in Spanish

- **Japanese (ja)**:
  - "セットアップスクリプトを実行" / "クリーンアップスクリプトを実行"
  - Tooltips in Japanese

- **Korean (ko)**:
  - "설정 스크립트 실행" / "정리 스크립트 실행"
  - Tooltips in Korean

* Done! I've removed all the fallback strings from the `t()` calls. All 5 translation calls now use just the key:

- `{t('followUp.scriptsDisabledWhileRunning')}`
- `{t('followUp.runSetupScript')}`
- `{t('followUp.noSetupScript')}`
- `{t('followUp.runCleanupScript')}`
- `{t('followUp.noCleanupScript')}`
2025-12-04 17:57:43 +00:00
Louis Knight-Webb
0d2e77dceb The fix is complete. Here's a summary of the changes: (#1435)
## Summary

Fixed GitHub issue #1433 - "Clicking on add review comment button no longer focus on the edit box"

### Changes Made

**1. `frontend/src/components/ui/wysiwyg.tsx`** (+5 lines)
- Added import for `AutoFocusPlugin` from `@lexical/react/LexicalAutoFocusPlugin`
- Added `autoFocus?: boolean` prop to `WysiwygProps` type
- Conditionally renders `<AutoFocusPlugin />` when `autoFocus` is true and editor is editable

**2. `frontend/src/components/diff/CommentWidgetLine.tsx`** (+1 line)
- Added `autoFocus` prop to the `WYSIWYGEditor` when creating new review comments

**3. `frontend/src/components/diff/ReviewCommentRenderer.tsx`** (+1 line)
- Added `autoFocus` prop to the `WYSIWYGEditor` when editing existing review comments

### Verification
- TypeScript type check: Passed
- ESLint: Passed
- Prettier: Passed
2025-12-04 17:48:26 +00:00
GitHub Action
6805be6962 chore: bump version to 0.0.128 2025-12-04 17:31:57 +00:00
Britannio Jarrett
63423f970b The issue was that links in the markdown view (used for user messages, tool outputs, etc.) were styled with text-primary, which resolves to the same color as the background or main text in some themes (effectively making them invisible or indistinguishable). I updated the WYSIWYGEditor configuration to use standard blue coloring for links in both light and dark modes. (#1418)
**Changes applied:**

1.  **Modified `frontend/src/components/ui/wysiwyg.tsx`**:
    -   Updated the `theme.link` property to use `text-blue-600` (light mode) and `dark:text-blue-400` (dark mode).
    -   Added hover states `hover:text-blue-800` and `dark:hover:text-blue-300` for better interactivity.

This change ensures links are clearly visible and themed correctly in user messages and all other places where the `WYSIWYGEditor` is used for markdown rendering.

I verified the changes by running `pnpm run check`, which passed successfully.
2025-12-04 17:28:54 +00:00
Louis Knight-Webb
58aecc0377 Done. All three changes have been applied: (#1432)
1. Added `text-sm` to the container wrapper (line 190)
2. Removed redundant `text-sm` from the `paragraph` theme (line 102)
3. Removed redundant `text-sm` from the `code` theme (line 129)

Now all markdown elements in the WYSIWYG editor will inherit `text-sm` from the container, ensuring consistent font sizing for paragraphs, lists, blockquotes, and other elements.
2025-12-04 17:27:10 +00:00
Alex Netsch
ef1ba1b4bb Glob copy files (#1420)
* Simplify glob file search

Switch to globwalk

* Cleanup

* Fix single file continue, add timeout to copy files

* Remove error in copy file dropdown when no match found

* Remove error message for file search text are, remove dead code

* Move copy logic to copy.rs
2025-12-04 15:36:14 +00:00
Louis Knight-Webb
9bfaa6dde5 Add button to copy all process logs (vibe-kanban) (#1429)
* Done! I've implemented the "Copy all process logs" button for the `ViewProcessesDialog`. Here's a summary of the changes:

**Files modified:**

1. **`ProcessLogsViewer.tsx`** - Added `onLogsChange` callback prop and exported the `LogEntry` type

2. **`ProcessesTab.tsx`** - Added `onLogsChange` prop that passes through to ProcessLogsViewer, and clears logs when no process is selected

3. **`ViewProcessesDialog.tsx`** - Added the copy button with:
   - State management for logs and copied feedback
   - Copy button in the dialog header (right-aligned)
   - Tooltip showing "Copy logs" / "Copied!"
   - Button disabled when no logs are available
   - Uses `Copy`/`Check` icons from lucide-react

4. **i18n files** (en, es, ko, ja) - Added translation keys:
   - `copyLogs`: "Copy logs"
   - `logsCopied`: "Copied!"

**Behavior:**
- The copy button appears in the dialog header next to the title
- When clicked, it copies all log content (joined by newlines) to clipboard
- Shows a checkmark icon for 2 seconds after successful copy
- Button is disabled when viewing the process list (no logs selected)

* Cleanup script changes for task attempt 2060681f-e4c3-46a7-aca3-3e9705b53248

* Fixed! I wrapped the `Tooltip` component in `TooltipProvider` as required by shadcn/ui. The error should now be resolved - the copy button should work properly.

* Done! I've made the following changes:

1. Removed the `Tooltip`, `TooltipProvider`, `TooltipContent`, and `TooltipTrigger` components
2. Added `mr-8` margin to position the button left of the dialog's native close button
3. Added `text-green-500` to the `Check` icon for green success feedback

The button should now be properly positioned and show a green checkmark when logs are copied.

* Done! Changed `text-green-500` to `text-success` so the check icon uses the theme's success color instead of a hardcoded green.

* Done! I've fixed the vertical alignment by:

1. Moving the button outside the `DialogHeader` and positioning it absolutely
2. Using `absolute right-12 top-4` to place it exactly to the left of the close button (which is at `right-4 top-4`)
3. Using `h-4 w-4` to match the close button's icon size
4. Added `rounded-sm opacity-70 hover:opacity-100` to match the close button's styling

The copy button should now be perfectly aligned with the close button.

* Done! I've moved the copy button to `ProcessesTab` next to the "Back to list" button. The changes:

1. **Removed** the absolute-positioned icon button from `ViewProcessesDialog`
2. **Added** a text button "Copy logs" / "Copied!" in `ProcessesTab` next to "Back to list"
3. **Moved** the i18n keys from `viewProcessesDialog` to `processes` section
4. The button uses the same styling as the "Back to list" button for consistency
5. Shows green "Copied!" text for 2 seconds after successful copy

* Done! I've made the following changes to address the review comment:

1. **`ProcessLogsViewer.tsx`**: Removed the `export` keyword from `LogEntry` type - it's now internal to the file
2. **`ProcessesTab.tsx`**: Changed import from `LogEntry` to `PatchType` from `shared/types`, and updated the state and callback types accordingly

The type check passes successfully.

* Done! I've addressed the review comment by:

1. **Lifted `useLogStream` to `ProcessesTab`** - Now `ProcessesTab` calls `useLogStream(selectedProcess?.id ?? '')` directly, giving it direct access to the logs for the copy button

2. **Removed the hacky `onLogsChange` callback** - No more `useEffect` to propagate logs up via callback

3. **Using `ProcessLogsViewerContent` directly** - `ProcessesTab` now imports and uses `ProcessLogsViewerContent` with the logs from `useLogStream`

4. **Kept `ProcessLogsViewer` as a simpler component** - The default export still exists for other potential consumers, but it's now a simple wrapper without the callback prop

The data flow is now cleaner:
- `ProcessesTab` owns the log data via `useLogStream`
- `ProcessLogsViewerContent` is a pure presentation component
- No callback prop threading needed

* Done! I've added proper translations for the i18n keys:

- **Spanish (es):** "Copiar registros" / "¡Copiado!"
- **Korean (ko):** "로그 복사" / "복사됨!"
- **Japanese (ja):** "ログをコピー" / "コピーしました!"
2025-12-04 15:33:38 +00:00
Louis Knight-Webb
fa4a8f9e22 Cleanup task follow up section loading (vibe-kanban) (#1430)
Solve component completely unmounting when navigating between task attempts
2025-12-04 14:56:27 +00:00
Louis Knight-Webb
b6a4f76e43 Both TypeScript and ESLint pass. The fix is complete. (#1427)
**Summary**: Added a single guard condition to the `useEffect` that syncs `localMessage` from scratch data. When `isTextareaFocused` is true, the effect now returns early, preventing the cursor jump issue caused by the WebSocket sync overwriting the local message while the user is actively typing.
2025-12-04 13:06:46 +00:00
Alex Netsch
e1cb24923a All done! I've updated the copy, edit, and delete buttons in frontend/src/components/ui/wysiwyg.tsx to use semantic colors: (#1412)
**Changes made:**
- Button backgrounds: `bg-foreground` → `bg-muted`
- Icon colors: `text-background` → `text-muted-foreground`
- Check icon (copied state): `text-green-600` → `text-success`

These semantic colors will now adapt correctly to both light and dark modes, following the established patterns in the codebase.
2025-12-04 12:35:30 +00:00
GitHub Action
c92b769fe1 chore: bump version to 0.0.127 2025-12-03 13:11:58 +00:00
Solomon
a763a0eae9 Migrate task sharing to ElectricSQL + Tanstack DB (#1379)
* WIP - Migrate task sharing to ElectricSQL + Tanstack DB

* WIP auth proxy

* Simplify electric host

* Electric token

Only set in cloud. Acts like a DB password.

* Add org membership validation

* fix Electric auth param

* trigger dev deployment

* Validate where clause

* Simplify check macro

* Cleanup

* Reduce Electric Postgres privileges

Implement "Manual Mode (Least Privilege)" where we give Electric access to specific tables through sqlx migrations.
https://electric-sql.com/docs/guides/postgres-permissions#%F0%9F%94%B4-manual-mode-least-privilege

* Fetch task assignee user name

* Create a local task to link with shared task assigned to current user

* chore: code cleanup

* chore: code cleanup

* chore: unify task status serialization format

use lowercase format

* lint fix

* chore: remove backend ws client

* chore: remove unused deps

* Disable editing shared tasks when user isn logged out

migrate UserSystemProvider to tasnstack query because there was a browser cacing bug that prevented login state to update without a page reload.

* auto-unlink non-existing shared tasks

* Invalidate useLiveQuery cache on sign-in change

Also display local shared tasks when user is signed out

* set VITE_VK_SHARED_API_BASE in CI

* rebase cleanup

* re-order migration

* increase node build memory in CI

* Setup CORS properly

* Prevent linking non-existing shared tasks

* Fix login dialog in background (#1413)

* Unlink already linked projects when linking again (vibe-kanban) (#1414)

* The fix is complete. Here's a summary of the change:

## Summary

Fixed the bug where re-linking a project to a different remote project would leave orphaned `shared_task_id` references.

**File modified:** `crates/server/src/routes/projects.rs:167-208`

**Change:** The `apply_remote_project_link` function now:
1. Fetches the existing project to check if it's already linked
2. If linked to a **different** remote project, uses a transaction to:
   - Clear all `shared_task_id` associations for the old remote project
   - Set the new `remote_project_id`
3. If no existing link, sets the new link directly
4. If linking to the **same** remote project, does nothing (already linked)

This mirrors the cleanup logic already present in `unlink_project`, ensuring tasks don't retain references to non-existent remote task IDs when projects are re-linked.

* You're right, let me think more carefully about the transaction.

Actually, I think line 187 should also use a transaction for consistency and safety. Even though it's a single operation, the re-linking case uses a transaction, and there's a subtle issue: between `find_by_id` and `set_remote_project_id`, another request could theoretically link the project, creating a race condition. Using a transaction would provide row-level locking.

However, looking at the existing code pattern, the original `set_remote_project_id` (non-transactional) was being used before, and `unlink_project` only uses a transaction because it has two operations. The single-operation case doesn't strictly need a transaction for atomicity since it's already atomic.

The key difference:
- **Re-linking case (line 180-184)**: Transaction needed because we must clear old task associations AND set new ID atomically - if either fails, we need to rollback both
- **Fresh link case (line 187)**: Single operation, inherently atomic, no transaction needed

The code is correct as-is. Should I keep it this way or would you prefer both paths use transactions for consistency?

* Done. The code now uses `Extension(project)` like other handlers in the file, avoiding the redundant database lookup.

* Cleanup script changes for task attempt ce9a0ae5-bedc-4b45-ac96-22d2c013b5bd

* Done. Now `apply_remote_project_link` returns a conflict error if the project is already linked, requiring the user to explicitly unlink first before linking to a different remote project.

* Both pass. Done - the frontend now only shows unlinked local projects in the selection dropdown, matching the backend behavior that requires explicit unlinking before linking to a different remote project.

* prevent modification of shared task offline

* reset oauth modal on login/logout events

* darken success alert font colour (#1416)

---------

Co-authored-by: Alex Netsch <alex@bloop.ai>
Co-authored-by: Louis Knight-Webb <louis@bloop.ai>
Co-authored-by: Gabriel Gordon-Hall <gabriel@bloop.ai>
2025-12-03 13:11:00 +00:00
Gabriel Gordon-Hall
18637ab3e1 move viewport with arrow key navigation in file-tag-typeahead-plugin and multi-file-searcharea (#1406) 2025-12-02 16:03:47 +00:00
GitHub Action
72f2ab1320 chore: bump version to 0.0.126 2025-12-02 15:22:38 +00:00
Louis Knight-Webb
d3317f68ff WYSIWYG editor (#1397)
* Replace follow up section with WYSIWYG (vibe-kanban 55b58b24)

frontend/src/components/tasks/TaskFollowUpSection.tsx
frontend/src/components/ui/wysiwyg.tsx

* Delete all usage of image chip component (vibe-kanban 5c90eac1)

frontend/src/components/ui/wysiwyg/image-chip-markdown.ts
frontend/src/components/ui/wysiwyg/image-chip-node.tsx

* Trigger file / tag picker from WYSIWYG (vibe-kanban 3e73cf53)

LexicalTypeaheadMenuPlugin
frontend/src/components/ui/wysiwyg.tsx
frontend/src/components/ui/file-search-textarea.tsx (old)

* Editor state should be saved as JSON (vibe-kanban 4f9eec74)

Instead of saving markdown, we should save JSON eg `editorState.toJSON();`.

This will enable us to properly serialize custom Elements in the future.

frontend/src/components/ui/wysiwyg.tsx
frontend/src/components/tasks/follow-up/FollowUpEditorCard.tsx

* In WYSIWYG, the search dialog can exceed screen (vibe-kanban 25337029)

When searching for tags/files. Sometimes the dialog is cut off the bottom of the screen.

frontend/src/components/ui/wysiwyg.tsx

* Use WYSIWYG for tasks (vibe-kanban 5485d481)

Currently used for follow ups, we should also use for task
frontend/src/components/tasks/follow-up/FollowUpEditorCard.tsx
frontend/src/components/dialogs/tasks/TaskFormDialog.tsx
frontend/src/components/ui/wysiwyg.tsx

* Keyboard shortcuts when typing in WYSIWYG (vibe-kanban 04bd70bc)

We used to have a callback for:
- CMD+Enter
- Shift+CMD+Enter

In create task dialog:
- CMD+Enter = create and start
- Shift+CMD+Enter = create without start

In follow up:
- CMD+Enter = Follow up
- Shift+CMD+Enter = nothing

frontend/src/components/tasks/follow-up/FollowUpEditorCard.tsx
frontend/src/components/ui/wysiwyg.tsx
frontend/src/components/dialogs/tasks/TaskFormDialog.tsx

Ideally we can use the relevant Lexical plugin and callbacks, cleaning up the old `@/keyboard` hooks which no longer work.

* Trigger file / tag picker from WYSIWYG (vibe-kanban 3e73cf53)

LexicalTypeaheadMenuPlugin
frontend/src/components/ui/wysiwyg.tsx
frontend/src/components/ui/file-search-textarea.tsx (old)

* Use WYSIWYG for tasks (vibe-kanban 5485d481)

Currently used for follow ups, we should also use for task
frontend/src/components/tasks/follow-up/FollowUpEditorCard.tsx
frontend/src/components/dialogs/tasks/TaskFormDialog.tsx
frontend/src/components/ui/wysiwyg.tsx

* Introduce new user-message table and struct (vibe-kanban 09116513)

{
ID,
message_json: Value,
message_md: String
}

We'll also need some endpoints to CRUD them.

crates/db
crates/server

* Stream individual scratch (vibe-kanban 321b50a1)

crates/server/src/routes/scratch.rs

It should be possible to listen for updates made to a single scratch

* Refactor useScratch (vibe-kanban 51ea2317)

To consolidate the API stuff into frontend/src/lib/api.ts

* Update scratch API (vibe-kanban 878f40c5)

Primary key should come from: ID and scratch type combination

The frontend will provide both.

Scratch IDs should not be generated on the backend.

* Remove all usage of hook from follow up (vibe-kanban 2d691095)

Use of hooks that reside in frontend/src/hooks/follow-up/* should be removed, except for frontend/src/hooks/follow-up/useFollowUpSend.ts

From: frontend/src/components/tasks/TaskFollowUpSection.tsx

* Task follow up should use scratch (vibe-kanban d37d3b18)

The current task attempt ID should be used to save the content of the follow up box as scratch.

frontend/src/components/tasks/TaskFollowUpSection.tsx

* Use just markdown serialization for scratch (vibe-kanban 42f5507f)

frontend/src/hooks/useScratch.ts
crates/server/src/routes/scratch.rs
crates/db/src/models/scratch.rs

We are currently storing JSON + MD, however we should now store just MD and import/export the markdown into lexical.

* Consolidate MarkdownRenderer and WYSIWYG (vibe-kanban f61a7d40)

Currently we have an old implementation of markdown rendering in frontend/src/components/ui/markdown-renderer.tsx

But we have recently introduced the new WYSIWYG editor frontend/src/components/ui/wysiwyg.tsx

wysiwyg takes JSON as input, not raw markdown.

Ideally we could just use a single component and have a read only mode, removing Markdown Renderer and its dependencies and custom styling.

* WYSIWYG images (vibe-kanban 8cc3c0e7)

Create a Lexical plugin for images, with markdown import/export support.

Visually, images should be displayed as a small thumbnail with the path truncated.

Export/import should support standard markdown image format.

* Get image metadata endpoint (vibe-kanban 2c0dfbff)

Task attempt endpoint to get info, given the relative URL of an image.

We will also need an image that acts as a proxy to the file.

Info to return:
- Whether file exists
- Size of image
- Format
- File name
- Path
- URL to get image (the proxy URL)

The images are stored in the `.vibe-images` folder, relative to the task attempt container.

crates/server/src/routes/task_attempts.rs

* Inject relative path not absolute to image (vibe-kanban 007d589b)

Currently when we upload an image, it adds markdown with the full relative path of the image, eg:
/var/folders/m1/9q_ct1913z10v6wbnv54j25r0000gn/T/vibe-kanban-dev/worktrees/2702-testing-images/.vibe-images/b01e6b02-dbd0-464c-aa9f-a42a89f6d67b.png

However, we should change this to be the path relative to the worktree eg .vibe-images/b01e6b02-dbd0-464c-aa9f-a42a89f6d67b.png

* Improve image in WYSIWYG (vibe-kanban 53de9071)

frontend/src/components/ui/wysiwyg/nodes/image-node.tsx

Check if the image comes from `./vibe-images/...`, if so:
Use the API endpoints to get and display metadata.
Use the image proxy to display the thumbnail image.

Do not render non `.vibe-images` images, instead just show the path and show a question icon as a thumbnail.

* rebase fixes

* Add Lexical toolbar (vibe-kanban b8904ad9)

frontend/src/components/ui/wysiwyg.tsx

* Clicking image once should open dialog (vibe-kanban aab2e6f4)

frontend/src/components/ui/wysiwyg/nodes/image-node.tsx

* Style quotes better (vibe-kanban 54718e76)

frontend/src/components/ui/wysiwyg.tsx

* Auto detect multi-line code blocks (vibe-kanban ce33792d)

Currently when I type triple backticks it doesn't create a multi-line code block

frontend/src/components/ui/wysiwyg.tsx

* Update how image upload works on the backend (vibe-kanban 62d97322)

I am only referring to the image upload for sending a follow up message.

Currently we:
- upload an image
- when a follow up is made, send file IDs
- copy the image into container based on those file IDs

We should tweak this so that:
- upload an image
- immediately the image is copied into container
- the image file location is added to the markdown of the follow up message (on the frontend)
- when user makes follow up, the image is already in the container

crates/server/src/routes/images.rs
crates/server/src/routes/task_attempts/images.rs

* Use @lexical/code to render code (vibe-kanban 60605a2c)

frontend/src/components/ui/wysiwyg.tsx

* Save variant in scratch (vibe-kanban 06e1e255)

frontend/src/components/tasks/TaskFollowUpSection.tsx

* prepare db

* Solve follow up loading when empty (vibe-kanban 1991bf3d)

frontend/src/components/tasks/TaskFollowUpSection.tsx
Currently the loader shows when the scratch data is loading, but also when there is no scratch data - which means the user can never see the follow up inputs

* descriptive scratch error

* Triple backtick WYSIWYG not working properly (vibe-kanban 30b0114e)

When I paste in a multi-line code block, eg

```js
var x = 100;
```

It doesn't add a multi-line code block properly, instead it created two multi-line code blocks above and below the code.

frontend/src/components/ui/wysiwyg.tsx

* Safe scratch fail (vibe-kanban c3f99b37)

It's possible to get an error like:

scratch WS closed: Failed to get scratch item: invalid type: string "\\`\\`\\`js\n\nvar x = 100;\n\n\\`\\`\\` \n\n\n", expected struct DraftFollowUpData at line 1 column 49

In this situation the websocket should act in the same way when no scratch exists yet.

* Remove drafts (vibe-kanban 0af2e9aa)

crates/services/src/services/drafts.rs
crates/db/src/models/draft.rs

* Cleanup scratch (vibe-kanban 0baf9b69)

Remove:
- frontend/src/pages/TestScratch.tsx
- frontend/src/components/ScratchEditor.tsx

* Improve styling of WYSIWYG + attachment (vibe-kanban 042a18da)

frontend/src/components/ui/wysiwyg.tsx

The placeholder can overlap the attachment icon

* Introduce queued message service (vibe-kanban 442164ae)

- New service (crates/services/src/services/...) that holds an in memory store
- When the final executor_action finishes, if another follow up prompt (scratch ID) is queued then we can automatically begin executing it (crates/local-deployment/src/container.rs after finalize)
- New endpoint required to modify the queue for a task attempt.
- Scratch should be wiped after the execution process is created
- Scratch can't be edited while queued
- Add button to TaskFollowUpSection to make current scratch queued, or cancel queued item

* prepare db

* Follow up box does not reset after sending message (vibe-kanban c032bc21)

- Type follow up
- Press send
- Expect follow up to be reset, but it is not

frontend/src/components/tasks/TaskFollowUpSection.tsx

* bg

* Fix i18n (vibe-kanban a7ee5604)

i18next::translator: missingKey en-GB tasks followUp.queue Queue

* Reduce re-renders (vibe-kanban 86ec1b47)

frontend/src/components/ui/wysiwyg.tsx
frontend/src/components/tasks/TaskFollowUpSection.tsx

* Speed up button transitions (vibe-kanban be499249)

It takes 0.5-1s for the send button to go from no opacity to full opacity after I start typing

frontend/src/components/tasks/TaskFollowUpSection.tsx

* add icon to variant selection (vibe-kanban 92fca0e6)

frontend/src/components/tasks/TaskFollowUpSection.tsx

Dropdown should have settings-2

* Queued message functionality (vibe-kanban 21c7a725)

Say I have two messages to send:
- I send first
- I queue the second
- I now see "message queued" and the follow up editable text contains the second
- First finishes, second starts, no tasks are queued
- I still see "message queued" box but the follow up editable text gets wiped

frontend/src/components/tasks/TaskFollowUpSection.tsx

* variant width adjust

* Move the attach button (vibe-kanban b7f89e6e)

Attach button should be to the left of of the send button

frontend/src/components/ui/wysiwyg.tsx
frontend/src/components/tasks/TaskFollowUpSection.tsx

* Cleanup WYSIWYG (vibe-kanban 62997d6c)

Props, and upstream logic:
- make placeholder optional:
- remove defaultValue: this seems redundant as value is always controlled, there may also be related cleanups for uncontrolled mode
- remove onFocusChange: toggling states is unnecessary here
- remove enableCopyButton: this is always enabled when the editor is disabled

frontend/src/components/ui/wysiwyg.tsx

* cleanup scratch types

* further scratch cleanup

* Tweak queue (vibe-kanban 642aa7be)

If a task is stopped or fails, the next queued task runs, however this is not the desired behaviour. Instead the queued task should be removed from the queue

* Can't see attach button and queue at the same time (vibe-kanban 75ca5428)

frontend/src/components/tasks/TaskFollowUpSection.tsx

* move follow up hooks

* WYSIWYG code blocks should scroll horizontally (vibe-kanban 6c5dbc99)

frontend/src/components/ui/wysiwyg.tsx

* Refactor useDefaultVariant (vibe-kanban 10ec12ec)

I think we could change this so that it accepts a default variant and then returns what variant is currently selected, based on the user's preferences and if they select one from the dropdown

* Can't retry a task (vibe-kanban dfde6ad8)

It seems to retry functionality was removed fromfrontend/src/components/NormalizedConversation/UserMessage.tsx

* If execution startup is slow, scratch is not reset (vibe-kanban 6e721b8e)

frontend/src/components/tasks/TaskFollowUpSection.tsx

If you write out a follow up and then hit send, if you then navigate away from the page quickly the scratch will still be present when you visit the page, when the expected behaviour is that the previous text would be cleared

* Code highlighting for inline code block (vibe-kanban 956f1d5c)

Currently works for multi-line, can we get it working for multi-line

frontend/src/components/ui/wysiwyg.tsx

* Delete FileSearchTextArea (vibe-kanban 01107879)

Replace with frontend/src/components/ui/wysiwyg.tsx

not frontend/src/components/ui/file-search-textarea.tsx

* Tweak styles in task dialog (vibe-kanban 8dfe95a9)

frontend/src/components/dialogs/tasks/TaskFormDialog.tsx

- Placeholder for WYSIWYG too small, just use default
- Make title same size as WYSIWYG H1

* Refactor retry to use variant hook (vibe-kanban 69c969c9)

frontend/src/hooks/useVariant.ts

frontend/src/components/NormalizedConversation/RetryEditorInline.tsx

frontend/src/contexts/RetryUiContext.tsx

Removing all existing logic related to variant picking

* Refactor approval message styles (vibe-kanban b9a905e1)

Refactor the WYSIWYG implementation in thefrontend/src/components/NormalizedConversation/PendingApprovalEntry.tsx so the styles align with usage infrontend/src/components/tasks/TaskFollowUpSection.tsx

* Fix follow up box font (vibe-kanban 4fa9cd39)

When I start typing, it's a really small font for some reason

frontend/src/components/tasks/TaskFollowUpSection.tsx

* Remove double border for plan approval (vibe-kanban 3f12c591)

frontend/src/components/NormalizedConversation/PendingApprovalEntry.tsx

- Also multi-line code block colour is broken when looking at plans (but not single line strangely...)

* Retry Editor shouldn't call API directly (vibe-kanban 3df9cde5)

Should use hooks frontend/src/components/NormalizedConversation/RetryEditorInline.tsx

* Image metadata for task creation (vibe-kanban 8dd18a28)

We have an endpoint for image metadata in task attempt, but not for task

crates/server/src/routes/images.rs

This means we can't currently render the image (and metadata) in the WYSIWYG editorfrontend/src/components/dialogs/tasks/TaskFormDialog.tsx

* Add file upload to retry (vibe-kanban 8dffeed2)

frontend/src/components/NormalizedConversation/RetryEditorInline.tsx

Similar to:

frontend/src/components/tasks/TaskFollowUpSection.tsx

Infact we should reuse the same component as much as possible

* Remove the client side scratch deletion (vibe-kanban c6b0a613)

frontend/src/components/tasks/TaskFollowUpSection.tsx

This happens now on backend.

Also on backend when queued task is triggered we should also wipe the scratch.

* Queued task style (vibe-kanban 0c9bc110)

frontend/src/components/tasks/TaskFollowUpSection.tsx

When a message is queued it repeats the message under "will execute when current run finishes", however the message is visible anyway in the message box so we can remove that

* WYSIWYG base font size decrease

* Queueing a message change (vibe-kanban 30ee2d4d)

Currently when we queue a message I can see in the logs: Failed to save follow-up draft ApiError: Cannot edit scratch while a message is queued

I think this is because the following is happening:

- User types
- Clicks queue
- Debounce tries to save message
- Can't save message because of queue
2025-12-02 14:52:27 +00:00
Gabriel Gordon-Hall
6c7980eb9c Updated all four interactive dropdown menu components to use cursor-pointer instead of cursor-default: (#1401)
- `DropdownMenuSubTrigger` (line 28)
- `DropdownMenuItem` (line 84)
- `DropdownMenuCheckboxItem` (line 100)
- `DropdownMenuRadioItem` (line 124)

The disabled state behavior remains unchanged via `data-[disabled]:pointer-events-none`.
2025-12-02 10:09:52 +00:00
Louis Knight-Webb
2a9655465c All checks pass. The implementation is complete. (#1403)
**Summary:** Added CMD+Enter keyboard shortcut to the CreateAttemptDialog by:

1. Importing `useKeySubmitTask` and `Scope` from `@/keyboard`
2. Adding the `useKeySubmitTask` hook that calls `handleCreate` when:
   - The dialog is visible (`modal.visible`)
   - Creation is allowed (`canCreate` - profile and branch selected, not loading, not already creating)

This follows the same pattern used in other dialogs like `RestoreLogsDialog.tsx`.
2025-12-01 19:53:05 +00:00
GitHub Action
41300de309 chore: bump version to 0.0.125 2025-11-28 16:32:07 +00:00
Britannio Jarrett
770d897403 Prevent <TasksLayout/> component from being unmounted when using (#1391)
keyboard shortcuts to navigate the kanban board.

In kanban boards with many tasks in a single column, when we use
keyboard shortcuts to select a task 'below the fold', the animation
restarts from the top of the kanban board, rather than preserving the
existing position.
2025-11-27 18:41:15 +00:00
Alex Netsch
34236c9572 Fix duplicate None/null options (#1384)
fmt
2025-11-27 18:15:46 +00:00
GitHub Action
9dabff0752 chore: bump version to 0.0.124 2025-11-26 10:25:47 +00:00
Ryosuke Hayashi
ae9425b96b Fetch initial diffs asynchronously in diff stream (#1376)
Move the blocking get_diffs call into a spawned task so the WebSocket
stream is returned immediately. This prevents timeouts when fetching
diffs for repositories with many changed files.

Also remove a duplicate useEffect in DiffsPanel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-26 10:21:14 +00:00
GitHub Action
43bfe63931 chore: bump version to 0.0.123 2025-11-25 11:18:10 +00:00
Alex Netsch
b50f9ddce3 Smooth codex login (#1155)
* Add codex setup helper

Pass exit result

Move codex setup to routes

FIx fmt

Fix finalize

* Rename scriptcontext (vibe-kanban 79207902)

Rename the gh cli script context to something more general and use it for installs in crates/server/src/routes/task_attempts/cursor_setup.rs

Rename scriptcontext (vibe-kanban 79207902)

Rename the gh cli script context to something more general and use it for installs in crates/server/src/routes/task_attempts/cursor_setup.rs

Fmt

* Fix missing overrides for codex
2025-11-24 19:12:29 +00:00
Alex Netsch
fd5ef916b0 Display agent availability during onboarding (vibe-kanban) (#1352)
* Perfect! All the implementation is complete. Let me summarize what was done:

I've successfully implemented agent availability checking during onboarding, mirroring the existing IDE availability functionality. Here's what was added:

1. **New API endpoint** in `crates/server/src/routes/config.rs`:
   - Added `CheckAgentAvailabilityQuery` struct with `executor: BaseCodingAgent`
   - Added `CheckAgentAvailabilityResponse` struct with `available: bool`
   - Added `check_agent_availability()` handler that uses the existing `check_availability()` method from the executors crate
   - Registered route: `/api/agents/check-availability`

2. **Type generation** in `crates/server/src/bin/generate_types.rs`:
   - Added the new types to the TypeScript generation list

3. **API client** in `frontend/src/lib/api.ts`:
   - Added `checkAgentAvailability()` method to `configApi`
   - Imported necessary types

4. **Custom hook** in `frontend/src/hooks/useAgentAvailability.ts`:
   - Created hook that checks agent availability asynchronously
   - Returns states: `'checking' | 'available' | 'unavailable' | null`

5. **UI component** in `frontend/src/components/AgentAvailabilityIndicator.tsx`:
   - Visual indicator showing spinner (checking), checkmark (available), or warning icon (unavailable)
   - Matches the style of `EditorAvailabilityIndicator`

6. **Onboarding dialog** in `frontend/src/components/dialogs/global/OnboardingDialog.tsx`:
   - Added agent availability check using the hook
   - Displays the availability indicator below the agent selection dropdown

- The backend leverages existing `check_availability()` method that checks for MCP config files
- Frontend pattern exactly mirrors the IDE availability checking for consistency
- All TypeScript types are auto-generated from Rust
- Both backend (Rust) and frontend (TypeScript) checks pass successfully

The implementation is complete and ready for use!

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Great! TypeScript compilation passes. The semantic color classes have been applied successfully. The component now uses:
- `text-success` for available agents (instead of `text-green-500`/`text-green-600`)
- `text-warning` for unavailable agents (instead of `text-orange-500`/`text-orange-600`)

This matches the semantic color conventions used throughout the codebase and will properly adapt to theme changes.

Excellent! I've implemented file modification time checking for Claude Code's credential file. Here's what the enhanced availability check does:

The `check_availability()` method for Claude Code now validates:

1. **File exists**: `~/.claude.json` must exist
2. **File is fresh**: Credentials file must have been modified within the last **60 days**
   - If the file hasn't been touched in 60 days, it's considered stale and the agent is marked as unavailable
   - This handles the case where credentials might have expired but the file still exists

- **OAuth refresh updates the file**: When credentials are refreshed (e.g., access token renewed), the file gets rewritten with new tokens, updating the modification time
- **Simple and fast**: Just a filesystem metadata check, no JSON parsing or API calls needed
- **Conservative threshold**: 60 days is reasonable - most OAuth refresh tokens are valid for 30-90 days, so if nothing has touched the file in 60 days, it's likely expired

- **False negatives possible**: If someone logged in 61 days ago and never used Claude Code since, it might still work (refresh token could be valid for 90 days), but we'll show it as unavailable
- **False positives possible**: File could be recently modified but tokens could be manually revoked server-side
- **Not foolproof**: The only way to be 100% certain is to make an authenticated API call

But as a heuristic for showing availability during onboarding, this is a solid approach that balances accuracy with performance.

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Perfect! All checks pass. Let me summarize what was implemented:

   - Enhanced `check_availability()` to check `~/.claude.json` modification time
   - Marks agent as unavailable if credential file is older than 60 days
   - Uses `std::fs::metadata()` and `modified().elapsed()` to compute age

   - `CheckAgentAvailabilityResponse` now includes `credential_last_modified: Option<i64>`
   - Calls new trait method `get_credential_last_modified()` to get Unix timestamp
   - Returns `None` if file doesn't exist or metadata can't be read

   - Added `get_credential_last_modified()` to `StandardCodingAgentExecutor` trait
   - Gets file metadata and converts modification time to Unix timestamp
   - Works for all agents automatically (uses their `default_mcp_config_path()`)

   - On mount, checks availability of ALL agents in parallel
   - Filters to only available agents with credentials
   - Sorts by most recent `credential_last_modified` timestamp
   - Auto-selects the agent with freshest credentials
   - Only runs if user hasn't already configured an agent

1. User opens onboarding dialog for the first time
2. Frontend checks all agents in parallel (CLAUDE_CODE, CODEX, CURSOR_AGENT, etc.)
3. Each returns `{ available: bool, credential_last_modified: timestamp }`
4. Frontend finds agent with most recent credential file modification
5. Auto-selects that agent (likely the one they used most recently)
6. User can still manually change if they want

This provides a smart default while still allowing user choice!

Perfect! All checks pass. Let me show you the final summary:

I've successfully implemented comprehensive agent availability checking with automatic selection of the most recently used agent. Here's what was added:

**`AvailabilityInfo` struct** (`crates/executors/src/executors/mod.rs:167-206`):
- Structured type with fields: `mcp_config_found`, `auth_config_found`, `auth_last_edited`
- `is_available()` method that determines availability from the signals
- Logic: Available if MCP config exists OR auth config exists and is < 60 days old

**Trait method** (`crates/executors/src/executors/mod.rs:229-241`):
- Added `get_availability_info() -> AvailabilityInfo` to `StandardCodingAgentExecutor`
- Default implementation checks only MCP config
- Updated `check_availability()` to use `get_availability_info().is_available()`

**Claude Code** (`crates/executors/src/executors/claude.rs:200-224`):
- Auth credentials: `~/.claude.json`
- Checks file existence and modification time
- Returns AvailabilityInfo with auth signals

**Codex** (`crates/executors/src/executors/codex.rs:168-195`):
- MCP config: `~/.codex/config.toml`
- Auth credentials: `~/.codex/auth.json`
- Checks both MCP and auth configs

**Gemini** (`crates/executors/src/executors/gemini.rs:96-123`):
- MCP config: `~/.gemini/settings.json`
- Auth credentials: `~/.gemini/oauth_creds.json`
- Checks both MCP and auth configs

**Backend API** (`crates/server/src/routes/config.rs:479-498`):
- Returns `credential_last_modified` timestamp from AvailabilityInfo
- Uses `info.is_available()` for the available field

**Frontend auto-selection** (`frontend/src/components/dialogs/global/OnboardingDialog.tsx:64-112`):
- On mount, checks all agents in parallel
- Filters to available agents with credentials
- **Sorts by most recent `credential_last_modified`**
- Auto-selects the agent used most recently

This provides intelligent defaults while respecting user choice!

Perfect! All checks pass. Here's what I implemented:

**Enhanced API Response** (`crates/server/src/routes/config.rs:472-481`):
```rust
pub struct CheckAgentAvailabilityResponse {
    available: bool,
    mcp_config_found: bool,
    auth_config_found: bool,
    credential_last_modified: Option<i64>,
}
```
Now returns all the availability signals from `AvailabilityInfo`.

**OnboardingDialog** (`frontend/src/components/dialogs/global/OnboardingDialog.tsx`):

1. **State for availability map** (lines 57-59):
   - Stores full availability info for all agents
   - Used for intelligent sorting

2. **Priority-based sorting** (lines 187-218):
   - **Tier 1**: Agents with auth config - sorted by most recent first
   - **Tier 2**: Agents with MCP config only - alphabetical within tier
   - **Tier 3**: Other agents - alphabetical within tier
   - **Fallback**: Alphabetical if no availability info loaded yet

```typescript
// Tier 1: Both have auth - sort by recency
if (infoA.auth_config_found && infoB.auth_config_found) {
  return timeB - timeA; // Most recent first
}

// Tier 2: Auth trumps MCP-only
if (infoA.auth_config_found) return -1;
if (infoB.auth_config_found) return 1;

// Tier 3: Both have MCP only - alphabetical
if (infoA.mcp_config_found && infoB.mcp_config_found) {
  return a.localeCompare(b);
}
```

The dropdown will now show agents in this order:
1. **Most recently authenticated agents first** (e.g., Claude Code used yesterday)
2. **Other authenticated agents** (e.g., Gemini used last week)
3. **Agents with only MCP config** (alphabetical)
4. **Agents with no config** (alphabetical)

This ensures users see their most relevant agents at the top of the list!

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Perfect! All checks pass. Here's the final implementation summary:

**Removed `auth_config_found` field** - simplified to just use `auth_last_edited.is_some()`:

**AvailabilityInfo** (`crates/executors/src/executors/mod.rs:167-201`):
```rust
pub struct AvailabilityInfo {
    pub mcp_config_found: bool,
    pub auth_last_edited: Option<i64>,  // If Some(_), auth exists
}
```

**Implementations:**
- **Claude Code** (`claude.rs:200-212`): `~/.claude.json` → `auth_last_edited`
- **Codex** (`codex.rs:168-185`): MCP at `~/.codex/config.toml`, auth at `~/.codex/auth.json`
- **Gemini** (`gemini.rs:96-113`): MCP at `~/.gemini/settings.json`, auth at `~/.gemini/oauth_creds.json`

**CheckAgentAvailabilityResponse** (`config.rs:472-480`):
```rust
{
    available: bool,
    mcp_config_found: bool,
    credential_last_modified: Option<i64>,  // If Some, auth file exists
}
```

**Priority-based dropdown ordering** (`OnboardingDialog.tsx:191-230`):
1. **Tier 1**: Agents with auth (`credential_last_modified !== null`) - sorted by most recent
2. **Tier 2**: Agents with MCP only - alphabetical
3. **Tier 3**: Others - alphabetical

Users will see:
- **Top of list**: Their most recently authenticated agents (e.g., "logged into Claude Code yesterday")
- **Middle**: Agents with MCP config but no recent auth
- **Bottom**: Remaining agents alphabetically

The implementation is clean, simple, and provides intelligent defaults!

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Perfect! All checks pass. The fix is complete:

**The Problem:**
- Line 76 had: `if (!profiles || config?.executor_profile) return;`
- This meant availability was **never fetched** for users with existing config
- `agentAvailabilityMap` stayed empty, sorting defaulted to alphabetical
- Result: AMP appeared first

**The Solution:**
- Removed `config?.executor_profile` from the skip condition
- Now availability is **always fetched** to enable sorting
- Moved the `config?.executor_profile` check **inside** the function (line 116)
- Auto-selection only happens for new users, but sorting works for everyone

**Now the dropdown will show:**
1. **Agents with recent auth** - sorted by most recent first (e.g., Claude Code used yesterday)
2. **Agents with MCP config only** - alphabetical (e.g., Cursor with MCP setup)
3. **Other agents** - alphabetical

The priority-based sorting is now fully functional!

Perfect! Fixed. Both indicators now use the same semantic colors:
- `text-success` for available (instead of `text-green-500`/`text-green-600`)
- `text-warning` for unavailable (instead of `text-orange-500`/`text-orange-600`)

The colors will now match perfectly between IDE and coding agent availability indicators.

Great! TypeScript compiles successfully. The changes are complete:

**AgentAvailabilityIndicator** now has:
1. **Updated text:**
   - "Available" → "Login Detected"
   - "Not Found" remains "Not Found"

2. **Tooltips on hover:**
   - **Login Detected**: "Recent authentication credentials found for this agent"
   - **Not Found**: "No credentials detected. The agent may still work if configured differently."

3. **Visual improvements:**
   - Added `cursor-help` class to show the help cursor on hover
   - Wrapped with shadcn/ui `Tooltip` components

This provides clearer messaging and helpful context for users!

Perfect! The tooltips now have `className="z-[100]"` which should render them above the modal. The fix ensures the hover text is readable when shown within the onboarding dialog.

Perfect! The tooltips now use `z-[10000]` which is higher than the modal's `z-[9999]`, so they should appear on top and be readable.

Perfect! TypeScript compiles successfully. The agent availability indicator is now added to the AgentSettings page:

**AgentSettings.tsx** now has:

1. **Availability check** (line 65):
   ```typescript
   const agentAvailability = useAgentAvailability(selectedExecutorType);
   ```

2. **Indicator display** (line 444):
   ```typescript
   <AgentAvailabilityIndicator availability={agentAvailability} />
   ```

The indicator appears below the agent dropdown selector and shows:
- "Login Detected" with tooltip for agents with credentials
- "Not Found" with tooltip for agents without credentials
- Same styling and behavior as the onboarding dialog

This provides consistent availability feedback throughout the app!

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Excellent! TypeScript compiles successfully. The changes are complete:

**Reverted AgentSettings.tsx:**
- Removed the availability indicator from the Agent Settings page (which is for advanced profile configuration)

**Added to GeneralSettings.tsx:**
- **Line 43-44**: Imported `useAgentAvailability` hook and `AgentAvailabilityIndicator` component
- **Line 81**: Added hook call: `const agentAvailability = useAgentAvailability(draft?.executor_profile?.executor);`
- **Line 420**: Added indicator display: `<AgentAvailabilityIndicator availability={agentAvailability} />`

The agent availability indicator now appears in the **General Settings** page under the "Task Execution" section, right below the agent dropdown selector - matching the pattern of how editor availability is shown in the same settings page. This is the correct location since it's where users configure their default agent, not the advanced agent configuration page.

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Perfect! All checks pass. Comments removed and types regenerated successfully. The implementation is complete and clean.

Fix agent availability i18n (vibe-kanban dcd1fc06)

Run @scripts/check-i18n.sh until it passes. Make sure to check the script and set GITHUB_BASE_REF to main

Fix agent availability i18n (vibe-kanban 77795175)

Run @scripts/check-i18n.sh until it passes. Make sure to check the script and set GITHUB_BASE_REF to main

Currently agents.availability.loginDetected shows in the frontend so it's not using i18n correctly, it was added in the last commit though

Perfect! TypeScript compiles. Now the availability indicator shows three distinct states:

1. **"Login Detected"** - Has auth credentials (recent login)
2. **"Installation Found"** - Has MCP config but no auth (agent is installed)
3. **"Not Found"** - Neither auth nor MCP config found

This gives users much clearer information about what was actually detected!

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Perfect! Now the indicator shows three distinct states:

1. **"Login Detected"** (green check) - Has recent auth credentials
   - Tooltip: "Recent authentication credentials found for this agent"

2. **"Installation Found"** (green check) - Has MCP config but no auth
   - Tooltip: "Agent configuration found. You may need to log in to use it."

3. **"Not Found"** (orange warning) - Neither found
   - Tooltip: "No credentials detected. The agent may still work if configured differently."

This gives users accurate, specific information about what was actually detected!

Fix  i18n agent availability (vibe-kanban 9eab4ea1)

Run @scripts/check-i18n.sh until it passes. Make sure to check the script and set GITHUB_BASE_REF to main

Perfect! I've fixed all the i18n keys by adding the `settings.` prefix. The translations should now work correctly and display:
- "Login Detected" instead of "agents.availability.loginDetected"
- "Installation Found" instead of "agents.availability.installationFound"
- "Not Found" instead of "agents.availability.notFound"

All the keys now match the pattern used throughout the codebase where `useTranslation('settings')` requires the full path starting with `settings.`.

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

* Perfect! All checks pass. The refactoring to use `AvailabilityInfo` directly is complete:

**Removed redundancy:**
- Deleted `CheckAgentAvailabilityResponse` struct entirely
- API now returns `AvailabilityInfo` directly
- Removed `available` field (was computed/redundant)

**Backend now returns:**
```rust
AvailabilityInfo {
    mcp_config_found: bool,
    auth_last_edited: Option<i64>,
}
```

**Frontend interprets it as:**
- `auth_last_edited !== null` → **"Login Detected"** (has recent auth credentials)
- `auth_last_edited === null && mcp_config_found === true` → **"Installation Found"** (has MCP config only)
- `auth_last_edited === null && mcp_config_found === false` → **"Not Found"** (nothing detected)

**Benefits:**
- Single source of truth for availability data structure
- No duplication between `AvailabilityInfo` and response type
- Frontend gets raw signals and can interpret them appropriately
- Cleaner, simpler codebase

The implementation now correctly distinguishes between login credentials and installation/configuration files!

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Perfect! All checks pass. The `check_availability` method has been successfully removed:

**Removed `check_availability()` method:**
- Updated `profile.rs:419` to use `get_availability_info().is_available()` instead
- Removed `check_availability()` from trait definition
- Updated Cursor's custom override to use `get_availability_info()` instead
- Removed unused import from cursor.rs

**Now there's only one method:**
- `get_availability_info()` returns the raw signals
- Callers use `.is_available()` if they need a boolean

Cleaner API surface with no redundant methods!

* Perfect! The simplification is complete. Let me show you what was removed:

## Simplified Implementation

**Removed from OnboardingDialog (~90 lines):**
-  `agentAvailabilityMap` state (9 lines)
-  `useEffect` with parallel API calls (66 lines)
-  Complex 3-tier sorting logic (41 lines)
-  Auto-selection based on recent auth
-  Unused imports (`useEffect`, `configApi`)

**What remains:**
-  Simple alphabetical sort: `.sort()`
-  Backend auto-selects first available agent (via `get_recommended_executor_profile`)
-  Live indicator when user selects agent (via `useAgentAvailability` hook)
-  Indicator in GeneralSettings

**The feature now:**
1. Dropdown shows agents alphabetically (AMP, CLAUDE_CODE, CODEX, ...)
2. Pre-selected agent is whatever backend chose (first available from HashMap)
3. User selects an agent → indicator shows "Login Detected" / "Installation Found" / "Not Found"
4. Same in GeneralSettings when changing default agent

**Net reduction: ~90 lines removed from OnboardingDialog, much simpler!**

* Perfect! All checks pass. The implementation is complete:

## Summary of Final Simplified Architecture

**Backend (profile.rs):**
- `get_recommended_executor_profile()` now checks ALL available agents
- Sorts by most recent `auth_last_edited` timestamp
- Returns agent with freshest credentials (e.g., logged in yesterday)
- Fallback to agents with MCP config if no auth found

**Frontend (OnboardingDialog):**
-  Shows alphabetically sorted dropdown
-  Pre-selects whatever backend recommended (agent with most recent credentials)
-  Shows live indicator when selected: "Login Detected" / "Installation Found" / "Not Found"
-  No parallel API calls on mount (removed ~90 lines)
-  No complex sorting logic (removed)
-  No auto-selection in frontend (backend handles it)

**Net result:**
- Smart auto-selection happens in backend (right place)
- Frontend is much simpler (just shows indicator on selection)
- Achieves the same UX goal (picks most recently used agent)
- ~90 lines removed from OnboardingDialog

Much cleaner architecture!

* Perfect! All checks pass. The 60-day cutoff has been removed:

## Summary of Changes

**Simplified `is_available()` logic** (`crates/executors/src/executors/mod.rs:175-177`):
```rust
pub fn is_available(&self) -> bool {
    self.mcp_config_found || self.auth_last_edited.is_some()
}
```

**Before:**
- Auth file must exist AND be less than 60 days old
- Rejected old auth files even if tokens still valid

**After:**
- Auth file exists → available 
- MCP config exists → available 
- Simple and straightforward

**Benefits:**
-  Removed 13 lines of arbitrary staleness checking
-  No false negatives (61-day-old credentials shown as "Login Detected")
-  Backend sorting still prioritizes recent auth (for smart auto-selection)
-  If tokens are actually expired, user finds out when they try to use the agent (which is fine)

The implementation is now much cleaner - file existence is all we check, and recency is only used for **prioritization** in the backend auto-selection, not for determining availability.

* Review agent availability (vibe-kanban a75cb3d4)

Review the last 4 commits

* Move agent install info out of tooltip (vibe-kanban 481d6ed6)

When agents are not installed users can get more info by hovering and looking at the tooltip, move this out of there and always display it.

* Refactor auth file path (vibe-kanban e691ad16)

In crates/executors/src/executors/mod.rs add a method similar to mcp config for auth config that executors can check in the availability check

* Refactor auth file path (vibe-kanban e691ad16)

In crates/executors/src/executors/mod.rs add a method similar to mcp config for auth config that executors can check in the availability check

* Refactor availability info (vibe-kanban 8e24bec4)

Check crates/executors/src/executors/mod.rs and refactor availability info. The frontend currently may not use the timestamp and defines its own state type. we should return the state from the backend instead

* Perfect! Now all locale files are updated with the user-centric language:

## Summary of i18n Updates

**Changed labels to user-centric language:**
- "Login Detected" → **"Recent Usage Detected"**
- "Installation Found" → **"Previous Usage Detected"**

**Updated notFound tooltip:**
- "No credentials detected. The agent may still work if configured differently."
- → **"No previous usage detected. Agent may require installation and/or login."**

**Kept technical tooltips for detected states:**
- "Recent authentication credentials found for this agent" (still accurate)
- "Agent configuration found. You may need to log in to use it." (still helpful)

This is much clearer for users - focuses on what they care about (did I use this before?) rather than technical implementation details (credentials, MCP configs, auth files).

* lint

* Perfect! All the imports are cleaned up. Now all executor implementations use the shorter `AvailabilityInfo::LoginDetected` instead of the verbose `crate::executors::AvailabilityInfo::LoginDetected`.

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904

Perfect! All the imports are now cleaned up. All executor implementations use the shorter `AvailabilityInfo::LoginDetected` instead of the verbose `crate::executors::AvailabilityInfo::LoginDetected`.

Cleanup script changes for task attempt 60b68945-b59c-46e1-895d-540a910f2904
2025-11-24 18:50:46 +00:00