flare
docs.html

Documentation

How flare works, what it reads and what it sends, what it costs to run, and every setting, IPC call and command it has. The README covers the same ground in less depth.

Overview

flare is two programs that work as one:

  • The widget (ui/, QML for Quickshell) draws the notch, its hover card, the usage panel and the settings page. It never reads a provider itself.
  • flare (core/ and cli/, Rust) does all the reading. The widget runs it, it prints what it found as JSON on stdout, and it exits.

That split is deliberate: the widget stays a thin view, every file and network read goes through one small binary you can run and inspect by hand (flare, flare doctor), and a slow provider never freezes the shell.

flare reads six tools: Claude Code, Codex, Cursor, OpenCode, Antigravity and Kiro. Each is read the way it keeps its own numbers: a rate limit becomes a ring, a pay-as-you-go tool shows today's tokens, a credit plan shows the credits left.

How it works

Everything flare does happens on your machine except one kind of request: in the default official mode, a provider's own usage endpoint is asked for your numbers, with the sign-in its CLI already keeps. In local mode even that is gone.

flare's parts and how they talk Inside your machine, the Quickshell widget runs the flare binary on each refresh and reads its JSON back. flare reads the providers' own files and its own state directory. Only in official mode does flare reach outside, to each provider's usage endpoint over HTTPS. The widget also starts flare watch, which sends desktop notifications. YOUR MACHINE THE INTERNET Quickshell widget ui/ · QML notch, card, panels flare Rust binary one short run per refresh The providers' own files ~/.claude, ~/.codex, opencode.db, ~/.gemini, ~/.kiro — read only ~/.local/state/flare snapshots, history, pacing flare watch notify-send Usage endpoints HTTPS, official mode only one per provider runs it JSON reads starts while [notify] is on GET + sign-in
The only line that leaves your machine is the amber one, and only in official mode.

One refresh, step by step

  1. The poll timer fires, every poll.interval_secs (30 s by default), and only while the notch is actually on screen, the usage panel is open, or a shortcut brought it in. A notch tucked away in hover or shortcut mode reads nothing.
  2. The widget runs flare --provider all. It finds the binary beside the module (target/release/flare), on PATH, in ~/.local/bin, in ~/.cargo/bin, or at flare.binary_path.
  3. Every enabled provider is read at once, each on its own thread, so one slow endpoint never holds up the rest.
  4. Each provider checks its own pace. The widget may refresh every 30 s, but network reads keep their own rhythm: Claude at most every minute; Codex, Cursor and Kiro every five. Between those, the last reading is reused. A server that said "slow down" is not asked again before its deadline, which survives restarts.
  5. The provider reads: its own files on disk, and in official mode its usage endpoint.
  6. flare records the reading, its history and the session log in ~/.local/state/flare.
  7. flare prints JSON and exits. Nothing stays running between refreshes.
  8. The widget draws the rings, the card and the panels from that JSON.

The usage panel's week of hourly use comes from a second, heavier command, flare activity, which runs only while the panel is open and once every five minutes at most.

What it reads and sends

Per provider, in the default official mode. In local mode the "sent" column is empty for everyone: flare never opens a network connection.

ProviderRead on your machineSent in official mode, and where
Claude CodeThe OAuth token in ~/.claude/.credentials.json; the email in ~/.claude.json; reply logs in ~/.claude/projects; live sessions in ~/.claude/sessions; the status line captureThe token as a bearer header, plus anthropic-beta: oauth-2025-04-20api.anthropic.com/api/oauth/usage
CodexThe session in ~/.codex/auth.json; rollout logs in ~/.codex/sessionsThe access token as a bearer header and the ChatGPT-Account-Id it nameschatgpt.com/backend-api/wham/usage
CursorThe editor's session from ~/.config/Cursor/User/globalStorage/state.vscdbThat session, as the editor itself sends it — only after you say yes oncecursor.com/api/usage-summary
OpenCodeIts database, ~/.local/share/opencode/opencode.db; provider names (not keys) from its auth.jsonNothing
AntigravityThe status line capture; conversation databases in ~/.gemini/antigravity-cli/conversationsNothing
KiroThe sign-in kiro-cli keeps in ~/.local/share/kiro-cli/data.sqlite3; sessions in ~/.kiro/sessions/cliThe sign-in as a bearer headerq.<region>.amazonaws.com/getUsageLimits

