github-search-cli 9.8.0 → 9.8.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-search-cli",
3
- "version": "9.8.0",
3
+ "version": "9.8.2",
4
4
  "description": "Search GitHub from your terminal — repo search, trending repos, npm package search, bookmarks, deep-dive, compare, export. Keyboard-driven TUI + pipeable CLI.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1",
@@ -0,0 +1,74 @@
1
+ # What's New in ghfind 9.8.1
2
+
3
+ Agent skills and read-only CLI views for scripts.
4
+
5
+ This is a patch release. It also corrects the v9.8.0 release notes, which
6
+ claimed README image rendering worked. It does not, and this release does not
7
+ fix it — see the note at the end.
8
+
9
+ ### Added
10
+
11
+ **`ghfind skill search <query>`**
12
+
13
+ - Finds agent skills by keyword and prints why each one matched.
14
+ - Scans `SKILL.md` files under `~/.codex/skills`, `~/.agents/skills`,
15
+ `<project>/.codex/skills` and `<project>/.github/skills`, in that
16
+ priority order, de-duplicated by name.
17
+ - `--remote` queries the public [skills.sh](https://skills.sh) ecosystem
18
+ instead, returning each hit's install count and `npx skills add` command.
19
+ - `GHFIND_SKILL_ROOTS` overrides which directories are scanned.
20
+
21
+ **MCP tool `ghfind_skill_search`**
22
+
23
+ - Agents can search skills as soon as they connect: `source: "local"` (the
24
+ default) searches installed skills, `source: "registry"` searches
25
+ skills.sh. Read-only — it returns install commands and never writes to disk.
26
+
27
+ **Read-only CLI views of local state**
28
+
29
+ - `ghfind bookmarks [query]`, `ghfind history [query]`, `ghfind saved`
30
+ and `ghfind topics`, each with `--json` for agents. They print what the
31
+ TUI has stored and never modify it.
32
+
33
+ **`ghfind readme <owner/repo>` and `ghfind share <owner/repo>`**
34
+
35
+ - `ghfind readme` prints a repository's README (`--raw` skips the header,
36
+ `--json` returns `{ owner, name, sourceUrl, text }`), bringing the TUI's
37
+ README viewer to scripts and agents.
38
+ - `ghfind share` prints a share snippet (`--as markdown|plain|gh-cli|short`),
39
+ with `--copy` to also put it on the clipboard.
40
+
41
+ **Shell completions**
42
+
43
+ - bash, zsh and fish completions cover every new subcommand and flag.
44
+
45
+ ### Changed
46
+
47
+ - The bundled `ghfind-cli` agent skill documents skill search and the new
48
+ read-only commands, so CLI-first agents discover them without reading
49
+ `--help`.
50
+
51
+ ### Fixed
52
+
53
+ **README image rendering is not working — please read this before upgrading**
54
+
55
+ The 9.8.0 README viewer added inline image support. On real repositories it
56
+ does not work reliably, and this release does not fix it. Known failures, all
57
+ verified against live READMEs:
58
+
59
+ - **An image wrapped in a link renders nothing.** `[![alt](shot.png)](url)`
60
+ is a common way to write a linked screenshot. The whole block is treated as
61
+ prose and the image is dropped. This affects repositories such as
62
+ `sst/opencode`.
63
+ - **A README with hundreds of small images is unusable.** `openclaw/openclaw`
64
+ has 661 `<img>` tags, 648 of them 48x48 sponsor avatars. Each becomes a
65
+ full screen of terminal cells, so the document is 15,552 rows tall.
66
+ - **An image smaller than 100px is skipped**, judged by the `width`/`height`
67
+ its `<img>` tag declares. A README that understates an image's size hides
68
+ that image.
69
+ - **SVG is not rendered.**
70
+
71
+ Plain `![]()` images and large banners do render, correctly and quickly. The
72
+ v9.8.0 notes described image rendering as working; that was wrong, and it is
73
+ corrected here. Measurements, root causes and the remaining work are tracked
74
+ in `.issues/tickets/readme-image-rendering.md`.
@@ -0,0 +1,65 @@
1
+ # v9.8.2
2
+
3
+ A maintenance release: twenty-three fixes from a full static audit of the
4
+ codebase, most of them invisible until something went wrong. Nothing user-facing
5
+ was added or removed — every flag, key and command behaves as it did in 9.8.1.
6
+
7
+ ## The one that mattered
8
+
9
+ A GitHub token passed as `--token` could end up in a public URL. When ghfind
10
+ crashed, it built a GitHub issue link straight from `process.argv` — argv
11
+ included your token — and opened it in a browser. The docblock claimed the
12
+ command line was redacted; it was not. Every field that reaches the issue now
13
+ goes through redaction first, and when ghfind sees a credential it prints the
14
+ URL for you to copy instead of opening a browser tab automatically.
15
+
16
+ ## Security, in full
17
+
18
+ - Tokens are no longer sent to `raw.githubusercontent.com` when fetching a
19
+ README — only to `api.github.com`, and only for a private repository.
20
+ - Skill sources and ids from the public skills.sh registry are validated before
21
+ they can reach a shell command or a URL, so a hostile registry entry cannot
22
+ smuggle `;`, `$(`, backticks or a path traversal into `npx skills add`.
23
+ - The config file that stores your token is now written with `0600`
24
+ permissions. On Windows the file still inherits the directory ACL, so this
25
+ closes the hole on POSIX only.
26
+ - `--doctor` masks the token rather than printing a raw prefix.
27
+
28
+ ## Reliability
29
+
30
+ The most user-visible correctness fix: recoverable errors were treated as
31
+ crashes. The top-level catch tested `err.name === "SearchCliError"`, a string
32
+ that is never true, so a rate limit, a network blip or a layout change on
33
+ GitHub's trending page all opened a browser tab asking you to file a bug. That
34
+ branch now works, and those errors print a short explanation and exit.
35
+
36
+ `--compare` and the `ghfind_compare_repos` MCP tool both resolved repository
37
+ names through a free-text search with no `repo:` qualifier and never checked
38
+ the `fullName` that came back, so `ghfind --compare oven/bun oven-sh/bun` could
39
+ quietly compare the wrong two repositories. Both now resolve exactly.
40
+
41
+ State files — bookmarks, history, sessions, saved searches, notifications —
42
+ were written directly to their live path. A crash, a full disk, or a reader
43
+ caught mid-write left a truncated JSON file, which the loader then silently
44
+ replaced with defaults: your data was gone with no error. Writes are now atomic
45
+ (temp file plus rename).
46
+
47
+ ## Smaller fixes
48
+
49
+ Notification IDs restarted after "dismiss all"; `history --delete` removed
50
+ every duplicate entry instead of one; an invalid `--sort` value degraded
51
+ silently instead of erroring; a failed "load more" in the TUI wiped the results
52
+ already on screen; `--markdown` did not escape `|` in cells while `--csv`
53
+ did; the in-memory cache was not a real LRU; local skill scanning re-walked the
54
+ filesystem on every keystroke; and skill ids containing `_` — common on GitHub
55
+ — were wrongly rejected.
56
+
57
+ ## Verification
58
+
59
+ 482 tests, plus `typecheck`, `lint`, `format:check` and `build` all green.
60
+ The security fixes were additionally verified with runnable probes that assert
61
+ no token substring survives URL encoding in the generated crash-report URL, that
62
+ none of twelve `raw.githubusercontent.com` requests carries an `Authorization`
63
+ header, and that seven of eight hostile skill payloads are rejected.
64
+
65
+ `README.md` test count updated to match the real suite.
package/scripts/brew.rb CHANGED
@@ -1,53 +1,53 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- # Homebrew formula for ghfind.
5
- #
6
- # NOTE: this file is a template, kept in-tree for a future homebrew tap.
7
- # Before use:
8
- # 1. Replace VERSION with the released version tag.
9
- # 2. Fill SHA256 placeholders from the SHA256SUMS.txt asset attached to
10
- # the GitHub Release (the release workflow generates it).
11
- # 3. Host it in a tap repo (e.g. codersdfs/tap) at Formula/ghfind.rb,
12
- # then: brew install codersdfs/tap/ghfind
13
- # See marketing/README.md (Track B) for the rollout order.
14
-
15
- class Ghfind < Formula
16
- desc "Search GitHub repos from your terminal — trending, bookmarks, deep-dive"
17
- homepage "https://github.com/codersdfs/search-cli"
18
- version "9.5.1"
19
- license "MIT"
20
-
21
- livecheck do
22
- url :stable
23
- regex(/^v?(\d+(?:\.\d+)+)$/i)
24
- end
25
-
26
- on_macos do
27
- if Hardware::CPU.arm?
28
- url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-darwin-arm64"
29
- sha256 "SHA256_PLACEHOLDER"
30
- else
31
- url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-darwin-x64"
32
- sha256 "SHA256_PLACEHOLDER"
33
- end
34
- end
35
-
36
- on_linux do
37
- if Hardware::CPU.arm? && Hardware::CPU.is_64_bit?
38
- url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-linux-arm64"
39
- sha256 "SHA256_PLACEHOLDER"
40
- else
41
- url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-linux-x64"
42
- sha256 "SHA256_PLACEHOLDER"
43
- end
44
- end
45
-
46
- def install
47
- bin.install "ghfind" => "ghfind"
48
- end
49
-
50
- test do
51
- assert_match version.to_s, shell_output("#{bin}/ghfind --version")
52
- end
53
- end
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ # Homebrew formula for ghfind.
5
+ #
6
+ # NOTE: this file is a template, kept in-tree for a future homebrew tap.
7
+ # Before use:
8
+ # 1. Replace VERSION with the released version tag.
9
+ # 2. Fill SHA256 placeholders from the SHA256SUMS.txt asset attached to
10
+ # the GitHub Release (the release workflow generates it).
11
+ # 3. Host it in a tap repo (e.g. codersdfs/tap) at Formula/ghfind.rb,
12
+ # then: brew install codersdfs/tap/ghfind
13
+ # See marketing/README.md (Track B) for the rollout order.
14
+
15
+ class Ghfind < Formula
16
+ desc "Search GitHub repos from your terminal — trending, bookmarks, deep-dive"
17
+ homepage "https://github.com/codersdfs/search-cli"
18
+ version "9.5.1"
19
+ license "MIT"
20
+
21
+ livecheck do
22
+ url :stable
23
+ regex(/^v?(\d+(?:\.\d+)+)$/i)
24
+ end
25
+
26
+ on_macos do
27
+ if Hardware::CPU.arm?
28
+ url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-darwin-arm64"
29
+ sha256 "SHA256_PLACEHOLDER"
30
+ else
31
+ url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-darwin-x64"
32
+ sha256 "SHA256_PLACEHOLDER"
33
+ end
34
+ end
35
+
36
+ on_linux do
37
+ if Hardware::CPU.arm? && Hardware::CPU.is_64_bit?
38
+ url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-linux-arm64"
39
+ sha256 "SHA256_PLACEHOLDER"
40
+ else
41
+ url "https://github.com/codersdfs/search-cli/releases/download/v#{version}/ghfind-linux-x64"
42
+ sha256 "SHA256_PLACEHOLDER"
43
+ end
44
+ end
45
+
46
+ def install
47
+ bin.install "ghfind" => "ghfind"
48
+ end
49
+
50
+ test do
51
+ assert_match version.to_s, shell_output("#{bin}/ghfind --version")
52
+ end
53
+ end
@@ -18,6 +18,7 @@ import { join, dirname } from "node:path";
18
18
  import { fileURLToPath } from "node:url";
