Skip to content

feat(code-nav): ship capability-gated search_symbols (CLI + MCP) - #10

Merged
jlitola merged 1 commit into
mainfrom
blue-year
Apr 20, 2026
Merged

feat(code-nav): ship capability-gated search_symbols (CLI + MCP)#10
jlitola merged 1 commit into
mainfrom
blue-year

Conversation

@jlitola

@jlitola jlitola commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add a capability-gated code CLI command group and the MCP search_symbols tool. Finds functions, classes, modules, and doc sections inside any indexed dependency by exact-token search; returns source chunks with line ranges, symbol kind, and category.
  • Expose a --category broad filter (callable / type / module / data / documentation) and a 27-value --kind precise filter. Defaults to --intent production so top results are production source; --intent all opts back into tests / benchmarks / examples.
  • Wire shape is identical across CLI --json and MCP content[0].text, enforced by a deep-equal parity test across five fixtures (success, zero-result, NOT_FOUND, INDEXING, INVALID_ARGUMENT). Errors always return valid JSON with a stable code (13-value taxonomy) and a top-level retryable flag. GITHITS_DEBUG=code-nav emits PII-safe stderr diagnostics for error paths.
  • Feature remains hidden from public / default flows. code CLI registers only when the startup token advertises code_navigation, when GITHITS_CODE_NAVIGATION=1 is set locally, when an opaque GITHITS_API_TOKEN is present, or when stored auth has expired. MCP tool registration is stricter (capability advertised, or "unknown" + opaque env token). The gate will be removed in a follow-up once the feature is ready for public rollout.

Verification

  • bun test — 547 pass across 41 files
  • bun run typecheck — clean
  • bun run build — clean
  • Live smoke tests against production backend across npm, pypi, crates, packagist, scoped packages (npm:@types/node), and pinned versions (pypi:requests@2.32.3). Covers happy path, zero-result hint pass-through, VERSION_NOT_FOUND envelope, INDEXING retry guidance, --category / --kind filters, --intent all sugar, debug logging
  • githits code search --help and githits code --help render the new options; no "experimental", "PoC", or internal backend-service names appear in user-facing surfaces

Architecture

Shared source-of-truth helpers under src/shared/ are imported by both the CLI command and the MCP tool, so defaults, request construction, error classification, and JSON envelope shape cannot drift:

  • code-navigation-defaults.ts — canonical timeouts + FILE_INTENT_ALL sentinel
  • search-symbols-request.tsbuildSearchSymbolsParams with a defaulted: [...] echo
  • search-symbols-response.ts — success / error envelope builders
  • code-navigation-error-map.ts — classifier for the 13-code MappedError union
  • package-spec.ts — hardened parser (rejects unknown registry prefixes, preserves scoped npm names)
  • debug-log.tsGITHITS_DEBUG-gated diagnostics

The parity rules are codified in docs/implementation/mcp-cli-parity.md with stable rule IDs (PARITY-NAMING, PARITY-DEFAULTS, PARITY-REQUEST, PARITY-JSON-KEYS, PARITY-ERROR-ENVELOPE) cited by the parity test.

Known follow-ups

  • Backend-side: distinguishing totalMatches from returnedCount and adding an offset argument for real pagination. Until it lands, the CLI header stays at N match(es) (more available) and no --offset flag is exposed. Tracked in docs/implementation/tools.md under "Pending backend follow-ups".

@nathanBurg nathanBurg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to merge once conflicts resolved!

@jlitola
jlitola force-pushed the blue-year branch 2 times, most recently from fc72028 to 886cae9 Compare April 20, 2026 06:12
Adds GitHits' code-navigation surface behind the `code_navigation`
capability gate: a CLI command `githits code search <package>
[query]` and the MCP tool `search_symbols`, both backed by the
configured code-navigation endpoint. Finds functions, classes,
modules, and doc sections inside any indexed dependency by
exact-token search and returns source chunks with line ranges,
symbol kind, and category. The gate remains — this is a feature-
flagged rollout, not a public launch.

## CLI

`githits code search` with `search-symbols` retained as a
Commander alias. Package spec is `<registry>:<name>[@<version>]`
(npm default; nine registries supported; scoped npm names
preserved). Options:

- `--category <callable|type|module|data|documentation>` — broad
  filter, the preferred surface.
- `--kind <kind>` — precise-construct filter over the full
  27-value taxonomy (function, method, trait, namespace, record,
  struct, enum, protocol, etc.). Prefer `--category` for most
  cases.
- `--intent <intent|all>` — file intent, defaults to `production`
  so top results are production source; `all` opts back into
  tests/benchmarks/examples.
- `--wait <seconds>` — max indexing wait (0–60; default 20, above
  backend p50). Accepts `10` or `10s`; rejects `10ms`.
- `--keywords "a,b,c"` + repeatable `--keyword <w>` — merged,
  deduplicated, capped at 20 client-side.
- `--match-mode <or|and>`, `--file <prefix>`, `--limit <n>`
  (1–50, default 25), `--json`.

Terminal output leads each entry with `path:startLine-endLine
[kind]`, followed by the symbol name and a 3-line dedented
snippet from the `code` field. Commit-SHA refs truncate to 7
chars; trivial `v`-prefix differences on the `(requested X)`
annotation are suppressed; the literal `[fallback]` label on
unclassified chunks is suppressed. Zero-result output surfaces
the backend hint verbatim, falling back to filter-adaptive
client suggestions. All enum values accepted case-insensitively.

