Commit Graph

54 Commits

Author SHA1 Message Date
morgmart 38423edd55 centralize provider selection state in agentStore (#54)
* feat: centralize provider selection state in agentStore

Move provider discovery and selection from per-component state to a
shared store, ensuring the selected provider persists across screens
and is loaded once on app startup. Adds loading state for the provider
dropdown and removes hardcoded provider labels in favor of API data.

* fix: restore default provider after persona override
2026-04-01 11:37:03 -07:00
Wes 396ab4154b feat: surface ACP model and token info in status bar and chat input (#53)
* feat: wire ACP token/context info into status bar and chat input

Surface model name and context window usage in the UI:
- Redesign StatusBar with Bot icon and model name display
- Add contextLimit to TokenState for context window tracking
- Wire tokenState from chatStore through ChatView to ChatInput
- Add acp:usage_update event listener in useAcpStream (ready for
  backend plumbing)

The ContextRing in ChatInputToolbar will activate once the backend
emits acp:usage_update events with context window size data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: show truncated session ID next to copy button in status bar

The previous change hid the session ID text and only showed the copy
icon. Restore the first 8 characters of the session ID as visible text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove always-red status dot from StatusBar

The status dot was permanently red because `setConnected(true)` is
never called anywhere in the codebase. Remove the dead code: the dot,
the statusColor map, the status prop, and the connectionStatus
derivation in AppShell.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use npx for biome in justfile fmt-check recipe

The bare `biome` command fails when invoked from lefthook's pre-push
hook because node_modules/.bin isn't on PATH. Use `npx biome` to
match the pattern used in pre-commit hooks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: respect direct accumulatedTotal in updateTokenState reducer

The reducer always recomputed accumulatedTotal as accumulatedInput +
accumulatedOutput, silently dropping any directly-passed value. This
meant acp:usage_update events (which only provide a total used count)
were ignored, keeping the context ring at 0.

Now partial.accumulatedTotal takes precedence when provided, falling
back to the input + output sum otherwise.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 11:12:04 -07:00
Wes 10632df518 chore: migrate biome from hermit to pnpm devDependency (#52)
- Pin @biomejs/biome to exact version 2.4.9 (was ^2.4.6)
- Remove biome hermit package (bin/biome, bin/.biome-2.4.9.pkg)
- Update justfile fmt-check to resolve biome via pnpm exec

This ensures the VS Code extension, pre-commit hooks, and CI all
resolve biome from node_modules consistently, without relying on
hermit PATH activation.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 10:39:14 -07:00
Wes c515bb48c2 feat: make personas optional (#51)
* feat: make personas optional — allow chatting without a persona

Adds a "Goose" / no-persona option to the persona picker so users can
chat directly with the agent without any persona system prompt injected.
HomeScreen and ChatView now default to no persona selected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update HomeScreen test for optional persona default

The HomeScreen now defaults to no persona selected, so the placeholder
reads "Message Goose..." instead of "Message Solo...".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 10:19:53 -07:00
Wes 188ca21ac0 feat: add e2e tests for personas and skills views (#50)
Add comprehensive Playwright e2e tests for the Personas and Skills
features (34 new tests). Includes a Tauri IPC mock fixture that injects
window.__TAURI_INTERNALS__ via page.addInitScript(), enabling tests to
run against the static build without the Tauri backend.

Tests cover: navigation, card/list rendering, create/edit dialogs,
form validation, dropdown menus, delete confirmations, search filtering,
empty states, and built-in vs custom persona behavior.

Also fixes lefthook pre-commit format hook to use npx for biome.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 09:59:56 -07:00
morgmart 5bdb225a07 fix: restore project prompt editor workflow (#49) 2026-04-01 09:24:08 -07:00
Wes 93f3509906 centralize built-in personas in the backend (#48)
* Update built-in personas to Solo, Scout, and Ralph

Replace the previous 4 built-in personas (Goose, Scout, Reviewer, Architect)
with 3 new ones pulled from Sprout: Solo (default), Scout, and Ralph.
Avatars resized from 128x128 to 64x64 to match UI display sizes.
Persona constants extracted to builtin_personas.rs to stay under file size limit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Refactor: remove duplicated system prompts from frontend

Replace the ~300-line BUILTIN_PERSONAS array (which duplicated all system
prompts from the Rust backend) with minimal BUILTIN_PERSONA_STUBS that
only carry metadata. The backend is the single source of truth for
system prompts, served via listPersonas() on startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: remove legacy frontend persona stubs

* style: fix rust formatting

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 09:14:49 -07:00
Alex Hancock b056a77a4f fix: adjust protocol name in AGENTS.md (#47) 2026-04-01 08:29:19 -07:00
Bradley Axen 71d5c94137 refactor: use ~/.agents/skills as standard skill storage path (#46)
* feat: use ~/.agents/skills as standard skill storage path

Migrate skill storage from ~/.goose/skills/ to ~/.agents/skills/ to
align with the cross-agent standard (agentskills.io) convention where
global skills live at ~/.agents/skills/ and local skills at
<project>/.agents/skills/.

Changes:
- Update skills_dir() in Rust backend to use .agents/skills
- Add AGENTS_STANDARD_DIR and SKILLS_DIR constants to storage.ts
- Existing .goose/ constants preserved for other goose-specific data

* chore: remove dead storage.ts constants file

Delete src/shared/constants/storage.ts and the now-empty constants/
directory. None of the constants (GOOSE_DIR, RECIPES_DIR, AGENTS_DIR,
SESSIONS_DIR, EXTENSIONS_DIR, AGENTS_STANDARD_DIR, SKILLS_DIR) were
imported anywhere in the frontend codebase.

Storage path logic lives entirely in the Rust backend (e.g.
skills_dir() in src-tauri/src/commands/skills.rs), and the frontend
communicates through Tauri invoke() commands. Keeping unused path
constants in the frontend was misleading dead code.
2026-04-01 08:26:37 -07:00
Bradley Axen 6bf9eff796 Remove unused goosed sidecar and HTTP fetch infrastructure (#45)
The app communicates with AI backends exclusively through ACP
(stdin/stdout JSON-RPC), not through goosed's HTTP API. The sidecar
was spawned and health-checked but none of its endpoints were ever
called — apiFetch had zero callers and useSidecar had zero importers.

Removed:
- src-tauri/src/commands/sidecar.rs (SidecarState, start/stop/health commands)
- src/shared/hooks/useSidecar.ts (unused React hook)
- src/shared/api/fetch.ts (apiFetch/clearServerConfigCache, zero callers)
- reqwest and rand Cargo dependencies (only used by sidecar)

Updated AGENTS.md to document ACP
2026-04-01 07:17:35 -07:00
Matt Toohey 39fbd47825 feat: add local persona avatars with drag-and-drop upload (#44)
Local persona avatars now support both dropped URLs and uploaded image files. Local files are stored in ~/.goose/avatars/, exposed through Tauri's asset protocol, and rendered through shared avatar resolution helpers.

The persona editor now uses a drag-and-drop avatar picker, and all persona avatar surfaces were updated to handle local avatar sources correctly.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 18:35:03 +11:00
Matt Toohey 903f78a93b feat: add multi-persona chat context and attribution (#42)
* feat: add persona targeting fields to message metadata

Add target_persona_id, target_persona_name, and acp_session_key fields
to message metadata in both Rust and TypeScript types.

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

* feat: add composite key storage and persona-filtered messages to ACP store

Support per-persona ACP sessions via {sessionId}__{personaId} composite keys,
and add persona-based message filtering for multi-persona chat.

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

* feat: add catch-up context builder for multi-persona conversations

Add persona_name field to Rust MessageMetadata. Create catchup module
that builds context text from intervening messages when switching
between personas, so each persona stays informed about conversation
activity from other participants.

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

* feat: wire persona params through AcpService with catch-up context

Set persona metadata on user and assistant messages. Build catch-up
context from intervening messages and include it in the effective
prompt sent to the ACP driver.

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

* feat: pass persona params through frontend API to Tauri backend

Add personaId and personaName parameters to acpSendMessage so the
frontend can target specific personas when sending messages.

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

* feat: set persona target metadata on user messages in useChat hook

When personaInfo is provided, set targetPersonaId and targetPersonaName
on the user message metadata so downstream systems can identify which
persona each message is addressed to.

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

* feat: use composite session key in ACP registry for per-persona cancellation

Update registry register/deregister/cancel to use composite keys
({sessionId}__{personaId}) so cancelling one persona's stream doesn't
affect another persona's active session.

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

* feat: add persona selector UI and message attribution display

Add dropdown persona picker in chat input for selecting which persona
to address. Display persona attribution on message bubbles showing
sender/recipient persona names.

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

* fix: resolve code review issues in multi-persona chat

- Fix UTF-8 panic in truncate_text by using char_indices instead of byte slicing
- Remove dead code in catchup.rs (unreachable else branch after early return)
- Pass persona params through to acpSendMessage in useChat hook
- Deduplicate composite key logic by delegating store.effective_key to make_composite_key

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

* fix: address remaining multi-persona review issues

Stabilize catch-up context by excluding the current prompt by message ID instead of array position. Preserve original persona targeting on retry, split composite registry keys in running-session payloads, keep assistant agent names visible alongside persona badges, and remove unused acpSessionKey metadata.

* fix: use per-message persona names in chat history

* fix: restore classic assistant name styling

* fix: remove user persona attribution from chat history

* fix: resolve multi-persona review follow-ups

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:15:00 +11:00
morgmart 5ae02923f0 unify chat input project and provider controls (#43)
* feat: unify chat input project and provider controls

* fix: apply code review fixes for formatting and type safety

* test: mock syntax highlighter in vitest setup

* test: update smoke placeholder assertion
2026-03-31 23:07:55 -07:00
morgmart 5cf78e7dc3 expand design system primitives with shadcn compatibility (#41)
Add shadcn CSS variable compatibility layer, new button variants (toolbar, icon sizes), and complete dropdown menu components (checkbox, radio, sub-menus). Fix focus styles to use focus-visible for keyboard-only indicators.

Made-with: Cursor
2026-03-31 22:36:41 -07:00
Matt Toohey 76495b5e6c feat: add prompt editor to project creation (#40)
* feat: change ProjectInfo working_dir to working_dirs Vec<String>

Update backend data model to support multiple working directories.
Add backward-compatible deserialization for old workingDir format.
Update create_project and update_project command signatures.
Exclude .agents from biome to fix pre-existing Markdown check errors.

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

* feat: update TS API layer for workingDirs array

Change ProjectInfo.workingDir to workingDirs: string[] and update
createProject/updateProject function signatures.

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

* feat: add PromptEditor component with blue capsule rendering

ContentEditable component that renders include: lines as styled
blue capsules while keeping plain text editing for other lines.

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

* feat: integrate PromptEditor into CreateProjectDialog

Replace dedicated working directory input with unified prompt editor.
Add directory button inserts include: lines. Parse on save extracts
workingDirs array from include: lines.

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

* feat: update chatProjectContext for workingDirs array

getProjectFolderOption returns array of folder options.
buildProjectSystemPrompt lists all working directories.

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

* feat: update UI components for workingDirs array

Update AppShell, ProjectsView, ChatView, and other components
to use workingDirs instead of workingDir.

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

* fix: resolve code review feedback for PromptEditor and CreateProjectDialog

- Revert biome.json ignoreUnknown back to false (exclude alone suffices)
- Extract shared INCLUDE_RE regex to lib/includePattern.ts
- Only parse include: lines from beginning of text, not anywhere
- Fix contentEditable cursor jumping with lastPushedValue ref
- Add onPaste handler to ensure only plain text is inserted
- Add accessibility attrs (role, tabIndex, aria-multiline, aria-label)
- Add overflow-y-auto so resize-y works on the contentEditable div
- Add dark:text-blue-400 for capsule text contrast in dark mode
- Fix biome lint warnings (useTemplate, noUselessContinue)

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

* fix: re-render PromptEditor HTML on every input to sync capsule styling

The contentEditable div was not re-rendering its innerHTML on user
input, so blue capsule styling from include: lines persisted even
after the text was edited to no longer match the regex. Now handleInput
re-renders via renderLines on every keystroke, with caret position
save/restore to avoid cursor jumping.

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

* fix: use line/column caret positioning in PromptEditor re-render

The flat character offset approach (range.toString().length) did not
count implicit newlines between contentEditable <div> elements,
causing the caret to land in the wrong position after re-rendering.
Switch to line/column positioning that maps directly to the
div-per-line DOM structure.

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

* fix: show instructions in edit dialog after project creation

handleSave called onClose() (the prop) instead of handleClose(),
so form state was never reset. When reopening the edit dialog for
the same project, the prompt state matched the stale value, React
skipped the re-render, and PromptEditor's innerHTML was never set.

Two fixes:
- handleSave now calls handleClose() to reset form state after save
- PromptEditor initializes lastPushedValue to null so innerHTML is
  always set on mount, not just on external changes

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

* fix: stop PromptEditor from reordering characters when typing quickly

The contentEditable innerHTML was being destroyed and recreated on
every keystroke to update capsule styling, which caused the browser
to insert queued characters at wrong positions. Now only re-render
innerHTML when the set of include: lines actually changes.

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

* fix: address prompt editor review issues

* fix: preserve PromptEditor cursor when typing include lines

* fix: clear stale include styling in prompt editor

* fix: append selected project directories and skip duplicates

Append Add directory selections to the bottom of the prompt editor instead of inserting them ahead of the prompt body.

Skip duplicate directory insertions by normalizing include paths against the user's home directory so ~/... and absolute paths compare as the same location.

* fix: tighten project working dirs parsing

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 16:27:24 +11:00
morgmart 3085e1558b add shadcn compatibility layer and separate brand from accent colors (#39)
Split the semantic "accent" color into two concepts: brand (user-customizable
accent color) and accent (shadcn's neutral hover state). This enables using
shadcn/ui components with minimal modification while preserving the existing
theming system.
2026-03-31 21:44:13 -07:00
Matt Toohey 3eff914e60 refactor: consume shared doctor crate from builderbot (#38)
* refactor: consume shared doctor crate from builderbot

Replace the local doctor module (agents.rs, checks.rs, mod.rs, resolve.rs,
types.rs) with thin Tauri command wrappers that delegate to the shared
doctor crate extracted into block/builderbot.

- Add doctor git dependency pointing to the doctor branch in builderbot
- Delete src-tauri/src/commands/doctor/ directory (5 files, ~37KB)
- Create src-tauri/src/commands/doctor.rs with run_doctor and
  run_doctor_fix wrappers plus pub use re-exports of shared types
- No changes needed to commands/mod.rs (Rust resolves doctor.rs
  automatically) or lib.rs (same command function names)

* build: pin doctor dependency to merged commit

Switch the doctor crate from tracking the `doctor` branch to a specific
rev pin (bfce292) now that the branch has been merged into main.

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

* fix: remove unused doctor imports flagged by clippy

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 15:23:13 +11:00
morgmart 7d5d789687 Revert "maximize desktop window on startup (#33)" (#37) 2026-03-31 18:57:10 -07:00
Matt Toohey 7c127eacba refactor: split doctor.rs and acp.rs into sub-modules (#36)
* refactor: split doctor.rs and acp.rs into sub-modules

Split two large files into directory modules to remove file size
exceptions from check-file-sizes.mjs:

- commands/doctor.rs (997 lines) → doctor/{mod,types,resolve,checks,agents}.rs
- services/acp.rs (509 lines) → acp/{mod,payloads,writer,store,registry}.rs

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

* fix: narrow acp payload struct visibility from pub(crate) to pub(super)

These structs are only used within the acp module itself, so pub(super)
is the minimal correct visibility, consistent with the doctor module.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 12:24:51 +11:00
Matt Toohey 971064cfa5 fix: resume existing ACP sessions instead of always creating new ones (#35)
* fix: resume existing ACP sessions instead of always creating new ones

Each message was spawning a fresh ACP session because send_prompt always
passed None as the agent_session_id. The TauriStore already persisted
the agent-to-session mapping via set_agent_session_id, but never read it
back. Added get_agent_session_id to TauriStore and wired it into
send_prompt so subsequent messages in a chat resume the existing session.

Also excludes .agents/ from biome checks to fix pre-existing hook failure
on markdown skill files.

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

* fix: address code review comments on ACP session resumption

Use a typed Deserialize struct instead of serde_json::Value for
get_agent_session_id to catch schema drift at compile time, and fix
missing space after // in section divider comments.

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

* style: fix Rust formatting and simplify get_agent_session_id

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

* fix: remove unused Deserialize import in acp.rs

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

* style: restore section divider comments in acp.rs

The previous formatting commits inadvertently stripped the
`// ---------------------------------------------------------------------------`
section dividers. Restore them for all five sections and add a narrow
file-size exception (550 lines) since the dividers push the file just
past the 500-line default.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 11:59:48 +11:00
morgmart 21af4ce4da fix: reuse open empty new chat tabs (#34) 2026-03-31 17:22:00 -07:00
morgmart 8341420435 maximize desktop window on startup (#33) 2026-03-31 16:29:01 -07:00
morgmart bd459e25d4 add shared agent skills for code-review and create-pr (#32)
Skills are stored in .agents/skills/ as the single source of truth,
with symlinks from .cursor/, .claude/, and .codex/ so all three
coding agents can discover them.

Made-with: Cursor
2026-03-31 16:00:31 -07:00
tulsi 430d5ba78c add inline rename for sidebar chats (#28)
* add inline chat rename in sidebar

* format sidebar rename test for biome
2026-03-31 15:44:02 -07:00
Wes a2133170b5 fix: start app on Home screen and use ACP title logic for project chats (#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>
2026-03-31 15:43:49 -07:00
Wes 24d1e44c2b fix: remove backtick decorations from inline code elements (#30)
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>
2026-03-31 15:31:07 -07:00
Wes f63fa19aba [codex] Use project settings in project chats (#29)
* Use project settings in project chats

* Allow clippy on ACP send command
2026-03-31 15:22:56 -07:00
Wes ef9aaf6573 feat: clean up chat message rendering (#27) 2026-03-31 14:36:18 -07:00
Wes 562deafe4d [codex] Archive chats and restore them from settings (#26)
* 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>
2026-03-31 12:43:49 -07:00
Wes f29cebb505 feat: wire up ACP session metadata and persist across restarts (#25)
* 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>
2026-03-31 10:42:09 -07:00
Wes f5c8ba168f feat: persona picker with @ mention support (#24)
* 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>
2026-03-31 08:42:28 -07:00
Matt Toohey 20f9ce4163 chore: bump acp-client to latest builderbot HEAD (dbd5bc9c) (#23)
Update acp-client git dependency from 17e60981 to dbd5bc9c.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:10:56 +11:00
Matt Toohey d6d58dffd3 feat: session persistence with tab state and sidebar recents (#22)
* 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>
2026-03-31 16:47:48 +11:00
Matt Toohey 1b628a56cb feat: add doctor health-check UI and backend to preferences dialog (#21)
* 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>
2026-03-31 15:59:31 +11:00
Matt Toohey bffcea2823 feat: use acp-client from builderbot repo as git dependency (#20)
* 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>
2026-03-31 14:47:50 +11:00
Matt Toohey cf68533896 feat: add branch-based dev icons and stable ports for worktree disambiguation (#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
2026-03-31 14:09:58 +11:00
Wes dc516fdb19 feat: add ACP provider selector to chat and home (#18)
* 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>
2026-03-30 19:42:25 -07:00
Wes 8c3471f43f Fix settings page (#17)
* 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>
2026-03-30 19:21:31 -07:00
Wes c7ec58f660 refactor: remove unused root-level stores and types (#16)
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>
2026-03-30 18:51:26 -07:00
Wes a1893bb184 feat: add projects feature to sidebar (#15)
* 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>
2026-03-30 18:10:39 -07:00
Matt Toohey e2fa4e8619 fix: prevent horizontal scrolling in sidebar nav (#14)
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>
2026-03-31 10:52:50 +11:00
Matt Toohey c6739f6327 fix: settings modal X button not dismissing dialog (#13)
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>
2026-03-31 10:44:44 +11:00
Wes 5b1a8ba0c3 feat: add ACP client integration for multi-agent support (#12)
* 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>
2026-03-30 15:57:13 -07:00
Wes fde43f3038 Replace default Tauri icons with Goose app icon (#11)
* 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>
2026-03-30 15:39:09 -07:00
Wes 3b8baf07e3 fix: persona & skill dialog layouts + drag-and-drop on new cards (#9)
* 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>
2026-03-30 15:39:01 -07:00
Wes a088b17b80 feat: personas & skills — rename, import/export, creation, and layout 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>
2026-03-30 14:31:33 -07:00
renovate[bot] aed88d7242 chore(deps): update actions/cache action to v5 (#6)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-30 14:10:28 -07:00
Wes cca7907ef3 feat: chat experience preparation — UI components, stores, and goose2.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>
2026-03-30 10:31:32 -07:00
renovate[bot] e6750f0d95 chore(deps): update all non-major dependencies (#5)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-30 10:25:23 -07:00
Wes ea7d712cf0 feat: add Skills and Agents pages with sidebar navigation (#4)
- 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
2026-03-29 20:34:23 -07:00