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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"metadata": {
"description": "GitHits plugins for Claude Code - code examples from global open source",
"version": "0.2.3"
"version": "0.3.0"
},
"plugins": [
{
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "githits",
"version": "0.2.3",
"version": "0.3.0",
"description": "Code examples from global open source for developers and AI assistants",
"author": {
"name": "GitHits"
Expand Down
2 changes: 1 addition & 1 deletion .plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "githits",
"version": "0.2.3",
"version": "0.3.0",
"description": "Code examples from global open source for developers and AI assistants",
"author": {
"name": "GitHits"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ npx githits init

`init` authenticates with your [GitHits](https://githits.com) account, then auto-detects your installed coding tools and configures each one with GitHits MCP.

Supported tools: Claude Code, Cursor, Windsurf, VS Code / Copilot, Cline, Claude Desktop, Codex CLI, Gemini CLI, and Google Antigravity.
Supported tools: Claude Code, Cursor, Windsurf, VS Code / Copilot, Cline, Claude Desktop, Codex CLI, Gemini CLI, Google Antigravity, and OpenCode.

If you are using a tool that is not listed above, use the manual MCP setup instructions near the end of this README.

Expand Down
7 changes: 4 additions & 3 deletions commands/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ context summary:

## MCP Tools

This plugin connects to the GitHits MCP server and always exposes three core tools:
This plugin connects to the GitHits MCP server and always exposes these core tools:

- **get_example** — Find code examples by describing what you need in natural
language. Requires `query`; `language` is optional and inferred when omitted.
- **search_language** — Look up supported programming language names when you
need to force a specific language.
- **feedback** — Rate a search result to improve future quality.

Additional indexed dependency/package tools such as `search`, `pkg_info`,
`code_files`, and `code_grep` are available by default.
Additional indexed dependency/package tools are available by default:
`search`, `search_status`, `docs_list`, `docs_read`, `pkg_info`, `pkg_vulns`,
`pkg_deps`, `pkg_changelog`, `code_files`, `code_read`, and `code_grep`.

## Authentication

Expand Down
6 changes: 3 additions & 3 deletions docs/implementation/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The CLI exposes `example`, `languages`, `feedback`, top-level indexed `search` /
|---|---|---|---|
| `init` | — | `-y, --yes`, `--skip-login` | Authenticate and set up MCP server for coding agents |
| `example <query>` | `<query>` | `-l, --lang <language>`, `--license <mode>`, `--explain`, `--json` | Search for code examples |
| `search <query>` | `--in <target>` | `--source <source>`, `--kind <kind>`, `--category <category>`, `--path-prefix <prefix>`, `--intent <intent>`, `--public`, `--name <name>`, `--lang <language>`, `--allow-partial`, `--limit <n>`, `--offset <n>`, `--wait <seconds>`, `--json` | Unified indexed search across dependency/repository code, docs, and symbols |
| `search <query>` | `--in <target>` | `--source <source>`, `--kind <kind>`, `--category <category>`, `--path-prefix <prefix>`, `--intent <intent>`, `--public`, `--name <name>`, `--lang <language>`, `--allow-partial`, `--limit <n>`, `--offset <n>`, `--wait <seconds>`, `--json` | Unified indexed search across dependency/repository code, docs, and symbols. Defaults to 10 results. |
| `search-status <search-ref>` | `<search-ref>` | `--json` | Check progress, fetch partial hits, or fetch final results for a prior unified search |
| `languages [query]` | — | `--json` | List or filter supported languages |
| `feedback <solution_id>` | `--accept` or `--reject` | `-m, --message <text>`, `--json` | Submit feedback on a search result |
Expand All @@ -34,7 +34,7 @@ githits init --skip-login # Skip authentication, configure tools only

Authenticates with GitHits (via OAuth in the browser), then scans for available coding agents, checks which are already configured, and sets up unconfigured ones with your confirmation. All agents are pre-checked before any setup begins, so the status display is fully resolved. CLI agents are considered available only when their executable is on `PATH`; related dot-directories alone do not count. Config-file agents remain filesystem-detected using their known app/config directories. If already authenticated, the login step is skipped automatically. If login fails, the user is prompted to continue with tool setup anyway. If all detected agents are already configured, exits early with a summary.

Supports Claude Code, Cursor, Windsurf, VS Code / Copilot, Cline, Claude Desktop, Codex CLI, Gemini CLI, and Google Antigravity. Uses plugin install (Claude Code), CLI commands (Codex, Gemini CLI), and atomic config file writes (Cursor, Windsurf, VS Code, Cline, Claude Desktop, Google Antigravity). CLI agents use read-only check commands (e.g., `claude plugin list`) to determine configuration status before prompting.
Supports Claude Code, Cursor, Windsurf, VS Code / Copilot, Cline, Claude Desktop, Codex CLI, Gemini CLI, Google Antigravity, and OpenCode. Uses plugin install (Claude Code), CLI commands (Codex, Gemini CLI), and atomic config file writes (Cursor, Windsurf, VS Code, Cline, Claude Desktop, Google Antigravity, OpenCode). CLI agents use read-only check commands (e.g., `claude plugin list`) to determine configuration status before prompting.

The command uses `createContainer()` lazily for the login step. Tool detection and configuration use lightweight dependencies that don't require auth.

Expand Down Expand Up @@ -72,7 +72,7 @@ Unified search spans indexed dependency and repository code, docs, and explicit

**Intent filter.** When `--intent` is omitted, unified search sends no file-intent filter. Pass `--intent production` or another specific intent only when you want to narrow the result set. Some sources can still ignore `fileIntent`; when they do, the JSON `sourceStatus` block and terminal notes report that explicitly.

**Complete-by-default results.** The CLI sends `allowPartialResults: false` unless `--allow-partial` is passed. If indexing does not complete within the wait window, the default behavior returns a `searchRef` and progress summary instead of partial hits. With `--allow-partial`, available hits are included while remaining sources continue indexing. `--wait` is in seconds (0-60, default 20).
**Complete-by-default results.** The CLI sends `allowPartialResults: false` unless `--allow-partial` is passed. If indexing does not complete within the wait window, the default behavior returns a `searchRef` and progress summary instead of partial hits. With `--allow-partial`, available hits are included while remaining sources continue indexing. `--limit` defaults to 10 results. `--wait` is in seconds (0-60, default 20).

The original unified-search plan envisaged hiding partial mode entirely in v1 to make results trustworthy by default. We kept the flag exposed because some agent and CLI flows benefit from "show me what you have so far"; the trust contract is preserved by keeping the *default* false. Users and agents must explicitly opt in, so a vanilla `search` call still cannot return incomplete results by surprise.

Expand Down
4 changes: 2 additions & 2 deletions docs/implementation/mcp-cli-parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,8 @@ When a new tool lands with both MCP and CLI surfaces:
values never round-trip as caller intent.
- **`entries: { count, items }` shape.** Mirrors `runtime: {count,
items}` from `pkg_deps`.
- **`source: null` promoted to `NOT_FOUND`.** The service layer
promotes the null-source case to a typed
- **Missing source promoted to `NOT_FOUND`.** The service layer
promotes null or empty `source` to a typed
`PackageIntelligenceChangelogSourceNotFoundError` with a message
naming the sources tried (GitHub Releases, CHANGELOG.md, HexDocs).
- **`--verbose` / `--no-body` / `--json` interaction.** Default
Expand Down
10 changes: 5 additions & 5 deletions docs/implementation/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ The CLI exposes MCP tools that mirror the backend's MCP server. This document ex

GitHits has two MCP server implementations:

- **Backend** (`githits-backend`) — Python/FastMCP, runs as a hosted service at `mcp.githits.com`. Surfaces only the example-search workflow (`get_example`, `search_language`, `feedback`).
- **CLI** (`githits-cli`) — TypeScript/MCP SDK, runs locally via `githits mcp start`. Surfaces the full set, including unified `search`, package intelligence (`pkg_*`), and code navigation (`code_*`).
- **Backend** (`githits-backend` / PkgSeer MCP) — Python/FastMCP, runs as hosted MCP services. Production exposes both the core example-search workflow (`get_example`, `search_language`, `feedback`) and indexed package/source tooling.
- **CLI** (`githits-cli`) — TypeScript/MCP SDK, runs locally via `githits mcp start`. Surfaces the same public tool families, including unified `search`, package intelligence (`pkg_*`), docs (`docs_*`), and code navigation (`code_*`).

The two surfaces overlap on the example-search workflow only; for the overlapping tools the parameters and descriptions stay aligned. Tools that exist only on the CLI surface (code navigation, package intelligence, unified `search`) are not constrained by backend parity.
The CLI mirrors the production MCP tool contract where equivalent tools exist. Core example-search tool descriptions are kept aligned with GitHits backend wording; indexed package/source tool descriptions are kept aligned with the PkgSeer/GitHits indexed-service contract.

## Current Tools

Expand All @@ -20,7 +20,7 @@ The two surfaces overlap on the example-search workflow only; for the overlappin
| `get_example` | `query`, `language?`, `license_mode?` | Search for canonical code examples. Returns markdown-formatted results. If `language` is omitted, the backend infers it from the query. |
| `search_language` | `query` | Find supported programming language names before searching. |
| `feedback` | `solution_id`, `accepted`, `feedback_text?` | Submit feedback on a search result to improve quality. |
| `search` | `query`, `target?`, `targets?`, `sources?`, `category?`, `kind?`, `path_prefix?`, `file_intent?`, `public_only?`, `name?`, `language?`, `allow_partial_results?`, `limit?`, `offset?`, `wait_timeout_ms?`, `format?` | Unified indexed dependency/repository discovery search across code, docs, and symbols. Omit `file_intent` to search across all intents; set it only when you want to narrow results, and note that some sources may ignore it and report that in `sourceStatus`. Prefer `sources:["symbol"]` for symbol-shaped unified search. Complete-by-default; set `allow_partial_results: true` to receive available partial hits while indexing continues. `format` defaults to `text-v1` for compact agent output; pass `format: "json"` for the structured envelope. |
| `search` | `query`, `target?`, `targets?`, `sources?`, `category?`, `kind?`, `path_prefix?`, `file_intent?`, `public_only?`, `name?`, `language?`, `allow_partial_results?`, `limit?`, `offset?`, `wait_timeout_ms?`, `format?` | Unified indexed dependency/repository discovery search across code, docs, and symbols. Omit `file_intent` to search across all intents; set it only when you want to narrow results, and note that some sources may ignore it and report that in `sourceStatus`. Prefer `sources:["symbol"]` for symbol-shaped unified search. Complete-by-default; `limit` defaults to 10 to keep agent output compact. Set `allow_partial_results: true` to receive available partial hits while indexing continues. `format` defaults to `text-v1` for compact agent output; pass `format: "json"` for the structured envelope. |
| `search_status` | `search_ref` | Check progress, fetch partial hits when the original request used `allow_partial_results: true`, or fetch final results for a prior unified search. |
| `docs_list` | `registry`, `package_name`, `version?`, `limit?`, `after?` | List hosted/crawled and repository-backed documentation pages for a package. Entries include stable page IDs for `docs_read`; repo-backed entries include exact source metadata for `code_read` follow-up. |
| `docs_read` | `page_id`, `start_line?`, `end_line?` | Read a documentation page by page ID. Supports bounded line ranges for long pages; repo-backed pages include exact file follow-up metadata. |
Expand Down Expand Up @@ -104,7 +104,7 @@ The two surfaces overlap on the example-search workflow only; for the overlappin

**Version validation.** Same rule as `pkg_vulns` / `pkg_deps`: tag-style `v`-prefixed inputs on `from_version` / `to_version` are rejected client-side with `INVALID_ARGUMENT`. `<spec>@<version>` is also rejected — the `pkg changelog` family has no single-version query, and silently remapping to `to_version` would be a client-invented semantic shift. Hint text redirects callers to `--to` / `to_version`.

**NOT_FOUND semantics.** Backend `source === null` is promoted to a typed `PackageIntelligenceChangelogSourceNotFoundError` at the service boundary, which the shared classifier routes to the `NOT_FOUND` envelope with a message naming the sources that were tried. Empty `entries.items: []` with a valid `source` is a success, not an error — "no entries in this range" is a legitimate neutral outcome.
**NOT_FOUND semantics.** Backend `source === null` or `source === ""` is promoted to a typed `PackageIntelligenceChangelogSourceNotFoundError` at the service boundary, which the shared classifier routes to the `NOT_FOUND` envelope with a message naming the sources that were tried. Empty `entries.items: []` with a valid `source` is a success, not an error — "no entries in this range" is a legitimate neutral outcome.

**Overlap with `pkg_info`.** `pkg_info` already surfaces a short-form `recentChanges` block (from the backend's `latestChangelogs` field on `PackageSummaryResult`). For a quick "what shipped recently" glance embedded in a package overview, use `pkg_info`. For the full range-capable, body-rich, `include_bodies`-toggleable changelog with `--no-body` timeline mode and repo-URL addressing, use `pkg_changelog`.

Expand Down
2 changes: 1 addition & 1 deletion gemini-extension.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "githits",
"version": "0.2.3",
"version": "0.3.0",
"description": "Code examples from global open source for developers and AI assistants.",
"mcpServers": {
"githits": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "githits",
"description": "CLI companion for GitHits - code examples from global open source for developers and AI assistants",
"version": "0.2.3",
"version": "0.3.0",
"type": "module",
"files": [
"dist",
Expand Down
2 changes: 1 addition & 1 deletion plugins/claude/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "githits",
"version": "0.2.3",
"version": "0.3.0",
"description": "Code examples from global open source for developers and AI assistants",
"author": {
"name": "GitHits"
Expand Down
7 changes: 4 additions & 3 deletions plugins/claude/commands/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ context summary:

## MCP Tools

This plugin connects to the GitHits MCP server and always exposes three core tools:
This plugin connects to the GitHits MCP server and always exposes these core tools:

- **get_example** — Find code examples by describing what you need in natural
language. Requires `query`; `language` is optional and inferred when omitted.
- **search_language** — Look up supported programming language names when you
need to force a specific language.
- **feedback** — Rate a search result to improve future quality.

Additional indexed dependency/package tools such as `search`, `pkg_info`,
`code_files`, and `code_grep` are available by default.
Additional indexed dependency/package tools are available by default:
`search`, `search_status`, `docs_list`, `docs_read`, `pkg_info`, `pkg_vulns`,
`pkg_deps`, `pkg_changelog`, `code_files`, `code_read`, and `code_grep`.

## Authentication

Expand Down
2 changes: 1 addition & 1 deletion src/commands/mcp-instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const MULTI_TURN_TIP =
* Mirrors `getMcpToolDefinitions` so the instructions stay aligned
* with the registered tool surface.
*/
export function buildMcpInstructions(deps: Dependencies): string {
export function buildMcpInstructions(_deps: Dependencies): string {
const sections = [CORE_BLOCK];

const bullets: string[] = [];
Expand Down
24 changes: 24 additions & 0 deletions src/commands/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it, mock, spyOn } from "bun:test";
import type {
UnifiedSearchIncomplete,
UnifiedSearchOutcome,
UnifiedSearchParams,
UnifiedSearchProgress,
UnifiedSearchSessionStatus,
} from "../services/code-navigation-service.js";
Expand Down Expand Up @@ -108,6 +109,29 @@ describe("searchAction", () => {
consoleSpy.mockRestore();
});

it("uses the shared search default limit and preserves explicit limits", async () => {
const search = mock((_: UnifiedSearchParams) =>
Promise.resolve(defaultUnifiedSearchOutcome),
);
const deps = createDeps({
codeNavigationService: createMockCodeNavigationService({ search }),
});
const consoleSpy = spyOn(console, "log").mockImplementation(() => {});

await searchAction("router middleware", { in: ["npm:express"] }, deps);
expect(search.mock.calls[0]?.[0]?.limit).toBe(10);

search.mockClear();
await searchAction(
"router middleware",
{ in: ["npm:express"], limit: "25" },
deps,
);
expect(search.mock.calls[0]?.[0]?.limit).toBe(25);

consoleSpy.mockRestore();
});

it("does not send a file-intent filter unless the caller explicitly set one", async () => {
const search = mock<
(
Expand Down
2 changes: 1 addition & 1 deletion src/commands/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export function registerSearchCommand(program: Command) {
"--allow-partial",
"Include hits already available while indexing continues; a searchRef is still returned so search-status can fetch the rest",
)
.option("--limit <n>", "Max results (1-100)")
.option("--limit <n>", "Max results (1-100, default: 10)")
.option("--offset <n>", "Result offset")
.option(
"--wait <seconds>",
Expand Down
30 changes: 30 additions & 0 deletions src/services/package-intelligence-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MalformedPackageIntelligenceResponseError,
PackageIntelligenceAccessError,
PackageIntelligenceBackendError,
PackageIntelligenceChangelogSourceNotFoundError,
PackageIntelligenceFeatureFlagRequiredError,
PackageIntelligenceNetworkError,
PackageIntelligenceServiceImpl,
Expand Down Expand Up @@ -1291,6 +1292,35 @@ describe("PackageIntelligenceServiceImpl.packageVulnerabilities", () => {
});
});

describe("PackageIntelligenceServiceImpl — packageChangelog", () => {
const ENDPOINT = "https://pkgseer.dev";

it("treats an empty source as no changelog data", async () => {
const fetchFn = mock(() =>
Promise.resolve(
jsonResponse({
data: {
packageChangelog: {
package: { name: "express", registry: "NPM" },
source: "",
entries: [],
},
},
}),
),
);
const service = new PackageIntelligenceServiceImpl(
ENDPOINT,
createMockTokenProvider(),
asFetchFn(fetchFn),
);

await expect(
service.packageChangelog({ registry: "NPM", packageName: "express" }),
).rejects.toBeInstanceOf(PackageIntelligenceChangelogSourceNotFoundError);
});
});

describe("PackageIntelligenceServiceImpl — packageDependencies", () => {
const ENDPOINT = "https://pkgseer.dev";

Expand Down
Loading
Loading