Skip to content

Move goose2#8516

Merged
jamadeo merged 151 commits intomainfrom
move-goose2
Apr 14, 2026
Merged

Move goose2#8516
jamadeo merged 151 commits intomainfrom
move-goose2

Conversation

@jamadeo
Copy link
Copy Markdown
Member

@jamadeo jamadeo commented Apr 13, 2026

Moves block/goose2 into the main goose repo, plus a few small changes to get things running in the new location.

block-open-source Bot and others added 30 commits March 27, 2026 16:44
…ture (#2)

* feat: scaffold Tauri + React app with window dragging and app icon

Sets up the goose2 desktop app with:
- Tauri 2 + React 19 + Vite + Tailwind CSS
- Custom titlebar with drag-to-move and double-click maximize/restore
- Window state persistence via tauri-plugin-window-state
- App icon ported from goose-desktop (cyan/amber yin-yang design)
- Theme system with dark/light/system modes
- Hermit toolchain (node, pnpm, biome, just, lefthook)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add app shell layout with tabs, sidebar, and status bar

Adds the main application layout ported from goose2.0's visual design:
- TabBar with session tabs, home button, and sidebar toggle
- Collapsible sidebar with smooth width transition
- Status bar with model name, token count, and status indicator
- AppShell component wiring everything together

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add settings modal with theme section + quality fixes

Settings:
- Settings modal with sidebar nav (Appearance, General, About)
- Theme selector (Light/Dark/System) wired to ThemeProvider
- Accent color picker and density selector (visual placeholders)
- Open via Cmd+, or Settings button in sidebar

Quality fixes:
- Fix stale state bug in tab close handler
- Fix unreachable useTheme guard, add OS theme change listener
- Improve accessibility (ARIA roles, labels, semantic elements)
- Add prefers-reduced-motion support
- Extract shared Tab type to features/tabs/types.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove references to nonexistent theme-tokens module

The previous commit introduced imports for a theme-tokens module
that doesn't exist. Remove those imports and define the needed
types locally in ThemeProvider.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: wire up accent colors and density, add AGENTS.md

Theming:
- ThemeProvider now manages accent color and density (persisted to localStorage)
- Accent color applied via --color-accent CSS variable on :root
- Density applied via --density-spacing CSS variable (multiplier)
- AppearanceSettings uses ThemeProvider context instead of local state
- Tailwind config maps accent color variable
- Settings modal animations enhanced to match goose2.0 reference
- Component classes updated to use semantic token names

Documentation:
- AGENTS.md captures all project conventions, patterns, and decisions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace ambiguous Tailwind arbitrary durations with config values

Add duration-400 and duration-600 to Tailwind config to avoid
ambiguous class warnings from arbitrary value syntax.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: sidebar minimizes to icon strip instead of hiding

The sidebar now shrinks to a 48px icon-only column when toggled,
instead of disappearing completely. Icons stay visible with tooltips,
labels and section headers are hidden. Smooth width transition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: improve settings button alignment and styling in sidebar

Match goose2.0's footer pattern with proper padding, full-opacity
border, flex alignment, and smooth transition between states.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add home screen with clock, greeting, and hidden status bar

Home screen shows a live digital clock, time-based greeting, and
placeholder chat input. Status bar hides when on the home view
(no active tab), matching goose2.0's clean home experience.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: improve home screen to match goose2.0, add chat view

Home screen: large mono clock with AM/PM, time-based greeting,
goose2.0-style chat input (rounded-2xl, shadow-lg, model badge).
Chat view: input at bottom with scrollable messages area.
Home vs chat differentiated by active tab state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: chat view now fills the full available height

Remove flex from main element so child components can properly
resolve h-full against the block parent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add testing infrastructure (Vitest, Playwright, CI)

Testing:
- Vitest for unit/component tests with jsdom + React Testing Library
- 13 tests across 3 test files (ThemeProvider, cn utility, HomeScreen)
- Playwright for E2E smoke tests (4 tests)
- Test setup with matchMedia mock for jsdom

Tooling:
- Just targets: test, test-watch, test-coverage, test-e2e
- Lefthook pre-push now runs unit tests
- CI gate includes tests

CI/CD:
- GitHub Actions workflow with 4 jobs: lint, test, desktop, rust-lint
- Playwright artifacts uploaded on failure
- Rust caching for faster builds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve all Biome formatting and lint issues

Auto-format all files with Biome. Fix lint issues:
- Add type="button" to all interactive buttons
- Replace span[role=button] with proper button element
- Add dialog role to settings modal
- Biome-ignore for standard accessibility !important pattern

All checks pass: pnpm check, pnpm test, pnpm build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: add src-tauri/target/ to .gitignore

Prevent Rust/Tauri build artifacts from being committed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: exclude Tauri generated schemas from Biome formatting

* chore: update .gitignore for full project coverage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pin CI actions to commit SHAs for Semgrep compliance

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve CI failures and improve lefthook auto-formatting

- Fix dtolnay/rust-toolchain action SHA (was invalid, causing all Rust
  CI jobs to fail)
- Fix Biome folder ignore pattern (remove trailing /**)
- Update lefthook pre-commit to auto-format with biome instead of
  just checking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use vite preview directly with explicit host for E2E server

The Playwright webServer was using `pnpm preview --port 4173` which
invoked vite preview without --host, causing it to bind to localhost
(IPv6 ::1) instead of 127.0.0.1. This caused ERR_CONNECTION_REFUSED
in CI and locally. Switch to npx vite preview with --host 127.0.0.1
and --strictPort to ensure reliable binding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: gracefully handle non-Tauri browser environment

Check for Tauri runtime before calling window APIs so the app
renders correctly in browser-based E2E tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: make E2E smoke tests more robust with longer timeouts

- Add 10s timeout for element visibility (CI can be slow to render)
- Use .first() for model badge to handle multiple matches
- Fix strict mode violation for duplicate text matches

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: smooth transition for status bar show/hide

Status bar now slides and fades in/out with a 300ms transition
instead of appearing/disappearing abruptly when switching views.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use dynamic import for Tauri API to prevent browser crash

Static import of @tauri-apps/api/window fails at module load time
in non-Tauri environments. Use dynamic import() guarded by
__TAURI_INTERNALS__ check so the app renders in Playwright.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use python http.server for E2E tests, start with empty tabs

Switch Playwright webServer from npx vite preview to python3 http.server
for more reliable static file serving in CI. Start app with no tabs
open (home screen by default).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: align E2E setup with Sprout's proven configuration

- Use url instead of port for webServer readiness check
- Remove -b 127.0.0.1 flag from python http.server command
- Add explicit cwd to webServer config
- Use pnpm exec instead of npx for Playwright in CI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove unused @tauri-apps/plugin-opener JS dependency

The Rust-side tauri-plugin-opener is still registered, but the JS
binding package was never imported from frontend code. Removing it
keeps the dependency list honest. Dark mode colors already match
goose2.0 — no changes needed there.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: port floating sidebar from goose2.0

- Rewrite Sidebar.tsx with floating panel design (backdrop blur, rounded corners, shadow)
- Smooth collapse/expand animation (48px ↔ 240px) with staggered label transitions
- Search bar morphs between icon-only and full bar with ⌘K badge
- Nav items with staggered fade-in animation delays
- Collapsible recent chats section
- User avatar footer as settings trigger
- GooseIcon SVG in header (replaces emoji)
- Update AppShell layout: sidebar wrapped in padding div for floating effect
- Add ⌘B keyboard shortcut for sidebar toggle
- All color tokens mapped to goose2 design system

* fix: add missing GooseIcon and GooseLogo files

* fix: remove duplicate sidebar toggle, fix traffic lights on blur

- Remove PanelLeft sidebar toggle from TabBar (sidebar has its own toggle)
- Add solid background behind macOS traffic light area so window controls
  remain visible when the window loses focus
- Clean up unused onSidebarToggle prop from TabBar/AppShell

* fix: make TabBar fully opaque so macOS traffic lights stay visible on blur

The previous fix added a solid background div behind the traffic light
area, but it rendered within the semi-transparent parent context
(bg-background/80 backdrop-blur-sm). The OS compositor still saw
transparency and hid the traffic lights on window blur.

Simply making the TabBar background fully opaque fixes this properly.

* fix: clean up nav items, subtle ⌘K badge, remove backdrop-blur for traffic lights

- Nav items: keep only New Chat, Agents, Skills (removed Tasks, Artifacts, Apps)
- ⌘K badge: lighter text color (foreground-secondary/40), no background
- Sidebar: remove backdrop-blur-xl and make bg fully opaque — fixes macOS
  traffic lights disappearing on window blur

* fix: improve sidebar border contrast in dark mode

In dark mode, border-primary (#3f434b) and background-secondary (#3f434b)
are identical, making sidebar dividers and the search border invisible.

Switched all internal sidebar borders/dividers to border-secondary (#525b68)
which provides clear contrast against the sidebar background in both themes.

* fix: align dark mode palette with goose2.0

Updated dark mode color tokens to match goose2.0's much darker palette:
- background-primary: #22252a → #0d0d0d (near black, was too light)
- background-secondary: #3f434b → #141414 (sidebar, card-like)
- background-tertiary: #474e57 → #262626 (muted surfaces)
- Borders, text, and ring colors adjusted accordingly
- This also fixes the dark mode border visibility issue since
  border-primary (#262626) is now distinct from background-secondary (#141414)

* fix: revert custom traffic light code, rely on Tauri's built-in trafficLightPosition

The custom Rust code that called setHidden:NO on focus events was
interfering with Tauri's native traffic light positioning, causing
them to shift down. Removed the custom code entirely — Tauri's
trafficLightPosition config in tauri.conf.json handles positioning
correctly on its own.
- Add SkillsView and AgentsView stub pages matching goose2.0 layout
  (title left, action button right, search bar, empty state)
- Add MainPanelLayout shared component for page layout
- Add SearchBar shared component for page-level search
- Add page-transition CSS animation (matching goose2.0)
- Add storage path constants (~/.goose/)
- Wire up sidebar nav items (Agents, Skills) with view routing
- Add AppView type and view state management to AppShell
- Sidebar active state uses subtle bg-background-secondary treatment
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
….0 visual match (#7)

* feat: chat experience preparation — UI components, stores, and goose2.0-style chat input

Add chat and agent management UI infrastructure without ACP protocol implementation:
- Rich chat UI: ChatInput (matching goose2.0 design with mode/model/folder pickers),
  MessageBubble, MessageTimeline, StreamingIndicator, ThinkingBlock, ToolCallCard
- Agent/Persona management: PersonaCard, PersonaEditor, PersonaGallery, AgentConfig
- Zustand stores for chat state, agent state, session management
- Rust backend: persona CRUD, session storage, sidecar management commands
- Shared UI primitives: DropdownMenu, Tooltip, Popover (Radix UI)
- Type system for messages (text, images, tool calls, thinking, reasoning)
- Fix Agents page spacing to match Skills page layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: match goose2.0 visual design — theme colors, message styling, and UI polish

- Update theme colors to pure monochrome (remove blue-gray tint) matching goose2.0
- Restyle MessageBubble: remove bubble backgrounds, right-align user messages,
  smaller avatars, dimmed agent text, hover-reveal actions
- Restyle ToolCallCard as compact inline pills with status colors and elapsed time
- Restyle ThinkingBlock with brain icon circle and left-border content indent
- Fix tab close button right padding (px-3 → pl-3 pr-1.5)
- Replace hardcoded "WB" initials with generic User icon in sidebar

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve Rust fmt and clippy dead_code warnings for CI

Allow dead_code on preparation types/methods that will be wired up
when ACP integration lands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use getByPlaceholderText in E2E test for chat input

The placeholder text is an HTML attribute, not visible text content,
so getByText doesn't find it. Switch to getByPlaceholderText.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use CSS selector for placeholder text in E2E smoke test

getByPlaceholderText is not available in this Playwright version.
Use locator with attribute selector instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
… fixes (#8)

* feat: personas, skills, and import/export improvements

- Rename "Agents" to "Personas" across the UI (sidebar, headings, descriptions)
- Load markdown persona files from ~/.goose/agents/ on startup with periodic
  refresh (60s interval + window focus), parsed from YAML frontmatter + body
- Implement persona import/export matching sprout's .persona.json v1 format
  with export from card menu and import via file picker
- Implement "New Skill" creation dialog with kebab-case validation, writing
  SKILL.md files to ~/.goose/skills/{name}/
- Add backend path traversal protection for skill name validation
- Fix persona creation to persist through backend API (was local-only)
- Fix import to refresh from backend instead of creating store duplicates
- Disable delete button for disk-loaded personas in the UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add edit, duplicate, export, and import for skills

- Add update_skill backend command to edit existing SKILL.md files
- Add export_skill command returning sprout-compatible .skill.json format
- Add import_skills command with file validation and name collision handling
- Convert CreateSkillDialog to support edit mode (read-only name, pre-filled fields)
- Add dropdown menu on skill cards with Edit, Duplicate, Export, Delete
- Add Import button with file picker on Skills page
- Duplicate generates unique {name}-copy suffix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: make skills and personas pages fill full height with proper scrolling

Both pages now use flex column layout with h-full to fill the parent
container, and an inner scrollable wrapper with overflow-y-auto for
content overflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: fix cargo fmt and clippy warnings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: delete persistence, confirmation dialogs, and export notifications

- Fix persona deletion to persist via backend API (was local-only)
- Add delete confirmation dialog for both personas and skills
- Show export notification toast with filename after successful export

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: hide delete option for built-in and disk-loaded personas

Instead of showing a disabled delete button, hide it entirely for
personas that can't be deleted (built-in and markdown-loaded).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: update PersonaCard test for hidden delete on built-in personas

The delete menu item is now hidden (not disabled) for built-in and
disk-loaded personas, so the test checks it's absent rather than disabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add comprehensive tests for skills and persona features

- CreateSkillDialog: 17 tests (rendering, name validation, edit mode, form submission)
- SkillsView: 11 tests (rendering, search, card menu, delete confirmation)
- usePersonas hook: 6 tests (loading, CRUD, refresh)
- Agents API: 3 tests (export, import, refresh invoke calls)

Total test count: 100 → 137

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: persona & skill dialog layouts + drag-and-drop on new cards

Restructure PersonaEditor and CreateSkillDialog to use fixed
header/footer with a scrollable middle area so action buttons
stay visible. Add useFileImportZone hook and wire drag-and-drop
file import onto the "New Persona" and "New Skill" cards.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: update SkillsView empty state text assertion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Replace default Tauri icons with Goose app icon

Copied the goose silhouette icon from the goose desktop app into all
Tauri icon slots (macOS, Windows, iOS, Android) so the app displays
the correct brand icon.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add proper padding to app icon for correct dock sizing

The icon was filling the entire canvas edge-to-edge, making it appear
~25% larger than other apps in the dock and Alt+Tab bar. Added ~10%
transparent padding on each side following Apple's icon guidelines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add ACP client integration for multi-agent support

Port the acp-client crate from builderbot and integrate it into the
goose2 Tauri app. This enables connecting to ACP-compatible agents
(Goose, Claude Code, Codex, Pi, Amp) over stdio.

Rust backend:
- acp-client crate: AcpDriver, MessageWriter/Store traits, agent
  discovery, session management with replay, !Send workaround
- services/acp.rs: TauriMessageWriter (streams via events),
  TauriStore (session persistence), AcpService
- commands/acp.rs: discover_acp_providers, acp_send_message

Frontend:
- api/acp.ts: provider discovery + message sending
- useAcpStream.ts: Tauri event listener for streaming responses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: wire ACP chat flow with default Goose agent

- Create default Goose ACP agent on first launch (auto-selected)
- Replace mock chat with real ACP routing in useChat
- Enable useAcpStream in ChatView for streaming responses
- Add 13 tests for ACP streaming + default agent creation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: nested button hydration error and streaming text accumulation

- TabBar: restructure tab as div[role=tab] with span close target to
  eliminate button-inside-button HTML violation
- useAcpStream: accumulate text chunks in a ref before passing to
  updateStreamingText, so the full text is displayed instead of only
  the last chunk
- useChat: remove unused biome suppression comment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve three critical ACP messaging bugs

1. Parameter name mismatch: use snake_case keys in Tauri invoke
   to match Rust function parameter names
2. Premature cleanup: don't clear streamingMessageId in finally
   block — acpSendMessage returns immediately (Rust runs in bg),
   let useAcpStream handle cleanup on acp:done event
3. Timing race: enable useAcpStream when streamingMessageId is set
   (not chatState), so listener is active before events arrive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve three ACP streaming bugs

- Use camelCase params in acp_send_message invoke (Tauri 2 default)
- Always enable useAcpStream listener to avoid missing events
- Clear streamingMessageId on non-abort errors to unstick UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract duplicate findLastIndex to shared utility

Both chatStore.ts and useChat.ts had identical findLastIndex helper
functions. Extracted to src/shared/lib/arrays.ts following the existing
shared/lib pattern (cn.ts).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove binaryPath from AcpProvider, rewrite hook tests

- Remove binaryPath from AcpProvider interface (backend only sends id/label)
- Rewrite useAcpStream tests to mock Tauri listen() and use renderHook()
- Tests now verify actual hook behavior: event dispatch, session filtering,
  text accumulation, cleanup on unmount, enabled flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add markdown rendering and fix UI spacing to match goose-spence

Add MarkdownContent component with syntax highlighting, GFM support,
and copy-to-clipboard on code blocks. Update MessageBubble, ThinkingBlock,
and MessageTimeline to use markdown rendering and proper spacing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct inline code detection for react-markdown v9 and add dark mode prose support

Remove deprecated `inline` prop and detect block code via language class or newline content.
Add `dark:prose-invert` so prose text is readable in dark mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add AcpSessionRegistry for robust process lifecycle management

Prevents dangling `goose acp` processes by tracking running ACP sessions
with CancellationTokens, cancelling all on app exit, and cleaning up
stale session files on startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: fix cargo fmt formatting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The content div overlapped the absolutely-positioned close button,
intercepting click events. Adding z-10 ensures the button stays above
the content layer.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When overflow-y is set to auto, browsers implicitly set overflow-x to
auto as well, creating an unwanted horizontal scrollbar. Explicitly set
overflow-x-hidden on the sidebar nav element.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add projects backend and frontend foundation

- Rust backend: CRUD for projects stored at ~/.goose/projects/{slug}/project.json
- Frontend: API layer, Zustand store, CreateProjectDialog, ProjectsView
- Project model: name, description, prompt, icon, color, preferredProvider/Model, workingDir, useWorktrees
- Tab type extended with optional projectId

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: redesign sidebar with projects + chats hierarchy

Replace the "Recent" section with a goose-spence-style layout:
- Projects section with collapsible groups and nested chats
- Standalone chats section for sessions not in any project
- Remove "Projects" from top nav items (now in sidebar body)
- Extract SidebarProjectsSection to stay under file size limit
- Wire AppShell to pass project data and callbacks to Sidebar

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add [+] button to PROJECTS header in sidebar

- PROJECTS section always visible (even with no projects)
- [+] button on PROJECTS header opens CreateProjectDialog
- Wire onCreateProject through Sidebar → AppShell
- CreateProjectDialog rendered in AppShell, refreshes project list on create

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: simplify project dialog and use color dots

- Remove Description field (Prompt serves as description)
- Hide Icon field (always uses default)
- Remove Preferred Model field
- Replace Provider text input with ACP provider dropdown
- Add Browse button for Working Directory (with Tauri dialog fallback)
- Show color dots instead of emoji icons in sidebar and project cards
- Show prompt preview in project cards instead of description

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: always-visible create project button and 3-dot menus

- Make [+] create project button always visible (not hover-only)
- Match button size/alignment with "New chat" buttons
- Add 3-dot menus (Edit/Archive) to project headers
- Add 3-dot menus (Edit/Archive) to chat items (nested + standalone)
- Menus appear on hover, close on outside click

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: fix alignment, edit project dialog, browse button, menu behavior

- Align [+] create project button with project row [+] buttons (px-1.5)
- Wire "Edit" on project menu to open CreateProjectDialog with project data
- Chat menus now only show "Archive" (removed Edit)
- Install tauri-plugin-dialog for native folder picker (Browse button)
- Add dialog:allow-open to Tauri capabilities
- Fix 3-dot menus staying visible: force opacity when dropdown is open
- Remove unused onEditChat prop

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: always-visible 3-dot menus and tighter header alignment

- Make 3-dot menus always visible at 60% opacity (not hover-dependent)
- Remove mr-1 from PROJECTS header [+] button to reduce right gap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: proper hover behavior for 3-dot menus and header alignment

- Restore hover show/hide for 3-dot menus with open-state override
- When dropdown is open, force trigger button visible (opacity-100)
- When closed, standard group-hover behavior applies
- Fix header [+] alignment: remove container px, use pl on text + mr-1 on button
- Both PROJECTS header [+] and project row [+] now have identical right offset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: auto-expand project when creating a new chat in it

When the active tab changes to one inside a project, automatically
expand that project in the sidebar so the new chat is visible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve pre-push lint issues

- cargo fmt: fix Rust formatting in projects.rs
- clippy: allow too_many_arguments on create_project and update_project
- biome: replace non-null assertions with local variable in Sidebar.tsx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: wire archive actions and add project ordering

- Archive project: deletes from filesystem and refreshes sidebar
- Archive chat: closes the tab and cleans up session
- Add `order` field to ProjectInfo (auto-assigned on create, preserved on update)
- Projects sorted by order instead of alphabetically
- Backwards-compatible: existing projects without order default to 0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: consistent hover show/hide for [+] and 3-dot buttons

Use invisible/visible + opacity combo for both ItemMenu trigger and
[+] new chat button. The invisible class provides reliable show/hide
while opacity handles the visual transition. Both now appear and
disappear in sync on group hover.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: soft-delete archive for projects with restore in settings

- Add archivedAt field to ProjectInfo (null = active, timestamp = archived)
- list_projects filters out archived; new list_archived_projects returns only archived
- archive_project sets archivedAt timestamp; restore_project clears it
- Archive action in sidebar now soft-deletes instead of hard-deleting
- Settings modal shows "Archived Projects" section with Restore and Delete buttons
- Delete permanently option available for true removal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: move archived projects to dedicated Projects tab in settings

Add a "Projects" tab to the settings modal with the FolderKanban icon.
Archived projects section moved from General to its own tab for
cleaner organization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add confirmation dialog for permanent project deletion

Clicking "Delete" on an archived project in Settings now shows a
confirmation dialog before permanently removing the project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The root /src/stores/ and /src/types/ directories contained dead code
that was superseded by feature-level stores and /src/shared/types/.
None of these files were imported anywhere in the active codebase.

Removed:
- src/stores/ (agentConfigStore, chatStore, sessionStore, skillStore)
- src/types/ (chat.ts, index.ts)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: repair broken settings modal layout

The <nav> element with settings navigation buttons was a sibling of the
sidebar div instead of being nested inside it, and the main flex container
was missing its closing tag. This broke the sidebar/content flex layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: restore missing padding wrapper around Settings title

The merge in #15 dropped a wrapper div with px-4 py-4 padding and
entrance animation around the "Settings" heading, causing it to be
smashed against the top-left corner of the sidebar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: consolidate types and stores into feature-sliced architecture

Removed root-level src/types/ and src/stores/ directories that contained
outdated duplicates of types and unused store implementations. Moved unique
config types (AgentConfig, Skill, Provider, Model) to src/shared/types/config.ts.
Relocated stores to their owning features (agentConfigStore → agents,
sessionStore → chat, skillStore → skills). Deduplicated ChatState, TokenState,
and Session types — src/shared/types/chat.ts is now canonical. Updated
AGENTS.md with clear guidelines on where types and stores should live.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: replace mode selector with ACP provider selector in chat input

Replace the placeholder mode selector (agent/ask/plan) with a functional
provider selector that discovers available ACP providers at runtime and
routes messages through the selected provider.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: derive chat agent name from selected provider

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update HomeScreen tests for provider selector

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use flexible placeholder matcher in e2e smoke test

The Home page placeholder now reflects the selected ACP provider name
instead of hardcoded "Goose". Use a starts-with selector so the test
passes regardless of which provider label is shown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…iguation (#19)

* feat: add branch-based dev icon for worktree disambiguation

Add a Swift script that generates a modified .icns file with a blue badge
containing the current branch name. When running in a git worktree, the
`just dev` and `just dev-debug` recipes automatically detect this and
generate a badged icon, making it easy to distinguish between multiple
dev instances in the dock.

- scripts/generate-dev-icon.swift: Generates badged icons with text
  wrapping on hyphens, multi-size iconset generation via iconutil
- justfile: dev and dev-debug recipes now detect worktrees and pass
  icon override config to Tauri via --config JSON merge
- Generated icons go to src-tauri/target/dev-icons/ (already gitignored)

* feat: derive stable dev port from working directory for worktree disambiguation

Each worktree gets a stable, unique port derived from the SHA-256 hash of
the working directory path (range 10000-64999). This prevents port conflicts
when running multiple dev instances simultaneously.

- justfile: dev and dev-debug recipes compute VITE_PORT and pass it via
  --config JSON merge to override devUrl and beforeDevCommand
- vite.config.ts: server port and HMR port read from VITE_PORT env var,
  falling back to 1520 for standalone frontend dev (just dev-frontend)
- Icon config in dev recipe now merges into existing TAURI_CONFIG via
  python3 JSON manipulation instead of replacing it
- Icon config in dev-debug recipe appends a separate --config flag
* feat: use acp-client from builderbot repo as git dependency

Remove the local acp-client crate (crates/acp-client/) and replace it
with a git dependency pointing to block/builderbot on the main branch.

The builderbot version is a strict superset of the local copy — same
public API (AcpDriver, AgentDriver, MessageWriter, Store,
discover_providers) plus remote workspace support, concurrent provider
discovery, structured logging, and graceful shutdown.

Changes:
- Remove 'crates/acp-client' from workspace.members in Cargo.toml
- Replace path dependency with git dependency (block/builderbot, main)
- Delete src-tauri/crates/acp-client/ directory (5 files, ~1640 lines)
- Verified cargo check passes with no code changes needed in goose2

* fix: pin acp-client to rev and clean up config

- Pin acp-client git dependency to rev 5af819e instead of branch=main
  for reproducible builds
- Remove redundant single-member [workspace] table from Cargo.toml
- Remove deprecated baseUrl from tsconfig.json (TS 5.9+ deprecation)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update acp-client pin to latest merge commit

Pin acp-client to rev 17e6098 (builderbot PR #554 merge commit)
to pick up the latest acp crate changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add doctor health-check backend commands

* feat: add doctor health-check UI to preferences dialog

Port the doctor feature frontend from staged into React components that
match goose2's patterns (Tailwind CSS, lucide-react, cn() utility).

New files:
- src/shared/api/doctor.ts: Tauri API bindings for run_doctor and
  run_doctor_fix commands
- src/features/settings/ui/DoctorCheckRow.tsx: Individual check row
  component with status icons, fix command dialog, and external link
  support
- src/features/settings/ui/DoctorSettings.tsx: Doctor panel with
  auto-run on mount, re-run button, copy debug report, and
  tool/agent section grouping

Modified:
- src/features/settings/ui/SettingsModal.tsx: Add Doctor tab with
  Stethoscope icon between Appearance and General nav items

* fix: add right padding to doctor header to avoid overlap with close button

The Re-run and Copy details buttons in the Doctor settings panel were
rendered behind the modal's X close button due to insufficient clearance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: align doctor panel heading and nav position with other settings panels

Remove the inline Stethoscope icon from the Doctor heading to match
the plain h3 style used by all other settings panels. Move the Doctor
nav item to sit between Projects and About in the sidebar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: refresh ACP providers after doctor checks and fixes

After running doctor checks or applying a fix, call discoverAcpProviders()
so newly-installed agents appear in the agent selector without requiring
an app reload. This matches the Staged implementation where
DoctorSettingsPanel.svelte calls refreshProviders() after runChecks().

Add a runChecksAndRefresh wrapper that awaits runChecks() then calls
discoverAcpProviders(), guarded by the existing mountedRef check. Use it
in all three call sites: useEffect on mount, Re-run button onClick, and
the onFixed callback passed to DoctorCheckRow.

* fix: use Tauri opener plugin instead of window.open for external URLs

Replace window.open(url, '_blank') with openUrl() from
@tauri-apps/plugin-opener in DoctorCheckRow. In a Tauri webview,
window.open is unreliable for opening URLs in the system browser.
The opener plugin uses the native OS handler and is already configured
in the Tauri backend (opener:default capability).

- Add @tauri-apps/plugin-opener as a frontend dependency
- Import openUrl from @tauri-apps/plugin-opener
- Replace window.open(check.fixUrl, '_blank') with void openUrl(check.fixUrl)

* fix: use ID-based fix execution instead of passing shell commands over IPC

The frontend previously sent raw shell command strings to the backend's
run_doctor_fix handler, which would execute them directly. This is a
security concern as the IPC boundary should not accept arbitrary commands.

Now the backend is the single source of truth for fix commands. The
frontend sends only a check ID and fix type ("command" or "bridge"),
and the backend looks up the corresponding command from the static
AgentCheckInfo definitions or hardcoded tool check commands. Unknown
IDs or fix types are rejected with an error.

- Add fix_type field to DoctorCheck struct (set alongside fix_command)
- Add lookup_fix_command() that maps check_id + fix_type to a command
- Rewrite run_doctor_fix to accept (check_id, fix_type) instead of a
  raw command string
- Update frontend API and DoctorCheckRow to pass check.id + check.fixType

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve doctor code review comments

- Replace stringly-typed fix_type (Option<String>) with a FixType enum
  on both backend and frontend, making invalid states unrepresentable
  and eliminating the wildcard fallback in lookup_fix_command
- Collapse duplicate "not installed" fallback branches in
  check_agent_install into a single branch that conditionally appends
  the main-binary search output
- Align fix confirmation dialog with existing dialog patterns: add
  role="dialog", aria-modal, aria-label, Escape key handler, and match
  the class ordering used by other confirmation dialogs in the codebase

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: individual chat sessions with persistence and tab management

Add full chat session lifecycle — create, switch, persist, and restore
individual chat sessions backed by ACP. Key capabilities:

- Session persistence: messages saved to per-session directories,
  metadata consolidated in ~/.goose/sessions/metadata.json
- Tab state management: Zustand-based chatSessionStore with tab
  open/close/reorder/activate, persisted to ~/.goose/ui_state.json
- Session restoration: historical messages loaded lazily from backend
  on tab select; tab state restored and validated on app launch
- Sidebar recents: all sessions shown sorted by updatedAt, with
  visual distinction between open and background sessions
- ACP integration: multi-agent support via acp-client, provider
  selector in chat and home views
- UI foundations: skills/personas pages, projects sidebar, settings
  fixes, dev icons, chat components, and visual polish

Also includes earlier PR work: sidebar navigation, ACP client,
Tauri icon, dependency updates, and various bug fixes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: fix Rust formatting in session and UI state commands

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve code review issues in session persistence and UI

- Fix race condition: acquire mutex in get_messages to prevent reading
  partially-written message files during concurrent add_message calls
- Log errors in save_metadata/save_messages instead of silently
  discarding them, aiding diagnosis of disk/serialization failures
- Refactor AppShell mount effect from nested .then() to async IIFE
- Use Map for O(1) session lookups in tab derivation
- Only remove session from local store on successful backend delete
  in archiveSession to avoid stale entries on failure
- Add proper exhaustive deps to ChatView initial message effect,
  guarded by ref to maintain mount-only semantics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update acp-client git dependency from 17e60981 to dbd5bc9c.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add persona picker and @ mention support in chat

Replace the provider-first model with a persona-first interaction pattern.
Users now pick a Persona (with its own system prompt, default provider, and
model) from a dropdown in both the home screen and chat input. Typing @ in
the chat textarea opens an autocomplete to mention any persona inline,
switching context for that message. Provider selection is still available as
a secondary override.

New components:
- PersonaPicker: dropdown grouped by built-in / custom personas
- MentionAutocomplete: floating autocomplete triggered by @ in textarea
- ChatInputToolbar: extracted bottom bar to keep ChatInput under 500 lines
- useMentionDetection: hook for detecting @ mention state in text input

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: wire persona system prompts through to ACP backend and fix mention UX

- Pass persona system_prompt from frontend through Tauri to AcpService
- Prepend persona instructions to user prompt in <persona-instructions> tags
- Add keyboard navigation (arrow keys, Enter/Tab) to @ mention autocomplete
- Fix PersonaPicker description extraction (was using index [1] instead of [0])
- Simplify ChatView handleSend by letting useChat handle system prompt injection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: enhance persona picker with @ mentions on home screen, switch notifications, and per-message persona tracking

Adds @ mention autocomplete to the HomeScreen input so users can invoke personas
before starting a chat. Shows system notifications when switching personas mid-chat,
and tracks which persona generated each assistant message in the timeline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: persist persona selection across tab switches and app restarts

Store the selected persona per session in ui_state.json alongside
the existing tab state. When switching tabs or reopening the app,
the previously selected persona is restored for each chat session.

Changes:
- Backend: add persona_per_session HashMap to UiState with
  serde(default) for backward compatibility
- chatSessionStore: add personaId to ChatSession, save/restore
  persona mapping in persistTabState/loadTabState
- ChatView: save persona changes to session store
- AppShell: pass session's stored personaId to ChatView, store
  personaId when creating new sessions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve 4 bugs found in persona picker audit

1. Error state stuck: acpSendMessage errors now reset chatState to
   "idle" so the user can continue sending messages after failures.

2. Stop button cancels backend: stopGeneration now calls
   acp_cancel_session to terminate the Rust ACP session, preventing
   orphaned streaming events after the UI resets.

3. @ mention persona race condition: when switching personas via @
   mention, the message send is deferred to the next render cycle
   so useChat picks up the new persona's system prompt instead of
   the stale one.

4. Deleted persona fallback: if a persisted personaId references a
   deleted persona, ChatView now falls back to builtin-goose (or
   the first available persona) and updates the session store.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: load personas eagerly at app startup

Personas were only loaded when visiting the Personas page (via the
usePersonas hook in AgentsView). This meant the persona picker in
HomeScreen and ChatInput showed empty until the user navigated to
the Personas page at least once.

Now AppShell loads personas from the backend in its startup effect,
alongside sessions and tab state. This ensures personas are
available immediately in all views.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: fix cargo fmt in acp service

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update tests for persona picker changes and silence clippy

- Update ChatInput tests for new toolbar structure and persona props
- Update HomeScreen tests to mock persona store instead of ACP providers
- Add #[allow(clippy::too_many_arguments)] on send_prompt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: surface ACP session title, model state, and session ID in UI

Wire up new acp-client MessageWriter callbacks (on_session_info_update,
on_model_state_update) to Tauri events and frontend listeners. Session
titles and model names from ACP providers now update the sidebar, tabs,
and status bar reactively. Fallback title generation from the first user
message when the provider doesn't send SessionInfoUpdate. Add session ID
display with copy button to the status bar. Fix doubled streaming text
caused by React StrictMode double-mounting with async listener cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: persist session metadata to backend across app restarts

Extend the backend Session struct with provider_id, persona_id, and
model_name fields. Add a generic update_session Tauri command so the
frontend can write-through any metadata change to disk. The frontend
chatSessionStore now persists title, provider, persona, and model on
every updateSession call and restores them from the backend on load.
Remove the persona-per-session hack from ui_state — persona now lives
on the session itself. Hide token count in status bar when zero.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update e2e test for removed model badge default

The home screen no longer shows a hardcoded "Claude Sonnet 4" text since
model info now comes from ACP. Update the smoke test to check for the
persona picker ("Goose") which is always visible on the home screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove stale model badge e2e test

The model badge test matched a hidden SVG <title>Goose</title> element
instead of visible UI. The underlying feature (static model name on home
screen) no longer exists — model info is now dynamic from ACP. The chat
input placeholder is already covered by an existing test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add archive/unarchive session support to frontend types, API, and store

Replace the delete-based archiveSession with actual archive/unarchive API
calls. Add archivedAt field to Session and ChatSession types, wire up new
Tauri invoke bindings, and update the Zustand store to mark sessions as
archived instead of removing them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add show more/less toggle to sidebar project and recents sections

Show 3 chats by default in both project sections and recents. For ≤8
items, toggle inline expansion. For >8 items, navigate to the full
projects view instead. Follows the goose-spence pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Archive chats and restore them from settings

* Refresh project list after restore

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Use project settings in project chats

* Allow clippy on ACP send command
The @tailwindcss/typography plugin injects backtick characters via
CSS ::before and ::after pseudo-elements on <code> tags. This caused
inline code to display with visible backticks around the highlighted
text. Override with content-none on the prose-code pseudo-elements.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ts (#31)

Always open the app on the Home page instead of restoring the last active
chat. Tabs are still restored in the tab bar so nothing is lost.

Project chats created via the "+" button now start with the title "New Chat"
instead of the project name, allowing the ACP title logic to generate a
proper title from the first message or session info — matching how regular
chats work.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jamadeo jamadeo requested a review from baxen April 13, 2026 19:23
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bede5a5154

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +393 to +395
SessionUpdate::UserMessageChunk(chunk) => {
if let AcpContentBlock::Text(text) = &chunk.content {
// Finalize any in-progress assistant message and count replay event
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Replay image user chunks instead of dropping them

The replay path only handles UserMessageChunk content when it is text, so non-text user blocks are silently discarded during acp_load_session. Since prompt sending explicitly supports image blocks, reloading a session with image attachments loses those attachments (and image-only prompts degrade to empty/placeholder text), which corrupts visible history for that conversation.

Useful? React with 👍 / 👎.

Comment on lines +474 to +475
if let Some(message_id) = replay_msg_id {
let _ = self.app_handle.emit(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Create replay assistant message before tool events

Replay emits tool_call only when replay_message_id already exists, but that ID is created only in the AgentMessageChunk(Text) branch. If a replayed assistant turn starts with tool calls (or contains only tool events), those tool updates are dropped because no message was created yet, so reloaded history is incomplete. Initialize the replay message on first tool event as well.

Useful? React with 👍 / 👎.

jamadeo added 2 commits April 13, 2026 15:27
Adapted from the original goose2 repo CI with paths adjusted for
the monorepo layout (ui/goose2/). Only triggers on changes to
ui/goose2/** to avoid unnecessary runs.

Signed-off-by: Jack Amadeo <jackamadeo@squareup.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de6ddad44d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +218 to +222
store.setChatState(sessionId, "streaming");
// When images are present with no text, pass a single space so the ACP
// driver doesn't send an empty text content block that goose rejects.
const acpPrompt = text.trim() || (images?.length ? " " : text);
await acpSendMessage(sessionId, providerId, acpPrompt, {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip send when stop is requested during session prepare

stopGeneration() can run while acpPrepareSession/acpSetModel are still awaiting, but this path never checks whether the local abort was triggered before transitioning to streaming and dispatching the prompt. In that race (especially on first-send draft sessions), the backend can treat the request as canceled while the UI has already entered streaming, and no matching completion event is guaranteed, leaving the session stuck busy. Add an abort/cancel guard immediately after the prepare/model awaits and return early before setChatState("streaming") + acpSendMessage.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd874dc281

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +188 to +189
title: text.trim().slice(0, 100),
updatedAt: new Date().toISOString(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve default title for image-only first prompts

When the first user turn contains only images, text.trim() is empty, so this path immediately sets the session title to "". Because the title is no longer equal to the default placeholder, the later acp:done fallback that derives a better title is skipped, leaving the chat with a permanently blank title in the sidebar/history. Keep the default title (or use a non-empty fallback) when trimmed text is empty.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff12d2f684

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +449 to +452
if let Some(route) = routes.get_mut(&goose_session_id) {
route.replay_message_id = Some(new_id.clone());
route.replay_events += 1;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Track all replay notifications before marking replay complete

load_session_inner waits for replay completion using get_replay_event_count, but this counter is only incremented when a new replay assistant message is created (route.replay_events += 1) and not for later chunks of that same message. If additional replay text/tool chunks are still queued, the drain check can finish early, finalize_replay emits acp:done, and late chunks are then ignored by the frontend’s completed-message guard, truncating restored history for multi-chunk replies.

Useful? React with 👍 / 👎.

Comment on lines +455 to +458
dispatcher.emit_model_state(local_session_id, None, models);
}
if let Some(options) = &response.config_options {
dispatcher.emit_model_state_from_options(local_session_id, None, options);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve provider identity when loading existing sessions

When replaying an existing session, model-state events are emitted with provider_id: None, so sessions without prior overlay metadata reopen with no provider attached in the UI. The chat view then falls back to a global/default provider, and the next prompt can re-prepare the ACP session with that fallback provider instead of the session’s original one, causing unintended provider switches and potential auth/model failures.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40308baff7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

format!("Failed to build ACP request body: {error}")
})?;
let response_value = connection
.ext_method(ExtRequest::new("goose/providers/list", params.into()))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use _goose namespace for ACP custom requests

This request uses "goose/providers/list", but custom ACP methods in this repo are declared under "_goose/..." (see crates/goose-sdk/src/custom_requests.rs and ui/acp/src/generated/client.gen.ts). Using the unprefixed name can return method-not-found from goose-acp, which breaks provider discovery; the same naming mismatch is also present for session export/import and working-dir update calls, so multiple ACP features can fail at runtime.

Useful? React with 👍 / 👎.

.await
{
Ok(response) => response,
Err(_) => return Ok(None),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate load_session errors instead of silently creating new sessions

This branch converts any load_session failure into Ok(None), and the caller then falls back to new_session. That means transient ACP/RPC failures are treated like “session not found,” silently forking the conversation into a brand-new session and losing continuity with the existing history. Only a real not-found condition should return None; other errors should be surfaced.

Useful? React with 👍 / 👎.

morgmart and others added 2 commits April 13, 2026 17:13
* add edge-case-finder agent skill

Adds a QA-focused skill that analyzes branch changes to systematically
find edge cases, error states, and untested user flow paths in UI code.
Includes a comprehensive checklist reference covering empty states,
error handling, async behavior, boundary conditions, input variations,
navigation, permissions, and accessibility.

* respect the selected change set when reading diffs

The skill asked users to choose between committed and uncommitted
changes but then only used main...HEAD commands. Add explicit
commands for staged and unstaged diffs so the analysis matches
the user's selection.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 491683ba3e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +379 to +381
if let Some(prepared_session) = prepared_session {
let mut guard = state.lock().await;
guard.sessions.insert(composite_key, prepared_session);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register local key when caching a newly prepared session

This stores a newly prepared session only under composite_key, but prepare_session_inner later relies on prepared_session_for_key(..., local_session_id) to reuse the same Goose session across persona-specific composite keys. On the first persona switch in a chat, the local fallback key is missing, so a second Goose session is created and conversation context is split unexpectedly. Use the existing dual-key registration path here as well so subsequent persona keys resolve to the same prepared session.

Useful? React with 👍 / 👎.

Comment on lines +95 to +99
dispatcher.clear_writer(&goose_session_id).await;
clear_cancel_requested(state, &composite_key).await;

if result.is_ok() && !canceled {
writer.finalize().await;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Drain pending prompt events before clearing writer

The writer is cleared immediately after prompt() returns, then acp:done is emitted; however this codebase already documents that ACP notifications are dispatched asynchronously after RPC completion. If late AgentMessageChunk/tool notifications are still queued, they no longer have a live writer and are dropped/misrouted after completion, truncating the assistant response intermittently under scheduler delay. Keep the writer attached until queued stream notifications are drained before finalizing.

Useful? React with 👍 / 👎.

@jamadeo jamadeo enabled auto-merge April 14, 2026 14:29
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d5ade3361

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +205 to +207
let req = ForkSessionRequest::new(
session_id,
std::env::current_dir().unwrap_or_default(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use source working dir when forking a session

The fork path hard-codes std::env::current_dir() as the new session cwd, so duplicating a chat from History does not preserve the original project/worktree context and can execute subsequent tool calls in the app process directory instead. This is user-visible when the source session was tied to a different repo: the duplicated conversation opens, but follow-up prompts run against the wrong filesystem location until manually corrected.

Useful? React with 👍 / 👎.

@jamadeo jamadeo added this pull request to the merge queue Apr 14, 2026
Merged via the queue into main with commit 482f196 Apr 14, 2026
24 of 25 checks passed
@jamadeo jamadeo deleted the move-goose2 branch April 14, 2026 14:47
michaelneale added a commit that referenced this pull request Apr 15, 2026
…rning

* origin/main:
  move agent skills to repo root for tool discoverability (#8535)
  port goose2 chat attachments into goose (#8534)
  chore(release): bump version to 1.31.0 (minor) (#8527)
  feature: provider & model config (#8515)
  Move goose2 (#8516)
  feat: onboarding UX for the TUI (#8513)
  Set MACOSX_DEPLOYMENT_TARGET=12.0 in build-cli.yml (#8525)
  Improve local inference settings and model downloader (#8467)
  Add prompt injection mitigation logging back (#8518)
  feat(providers): add llama-swap declarative provider (#8462)
  fix: Unable to Run `goose update` on Linux (#8465)
  Add vision/image support for local inference models (#8442)
matt2e added a commit that referenced this pull request Apr 16, 2026
…arsing

Use sqlx's native DateTime<Utc> decoding for thread timestamp columns
instead of fetching as String and manually parsing. This was missed when
the baxen/goose2 branch was merged into main via PR #8516.

The manual parsing silently falls back to Utc::now() on parse failure,
which could produce incorrect timestamps. Letting sqlx decode directly
is both more correct and simpler.
matt2e added a commit that referenced this pull request Apr 16, 2026
…arsing

Use sqlx's native DateTime<Utc> decoding for thread timestamp columns
instead of fetching as String and manually parsing. This was missed when
the baxen/goose2 branch was merged into main via PR #8516.

The manual parsing silently falls back to Utc::now() on parse failure,
which could produce incorrect timestamps. Letting sqlx decode directly
is both more correct and simpler.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
matt2e added a commit that referenced this pull request Apr 16, 2026
…arsing

Use sqlx's native DateTime<Utc> decoding for thread timestamp columns
instead of fetching as String and manually parsing. This was missed when
the baxen/goose2 branch was merged into main via PR #8516.

The manual parsing silently falls back to Utc::now() on parse failure,
which could produce incorrect timestamps. Letting sqlx decode directly
is both more correct and simpler.

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants