Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ src/
completions.ts # Pure shell-completion generators: generateCompletion(),
# detectShell(), getCompletionFilePath() — no I/O
group.ts # groupByTeamPrefix — team-prefix grouping logic
regex.ts # Pure query parser: isRegexQuery(), buildApiQuery()
# Detects /pattern/ syntax, derives safe API term,
# returns RegExp for local client-side filtering — no I/O
render.ts # Façade re-exporting sub-modules + top-level
# renderGroups() / renderHelpOverlay()
tui.ts # Interactive keyboard-driven UI (navigation, filter mode,
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ github-code-search query "useFeatureFlag" --org my-org --group-by-team-prefix pl

Get a team-scoped view of every usage site before refactoring a shared hook or utility.

**Regex search — pattern-based code audit**

```bash
github-code-search query "/from.*['\"\`]axios/" --org my-org
```

Use `/pattern/` syntax to run a regex search. The CLI automatically derives a safe API query term and filters results locally — no manual post-processing needed. Use `--regex-hint` to override the derived term when auto-extraction is too broad.

## Why not `gh search code`?

The official [`gh` CLI](https://cli.github.com/) does support `gh search code`, but it returns a **flat paginated list** — one result per line, no grouping, no interactive selection, no structured output.
Expand Down
94 changes: 48 additions & 46 deletions bun.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions docs/.vitepress/theme/ComparisonTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ const ROWS: Row[] = [
gcs: true,
docLink: "/usage/interactive-mode",
},
{
feature: "Regex queries (/pattern/flags)",
desc: "Use full regular expressions in queries — top-level alternation (A|B|C) maps to GitHub OR, client-side filtering applies the real pattern. GitHub supports regex in the web UI only, not in the REST API or gh CLI.",
gh: false,
gcs: true,
docLink: "/usage/search-syntax",
},
{
feature: "Pagination (up to 1\u202f000 results)",
desc: "Both tools auto-paginate the GitHub search API \u2014 up to 1\u202f000 results per query.",
Expand Down
8 changes: 8 additions & 0 deletions docs/.vitepress/theme/UseCaseTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ const USE_CASES: UseCase[] = [
"Get a team-scoped view of every usage site before refactoring a shared hook or utility. Essential for onboarding or large-scale refactors.",
command: `github-code-search query "useFeatureFlag" --org my-org --group-by-team-prefix platform/`,
},
{
id: "semver",
label: "Semver / version audit",
headline: "Which repos are pinned to a vulnerable minor version?",
description:
"Use regex syntax to target a precise version range — something a plain keyword search cannot do. Find every repo still locked to axios 1.x, react 17.x, or any other outdated pin, then export the list to a migration issue.",
command: `github-code-search query '/"axios": "1\\./' --org my-org`,
},
];

const active = ref(0);
Expand Down
23 changes: 7 additions & 16 deletions docs/architecture/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@ The three pure functions called by the CLI parser to transform raw API results
into a filtered, grouped, formatted output.

```mermaid
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Poppins, Aestetico, Arial, sans-serif", "primaryColor": "#66CCFF", "primaryTextColor": "#000000", "lineColor": "#0000CC", "tertiaryColor": "#FFCC33"}, "themeCSS": ".label,.nodeLabel,.cluster-label > span{font-family:Poppins,Arial,sans-serif;letter-spacing:.2px} .cluster-label > span{font-weight:600;font-size:13px} .edgePath .path{stroke-width:2px}"}}%%
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Poppins, Aestetico, Arial, sans-serif", "primaryColor": "#9933FF", "primaryTextColor": "#ffffff", "lineColor": "#0000CC", "tertiaryColor": "#FFCC33"}, "themeCSS": ".label,.nodeLabel,.cluster-label > span{font-family:Poppins,Arial,sans-serif;letter-spacing:.2px} .cluster-label > span{font-weight:600;font-size:13px} .edgePath .path{stroke-width:2px}"}}%%
C4Component
title Level 3a: CLI data pipeline

UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="1")
UpdateLayoutConfig($c4ShapeInRow="5", $c4BoundaryInRow="1")

Container(cli, "CLI parser", "github-code-search.ts", "Orchestrates filter,<br/>group, output and<br/>shell completions")

Container_Boundary(core, "Pure-function core — no I/O") {
Component(regexParser, "Query parser", "src/regex.ts", "isRegexQuery()<br/>buildApiQuery()")
Component(aggregate, "Filter & aggregation", "src/aggregate.ts", "aggregate()<br/>exclude repos & extracts")
Component(group, "Team grouping", "src/group.ts", "groupByTeamPrefix()<br/>flattenTeamSections()")
Component(outputFn, "Output formatter", "src/output.ts", "buildOutput()<br/>markdown or JSON")
Component(completions, "Shell completions", "src/completions.ts", "generateCompletion()<br/>detectShell()<br/>getCompletionFilePath()")
}

Rel(cli, regexParser, "Parse regex<br/>query")
UpdateRelStyle(cli, regexParser, $offsetX="35", $offsetY="-17")

Rel(cli, aggregate, "Filter<br/>CodeMatch[]")
UpdateRelStyle(cli, aggregate, $offsetX="0", $offsetY="-17")

Expand All @@ -37,11 +41,6 @@ C4Component
Rel(cli, completions, "Generate<br/>script")
UpdateRelStyle(cli, completions, $offsetX="-90", $offsetY="-17")

UpdateElementStyle(cli, $bgColor="#FFCC33", $borderColor="#0000CC", $fontColor="#000000")
UpdateElementStyle(aggregate, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(group, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(outputFn, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(completions, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
```

## 3b — TUI render layer
Expand All @@ -52,7 +51,7 @@ The render-layer modules called by the TUI on every redraw. Most live in
provides shared pattern-matching helpers used by several render modules.

```mermaid
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Poppins, Aestetico, Arial, sans-serif", "primaryColor": "#66CCFF", "primaryTextColor": "#000000", "lineColor": "#0000CC", "tertiaryColor": "#FFCC33"}, "themeCSS": ".label,.nodeLabel,.cluster-label > span{font-family:Poppins,Arial,sans-serif;letter-spacing:.2px} .cluster-label > span{font-weight:600;font-size:13px} .edgePath .path{stroke-width:2px}"}}%%
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Poppins, Aestetico, Arial, sans-serif", "primaryColor": "#9933FF", "primaryTextColor": "#ffffff", "lineColor": "#0000CC", "tertiaryColor": "#FFCC33"}, "themeCSS": ".label,.nodeLabel,.cluster-label > span{font-family:Poppins,Arial,sans-serif;letter-spacing:.2px} .cluster-label > span{font-weight:600;font-size:13px} .edgePath .path{stroke-width:2px}"}}%%
C4Component
Comment on lines 53 to 55
title Level 3b: TUI render layer

Expand Down Expand Up @@ -97,14 +96,6 @@ C4Component
Rel(selection, filterMatch, "Uses pattern<br/>matchers")
UpdateRelStyle(selection, filterMatch, $offsetX="165", $offsetY="-25")

UpdateElementStyle(tui, $bgColor="#FFCC33", $borderColor="#0000CC", $fontColor="#000000")
UpdateElementStyle(rows, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(filterMatch, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(summary, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(filter, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(selection, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(highlight, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
UpdateElementStyle(outputFn, $bgColor="#9933FF", $borderColor="#0000CC", $fontColor="#ffffff")
```

## Component descriptions
Expand Down
23 changes: 12 additions & 11 deletions docs/reference/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@ github-code-search completions [--shell <shell>]

## Search options

| Option | Type | Required | Default | Description |
| ----------------------------------- | --------------------------------- | -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--org <org>` | string | ✅ | — | GitHub organization to search in. Automatically injected as `org:<org>` in the query. |
| `--exclude-repositories <repos>` | string | ❌ | `""` | Comma-separated list of repositories to exclude. Short form (`repoA,repoB`) or full form (`org/repoA,org/repoB`) both accepted. |
| `--exclude-extracts <refs>` | string | ❌ | `""` | Comma-separated extract refs to exclude. Format: `repoName:path/to/file:index`. Short form (without org prefix) accepted. |
| `--no-interactive` | boolean (flag) | ❌ | `true` (on) | Disable interactive mode. Interactive mode is **on** by default; pass this flag to disable it. Also triggered by `CI=true`. |
| `--format <format>` | `markdown` \| `json` | ❌ | `markdown` | Output format. See [Output formats](/usage/output-formats). |
| `--output-type <type>` | `repo-and-matches` \| `repo-only` | ❌ | `repo-and-matches` | Controls output detail level. `repo-only` lists repository names only, without individual extracts. |
| `--include-archived` | boolean (flag) | ❌ | `false` | Include archived repositories in results (excluded by default). |
| `--group-by-team-prefix <prefixes>` | string | ❌ | `""` | Comma-separated team-name prefixes for grouping result repos by GitHub team (e.g. `squad-,chapter-`). Requires `read:org` scope. |
| `--no-cache` | boolean (flag) | ❌ | `true` (on) | Bypass the 24 h team-list cache and re-fetch teams from GitHub. Cache is **on** by default; pass this flag to disable it. Only applies with `--group-by-team-prefix`. |
| Option | Type | Required | Default | Description |
| ----------------------------------- | --------------------------------- | -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--org <org>` | string | ✅ | — | GitHub organization to search in. Automatically injected as `org:<org>` in the query. |
| `--exclude-repositories <repos>` | string | ❌ | `""` | Comma-separated list of repositories to exclude. Short form (`repoA,repoB`) or full form (`org/repoA,org/repoB`) both accepted. |
| `--exclude-extracts <refs>` | string | ❌ | `""` | Comma-separated extract refs to exclude. Format: `repoName:path/to/file:index`. Short form (without org prefix) accepted. |
| `--no-interactive` | boolean (flag) | ❌ | `true` (on) | Disable interactive mode. Interactive mode is **on** by default; pass this flag to disable it. Also triggered by `CI=true`. |
| `--format <format>` | `markdown` \| `json` | ❌ | `markdown` | Output format. See [Output formats](/usage/output-formats). |
| `--output-type <type>` | `repo-and-matches` \| `repo-only` | ❌ | `repo-and-matches` | Controls output detail level. `repo-only` lists repository names only, without individual extracts. |
| `--include-archived` | boolean (flag) | ❌ | `false` | Include archived repositories in results (excluded by default). |
| `--group-by-team-prefix <prefixes>` | string | ❌ | `""` | Comma-separated team-name prefixes for grouping result repos by GitHub team (e.g. `squad-,chapter-`). Requires `read:org` scope. |
| `--no-cache` | boolean (flag) | ❌ | `true` (on) | Bypass the 24 h team-list cache and re-fetch teams from GitHub. Cache is **on** by default; pass this flag to disable it. Only applies with `--group-by-team-prefix`. |
| `--regex-hint <term>` | string | ❌ | — | Override the API search term used when the query is a regex (`/pattern/`). Useful when auto-extraction produces a term that is too broad or too narrow. See [Regex queries](/usage/search-syntax#regex-queries). |

## Global options

Expand Down
47 changes: 47 additions & 0 deletions docs/usage/search-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,53 @@ github-code-search "useFeatureFlag repo:fulll/billing-api repo:fulll/auth-servic
github-code-search "password= language:TypeScript NOT filename:test" --org fulll
```

## Regex queries

`github-code-search` supports regex syntax using the `/pattern/flags` notation, just like the GitHub web UI.

Because the GitHub Code Search API does not natively support regex, the CLI automatically extracts a representative literal term from the regex to send to the API, then filters the returned results locally with the full pattern. In most cases this is fully transparent.

```bash
# Imports using the axios module (any quote style)
github-code-search "/from.*['\"\`]axios/" --org fulll

# Axios dependency in package.json (any semver prefix)
github-code-search '/"axios": "[~^]?[0-9]"/ filename:package.json' --org fulll

# Old library require() calls
github-code-search "/require\\(['\"](old-lib)['\"]\\)/" --org fulll

# Any of TODO, FIXME or HACK comments
github-code-search "/TODO|FIXME|HACK/" --org fulll
```

::: tip Top-level alternation
When the regex contains a **top-level `|`** (e.g. `TODO|FIXME|HACK`), the CLI sends
an `A OR B OR C` query to the GitHub API so that **all branches are covered** — no results are missed.
:::

### When auto-extraction is not precise enough

If the extracted term is very short (fewer than 3 characters), the CLI will exit with a warning and ask you to provide a manual hint:

```text
⚠ Regex mode — No meaningful search term could be extracted from the regex pattern. Use --regex-hint <term> to specify the term to send to the GitHub API.
```

Use `--regex-hint` to override the API search term while still applying the full regex filter locally:

```bash
github-code-search '/"axios":\s*"[~^]?[0-9]/ filename:package.json' \
--org fulll \
--regex-hint '"axios"'
```

::: warning API coverage
The GitHub Code Search API returns **at most 1,000 results** per query. The regex filter
is applied to those results; results beyond the API cap can never be seen. Refine the
query with qualifiers (`language:`, `path:`, `filename:`) to keep the result set small.
:::

## API limits

The GitHub Code Search API returns at most **1,000 results** per query. If your query returns more, refine it with qualifiers (especially `language:` or `path:`) to stay below the limit.
Expand Down
Loading
Loading