feat(agents): add server-side projects with working directory support#4273
Open
KyleAMathews wants to merge 27 commits intomainfrom
Open
feat(agents): add server-side projects with working directory support#4273KyleAMathews wants to merge 27 commits intomainfrom
KyleAMathews wants to merge 27 commits intomainfrom
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces localStorage-only implementation with server-backed CRUD. Projects are now persisted on disk via the agents-server API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…icker Replaces the old Select-based project picker with a centered hero layout: "Let's build <project-name> ^" with a Popover for project selection and inline creation with path validation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cycles through verbs like "Let's ship", "Let's create", "Let's explore" every 4 seconds. Bumps heading to size 7. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds @fontsource-variable/figtree and imports it in main.tsx so the font actually loads. Updates --ds-font-body token accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix useRotatingVerb off-by-one (last verb never appeared as initial) - Reconcile stale activeProjectId after fetching projects from server - Validate workingDirectory exists before running horton agent - Expand ~ in paths server-side (validatePath) - Use <Button> component for project create form - Simplify readProjects ENOENT check, updateProject find, error parsing - Change "Let's hack on" to "Let's hack" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4273 +/- ##
==========================================
- Coverage 63.06% 62.50% -0.57%
==========================================
Files 184 186 +2
Lines 19932 20274 +342
Branches 4962 5052 +90
==========================================
+ Hits 12571 12673 +102
- Misses 7358 7597 +239
- Partials 3 4 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
… routes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Warmer page/sidebar backgrounds, softer tool-call cards with subtle shadows and rounder corners, pill-shaped badges, gentler sidebar selection tint, composer elevation, and cleaner spacing/hierarchy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace browser-default blue focus outline on tool card toggles with subtle accent bg on focus-visible - Section labels (Command, Output, etc.) use small-caps + 600 weight so they read as proper section headers - Warmer code block background via color-mix - Jump-to-bottom button: smaller, translucent, anchored bottom-right - Spawned/stopped timestamps left-aligned with border-left rail - Sidebar type labels dropped to xs, project headers bolder - Wider transcript column (72ch / 88ch) for code-heavy sessions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Blue-charcoal dark mode palette, 4-level text hierarchy, radial wash, brighter pill controls, raised composer contrast, and tighter center stack spacing. Completes the dark mode visual pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…iling padding Shiki's multi-theme mode sets `color: var(--shiki-light)` as an inline style, which always beats the CSS dark-mode override. Strip the inline color and let CSS rules handle theme switching. Also trim the trailing empty line Shiki appends to tokenized output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nk + route loader - connectEntityStream is now cached (baseUrl+entityUrl key) so the route loader and useEntityTimeline share the same db instance - registerActiveBaseUrl/getActiveBaseUrl lets the route loader read the active server URL outside React context (set by useServerConnection) - entityRoute gets a loader that awaits connectEntityStream so the db is ready before the component renders - defaultPreload:'intent' on the router fires the loader on Link hover - SidebarRow swapped from div[role=button] to <Link preload='intent'> so hovering a session row starts the stream connection immediately - useEntityTimeline accepts an optional preloadedDb arg; when provided it skips connectEntityStream and uses the loader-supplied instance - closeEntityStream evicts the cache and closes the db on cleanup - SidebarRow.module.css: text-decoration:none to suppress link underline
…on handled by Link
…dget truncation Budget truncation was dropping individual messages by token size, which could split tool_call/tool_result pairs — leaving orphaned tool_use or tool_result blocks that violate the Claude API. Now oversized tool_call/tool_result messages get their content replaced with a truncation stub pointing to load_timeline_range, preserving pairing while still saving budget. Also merges consecutive assistant messages (text + tool_call) in toAgentHistory to prevent consecutive assistant blocks that the Claude API rejects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… truncation The stub approach alone doesn't handle the case where budget exhaustion drops a tool_call while its tool_result survives (or vice versa). After the budget loop, scan accepted messages and drop any tool_call without a matching tool_result and any tool_result without a matching tool_call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Agents now have projects (named directories) that set the agent's working directory and group sessions in the sidebar. Also adds a 404 page, UI polish, and AGENTS.md context loading. Previously, all sessions shared the server's startup cwd and appeared in a flat timeline.
Approach
Projects — three layers, bottom-up
1. Server-side project store (
project-store.ts)Projects persist to
~/Library/Application Support/electric-agents/projects.json(or OS equivalent) viaenv-paths. Plain JSON read/write — no database needed for a handful of user-defined entries. Path validation expands~and resolves symlinks so stored paths are always canonical absolute paths.2. REST API (
project-routes.ts)ProjectRoutesclass following the existingElectricAgentsRoutespattern:GET/POST /_electric/projects— list and createPATCH/DELETE /_electric/projects/:id— update and deletePOST /_electric/validate-path— pre-submit validation for immediate UI feedbackWired into
server.tsbefore entity-type routes so the path prefix doesn't collide.3. UI + agent wiring
useProjectshook rewritten from localStorage to server APINewSessionPageredesigned with a hero heading (rotating verbs: "Let's ship", "Let's build", etc.) and a popover project picker with inline create formworkingDirectoryin spawn args and the session is tagged with the project ID for sidebar groupingcreationSchemaaccepts an optionalworkingDirectory; the handler computeseffectiveCwd(prefer spawn arg, fall back to server default) and validates the directory exists before running — writes a clear error to the stream if invalid404 page
EntityPagenow detects missing entities: if 2 seconds pass with the entities collection loaded and no match, it shows a "Not found" page instead of spinning "Loading entity..." forever. Unknown routes are caught bynotFoundComponenton the root route. Removed the silent redirect-on-error fromGenericEntityBody— errors now surface in the timeline instead of silently navigating home.AGENTS.md context
Horton now reads
AGENTS.mdfrom the working directory (if it exists) and includes it as a stable context source, wrapped in XML tags. This gives agents project-specific instructions without manual prompting.UI polish
Warmer light theme, tool trace refinements, focus fixes, wider chat column, font switch from Inter to Figtree.
Key invariants
activeProjectIdin localStorage is reconciled against the server list on load — stale references to deleted projects are clearedeffectiveCwdexists before tool setup; invalid directories produce a user-visible error instead of mysterious tool failuresworkingDirectoryis filtered from the composer's inline pill controls so it doesn't leak as a user-facing schema fieldNon-goals
Projecttype. The interface is duplicated between server and client. Will be unified when the projects data model is redesigned.Verification
Manual:
~/some/pathand absolute paths)Files changed
agents-server/src/project-store.tsenv-paths,~expansionagents-server/src/project-routes.tsagents-server/src/server.tsagents/src/agents/horton.tsworkingDirectoryspawn arg,effectiveCwdvalidation, AGENTS.md contextagents-server-ui/src/router.tsxagents-server-ui/src/hooks/useProjects.tsxagents-server-ui/src/components/NewSessionPage.tsxagents-server-ui/src/components/NewSessionPage.module.cssagents-server-ui/src/ui/tokens.css--ds-text-sm, warmer paletteagents-server-ui/src/components/toolBlock.module.cssagents-server-ui/src/components/EntityTimeline.module.cssagents-server-ui/src/router.module.css🤖 Generated with Claude Code