@gkoreli/ghx 2.0.2 → 2.1.5

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
@@ -17,18 +17,39 @@ ghx eliminates this by encoding the right defaults into every command. One call
17
17
  ## Install
18
18
 
19
19
  ```bash
20
- # Build from source
21
- cd v2 && go build -o ghx .
20
+ # Zero install — just run it
21
+ npx @gkoreli/ghx explore vercel/next.js
22
22
 
23
23
  # Homebrew
24
24
  brew install gkoreli/tap/ghx
25
25
 
26
- # One-liner
27
- curl -sfL https://raw.githubusercontent.com/gkoreli/ghx/mainline/install.sh | bash
26
+ # npm (global)
27
+ npm install -g @gkoreli/ghx
28
+
29
+ # Go
30
+ go install github.com/gkoreli/ghx/v2@latest
31
+
32
+ # Build from source
33
+ cd v2 && go build -o ghx .
28
34
  ```
29
35
 
30
36
  Requires: [gh CLI](https://cli.github.com/) authenticated (`gh auth login`).
31
37
 
38
+ ### MCP Config (Claude Desktop, Cursor)
39
+
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "ghx": {
44
+ "command": "npx",
45
+ "args": ["@gkoreli/ghx", "serve"]
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ No install step — npx downloads and caches the binary on first run.
52
+
32
53
  ## Commands
33
54
 
34
55
  ```bash
package/npm/bin/ghx ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ const { platform, arch } = process;
3
+
4
+ const PLATFORMS = {
5
+ darwin: { arm64: "@gkoreli/ghx-darwin-arm64/ghx", x64: "@gkoreli/ghx-darwin-x64/ghx" },
6
+ linux: { arm64: "@gkoreli/ghx-linux-arm64/ghx", x64: "@gkoreli/ghx-linux-x64/ghx" },
7
+ win32: { arm64: "@gkoreli/ghx-win32-arm64/ghx.exe", x64: "@gkoreli/ghx-win32-x64/ghx.exe" },
8
+ };
9
+
10
+ const bin = PLATFORMS[platform]?.[arch];
11
+ if (!bin) {
12
+ console.error(`ghx: unsupported platform ${platform}/${arch}`);
13
+ process.exitCode = 1;
14
+ } else {
15
+ const result = require("child_process").spawnSync(
16
+ require.resolve(bin),
17
+ process.argv.slice(2),
18
+ { shell: false, stdio: "inherit" }
19
+ );
20
+ if (result.error) throw result.error;
21
+ process.exitCode = result.status;
22
+ }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@gkoreli/ghx",
3
- "version": "2.0.2",
3
+ "version": "2.1.5",
4
4
  "description": "Agent-first GitHub code exploration. GraphQL batching, code maps, codemode TypeScript sandbox, MCP server.",
5
5
  "bin": {
6
- "ghx": "./ghx"
6
+ "ghx": "npm/bin/ghx"
7
7
  },
8
8
  "keywords": [
9
9
  "github",
@@ -16,25 +16,22 @@
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
19
- "url": "https://github.com/gkoreli/ghx"
19
+ "url": "git+https://github.com/gkoreli/ghx.git"
20
20
  },
21
21
  "files": [
22
- "ghx",
23
- "postinstall.js",
24
- "v2/SKILL.md",
25
- "v2/MCP-SKILL.md",
22
+ "npm/bin/ghx",
26
23
  "README.md",
27
24
  "LICENSE"
28
25
  ],
29
- "os": [
30
- "darwin",
31
- "linux",
32
- "win32"
33
- ],
34
26
  "engines": {
35
27
  "node": ">=16"
36
28
  },
37
- "scripts": {
38
- "postinstall": "node postinstall.js"
29
+ "optionalDependencies": {
30
+ "@gkoreli/ghx-darwin-arm64": "2.1.5",
31
+ "@gkoreli/ghx-darwin-x64": "2.1.5",
32
+ "@gkoreli/ghx-linux-arm64": "2.1.5",
33
+ "@gkoreli/ghx-linux-x64": "2.1.5",
34
+ "@gkoreli/ghx-win32-arm64": "2.1.5",
35
+ "@gkoreli/ghx-win32-x64": "2.1.5"
39
36
  }
40
37
  }
package/postinstall.js DELETED
@@ -1,59 +0,0 @@
1
- #!/usr/bin/env node
2
- // Downloads the ghx Go binary from GitHub releases on npm install
3
- const { execSync } = require("child_process");
4
- const fs = require("fs");
5
- const path = require("path");
6
- const https = require("https");
7
-
8
- const REPO = "gkoreli/ghx";
9
- const BIN = path.join(__dirname, "ghx");
10
-
11
- const PLATFORM_MAP = { darwin: "darwin", linux: "linux", win32: "windows" };
12
- const ARCH_MAP = { x64: "amd64", arm64: "arm64" };
13
-
14
- const os = PLATFORM_MAP[process.platform];
15
- const arch = ARCH_MAP[process.arch];
16
- if (!os || !arch) {
17
- console.error(`Unsupported platform: ${process.platform}/${process.arch}`);
18
- process.exit(1);
19
- }
20
-
21
- const ext = os === "windows" ? "zip" : "tar.gz";
22
- const url = `https://github.com/${REPO}/releases/latest/download/ghx_${os}_${arch}.${ext}`;
23
-
24
- function download(url, dest) {
25
- return new Promise((resolve, reject) => {
26
- https.get(url, (res) => {
27
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
28
- return download(res.headers.location, dest).then(resolve, reject);
29
- }
30
- if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
31
- const file = fs.createWriteStream(dest);
32
- res.pipe(file);
33
- file.on("finish", () => file.close(resolve));
34
- }).on("error", reject);
35
- });
36
- }
37
-
38
- async function main() {
39
- const tmp = path.join(__dirname, `ghx-download.${ext}`);
40
- try {
41
- console.log(`Downloading ghx (${os}/${arch})...`);
42
- await download(url, tmp);
43
- if (ext === "tar.gz") {
44
- execSync(`tar xzf "${tmp}" -C "${__dirname}" ghx`, { stdio: "pipe" });
45
- } else {
46
- execSync(`unzip -o "${tmp}" ghx.exe -d "${__dirname}"`, { stdio: "pipe" });
47
- }
48
- fs.chmodSync(BIN, 0o755);
49
- console.log("ghx installed successfully");
50
- } finally {
51
- try { fs.unlinkSync(tmp); } catch {}
52
- }
53
- }
54
-
55
- main().catch((e) => {
56
- console.error(`Failed to install ghx: ${e.message}`);
57
- console.error("Install manually: https://github.com/gkoreli/ghx#install");
58
- process.exit(1);
59
- });
package/v2/MCP-SKILL.md DELETED
@@ -1,175 +0,0 @@
1
- ---
2
- name: ghx-mcp
3
- description: GitHub code exploration via MCP. 7 tools — 5 direct + code meta-tool + search_tools. The code tool lets you write JS programs that compose operations in one round-trip.
4
- ---
5
-
6
- # ghx MCP — GitHub Code Exploration via MCP
7
-
8
- 7 tools for GitHub exploration. 5 direct tools for simple queries. 1 `code` meta-tool for complex multi-step operations. 1 `search_tools` for discovery.
9
-
10
- ## Tools
11
-
12
- ### Direct Tools (simple one-shot queries)
13
-
14
- | Tool | Input | What it does |
15
- |------|-------|-------------|
16
- | `explore` | `repo` (required), `path` | Branch, file tree, README in 1 API call |
17
- | `read` | `repo` + `paths` (required), `grep`, `lines`, `map` | Read 1-10 files in 1 API call. `map` = signatures only (~92% reduction) |
18
- | `search` | `query` (required), `limit`, `full` | Code search with AND matching + matching lines |
19
- | `repos` | `query` (required), `limit` | Search repos with README preview |
20
- | `tree` | `repo` (required), `path` | Full recursive file tree |
21
-
22
- ### Meta-Tools (compose operations)
23
-
24
- | Tool | Input | What it does |
25
- |------|-------|-------------|
26
- | `code` | `code` (required) | Execute JS that calls any combination of the 5 tools above |
27
- | `search_tools` | `query` (optional) | List available tools with TypeScript type stubs |
28
-
29
- ## When to Use `code` vs Direct Tools
30
-
31
- **Direct tools** — simple, single-operation queries:
32
- ```
33
- explore({ repo: "vercel/next.js" }) → one call, one result
34
- read({ repo: "vercel/next.js", paths: "README.md" }) → one call, one result
35
- ```
36
-
37
- **`code` tool** — multi-step, filtering, conditional logic:
38
- ```javascript
39
- // Explore → filter → read in ONE round-trip (not three)
40
- var repo = codemode.explore({ repo: "vercel/next.js" });
41
- var tsFiles = repo.files.filter(f => f.name.endsWith(".ts")).slice(0, 5);
42
- var contents = codemode.read({ repo: "vercel/next.js", files: tsFiles.map(f => f.name), map: true });
43
- return contents;
44
- ```
45
-
46
- **Rule of thumb:** If you need the result of tool A to decide what to call for tool B → use `code`. Otherwise use direct tools.
47
-
48
- ## The `code` Tool
49
-
50
- ### Available API
51
-
52
- ```typescript
53
- type ExploreInput = { repo: string; path?: string }
54
- type ReadInput = { repo: string; files: string[]; grep?: string; map?: boolean }
55
- type ReposInput = { query: string; limit?: number }
56
- type SearchInput = { query: string; limit?: number; fullMode?: boolean }
57
- type TreeInput = { repo: string; path?: string }
58
-
59
- declare const codemode: {
60
- explore: (input: ExploreInput) => any;
61
- read: (input: ReadInput) => any;
62
- repos: (input: ReposInput) => any;
63
- search: (input: SearchInput) => any;
64
- tree: (input: TreeInput) => any;
65
- }
66
- ```
67
-
68
- Use `search_tools` to get the latest type stubs at runtime.
69
-
70
- ### Writing Code
71
-
72
- ```javascript
73
- // ✅ Correct: plain JavaScript, synchronous calls, return a value
74
- var repo = codemode.explore({ repo: "dop251/goja" });
75
- return { branch: repo.branch, fileCount: repo.files.length };
76
-
77
- // ❌ Wrong: await (tools are synchronous, await causes transpile error)
78
- const r = await codemode.explore(...) // top-level await not supported
79
-
80
- // ❌ Wrong: TypeScript syntax
81
- const r: ExploreResult = codemode.explore(...) // no type annotations
82
-
83
- // ❌ Wrong: no return value
84
- codemode.explore({ repo: "dop251/goja" }); // result is lost
85
- ```
86
-
87
- ### Rules
88
-
89
- - Write plain JavaScript, not TypeScript (no type annotations, interfaces, generics)
90
- - `codemode.*` calls are synchronous — do NOT use `await`
91
- - Must `return` a value — the return value is what you see in the response
92
- - `console.log()` output appears in the response under "Console:" (useful for debugging)
93
- - Max 20 tool calls per execution
94
- - Max 64KB code size
95
- - Response truncated at 24K chars (use `map: true` or `grep` to reduce output)
96
-
97
- ### Patterns
98
-
99
- **Explore and filter:**
100
- ```javascript
101
- var repo = codemode.explore({ repo: "owner/repo" });
102
- var goFiles = repo.files.filter(f => f.name.endsWith(".go"));
103
- return goFiles.map(f => f.name);
104
- ```
105
-
106
- **Map multiple files (understand structure before reading):**
107
- ```javascript
108
- var repo = codemode.explore({ repo: "owner/repo" });
109
- var srcFiles = repo.files.filter(f => f.name.startsWith("src/") && f.type === "blob").slice(0, 8);
110
- return codemode.read({ repo: "owner/repo", files: srcFiles.map(f => f.name), map: true });
111
- ```
112
-
113
- **Search then read matches:**
114
- ```javascript
115
- var hits = codemode.search({ query: "GraphQL repo:owner/repo", limit: 5 });
116
- var files = hits.results.map(r => r.path);
117
- return codemode.read({ repo: "owner/repo", files: files });
118
- ```
119
-
120
- **Conditional logic:**
121
- ```javascript
122
- var repo = codemode.explore({ repo: "owner/repo" });
123
- var hasGo = repo.files.some(f => f.name === "go.mod");
124
- var hasPython = repo.files.some(f => f.name === "requirements.txt");
125
- if (hasGo) {
126
- return { lang: "go", mod: codemode.read({ repo: "owner/repo", files: ["go.mod"] }) };
127
- } else if (hasPython) {
128
- return { lang: "python", reqs: codemode.read({ repo: "owner/repo", files: ["requirements.txt"] }) };
129
- }
130
- return { lang: "unknown", files: repo.files.slice(0, 10) };
131
- ```
132
-
133
- ## Chain of Thought
134
-
135
- **Always start surgical, escalate only when needed.**
136
-
137
- ```
138
- 1. explore({ repo: "owner/repo" }) → What's in this repo?
139
- 2. read({ repo: "...", paths: "f1,f2", map: true }) → What do these files define? (92% fewer tokens)
140
- 3. read({ repo: "...", paths: "f1", grep: "pattern" }) → Where exactly is X?
141
- 4. read({ repo: "...", paths: "f1" }) → Full file (only when needed)
142
- ```
143
-
144
- **When to escalate to `code`:**
145
- - Step 1 result determines what to do in step 2 → use `code` (one round-trip)
146
- - Need to filter/transform results before returning → use `code`
147
- - Simple single query → use direct tool
148
-
149
- ## Search Query Syntax
150
-
151
- Same as GitHub REST code search API. Multi-word = AND matching.
152
-
153
- **Valid qualifiers:** `repo:`, `org:`, `path:`, `filename:`, `extension:`, `language:`, `in:file`, `in:path`
154
-
155
- **DO NOT USE (web-only, silently wrong):** `OR`, `NOT`, `symbol:`, `content:`, `is:`, regex
156
-
157
- **Rate limit:** 9 req/min for code search. Refine queries, don't paginate.
158
-
159
- ## Gotchas
160
-
161
- 1. **`read` paths are comma-separated.** `paths: "f1.go,f2.go"` not `paths: ["f1.go", "f2.go"]`.
162
- 2. **Response truncation at 24K chars.** Use `map: true` or `grep` to keep results small. The `code` tool is especially prone to this when reading multiple full files.
163
- 3. **`search` uses AND matching.** `"foo bar"` finds files with both words anywhere. For exact phrase, wrap in escaped quotes: `"\"foo bar\""`.
164
- 4. **Web-only qualifiers silently degrade.** `symbol:`, `OR`, `NOT` are treated as literal text in the REST API.
165
- 5. **`code` tool: no TypeScript.** Type stubs are for your reference. Write plain JS.
166
- 6. **`code` tool: return is required.** Bare expressions don't auto-return (except simple identifiers). Always use `return`.
167
-
168
- ## Anti-Patterns
169
-
170
- - ❌ Three sequential tool calls when `code` can do it in one
171
- - ❌ Reading full files when you need 10 lines — use `grep` or `map: true`
172
- - ❌ Paginating broad searches — refine with `repo:`, `language:`, `path:`
173
- - ❌ Writing TypeScript in the `code` tool — stripped by transpiler but may cause subtle issues
174
- - ❌ Forgetting `return` in `code` — you get `undefined` back
175
- - ❌ Reading 10 full files in `code` — hits 24K truncation. Use `map: true` first
package/v2/SKILL.md DELETED
@@ -1,129 +0,0 @@
1
- ---
2
- name: ghx
3
- description: GitHub code exploration for AI agents. CLI + codemode. One command does what takes 3-5 API calls. Write JS programs that compose operations in one round-trip.
4
- ---
5
-
6
- # ghx — GitHub Code Exploration for AI Agents
7
-
8
- Use `ghx` via `execute_bash` for anything on GitHub — repos, files, code search, codemode. Authenticated via `gh` CLI, structured output, zero context overhead.
9
-
10
- ## Commands
11
-
12
- ```bash
13
- ghx explore <owner/repo> # Branch + tree + README in 1 API call
14
- ghx explore <owner/repo> <path> # Subdirectory listing
15
- ghx read <owner/repo> <f1> [f2] [f3] # Read 1-10 files in 1 API call (GraphQL batching)
16
- ghx read <owner/repo> --map <f1> [f2] # Structural map: signatures, imports, types (~92% token reduction)
17
- ghx read <owner/repo> --grep "pat" <f> # Read file, show only matching lines (2 lines context)
18
- ghx read <owner/repo> --lines 42-80 <f> # Read specific line range
19
- ghx repos "<query>" # Search repos with README preview in 1 GraphQL call
20
- ghx search "<query>" # Code search (AND matching, shows matching lines)
21
- ghx search --full "<query>" # Code search without line truncation
22
- ghx tree <owner/repo> [path] # Full recursive tree listing
23
- ghx code "<js>" # Execute JS with access to all ghx tools
24
- ghx code - # Read code from stdin
25
- ghx code --list # List available tools with type stubs
26
- ```
27
-
28
- **Exit codes:** 0 = success, 1 = no results, 2 = usage error.
29
-
30
- ## Chain of Thought: Progressive Disclosure
31
-
32
- **Start surgical, escalate only when needed.**
33
-
34
- ```
35
- 1. ghx explore owner/repo → What's in this repo? (structure + README)
36
- 2. ghx read owner/repo --map *.ts → What do these files define? (signatures only, 92% fewer tokens)
37
- 3. ghx read owner/repo --grep "X" f → Where exactly is X in this file? (targeted lines)
38
- 4. ghx read owner/repo f → Show me the full file (only when needed)
39
- ```
40
-
41
- At 92% reduction, `--map` lets you scan 7 files in the space of reading 1 full file.
42
-
43
- ## When to Use `ghx code`
44
-
45
- Use direct commands for simple one-shot queries. Use `ghx code` when you need to:
46
- - **Chain operations** — explore → filter → read in one shot
47
- - **Filter results** — JS logic runs locally, not in the LLM
48
- - **Conditional logic** — read different files based on what you find
49
-
50
- ```bash
51
- # Simple: use direct command
52
- ghx explore vercel/next.js
53
-
54
- # Complex: use ghx code (one round-trip instead of three)
55
- ghx code "
56
- var repo = codemode.explore({ repo: 'vercel/next.js' });
57
- var goFiles = repo.files.filter(f => f.name.endsWith('.go'));
58
- var contents = codemode.read({ repo: 'vercel/next.js', files: goFiles.map(f => f.name) });
59
- return contents;
60
- "
61
- ```
62
-
63
- ### Codemode API
64
-
65
- ```bash
66
- ghx code --list # See all available tools with type stubs
67
- ```
68
-
69
- ```typescript
70
- declare const codemode: {
71
- explore: (input: { repo: string; path?: string }) => any;
72
- read: (input: { repo: string; files: string[]; grep?: string; map?: boolean }) => any;
73
- repos: (input: { query: string; limit?: number }) => any;
74
- search: (input: { query: string; limit?: number; fullMode?: boolean }) => any;
75
- tree: (input: { repo: string; path?: string }) => any;
76
- }
77
- ```
78
-
79
- ### Codemode Rules
80
-
81
- - Write plain JavaScript, not TypeScript (no type annotations)
82
- - `codemode.*` calls are synchronous — no `await` needed
83
- - Must `return` a value — bare expressions don't auto-return (except simple identifiers)
84
- - Must `return` a value — bare expressions don't auto-return (except simple identifiers)
85
- - Console output goes to stderr, return value goes to stdout
86
- - Max 20 tool calls per execution, 64KB code size limit
87
-
88
- ## Search Query Syntax
89
-
90
- `ghx search` uses GitHub REST code search API. Multi-word = AND matching (both words anywhere in file).
91
-
92
- ```bash
93
- ghx search "addClass repo:jquery/jquery" # Scoped to repo
94
- ghx search "useState language:typescript" # Language filter
95
- ghx search "filename:package.json repo:owner/repo" # Find specific filename
96
- ghx search '"exact phrase" repo:plausible/analytics' # Exact phrase (shell quotes)
97
- ghx search "path:llms.txt" # Find files by name
98
- ```
99
-
100
- **Valid qualifiers:** `repo:`, `org:`, `path:`, `filename:`, `extension:`, `language:`, `in:file`, `in:path`, `size:`, `fork:true`
101
-
102
- **DO NOT USE (web-only, silently wrong):** `OR`, `NOT`, `symbol:`, `content:`, `is:`, regex
103
-
104
- **Rate limit:** 9 req/min for code search. Refine queries, don't paginate.
105
-
106
- ## Gotchas
107
-
108
- 1. **Web-only qualifiers silently degrade.** `symbol:`, `OR`, `NOT` are treated as literal text. ghx warns on stderr.
109
- 2. **`gh search code` wraps in quotes.** `gh search code "foo bar"` = exact phrase. `ghx search "foo bar"` = AND. Use ghx.
110
- 3. **Flag ordering in `read`.** `ghx read owner/repo file --map` works. `ghx read --map owner/repo file` does NOT.
111
- 4. **Not all repos use `main`.** ghx handles this automatically.
112
- 5. **Unknown flags are rejected.** Exit 2 with clear error. Intentional — prevents silent query corruption.
113
-
114
- ## Anti-Patterns
115
-
116
- - ❌ `web_fetch` on github.com — HTML noise, zero useful info
117
- - ❌ Reading entire large files when you need 10 lines — use `--grep` or `--lines`
118
- - ❌ Multiple `gh api` calls for explore — use `ghx explore` (1 call)
119
- - ❌ Using web-only qualifiers in search — silently wrong results
120
- - ❌ Paginating broad searches — refine with `repo:`, `language:`, `path:` instead
121
- - ❌ `gh search code` for multi-word queries — silently wraps in quotes, returns nothing
122
-
123
- ## Best Practices
124
-
125
- - **Batch reads.** `ghx read repo f1 f2 f3` = 1 API call. Three separate reads = 3 calls.
126
- - **Map before reading.** `--map` first, then `--grep` or `--lines` for specifics.
127
- - **Refine search, don't paginate.** Add qualifiers instead of fetching page 2.
128
- - **Use `ghx code` for multi-step workflows.** One round-trip beats three sequential commands.
129
- - **Check exit codes.** 0 = results, 1 = no results (broaden query), 2 = usage error (fix command).