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
6 changes: 3 additions & 3 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ Find code examples from open source repositories.
**Parameters:**

- `query` (string, required) - natural language description of what you need
- `language` (string, required) - programming language name (use `search_language` to verify)
- `language` (string, optional) - programming language name; omit it to let GitHits infer the language from the query
- `license_mode` (string, optional) - one of `strict` (default), `yolo`, or `custom`

### search_language

Look up supported programming language names. Use this before calling `get_example` to get the correct language identifier.
Look up supported programming language names. Use this before calling `get_example` only when you need to force a specific language and the exact name is uncertain.

**Parameters:**

Expand Down Expand Up @@ -60,7 +60,7 @@ Do not use `get_example` for:

## How to Search Well

- Call `search_language` first to confirm the correct language name before calling `get_example`
- Pass `language` only when you need to force a specific language; call `search_language` first if the exact language name is uncertain
- Formulate queries as natural language questions (e.g., "How to stream responses with the Vercel AI SDK in Next.js")
- Include specific error messages, library names, or API names when relevant
- Keep queries focused: 3-4 technical terms maximum
Expand Down
13 changes: 7 additions & 6 deletions commands/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ description: Search for canonical code examples from open source via GitHits

Search for code examples using GitHits for the query: "$ARGUMENTS"

Use the GitHits MCP `get_example` tool with the user's query. The tool requires two
parameters:
Use the GitHits MCP `get_example` tool with the user's query.

Required parameter:

- **query**: The user's search query, formulated in natural language.
- **language**: The programming language. If the language is unclear from
context, use the `search_language` tool first to find the correct language
name.

Optional parameter:
Optional parameters:

