Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
2243 commits
Select commit Hold shift + click to select a range
5ffc212
Make sandbox-exec say which guard refused it (#366)
freekmurze Sep 21, 2026
1140672
Make the sandbox supervisor say which guard refused it too
freekmurze Sep 22, 2026
048ec5b
Let a sandboxed process stay readable to its own supervisor (#370)
freekmurze Sep 22, 2026
9408a39
Declare prctl with the shape this build already gives it
freekmurze Sep 22, 2026
520b94b
Merge remote main before Bloom 1.34.1
freekmurze Sep 22, 2026
c180cde
Read a sandboxed exec as its owner, because root may not
freekmurze Sep 22, 2026
e9a5ec3
Let a click close a completed turn that opened itself
freekmurze Sep 22, 2026
5384eca
Draw no worked-for rule where a background task interrupted
freekmurze Sep 22, 2026
94e6a61
Lead the running-work plate with what is running (#373)
freekmurze Sep 22, 2026
4b06e91
Choose a project settings pane from a sidebar (#374)
freekmurze Sep 22, 2026
87b3976
Let the attestor read as the owner too, and share one way of doing it
freekmurze Sep 22, 2026
2366c04
Attest from what the supervisor recorded, not from a live process
freekmurze Sep 22, 2026
6533081
Make the shared worktree, since nothing else can any more
freekmurze Sep 22, 2026
0a857eb
Share the worktree in both directions, not just one
freekmurze Sep 22, 2026
90b3d11
Put the worktree back before cloning into it
freekmurze Sep 22, 2026
f0d6638
Draw a turn's subagent rows without being asked (#382)
freekmurze Sep 22, 2026
0881186
Stop telling git to use a client certificate called nothing
freekmurze Sep 22, 2026
ecb7e8d
Freeze the transcript while a pane changes width, and fade to the reflow
freekmurze Sep 22, 2026
95bb963
Stop paying a macOS runner to compile a branch that is not ready
freekmurze Sep 22, 2026
4047011
Revert "Freeze the transcript while a pane changes width, and fade to…
freekmurze Sep 22, 2026
50e889d
Ask the launch gate what a credential is for, not the process tree (#…
freekmurze Sep 22, 2026
805586f
Take a GitHub credential that does not expire, and say why when one i…
freekmurze Sep 22, 2026
41c70bd
Let a workspace finish setting up: derived service ids, lower case UL…
freekmurze Sep 22, 2026
e42b2ec
Give a setup script the groups its user is actually in (#388)
freekmurze Sep 22, 2026
1ed730f
Name Opus 5.5, and keep a window on a minor version from splitting it…
freekmurze Sep 22, 2026
69ea887
Run agents as one sandbox user, find them on PATH, share the bridge (…
freekmurze Sep 22, 2026
3edc130
Forward the Anthropic betas Claude names (#392)
freekmurze Sep 22, 2026
f512fa7
Show a new Cloud workspace's build as one card of steps (#393)
freekmurze Sep 22, 2026
91b8fd1
Give a Cloud workspace its terminal back (#394)
freekmurze Sep 23, 2026
5cc97fd
Take the Cloud build's stale RootView title anchor out (#395)
freekmurze Sep 23, 2026
c3daa77
Group the sidebar by project by default, and list it first (#397)
freekmurze Sep 23, 2026
ffdea51
Link the agent CLIs where the image keeps them instead of copying the…
freekmurze Sep 23, 2026
58fb64e
Keep a restarting Runner's model grants alive (#396)
freekmurze Sep 23, 2026
91ae691
Draw a turn's worked-for line above its work (#398)
freekmurze Sep 23, 2026
c5c007a
Drop the note a turn left after getting through retries (#399)
freekmurze Sep 23, 2026
8dec4f8
Run Cloud terminals and services properly as the sandbox user (#400)
freekmurze Sep 23, 2026
62eced4
Offer the pane menu on a Cloud workspace's browser (#401)
freekmurze Sep 23, 2026
07f3e94
Give what setup built to the shared group, not only its mode (#402)
freekmurze Sep 23, 2026
a4430b1
Forward a port from a Cloud workspace to this Mac (#377)
freekmurze Sep 23, 2026
f78011b
Offer to continue a chat once its usage limit resets (#403)
freekmurze Sep 23, 2026
ac4b317
Avoid composer refreshes for every transcript row
freekmurze Sep 23, 2026
29722f2
Merge pull request #404 from spatie/freekmurze/greenland-sea-2
freekmurze Sep 23, 2026
a54985a
Skip layout for stored stream delta rows
freekmurze Sep 23, 2026
470c1d1
Merge pull request #405 from spatie/freekmurze/skip-empty-stream-layout
freekmurze Sep 23, 2026
6ac1342
Retry the opening fetch and always deliver a turn's end (#406)
freekmurze Sep 23, 2026
a9dad40
Avoid duplicate editor work while typing
freekmurze Sep 23, 2026
a0e58d0
Avoid rebuilding unchanged composer footer while typing
freekmurze Sep 23, 2026
84e4a3a
Reopen a Cloud workspace's event stream when nothing is running it (#…
freekmurze Sep 23, 2026
a6df4df
Close a stopped turn the CLI never reported (#408)
freekmurze Sep 23, 2026
d302544
Keep a Cloud question open until it is answered (#409)
freekmurze Sep 23, 2026
00be523
Omit stored stream events from transcript table
freekmurze Sep 23, 2026
aadf9d0
Avoid sleep helper checks on ordinary startup
freekmurze Sep 23, 2026
8e413e5
Merge pull request #412 from spatie/freekmurze/skip-empty-stream-layo…
freekmurze Sep 23, 2026
ae75dac
Fix the three tests that turned main red (#410)
freekmurze Sep 23, 2026
fe48511
Carry the Runner's PATH through sudo into a person-bound launch (#411)
freekmurze Sep 23, 2026
e215651
Give the Cloud agent only the image's permission settings, not its Sp…
freekmurze Sep 23, 2026
253fae9
Pass a model provider's own error through the Runner's gateway (#414)
freekmurze Sep 23, 2026
3f161d5
Resume a Cloud chat's conversation on every turn (#416)
freekmurze Sep 23, 2026
0cfb482
Let a Cloud agent commit, push and open a pull request (#415)
freekmurze Sep 23, 2026
ead152f
Let a Cloud agent's own shell push, and clone again (#418)
freekmurze Sep 23, 2026
3487b84
Draw every workspace state's mark in the sidebar again (#419)
freekmurze Sep 23, 2026
6f79aaa
Stop the composer flickering at a legacy scroller's wrap boundary (#421)
freekmurze Sep 23, 2026
bbaab3b
Keep a dead key's marked text in the composer (#422)
freekmurze Sep 23, 2026
10218d2
Rely on CI for tests unless the owner asks for a local run (#424)
freekmurze Sep 23, 2026
a692cbe
Show a proposed plan formatted in the chat, and let a preset start in…
freekmurze Sep 23, 2026
3a9297f
Hold Cloud review reads until the clone finishes, and measure the dif…
freekmurze Sep 23, 2026
92e481f
Improve divider resize responsiveness (#417)
freekmurze Sep 23, 2026
d828cbe
Keep pane content visible during inspector resize (#425)
freekmurze Sep 23, 2026
a9eac35
Animate the Cloud setup step, and quieten the card and inspector whil…
freekmurze Sep 23, 2026
f0cdc4b
Fix the pending bubble's wrap and three setup card layout bugs
freekmurze Sep 23, 2026
c24aec8
Say why "Creating the machine" is slow, and log a Cloud connection's …
freekmurze Sep 23, 2026
8d7667f
Open a forwarded database as the workspace's own user (#428)
freekmurze Sep 23, 2026
85e27fe
Name the Cloud connection as the wait, and stop claiming an unread br…
freekmurze Sep 23, 2026
b544aa3
Smooth chat pane resize on both dividers (#430)
freekmurze Sep 23, 2026
3c00341
Make the Ports popover compact, databases first (#431)
freekmurze Sep 23, 2026
62eef46
Only draw a background wake for a task the parent owns (#433)
freekmurze Sep 23, 2026
f7808f2
Run the macOS CI pair nightly and before releases, not on pull reques…
freekmurze Sep 23, 2026
a327e2a
Remove the nightly schedules (#435)
freekmurze Sep 23, 2026
6872acf
Show live Cloud setup step duration (#436)
freekmurze Sep 24, 2026
898422e
Fix BloomClient's test abort and the git wrapper test race (#432)
freekmurze Sep 24, 2026
777d026
Draw the empty preview as a page rather than a line of text (#437)
freekmurze Sep 24, 2026
a02aa53
Draw the preview page with Bloom's real mark (#438)
freekmurze Sep 24, 2026
7a492aa
Close the Ports popover on a second click of its button (#439)
freekmurze Sep 24, 2026
b8f2795
Use workspace database login when opening forwarded ports (#440)
freekmurze Sep 24, 2026
350235b
Offer database login when Cloud credentials are unknown (#441)
freekmurze Sep 24, 2026
0e22a14
Restore Bloom Cloud sidebar card with setup prompt (#442)
freekmurze Sep 24, 2026
f91001c
Remember sidebar visibility across launches
freekmurze Sep 24, 2026
9f358c0
Merge pull request #443 from spatie/freekmurze/lingayen-gulf
freekmurze Sep 24, 2026
dfc5011
Speed up long transcript opening and scrolling (#444)
freekmurze Sep 24, 2026
ac2e53c
Avoid redundant diff scroll position updates (#445)
freekmurze Sep 24, 2026
e53a42c
Give private Cloud agents scoped MCP proxies (#446)
freekmurze Sep 24, 2026
2c3cb74
Copy full commit hash from History rows (#447)
freekmurze Sep 24, 2026
a13b8bb
Keep person mentions in the address pill (#449)
freekmurze Sep 24, 2026
561e1fc
Keep local file mentions out of cloud addressing (#450)
freekmurze Sep 24, 2026
c044fc2
Route Cloud Codex turns through model gateway (#448)
freekmurze Sep 24, 2026
a77eac2
Accept generic Cloud MCP connection names (#451)
freekmurze Sep 24, 2026
3ce6317
Fix browser empty state backgrounds (#452)
freekmurze Sep 24, 2026
ee2511a
Clarify Cloud workspace agent permissions (#453)
freekmurze Sep 24, 2026
e55c320
Ask Cloud agent to merge pull requests (#454)
freekmurze Sep 24, 2026
f40656a
Let Cloud agents archive workspaces and mark sleeping rows (#455)
freekmurze Sep 24, 2026
b071843
Animate Cloud workspace deletion in Mac app (#456)
freekmurze Sep 24, 2026
06d37c6
Give Cloud MCP configs the agent's shared group (#457)
freekmurze Sep 24, 2026
606d7c7
Reduce transcript jumps and prioritise conversation loading (#458)
freekmurze Sep 24, 2026
4d53846
Verify Cloud MCP config group with POSIX ownership (#460)
freekmurze Sep 24, 2026
303039c
Let Cloud agents read uploaded attachments (#462)
freekmurze Sep 24, 2026
08e83dc
Reduce repeat Cloud polling requests (#461)
freekmurze Sep 24, 2026
17a7fb7
Show cloud file contents and add confirmed file deletion (#463)
freekmurze Sep 24, 2026
cc76f87
Make cloud image attachments readable to agents (#459)
freekmurze Sep 24, 2026
6598677
Add Cloud workspace rename to sidebar menu (#464)
freekmurze Sep 24, 2026
0da2725
Improve Cloud workspace connection state
freekmurze Sep 24, 2026
bf9d2b1
Merge pull request #465 from spatie/freekmurze/gulf-of-antalya-2
freekmurze Sep 24, 2026
865e6b4
Add delete file to workspace protocol schema
freekmurze Sep 24, 2026
d41e118
Merge pull request #466 from spatie/freekmurze/fix-delete-file-protoc…
freekmurze Sep 24, 2026
8fc94ba
Show MCP elicitation details in permission card
freekmurze Sep 24, 2026
24bdadf
Merge pull request #467 from spatie/freekmurze/celtic-sea-2
freekmurze Sep 24, 2026
ead184a
Preview Cloud attachments from the machine
freekmurze Sep 24, 2026
81dd20c
Merge pull request #468 from spatie/freekmurze/cloud-attachment-preview
freekmurze Sep 24, 2026
5dc62f7
Complete Codex MCP elicitation replies (#469)
freekmurze Sep 24, 2026
2d4dff4
Improve Cloud share link controls
freekmurze Sep 24, 2026
2f21916
Merge pull request #470 from spatie/freekmurze/saint-georges-channel
freekmurze Sep 24, 2026
5eb1a4a
Show Cloud project names in workspace picker
freekmurze Sep 24, 2026
5fbc25d
Merge pull request #471 from spatie/freekmurze/english-channel
freekmurze Sep 24, 2026
77ed316
Simplify Cloud preview fallback pages (#472)
freekmurze Sep 24, 2026
fe44d8a
Fix Cloud split tab selection recursion (#473)
freekmurze Sep 24, 2026
17379f9
Add file controls to cloud workspace files (#474)
freekmurze Sep 24, 2026
28bfbd5
Keep Cloud sent message animation across queue handoff (#475)
freekmurze Sep 24, 2026
878e311
Fix browser address text selection (#476)
freekmurze Sep 24, 2026
e42ec09
Show local project artwork in Cloud workspace picker (#477)
freekmurze Sep 25, 2026
25b6cec
Highlight cloud file previews (#478)
freekmurze Sep 25, 2026
babb536
Remove address chip with Backspace at composer start (#479)
freekmurze Sep 25, 2026
1fde1a7
Keep requested screenshots visible after folded work
freekmurze Sep 25, 2026
1b6005d
Merge pull request #480 from spatie/freekmurze/bering-sea
freekmurze Sep 25, 2026
fb96773
Keep archive confirmation layout stable
freekmurze Sep 25, 2026
df49a90
Merge pull request #481 from spatie/freekmurze/barents-sea-2
freekmurze Sep 25, 2026
c1de21d
Add Cmd+R to reload the active browser tab
freekmurze Sep 26, 2026
ffc9721
Merge pull request #482 from spatie/freekmurze/korea-strait
freekmurze Sep 26, 2026
0234d51
Speed up composer dictation paste (#483)
freekmurze Sep 26, 2026
b3aca22
Return Bloom to local workspaces (#484)
freekmurze Sep 27, 2026
8074bfd
Update stored tab spelling test after setup removal (#485)
freekmurze Sep 27, 2026
d335181
Offer Claude Sonnet 5.5 before the CLI lists it (#486)
freekmurze Sep 29, 2026
da26558
Avoid flashing Archive anyway during quick safety checks (#487)
freekmurze Sep 29, 2026
feb35ec
Reflect scrolled browser page in its toolbar (#488)
freekmurze Sep 29, 2026
9c79dcf
Speed up transcript scrolling and workspace arrival (#489)
freekmurze Sep 29, 2026
03998b0
Offer GPT-6.1 Sol while Codex rollout catches up
freekmurze Sep 29, 2026
9a852af
Merge pull request #490 from spatie/freekmurze/gulf-of-guinea
freekmurze Sep 29, 2026
1349eb9
Finish workspace feedback requests (#491)
freekmurze Sep 30, 2026
4e74f76
Add Show in Finder to transcript file links (#492)
freekmurze Sep 30, 2026
73dcc9e
Avoid welcome window crash during animated resize (#493)
freekmurze Sep 30, 2026
358d3da
Make agent workspace limit configurable
freekmurze Sep 30, 2026
169cee5
Merge pull request #494 from spatie/freekmurze/great-australian-bight
freekmurze Sep 30, 2026
b30834e
Show prompt copy icon on bubble hover (#495)
freekmurze Sep 30, 2026
2863b6c
Restore archived workspaces through the MCP bridge
freekmurze Oct 1, 2026
560dc1d
Merge pull request #496 from spatie/freekmurze/mona-passage
freekmurze Oct 1, 2026
206fd09
Use permission preferences for Ask Bloom (#497)
freekmurze Oct 1, 2026
3d96399
Add local workspace merge without GitHub
freekmurze Oct 1, 2026
ecf1477
Merge branch 'freekmurze/implement-third-request-from-customer-merge'
freekmurze Oct 1, 2026
10cf8e7
Add project-free conversation workspaces
freekmurze Oct 1, 2026
a11ec7d
Merge remote-tracking branch 'origin/main' into freekmurze/manila-bay
freekmurze Oct 1, 2026
817b526
Merge pull request #498 from spatie/freekmurze/manila-bay
freekmurze Oct 1, 2026
e9a6999
Reuse transcript heights across tab switches
freekmurze Oct 1, 2026
b622f05
Merge branch 'freekmurze/implement-first-request-from-customer-agents'
freekmurze Oct 1, 2026
1edc5ab
Merge remote main
freekmurze Oct 1, 2026
9533eff
Polish sidebar conversations and archive navigation
freekmurze Oct 1, 2026
c5f8469
Offer direct GitHub merges in PR confirmation
freekmurze Oct 1, 2026
a8a1fe9
Refine conversation status and creation sheet
freekmurze Oct 1, 2026
0ea2ec9
Remove sidebar footer help control
freekmurze Oct 1, 2026
9af2944
Show branch actions in a confirmation popover
freekmurze Oct 1, 2026
11145ea
Add local workspace merge and clearer project headers
freekmurze Oct 1, 2026
32c3b05
Allow sidebar sections to be reordered
freekmurze Oct 1, 2026
28b4044
Merge remote-tracking branch 'origin/main' into freekmurze/create-blo…
freekmurze Oct 1, 2026
52e0b51
Merge pull request #499 from spatie/freekmurze/create-bloom-workspace
freekmurze Oct 1, 2026
51005a2
Reveal answer copy on hover and animate sidebar section reorder
freekmurze Oct 1, 2026
47781d9
Add tabs and split panes to conversations
freekmurze Oct 1, 2026
cbef11b
Simplify create PR popover and remove local merge
freekmurze Oct 1, 2026
5f2adb5
Restore project titles and make sidebar sections draggable
freekmurze Oct 1, 2026
4e192fa
Space project groups and align conversation action
freekmurze Oct 1, 2026
690a9be
Remove sidebar selection focus outline
freekmurze Oct 1, 2026
ad5e2e1
Swap sidebar sections with direct heading drag
freekmurze Oct 1, 2026
4b0f6ba
Offer agent or direct pull request creation
freekmurze Oct 2, 2026
230a207
Use Create PR for both creation routes
freekmurze Oct 2, 2026
72a7212
Track sidebar section drags from title text
freekmurze Oct 2, 2026
3e9d8b2
Show conversation archive control and surface archived chats
freekmurze Oct 2, 2026
1735369
Confirm conversation archiving in sidebar popover
freekmurze Oct 2, 2026
86180eb
Mix conversations into dated Home workspace list
freekmurze Oct 2, 2026
8f67b0f
Defer transcript height reports until layout completes
freekmurze Oct 2, 2026
24d6eb4
Merge pull request #500 from spatie/freekmurze/fix-message-rendering-…
freekmurze Oct 2, 2026
23cbdf5
Keep Home row hover updates local while scrolling
freekmurze Oct 2, 2026
143fff4
Route menu shortcuts to highlighted workspace
freekmurze Oct 2, 2026
f2cbbd5
Merge pull request #501 from spatie/freekmurze/fix-worktree-shortcuts
freekmurze Oct 2, 2026
77cd572
Focus terminal after opening its tab
freekmurze Oct 2, 2026
40ca5b7
Merge pull request #502 from spatie/freekmurze/denmark-strait
freekmurze Oct 2, 2026
f86fa64
Keep long transcript scrolling responsive (#503)
freekmurze Oct 3, 2026
e7fa892
Preserve transcript position when sending (#504)
freekmurze Oct 3, 2026
2736419
Fix inspector width and project browsing (#505)
freekmurze Oct 3, 2026
dc605e2
Resume pending messages after permission answers
freekmurze Oct 4, 2026
725af86
Merge pull request #506 from spatie/freekmurze/natuna-sea
freekmurze Oct 4, 2026
32d725e
Allow workspace agents to add existing projects (#507)
freekmurze Oct 4, 2026
aab0b80
Allow Bloom Dev restarts during development
freekmurze Oct 4, 2026
d9db6a7
Share transcript presentation with the browser client
freekmurze Oct 4, 2026
607933f
Bring browser client closer to Bloom
freekmurze Oct 4, 2026
1742a4c
Keep mobile file review behind a toolbar control
freekmurze Oct 4, 2026
ce57c38
Open browser client without manual connection
freekmurze Oct 4, 2026
e38199d
Bring browser attachments and agent responses closer to Mac
freekmurze Oct 5, 2026
7a6d79a
Polish browser client layout and controls
freekmurze Oct 5, 2026
3322f7e
Use Bloom mark and streamline Dev browser access
freekmurze Oct 5, 2026
04ecd20
Match browser composer keys and controls to Mac
freekmurze Oct 5, 2026
0658143
Document browser access and mark it beta
freekmurze Oct 5, 2026
b24bf83
Keep long transcripts responsive
freekmurze Oct 6, 2026
f07fe90
Merge pull request #508 from spatie/freekmurze/bothnian-bay
freekmurze Oct 6, 2026
a10e50b
Mark browser access as alpha
freekmurze Oct 6, 2026
3141dc9
Merge remote-tracking branch 'origin/main' into freekmurze/gulf-of-me…
freekmurze Oct 6, 2026
32af01b
Merge pull request #509 from spatie/freekmurze/gulf-of-mexico
freekmurze Oct 6, 2026
3e5dd97
Cache transcript text measurements across widths
freekmurze Oct 7, 2026
700c193
Merge pull request #510 from spatie/freekmurze/strait-of-dover
freekmurze Oct 7, 2026
b681bf5
Stop headless Chrome after DMG background rendering
freekmurze Oct 7, 2026
83e0573
Merge pull request #511 from spatie/freekmurze/dmg-render-timeout
freekmurze Oct 7, 2026
e2e99c8
Add project account profiles and fix sidebar reordering
freekmurze Oct 7, 2026
a1e65ad
Merge pull request #512 from spatie/freekmurze/leyte-gulf
freekmurze Oct 7, 2026
5e1b303
Keep a long pasted turn inside its bubble
HugoHeneault Oct 8, 2026
1a5aa3b
Refresh transcript and file views during live work
freekmurze Oct 8, 2026
e39ca35
Merge pull request #513 from spatie/freekmurze/gulf-of-antalya
freekmurze Oct 8, 2026
fa9a837
Keep offscreen transcript row graphs stable during measurement (#514)
freekmurze Oct 8, 2026
80879ae
Keep sidebar row content visible at rest
freekmurze Oct 8, 2026
4646a8d
Merge pull request #515 from spatie/freekmurze/sargasso-sea
freekmurze Oct 8, 2026
5ea2089
Edit a code block and attach it to the next message
HugoHeneault Oct 9, 2026
4efe8bb
End transcript selection when mouse release is lost
freekmurze Oct 9, 2026
1bb3218
Merge pull request #516 from spatie/freekmurze/bellingshausen-sea
freekmurze Oct 9, 2026
b56485f
Remove search overlay host while closed
freekmurze Oct 9, 2026
f613447
Merge pull request #517 from spatie/freekmurze/bellingshausen-sea
freekmurze Oct 9, 2026
7c2b9b7
Fade streaming replies without a per-frame timeline (#26)
joshdonnell Oct 12, 2026
2862cba
Show an Active chip on Home between All and Archived (#14)
HugoHeneault Oct 12, 2026
a61bf87
Show a permission ask's whole reason instead of cutting it in the mid…
HugoHeneault Oct 12, 2026
7ca34ce
Keep the keyboard in the composer while an agent drives a browser pan…
HugoHeneault Oct 12, 2026
22b98b5
Start new workspaces on the last model chosen in a footer (#15)
HugoHeneault Oct 12, 2026
7b0e1bb
Offer "Archive Anyway" and "Retry" when the archive script fails (#16)
HugoHeneault Oct 12, 2026
baa6b7b
fix(transcript): stop freezes when switching to workspaces with neste…
fritsbits Oct 12, 2026
b3c11eb
Select and copy across a whole conversation, and copy a sent message …
HugoHeneault Oct 12, 2026
c6f6c47
Merge PR #6 and preserve transcript measurement cache
freekmurze Oct 12, 2026
ef36d4b
Merge PR #20 with shared transcript entry context
freekmurze Oct 12, 2026
3e639db
Keep release build numbers above the last shipped build
freekmurze Oct 12, 2026
1479d6d
Keep running Codex chats on their chosen backend
freekmurze Oct 12, 2026
cad7a36
Stream answers as plain text and draw markdown once they land (#29)
joshdonnell Oct 13, 2026
f63c68f
Send typing and pasting with nothing focused to the chat composer (#30)
freekmurze Oct 13, 2026
6869392
Preview SVG files in the Files tab (#31)
freekmurze Oct 13, 2026
7cfdbc3
Merge private Bloom history into public repository
freekmurze Oct 13, 2026
5655c59
Count restored history for release build numbers
freekmurze Oct 13, 2026
c86311f
Merge latest public Bloom changes
freekmurze Oct 13, 2026
03ef789
Merge pull request #34 from spatie/freekmurze/sync-private-history
freekmurze Oct 13, 2026
137205d
Redraw the merge button when merging becomes possible (#33)
HugoHeneault Oct 13, 2026
75ffe2a
Pin today's GitHub behaviour before adding GitLab
HugoHeneault Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .claude/skills/bloom-dev-build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ changes merely to try them locally. Fast mode cannot be combined with a revision
`xcode-select -p` and `xcrun swift --version` when setup is uncertain. Signing is ad hoc by default;
no release certificate or notarisation credentials are needed. Running App Intents through
Shortcuts requires a real signing identity.
3. Run the selected command. Only restart the dev app when authorised. `make dev-fast` and
`make dev` both quit and relaunch the dev copy. `--no-launch` still replaces the installed bundle;
an already running copy does not switch to the new code until restarted.
3. Run the selected command. Bloom Dev may be restarted as needed, as described in `CLAUDE.md`.
`make dev-fast` and `make dev` both quit and relaunch the dev copy. `--no-launch` still replaces
the installed bundle; an already running copy does not switch to the new code until restarted.
4. Verify the bundle at the path printed by the script. With `--no-install`, use that build path;
otherwise use `~/Applications/Bloom Dev.app`. Check its `Contents/Info.plist` and signature:
- `CFBundleIdentifier` is `be.spatie.bloom.dev`.
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ jobs:
- name: Check the house rules
run: ./Tools/house-rules.sh

- name: Check the browser client script
run: node Tools/check-local-web-client.mjs

# And the rules that are the language's rather than ours, which is the other half.
# `.swiftlint.yml` is tuned rather than default: the head of that file has the counts every
# disabled rule produced and why each one is off.
Expand Down
13 changes: 7 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,16 +324,17 @@ have to, and names the file and line.
The owner is sitting at this Mac, working, while you run. Anything that takes focus, moves the
pointer, opens or closes a window, launches or quits an application, or puts a window in front of
what he is reading is an interruption to a person, not a step in a task. **Ask before doing any of
it.**
it, except for restarting Bloom Dev as described below.**

Needs permission every time: launching an app in a way that activates it (`open -g` does not, and
is the one to reach for), `NSApp.activate` or anything that makes a process frontmost, quitting or
signalling any application, sending a keystroke or a click or any synthetic event, capturing or
recording the **display**, changing a system setting including the appearance, and opening a
browser or an editor.
signalling an application other than Bloom Dev, sending a keystroke or a click or any synthetic
event, capturing or recording the **display**, changing a system setting including the appearance,
and opening a browser or an editor.

Needs no permission: offscreen rendering, anything headless, and capturing **your own** window by
its window id after opening it with `open -g`.
its window id after opening it with `open -g`. Agents may quit and relaunch Bloom Dev as needed for
development. Relaunch it with `open -g` so it does not take focus.

If a piece of work cannot be verified without one of these, say so and stop. An honest "I could
not photograph this" is a better outcome than an interruption, and it is what several agents have
Expand All @@ -356,7 +357,7 @@ existing dev data, and restart only the dev copy. Fast mode cannot be combined w

For agent verification without installation or launch, use
`./Tools/dev-build.sh --fast --no-install`. To install without restarting, use
`./Tools/dev-build.sh --fast --no-launch`. Restarting the dev app still needs authorisation.
`./Tools/dev-build.sh --fast --no-launch`.
See `.claude/skills/bloom-dev-build/SKILL.md` for the full workflow.

**Bloom Dev has its own identity:** bundle id `be.spatie.bloom.dev`, and with it its own
Expand Down
41 changes: 36 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,42 @@ account. Agent CLIs use their own authentication and provider connections.

Workspaces live under `~/bloom/workspaces.noindex` by default. The `.noindex` suffix keeps Spotlight
from indexing every worktree. Bloom also supports multiple chats in one workspace, reusable quick
prompts and subagents that work in the same branch. **Ask Bloom** is a separate conversation area
for questions that do not need a worktree.

Useful shortcuts include Cmd+N for a new workspace, Cmd+P to search files in the current workspace
and Cmd+T for a new conversation. See [the menu reference](docs/MENUS.md) for the full list.
prompts and subagents that work in the same branch. Conversation workspaces appear below projects
in the sidebar and need no project, Git repository or worktree.

Useful shortcuts include Cmd+N for a new workspace, Cmd+J for a project-free conversation and
Cmd+P to search files in the current project workspace. See [the menu reference](docs/MENUS.md)
for the full list.

### Browser access (alpha)

Browser access is in alpha and may change or be removed at any time. Bloom can show conversations
in a browser while the Mac app is running. The browser can send
prompts, create projects and workspaces, and inspect transcripts and file references. Some Mac
features, including native diff review and terminal panes, are not available there yet.

On the same Mac, open **Settings > Browser Access**, enable **Allow browser access**, then choose
**Open in Browser**. Bloom opens a one-use link, so you do not need to type an access code. Bloom Dev
also accepts direct connections to `http://127.0.0.1:53544` on the host Mac without a code.

To open Bloom from a phone or another computer through Tailscale:

1. Install [Tailscale](https://tailscale.com/docs/install) on both devices and sign in to the same
tailnet. Keep Bloom running on the Mac.
2. In Bloom's Browser Access settings, enable access and expand **Manual connection**. Note the
port in the address and use **Copy Code** for the access code.
3. In Terminal on the Mac, run `tailscale serve --bg PORT`, replacing `PORT` with the port from
Bloom. For example, Bloom Dev uses `tailscale serve --bg 53544`.
4. Tailscale prints a private HTTPS address such as `https://your-mac.your-tailnet.ts.net`. Open
it on the other device and enter Bloom's access code. [Tailscale Serve](https://tailscale.com/docs/features/tailscale-serve)
keeps this address within your tailnet.

The hostname names your Mac in Tailscale, not a separate Bloom account. You can [rename the Mac in
Tailscale](https://tailscale.com/docs/concepts/machine-names) if you want a shorter hostname. The
regular Bloom app chooses a new browser port and access code when it restarts. If the port changes,
run `tailscale serve --bg PORT` again with the new port and use the new access code. Bloom Dev keeps
port 53544, but its remote connections still require the code. To stop remote access, run
`tailscale serve off` on the Mac or turn off browser access in Bloom.

### Repository settings

Expand Down
6 changes: 3 additions & 3 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ you ticked the box on. Either one puts the item on Sparkle's `beta` channel,
which nobody sees unless the app asks for that channel.

The version in the bundle comes from the tag, not from `Resources/Info.plist`.
The build number is 2132 plus the number of commits reachable from the tag. The
offset preserves update order after the repository history was reset. Sparkle
compares that number, which is why the workflow checks out with full history.
The build number is the number of commits reachable from the tag, including the
restored release history. Sparkle compares that number, which is why the workflow
checks out with full history.

To retry a release, prefer rerunning its original release-event workflow with
`gh run rerun <run-id>`. This preserves the GitHub prerelease flag and asset attachment step.
Expand Down
1 change: 1 addition & 0 deletions Sources/Bloom/BloomApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ struct BloomApp: App {
// resize. It is how somebody finally watches the transcript go blank. See `ComposerProbe`.
if ComposerProbe.isRequested { ComposerProbe.schedule() }
if TranscriptLayoutProbe.isRequested { TranscriptLayoutProbe.schedule() }
if TranscriptAppendProbe.isRequested { TranscriptAppendProbe.schedule() }
if CommentFocusProbe.isRequested { CommentFocusProbe.schedule() }
if MessageArrivalProbe.isRequested { MessageArrivalProbe.schedule() }
if ArchiveFailureProbe.isRequested { ArchiveFailureProbe.schedule() }
Expand Down
2 changes: 1 addition & 1 deletion Sources/Bloom/Design/ComposerPickerGallery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ struct ComposerPickerGallery: View {

VStack(alignment: .leading, spacing: Metrics.pane) {
panel(
"Ask Bloom, where the footnote is the one thing left in it",
"A conversation, where the footnote is the one thing left in it",
options: permissionOptions(on: .claudeCode),
selection: PermissionMode.auto.rawValue,
heading: "Permission mode",
Expand Down
23 changes: 23 additions & 0 deletions Sources/Bloom/Design/Confirmation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ struct ConfirmationSheet: View {
let confirmation: Confirmation
let onConfirm: () -> Void
let onCancel: () -> Void
/// Shown only when the question has a third answer, such as trying a failed step again.
var onAlternative: (() -> Void)?

/// Which button the keyboard is on. It starts on the safe one, and `Keys` says why that is
/// not a detail.
Expand Down Expand Up @@ -61,6 +63,7 @@ struct ConfirmationSheet: View {
case .standard:
VStack(spacing: Layout.betweenButtons) {
confirmButton
alternativeButton
cancelButton
}
.padding(.horizontal, Layout.buttonInset)
Expand Down Expand Up @@ -102,6 +105,17 @@ struct ConfirmationSheet: View {
.focused($focus, equals: .confirm)
}

@ViewBuilder
private var alternativeButton: some View {
if let label = confirmation.alternativeLabel, let onAlternative {
Button { onAlternative() } label: {
Text(label).frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.controlSize(.large)
}
}

/// The answer that changes nothing, and the one Escape is wired to.
private var cancelButton: some View {
Button { onCancel() } label: {
Expand Down Expand Up @@ -249,6 +263,8 @@ struct Confirmation: Equatable, Sendable {
var cancelLabel: String
var tone: ConfirmationTone = .destructive
var layout: ConfirmationLayout = .standard
/// A third answer between confirming and cancelling. Drawn by the standard layout only.
var alternativeLabel: String?
}

enum ConfirmationLayout: Equatable, Sendable {
Expand Down Expand Up @@ -301,6 +317,7 @@ extension View {
_ item: Binding<Item?>,
_ question: @escaping (Item) -> Confirmation,
onConfirm: @escaping (Item) -> Void,
onAlternative: ((Item) -> Void)? = nil,
onCancel: @escaping () -> Void = {}
) -> some View {
sheet(isPresented: item.isPresent()) {
Expand All @@ -314,6 +331,12 @@ extension View {
onCancel: {
item.wrappedValue = nil
onCancel()
},
onAlternative: onAlternative.map { alternative in
{
item.wrappedValue = nil
alternative(value)
}
}
)
}
Expand Down
17 changes: 5 additions & 12 deletions Sources/Bloom/Design/JumpProbe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@ import BloomCore
///
/// The sixth of the family, and the only one that is not about frame times. The others ask what a
/// gesture COST; this one asks whether a gesture DID WHAT IT SAID, because the bug it was written
/// for was not slow, it was wrong. The jump pill scrolled one frame, that frame posted
/// `boundsDidChange`, the coordinator read its own write as the reader taking hold, and the glide
/// stopped itself at nothing. With Reduce Motion on it worked, because that path jumps rather than
/// glides, which is exactly the kind of fault that survives a hand test: whoever checks it once,
/// in the wrong accessibility setting, comes away satisfied.
///
/// `TranscriptModel.submit` bumps the same counter, so "the chat does not scroll down when I say
/// something" was the same bug wearing different clothes. Both are covered here.
/// for was not slow, it was wrong. The jump pill once scrolled one frame, that frame posted
/// `boundsDidChange`, and the glide stopped short. The pill now places the table at its end
/// immediately, and this probe checks that the placement remains correct as heights change.
///
/// Bloom --jump-probe /tmp/jump.json --jump-workspace <id>
/// [--jump-settle 2500] [--window-size 1440x900]
Expand All @@ -42,10 +37,8 @@ enum JumpProbe {

/// How long a jump is given before it is judged, in milliseconds.
///
/// Generous on purpose. The glide is a display link over a fixed duration, and the standing
/// instruction it hands over to is re-asserted on the next run loop turn and again on every
/// height correction, so the honest question is where the view came to rest rather than where
/// it was a frame after the request.
/// Generous on purpose. The standing instruction is re-asserted on the next run loop turn and
/// again on every height correction, so this checks that the instant placement stays put.
private static var settle: Duration {
.milliseconds(ProbeHarness.count("--jump-settle", or: 2_500))
}
Expand Down
1 change: 1 addition & 0 deletions Sources/Bloom/Design/MenuProbe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ enum MenuProbe {
NSHostingMenu(rootView: SidebarFilterMenuItems(
filter: .constant(.all),
showsHiddenProjects: .constant(true),
showsEmptyProjects: .constant(true),
hiddenCount: 2,
grouping: .constant(SidebarGrouping.fallback.rawValue)
))
Expand Down
61 changes: 38 additions & 23 deletions Sources/Bloom/Design/MessageArrivalEffect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,49 @@ extension View {
}
}

/// Time belongs to the message, not the hosting cell. This survives the instant-to-saved handoff
/// and ignores the table's animation-free layout transaction. Only these drawing attributes tick;
/// the supplied content is not rebuilt or re-parsed by the timeline closure.
/// Fades a reply in as it starts to stream. Time belongs to the message, not the hosting cell, so
/// a new view for an arrival already under way picks up where it is and survives the handoff from
/// the streaming slot to the stored row.
///
/// One structure and one explicit animation. This was an `if let` around a
/// `TimelineView(.animation)`, which could rebuild the whole reply on its second token while its
/// markdown `VStack` was growing, and crashed inside SwiftUI's stack layout. Explicit, as in
/// `ArrivingRow`, because the cell root clears implicit animation.
private struct MessageArrivalEffect: ViewModifier {
let arrival: MessageArrival?
@Environment(\.accessibilityReduceMotion) private var reduceMotion
@State private var finishedArrival: MessageArrival?

/// Taken once, when this view is made. Reduce Motion is applied in `settle`, because the
/// environment cannot be read here.
@State private var opacity: Double

init(arrival: MessageArrival?) {
self.arrival = arrival
_opacity = State(initialValue: Self.opacity(of: arrival, at: Date()))
}

func body(content: Content) -> some View {
// Sent bubbles enter through the composer's occlusion as the transcript scrolls.
// A second fade, scale and offset on the row made that one movement look disconnected.
if let arrival, arrival.style == .reply {
let finished = finishedArrival == arrival
TimelineView(.animation(paused: finished || reduceMotion)) { context in
let pose = finished ? .settled : arrival.pose(at: context.date, reduceMotion: reduceMotion)
content
.opacity(pose.opacity)
.scaleEffect(pose.scale, anchor: .bottomTrailing)
.offset(y: pose.rise)
}
.task(id: arrival) {
let remaining = arrival.remaining(at: Date())
if remaining > 0 { try? await Task.sleep(for: .seconds(remaining)) }
guard !Task.isCancelled else { return }
finishedArrival = arrival
}
} else {
content
content
.opacity(opacity)
.onChange(of: arrival, initial: true) { _, arrival in settle(arrival) }
}

private func settle(_ arrival: MessageArrival?) {
// Text the reader can already see is not faded again.
guard opacity < 1 else { return }
let remaining = arrival?.remaining(at: Date()) ?? 0
guard arrival?.style == .reply, !reduceMotion, remaining > 0 else {
opacity = 1
return
}
// The cubic ease out `MessageArrival.pose` describes.
withAnimation(.timingCurve(0.33, 1, 0.68, 1, duration: remaining)) { opacity = 1 }
}

/// Only a reply fades. Sent bubbles already move in behind the composer, and a second fade
/// made that look disconnected.
private static func opacity(of arrival: MessageArrival?, at date: Date) -> Double {
guard let arrival, arrival.style == .reply else { return 1 }
return arrival.pose(at: date, reduceMotion: false).opacity
}
}
1 change: 1 addition & 0 deletions Sources/Bloom/Design/SidebarIndentGallery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ struct SidebarIndentGallery: View {
private func header(count: Int) -> some View {
RepoHeaderRow(
repo: Self.repo,
isFirstProject: true,
hasUnreadWork: count > 0,
workspaceCount: count,
onCreateWorkspace: { _ in }
Expand Down
16 changes: 16 additions & 0 deletions Sources/Bloom/Design/SwitchProbe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ enum SwitchProbe {
let ticker = Ticker(view: contentView)
ticker.start()
SwitchTrace.isEnabled = true
harness.markStarted()

var runs: [JSONValue] = []

Expand Down Expand Up @@ -117,6 +118,19 @@ enum SwitchProbe {
}
}

// A focused performance run ends here. The position, rapid-switch and background-update
// checks below answer separate correctness questions and extend a timed run by minutes.
if ProbeHarness.isPresent("--switch-timing-only") {
SwitchTrace.isEnabled = false
ticker.stop()
harness.write(.object([
"driver": .string(driver),
"order": .strings(order.map(\.rawValue)),
"runs": .array(runs),
].merging(harness.conditions(window: window)) { mine, _ in mine }))
exit(0)
}

// Whether a reader who was NOT at the live end is put back where they were, which is the
// half of `TranscriptResume` the runs above cannot reach: a probe that only ever sits at
// the end measures the flag, never the anchor.
Expand Down Expand Up @@ -154,6 +168,7 @@ enum SwitchProbe {
return [:]
}
ticker.beginRun()
let cpuBefore = ProbeHarness.mainThreadCPUSeconds()
// Which pane's layout the freeze is in. `FrameProbe` uses the same counters for the same
// reason: "the window stood still for 300ms" is not an answer until it says which of the
// two columns stood still.
Expand Down Expand Up @@ -203,6 +218,7 @@ enum SwitchProbe {
"panePasses": .map(PaneLayoutTiming.timeline()),
"transcriptHold": .map(TranscriptHoldCensus.summary()),
"worstFrameMs": .number(ticker.intervalsMs.max() ?? 0),
"mainThreadCpuMs": .number((ProbeHarness.mainThreadCPUSeconds() - cpuBefore) * 1_000),
]
}

Expand Down
Loading
Loading