d671230
shelley: display image in read_image tool using base64 data from toolResult
Click to expand commit body
Prompt: In a new worktree: have the read image tool display the image being read. It's in the client data already so we may as well display it. See the screenshot tool for an example.
The read_image tool already returns the image as base64 in the toolResult.
Use that data directly to render the image as a data URL instead of
requiring a separate Display field with a URL path.
Philip Zeyliger
created
92f359c
shelley/ui: fix Monaco diff gutter display on mobile for PatchTool
Click to expand commit body
Prompt: In a new worktree reset to origin main and fix the weird formatting in the screenshot on Monaco diffs. Note how the left hitter looks like it's repeating part of the right.
The PatchTool component's Monaco diff editor was missing the CSS rules
to hide the gutter/margin on mobile devices. This caused the left gutter
area to display clipped content (showing partial text like the first
letter of each line).
Added the same mobile CSS rules that were already present for the
DiffViewer component to the PatchTool Monaco editor:
- Hide the margin and margin-view-overlays completely
- Adjust editor-scrollable and lines-content positioning
- Ensure the diff editor uses full width on mobile
Philip Zeyliger
created
1f135e9
shelley: use Monaco diff view for patch tool
Click to expand commit body
Hey, you can leave comments too, why not.
Prompt: In a new worktree (reset to origin/main after fetching), work on
the UI of the patch tool. See if you can use the monaco diff view to
produce a side-by-side from that patch display. (Note that you can
modify the display output of the tool if you need to, within reason.) Do
side-by-side if there's space; not side-by-side if there isn't; theme
(light/dark) should follow the rest of the interface. There's a separate
diff view which can be leaned on, though the interfaces are quite
different. It would be nice to have commenting in both
- Modify patch tool backend to return structured display data with old/new content
- Update PatchTool.tsx to use Monaco diff editor
- Side-by-side view on desktop, inline view on mobile (<768px)
- Theme follows dark/light mode automatically via MutationObserver
- Commenting support: click on lines to add comments (works in both streaming and static views)
- Pass onCommentTextChange prop through Message.tsx for commenting after page reload
- Removed legacy text-based diff fallback (always use Monaco now)
Philip Zeyliger
created
5924103
shelley/ui: fix manifest.json failing to load behind auth proxy
Click to expand commit body
Add crossorigin="use-credentials" to manifest link so cookies are sent
when fetching manifest.json through authentication proxies.
a034ff6
shelley: add co-author trailer to git commits
Click to expand commit body
When Shelley runs git commit commands, automatically inject
--trailer "Co-authored-by: Shelley <https://exe.dev/shelley>"
to attribute the commit as co-authored.
Uses AST parsing/modification via mvdan.cc/sh to reliably
inject the trailer flag after 'commit' in any git commit command.
Prompt: In a new worktree (fetch and reset to main) make it so that Shelley commit messages include a coauthored by Shelley line. Give me some approaches to chooose from before you do it
Philip Zeyliger
created
df427a6
shelley: linkify HTTP/HTTPS URLs in LLM responses
Click to expand commit body
Prompt: I'm a new work tree have the http/https URLs produced by the LLM converted to html links. Be safe via a vis parsing. Add some js tests for the parsing you do. (I don't think we have a test infra; add something.)
- Add linkify.tsx utility that safely parses and converts URLs to clickable links
- Modify Message.tsx to use linkifyText for text content
- Add CSS styling for links with proper hover states
- Add 26 unit tests for URL parsing edge cases
- Add Playwright e2e test for URL linkification
Security: Only matches http:// and https:// URLs (not javascript:, etc.)
Links open in new tabs with rel="noopener noreferrer"
Philip Zeyliger
created
0ecb28a
shelley: intercept PageUp/PageDown in diff modal
Click to expand commit body
Prompt: make it so that in shelley, when i use pageup/pagedown when the diff modal is open, it goes to the diff modal and not to the background.
When the diff modal is open, PageUp/PageDown should scroll the diff
editor content, not the background chat. This adds handling to intercept
those keys and forward them to the Monaco editor's built-in cursor page
navigation.
Start with the people who have signed the CLA for sketch.
David Crawshaw
created
75464ab
shelley/ui: fix context window bar not updating on conversation switch
Click to expand commit body
Prompt: When you navigate between conversations, the "progress bar" for the
context window seems to get lost or reset to 0. If I navigate between
conversations, it should show me the progress bar as of the conversation I'm on!
When navigating between conversations, the context window progress bar
would sometimes not update correctly. This was because the server uses
'omitempty' on the context_window_size field, so when it's 0 (e.g., for
conversations with no agent responses yet), the field is omitted from
the JSON response.
Previously, the frontend checked 'typeof response.context_window_size === "number"'
which would be false when the field was omitted, leaving the old value
in state. Now we always update the context window size when loading a
conversation, defaulting to 0 if the field is not present.
Added a test to verify context_window_size is correctly returned when
navigating between different conversations.
- Change hooks.post_install to hooks.post.install per GoReleaser v2 schema
- Use system_command with args array instead of system with inline args
π€ Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Philip Zeyliger
and
Claude
created
af021f3
shelley: fix ETag caching: remove immutable, support weak validators and multiple ETags
Click to expand commit body
Prompt: can you reset to origin/main and double-check that etag caching
is working correctly. I notice the etags have quotes around them in the
browser console, which is a bit weird. I cherry-picked a change and
rebuilt recently, and it didn't get picked up right away until i cleared
cache
The ETag-based caching wasn't working correctly because:
1. The Cache-Control header had 'immutable' which tells browsers to NEVER
revalidate during the max-age period, even if ETags are present.
Changed to 'max-age=0, must-revalidate' to force ETag checking.
2. The If-None-Match comparison was too strict - it only checked exact
string match. Per RFC 7232:
- Weak validators (W/"...") should match strong validators
- If-None-Match can contain multiple comma-separated ETags
- Any matching ETag should trigger a 304 Not Modified
Added etagMatches() helper function with comprehensive tests to handle
all these cases correctly.
Note: The quotes around ETags in the browser console are correct per HTTP
spec - ETags are required to be quoted strings.
Philip Zeyliger
created
433cb9b
shelley/ui: show hostname in status bar, hint about image/file in placeholder
Click to expand commit body
https://github.com/boldsoftware/exe.dev/issues/92
Prompt: Instead of 'Ready' in the status bar, let's write 'Ready on (hostname)' to indicate the hostname the user is on. Instead of 'type your message' as a placeholder, mention briefly that a user could paste an image or attach a file. Do it very succicnctly
- Change status bar from 'Ready' to 'Ready on {hostname}'
- Update input placeholder to 'Message, paste image, or attach file...'
Prompt: In this view, in desktop land, the file indicator should have a (1/10) or whatever to indicate that it's fine 1 of 10. Also, when we click next change, we should navigate from where the user is looking at now, down. It should never scroll up or to a previous file; always "down" or to the next file. When the user navigatesr to the diff view, we should already be scrolled to the first "diff" not the top of the file. When the user first opens the diff view, on desktop, we should have a toast that tells them about the keyboard navigation, that disappears after a second.
- Add file indicator (1/N) next to file selector showing current file position
- Change 'next change' to only navigate down/forward, never scroll up
- Auto-scroll to first diff when opening a file
- Show keyboard navigation hint toast on first open (desktop only)
- Add CSS for new file indicator and hint toast styling
- Change archives.format to archives.formats (list)
- Replace deprecated brews with homebrew_casks
- Pin goreleaser version to "~> v2" instead of latest
- Update README for cask install command
π€ Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
bd170bb
shelley: add GitHub Actions for auto-release and tests
Click to expand commit body
- Add release workflow that triggers on every push to main
- Add test workflow that runs Go tests on push/PR
- Add .goreleaser.yml for cross-platform builds (linux/darwin, amd64/arm64)
- Update README with installation instructions (curl, homebrew, source)
Versions follow pattern v0.N.9OCTAL where N is commit count and
9OCTAL is the SHA encoded as octal.
π€ Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Philip Zeyliger
and
Claude
created
3916a4d
shelley/ui: copy assets (manifest.json, icons) during build
Click to expand commit body
Prompt: I see errors fetching manifest.json when i load up shelley in the chrome inspector
The build script was not copying files from src/assets/ to dist/,
causing manifest.json and icons to return 404 errors in Chrome.
This was visible as errors in Chrome DevTools console.
b938c9f
loop: show error when LLM response hits max tokens limit
Click to expand commit body
Prompt: Fix https://github.com/boldsoftware/exe.dev/issues/84 . See how boldsoftware/sketch dealt with a similar problem.
(follow-up: don't auto-continue, just error out and tell the LLM to use smaller outputs)
(follow-up: fix patch tool spinning on truncated/malformed input)
(follow-up: fix bash description to talk about input not output, remove unused anthropic-beta header)
When the LLM returns a response with StopReasonMaxTokens, the loop now
records an error message informing the user about the truncation and
instructing the LLM to use smaller incremental changes.
Changes:
- loop.go: handleMaxTokensTruncation() records error message instead of
auto-continuing
- ant.go: Remove the 128k retry logic and unused TokenEfficientToolUse field
- bash.go: Add note about 60k token limit for command input
- patch.go: Add note about 60k token limit and suggest incremental patches
- patch.go: Fix patchParse to give clear error when patches field is
missing/empty (e.g., from truncated LLM response) instead of spinning
- Tests updated accordingly
Fixes https://github.com/boldsoftware/exe.dev/issues/84
Philip Zeyliger
created
c1a5f3d
shelley: add HTTP compression with content-based ETags
Click to expand commit body
Prompt: Work on http compression in Shelley. We should return compressed data if the http client supports it. Ideally our large artifacts (like Monaco) are compressed at build time and served with the relevant headers for caching. Gzip is fine.
Follow-up: Since pretty much all clients support gzip, can we make our binary smaller by only keeping the compressed versions? Add a TODO in the SSE code to think about compression in the future. I'm worried about aggressive caching without any cache-busting mechanism. Since we have git version info, can we add ?sha=... to the relevant assets? Or can we use etags instead?
Follow-up 2: Use mime.TypeByExtension instead of custom function. Make gzip per-handler instead of middleware so we don't need path awareness.
Follow-up 3: Compute actual content checksums during build and use those as ETags instead of git SHA. This way monaco-editor.js gets cached effectively even across git commits if it didn't change.
Follow-up 4: Remove compression from small response handlers (chat, cancel, archive, etc). Convert handleConversation dispatcher to use http.ServeMux with path patterns.
Build-time compression:
- Generate gzip versions of JS/CSS with SHA256 checksums
- Only .gz files embedded (reduces binary size)
- Checksums stored in checksums.json for ETag generation
Static asset serving:
- Content-based ETags from checksums
- Returns 304 Not Modified on ETag match
- Cache-Control: public, max-age=31536000, immutable
- Decompresses on-the-fly for non-gzip clients
Per-handler gzip compression:
- gzipHandler() wraps handlers with large responses
- Small response handlers (version, chat, cancel, etc) not wrapped
- SSE stream not compressed (with TODO for future)
- /api/conversation/ routes use http.ServeMux with method+path patterns
Philip Zeyliger
created
8caf4af
shelley: Make new conversation button black/white; move drawer button to header on mobile
Click to expand commit body
Prompt: make the green new conversation button just black and white in
the top bar. In the conversation drawer, if the drawer is always visible
like on desktop we don't need the second new conversation button. We
still need it on mobile, but it can be smaller and in the header
- Change btn-new from green to black/white (uses text-primary/bg-base)
- Remove full-width New Conversation button from drawer body
- Add small + icon button in drawer header (mobile only, hide-on-desktop)
- Group header action buttons in drawer-header-actions container
Philip Zeyliger
created
4cf3ff1
motd/shelley: use FQDN in URLs instead of short hostname
Click to expand commit body
Prompt: When shelley gets its prompt, does it use hostname or hostname -f? Also its welcome message
Prompt: great. also change the hostname in ChatInterface.tsx
Use hostname -f in motd hints and add .exe.xyz suffix in Shelley UI
so URLs display as bloggy.exe.xyz instead of just bloggy.
π€ Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Philip Zeyliger
and
Claude
created
8f3a083
shelley/ui: fix build-info.json timestamp to be int64
Click to expand commit body
Prompt: in a new worktree, fetch, reset to origin/main, fix the warning here
$ shelley version
Warning: failed to parse build-info.json: json: cannot unmarshal string into Go struct field .timestamp of type int64
The embedfs.go staleness checker expects timestamp to be an int64
(Unix milliseconds), but build.js was writing an ISO string.
Fix by using Date.now() instead of new Date().toISOString() for the
timestamp field, matching what build-info.js already does. Also add
the srcDir field for consistency.
Philip Zeyliger
created
e18e713
shelley: fix agentWorking to ignore gitinfo messages
Click to expand commit body
Prompt: in a new worktree, reset to origin/main: i was expecting this gitinfo message to be marked end of turn
gitinfo messages are passive notifications about git state changes.
They should not affect whether the agent is considered to be working.
Previously, a gitinfo message after an agent's end-of-turn message
would cause agentWorking() to return true, because it looked like
there was a new non-agent message requiring agent attention.
Now we skip over trailing gitinfo messages when determining the
'last' message for the agentWorking check.
948b698
shelley: remove unused prompt.go and prompt.txt
Click to expand commit body
Prompt: Is prompt.txt still in use meaningfully? It looks like system_prompt is the thing that's actually used? Clean it up.
These files defined GenerateSystemPrompt() in cmd/shelley but it was
never called. The actual system prompt generation is in
shelley/server/system_prompt.go which is used by convo.go.
Trying to address https://github.com/boldsoftware/exe.dev/issues/7 by
telling shelley to instruct about exe.dev stuff. Also lightly
encouraging git usage more.
Philip Zeyliger
created
573b34b
shelley: resize large images in read_image and screenshot tools
Click to expand commit body
When conversations have many images, Anthropic enforces a 2000px max
dimension limit per image. Previously, if an uploaded image exceeded
this limit, the API would return a 400 error and the conversation
would be stuck.
This fix proactively resizes images in the browser tools:
- read_image: resizes images that exceed max dimension before returning
- browser_take_screenshot: resizes screenshots that exceed max dimension
The resize preserves aspect ratio and uses bilinear interpolation.
A note '[resized to fit API limits]' is added to the description
when resizing occurs.
Changes:
- Add MaxImageDimension() method to llm.Service interface
- Implement MaxImageDimension() for all LLM service implementations
- New imageutil package provides image resizing utilities
- BrowseTools now accepts maxImageDimension parameter
- ToolSet passes the LLM's max dimension to browser tools
Prompt: In a new worktree, fix what happened in conversation
add-dark-mode-to-shelley where a too big image made it impossible to
continue the conversation after an anthropic error. Ideally the agent
would get the error and handle it⦠I don't think boldsoftware/sketch had
this problem. Write a test against the real anthropic api to test this.
Philip Zeyliger
created
b3d6bb9
shelley: apply light/dark mode setting to Monaco diff viewer
Click to expand commit body
Prompt: in a new worktree, reset to origin/main. The light/dark mode setting doesn't apply yet to the monaco diff view; fix that; it should apply there too.
- Add isDarkModeActive() helper to theme service
- Set Monaco theme based on current dark mode state when creating editor
- Use MutationObserver to watch for dark class changes on documentElement
- Dynamically update Monaco theme via monaco.editor.setTheme() when mode changes
The Monaco diff viewer now correctly shows 'vs-dark' theme in dark mode and
'vs' theme in light mode, and updates instantly when the user toggles themes.
Philip Zeyliger
created
2a537f1
shelley/ui: fix IME character conversion triggering form submission
Click to expand commit body
Prompt: In a new worktree, reset to origin/main, and fix https://github.com/boldsoftware/exe.dev/issues/71
When using an IME (Input Method Editor) for Japanese/Chinese input,
pressing Enter to confirm character conversion (e.g., hiragana to
kanji) was causing unintended form submissions.
The fix checks KeyboardEvent.isComposing before handling Enter key
events, which is the standard way to detect if an IME composition
is in progress.
Fixes boldsoftware/exe.dev#71
Philip Zeyliger
created
bb80eb0
shelley/ui: add keyboard shortcuts for diff view navigation
Click to expand commit body
Prompt: In a new worktree, add keyboard shortcuts for the diff view, when in comment mode. "." should go to next change. ">" to next file. Similar for "," and "<". Obviously these should only happen when in comment mode and not when a comment is open for editing.
In comment mode (when not editing a comment), add shortcuts:
- . (period) - go to next change
- , (comma) - go to previous change
- > (shift+period) - go to next file
- < (shift+comma) - go to previous file
These shortcuts only work when:
1. In comment mode (not edit mode)
2. The comment dialog is closed
Also update button tooltips to show the keyboard shortcuts.
Use capture phase on the keydown listener to intercept events before
Monaco editor handles them.
Philip Zeyliger
created
17d74c6
shelley: fix gitinfo messages incorrectly showing agent working
Click to expand commit body
phil: I increasingly think we should make the is-agent-working
state explicit and out of band with the messages (especially since
server restart can make it wrong), but haven't done that.
Prompt: after "diff-shortcuts now at bc5feb56" I get the "agent is
working" message, even though the turn is over. Fix that.
Gitinfo messages are created when the agent makes a commit. They always
happen at the end of a turn, so isEndOfTurn should return true for them.
This prevents the UI from incorrectly showing "agent is working" after
a gitinfo message is published.
Philip Zeyliger
created
f157f45
test: use --no-verify in git commit tests to skip hooks
Click to expand commit body
Prompt: Fix the test certainly.
The TestGitStateChangeCreatesGitInfoMessage test was failing because a
global commit-msg hook at ~/.config/git/hooks/commit-msg requires
agent-driven commits to include a 'Prompt:' section.
Fix by adding --no-verify to git commit commands in two places:
1. The runCmd helper function for setup commits
2. The predictable service message that triggers the agent commit
Philip Zeyliger
created
e8a64dc
fix: browser_eval tool now shows expression in UI
Click to expand commit body
Prompt: in a new worktree based on origin/main, fix the tool output for the browser JS exectue tool: it should show the script that we're asking the browser to run as well; currently the input is always blank.
The BrowserEvalTool component was looking for 'script' in the input,
but the actual tool uses 'expression' as the parameter name.
- Updated BrowserEvalTool.tsx to extract 'expression' instead of 'script'
- Fixed test fixture data in predictable.go to match
Philip Zeyliger
created
cc14355
shelley: reset context window size when switching to new conversation
Click to expand commit body
Prompt: In a new worktree, make sure that the usage meter is tied to the relevant conversation. Currently it's not by conversation id, so the latest shows up incorrectly
The usage meter (context window progress bar) was showing stale data from
the previous conversation when the user started a new conversation. This
was because the contextWindowSize state was not reset to 0 when the
conversationId became null.
Fixed by adding setContextWindowSize(0) in the useEffect that handles
conversation changes, alongside the existing setMessages([]) call.
Philip Zeyliger
created
9068197
shelley: fix URL slug not being respected on page reload
Click to expand commit body
Prompt: In a new worktree, fix the fact that reloading on one conversation
with a specific url (that's tied to the xknversstion) ends up going to the
most recent conversation.
When navigating directly to a URL like /c/my-conversation, the app was
incorrectly showing the most recent conversation instead of the one
specified in the URL.
The issue was a race condition: on initial render, the useEffect that
updates the URL based on currentConversationId would run with
currentConversationId=null, causing updateUrlWithSlug to change the URL
from /c/my-conversation to / before resolveInitialSlug could read it.
Fix: Capture the initial slug from the URL at module load time (before
any React rendering), and use that captured value in resolveInitialSlug
instead of reading from the URL which may have already changed.
Philip Zeyliger
created
e1fb156
shelley: add dark mode with system/light/dark theme switching
Click to expand commit body
Prompt: fetch, and in a new worktree based on origin/main, add dark mode to Shelley. In the "dot dot dot" menu in the top right, have a way to switch between system/light/dark, and default to the system mode.
Prompt: menu is taking too much space; can you just have one row, with the three buttons for the three modes?
Prompt: Lots of dark on dark still in dark mode. Not readable. Fix it.
- Add theme service (services/theme.ts) to manage theme state and persistence
- Theme preference stored in localStorage and defaults to 'system'
- Add compact theme toggle row to overflow menu with icon-only buttons
- Apply dark class to document root based on selected theme
- Listen for system color scheme changes when in 'system' mode
- Initialize theme before React render to avoid flash
- Fix text colors for dark mode readability (body, drawer, messages)
- Add link-color CSS variable for both light and dark modes
The UI already had dark mode CSS variables defined (.dark class in styles.css),
this change adds the mechanism to switch between themes and fixes text color
inheritance for proper dark mode contrast.
Philip Zeyliger
created
bd738fb
shelley: add git state tracking and clickable gitinfo messages
Click to expand commit body
When we're in a git repo, and the state has changed, let's tell the
user, to indicate that they can go look at the diff if they'd like.
Prompt: add git state tracking so agent commits show up in the UI
Track git state changes during agent loops and display them in the UI
as clickable messages that open a diff viewer.
Changes:
- Add gitstate package for tracking git repo state (branch, commit, subject)
- Add migration 008 to include 'gitinfo' in message type constraint
- Record gitinfo messages in db when commits are detected
- Publish gitinfo messages to SSE subscribers for real-time updates
- Display gitinfo as single-line "branch now at sha subject [diff]"
- Style diff as clickable link that opens DiffViewer
- Fix AgentWorking and isEndOfTurn to handle gitinfo messages correctly
π€ Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
0c25add
shelley: fix empty assistant content causing API error
Click to expand commit body
Prompt: I saw that in a conversation (git-state-tracking-in-worktrees).
Can you create a new worktree, and investigate how shelley could have
gotten into this state? (error: messages.361: all messages must have
non-empty content except for the optional final assistant message)
When the LLM ends its turn without producing any output (empty Content
array), subsequent messages would fail with the API error:
'messages.N: all messages must have non-empty content except for the
optional final assistant message'
This can happen when the model decides it has nothing more to say after
a tool result. The fix adds placeholder content '(no response)' to empty
assistant messages that are not at the end of the conversation history.
The empty assistant message is allowed by the API if it's the final
message, so we only add placeholder content when there are more messages
following it.
Philip Zeyliger
created
a6dbb63
shelley: Improve diff viewer UI for desktop and mobile
Click to expand commit body
Prompt: Work on the diffs view in shelley:
1. On desktop, top bar should have commit selector, file selector, nav buttons, mode toggle - no collapsibility
2. On mobile, selectors 50/50 at top, floating nav buttons at bottom (larger), extra whitespace to scroll past
3. Change prev/next file icons from >| to >> (double chevrons)
4. Fix mobile CSS: full width, fix gutter wraparound
5. Mobile commenting: clicking on line should work
6. Desktop: close button on far right, dropdowns take space
7. Comment dialog on top half of screen on mobile, auto-focus
8. Fix open/close/reopen bug
9. Comment format: > filename:123: code
Desktop:
- Remove collapsible selectors, show all controls in one row
- Selectors expand to fill space, controls (nav, mode, close) on right
- Change prev/next file icons to double chevrons (>>)
Mobile:
- Selectors 50/50 at top with close button
- Floating nav buttons at bottom (larger, with mode toggle)
- Comment dialog in top half of screen for keyboard space
- Auto-focus comment textarea
- Fix CSS: full width, hide gutter properly
- Extra padding at bottom for scrolling past buttons
- onMouseUp handler for touch commenting
Bug fixes:
- Fix open/close/reopen by resetting state and disposing editor on close
Comment format:
- Changed to: > filename:123: code snippet
Comment text