What never leaves your machine: your prompts, your replies, your code, your project names, your sessions, your history, your config. The endpoints above answer with percentages, reset times and plan names; that is all flare asks for. There is no telemetry, no analytics and no update check.

What it costs to run

Measured on 23 September 2026, with a heavy week of Claude Code logs. Your numbers will differ with your log sizes and hardware, but not by an order of magnitude.

Machine
HP Victus laptop
CPU
Intel Core i5-12500H, 12 cores, 16 threads
Graphics
Intel Iris Xe · NVIDIA GeForce RTX 3050 Ti Laptop
Memory
16 GB
Storage
512 GB NVMe SSD
System
CachyOS, Linux 7.2.6
Desktop
Hyprland 0.56.2 · Quickshell 0.3.1 · Qt 6.11

Memory

The views were measured with software rendering in a bare Quickshell, so the 74 MB they add includes loading Qt Quick itself. Inside a shell that already draws a bar or a dock, most of that is shared and the real cost is lower. flare itself holds no memory between refreshes: each run exits.

Processor

152 msone flare --provider all, wall time
~0.2 sCPU per run, across its threads
≈ 0.6 %of one core, averaged, while the notch is on screen at the default 30 s
0.13 %of one core for the idle views themselves

Tucked away in hover or shortcut mode, the timer stops and the cost drops to zero. flare activity (the usage panel's week) takes about 0.3 s and runs only while that panel is open.

Network

Those are ceilings, reached only while the widget keeps refreshing. A 429 backs off from a minute, doubling to fifteen, and honours the server's Retry-After. local mode makes no requests at all.

Security and privacy

  • Credentials are borrowed, read-only. flare reads the sign-in each CLI already keeps. It never writes, copies, refreshes or stores a token, and never prints one: flare doctor describes a token by its length.
  • An expired token is never sent. A 401 or 403 re-reads the credential once, in case the CLI just rotated it, and otherwise waits for you to open the CLI.
  • Renewing a sign-in is left to the tool itself. Shortly before Claude's token expires, flare runs claude -p with no prompt and no input, which renews and exits; its output is discarded. Kiro's is renewed with kiro-cli whoami. Pin either with claude.binary_path / kiro.binary_path instead of trusting the first match on PATH.
  • Cursor asks first. Reading Cursor means borrowing a live session cookie from the editor's private state. The widget asks once; declining keeps Cursor out of official mode until data.cursor_consent changes.
  • State files hold no secrets, only readings, history, pacing and the session log. They are written 0600 through a temporary file and a rename, so a crash never leaves half a file.
  • Log trees are read defensively. Symlinks are never followed, recursion stops at depth 12, collection stops at 20 000 files, and a damaged line is reported without losing the rest of the file.
  • The status line hooks are passive. They make no network request, never write Claude Code's or agy's own files, and a failed capture never delays or breaks your status line.
  • Jumping to a session reads only what it needs: the session's own /proc/<pid> entries, and from its environment only kitty's window variables.
  • Nothing phones home. No telemetry, no analytics, no update check. The only outbound requests are the usage endpoints in the table above.

Install

Requires Quickshell 0.3+ and Qt 6.6+, on Hyprland (other Wayland compositors run the widget, without jumping to a session's window).

$ git clone https://github.com/lunanoir21/flare-notch
$ cd flare-notch
$ ./install.sh

install.sh:

  • builds flare with Cargo if Rust 1.85+ is installed; otherwise downloads the latest release build (x86_64, glibc 2.39+);
  • puts it in ~/.local/bin (set FLARE_BIN_DIR to change that);
  • adds flare to your app launcher: ~/.local/share/applications/flare.desktop, its icon, and flare-settings beside the binary;
  • runs flare doctor, so you see at once what it found.

By hand, cargo build --release is enough: the widget finds the binary it was built next to. The prebuilt binary on its own: flare-x86_64-linux.tar.gz (sha256).

Running it

On its own

$ quickshell -p /path/to/flare-notch/ui

Inside the shell you already have

import "path/to/flare-notch/ui" as Flare

ShellRoot {
    Flare.FlareHost {}
}

At login

In hyprland.conf:

exec-once = quickshell -p /path/to/flare-notch/ui

From your app launcher

Open flare from rofi, wofi, fuzzel or any launcher that reads .desktop files. It opens the settings page in whichever running Quickshell has flare loaded, and starts the widget on its own first if none does.

Using the widget

Three looks — notch.style

classic
Codenotch's notch: a black body on the left or right edge, one ring per provider.
aura
One provider at a time, large and tinted with its colour; the others wait below as small logos. next / prev step through them.
compact
A thin strip on the top or bottom edge that opens into a panel with a row per provider, on click or on hover (compact.open_on).

Meeting the edge — notch.mount

bridge
Welded to the edge with inverse rounded corners. The default.
floating
A rounded panel held notch.gap pixels off the edge.
flush
A strip along the whole edge that flares into the screen at both ends.

When it shows — notch.reveal

always
Always on screen.
hover
Tucked past the edge; slides in after the pointer rests there for reveal_delay_ms, out hide_delay_ms after it leaves.
shortcut
Tucked away until qs ipc call flare toggleVisible, which you bind to a key.

Reading a ring

A ring fills clockwise from 12 o'clock with the share of the provider's headline limit used. Under it, notch.label shows the percentage, the time until the reset, or both. A provider with no limit (OpenCode) shows today's tokens instead, and a credit plan (Kiro) shows the credits left.

With theme.ring_color = "monochrome" rings are drawn in contrast only, with one accent reserved for a critical state; "provider" paints each ring in its own aura colour. The hover card's bars go green, orange and red as a limit fills. A reading that could not be refreshed is dimmed, never invented.

With the pointer

Hover a ring
Opens its card: every limit with its reset time, the plan, the login, and the open sessions.
Click a ring
Opens that provider's own usage page (Claude, Codex, Cursor).
The card's arrow
Opens the usage panel on that provider.
Right-click
Opens the settings page.
Drag
Moves it along its edge; the offset is saved.

Open sessions

The card lists the Claude Code sessions running right now, folded under a Sessions header, each with its name, project, age and state: working, waiting on you (with what it is waiting for), or idle. Clicking a row brings its terminal to the front.

The list comes from the records Claude Code itself writes to ~/.claude/sessions/<pid>.json. A record counts only while its process is alive and still the one that wrote it (the start time in /proc/<pid>/stat must match), and, on Hyprland, while its terminal still has a window.

In kitty, the jump can also select the session's tab or split. That needs kitty's remote control on a socket, in kitty.conf:

allow_remote_control socket-only
listen_on unix:@kitty

The same jump from a terminal: flare focus <pid>. sessions.show = false hides the list entirely.

The usage panel

Open it from the arrow on a card, or with qs ipc call flare usage <provider>. A deck of provider cards runs across the top; pick one with the pointer or ← / →, and Esc closes the panel. For the one picked:

  • When you use it: the week of its longest limit hour by hour as a heatmap (the last seven days for a provider with no limit), with the busiest hours, the quietest day and the reset.
  • Today's sessions on a timeline, closed ones included; click an open Claude Code one to jump to it.
  • Limits: each one, how much of its time has gone, and where today's pace takes it.
  • How the longest limit filled over its period, with today's pace dashed ahead.
  • The last seven days and the hours of the day.
  • The models that took the most this week.
  • This week's sessions as a table.

Token counts are read from each provider's own logs, every reply counted once (Claude Code repeats a reply's usage on each content block, and a resumed session copies old replies; neither is double counted). Kiro counts credits instead. usage.all_providers = true also lists providers switched off in the widget.

More than one login

Two Claude Code logins are two rings, and so are two Codex ones. A folder ~/.claude-<name> or ~/.codex-<name> that holds a sign-in is found on its own and becomes claude:<name>, with the name's initial on its ring. Sign in to one by pointing the CLI at it once:

$ CLAUDE_CONFIG_DIR=~/.claude-work claude      # then /login
$ CODEX_HOME=~/.codex-work codex login

A login kept anywhere else goes in the config:

[[account]]
provider = "claude"        # or "codex"
name = "work"              # lower-case letters, digits, - and _
home = "~/work/.claude"    # what CLAUDE_CONFIG_DIR / CODEX_HOME is set to
color = "#E0A458"          # optional: aura's colour for this login

The default login keeps the plain id (claude), so a config and history from before carry on unchanged. Every login is read, renewed, recorded and reported on its own; providers.claude = false hides every Claude login at once, and providers.accounts_off hides them one by one. Everything that takes a provider takes a login too: card claude:work, flare --provider claude:work.

Notifications

flare watch is the one long-running process: the widget starts it while any notification is switched on, and it exits when they all are off or the widget goes away. It rescans sessions every two seconds (a few small files, no child processes) and usage at the widget's pace, never more than once a minute. It sends, with notify-send:

notify.waiting
A session stops and waits on you. The notification's action jumps to it.
notify.limit
A limit reaches notify.limit_at percent (50–100), once per limit period.
notify.reset
A limit you had been using resets.

Starting the watcher never replays what was already true: the first reading of anything is only a baseline.

Providers

Claude Code

Official: api.anthropic.com/api/oauth/usage with Claude Code's own OAuth token; the session and weekly limits, and any model-scoped weekly ones. Renewed with claude -p a few minutes before expiry, once per token, at most every ten minutes. Where the endpoint cannot answer, a status line capture younger than half an hour stands in.

Local: the status line capture only.

Tokens: ~/.claude/projects/**/*.jsonl. Pace: 1 min.

Codex

Official: chatgpt.com/backend-api/wham/usage with the session in ~/.codex/auth.json; the primary and secondary windows, and Spark and code review limits under their own headings. The token is never refreshed: a 401 means open Codex once.

Local and fallback: the limits Codex wrote into its newest rollout log, current for five minutes.

Pace: 5 min.

Cursor

Official: cursor.com/api/usage-summary with the editor's own session, after a one-time yes.

Local: Cursor keeps no usage on disk, so nothing.

Pace: 5 min.

OpenCode

Runs on your own API keys, so there is no limit to show: the ring shows today's tokens. Everything comes from its local database, in both modes; sessions and models from its history.

Antigravity

agy keeps each model group's quota in memory and hands it only to its status line command, so the quotas come from the status line capture. Tokens, models and sessions come from its per-conversation databases. No network, in either mode.

Kiro

Official: getUsageLimits with the sign-in kiro-cli keeps; the month's credits, shown as credits left, and when they refill. The sign-in lasts an hour and is renewed shortly before by running kiro-cli whoami.

Local: credits per request from ~/.kiro/sessions/cli, with no allowance. Pace: 5 min.

Status line captures

Claude Code and agy hand their live limits only to their status line command. The hooks in hooks/ save that payload to ~/.local/state/flare and then run your real status line unchanged.

Claude Code

Needed for local mode; in official mode it only stands in when the endpoint is down. In ~/.claude/settings.json, put the hook in front of the command you already use:

"statusLine": {
  "type": "command",
  "command": "/path/to/flare-notch/hooks/claude-statusline-capture.sh npx -y @owloops/claude-powerline@latest"
}

The hook runs, in order: its own arguments as the command; $FLARE_STATUSLINE_DELEGATE; statusline-command.sh in Claude Code's directory. With none of them it captures and prints nothing. Each login gets its own capture file, so every login's status line can point at the same hook.

Antigravity

In ~/.gemini/antigravity-cli/settings.json, keeping agy's own line:

"statusLine": {
  "command": "/path/to/flare-notch/hooks/agy-statusline-capture.sh",
  "enabled": true,
  "stack_with_default": true
}

Configuration reference

Everything lives in ~/.config/flare/config.toml. Every key is optional; delete a line to get its default back. The settings page and flare config set write the same file, keep its comments, and refuse a value the key cannot hold. The widget picks up a saved change within a second. flare config init writes a commented file holding every default.

KeyValuesDefaultWhat it does
[data]
data.modeofficial, localofficialWhere numbers come from. local never touches the network.
data.cursor_consentunset, granted, declinedunsetYour answer to reading Cursor's live session.
[theme] and [ui]
theme.modeblack, white, autoblackauto follows the desktop's light/dark preference.
theme.ring_colormonochrome, providermonochromeRings in contrast only, or in each provider's aura colour.
ui.languageauto, en, trautoauto follows LC_ALL, LC_MESSAGES or LANG.
[notch]
notch.styleclassic, aura, compactclassicThe look.
notch.mountbridge, floating, flushbridgeHow it meets the edge.
notch.gap0–648Floating only: pixels between the panel and the edge.
notch.revealalways, hover, shortcutalwaysWhen it is on screen.
notch.reveal_delay_ms0–500080Hover: rest on the edge this long before it slides in.
notch.hide_delay_ms0–5000400Hover: slide away this long after the pointer leaves.
notch.edgeleft, rightleftEdge for classic and aura.
notch.offsetpixels0Along the edge from the centre; positive moves it down.
notch.scale0.5–2.01.0Size multiplier.
notch.screenoutput name""As hyprctl monitors names it. Empty: every screen.
notch.labelpercent, time, bothpercentWhat sits under each ring.
[compact]
compact.edgetop, bottomtopEdge for the compact strip.
compact.offsetpixels0Along the edge from the centre; positive moves it right.
compact.open_onclick, hoverclickHow the panel opens.
[providers]
providers.claude.kirotrue, falsetrueOne switch per provider, for all its logins.
providers.orderlist of idsclaude, codex, opencode, cursor, antigravity, kiroDrawing order, and the order aura steps through. Logins by id; one left out follows its provider.
providers.find_accountstrue, falsetrueFind logins in ~/.claude-<name> and ~/.codex-<name>.
providers.accounts_offlist of ids[]Logins switched off one by one, e.g. claude:work.
[aura], [sessions], [usage]
aura.claude.kiro#RRGGBBeach provider's colourAura's tint, and the ring colour with ring_color = "provider".
sessions.showtrue, falsetrueThe card's session list.
usage.all_providerstrue, falsefalseAlso list, and read, switched-off providers in the usage panel.
[notify]
notify.waitingtrue, falsetrueA session waits on you.
notify.limittrue, falsetrueA limit reaches limit_at.
notify.limit_at50–10090The threshold, in percent.
notify.resettrue, falsetrueA used limit resets.
[poll], [scan]
poll.interval_secs≥ 530Widget refresh. Network reads keep their own slower pace.
scan.window_days≥ 11Days of local logs counted toward token and credit totals.
Programs
claude.binary_pathpathPATH lookupThe claude used to renew the token.
kiro.binary_pathpathPATH lookupThe kiro-cli used to renew the sign-in.
codex.binary_path, opencode.binary_pathpathPATH lookupPin a program that is not on PATH.
flare.binary_pathpathsearchFor a compositor that starts Quickshell without your login shell's PATH.
[[account]]
provider, name, home, colorsee More than one loginA login that lives somewhere find_accounts does not look.

List keys take either form: flare config set providers.order claude,codex,opencode or '["claude", "codex"]'. A file that fails to parse or validate falls back to defaults and says why in flare doctor and on the settings page; a typo never stops the widget from drawing.

IPC reference

flare listens on Quickshell IPC as the target flare.

CallDoes
next, prevStep aura to the next or previous provider.
toggleOpen or close the compact panel.
toggleVisible, show, hideBring the widget in or tuck it away (hover and shortcut reveal).
toggleSessionsOpen or fold the card's session list.
card <provider>Open a provider's card without the pointer, bringing the notch in; again to close.
usage <provider>Open the usage panel on a provider; again to close.
style classic|aura|compactSwitch the look (saved to the config).
settingsOpen or close the settings page.
openSettingsOpen the settings page, leaving it open if it already is.
refreshRead now, skipping each provider's pace (a server's retry deadline still holds).