## MCP tool

`search_symbols`. Wire shape identical to the CLI `--json`
envelope; deep-equal enforced by a shared parity test across
five fixtures (success, zero-result, NOT_FOUND, INDEXING,
INVALID_ARGUMENT). Error results always valid JSON in
`content[0].text`. Tool description positions `category` as the
preferred filter and documents the INDEXING retry pattern (retry
with `wait_timeout_ms: 60000`).

## Shared source-of-truth helpers

- `src/shared/code-navigation-defaults.ts` —
  DEFAULT_WAIT_TIMEOUT_MS (20s), MAX_WAIT_TIMEOUT_MS (60s
  ceiling), SEARCH_SYMBOLS_DEFAULT_FILE_INTENT ("PRODUCTION"),
  FILE_INTENT_ALL sentinel that translates to "omit the GraphQL
  variable" at the service layer.
- `src/shared/search-symbols-request.ts` —
  buildSearchSymbolsParams with a `defaulted: [...]` echo so
  agents see which fields the client filled in silently.
- `src/shared/search-symbols-response.ts` — success/error envelope
  builders. No underscore-prefixed keys; `retryable` top-level on
  errors for agents that want one boolean over a per-code table.
- `src/shared/code-navigation-error-map.ts` — single classifier
  for the 13-code MappedError union
  (NOT_FOUND, VERSION_NOT_FOUND, INDEXING, UNRESOLVABLE,
  ACCESS_DENIED, AUTH_REQUIRED, NETWORK, INVALID_ARGUMENT,
  BACKEND_ERROR, TIMEOUT, RATE_LIMITED, PROTOCOL_ERROR, UNKNOWN).
  Table-tested; no UNKNOWN on any recognised error class.
- `src/shared/package-spec.ts` — hardened parser: rejects unknown
  registry prefixes (`foobar:baz`), malformed specs, preserves
  scoped npm names.
- `src/shared/normalise-keywords.ts` — comma + repeated-flag
  merger with cap enforcement.
- `src/shared/debug-log.ts` — `GITHITS_DEBUG`-gated stderr
  diagnostics. PII policy: codes, detail keys, error class names;
  never message text, tokens, or response bodies.

## Service layer

Always requests `mode: DETAILED`; always selects `code`,
`resolution`, `kind`, and `category`. Nullable `data` and
`searchSymbols` accepted in the Zod response schema. `fetch()`
wrapped so connectivity failures surface as
`CodeNavigationNetworkError`. Full dispatch on `extensions.code`
covering the backend's 14 error codes, with `extensions.retryable`
propagated end-to-end. The legacy `chunkType` field is no longer
selected or surfaced client-side; `kind` is the single source of
truth (the backend handles its own chunk-to-symbol fallback
internally). Message-text heuristics retained as a rollover
fallback for backends that predate the structured error codes.

## Docs

- New `docs/implementation/mcp-cli-parity.md` — rules for
  dual-surface tools (PARITY-NAMING, PARITY-DEFAULTS,
  PARITY-REQUEST, PARITY-JSON-KEYS, PARITY-ERROR-ENVELOPE) with
  stable rule IDs cited by the parity test.
- `docs/implementation/tools.md`,
  `docs/implementation/cli-commands.md`,
  `docs/implementation/config.md` updated with the new command,
  response shape, and troubleshooting notes (including
  `GITHITS_DEBUG=code-nav`).
- `tools.md` carries a "Pending backend follow-ups" note for the
  single remaining backend-side task (distinguishing
  totalMatches from returnedCount and adding an offset arg for
  pagination).

## Tests

547 passing across 41 files. Highlights:

- Classifier table covers every MappedErrorCode with fixtures
  for both `extensions.code` dispatch and legacy message-text
  fallback.
- Parity test asserts CLI `--json` and MCP `content[0].text`
  produce deep-equal JSON for the same inputs across five
  fixture cases.
- Service tests exercise the full GraphQL response surface:
  null data, VERSION_NOT_FOUND with structured details, TIMEOUT
  / RATE_LIMITED retryable dispatch, extensions.code-takes-
  precedence over message heuristics, legacy message fallback
  for pre-deploy backends.
- CLI tests cover every option: `--category`, the expanded
  `--kind` taxonomy, `--wait` seconds + `s` suffix + rejection
  of `ms`, keyword merge + dedup + cap, case-insensitive enum
  values, zero-result adaptive suggestions, capability-gate
  registration.
- Debug-log test asserts PII policy (no `message` field in
  payloads) and scope-syntax parsing.

Supersedes the earlier gated code-navigation PoC that was
scaffolded under a feature flag — the capability gate remains
(commands only register when the startup token advertises
`code_navigation`, when `GITHITS_CODE_NAVIGATION=1` is set
locally, when an opaque `GITHITS_API_TOKEN` is present, or when
stored auth has expired), but the implementation underneath is
now the production surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jlitola jlitola changed the title Add gated code navigation PoC feat(code-nav): ship capability-gated search_symbols (CLI + MCP) Apr 20, 2026
@jlitola
jlitola merged commit 0a2fb61 into main Apr 20, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants