@gkoreli/ghx 0.2.0 → 2.0.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/README.md CHANGED
@@ -1,121 +1,117 @@
1
- # ghx
1
+ # ghx — GitHub Code Exploration for AI Agents
2
2
 
3
- GitHub code exploration for agents and humans. One command does what takes 3-5 API calls with any other tool.
3
+ One command does what takes 3-5 API calls. Batch file reads, code maps, search — all via `gh` CLI. Write JS programs that compose operations in one round-trip.
4
4
 
5
- - **Repos** — search repos with README preview in 1 GraphQL call
6
- - **Explore** a repo (tree + README) in 1 API call
7
- - **Read** 1-10 files in 1 API call via GraphQL batching
8
- - **Map** code structure with ~92% token reduction
9
- - **Search** code with AND matching, matching context, token protection
5
+ ## Why
6
+
7
+ AI agents exploring GitHub face a reliability gap: *"Did I find nothing because nothing exists, or because I used the tool wrong?"* Raw `gh` commands have silent failure modes — `gh search code` wraps in quotes without telling you, `gh api contents/` returns base64, README requires a separate call. The agent can't distinguish "no results" from "wrong flags."
10
8
 
11
- Bash script. One dependency: `gh` CLI. Cross-platform (macOS, Linux, Windows via Git Bash/WSL).
9
+ ghx eliminates this by encoding the right defaults into every command. One call returns enough context to decide the next action.
10
+
11
+ | Tool | Files per call | Matching context | Programmable | Dependencies |
12
+ |------|---------------|-----------------|-------------|-------------|
13
+ | GitHub MCP | 1 | No | No (~10K token schemas) | Go binary |
14
+ | `gh` CLI | 1 | No | No (exact phrase, base64, no README) | `gh` |
15
+ | **ghx** | **1-10 (batch)** | **Yes** | **Yes (codemode)** | **`gh`** |
12
16
 
13
17
  ## Install
14
18
 
15
19
  ```bash
16
- # npm (recommended)
17
- npm install -g @gkoreli/ghx
20
+ # Build from source
21
+ cd v2 && go build -o ghx .
18
22
 
19
- # npx (zero install)
20
- npx @gkoreli/ghx --help
23
+ # Homebrew
24
+ brew install gkoreli/tap/ghx
21
25
 
22
- # curl
23
- curl -sf https://raw.githubusercontent.com/gkoreli/ghx/main/install.sh | sh
26
+ # One-liner
27
+ curl -sfL https://raw.githubusercontent.com/gkoreli/ghx/mainline/install.sh | bash
24
28
  ```
25
29
 