For Hyprland, with flare inside the shell at ~/.config/quickshell/shell.qml:

bind = SUPER, right,     exec, qs ipc call flare next
bind = SUPER, left,      exec, qs ipc call flare prev
bind = SUPER, U,         exec, qs ipc call flare toggle
bind = SUPER SHIFT, U,   exec, qs ipc call flare toggleVisible
bind = SUPER, C,         exec, qs ipc call flare card claude

Add -p /path/to/shell.qml after qs when your shell lives elsewhere, or --pid <pid> when more than one Quickshell runs (qs list --all).

Command line

In JSON mode stdout carries JSON and nothing else; diagnostics go to stderr. doctor is the one plain-text command.

CommandDoes
flareRead every provider and print a JSON array. The widget runs this.
flare --provider <id>One provider or login (claude, claude:work, …) as a JSON object.
flare --refreshRead now instead of waiting for each provider's pace.
flare doctorWhere flare looked and what it found, per provider and login. Never prints a secret.
flare activity --provider <id>Use per hour over the last eight days, with the models, as JSON.
flare focus <pid>Bring a live session's terminal to the front (Hyprland).
flare watchStay running and send notifications, as [notify] sets them.
flare config pathPrint the config file's path.
flare config init [--force]Write a commented config with every default.
flare config getThe effective config, the drawing order and every login, as JSON.
flare config set <key> <value>Set one key, keeping the file's comments.

