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 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
+ `[![alt](shot.png)](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`, `size`, `in`.
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 # 482 tests
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 # 414 tests
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
- **README image rendering is currently incomplete.** Images that do render look
335
- right, but several common cases silently show nothing:
336
-
337
- - **An image wrapped in a link does not render at all.** This is the common
338
- badge-and-screenshot style: `[![alt](shot.png)](https://example.com)`.
339
- The whole block is treated as prose and the image is dropped.
340
- - **An image smaller than 100px is skipped**, judged by the `width`/`height`
341
- the README declares in its `<img>` tag. A README that understates an
342
- image's size hides that image.
343
- - **SVG is not rendered** — the rasteriser cannot draw it.
344
- - Markdown `![](url)` images have no declared size, so they are downloaded
345
- before the size check can reject them. A README full of them is slow to open.
346
-
347
- A README using none of the above — plain `![]()` images and a few large
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 — `[![alt](shot.png)](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 the remaining work.
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 `[![alt](shot.png)](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