* 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
* 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>
- 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>
* 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>
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>
* 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>
* 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.
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
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>
* 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>
* 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>
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.
* 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>
* 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>
* 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>
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
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>
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>
* 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>
* 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 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: 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>
* 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: 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 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: 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>
* 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>
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>
* 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>
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>
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>
* 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>
* 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>
* 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>
* 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>
* 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>