The JSON output

Each provider or login is one object. The keys are stable from 1.0.0 on: new ones may appear, existing ones keep their meaning.

KeyMeaning
providerThe id: claude, or claude:work for another login.
statusSee the statuses below.
noteOne line on why the status is what it is.
sourceofficial or local: where these windows came from.
account, planWhose readings these are (a label, never used to sign in) and the plan.
meteredWhether the provider has a rate limit at all.
hiddenSwitched off in the widget, read only for the usage panel.
windowsEach limit: id, label, group, used (0–1), window_minutes, resets_at, resets_in_secs, reset_elapsed, and for a credit limit amount (used, limit, unit).
headlineThe window the ring draws, by id.
fetched_at, backoff_untilWhen the reading was taken; no network read before this time.
tokens_today, credits_todayOver scan.window_days.
cost_today_usd, cost_is_estimatedWhere a provider reports cost; an estimate is never a bill.
errorCategory-prefixed detail, e.g. parse error: ….
sessionsOpen now: pid, name, project, state, waiting_for, started_at.
historyPer window id, [unix seconds, fraction used] over the window's length.
session_logSessions seen over the last week, closed ones included.

Statuses

ok
A reading taken just now, or recent enough to stand (five minutes).
stale
The last good reading, kept because a fresh one could not be taken. Drawn dimmed.
needs_auth
No usable sign-in. Open the provider's CLI once.
needs_consent
Cursor, before you have answered the one-time question.
backoff
Asked to slow down, with no earlier reading to show.
error
A fresh reading failed and there is no earlier one.
none
Present, but nothing metered or recorded yet.
absent
Not installed here; no cell is drawn.