26
- Requires [`gh` CLI](https://cli.github.com/) authenticated (`gh auth login`).
27
-
28
- [![npm](https://img.shields.io/npm/v/@gkoreli/ghx)](https://www.npmjs.com/package/@gkoreli/ghx)
29
-
30
- ### Platform Support
31
-
32
- | Platform | Status | Notes |
33
- |----------|--------|-------|
34
- | macOS | ✅ Native | bash + readlink -f (12.3+) |
35
- | Linux | ✅ Native | bash + GNU coreutils |
36
- | Windows | ✅ Git Bash / WSL | Ships with Git for Windows. Raw cmd.exe/PowerShell not supported |
30
+ Requires: [gh CLI](https://cli.github.com/) authenticated (`gh auth login`).
37
31
 
38
- If you have `gh` CLI working, ghx works too — same prerequisites.
39
-
40
- ## Usage
32
+ ## Commands
41
33
 
42
34
  ```bash
43
- # Search repos name, stars, language, README preview in 1 call
44
- ghx repos "react state management"
35
+ ghx explore <owner/repo> # Branch + tree + README in 1 API call
36
+ ghx explore <owner/repo> <path> # Subdirectory listing
37
+ ghx read <owner/repo> <f1> [f2] [f3] # Read 1-10 files (GraphQL batching)
38
+ ghx search "<query>" # Code search with matching lines
39
+ ghx repos "<query>" # Repo search with README preview
40
+ ghx tree <owner/repo> [path] # Full recursive tree
41
+ ```
45
42
 
46
- # Explore a repo — branch, file tree, and README in 1 API call
47
- ghx explore plausible/analytics
43
+ ## Codemode
48
44
 
49
- # Read multiple files in 1 API call
50
- ghx read plausible/analytics mix.exs assets/js/dashboard/stats/bar.js
45
+ Write JS programs that compose multiple operations in one round-trip. All `codemode.*` calls are synchronous — no `await`. Full TypeScript type stubs with return types are injected into the sandbox.
51
46
 
52
- # Code map — signatures, imports, types only (~92% token reduction)
53
- ghx read plausible/analytics --map lib/plausible/stats/query.ex
47
+ ```bash
48
+ # What branch is this repo on?
49
+ ghx code 'var r = codemode.explore({repo: "vercel/next.js"}); return r.branch;'
54
50
 
55
- # Grep within a remote file (2 lines context)
56
- ghx read plausible/analytics --grep "defmodule" lib/plausible/stats/query.ex
51
+ # Search + read composition
52
+ ghx code 'var hits = codemode.search({query: "useState repo:vercel/next.js", limit: 3});
53
+ var first = codemode.read({repo: "vercel/next.js", files: [hits.matches[0].path]});
54
+ return {file: hits.matches[0].path, lines: first[0].content.split("\n").length};'
57
55
 
58
- # Read specific line range
59
- ghx read plausible/analytics --lines 42-80 lib/plausible/stats/query.ex
56
+ # See what tools and types are available
57
+ ghx code --list
58
+ ```
60
59
 
61
- # Search code (AND matching, shows matching lines, token-protected)
62
- ghx search "useState repo:facebook/react"
63
- ghx search "path:llms.txt extension:txt"
60
+ Type stubs tell the LLM exactly what fields exist — no guessing:
64
61
 
65
- # Full recursive tree
66
- ghx tree plausible/analytics assets/js
62
+ ```typescript
63
+ declare const codemode: {
64
+ explore: (input: ExploreInput) => { description: string; branch: string; files: { name: string; type: string }[]; readme: string };
65
+ search: (input: SearchInput) => { total: number; incomplete: boolean; matches: { repo: string; path: string; fragment: string }[] };
66
+ tree: (input: TreeInput) => string[];
67
+ // ...
68
+ }
67
69
  ```
68
70
 
69
- ## Why
70
-
71
- AI agents exploring GitHub face a reliability gap: *"Did I find nothing because nothing exists, or because I used the tool wrong?"* ghx eliminates this with smart defaults — AND matching instead of exact phrase, README previews instead of bare names, matching context instead of bare paths. The right behavior is the default behavior.
71
+ ## MCP Server
72
72
 
73
- | Tool | Files per call | Matching context | Smart defaults | Dependencies |
74
- |------|---------------|-----------------|---------------|-------------|
75
- | GitHub MCP | 1 | No | No (~10K token schemas) | Go binary |
76
- | `gh` CLI | 1 | No | No (exact phrase, base64, no README) | `gh` |
77
- | **ghx** | **1-10 (batch)** | **Yes** | **Yes** | **`gh`** |
73
+ ```bash
74
+ ghx serve # stdio (for Claude, Cursor, etc.)
75
+ ghx serve --http :8080 # HTTP transport
76
+ ```
78
77
 
79
- ## Agent Skill Integration
78
+ 7 tools: `explore`, `read`, `search`, `repos`, `tree`, `code` (meta-tool), `search_tools`.
80
79
 
81
- `ghx skill` outputs the full [`SKILL.md`](./SKILL.md) to stdout — designed for eager context injection via spawn hooks:
80
+ ## Agent Integration
82
81
 
83
- ```json
84
- {
85
- "hooks": {
86
- "agentSpawn": [
87
- {"command": "ghx skill"}
88
- ]
89
- }
90
- }
82
+ ```bash
83
+ ghx skill # CLI skill (for SKILL.md injection)
84
+ ghx skill --mcp # MCP skill
91
85
  ```
92
86
 
93
- This loads the skill eagerly into every sessionnot on-demand like a typical skill file. Use this for agent identities where GitHub exploration is a core capability (not occasional). The agent always has the latest ghx knowledge (commands, gotchas, search strategy) without needing to load it mid-conversation.
87
+ Designed for eager context injection via spawn hooksthe agent always has the latest ghx knowledge without loading it mid-conversation.
94
88
 
95
- SKILL.md is included in the npm package and resolved via symlink, so this works with all installation methods.
89
+ ## How It Works
96
90
 
97
- ## Code Map (`--map`)
91
+ Wraps `gh` CLI with GraphQL batching. `repos` and `explore` batch search + metadata + README into 1 call. `read` uses GraphQL aliases to fetch up to 10 files in 1 call. `search` hits REST `/search/code` with `text_matches` for matching context and 200-char token protection.
98
92
 
99
- The `--map` flag extracts only structural declarations imports, exports, function signatures, class definitions, type declarations. Implementation bodies are stripped.
93
+ Codemode runs JS in a [goja](https://github.com/nicholasgasior/goja) sandbox with esbuild TypeScript transpilation. Tools are injected as synchronous functions on a `codemode` global object. Max 20 tool calls per execution, 64KB code size limit.
100
94
 
101
- | File | Full | Map | Reduction |
102
- |------|------|-----|-----------|
103
- | repomix/parseFile.ts | 5,599 | 812 | 86% |
104
- | github-mcp/repositories.go | 68,862 | 1,551 | 97.7% |
105
- | aider/repomap.py | 27,346 | 1,496 | 94.5% |
95
+ ## Architecture
106
96
 
107
- Average: **92% reduction**. An agent can map 16 files in the space of reading 1 file fully.
97
+ ```
98
+ v2/
99
+ ├── pkg/ghx/ — core library (Explore, Read, Search, Repos, Tree)
100
+ ├── pkg/codemode/ — JS executor (goja sandbox, TS transpilation, type generation)
101
+ └── cmd/ — CLI frontend (cobra) + MCP server (mcp-go)
102
+ ```
108
103
 
109
- Supported: TypeScript/JavaScript, Python, Go, Rust, Java/Kotlin, Ruby. Generic fallback for unknown extensions.
104
+ See [docs/adr/](docs/adr/) for architectural decisions.
110
105
 
111
- ## How It Works
106
+ ## v1 (bash)
112
107
 
113
- `ghx` wraps `gh` CLI with GraphQL batching. `repos` and `explore` use GraphQL to batch search + metadata + README into 1 call. `read` uses GraphQL aliases to fetch up to 10 files in 1 call. `search` hits the REST `/search/code` endpoint with `text_matches` for matching context and 200-char token protection.
108
+ The original bash implementation is in [`v1/`](v1/). Zero dependencies beyond `gh` and `jq` useful if you just want a shell script you can drop anywhere without compiling Go.
109
+
110
+ ```bash
111
+ npm install -g @gkoreli/ghx # npm
112
+ cp v1/ghx /usr/local/bin/ghx # manual
113
+ ```
114
114
 
115
115
  ## License
116
116
 
117
117
  MIT
118
-
119
- ## For AI Agents
120
-
121
- See [`SKILL.md`](./SKILL.md) for agent-optimized instructions — chain of thought, gotchas, anti-patterns, and examples.
package/package.json CHANGED
@@ -1,18 +1,34 @@
1
1
  {
2
2
  "name": "@gkoreli/ghx",
3
- "version": "0.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "GitHub code exploration for agents and humans. Batch file reads, code maps, search — all via gh CLI.",
5
5
  "bin": {
6
- "ghx": "./ghx"
6
+ "ghx": "./v1/ghx"
7
7
  },
8
- "keywords": ["github", "cli", "code-exploration", "agent", "graphql", "code-map"],
8
+ "keywords": [
9
+ "github",
10
+ "cli",
11
+ "code-exploration",
12
+ "agent",
13
+ "graphql",
14
+ "code-map"
15
+ ],
9
16
  "license": "MIT",
10
17
  "repository": {
11
18
  "type": "git",
12
19
  "url": "https://github.com/gkoreli/ghx"
13
20
  },
14
- "files": ["ghx", "SKILL.md", "README.md", "LICENSE"],
15
- "os": ["darwin", "linux", "win32"],
21
+ "files": [
22
+ "v1/ghx",
23
+ "v1/SKILL.md",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "os": [
28
+ "darwin",
29
+ "linux",
30
+ "win32"
31
+ ],
16
32
  "engines": {
17
33
  "node": ">=16"
18
34
  }
package/v1/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # ghx v1 — Bash Implementation
2
+
3
+ > For the current Go version with codemode, MCP server, and typed stubs, see the [root README](../README.md).
4
+
5
+ The bash implementation — zero dependencies beyond `gh` CLI. Useful if you want a single shell script you can drop anywhere without compiling Go.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ # npm
11
+ npm install -g @gkoreli/ghx
12
+
13
+ # npx (zero install)
14
+ npx @gkoreli/ghx --help
15
+
16
+ # curl
17
+ curl -sf https://raw.githubusercontent.com/gkoreli/ghx/mainline/v1/install.sh | sh
18
+
19
+ # manual
20
+ cp v1/ghx /usr/local/bin/ghx
21
+ ```
22
+
23
+ Requires [`gh` CLI](https://cli.github.com/) authenticated (`gh auth login`).
24
+
25
+ ### Platform Support
26
+
27
+ | Platform | Status | Notes |
28
+ |----------|--------|-------|
29
+ | macOS | ✅ Native | bash + readlink -f (12.3+) |
30
+ | Linux | ✅ Native | bash + GNU coreutils |
31
+ | Windows | ✅ Git Bash / WSL | Ships with Git for Windows |
32
+
33
+ ## Usage
34
+
35
+ ```bash
36
+ ghx repos "react state management" # Search repos with README preview
37
+ ghx explore plausible/analytics # Branch + tree + README in 1 API call
38
+ ghx read plausible/analytics mix.exs assets/js/dashboard/stats/bar.js # Batch read
39
+ ghx read plausible/analytics --map lib/plausible/stats/query.ex # Code map (~92% reduction)
40
+ ghx read plausible/analytics --grep "defmodule" lib/plausible/stats/query.ex
41
+ ghx read plausible/analytics --lines 42-80 lib/plausible/stats/query.ex
42
+ ghx search "useState repo:facebook/react" # Code search with matching lines
43
+ ghx tree plausible/analytics assets/js # Full recursive tree
44
+ ```
45
+
46
+ ## Code Map (`--map`)
47
+
48
+ Extracts only structural declarations — imports, exports, function signatures, class definitions, type declarations. Implementation bodies are stripped.
49
+
50
+ | File | Full | Map | Reduction |
51
+ |------|------|-----|-----------|
52
+ | repomix/parseFile.ts | 5,599 | 812 | 86% |
53
+ | github-mcp/repositories.go | 68,862 | 1,551 | 97.7% |
54
+ | aider/repomap.py | 27,346 | 1,496 | 94.5% |
55
+
56
+ Average: **92% reduction**. Map 16 files in the space of reading 1 file fully.
57
+
58
+ Supported: TypeScript/JavaScript, Python, Go, Rust, Java/Kotlin, Ruby. Generic fallback for unknown extensions.
59
+
60
+ ## How It Works
61
+
62
+ Wraps `gh` CLI with GraphQL batching. `repos` and `explore` batch search + metadata + README into 1 call. `read` uses GraphQL aliases to fetch up to 10 files in 1 call. `search` hits REST `/search/code` with `text_matches` for matching context and 200-char token protection.
63
+
64
+ ## Agent Skill
65
+
66
+ ```bash
67
+ ghx skill # outputs SKILL.md to stdout
68
+ ```
69
+
70
+ See [`SKILL.md`](./SKILL.md) for agent-optimized instructions.
71
+
72
+ ## License
73
+
74
+ MIT
@@ -22,12 +22,17 @@ ghx read <owner/repo> <f1> [f2] [f3] # Read 1-10 files in 1 API call (Grap
22
22
  ghx read <owner/repo> --map <f1> [f2] # Structural map: signatures, imports, types (~92% token reduction)
23
23
  ghx read <owner/repo> --grep "pat" <f> # Read file, show only matching lines (2 lines context)
24
24
  ghx read <owner/repo> --lines 42-80 <f> # Read specific line range
25
- ghx repos "<query>" # Search repos with README preview in 1 GraphQL call
26
- ghx search "<query>" # Code search (REST API, AND matching, shows matching lines)
25
+ ghx repos "<query>" # Search repos with README preview (default: 10 results)
26
+ ghx repos "<query>" --limit 5 # Limit repo results (max: 20)
27
+ ghx search "<query>" # Code search (AND matching, default: 30 results)
28
+ ghx search "<query>" --limit 10 # Limit code search results (max: 100)
27
29
  ghx search --full "<query>" # Code search without line truncation (for minified files)
28
30
  ghx tree <owner/repo> [path] # Full recursive tree listing
29
31
  ```
30
32
 
33
+ **Exit codes:** 0 = results returned, 1 = no results (query valid), 2 = usage error (bad flags/args).
34
+ **Flag safety:** Unknown flags always error (exit 2). Never silently absorbed into queries.
35
+
31
36
  ## Chain of Thought: Progressive Disclosure
32
37
 
33
38
  **Always start surgical, escalate only when needed.** This mirrors how developers work: scan structure → identify interesting files → read specific sections.
@@ -157,6 +162,8 @@ AND matching is almost always what agents want. `gh search code "useState fetchD
157
162
 
158
163
  10. **`gh search repos` and `gh search code` use different rate limit pools.** Repo search: 30/min (generous). Code search: 10/min (restrictive). Don't assume one rate limit applies to both.
159
164
 
165
+ 11. **Unknown flags are rejected, not silently absorbed.** `ghx search "query" --json` exits 2 with a clear error. This is intentional — silent flag absorption was the #1 cause of agent failures (flags like `--limit` would get concatenated into the query string, corrupting it). If you get exit 2, check your flags.
166
+
160
167
  ## Anti-Patterns
161
168
 
162
169
  - ❌ `web_fetch`/`web_search` on github.com — returns HTML noise, wastes thousands of tokens for zero useful information
@@ -176,6 +183,8 @@ AND matching is almost always what agents want. `gh search code "useState fetchD
176
183
  - **Batch file reads.** `ghx read owner/repo f1 f2 f3` = 1 API call. Three separate reads = 3 calls.
177
184
  - **Map before reading.** `ghx read --map` first to understand structure, then `--grep` or `--lines` for specifics.
178
185
  - **Refine search, don't paginate.** If `ghx search` shows "201472 results (showing 30)", add qualifiers (`repo:`, `language:`, `path:`) — don't try to page through. 9 req/min rate limit makes pagination expensive.
186
+ - **Use `--limit` to control token budget.** `ghx repos "query" --limit 5` for quick checks, `--limit 15` for thorough discovery. `ghx search "query" --limit 10` when you only need top results.
187
+ - **Check exit codes.** 0 = got results, 1 = no results (query was valid, broaden it), 2 = usage error (fix your command).
179
188
  - **Use `gh api --cache 1h`** for repeated lookups when using raw `gh` commands.
180
189
  - **Use `--json fields --jq 'expr'`** on `gh` commands to get structured output and reduce noise.
181
190
  - **Piped output is machine-formatted.** Tab-delimited, no truncation, no color codes — agents always get clean output.
package/{ghx → v1/ghx} RENAMED
@@ -5,6 +5,8 @@
5
5
 
6
6
  set -euo pipefail
7
7
 
8
+ VERSION="0.2.1"
9
+
8
10
  cmd="${1:-help}"
9
11
  shift || true
10
12
 
@@ -73,13 +75,14 @@ read)
73
75
  --grep) grep_pattern="$2"; shift 2 ;;
74
76
  --lines) line_range="$2"; shift 2 ;;
75
77
  --map) map_mode=1; shift ;;
78
+ --*) echo "ghx read: unknown flag '$1'" >&2; exit 2 ;;
76
79
  *) files+=("$1"); shift ;;
77
80
  esac
78
81
  done
79
82
 
80
83
  if [[ ${#files[@]} -eq 0 ]]; then
81
84
  echo "Usage: ghx read <owner/repo> <path1> [path2] [--grep pattern] [--lines N-M]" >&2
82
- exit 1
85
+ exit 2
83
86
  fi
84
87
 
85
88
  # Get default branch
@@ -136,28 +139,37 @@ read)
136
139
  search)
137
140
  # Parse flags
138
141
  full_mode=""
142
+ limit=""
139
143
  args=()
140
- for arg in "$@"; do
141
- case "$arg" in
142
- --full) full_mode=1 ;;
143
- *) args+=("$arg") ;;
144
+ while [[ $# -gt 0 ]]; do
145
+ case "$1" in
146
+ --full) full_mode=1; shift ;;
147
+ --limit) [[ $# -lt 2 ]] && { echo "ghx search: --limit requires a value" >&2; exit 2; }
148
+ limit="$2"; shift 2 ;;
149
+ --*) echo "ghx search: unknown flag '$1'" >&2; exit 2 ;;
150
+ *) args+=("$1"); shift ;;
144
151
  esac
145
152
  done
146
153
  query="${args[*]}"
147
154
  if [[ -z "$query" ]]; then
148
- echo "Usage: ghx search <query> [--full]" >&2
155
+ echo "Usage: ghx search <query> [--full] [--limit N]" >&2
149
156
  echo "Examples: 'useState repo:owner/repo' | 'path:src/ extension:tsx language:typescript'" >&2
150
- exit 1
157
+ exit 2
158
+ fi
159
+ # Validate and clamp limit (API max: 100)
160
+ if [[ -n "$limit" ]]; then
161
+ [[ "$limit" =~ ^[0-9]+$ ]] || { echo "ghx search: --limit must be a number, got '$limit'" >&2; exit 2; }
162
+ (( limit > 100 )) && { echo "⚠ --limit clamped to 100 (API max)" >&2; limit=100; }
151
163
  fi
152
164
 
153
165
  # Prerequisite checks
154
166
  if ! command -v gh &>/dev/null; then
155
167
  echo "ghx requires the GitHub CLI (gh). Install: https://cli.github.com" >&2
156
- exit 1
168
+ exit 2
157
169
  fi
158
170
  if ! gh auth status &>/dev/null; then
159
171
  echo "GitHub code search requires authentication. Run: gh auth login" >&2
160
- exit 1
172
+ exit 2
161
173
  fi
162
174
 
163
175
  # Warn on web-only qualifiers (they silently become literal text in REST API)
@@ -168,9 +180,10 @@ search)
168
180
  done
169
181
 
170
182
  # Search with text_matches for matching context
183
+ per_page="${limit:-30}"
171
184
  response=$(gh api /search/code --method GET \
172
185
  -H "Accept: application/vnd.github.text-match+json" \
173
- -f q="$query" 2>&1) || {
186
+ -f q="$query" -f per_page="$per_page" 2>&1) || {
174
187
  echo "$response" >&2
175
188
  exit 1
176
189
  }
@@ -182,6 +195,7 @@ search)
182
195
  echo "$total results (showing $count)" >&2
183
196
  [[ "$incomplete" == "true" ]] && echo "⚠ Results may be incomplete (query timed out)" >&2
184
197
  [[ "$total" -gt 1000 ]] 2>/dev/null && echo "⚠ Query too broad — add repo:, language:, or path: to narrow" >&2
198
+ [[ "$count" -eq 0 ]] && exit 1
185
199
 
186
200
  # Output: repo path: matching context from fragment
187
201
  # Default: 200 char window centered on the match (prevents minified JS token explosion)
@@ -233,15 +247,29 @@ search)
233
247
 
234
248
  # ─── repos: search repos with README preview in 1 GraphQL call ───
235
249
  repos)
236
- query="$*"
250
+ limit=""
251
+ args=()
252
+ while [[ $# -gt 0 ]]; do
253
+ case "$1" in
254
+ --limit) [[ $# -lt 2 ]] && { echo "ghx repos: --limit requires a value" >&2; exit 2; }
255
+ limit="$2"; shift 2 ;;
256
+ --*) echo "ghx repos: unknown flag '$1'" >&2; exit 2 ;;
257
+ *) args+=("$1"); shift ;;
258
+ esac
259
+ done
260
+ query="${args[*]}"
237
261
  if [[ -z "$query" ]]; then
238
- echo "Usage: ghx repos <query>" >&2
239
- exit 1
262
+ echo "Usage: ghx repos <query> [--limit N]" >&2
263
+ exit 2
240
264
  fi
265
+ # Validate and clamp limit (GraphQL max: 100, but README fetching makes >20 expensive)
266
+ first="${limit:-10}"
267
+ [[ "$first" =~ ^[0-9]+$ ]] || { echo "ghx repos: --limit must be a number, got '$first'" >&2; exit 2; }
268
+ (( first > 20 )) && { echo "⚠ --limit clamped to 20 (README fetching makes larger values slow)" >&2; first=20; }
241
269
 
242
270
  response=$(gh api graphql -f query='
243
271
  {
244
- search(query: "'"$query"'", type: REPOSITORY, first: 5) {
272
+ search(query: "'"$query"'", type: REPOSITORY, first: '"$first"') {
245
273
  repositoryCount
246
274
  nodes {
247
275
  ... on Repository {
@@ -258,6 +286,8 @@ repos)
258
286
  }')
259
287
 
260
288
  echo "$response" | jq -r '.data.search.repositoryCount | "\(.) repos found"' >&2
289
+ count=$(echo "$response" | jq '.data.search.nodes | length')
290
+ [[ "$count" -eq 0 ]] && exit 1
261
291
  echo "$response" | jq -r '
262
292
  .data.search.nodes[] |
263
293
  .nameWithOwner as $name |
@@ -313,6 +343,10 @@ tree)
313
343
  ;;
314
344
 
315
345
  # ─── help ───
346
+ version|-v|--version)
347
+ echo "ghx $VERSION"
348
+ ;;
349
+
316
350
  help|*)
317
351
  cat <<'EOF'
318
352
  ghx — GitHub code exploration for agents and humans
@@ -320,8 +354,8 @@ ghx — GitHub code exploration for agents and humans
320
354
  Commands:
321
355
  ghx explore <owner/repo> [path] Branch + tree + README in 1 API call
322
356
  ghx read <owner/repo> <f1> [f2..] Read 1-10 files in 1 API call
323
- ghx repos <query> Search repos with README preview in 1 GraphQL call
324
- ghx search "<query>" Code search (AND matching, qualifiers: repo: path: language: extension: filename:)
357
+ ghx repos <query> [--limit N] Search repos with README preview (default: 10)
358
+ ghx search "<query>" [--limit N] Code search (AND matching, default: 30)
325
359
  ghx search --full "<query>" Code search without line truncation
326
360
  ghx skill Output SKILL.md (for agent context injection)
327
361
  ghx tree <owner/repo> [path] Full recursive tree listing
@@ -329,6 +363,12 @@ Commands:
329
363
  Read flags:
330
364
  --grep <pattern> Filter output to matching lines (case-insensitive, 2 lines context)
331
365
  --lines <N-M> Extract specific line range
366
+ --map Structural signatures only (~92% token reduction)
367
+
368
+ Exit codes:
369
+ 0 Success with results
370
+ 1 No results (query valid, nothing matched)
371
+ 2 Usage error (bad flags, missing args)
332
372
 
333
373
  Examples:
334
374
  ghx explore plausible/analytics