github-search-cli 9.8.2 → 9.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -0
- package/README.md +65 -26
- package/dist/cli.js +898 -184
- package/docs/HOW-TO.md +254 -0
- package/package.json +2 -1
- package/release-notes/9.8.3.md +98 -0
- package/release-notes/9.9.0.md +193 -0
- package/src/cli.ts +13 -11
- package/src/errors.ts +56 -5
- package/src/help.ts +11 -0
- package/src/image-loader.ts +57 -2
- package/src/markdown-images.ts +93 -4
- package/src/markdown-view.ts +49 -20
- package/src/mcp-server.ts +34 -2
- package/src/search-state.ts +8 -10
- package/src/search.ts +701 -12
- package/src/session.ts +58 -2
- package/src/trending.ts +2 -6
- package/src/tui.ts +236 -50
- package/src/types.ts +11 -1
- package/src/update-check.ts +72 -6
- package/src/watch.ts +16 -11
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,135 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Publish
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [9.9.0] — 2026-10-01 (released)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Queries are understood instead of passed through.** Every surface —
|
|
14
|
+
TUI, CLI, MCP, `--watch` — now parses and checks a query before spending an
|
|
15
|
+
API call, so a typo or a malformed value fails with a specific, actionable
|
|
16
|
+
message instead of an opaque 422 or a misleading "network error".
|
|
17
|
+
- Misspelled qualifiers are corrected: `langauge:Rust` → `language:Rust`,
|
|
18
|
+
`star:1000` → `stars:1000`, `push:>2024-01-01` → `pushed:>2024-01-01`.
|
|
19
|
+
Correction is scoped to the qualifiers ghfind knows, so a qualifier it has
|
|
20
|
+
never heard of (`is:`, `has:`, `props.*`) is passed through untouched
|
|
21
|
+
rather than mangled into something else.
|
|
22
|
+
- Counts accept `k`/`m` sugar: `stars:10k` → `stars:>=10000`,
|
|
23
|
+
`size:1m` → `size:>=1000000`, `stars:1k..10k` → `stars:1000..10000`.
|
|
24
|
+
A bare count keeps GitHub's exact-match meaning — `stars:100` still means
|
|
25
|
+
exactly 100.
|
|
26
|
+
- Values are validated per qualifier: numeric counts, `fork:true|false|only`,
|
|
27
|
+
`archived:true|false`, ISO8601 dates for `created`/`pushed`/`updated`,
|
|
28
|
+
the `visibility:` enum, and the `in:` searchable fields. An invalid value
|
|
29
|
+
reports the bad token and the fix, e.g. `⚠ Invalid query: "stars:abc" is
|
|
30
|
+
not a number — use stars:100, stars:>=100, or stars:1k..10k`.
|
|
31
|
+
- `validateQuery` is now called by the CLI, the MCP server and `--watch`, not
|
|
32
|
+
just the TUI. Previously only the TUI enforced the one existing rule.
|
|
33
|
+
- **Zero-result searches now suggest how to loosen the query.** Qualifiers are
|
|
34
|
+
ranked by how sharply they narrow results — a star or size threshold empties
|
|
35
|
+
a result set outright, so those are loosened first — and each suggestion
|
|
36
|
+
rewrites one qualifier rather than dropping it: `No results for "rust
|
|
37
|
+
stars:>=5000000"` now offers `Try: rust stars:>=500000`.
|
|
38
|
+
- **`GHFIND_LOG=<path>` writes TUI diagnostics to a file.** The TUI owns the
|
|
39
|
+
screen and cannot print diagnostics into it, and its logger was previously a
|
|
40
|
+
silent no-op — which made API failures undiagnosable. Token values are
|
|
41
|
+
redacted from logged URLs. Unset by default, so there is no cost in normal
|
|
42
|
+
use.
|
|
43
|
+
- **`docs/HOW-TO.md`** — a task-oriented guide: setting up a token, writing
|
|
44
|
+
queries, reading results, and what to do when a search returns nothing or an
|
|
45
|
+
error appears.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- **A forbidden response no longer claims SSO is the cause.** Three unrelated
|
|
50
|
+
conditions return 403 with rate-limit budget remaining — SSO not authorizing
|
|
51
|
+
the token, a secondary/abuse rate limit, and org resource restrictions —
|
|
52
|
+
and the message asserted the SSO case unconditionally, sending people to fix
|
|
53
|
+
a credential problem that did not exist. The message now reports GitHub's
|
|
54
|
+
own reason and names both plausible causes; the response headers and body
|
|
55
|
+
(which identify the real one) are written to `GHFIND_LOG`.
|
|
56
|
+
- **`forks` is recognised as a qualifier.** Only `fork` was listed, so
|
|
57
|
+
`forks:100` was fuzzy-matched into `fork:100` and lost its numeric
|
|
58
|
+
validation. `forks`, `topics` and `followers` are now known too, and
|
|
59
|
+
`fork` (boolean) stays distinct from `forks` (count).
|
|
60
|
+
- **Custom-property qualifiers are no longer misread as keywords.**
|
|
61
|
+
`props.environment:production` contains a dot, which the parser rejected, so
|
|
62
|
+
it silently became a literal search term instead of a qualifier.
|
|
63
|
+
- **`--watch` fails fast on a malformed query** instead of retrying the same
|
|
64
|
+
non-transient error five times.
|
|
65
|
+
- **The post-upgrade panel no longer reappears on every launch.** It is
|
|
66
|
+
triggered by `lastInstalledVersion !== currentVersion`, and nothing ever
|
|
67
|
+
cleared `lastInstalledVersion` — so after one upgrade the "Updated ghfind
|
|
68
|
+
X → Y" panel showed again on every single launch, including after dismissing
|
|
69
|
+
it with **later** or **never** (neither touched that field). The panel now
|
|
70
|
+
acknowledges itself when shown. A recorded version _newer_ than the running
|
|
71
|
+
one is deliberately left alone, so a rollback or a mid-session update is
|
|
72
|
+
still reported.
|
|
73
|
+
- **A prerelease is no longer offered as an upgrade.** The Node/compiled
|
|
74
|
+
fallback in the version comparison treated `9.9.0-beta.1` as newer than
|
|
75
|
+
`9.9.0` — advertising a downgrade. Comparison now follows npm ordering: a
|
|
76
|
+
prerelease counts only when its numeric core is genuinely ahead, and build
|
|
77
|
+
metadata (`+build.5`) is ignored rather than corrupting the parse.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## [9.8.3] — 2026-10-01 (released)
|
|
82
|
+
|
|
83
|
+
### Fixed
|
|
84
|
+
|
|
85
|
+
- **The TUI always opens on the landing menu again.** A saved session used to
|
|
86
|
+
skip it on every launch, and one user's session had an MCP handshake request
|
|
87
|
+
saved as its query, so startup auto-ran a garbage search instead of showing
|
|
88
|
+
the menu. Sessions now restore preferences and pre-fill the last query only
|
|
89
|
+
when a mode is picked; they never auto-run a search. Both save and restore
|
|
90
|
+
sanitize the session: JSON-shaped or oversized queries are dropped, and
|
|
91
|
+
malformed fields fall back to defaults, so a corrupted file can't hijack
|
|
92
|
+
startup again.
|
|
93
|
+
- **401/403 API errors now say what actually went wrong instead of "Network
|
|
94
|
+
error".** A rejected token (401) shows "Bad GitHub token (401)" with keys to
|
|
95
|
+
fix it; an authenticated-but-forbidden 403 (e.g. SAML/SSO enforcement) names
|
|
96
|
+
the SSO case; only genuine connectivity/server failures keep the generic
|
|
97
|
+
network message. A rate-limited search no longer swallows the error and
|
|
98
|
+
renders as a silent zero-result "success" — the real message reaches the
|
|
99
|
+
status bar, the MCP server reports it as structured backoff advice, and the
|
|
100
|
+
adapter no longer turns rate limits into empty results.
|
|
101
|
+
- **Recover-from-errors without leaving the TUI.** While the matching error is
|
|
102
|
+
showing, `t` opens an inline token prompt (saves to config and re-runs the
|
|
103
|
+
failed query), `u` clears the token and retries unauthenticated, `c` swaps in
|
|
104
|
+
a different token after a rate limit, and `r` retries. Documented in the help
|
|
105
|
+
overlay and README.
|
|
106
|
+
- **An image wrapped in a link now renders.** The badge-and-screenshot shape
|
|
107
|
+
`[](url)` — used by `sst/opencode` (its only hero image was
|
|
108
|
+
lost) and `withastro/astro` (23 of 25 images lost) — was treated as prose
|
|
109
|
+
because the markdown token walk rejected `link` tokens instead of recursing
|
|
110
|
+
into them. The image now renders and keeps its link as an OSC 8 hyperlink on
|
|
111
|
+
the image cells, so clicking it in the terminal opens the same URL.
|
|
112
|
+
- **A README full of small images no longer floods the screen.** Images whose
|
|
113
|
+
README declares an edge under 100 px (`<img width="48">` sponsor avatars,
|
|
114
|
+
badges) are filtered while parsing, before any request is made; images with
|
|
115
|
+
no declared size are measured from their header after the download
|
|
116
|
+
(`Bun.Image.metadata()`, header read only — no pixel decode) and rejected
|
|
117
|
+
before rasterising. On `openclaw/openclaw` (661 `<img>` tags, 648 of them
|
|
118
|
+
48×48 avatars) this takes the README from 649 downloads / 28.3 s to open to
|
|
119
|
+
1 download / ~0.5 s.
|
|
120
|
+
- A pure-image block whose refs are all filtered out renders as nothing instead
|
|
121
|
+
of falling back to prose, which would have printed the raw markdown of the
|
|
122
|
+
filtered-out image.
|
|
123
|
+
|
|
124
|
+
### Changed
|
|
125
|
+
|
|
126
|
+
- **Images smaller than 100 px on any edge are no longer rendered, even when
|
|
127
|
+
the declared size understates the real one** (two images in
|
|
128
|
+
`openclaw/openclaw` declare `width="48"` but are 420×420). The README is
|
|
129
|
+
treated as the authority on size; the alternative — rendering the avatar at
|
|
130
|
+
whatever the terminal fits — is what made these READMEs unusable.
|
|
131
|
+
- The TUI no longer skips the landing menu on startup: a saved session
|
|
132
|
+
restores sort/limit/trending tab and pre-fills the query when you pick Repo
|
|
133
|
+
Search, but the menu always comes first. See the session-sanitizing fix
|
|
134
|
+
above.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
9
138
|
## [9.8.2] — 2026-09-28 (released)
|
|
10
139
|
|
|
11
140
|
### Security
|
package/README.md
CHANGED
|
@@ -224,6 +224,15 @@ ghfind --completion fish | source
|
|
|
224
224
|
| `Esc` | Close current overlay |
|
|
225
225
|
| `q` | Quit ghfind |
|
|
226
226
|
|
|
227
|
+
**Error recovery** — active only while the matching error is in the status bar
|
|
228
|
+
|
|
229
|
+
| Key | Action |
|
|
230
|
+
| --- | --------------------------------------------- |
|
|
231
|
+
| `r` | Retry the failed search / trending load |
|
|
232
|
+
| `t` | Fix the GitHub token (401 bad token, 403 SSO) |
|
|
233
|
+
| `u` | Search without a token (401) |
|
|
234
|
+
| `c` | Change token (rate limit) |
|
|
235
|
+
|
|
227
236
|
**Leader menu** — press `Space`, navigate with `↑`/`↓`, press `Enter` to dispatch
|
|
228
237
|
|
|
229
238
|
Actions shown depend on current mode (search vs trending) and selection:
|
|
@@ -283,7 +292,22 @@ topic:cli -language:JavaScript
|
|
|
283
292
|
org:rust-lang language:Rust
|
|
284
293
|
```
|
|
285
294
|
|
|
286
|
-
Prefix with `-` to exclude. Supported: `language`, `stars`, `fork`, `archived`, `topic`, `user`, `org`, `repo`, `updated`, `pushed`, `visibility`, `license`, `created`, `
|
|
295
|
+
Prefix with `-` to exclude. Supported: `language`, `stars`, `forks`, `topics`, `followers`, `size`, `fork`, `archived`, `topic`, `user`, `org`, `repo`, `updated`, `pushed`, `visibility`, `license`, `created`, `in`.
|
|
296
|
+
|
|
297
|
+
Queries are parsed and checked before the request is sent:
|
|
298
|
+
|
|
299
|
+
- **Qualifier typos are corrected** — `langauge:Rust` → `language:Rust`,
|
|
300
|
+
`star:1000` → `stars:1000`. Correction is limited to the qualifiers listed
|
|
301
|
+
above, so `is:`, `has:` and `props.*` pass through untouched.
|
|
302
|
+
- **Counts accept `k`/`m`** — `stars:10k` means 10,000 or more. A bare count
|
|
303
|
+
keeps GitHub's exact-match meaning, so use `stars:>=100` or `stars:10k`
|
|
304
|
+
rather than `stars:100`.
|
|
305
|
+
- **An invalid value is rejected with the fix**, not sent to GitHub:
|
|
306
|
+
`⚠ Invalid query: "stars:abc" is not a number — use stars:100, stars:>=100, or stars:1k..10k`
|
|
307
|
+
- **An empty result suggests how to loosen the query** — qualifiers are ranked
|
|
308
|
+
by how sharply they narrow results, and the message offers a concrete retry.
|
|
309
|
+
|
|
310
|
+
See [docs/HOW-TO.md](docs/HOW-TO.md) for a task-oriented walkthrough.
|
|
287
311
|
|
|
288
312
|
---
|
|
289
313
|
|
|
@@ -300,7 +324,7 @@ Prefix with `-` to exclude. Supported: `language`, `stars`, `fork`, `archived`,
|
|
|
300
324
|
}
|
|
301
325
|
```
|
|
302
326
|
|
|
303
|
-
Env: `GITHUB_TOKEN`, `GHFIND_CONFIG`, `GHFIND_SKILL_ROOTS` (path-separator separated skill directories for `ghfind skill search`), `XDG_CONFIG_HOME`, `XDG_STATE_HOME`, `NO_COLOR` (disables colored `--doctor` output, per [no-color.org](https://no-color.org/))
|
|
327
|
+
Env: `GITHUB_TOKEN`, `GHFIND_CONFIG`, `GHFIND_LOG` (path — write TUI diagnostics there; unset by default), `GHFIND_SKILL_ROOTS` (path-separator separated skill directories for `ghfind skill search`), `XDG_CONFIG_HOME`, `XDG_STATE_HOME`, `NO_COLOR` (disables colored `--doctor` output, per [no-color.org](https://no-color.org/))
|
|
304
328
|
|
|
305
329
|
---
|
|
306
330
|
|
|
@@ -313,14 +337,14 @@ cd ghfind
|
|
|
313
337
|
# With Bun (recommended for TUI)
|
|
314
338
|
bun install
|
|
315
339
|
bun start # TUI
|
|
316
|
-
bun test #
|
|
340
|
+
bun test # 624 tests
|
|
317
341
|
bun run check # format check + tests — the green gate
|
|
318
342
|
bun run build # build dist/
|
|
319
343
|
|
|
320
344
|
# Or with npm
|
|
321
345
|
npm install
|
|
322
346
|
npm start # TUI (requires Node 20+)
|
|
323
|
-
npm test #
|
|
347
|
+
npm test # 624 tests
|
|
324
348
|
npm run build # build dist/
|
|
325
349
|
|
|
326
350
|
> Want to contribute? See [CONTRIBUTING.md](CONTRIBUTING.md) for commands,
|
|
@@ -331,23 +355,22 @@ npm run build # build dist/
|
|
|
331
355
|
|
|
332
356
|
## Known limitations
|
|
333
357
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
banners — renders fine and fast. See
|
|
358
|
+
README image rendering has one deliberate blind spot and one hard one:
|
|
359
|
+
|
|
360
|
+
- **Images smaller than 100 px on any edge are skipped.** For `<img>` tags the
|
|
361
|
+
declared `width`/`height` is trusted, so an image that understates its size
|
|
362
|
+
is hidden (a README that _overstates_ nothing is never affected). Markdown
|
|
363
|
+
`![]()` images declare no size, so they are measured from the image header
|
|
364
|
+
instead — still before any pixels are decoded.
|
|
365
|
+
- **SVG is not rendered** — the rasteriser cannot draw it, so SVGs (and
|
|
366
|
+
badge-service images, which are tiny SVGs with text baked in) show an
|
|
367
|
+
`🖼 alt` caption instead.
|
|
368
|
+
|
|
369
|
+
An image wrapped in a link — `[](url)`, the badge-and-screenshot
|
|
370
|
+
style — renders fine and keeps its link: the image cells carry an OSC 8
|
|
371
|
+
hyperlink, so clicking it opens the target in your browser. See
|
|
349
372
|
[.issues/tickets/readme-image-rendering.md](.issues/tickets/readme-image-rendering.md)
|
|
350
|
-
for the measurements and
|
|
373
|
+
for the architecture, measurements, and remaining work.
|
|
351
374
|
|
|
352
375
|
---
|
|
353
376
|
|
|
@@ -355,6 +378,28 @@ for the measurements and the remaining work.
|
|
|
355
378
|
|
|
356
379
|
Recent releases (full history in [CHANGELOG.md](CHANGELOG.md)):
|
|
357
380
|
|
|
381
|
+
### v9.9.0
|
|
382
|
+
|
|
383
|
+
- **Queries are understood before they are sent** — every surface (TUI, CLI, MCP, `--watch`) parses and checks a query first, so a typo or bad value fails with a specific message and a fix instead of an opaque GitHub 422 or a misleading "network error"; previously only the TUI ran validation at all
|
|
384
|
+
- **Qualifier typos are corrected** — `langauge:Rust` → `language:Rust`, `star:1000` → `stars:1000`, `push:>` → `pushed:>`; correction is limited to the qualifiers ghfind knows, so `is:`, `has:` and `props.*` are never mangled
|
|
385
|
+
- **Counts accept `k`/`m`** — `stars:10k` → `stars:>=10000`, `size:1m`, `stars:1k..10k`; a bare count keeps GitHub's exact-match meaning, so `stars:100` is still exactly 100
|
|
386
|
+
- **Values are validated per qualifier** — numeric counts, `fork:true|false|only`, `archived:true|false`, calendar-checked ISO8601 dates for `created`/`pushed`/`updated`, the `visibility:` enum, and `in:` fields; documented comma-lists like `in:name,description` still work
|
|
387
|
+
- **Zero results suggest how to loosen the query** — `No results for "rust stars:>=5000000"` now offers `Try: rust stars:>=500000`, ranked by how sharply each qualifier narrows results
|
|
388
|
+
- **`GHFIND_LOG=<path>` writes TUI diagnostics to a file** — the TUI's logger was a silent no-op, making API failures undiagnosable; token values are redacted from logged URLs
|
|
389
|
+
- **A 403 no longer claims SSO is the cause** — SSO enforcement, secondary rate limits and org restrictions all produce it, and the message asserted the first one unconditionally; it now reports GitHub's own reason
|
|
390
|
+
- **The post-upgrade panel no longer reappears on every launch** — it was triggered by a version inequality nothing ever cleared, so it showed after every single launch _including_ after "later" or "never"; it now acknowledges itself once shown
|
|
391
|
+
- **A prerelease is no longer offered as an upgrade** — the version comparison used by Node and the compiled binaries reported `9.9.0-beta.1` as newer than `9.9.0`, i.e. a downgrade; it now follows npm ordering and ignores build metadata
|
|
392
|
+
- **A new [how-to guide](docs/HOW-TO.md)** — token setup, writing queries, reading results, recovering from errors, and copy-paste recipes
|
|
393
|
+
|
|
394
|
+
### v9.8.3
|
|
395
|
+
|
|
396
|
+
- **401/403 say what actually happened** — a rejected token and an authenticated-but-forbidden request (e.g. SAML/SSO enforcement) both used to report "Network error", naming a connectivity problem you didn't have; only genuine connectivity and server failures keep that message now
|
|
397
|
+
- **A rate-limited search no longer looks like an empty one** — the adapter used to swallow the error and render a silent, successful "no repositories found"; the real message reaches the status bar, and the `ghfind_search_repos` MCP tool returns `rateLimited: true` with backoff advice instead of a tool failure
|
|
398
|
+
- **Recover from errors without leaving the TUI** — while the matching error is in the status bar, `t` opens an inline token prompt (saves it and re-runs the failed query), `u` clears the token and retries unauthenticated, `c` swaps in a different token, `r` retries
|
|
399
|
+
- **A corrupted session no longer hijacks startup** — a `session.json` holding an MCP handshake blob used to skip the landing menu and auto-run that garbage as a search on every launch; the menu always comes first now, and both save and restore sanitize the session
|
|
400
|
+
- **Images wrapped in a link render now** — the badge-and-screenshot shape `[](url)` rendered nothing at all, which cost `sst/opencode` its only image and `withastro/astro` 23 of its 25; the image cells carry an OSC 8 hyperlink, so clicking it opens the target
|
|
401
|
+
- **A README full of small images no longer floods the screen** — images the README declares under 100px on any edge are skipped before a request is made, and undeclared `![]()` images are measured from the image header before any pixel decode; on `openclaw/openclaw` (648 sponsor avatars) that is 649 fetches / 28.3s → 1 fetch / ~0.5s
|
|
402
|
+
|
|
358
403
|
### v9.8.2
|
|
359
404
|
|
|
360
405
|
- **Security: tokens no longer leak into crash reports** — an uncaught error auto-built a GitHub issue URL from raw `process.argv`, so a `--token` value ended up in a public URL; every field is redacted now, and ghfind prints the URL to copy instead of opening a browser when it finds a credential
|
|
@@ -380,12 +425,6 @@ Recent releases (full history in [CHANGELOG.md](CHANGELOG.md)):
|
|
|
380
425
|
[Known limitations](#known-limitations); the v9.8.0 notes wrongly described it
|
|
381
426
|
as working
|
|
382
427
|
|
|
383
|
-
### v9.8.0
|
|
384
|
-
|
|
385
|
-
- **README viewer renders real markdown** — headings, emphasis, links, lists, blockquotes, boxed tables, and fenced code now come from OpenTUI's markdown renderable with a theme-derived syntax style instead of a box-drawing text approximation
|
|
386
|
-
- **Images render inline — see [Known limitations](#known-limitations)** — relative and `/`-rooted paths resolve against the README's branch, `github.com/.../blob/...` links are rewritten to raw content, downloads are cached and size-capped, SVG and badge-service images are skipped, and anything unloadable falls back to an `🖼 alt` caption; press `i` in the viewer to toggle images
|
|
387
|
-
- **No new dependencies** — the PNG decoder, image-to-cells rasteriser, and fetch cache are plain TypeScript, and images draw with truecolor half blocks, so they work on any truecolor terminal without image-protocol support
|
|
388
|
-
|
|
389
428
|
## License
|
|
390
429
|
|
|
391
430
|
MIT
|