- **language**: The programming language. Omit it to let GitHits infer the
language from the query. If you need to force a specific language and the
exact name is uncertain, use the `search_language` tool first.
- **license_mode**: `"strict"` (default, excludes copyleft), `"yolo"` (all
licenses), or `"custom"` (user's blocklist).

Expand Down
6 changes: 3 additions & 3 deletions commands/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ context summary:
This plugin connects to the GitHits MCP server and always exposes three core tools:

- **get_example** — Find code examples by describing what you need in natural
language. Requires `query` and `language` parameters.
- **search_language** — Look up supported programming language names before
searching.
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`, `package_summary`,
Expand Down
13 changes: 7 additions & 6 deletions commands/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ Use GitHits example search for the query: "$ARGUMENTS"

This slash command is the older alias for `/githits:example`.

Use the GitHits MCP `get_example` tool with the user's query. The tool requires two
parameters:
Use the GitHits MCP `get_example` tool with the user's query.

Required parameter:

- **query**: The user's search query, formulated in natural language.
- **language**: The programming language. If the language is unclear from
context, use the `search_language` tool first to find the correct language
name.

Optional parameter:
Optional parameters:

- **language**: The programming language. Omit it to let GitHits infer the
language from the query. If you need to force a specific language and the
exact name is uncertain, use the `search_language` tool first.
- **license_mode**: `"strict"` (default, excludes copyleft), `"yolo"` (all
licenses), or `"custom"` (user's blocklist).

Expand Down
5 changes: 3 additions & 2 deletions docs/implementation/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The CLI exposes three always-on top-level commands: `example`, `languages`, and
| Command | Required Args | Options | Description |
|---|---|---|---|
| `init` | — | `-y, --yes`, `--skip-login` | Authenticate and set up MCP server for coding agents |
| `example <query>` | `-l, --lang <language>` | `--license <mode>`, `--explain`, `--json` | Search for code examples |
| `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-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 |
Expand Down Expand Up @@ -41,13 +41,14 @@ The command uses `createContainer()` lazily for the login step. Tool detection a
### `githits example`

```
githits example "how to use express middleware"
githits example "how to use express middleware" --lang javascript
githits example "async file reading" -l python --license yolo
githits example "react hooks patterns" -l typescript --explain
githits example "react hooks patterns" -l typescript --json
```

Default output is markdown (the API response). With `--explain`, an AI-generated explanation is included alongside the code example. With `--json`, output is `{ "result": "<markdown>", "solution_id": "<uuid>" }` (`solution_id` is omitted only if the markdown lacks a solution URL — pass it back to `feedback`). The MCP `get_example` tool always sends `include_explanation: false` since LLMs don't need the extra context.
Default output is markdown (the API response). `--lang` is optional; when omitted, the backend infers the language from the query. With `--explain`, an AI-generated explanation is included alongside the code example. With `--json`, output is `{ "result": "<markdown>", "solution_id": "<uuid>" }` (`solution_id` is omitted only if the markdown lacks a solution URL — pass it back to `feedback`). The MCP `get_example` tool always sends `include_explanation: false` since LLMs don't need the extra context.

### `githits search`

Expand Down
2 changes: 1 addition & 1 deletion docs/implementation/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Both expose the same tools with identical names, parameters, and descriptions. T

| Tool | Parameters | Description |
|---|---|---|
| `get_example` | `query`, `language`, `license_mode?` | Search for canonical code examples. Returns markdown-formatted results. |
| `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?` | 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. |
Expand Down
13 changes: 7 additions & 6 deletions plugins/claude/commands/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ description: Search for canonical code examples from open source via GitHits

Search for code examples using GitHits for the query: "$ARGUMENTS"

Use the GitHits MCP `get_example` tool with the user's query. The tool requires two
parameters:
Use the GitHits MCP `get_example` tool with the user's query.

Required parameter:

- **query**: The user's search query, formulated in natural language.
- **language**: The programming language. If the language is unclear from
context, use the `search_language` tool first to find the correct language
name.

Optional parameter:
Optional parameters:

- **language**: The programming language. Omit it to let GitHits infer the
language from the query. If you need to force a specific language and the
exact name is uncertain, use the `search_language` tool first.
- **license_mode**: `"strict"` (default, excludes copyleft), `"yolo"` (all
licenses), or `"custom"` (user's blocklist).

Expand Down
6 changes: 3 additions & 3 deletions plugins/claude/commands/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ context summary:
This plugin connects to the GitHits MCP server and always exposes three core tools:

- **get_example** — Find code examples by describing what you need in natural
language. Requires `query` and `language` parameters.
- **search_language** — Look up supported programming language names before
searching.
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`, `package_summary`,
Expand Down
13 changes: 7 additions & 6 deletions plugins/claude/commands/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ Use GitHits example search for the query: "$ARGUMENTS"

This slash command is the older alias for `/githits:example`.

Use the GitHits MCP `get_example` tool with the user's query. The tool requires two
parameters:
Use the GitHits MCP `get_example` tool with the user's query.

Required parameter:

- **query**: The user's search query, formulated in natural language.
- **language**: The programming language. If the language is unclear from
context, use the `search_language` tool first to find the correct language
name.

Optional parameter:
Optional parameters:

- **language**: The programming language. Omit it to let GitHits infer the
language from the query. If you need to force a specific language and the
exact name is uncertain, use the `search_language` tool first.
- **license_mode**: `"strict"` (default, excludes copyleft), `"yolo"` (all
licenses), or `"custom"` (user's blocklist).

Expand Down
3 changes: 2 additions & 1 deletion plugins/claude/skills/search/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ Authentication:
Guidelines:

- Prefer existing search context if it already answers the problem.
- Use `search_language` first when language naming is uncertain.
- Pass `language` only when you need to force a specific language; use
`search_language` first if the exact language name is uncertain.
- Use `get_example` for one focused example-search question at a time.
- When the task is about indexed dependency or repository internals and the
capability-gated tools are available, prefer unified `search` instead of
Expand Down
3 changes: 2 additions & 1 deletion skills/search/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ Authentication:
Guidelines:

- Prefer existing search context if it already answers the problem.
- Use `search_language` first when language naming is uncertain.
- Pass `language` only when you need to force a specific language; use
`search_language` first if the exact language name is uncertain.
- Use `get_example` for one focused example-search question at a time.
- When the task is about indexed dependency or repository internals and the
capability-gated tools are available, prefer unified `search` instead of
Expand Down
3 changes: 1 addition & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Getting started:
githits init Set up MCP for your coding agents
githits login Authenticate with your GitHits account
githits mcp Show MCP setup instructions
githits example "query" -l python Get code examples
githits example "query" Get code examples

Learn more at https://githits.com
Docs: https://app.githits.com/docs/
Expand All @@ -84,7 +84,6 @@ registerLanguagesCommand(program);
registerFeedbackCommand(program);
const argv = process.argv.slice(2);
const registrationArgv = stripRootRegistrationOptions(argv);
const helpInvocation = isHelpInvocation(registrationArgv);
const shouldLoadGatedHelpRegistration =
needsGatedHelpRegistration(registrationArgv);
const helpRegistrationOptions = shouldLoadGatedHelpRegistration
Expand Down
18 changes: 18 additions & 0 deletions src/commands/example.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ describe("exampleAction", () => {
consoleSpy.mockRestore();
});

it("calls service without language when --lang is omitted", async () => {
const searchFn = mock(() => Promise.resolve("result"));
const deps = createDeps({
githitsService: createMockGitHitsService({ search: searchFn }),
});
const consoleSpy = spyOn(console, "log").mockImplementation(() => {});

await exampleAction("hello world", {}, deps);

expect(searchFn).toHaveBeenCalledWith({
query: "hello world",
language: undefined,
licenseMode: undefined,
includeExplanation: undefined,
});
consoleSpy.mockRestore();
});

it("outputs JSON when --json flag provided", async () => {
const consoleSpy = spyOn(console, "log").mockImplementation(() => {});

Expand Down
8 changes: 6 additions & 2 deletions src/commands/example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { extractSolutionId } from "../shared/extract-solution-id.js";
import { AuthRequiredError, requireAuth } from "../shared/require-auth.js";

export interface ExampleOptions {
lang: string;
lang?: string;
license?: "strict" | "yolo" | "custom";
explain?: boolean;
json?: boolean;
Expand Down Expand Up @@ -53,6 +53,7 @@ const EXAMPLE_DESCRIPTION = `Get verified, canonical code examples from global o
For dependency, package, or repository source search, use \`githits search\` instead.

Examples:
githits example "how to use express middleware"
githits example "how to use express middleware" --lang javascript
githits example "async file reading" -l python --license yolo
githits example "react hooks patterns" -l typescript --explain
Expand All @@ -64,7 +65,10 @@ export function registerExampleCommand(program: Command) {
.summary("Get code examples from global open source")
.description(EXAMPLE_DESCRIPTION)
.argument("<query>", "Natural language example-search query")
.requiredOption("-l, --lang <language>", "Programming language")
.option(
"-l, --lang <language>",
"Optional programming language; omitted values are inferred by GitHits",
)
.addOption(
new Option("--license <mode>", "License filter mode")
.choices(["strict", "yolo", "custom"])
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 @@ -17,7 +17,7 @@ import type { Dependencies } from "../container.js";

const CORE_BLOCK = `GitHits surfaces verified, canonical code examples from global open source. Use it when you're stuck, the user is frustrated by repeated failed attempts, you need up-to-date API usage, or the user mentions GitHits.

Workflow: call \`search_language\` first if the language name is uncertain → call \`get_example\` with one focused question → send \`feedback\` on the returned solution_id so quality improves. Each search addresses a single issue; reuse context from prior results before re-searching.`;
Workflow: call \`get_example\` with one focused question, optionally passing \`language\` when the desired language is known; call \`search_language\` first only if you need to force a language and the exact name is uncertain. Send \`feedback\` on the returned solution_id so quality improves. Each search addresses a single issue; reuse context from prior results before re-searching.`;

const PACKAGE_TOOLS_PREAMBLE = `Package tools work with third-party dependency source plus registry metadata. Use them when a stack trace points into a dependency, you need to verify how a library actually works, or you're evaluating whether to add or upgrade a package.

Expand Down
54 changes: 38 additions & 16 deletions src/commands/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
createSearchStatusTool,
createSearchTool,
type ToolDefinition,
type ZodRawShape,
} from "../tools/index.js";
import {
buildMcpInstructions,
Expand All @@ -35,37 +36,58 @@ import {
*/
export function getMcpToolDefinitions(
deps: Dependencies,
): ToolDefinition<any, any>[] {
const tools: ToolDefinition<any, any>[] = [
createGetExampleTool(deps.githitsService),
createSearchLanguageTool(deps.githitsService),
createFeedbackTool(deps.githitsService),
): ToolDefinition<unknown>[] {
const tools: ToolDefinition<unknown>[] = [
eraseTool(createGetExampleTool(deps.githitsService)),
eraseTool(createSearchLanguageTool(deps.githitsService)),
eraseTool(createFeedbackTool(deps.githitsService)),
];

const gateOpen = isPackageToolsCapabilityOpen(deps);

if (gateOpen && deps.codeNavigationService) {
tools.push(createSearchTool(deps.codeNavigationService));
tools.push(createSearchStatusTool(deps.codeNavigationService));
tools.push(createListFilesTool(deps.codeNavigationService));
tools.push(createReadFileTool(deps.codeNavigationService));
tools.push(createGrepRepoTool(deps.codeNavigationService));
tools.push(eraseTool(createSearchTool(deps.codeNavigationService)));
tools.push(eraseTool(createSearchStatusTool(deps.codeNavigationService)));
tools.push(eraseTool(createListFilesTool(deps.codeNavigationService)));
tools.push(eraseTool(createReadFileTool(deps.codeNavigationService)));
tools.push(eraseTool(createGrepRepoTool(deps.codeNavigationService)));
}

if (gateOpen && deps.packageIntelligenceService) {
tools.push(createListPackageDocsTool(deps.packageIntelligenceService));
tools.push(createReadPackageDocTool(deps.packageIntelligenceService));
tools.push(createPackageSummaryTool(deps.packageIntelligenceService));
tools.push(
createPackageVulnerabilitiesTool(deps.packageIntelligenceService),
eraseTool(createListPackageDocsTool(deps.packageIntelligenceService)),
);
tools.push(
eraseTool(createReadPackageDocTool(deps.packageIntelligenceService)),
);
tools.push(
eraseTool(createPackageSummaryTool(deps.packageIntelligenceService)),
);
tools.push(
eraseTool(
createPackageVulnerabilitiesTool(deps.packageIntelligenceService),
),
);
tools.push(
eraseTool(createPackageDependenciesTool(deps.packageIntelligenceService)),
);
tools.push(
eraseTool(createPackageChangelogTool(deps.packageIntelligenceService)),
);
tools.push(createPackageDependenciesTool(deps.packageIntelligenceService));
tools.push(createPackageChangelogTool(deps.packageIntelligenceService));
}

return tools;
}

function eraseTool<TArgs, TSchema extends ZodRawShape>(
tool: ToolDefinition<TArgs, TSchema>,
): ToolDefinition<unknown> {
return {
...tool,
handler: (args, extra) => tool.handler(args as TArgs, extra),
};
}

/**
* Creates the MCP server with injected dependencies.
*/
Expand Down
7 changes: 0 additions & 7 deletions src/services/code-navigation-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,8 @@ import {
CodeNavigationBackendError,
CodeNavigationFileNotFoundError,
CodeNavigationIndexingError,
CodeNavigationNetworkError,
CodeNavigationServiceImpl,
CodeNavigationTargetNotFoundError,
CodeNavigationUnresolvableError,
CodeNavigationValidationError,
CodeNavigationVersionNotFoundError,
MalformedCodeNavigationResponseError,
} from "./code-navigation-service.js";
import { AuthenticationError } from "./githits-service.js";
import { createMockTokenProvider } from "./test-helpers.js";

function mockFetch(impl: () => Promise<Response>) {
Expand Down
10 changes: 10 additions & 0 deletions src/services/githits-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ describe("GitHitsServiceImpl", () => {
expect(body.license_mode).toBe("yolo");
});

it("omits language from JSON when not provided", async () => {
const fn = mockFetch(() => Promise.resolve(new Response("result")));

await service.search({ query: "test" });

const call = fn.mock.calls[0] as unknown as [string, RequestInit];
const body = JSON.parse(call[1].body as string);
expect(body).not.toHaveProperty("language");
});

it("passes include_explanation when set", async () => {
const fn = mockFetch(() => Promise.resolve(new Response("result")));

Expand Down
Loading
Loading