Gitcrawlchy
A bar widget for one command: gitcrawl search, over
gitcrawl's local SQLite mirror of a
GitHub repository's issues and pull requests.
Click gitcrawl's mark in the bar, describe a problem, and read what is already in the mirror.
What it is
A UI for a single CLI command, not a front end for gitcrawl.
gitcrawl search takes five flags. The panel is those five flags and the rows
they return:
| Flag | Control |
|---|---|
owner/repo |
the repository picker — discovered from your own mirror |
--query |
the text field |
--mode |
keyword / semantic / hybrid |
--scope |
threads / code / all |
--limit |
a row count, beside the query |
Nothing is filtered, re-ranked or reimplemented on this side: the rows are the
command's hits in the order it ranked them, and the footer prints the
invocation so the panel is checkable against the CLI.
Open gitcrawl hands the selected repository to gitcrawl tui, which is the
cluster browser and where triage belongs. Everything else gitcrawl does is
another command; bin/gitcrawlchy still shims the rest for scripts.
Controls are grouped by line: the repository and the handoff; the query and how
many rows to ask for; then --mode and --scope as two chip groups of three
pushed to opposite ends. Seven chips in one row read as a single seven-way
choice — two threes with a gap read as two decisions.
The footer prints the invocation across as many lines as it needs. It used to
elide, which meant a long query pushed --limit off the end and the line
stopped being the checkable thing it exists to be.
What a row shows
Only what the payload carries, which varies by mode: score comes back from
semantic and from the vector half of hybrid, and scope only under
--scope all. Both are shown when present rather than invented when absent.
Rows open html_url — gitcrawl's own link, not a path built from a number.
Why --limit is bounded, and why there is no "all"
--limit truncates the ranking and the payload carries no total, so a full page
means at least this many. The header says 25+ RESULTS · LIMIT REACHED
rather than presenting the ceiling as an answer, and the picker sits beside the
query because how many rows to ask for is part of the request.
There is no unlimited option, and gitcrawl has no flag for one: omitting
--limit returns 20 rather than everything, and --limit 0 and --limit -1
are both refused with expected positive integer. A very large number does
work — --limit 100000 on the returns all 6,085 matches — but that is 2.5 MB
of JSON and about 650 ms through the bridge before QML parses it and builds
six thousand rows, which is the cost profile that made the old cluster view
take twenty seconds to open. 200 is the ceiling for that reason.
When it comes back empty
gitcrawl writes its failures to stderr and nothing to stdout, so a UI that discards stderr has to guess why a search returned nothing. This one reports what the command said:
- a missing OpenAI key — semantic and hybrid embed the query at search time, so without it they return zero hits and say nothing
- a repository that is not in the mirror
- no vectors for the repository yet
And when the search genuinely matched nothing, the wording is about the mirror rather than about GitHub. Nothing in the search payload dates the mirror, so this panel cannot tell you it is stale — no matches means "not in the local mirror", which is not the same as "not reported".
The keyboard
The panel opens with its search field focused, so bare letters stay typeable
and navigation takes Alt and the arrows. Omarchy's PanelKeyCatcher bare
h/j/k/l would be swallowed by the field, which is why it is not used.
| Key | Does |
|---|---|
Alt + 1 / Alt + 2 / Alt + 3 |
--mode keyword / semantic / hybrid |
Alt + 4 / Alt + 5 / Alt + 6 |
--scope threads / code / all |
Alt + R |
next repository |
Alt + T |
open gitcrawl tui on the selected repository |
Alt + Left / Alt + Right |
previous / next mode |
Down / Up |
move the cursor through the rows |
Enter |
open the row under the cursor, or run the search when no row is picked |
Escape |
leave the row list, then close the panel |
Changing any flag re-runs the search when results are showing, because a flag only means something in terms of a result — switching mode and leaving the old rows up would label the previous answer with the new mode.
Why the mode picker is not decoration
On the omacom/omarchy corpus (7,046 threads), keyword search returns
nothing at all for a sentence a person would actually type. Semantic finds
the real duplicate:
| Query | keyword | semantic |
|---|---|---|
lock screen wont accept password |
0 | #3562 cant use the password in lockscreen |
audio crackling after update |
0 | #6037 Audio Quality Dropped Significantly… |
my laptop wont wake up after closing the lid |
0 | #3511 Screen does not wake up after… lid closed |
Repositories
search takes owner/repo as a positional argument, so the repository is part
of the query rather than a setting the panel happens to remember. Switching
clears the results and re-runs, so the previous repo's rows never sit under the
new repo's name.
--mode and --limit persist as preferences. --scope does not: code and
all are one-off detours, and a panel left on code would fail the next
search you opened it to run, for a reason from a previous session.
What this does not cover
The panel runs gitcrawl search and nothing else. No clusters, no sync, no
doctor, no pipeline — those are other commands, and gitcrawl tui or the CLI
is where they belong.
One consequence worth stating plainly: because status and doctor are gone,
the panel cannot tell you the mirror is stale. A search hit carries no sync
timestamp, so this is a limit of the command, not an omission — see When it
comes back empty.
bin/gitcrawlchy still dispatches the wider set for scripting. Not dispatched
at all:
| Area | Commands |
|---|---|
| Remote / cloud archives | remote status, remote archives, remote login, whoami, cloud publish |
| Portable stores | portable export, portable prune |
| Setup | init, configure |
| Control metadata | metadata |
| gh-shaped search | search issues|prs — a second usage of search, closer to gh search over the mirror than to the ranked local form |
| Superseded | gh (prints a migration notice), cluster-explain (alias for cluster-detail) |
The mark
BrandMark.qml draws gitcrawl's own logo — the three connected nodes — with
the geometry lifted from docs/social-card.svg in the gitcrawl repository, so
it is the real mark rather than an impression of it.
It paints monochrome in the bar, following the active theme, because Omarchy's
bar icons are monochrome and a single coloured logo in that row reads as a
mistake. Panel headers use gitcrawl's palette (#60a5fa, #2dd4bf,
#93c5fd), where colour belongs.
Below about 28px the nodes are fattened and the links thinned. The source proportions are drawn for a 118px logo; scaled straight down to a 22px bar icon the dots vanish and the three links dominate, so the mark reads as a play triangle instead of a graph.
Requirements
-
gitcrawlonPATH— no Go toolchain needed, the project publishes release archives:./scripts/install-gitcrawl.shInstalls to
~/.local/bin(override withPREFIX).The installer is pinned: the version and both architecture SHA-256 digests are written into the script itself, so what gets installed is fixed by the source you are reading rather than resolved from a mutable "latest" release at run time. The digest is checked against that pinned value — not against a
checksums.txtpublished alongside the download — and is verified before the archive is opened. HTTPS is required at every hop, a redirect off the expected release origin is refused, and any mismatch exits non-zero without installing anything.The download is also capped at 10 MiB — a small allowance over the pinned archives, which are 7,085,432 (amd64) and 6,502,668 (arm64) bytes.
curlrefuses an oversized declared length up front and aborts mid-transfer once the ceiling is crossed, and the stream is piped throughheadso a response that declares no length at all still cannot write past it. Either way the script exits non-zero with nothing installed, so a hostile or faulty response from the release origin cannot fill the disk while the digest it was always going to fail is computed. Move the ceiling in the same commit as the pin if a future archive outgrows it.Currently pinned to v0.9.2. To move to a newer gitcrawl, change
PINNED_TAGand both digests in the same commit; the script takes no version argument. Withoutgitcrawlpresent the plugin says so and points at the installer rather than reporting a pile of absences that are not real. -
GITHUB_TOKEN, orghauthenticated — the bridge borrowsgh auth token -
For embeddings: an OpenAI-compatible key at
~/.config/gitcrawl/openai.key(mode600). Embedding the whole corpus cost about $0.03.The key is needed at search time as well, not only when embedding: semantic and hybrid modes embed the query before comparing vectors. Without it gitcrawl returns zero hits and says nothing, which reads as "nothing has been reported".
Install
omarchy plugin add https://github.com/yamz8/gitcrawlchy.git --enable
omarchy bar put yamz8.gitcrawlchy --section right
omarchy restart shell
omarchy plugin add clones the repository, validates the manifest, and installs
it as yamz8.gitcrawlchy. Update later with
omarchy plugin update yamz8.gitcrawlchy.
To open the panel from the keyboard, add this to ~/.config/hypr/bindings.lua:
o.bind("SUPER + ALT + C", "Gitcrawlchy", "omarchy-shell yamz8.gitcrawlchy toggle")
Install from a local checkout
cp -R ./gitcrawlchy ~/.config/omarchy/plugins/yamz8.gitcrawlchy
omarchy plugin validate ~/.config/omarchy/plugins/yamz8.gitcrawlchy
omarchy plugin enable yamz8.gitcrawlchy
omarchy restart shell
Remove
omarchy bar remove yamz8.gitcrawlchy
omarchy plugin disable yamz8.gitcrawlchy
omarchy plugin remove yamz8.gitcrawlchy
omarchy restart shell
That takes the widget out of the bar and deletes
~/.config/omarchy/plugins/yamz8.gitcrawlchy. The plugin writes nothing outside
that folder: the mirror, the GitHub token and the embedding key all belong to
gitcrawl and are left alone. Remove the SUPER + ALT + C line from
bindings.lua yourself if you added one — this plugin never edits it.
Settings
Per-widget, in shell.json:
| Key | Default | Meaning |
|---|---|---|
repo |
(empty) | The owner/repo search runs against. Empty means "ask gitcrawl" |
repos |
"" |
Comma-separated list for the picker; it appears once there is more than one |
mode |
hybrid |
Starting --mode |
limit |
25 |
Starting --limit |
The plugin ships with no repository configured. Left unset, it asks
gitcrawl which repository your mirror is about — gitcrawl tui --json with no
argument reports the most recently updated one, which is the only supported way
to discover it. So a fresh install searches your mirror, not a repository
baked into the plugin. If nothing has been synced yet, the panel says so and
gives you the command.
Set repo to pin one. Set repos to switch between several — a picker appears
above the query box, and Alt + R walks the list. Switching clears the results and re-runs the
query against the new repository.
{ "id": "yamz8.gitcrawlchy", "repos": "omacom/omarchy,openclaw/gitcrawl" }
The list is configured because nothing in gitcrawl can produce it: no command
enumerates repositories, status --json counts them without naming them, and
tui --json reports only the one it is browsing. The alternative is reading
gitcrawl's SQLite schema directly, which is what the previous picker did and
the one place this plugin was coupled to gitcrawl's storage rather than its
interface.
Bridge
bin/gitcrawlchy is usable on its own — one JSON object per command,
diagnostics on stderr, NDJSON progress for long jobs:
gitcrawlchy status
gitcrawlchy clusters omacom/omarchy 6 | jq '.clusters[0]'
gitcrawlchy search omacom/omarchy "lid close" hybrid threads 25
gitcrawlchy refresh omacom/omarchy # streams
Notes
Editing this plugin's QML requires omarchy restart shell —
rescanPlugins reports success and keeps serving the old code.
License
MIT