Repository navigation
release: 0.4.0 - #47
Merged
Merged
Conversation
…s accounts The wrapper was a single shared scriptDir/grove-statusline.sh with each account's GROVE_USAGE_DIR/GROVE_ORIG baked in, and every account's settings.json pointed at it — so enabling monitoring on a second account clobbered the first's baked env (account A would write usage into B's dir and run B's statusline). Ship one wrapper per CLAUDE_CONFIG_DIR (keyed by canonicalized config dir); each settings.json points at its own. Regression-tested with two accounts.
On macOS 26.4 the MenuBarExtra scene machinery sends terminate: to the app moments after the status item appears (status-item visibility action → applicationShouldTerminate), so the menu-bar app quit at launch — the icon only flashed. Reproduced identically on the pre-change build, confirming it's the MenuBarExtra/OS interaction, not feature code. Replace the MenuBarExtra scene with a plain NSStatusItem + NSPopover hosting RootView (activation policy .accessory); the manual status item has no scene lifecycle that terminates the process, so it stays resident. Verified: process persists, 0 terminate events.
Menu bar (items 1,3,6): - Fresh bundle id dev.artemefimov.grove — the old identity's WindowServer menu-bar layout cache was corrupted by the MenuBarExtra crash-loop, pinning the status item off-screen behind Control Center (only a reboot clears it; a fresh id gets a clean visible slot, proven by A/B test). - Tree-only status item (no " Grove" text); real app bundle icon (AppIcon.icns + generator script). - Global click monitor dismisses the panel on an outside click. Perf (item 2): - refreshUsage now runs its file I/O + JSON parsing OFF the main actor and concurrently with the scan, so a cold transcript parse no longer freezes the panel on open. - Persistent ClaudeService / UsageAnalytics on AppState so their mtime parse-caches survive across scans (a fresh instance per 15s tick re-parsed every transcript). Marked @unchecked Sendable (lock-guarded caches). - os.Logger timings (subsystem dev.artemefimov.grove). Charts data (item 4): - AccountUsageAnalytics.daily: 7 calendar-day token buckets for the Daily Usage chart, with test.
Item 4/9 (charts): a native Swift Charts usage dashboard matching the reference — 5-Hour / Weekly limit bars (progress bar + reset + on-track/limit-close), Usage Rate line chart, Daily Usage bar chart (Sun…Today, coloured by intensity), Token Usage table + per-model breakdown. Overall + per-account columns side by side. Pure, fully-tested presentation models (DashboardPresentation). Item 4/23 (two-tab shell): RootShell hosts Projects | Charts tabs; the selection lives in AppState so it survives the panel closing (state preserved on minimize). Panel width adapts per tab. Item 4/7/24/25 (projects): the Projects tab is the primary view — each project card shows its 2 most recent Claude sessions as distinct, tappable blocks with a running/waiting/closed status and a Go/Resume terminal target. Powered by a cheap bounded recent-session indexer (ClaudeService.recentSessions) + a presentation join (ProjectSessionRows), refreshed off the main actor — no git scan needed for the routing flow. Snapshot: new charts.png scene + fixture session previews + daily data; projects scene resized for the new shell.
- Render every snapshot scene end-to-end (testAllScenesRenderToNonEmptyPNGs): drives every routed view body, doubling overall line coverage. - DashboardRenderTests / ViewStatesRenderTests: live Swift Charts paths, empty/no-data/scanning/search/error-banner branches. - ProjectSessionRows + AppState.openSession/refreshSessionIndex tests. - CmuxAppleScript backend tests via a scripted runner (list/select/close/error), plus the daily-usage and recent-session-index tests from the feature work. - Latency guard: the Projects-tab indexer stays < 0.5s with 80 transcripts. 452 tests; new feature code at 92–99% line coverage.
Lifts total line coverage to ~85% (87.5% excluding the NSApplication entry point and the real-socket cmux probe). 453 tests.
Adds targeted tests closing the gaps in the pure/mockable logic layer: - OAuthUsageClient: noCredentials/http/malformed/backoff-suppression/int-util. - ClaudeService: custom-account identity + organizationRateLimitTier, process validator toggling live processes, launch-command env prefix. - GroveError: every case's description. - AppState: project/account CRUD, model/effort setters, aggregateRemaining, monitoring toggle, cmux go/open-shell. - Config: minimal-JSON back-compat decode + full round-trip. Scripts/coverage.sh + COVERAGE.md define the policy: the unit-coverage GATE is the logic layer (services/models/presentation/state) at ≥95%; SwiftUI views are validated by snapshot/render tests, and real cmux/osascript/socket adapters + the NSApplication entry are validated by manual/integration testing.
Root cause of the jump: the panel was an NSPopover, which re-positions relative to the status button on every content-size change — so each tab/route switch (Projects↔Charts↔project scope) re-centered it. Replaced with a borderless GrovePanel pinned by its TOP-RIGHT corner to the icon: any resize grows it left/down, never jumps, and it's exactly right-edge-aligned (item 6). Fixes from the adversarial review of the panel change: - Drop .nonactivatingPanel — it blocked the panel from becoming key, so the search field got no keyboard. Plain .borderless + canBecomeKey + activate. - Observe preferredContentSize (KVO) and setContentSize + re-pin — an NSWindow does not reliably resize itself to a contentViewController's preferred size, so tab switches would have clipped instead of resizing. - invalidateShadow() on resize so the rounded window shadow tracks the shape. Runaway-CPU fix (also a real leak): RootView's 15s refresh .task kept running after the panel hid (orderOut doesn't cancel a .task) and in headless renders — spinning forever against the real ~/.claude. Gated it on AppState.isPanelOpen (.task(id:)), set by the controller on show/hide; false in tests/renders so the loop never starts. Full suite back to 41s (was hanging).
Claude Code runs statusLine.command via `sh -c`, and the production scriptDir (~/Library/Application Support/Grove/bin) contains a SPACE. StatuslineInstaller wrote the wrapper path UNQUOTED, so every render failed with "…/Library/Application: not found" (exit 127): the wrapper never ran, no rate-limit snapshot was ever written, and the dashboard showed zero limits for every account. Now the command is shell-quoted (and re-install recognises both the quoted and legacy-bare forms). Regression test runs the installed command through `sh -c` with spaces in the path and asserts a capture is written.
Real Claude captures send rate_limits.*.resets_at as a Unix epoch number, but UsageReader only read it as an ISO8601 string -> the limit % showed but the "Resets in" countdown was always blank. normalizedResetsAt now accepts an ISO string OR an epoch (seconds, or ms) and emits an ISO8601 string the rate-limit model parses. Tests cover both forms.
- Charts tab now shows ONE scope at a time (Overall / each account) chosen with ‹ › arrows (state.chartsScopeIndex, persisted). Same widgets, different scope. - No scroll: the panel is a single ~320 column with adaptive height, sized to fit all cards (RootView charts frame is width-fixed/height-adaptive, cap 820). - Removed the Usage Rate line chart. The per-session capture data can't support a trustworthy usage-over-time line: sessions cache their own last-seen limits, and many captures share one capturedAt with different (often stale) values — which drew the bogus near-vertical "spike". - Fixed the underlying staleness: currentWindow() now selects the latest capture whose resets_at is still in the FUTURE, so the 5h/weekly bars (and the aggregate) show the CURRENT value (e.g. 23%/5%) instead of a stale already-reset 53%/88%. Tests cover fresh-vs-stale selection and the all-stale fallback.
1. Token Usage columns span the full card width (evenly distributed, was bunched).
2. Charts and Projects tabs share one width (460) — identical, non-truncating
header/footer, no width-jump on tab switch.
3. Graph: a branch takes its horizontal offset in a SHORT rounded bend near the
lower-lane endpoint and is vertical otherwise — no gradual diagonal drift.
4. Limit cards show the absolute reset time ("Resets in: 59m at 10:03 PM"); Daily
Usage labels its unit ("tokens / day") and shows per-bar detail on hover.
5. Corner radii scaled back to standard macOS rounding (panel 16 / card 12 / field 8).
6. Sessions are now a distinct, bordered "Sessions" sub-table with detail
(status · title · account · model · tokens · cost · age · Go/Resume), set apart
from the workspace repos; junk caveat titles fall back to a short id.
7. Account card: identity (email · org · tier) on its own full-width row instead
of wrapping into the controls, which move to a separate aligned row; Remove is
snapshot-safe. Account limit bars also use the stale-aware currentWindow.
Charts snapshot scene widened to 460; DesignRadius tests updated. 479 tests pass.
…footer Charts is now a ~260 column (≈half the previous 460). To keep the shared RootShell header/footer clean at that width: the brand is the tree mark only (no "Grove" text), the tab strip is text-only chips (no wrap), the footer version is "v0.1.0" with fixed-size labels (no more "Ac/cou/nts" wrap). Token "This Month" row shortened to "Month" so the table fits the narrow column. Projects stays 460.
…s tag, standard corners
Item 2 — session statuses were бутафория: Claude Code writes live status
"busy"/"idle"/"shell" (never "waiting"), and buildProjectSessionRows mapped both
busy AND idle to "running". Now only "busy" is running; "idle"/"shell" read as
"waiting" — the finished-its-turn, awaiting-your-prompt state you route to.
Item 1.1 — Token Usage numbers and the Daily-Usage hover truncated ("$78.1…") at
the narrow Charts width. Fixed the layout: a narrow fixed label column so the four
numeric columns get the room, scale-don't-truncate (no dots), shortened model ids
("claude-sonnet-4-6" → "sonnet-4-6"), and widened Charts 260→290 — the narrowest
width where a 4-number row stays fully legible.
Item 4 — added an overall-limits tag to the header: "5h 62% · 59m" = tier-weighted
remaining capacity across accounts + the SOONEST upcoming reset (the next time any
account's window refreshes). New AppState.aggregateReset documents the principle.
Item 3 — corners reduced to standard macOS rounding (panel 10 / card 6 / field 5,
was 16/12/8) so text no longer crowds the curve.
Tests updated (radii, charts size, idle→waiting) + added aggregateReset and
shortModelName. 481 pass.
…ts the statusline can't cover Item 1.3 — work-account showed no limits (and Overall looked like one account) because Claude Code's statusline emits no rate_limits for a lightly-used custom account; its limits exist server-side but never reach the local capture. Grove now reads them straight from api/oauth/usage, authenticated with the account's own token. - ClaudeCredentials: KeychainCredentialsReader resolves the account's token from the macOS Keychain. Reverse-engineered Claude Code's service-name scheme — default account → "Claude Code-credentials"; custom CLAUDE_CONFIG_DIR → "Claude Code-credentials-<first 8 hex of sha256(configDir)>" (confirmed: the apple dir hashes to a0e8383e). Legacy .credentials.json kept as a fallback. - OAuthUsageClient now takes a CredentialsReading (Keychain by default) instead of reading a fixed file; existing file-based tests pass via the fallback. - AppState.refreshUsage consults the OAuth API ONLY for accounts whose statusline gave no fresh window (so the default account triggers no extra call/prompt), and folds the result into snapshotsByAccount as a synthetic capture — so the limit cards, the aggregate, and the header chip all light up uniformly. utilization is a 0–100 percentage (verified against the live endpoint), clamped, not scaled. Cached ≥3min with 429 backoff (already in the client). First read of another app's Keychain item prompts the user once to allow access. 12 new tests (credentials scheme + token parse + OAuth fold-in + skip gate). 493 pass.
The usage dashboard is now a SECOND always-on borderless panel docked to the LEFT of the main panel (tops aligned), opening/closing with it and re-gluing to its left edge on every resize/move — so charts stay visible beside whatever section the main window shows. The Projects|Charts tab strip is removed; the main header is now the brand + overall-limits chip + add. Adversarial review (5-dimension multi-agent pass) found and this commit fixes: - Charts panel no longer becomes key (new ChartsDisplayPanel, canBecomeKey=false): the main panel keeps key focus, so the search field and ⌘R/⌘Q keep working while you click the charts' scope arrows (buttons respond without key status). - Positioning is fully clamped: docks left, falls back to the right when there's no room, and both axes are clamped on-screen; the window height is capped to the visible screen so a tall dashboard never pushes its top above the menu bar. - windowDidResize ignores resizes delivered while a panel is hidden (no stale-anchor repositioning). - Removed now-dead RootTab/rootTab state. Also: a Token Usage tooltip documents the counting method (each message id counted once for the calendar month; resumes replay history but aren't re-billed) — verified against the transcripts: 50% of records are exact replayed duplicates, so tools that count every copy report several× higher. Grove's figures are the billable ones. 493 tests pass.
…session trend Visual overhaul toward the reference: - The WINDOW is now real Liquid Glass (.glassEffect), transparent — not a flat blur. Both panels (main + Charts) use it; RootView's dark scrim drops to a faint 0.12 so the glass shows through. - Content blocks are GRAY translucent cards (GlassCard no longer uses glass), with a pronounced iOS-26 squircle: card radius 6.9 → 10.5 (+~50%), field 5.75 → 8.5, window 14. - Tighter margins: dashboard padding 12 → 8, card padding → 10. - Limit bars thinner (height 9 → 5); daily-chart bars narrower (ratio .6 → .42). - Limit-card titles smaller (caption, full opacity — like "Resets in" but solid). New dashboard content: - Weekly Sonnet limit card (the reference's third bar). The statusline never emits it, so it comes from Anthropic's OAuth usage API (seven_day_sonnet); refreshUsage now folds OAuth limits in for EVERY account (authoritative + the only Sonnet source), keeping statusline captures as history/fallback. - 5-hour session trend: "avg N% · prev M%" + a signed "vs avg" delta, computed from the capture history (peak per completed 5h window). These apply to BOTH the Charts window and the main projects space. 495 tests pass. Note: the live Liquid Glass can't be shown in the offscreen snapshot renderer (glass renders invisible there) — verify it in the running app.
…rd icons, drop header - Status fix (B): Claude Code no longer writes sessions/<pid>.json records (that dir is empty), so every session read as "closed" even with 21 live `claude --resume` processes running. Grove now ALSO detects live sessions from the process table (session id parsed from `claude --resume <id>`); a recovered session shows "waiting" (ready for your next prompt) instead of "closed". Real status records, when present, still win. - Count fix (E): refresh() now scans EVERY configured project (the selected one each tick, the others once), so every project card shows its "N repos · M ws" — not only the project that's been opened. - Charts polish (A): limit percentages are now the same caption size as the titles (color, not size, conveys capacity); all card icons are a single green accent; Weekly Sonnet gets a distinct "Ⓢ" icon; Daily Usage and Token Usage icons accented. - Header removed (C): the brand + limits chip made no sense beside the always-on Charts window. The main window now opens straight to the project list with a simple accent "+ Add project" row at the top; the footer stays. 497 tests pass.
…t set, tidier session rows - Content substrates (both windows): ~20% darker + ~10% more transparent (white .20/.78 → .16/.70), NO light glass border, +20% corner radius (card 10.5 → 12.6, field 8.5 → 10.2, window 14 → 17). GlassCard drives both the Charts cards and the project cards, so it's consistent everywhere. - Projects window padding reduced 12 → 8 to match the Charts window. - Daily Usage bars are now wide with only ~3px gaps (ratio .42 → .88; snapshot bars fill their slot, spacing 6 → 3). - Session rows in the project cards recomposed Apple-style: title + Go/Resume on the top line, "status · location · age" below, faint borderless inset (no more bordered card-in-card); account chip dropped as clutter. - Effort picker now offers the FULL set from `claude --help` — low, medium, high, xhigh, max (was missing xhigh + max), via a shared ClaudeService.effortLevels. 497 tests pass.
…mation, launch prewarm - Swap (H): the Charts window is now the icon-anchored FIXED reference on the RIGHT; the projects/navigation window docks to its LEFT and grows leftward as routes change — so the thing that resizes often no longer shoves the fixed one. Reworked positioning: anchorChartsToIcon + dockMainLeftOfCharts (both clamped on-screen, left-else-right fallback). - Animation (G): route changes use a quick easeInOut cross-fade (.opacity) instead of a sliding spring, so the content settles cleanly while the window resizes — simpler and more native. - Prewarm (I): the heavier screens render once offscreen 0.4s after launch (fixture state, isSnapshotRender), so the first real navigation isn't paying SwiftUI's cold body-build cost. 497 tests pass.
When cmux refuses Brow's socket, the error told the user to set a socket password, which "Grove picks it up from ~/.config/cmux/cmux.json automatically"; the status-line wrapper written into Grove/bin said it was managed by Grove. Both say Brow now, as does the comment above the cmux code. A test reads every string literal in GroveCore's and GroveAppKit's sources, comments skipped, and fails on one that says Grove, except the write refused while Grove.app runs and the path of Grove's folder, which never moved.
"Your setup comes along untouched" is true of Grove's files, but not of what Brow starts doing. Grove.app never read the Keychain or called Anthropic; Brow's notch reads every account it finds, Workspaces' among them, polls the usage endpoint, keeps idle tokens fresh, and has the claude -p fallback on by default, which spends limit and starts a 5-hour window. And with the safety net no longer restoring past Claude Code's retention, the transcripts Grove kept putting back are swept at Claude Code's next clean-up. Coming from Grove (en, ru, zh-CN) gains a bullet on the notch, with where to turn the fallback off, and tells a Grove user to raise cleanupPeriodDays before switching to keep older sessions; the Workspaces guide points there.
…home Settings' launch command leaves CLAUDE_CONFIG_DIR unset for ~/.claude, judged by the process's own home. The cards now judge it by the home the accounts were found in, which AddAccountFlow already carries. The app's answer does not change; the README's Settings screenshot, whose accounts live in a made-up home, then shows the default account's command as the app would.
The two DemoScreenshotTests each had their own accounts, and the pictures disagreed: the notch showed Work 78 % / 61 %, Team 41 % / 93 % and Personal 12 % / 34 %, while Workspaces' pickers, which say they show what the notch read, showed 64/41, 22/78 and 8/19; Personal was ~/.claude in one and ~/.claude-accounts/personal in the other; Grove's config named the accounts "work" in one and by email in the other. And the menu bar's clock read "Fri 17 Oct", a Saturday. A DemoFixture target, linked by the two test bundles only, holds the accounts once: folders, names, plans, limits and their Workspaces settings, with Personal the default account the others share into. Both renders build from it; Workspaces' provider also hands back the names and plans. The notch's renders run at 09:41 today, and the menu bar's clock is formatted from that date, so its weekday is the date's.
The notch, Settings and Workspaces now show the same three accounts: Personal (the default account, ~/.claude), Work and Team, with the same plans and the same 5-hour and weekly use wherever two pictures meet, and Workspaces names them as the notch does. The menu bar's clock names the right weekday. Pictures whose pixels did not change are left as they were.
The READMEs' layout tables count about 1,350 tests: 1,346 run in debug and in release, six of them skipped unless asked for.
Troubleshooting said the lock "goes away with its process", which read as the file going away. It is the lock that macOS lets go of when the copy holding it quits, even after a crash; the empty file stays.
The test of RefreshTriggers' synchronous seed read NWPathMonitor's current path a second time and compared the two. The path is still being evaluated when start() reads it, so the second reading could differ: the test failed now and then, most often in release builds, with nothing wrong in the code. start() now reads the path through a closure the test replaces with a fixed answer. The test checks both answers, each against the opposite last word, so the old code, which left that stale value until the first callback, would still fail it.
… saved Turning capture off edits the account's settings.json first and saves Grove's config last, and that save is refused while Grove.app runs. So with Grove.app running, the switch put the user's own command back while the config still said the account was captured, and showed "Not saved". Turning capture on, and Share sessions, did the same the other way round: a wrapper installed, or session folders linked, that the config never recorded. The three actions now ask whether the config may be saved before they touch anything, and change nothing when it may not. The message is the same as before.
…g its files Grove.app running pauses Workspaces only when Workspaces is wanted. With it off, nothing changed: Settings kept the switches that give a captured status line back, Give them back and Remove from Workspaces enabled, though Grove.app writes the same config.json and settings.json. Turning capture off then edited settings.json and could not save the config (the previous commit makes that change nothing at all). Settings now disables those controls while Grove.app runs, whether Workspaces is paused or off, and the note under the account cards says to quit Grove. The host refuses such a change before it reads or writes anything, and follows Grove.app starting and quitting through the running-apps watch it already keeps.
What went wrong giving a status line back, or removing an account whose folder is gone, while Workspaces is not on was one value, shown under every capture switch and every gone-account row, so one account's failure appeared under all of them. Each failure is now kept by the account it concerns and shown under that account only. Give them back, in Settings › Workspaces, lists the failures of its accounts, each once: Grove.app running fails them all the same way. A failure goes when its account leaves the list. With Workspaces on, giving back shows its failure there too, instead of in the panel's banner.
Workspaces calls an account by the name of the notch's card that shares its folder. The notch folds one organisation's folders into one card, and Grove's config can hold an account for each: both then read "Work" in Resume as…, Migrate to…, New Claude, the session rows and the project's default account, where Grove's own names had told them apart. The first of them in Grove's config keeps the card's name, as Settings › Accounts joins the first to the card; the others keep Grove's names, which Settings lists them under. Messages from a change made with Workspaces off name the account the same way.
docs/workspaces.md said that while Workspaces is off no file is read or written for it, and docs/architecture.md that no state exists, so not even Grove's config.json is read; WorkspacesHost's own summary said the same. Neither is true since a captured status line can be given back with Workspaces off: Claude Code keeps running the wrapper, which keeps writing <account>/grove/usage, Settings reads Grove's config to list those accounts, and giving one back builds a state for that one change. Both docs now name that exception, as Settings, the README, SECURITY.md and the changelog already do. The architecture notes also say that the capture switch and Share sessions change nothing when the config could not be saved, and that Settings waits for Grove.app with Workspaces off.
The render draws the safety net on and two accounts capturing their status line, a setup Grove made, but its host had no Grove setup recorded and no config on disk, so the text above said the safety net "starts off for a new setup". True for a new setup, and still read as the contradiction it was meant to remove. The demo's settings now record a Grove setup, so the render carries the words a Grove setup is told, and a test that runs with the suite holds the render to that.
The README's picture of Settings › Workspaces draws a setup Grove made, the safety net on and accounts capturing, and now says what such a setup is told: it runs the projects, accounts and settings you had in Grove, and the safety net keeps the setting Grove gave it. Every other README picture renders the same as before and is left as it was.
Glim lives in the black beside the notch, in the strip below it, or in the pill on a display without one, and eats what your Claude Code accounts use: when the 5-hour number goes up, it takes a bite. Hours at the Mac without Claude Code make it hungry; time away never does. When the 5-hour limit is used up it falls asleep full until the window resets, and when the weekly limit is used up it hibernates in its den behind the camera until the weekly reset. Hover the notch and it hops into a new first row of the panel, with its state, its last meal and a hunger meter; a click on it there boops it. It draws only while someone can see it, follows Reduce Motion, Reduce Transparency and Increase Contrast, and keeps its state in pet.json. It is on by default. Settings › General › Show the pet turns it off, and with Show Brow in the notch off there is no pet at all.
The BrowKit line counts Model/, State/ and all of Pet/, the pet's views, senses and perf log included, but its comments and COVERAGE.md called it models and state without the views. They now list what it measures, and the line reads "BrowKit coverage" rather than "BrowKit logic coverage". The scope itself is unchanged.
The linker's debug map in Brow and in brow (its N_OSO and N_SO entries) named every object file and source folder by its absolute path on the build machine. build-brow.sh now strips it with strip -S before signing; nothing reads it at run time and no dSYM is made from these copies. release.sh refuses a binary that still names the source tree or carries the map.
The notch, the panel and the menu-bar item hung from a blue, purple and peach gradient; they now hang from cream, the colour of Glim, as the animations and the website do: #F3E8D2 under a #FAF4E7 menu-bar band of the same height, both flat. White glyphs all but vanish on that band, so the menu-bar item is drawn dark, the way a light menu bar tints a template image, in the ink #2A2218. It comes back from ImageRenderer in half floats, which were dithered on the way into the 8-bit PNG; they are now rounded into an 8-bit image first, so its desktop is one colour too. Only the five pictures with a desktop are re-rendered, and what hangs from it is unchanged, the clock included.
The English, Russian and Chinese pages say version 0.4.0 of 17 October 2026, as the changelog does, and gain a section about Glim, the pet beside the 5-hour readout: what it eats, its glow as its hunger meter, its naps when a limit is used up, the panel header, and that it sends nothing and reads no transcripts. The privacy list and the questions say the same. media/glim.webp and media/glim-still.webp are copies of notch.webp, holding the place of Glim's renders.
The closing paragraph about Workspaces becomes a section of its own, before the install notes, on the English, Russian and Chinese pages: what a workspace is, the two ways to move a session to the account with room, where to find Workspaces (Brow's item in the menu bar or the button in the notch panel), the brow tool with grove as its second name, cmux or Terminal.app, and Coming from Grove. Two screenshots from the README go with it: a project's workspaces and the launch sheet with each account's limits. The hero names Workspaces too. The install notes add the brow tool and how to start Workspaces, the requirements add git and cmux, and the checksum line takes --ignore-missing now that SHA256SUMS.txt covers brow-cli.zip as well. The questions add the menu-bar item macOS hides beside the notch, and the structured data lists the new features.
Grove's two guides become Brow pages, rewritten for 0.4.0: Run Claude Code across multiple repositories (/brow/multiple-repositories/) and Continue a Claude Code session on another account (/brow/move-session-to-another-account/). Workspaces opens from Brow's menu-bar item or the notch panel, the command-line tool is brow with grove as its second name, accounts are added and shared in Settings › Accounts, every account picker shows that account's limits, a start point only origin has is taken as origin/<name>, and the one-writer checks look for the session's folder as well as its ID. The comparison with claude --worktree from Grove's landing page joins the multi-repo guide. The landing pages, the accounts guide and the 404 page link to the new pages instead of the Grove site, and the sitemap lists them. The screenshots are the README's, as WebP.
Its structured data becomes the same graph as the two guides that moved from Grove: a TechArticle with its own @id, and a BreadcrumbList from Brow's page to the guide. The head gains article:modified_time.
The app icon's shaded grey tile, the website's favicons and the margin around the Copy my week picture turn flat cream, #F2DFB8; the shape, the notch and its two dots stay as they were.
Brow's colour becomes the cream of its pet Glim. The mark in the header of every page is a cream tile, #f2dfb8, instead of grey, with the same black notch and green and orange dots. The Download button and the skip link are cream with #2a2218 text and a faint edge, in light and dark alike; hover and keyboard focus darken the cream, and focus keeps its ring. Page colours, the pictures and the favicons stay as they are.
The hero, the calendar and the sign-in loops are rendered again from the app's own views, with Glim in the strip and in the panel's header, on the cream desktop of the screenshots. A new section shows Glim's day: two meals, asleep at the 5-hour limit, into its den at the weekly limit and out again at the reset. The social preview shows the panel with Glim.
The site's hero, calendar, sign-in and Glim loops, the notch with Glim between the readouts, the Accounts settings with their Workspaces section, and og.png, with alt texts that say what each one shows.
A write to history.jsonl within a quarter of a second of the last prompt was folded into it, so three quick appends whose events reached a slow machine 0.3 s apart counted as two prompts. Each write is now measured from the write before it.
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.
Grove is now part of Brow, as Workspaces, and the notch gets Glim, a pet that eats your limits. Grove's code comes in with its whole history, so this merges with a merge commit rather than a squash; the changelog has the details.