Files

PathWhat
~/.config/flare/config.tomlYour settings.
~/.local/state/flare/<id>.jsonThe last reading and its pacing (last attempt, back-off, renewal bookkeeping). No token.
~/.local/state/flare/history-<id>.jsonHow each limit filled.
~/.local/state/flare/sessions-<id>.jsonThe session log, kept eight days.
~/.local/state/flare/claude-statusline*.json, agy-statusline.jsonStatus line captures, one per login.
~/.local/bin/flare, flare-settingsThe binary and the launcher's helper.
~/.local/share/applications/flare.desktopThe launcher entry; its icon sits under ~/.local/share/icons/hicolor/scalable/apps/.

Everything under ~/.local/state/flare is safe to delete: flare starts over with an empty history.

Troubleshooting

Start with flare doctor: it prints, per provider and login, the files it looked for, whether a sign-in is usable, where renewal would run, the last capture, and the reading it gets.

The notch shows nothing at all
The widget cannot find flare. Check qs log; set flare.binary_path, which a compositor-started Quickshell needs when ~/.local/bin is not on its PATH.
A provider has no ring
It is not installed (status absent), or switched off in the settings page.
A ring is dimmed
The reading is stale: the endpoint failed or is backing off. The card's note says which.
"Credential expired" on Claude
flare could not renew the token. Run claude once; if renewal keeps failing, pin claude.binary_path.
Claude shows nothing in local mode
Point Claude Code's status line at the capture hook (above).
A second login is not found
The folder needs a sign-in in it (.credentials.json for Claude, auth.json for Codex), and its name must be lower-case letters, digits, - or _.
Clicking a session does nothing
Jumping needs Hyprland (hyprctl); selecting kitty's tab needs its remote control socket.
No notifications
notify-send must be installed and a notification daemon running; check that [notify] has something switched on.

Uninstall

rm ~/.local/bin/flare ~/.local/bin/flare-settings
rm ~/.local/share/applications/flare.desktop
rm ~/.local/share/icons/hicolor/scalable/apps/flare.svg
rm -r ~/.config/flare ~/.local/state/flare

Then remove the exec-once line or the FlareHost import, and the hook from any statusLine you pointed at it. flare changed nothing else.

Development

core/
The providers, config, sessions, history and pacing (flare-core).
cli/
The flare command.
ui/
The QML widget. Every visible string lives in Strings.qml, in English and Turkish.
hooks/
The status line captures.
tools/shots/
Makes every screenshot from a made-up world, drawn by flare's own QML with no compositor: tools/shots/shots.sh.
$ cargo test
$ cargo clippy -- -D warnings

CI runs both on every push. A release is a ## x.y.z section in CHANGELOG.md, the same version in Cargo.toml, and a pushed vX.Y.Z tag: the release workflow checks the three agree, tests, builds on Ubuntu 24.04 and publishes the binary and its checksum.