gnaw (what you do to a bone) is a standalone Babashka script for browsing BONE reports. When fzf is available, gnaw lets you browse BONE reports interactively.
Assuming bbin is installed:
bbin install io.github.bzg/gnaw
Create ~/.config/gnaw/config.edn:
{:my-addresses ["you@example.com" "alias@example.com"]
:text-browser "w3m" ;; "w3m", "lynx" or "links"
:diff-pager "delta" ;; "delta", "bat" or "diff-so-fancy"
:sources [{:urls ["https://example.com/reports/all.json"]
:name "Example ML"
:repo "https://example.com/repo"}
{:urls ["https://example.com/reports/all-open.json"
"https://example.com/reports/all-closed.json"]
:name "Another ML"}]}:my-addresses— your email address(es), used by-mto filter reports involving you. Can be a single string or a vector of strings.:text-browser— terminal browser for viewing reports onRET. When unset, gnaw probes forw3m,lynx,links, then falls back toxdg-open.:diff-pager— pager for viewing patches onC-v. When unset, gnaw probes fordelta,bat, then falls back to$PAGERorless.:sources— vector of report sources. Each source is a map with::urls— a non-empty vector of URLs/paths to BONEreports.jsonfiles whose reports are merged into one source (e.g. anall-open.jsonand anall-closed.json).:name— the source’s name. It is the unique key of a source: two sources may not share a:name, and it overrides thesourceof every report the source yields so the grouped files appear unified.--add-sourcefills it in from the siblingmeta.json’ssourcefield.:repo— (optional) public URL or local path of the associated code repository.
When adding an HTTP source, you may pass the full reports URL
(.../reports/all.json), another report file (.../reports/all-open.json),
or just the base URL (https://tracker.orgmode.org): gnaw resolves a bare
base to reports/all.json and reads reports/meta.json to record :name.
:skip-columns— vector of column names to hide by default (e.g.["priority" "score"]).:report— configuration forgnaw report(see Triage report).
gnaw [options] gnaw report [options] gnaw clear gnaw update
| Option | Description |
|---|---|
-f, --file FILE | Read reports from a JSON file |
-u, --url URL | Fetch reports from a URL |
-U, --urls-file FILE | Fetch and merge from URLs listed in FILE |
-M, --my-addresses EMAILS | Your email(s), comma-separated (overrides config) |
-n, --source NAME | Filter by source name |
-p, --min-priority N | Only show reports with priority >= N (1, 2 or 3) |
-s, --min-score N | Only show reports with score >= N (0–7) |
-S, --skip-columns COLS | Columns to hide, comma-separated |
-m, --mine | Show only reports involving your address(es) |
-c, --closed | Include closed reports |
- | Read JSON from stdin |
gnaw report prints a text triage report to stdout. It accepts the
same filtering options as interactive mode (-f, -u, -m, -c, etc.).
The report includes the following sections: overview, stale patches, stale bugs, active threads, recent reports, and owned reports.
The report can be configured in config.edn under the :report key:
{:report {:sections ["overview" "stale-patches" "stale-bugs"
"active-threads" "recent" "owned"]
:stale-days 14 ;; threshold for "stale" (default: 14)
:recent-days 7 ;; threshold for "recent" (default: 7)
:top-n 10}} ;; max items per section (default: 10):sections— list of sections to include in the report.:stale-days— number of days after which a report is considered stale.:recent-days— number of days to look back for recent reports.:top-n— maximum number of items shown in stale and active-threads sections (recentandownedare not capped).
| Command | Description |
|---|---|
-a, --add-source URL_OR_PATH | Add a reports.json source |
-r, --remove-source URL_OR_PATH | Remove a source |
-l, --list-sources | List configured sources |
Sources are stored in ~/.config/gnaw/config.edn under the :sources key.
--add-source accepts either a direct reports.json URL/path, which is
added as-is, or a tracker base URL (e.g. https://tracker.orgmode.org).
Given a base URL, gnaw reads reports/meta.json for the source name and the
list of available report files, then lets you pick which to add (via fzf, or
a numbered prompt when fzf is absent). The chosen files are grouped into one
named source:
{:urls ["https://tracker.orgmode.org/reports/all-open.json"
"https://tracker.orgmode.org/reports/all-closed.json"]
:name "Org mode ML"}| Command | Description |
|---|---|
clear | Empty the cache |
update | Fetch/update reports from all sources |
By default, gnaw reads cached reports if available. If no cache
exists, it fetches from sources once and caches the result. Use gnaw
update to refresh.
| Key | Action |
|---|---|
C-n | Move to the next line |
C-p | Move to the previous line |
RET | View report in terminal browser |
C-o | Open report in system browser |
C-v | View patch (fetched to cache) |
C-s | Change sort order |
C-r | Filter by report type |
C-b | Filter by source |
C-t | Filter by topic |
C-u | Update cache and reload |
C-h | Show help |
| C-x | Remove current filters |
Browse your reports from a local file:
gnaw -f reports.json -m
Browse all reports from a remote URL:
gnaw -u https://example.com/reports.json
Merge reports from multiple BONE instances:
gnaw -U my-urls.txt
The URLs file (-U) lists one URL per line; blank lines and #
comments are ignored.
Add a source and browse:
gnaw -a https://example.com/reports.json
Update cached reports and browse only yours:
gnaw update gnaw -m
| Path | Purpose |
|---|---|
~/.config/gnaw/config.edn | User configuration and sources |
~/.config/gnaw/cache/patches/ | Cached patches |
~/.config/gnaw/cache/reports/ | Cached reports.json files |
- Send a bug report with
[BUG] gnaw: <SHORT EXPLICIT BUG DESCRIPTION> - Send a patch with
[PATCH] gnaw: <COMMIT SUMMARY> - Send a feature request with
[FR] gnaw: <FEATURE REQUEST> - Share any other question or idea
You can also send me an email and support my work on liberapay.
This project uses Intentional Versioning, here are the three audiences:
Users: end users who browse BONE reports with gnawIntegrators: external packagersMaintainers: maintainers of the codebase
If you like Clojure(script), please consider supporting maintainers by donating to clojuriststogether.org.
Copyright © 2026 Bastien Guerry
Distributed under the Eclipse Public License 2.0.