19
19
  import { platform, arch } from "node:os";
20
20
  import { spawnSync } from "node:child_process";
21
+ import { createHash } from "node:crypto";
21
22
  import { inflateRawSync } from "node:zlib";
22
23
 
23
24
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -96,6 +97,35 @@ const key = `${platform()}-${arch}`;
96
97
  const assetName = map[key];
97
98
  const version = "1.3.14";
98
99
 
100
+ /**
101
+ * Pinned SHA256 of every Bun release asset this script can install, taken
102
+ * from that release's own SHASUMS256.txt. The download is verified before
103
+ * anything is written, so a tampered or truncated archive can never become
104
+ * an executable in vendor/.
105
+ *
106
+ * ponytail: hand-pinned per Bun release — regenerate this table (and bump
107
+ * `version`) whenever the release is bumped; the check is what makes the
108
+ * hardcoded `version` above trustworthy.
109
+ */
110
+ const SHA256 = {
111
+ "bun-windows-x64.zip":
112
+ "0a0620930b6675d7ba440e81f4e0e00d3cfbe096c4b140d3fff02205e9e18922",
113
+ "bun-windows-aarch64.zip":
114
+ "89841f5a57f2348b67ec0839b718f4bf4ea7d07c371c9ba4b77b6c790f918953",
115
+ "bun-darwin-x64.zip":
116
+ "4183df3374623e5bab315c547cfa0974533cd457d86b73b639f7a87974cd6633",
117
+ "bun-darwin-aarch64.zip":
118
+ "d8b96221828ad6f97ac7ac0ab7e95872341af763001e8803e8267652c2652620",
119
+ "bun-linux-x64.zip":
120
+ "951ee2aee855f08595aeec6225226a298d3fea83a3dcd6465c09cbccdf7e848f",
121
+ "bun-linux-x64-musl.zip":
122
+ "14bd9aedeebf1dba67e8def9531c89bc989ecfdf1de42e5bfcaf1b8cd9294719",
123
+ "bun-linux-aarch64.zip":
124
+ "a27ffb63a8310375836e0d6f668ae17fa8d8d18b88c37c821c65331973a19a3b",
125
+ "bun-linux-aarch64-musl.zip":
126
+ "b98e0ad3625c5c00d1d5b5ff55605c7adddbfae151861e68ade57b2d3b8703bb",
127
+ };
128
+
99
129
  if (!assetName) {
100
130
  console.error(
101
131
  `[ghfind] Unsupported platform: ${key}. Install Bun manually: https://bun.sh`,
@@ -116,6 +146,14 @@ try {
116
146
 
117
147
  const buffer = Buffer.from(await res.arrayBuffer());
118
148
 
149
+ const expected = SHA256[assetName];
150
+ const actual = createHash("sha256").update(buffer).digest("hex");
151
+ if (expected !== actual) {
152
+ throw new Error(
153
+ `checksum mismatch for ${assetName} (expected ${expected}, got ${actual})`,
154
+ );
155
+ }
156
+
119
157
  // Find the central directory file header signature (PK\x01\x02)
120
158
  const cdSig = "PK\x01\x02";
121
159
  let cdOffset = buffer.indexOf(cdSig, 0);
@@ -77,6 +77,12 @@ reported in text output and via the error message.
77
77
  ghfind deep-dive owner/repo --json
78
78
  Text output (no flag) prints the same sections formatted for the terminal.
79
79
 
80
+ ## Finding skills
81
+ ghfind skill search QUERY # skills installed on this machine
82
+ ghfind skill search QUERY --remote # the public skills.sh ecosystem
83
+ Results carry path+description (local) or install command (registry, read-only --
84
+ nothing is installed). The MCP server exposes the same search as
85
+ ghfind_skill_search.
80
86
  ## Rate limits
81
87
 
82
88
  60 req/hr unauthenticated, 5,000 with GITHUB_TOKEN (env var or \`ghfind login\`).
@@ -88,6 +94,14 @@ Bookmarks live in ghfind's local state, not GitHub. An agent can read the
88
94
  release feed with:
89
95
  ghfind --releases --json
90
96
  Marking releases seen and editing bookmarks is a TUI action; skip it.
97
+ ## Local state (read-only)
98
+ ghfind bookmarks [QUERY] --json # saved repos, newest first
99
+ ghfind history [QUERY] --json # past searches
100
+ ghfind saved --json # named searches
101
+ ghfind topics --json # popular GitHub topics
102
+ ghfind readme owner/repo # print a repo README
103
+ ghfind share owner/repo --as gh-cli|markdown|plain|short
104
+ These never write local state; skip them when nothing needs reading.
91
105
 
92
106
  ## Token efficiency
93
107