From 8aa5c8a271b3ed0beb5e6e91e43a284ba78e4577 Mon Sep 17 00:00:00 2001 From: Nathan Burg Date: Mon, 13 Apr 2026 18:54:45 -0700 Subject: [PATCH 1/3] feat(plugin): add Open Plugin and Claude marketplace assets Package Open Plugin manifests and command/skill metadata directly in githits-cli so plugin-capable agents can install from npm without a separate repository. This also adds Claude marketplace catalog metadata and installation docs for a consistent cross-agent setup flow. --- .claude-plugin/marketplace.json | 26 +++++++++++++++++++ .claude-plugin/plugin.json | 12 +++++++++ .mcp.json | 8 ++++++ .plugin/plugin.json | 12 +++++++++ README.md | 33 +++++++++++++++++++++++ commands/help.md | 46 +++++++++++++++++++++++++++++++++ commands/login.md | 36 ++++++++++++++++++++++++++ commands/logout.md | 16 ++++++++++++ commands/search.md | 23 +++++++++++++++++ commands/status.md | 22 ++++++++++++++++ package.json | 7 ++++- skills/search/SKILL.md | 36 ++++++++++++++++++++++++++ 12 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 .claude-plugin/marketplace.json create mode 100644 .claude-plugin/plugin.json create mode 100644 .mcp.json create mode 100644 .plugin/plugin.json create mode 100644 commands/help.md create mode 100644 commands/login.md create mode 100644 commands/logout.md create mode 100644 commands/search.md create mode 100644 commands/status.md create mode 100644 skills/search/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 00000000..54f147fd --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,26 @@ +{ + "name": "githits-plugins", + "owner": { + "name": "GitHits", + "email": "support@githits.com" + }, + "metadata": { + "description": "GitHits plugins for Claude Code - code examples from global open source", + "version": "0.1.5" + }, + "plugins": [ + { + "name": "githits", + "source": "./", + "description": "Code examples from global open source for developers and AI assistants", + "author": { + "name": "GitHits" + }, + "homepage": "https://githits.com", + "repository": "https://github.com/githits-com/githits-cli", + "license": "Apache-2.0", + "keywords": ["code-search", "open-source", "examples"], + "category": "developer-tools" + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 00000000..701a73ca --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "githits", + "version": "0.1.5", + "description": "Code examples from global open source for developers and AI assistants", + "author": { + "name": "GitHits" + }, + "homepage": "https://githits.com", + "repository": "https://github.com/githits-com/githits-cli", + "license": "Apache-2.0", + "keywords": ["githits", "code-examples", "search", "mcp", "ai"] +} diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 00000000..99bb85c6 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "githits": { + "command": "npx", + "args": ["-y", "githits", "mcp", "start"] + } + } +} diff --git a/.plugin/plugin.json b/.plugin/plugin.json new file mode 100644 index 00000000..701a73ca --- /dev/null +++ b/.plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "githits", + "version": "0.1.5", + "description": "Code examples from global open source for developers and AI assistants", + "author": { + "name": "GitHits" + }, + "homepage": "https://githits.com", + "repository": "https://github.com/githits-com/githits-cli", + "license": "Apache-2.0", + "keywords": ["githits", "code-examples", "search", "mcp", "ai"] +} diff --git a/README.md b/README.md index 751e5743..b4dacd46 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,14 @@ npx githits login This opens your browser to authenticate with your [GitHits](https://githits.com) account. Once logged in, set up your AI assistant: +### MCP Setup (works in all agents) + +Use this server command in MCP configuration files: + +```sh +npx -y githits mcp start +``` + **Claude Code** ```sh @@ -33,6 +41,31 @@ Add to your MCP settings JSON: } ``` +### Plugin Installation (Open Plugin standard) + +The npm package includes Open Plugin-compatible files: + +- `.plugin/plugin.json` (vendor-neutral, used by Cursor/Codex/Copilot-compatible hosts) +- `.claude-plugin/plugin.json` (Claude Code compatibility) +- `.claude-plugin/marketplace.json` (Claude Code marketplace catalog) +- `.mcp.json`, `skills/`, and `commands/` + +**Claude Code marketplace install** + +```sh +/plugin marketplace add githits-com/githits-cli +/plugin install githits@githits-plugins +``` + +For plugin-based hosts, install from npm/GitHub using your agent's plugin workflow and enable plugin `githits`. + +### Agent Coverage + +- **Cursor**: reads vendor-neutral `.plugin/` for Open Plugin installs +- **Claude Code**: supports `.claude-plugin/` and Open Plugin components +- **Codex**: supports Open Plugin components +- **GitHub Copilot**: supports Open Plugin components + That's it. Your assistant now has a `search` tool it will use automatically when it needs code examples. ## How It Works diff --git a/commands/help.md b/commands/help.md new file mode 100644 index 00000000..18ba741d --- /dev/null +++ b/commands/help.md @@ -0,0 +1,46 @@ +--- +description: Show available GitHits commands and usage +disable-model-invocation: true +--- + +# GitHits Help + +Run the GitHits CLI help command in the terminal: + +``` +npx -y githits help +``` + +Then display the command output clearly to the user, followed by this plugin +context summary: + +## Slash Commands + +- `/githits:search ` — Search for code examples from open source. +- `/githits:login` — Authenticate with your GitHits account. +- `/githits:status` — Show your current authentication status. +- `/githits:logout` — Remove stored credentials. +- `/githits:help` — Show this help message. + +## MCP Tools + +This plugin connects to the GitHits MCP server and exposes three tools: + +- **search** — 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. +- **feedback** — Rate a search result to improve future quality. + +## Authentication + +Run `npx -y githits login` to authenticate via browser, or set the +`GITHITS_API_TOKEN` environment variable for headless environments. + +If users want to verify MCP tools loaded, suggest `/mcp`. + +If the command fails, report the error and suggest running: + +``` +npx -y githits login +``` diff --git a/commands/login.md b/commands/login.md new file mode 100644 index 00000000..f3d4f0f2 --- /dev/null +++ b/commands/login.md @@ -0,0 +1,36 @@ +--- +description: Log in to your GitHits account +--- + +# Login + +Authenticate the user with GitHits by running the CLI login command in the +terminal: + +``` +npx -y githits login +``` + +This opens the user's browser for secure OAuth authentication. Tokens are stored +locally and refreshed automatically. + +If the environment has no display (SSH, CI, containers), use the `--no-browser` +flag instead: + +``` +npx -y githits login --no-browser +``` + +This prints a URL the user can open on another device. + +Other useful flags: + +- `--force` — re-authenticate even if already logged in. +- `--port ` — use a specific port for the local callback server. + +After running the command, inform the user of the result. If login succeeds, +confirm they are authenticated. If it fails, provide the error and suggest they +try again. + +Alternative: the user can set the `GITHITS_API_TOKEN` environment variable +instead of using browser login. diff --git a/commands/logout.md b/commands/logout.md new file mode 100644 index 00000000..94ce4443 --- /dev/null +++ b/commands/logout.md @@ -0,0 +1,16 @@ +--- +description: Log out of your GitHits account +--- + +# Logout + +Sign the user out of GitHits by running the CLI logout command in the terminal: + +``` +npx -y githits logout +``` + +This removes the locally stored authentication tokens. + +Confirm to the user that they have been logged out successfully. If the logout +fails, provide the error details. diff --git a/commands/search.md b/commands/search.md new file mode 100644 index 00000000..833acb7f --- /dev/null +++ b/commands/search.md @@ -0,0 +1,23 @@ +--- +description: Search for code examples from open source via GitHits +--- + +# Search + +Search for code examples using GitHits for the query: "$ARGUMENTS" + +Use the GitHits MCP `search` tool with the user's query. The tool requires two +parameters: + +- **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: + +- **license_mode**: `"strict"` (default, excludes copyleft), `"yolo"` (all + licenses), or `"custom"` (user's blocklist). + +Present the results clearly. After the user has reviewed the result, use the +`feedback` tool to report whether the example was helpful. diff --git a/commands/status.md b/commands/status.md new file mode 100644 index 00000000..2f207c9c --- /dev/null +++ b/commands/status.md @@ -0,0 +1,22 @@ +--- +description: Show your GitHits authentication status +--- + +# Status + +Check the user's current GitHits authentication status by running the CLI +command in the terminal: + +``` +npx -y githits auth status +``` + +This shows whether the user is authenticated, where credentials are sourced +from, and token expiry details when available. + +After running the command, report the status clearly. If the user is not +authenticated, suggest running: + +``` +npx -y githits login +``` diff --git a/package.json b/package.json index f07ef1cd..23402fbe 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,12 @@ "type": "module", "files": [ "dist", - "LICENSE" + "LICENSE", + ".plugin", + ".claude-plugin", + ".mcp.json", + "skills", + "commands" ], "module": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/skills/search/SKILL.md b/skills/search/SKILL.md new file mode 100644 index 00000000..53b28d1b --- /dev/null +++ b/skills/search/SKILL.md @@ -0,0 +1,36 @@ +--- +name: search +description: + Use GitHits MCP tools to find real-world code examples when model knowledge + is insufficient. +--- + +Use GitHits when: + +- You are blocked, uncertain about an API, or need up-to-date OSS usage. +- You have attempted a solution twice and it still fails - search for a working example before trying again. +- The user asks to search for examples or explicitly mentions GitHits. +- You are implementing non-trivial code in languages where confidence is lower. + +Authentication: + +- If any GitHits tool returns an authentication error, immediately run + `npx -y githits login` in the terminal. This opens the user's browser for + quick OAuth approval. Wait for the command to complete, then retry the + original tool call. +- If the environment has no display (SSH, containers), use + `npx -y githits login --no-browser` instead, which prints a URL for the + user to open on another device. +- Do NOT ask the user to run login manually -- handle it automatically. +- If login fails, inform the user of the error and suggest they set the + `GITHITS_API_TOKEN` environment variable as an alternative. + +Guidelines: + +- Prefer existing search context if it already answers the problem. +- Use `search_language` first when language naming is uncertain. +- Use `search` for one focused question at a time. +- After using results, send `feedback` with helpful/unhelpful outcome. + +Tool argument details and rich query guidance are provided directly in the MCP +tool descriptions; follow those descriptions as the source of truth. From d8c219619b20fbe8e7c1833b536e4b4c6f3d16df Mon Sep 17 00:00:00 2001 From: Nathan Burg Date: Mon, 13 Apr 2026 19:21:20 -0700 Subject: [PATCH 2/3] feat(plugin): add Gemini CLI extension support Consolidate Gemini extension assets into the main githits-cli repository so Gemini CLI can install directly from this repo. This keeps extension metadata and context content versioned with the published CLI package. --- GEMINI.md | 75 +++++++++++++++++++++++++++++++++++++++++++ README.md | 7 ++++ gemini-extension.json | 12 +++++++ package.json | 2 ++ 4 files changed, 96 insertions(+) create mode 100644 GEMINI.md create mode 100644 gemini-extension.json diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 00000000..572b68fc --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,75 @@ +# GitHits + +Code examples from global open source for developers and AI assistants. + +## Available Tools + +### search + +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) +- `license_mode` (string, optional) - one of `strict` (default), `yolo`, or `custom` + +### search_language + +Look up supported programming language names. Use this before calling `search` to get the correct language identifier. + +**Parameters:** + +- `query` (string, required) - partial or full language name to look up + +### feedback + +Rate a search result. Submit feedback after each search to improve future result quality. + +**Parameters:** + +- `solution_id` (string, required) - the ID of the search result +- `accepted` (boolean, required) - whether the result was useful +- `feedback_text` (string, optional) - additional context about why the result was or was not helpful + +## When to Use + +Use `search` when: + +- You are stuck or blocked on an implementation problem +- You need up-to-date examples for an API, library, or framework +- The user mentions GitHits or asks you to search for code examples +- You encounter an error you cannot resolve from your training data + +Do not use `search` for: + +- General knowledge questions that do not require code examples +- Problems you can already solve confidently + +## Authentication + +- If any GitHits tool returns an authentication error, immediately run + `npx -y githits login` in the terminal. This opens the user's browser for + quick OAuth approval. Wait for the command to complete, then retry the + original tool call. +- If the environment has no display (SSH, containers), use + `npx -y githits login --no-browser` instead, which prints a URL for the + user to open on another device. +- Do NOT ask the user to run login manually - handle it automatically. +- If login fails, inform the user of the error. + +## How to Search Well + +- Call `search_language` first to confirm the correct language name before calling `search` +- 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 +- Submit `feedback` after every search result you use or discard + +## License Filtering + +Results respect license filtering by default. Three modes: + +- **strict** (default) - excludes copyleft licenses +- **yolo** - includes all licenses +- **custom** - uses the user's blocklist configured at githits.com diff --git a/README.md b/README.md index b4dacd46..70cf1750 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,12 @@ The npm package includes Open Plugin-compatible files: /plugin install githits@githits-plugins ``` +**Gemini CLI extension install** + +```sh +gemini extensions install https://github.com/githits-com/githits-cli +``` + For plugin-based hosts, install from npm/GitHub using your agent's plugin workflow and enable plugin `githits`. ### Agent Coverage @@ -65,6 +71,7 @@ For plugin-based hosts, install from npm/GitHub using your agent's plugin workfl - **Claude Code**: supports `.claude-plugin/` and Open Plugin components - **Codex**: supports Open Plugin components - **GitHub Copilot**: supports Open Plugin components +- **Gemini CLI**: supports `gemini-extension.json` and `GEMINI.md` That's it. Your assistant now has a `search` tool it will use automatically when it needs code examples. diff --git a/gemini-extension.json b/gemini-extension.json new file mode 100644 index 00000000..e0a4b7cb --- /dev/null +++ b/gemini-extension.json @@ -0,0 +1,12 @@ +{ + "name": "githits", + "version": "0.1.5", + "description": "Code examples from global open source for developers and AI assistants.", + "mcpServers": { + "githits": { + "command": "npx", + "args": ["-y", "githits", "mcp", "start"] + } + }, + "contextFileName": "GEMINI.md" +} diff --git a/package.json b/package.json index 23402fbe..6e99f902 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ ".plugin", ".claude-plugin", ".mcp.json", + "gemini-extension.json", + "GEMINI.md", "skills", "commands" ], From c6f2714275873b1f26dc6497b522d6bd2c615a49 Mon Sep 17 00:00:00 2001 From: Nathan Burg Date: Fri, 17 Apr 2026 16:53:03 -0700 Subject: [PATCH 3/3] feat(plugins): unify Claude and Gemini init with latest MCP runtime --- .claude-plugin/marketplace.json | 4 +- .claude-plugin/plugin.json | 2 +- .mcp.json | 2 +- .plugin/plugin.json | 2 +- README.md | 42 ++++++++++- docs/implementation/plugin-packaging.md | 84 +++++++++++++++++++++ gemini-extension.json | 4 +- package.json | 3 +- plugins/claude/.claude-plugin/plugin.json | 12 +++ plugins/claude/.mcp.json | 8 ++ plugins/claude/commands/help.md | 46 +++++++++++ plugins/claude/commands/login.md | 36 +++++++++ plugins/claude/commands/logout.md | 16 ++++ plugins/claude/commands/search.md | 23 ++++++ plugins/claude/commands/status.md | 22 ++++++ plugins/claude/skills/search/SKILL.md | 36 +++++++++ src/commands/init/agent-definitions.test.ts | 8 +- src/commands/init/agent-definitions.ts | 9 +-- src/commands/init/setup-handlers.test.ts | 16 +--- src/plugin-config.test.ts | 47 ++++++++++++ src/plugin-manifest.test.ts | 24 ++++++ src/plugin-version-consistency.test.ts | 46 +++++++++++ 22 files changed, 455 insertions(+), 37 deletions(-) create mode 100644 docs/implementation/plugin-packaging.md create mode 100644 plugins/claude/.claude-plugin/plugin.json create mode 100644 plugins/claude/.mcp.json create mode 100644 plugins/claude/commands/help.md create mode 100644 plugins/claude/commands/login.md create mode 100644 plugins/claude/commands/logout.md create mode 100644 plugins/claude/commands/search.md create mode 100644 plugins/claude/commands/status.md create mode 100644 plugins/claude/skills/search/SKILL.md create mode 100644 src/plugin-config.test.ts create mode 100644 src/plugin-manifest.test.ts create mode 100644 src/plugin-version-consistency.test.ts diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 54f147fd..ad9bd292 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,12 +6,12 @@ }, "metadata": { "description": "GitHits plugins for Claude Code - code examples from global open source", - "version": "0.1.5" + "version": "0.1.8" }, "plugins": [ { "name": "githits", - "source": "./", + "source": "./plugins/claude", "description": "Code examples from global open source for developers and AI assistants", "author": { "name": "GitHits" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 701a73ca..0a456fb2 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "githits", - "version": "0.1.5", + "version": "0.1.8", "description": "Code examples from global open source for developers and AI assistants", "author": { "name": "GitHits" diff --git a/.mcp.json b/.mcp.json index 99bb85c6..13a905cc 100644 --- a/.mcp.json +++ b/.mcp.json @@ -2,7 +2,7 @@ "mcpServers": { "githits": { "command": "npx", - "args": ["-y", "githits", "mcp", "start"] + "args": ["-y", "githits@latest", "mcp", "start"] } } } diff --git a/.plugin/plugin.json b/.plugin/plugin.json index 701a73ca..0a456fb2 100644 --- a/.plugin/plugin.json +++ b/.plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "githits", - "version": "0.1.5", + "version": "0.1.8", "description": "Code examples from global open source for developers and AI assistants", "author": { "name": "GitHits" diff --git a/README.md b/README.md index 8437b604..83a34605 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,49 @@ The npm package includes Open Plugin-compatible files: - `.plugin/plugin.json` (vendor-neutral, used by Cursor/Codex/Copilot-compatible hosts) - `.claude-plugin/plugin.json` (Claude Code compatibility) - `.claude-plugin/marketplace.json` (Claude Code marketplace catalog) -- `.mcp.json`, `skills/`, and `commands/` +- `.mcp.json` (Open Plugin MCP server config for plugin hosts) +- `plugins/claude/` (Claude plugin runtime payload: `.claude-plugin/plugin.json`, `.mcp.json`, `skills/`, and `commands/`) -**Claude Code marketplace install** +Root `.claude-plugin/marketplace.json` provides marketplace metadata. Claude Code +loads the plugin runtime payload from `plugins/claude/`. + +**Claude Code Plugin (Marketplace)** + +Install from terminal (recommended): + +```sh +claude plugin marketplace add githits-com/githits-cli +claude plugin install githits@githits-plugins +``` + +This is preferred over in-session install so the plugin is loaded cleanly on +next `claude` launch. + +Alternative (inside Claude input): ```sh /plugin marketplace add githits-com/githits-cli /plugin install githits@githits-plugins ``` +If installed inside a running Claude session, reload/restart Claude if the +plugin is not immediately available. + +For unpublished/local testing of this repository: + +```sh +claude plugin marketplace add "$PWD" +claude plugin install githits@githits-plugins +``` + +By default, the plugin starts MCP with `npx -y githits@latest mcp start` so installs track the latest published GitHits CLI. + +For unpublished/local testing, install from your local repository path and verify behavior in your host before publishing. + +In Claude Code, run `/mcp` and confirm `plugin:githits:githits` is listed for the plugin path. + +Note: when running Claude in this repository directory, root `.mcp.json` can also register `githits` for project-level MCP. For plugin-only attribution during testing, run Claude from a different working directory. + **Gemini CLI extension install** ```sh @@ -122,7 +156,7 @@ If your tool is not in the supported `githits init` list, configure GitHits manu Use this MCP server command in your tool's MCP config (the host/agent runs this command): ```sh -npx -y githits mcp start +npx -y githits@latest mcp start ``` A typical MCP config looks like this (check your tool's docs for exact schema/key names): @@ -132,7 +166,7 @@ A typical MCP config looks like this (check your tool's docs for exact schema/ke "mcpServers": { "githits": { "command": "npx", - "args": ["-y", "githits", "mcp", "start"] + "args": ["-y", "githits@latest", "mcp", "start"] } } } diff --git a/docs/implementation/plugin-packaging.md b/docs/implementation/plugin-packaging.md new file mode 100644 index 00000000..449e4085 --- /dev/null +++ b/docs/implementation/plugin-packaging.md @@ -0,0 +1,84 @@ +# Plugin Packaging + +## Purpose + +This document defines how GitHits is packaged for Open Plugin hosts and Claude +Code marketplace installs, and explains why both root and Claude-specific MCP +config files exist. + +## Background + +GitHits now ships two plugin entry paths: + +- **Root Open Plugin package** for hosts that read `.plugin/` + root + `.mcp.json`. +- **Claude marketplace payload** for Claude Code installs via + `.claude-plugin/marketplace.json`. + +Earlier cutover work made root `.mcp.json` empty to avoid duplicate MCP server +visibility while testing Claude in this repo. That broke Open Plugin MCP +packaging expectations. The corrected contract keeps Open Plugin config at root +and keeps Claude payload self-contained under `plugins/claude/`. + +## Packaging Contract + +### Root Open Plugin package (canonical for Open Plugin hosts) + +Required components at repo root: + +- `.plugin/plugin.json` +- `.mcp.json` with `mcpServers.githits` +- `skills/` +- `commands/` + +Root `.mcp.json` is intentional and required for Open Plugin hosts. +Both root and Claude payload MCP configs launch with +`npx -y githits@latest mcp start` so plugin installs track the latest published +GitHits CLI by default. + +### Claude marketplace package + +Claude marketplace metadata lives at `.claude-plugin/marketplace.json`. +Marketplace plugin source is `./plugins/claude`, which contains: + +- `plugins/claude/.claude-plugin/plugin.json` +- `plugins/claude/.mcp.json` with `mcpServers.githits` +- `plugins/claude/skills/` +- `plugins/claude/commands/` + +This keeps Claude runtime payload explicit and marketplace-scoped. + +## Runtime Behavior in Local Development + +When running Claude from this repository directory: + +- plugin server appears as `plugin:githits:githits` +- root project `.mcp.json` can also register `githits` + +This dual visibility is a local testing artifact, not an `init` behavior bug. +For plugin-only attribution tests, run Claude from a different working +directory. + +## Verification + +Automated checks: + +- `src/plugin-config.test.ts` asserts root and Claude payload MCP server config +- `src/plugin-manifest.test.ts` asserts Claude marketplace source points to + `./plugins/claude` + +Manual checks: + +- Open Plugin host install reads root package MCP config +- Claude marketplace install loads `plugin:githits:githits` + +## Key Reference Files + +| File | Purpose | +|---|---| +| `.mcp.json` | Root Open Plugin MCP server registration | +| `.plugin/plugin.json` | Open Plugin manifest | +| `.claude-plugin/marketplace.json` | Claude marketplace catalog and source path | +| `plugins/claude/.mcp.json` | Claude payload MCP server registration | +| `src/plugin-config.test.ts` | MCP packaging regression checks | +| `src/plugin-manifest.test.ts` | Marketplace source path regression check | diff --git a/gemini-extension.json b/gemini-extension.json index e0a4b7cb..d2b7cfd5 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,11 +1,11 @@ { "name": "githits", - "version": "0.1.5", + "version": "0.1.8", "description": "Code examples from global open source for developers and AI assistants.", "mcpServers": { "githits": { "command": "npx", - "args": ["-y", "githits", "mcp", "start"] + "args": ["-y", "githits@latest", "mcp", "start"] } }, "contextFileName": "GEMINI.md" diff --git a/package.json b/package.json index f3265245..b7616f5c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "githits", "description": "CLI companion for GitHits - code examples from global open source for developers and AI assistants", - "version": "0.1.7", + "version": "0.1.8", "type": "module", "files": [ "dist", @@ -11,6 +11,7 @@ ".mcp.json", "gemini-extension.json", "GEMINI.md", + "plugins", "skills", "commands" ], diff --git a/plugins/claude/.claude-plugin/plugin.json b/plugins/claude/.claude-plugin/plugin.json new file mode 100644 index 00000000..0a456fb2 --- /dev/null +++ b/plugins/claude/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "githits", + "version": "0.1.8", + "description": "Code examples from global open source for developers and AI assistants", + "author": { + "name": "GitHits" + }, + "homepage": "https://githits.com", + "repository": "https://github.com/githits-com/githits-cli", + "license": "Apache-2.0", + "keywords": ["githits", "code-examples", "search", "mcp", "ai"] +} diff --git a/plugins/claude/.mcp.json b/plugins/claude/.mcp.json new file mode 100644 index 00000000..13a905cc --- /dev/null +++ b/plugins/claude/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "githits": { + "command": "npx", + "args": ["-y", "githits@latest", "mcp", "start"] + } + } +} diff --git a/plugins/claude/commands/help.md b/plugins/claude/commands/help.md new file mode 100644 index 00000000..18ba741d --- /dev/null +++ b/plugins/claude/commands/help.md @@ -0,0 +1,46 @@ +--- +description: Show available GitHits commands and usage +disable-model-invocation: true +--- + +# GitHits Help + +Run the GitHits CLI help command in the terminal: + +``` +npx -y githits help +``` + +Then display the command output clearly to the user, followed by this plugin +context summary: + +## Slash Commands + +- `/githits:search ` — Search for code examples from open source. +- `/githits:login` — Authenticate with your GitHits account. +- `/githits:status` — Show your current authentication status. +- `/githits:logout` — Remove stored credentials. +- `/githits:help` — Show this help message. + +## MCP Tools + +This plugin connects to the GitHits MCP server and exposes three tools: + +- **search** — 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. +- **feedback** — Rate a search result to improve future quality. + +## Authentication + +Run `npx -y githits login` to authenticate via browser, or set the +`GITHITS_API_TOKEN` environment variable for headless environments. + +If users want to verify MCP tools loaded, suggest `/mcp`. + +If the command fails, report the error and suggest running: + +``` +npx -y githits login +``` diff --git a/plugins/claude/commands/login.md b/plugins/claude/commands/login.md new file mode 100644 index 00000000..f3d4f0f2 --- /dev/null +++ b/plugins/claude/commands/login.md @@ -0,0 +1,36 @@ +--- +description: Log in to your GitHits account +--- + +# Login + +Authenticate the user with GitHits by running the CLI login command in the +terminal: + +``` +npx -y githits login +``` + +This opens the user's browser for secure OAuth authentication. Tokens are stored +locally and refreshed automatically. + +If the environment has no display (SSH, CI, containers), use the `--no-browser` +flag instead: + +``` +npx -y githits login --no-browser +``` + +This prints a URL the user can open on another device. + +Other useful flags: + +- `--force` — re-authenticate even if already logged in. +- `--port ` — use a specific port for the local callback server. + +After running the command, inform the user of the result. If login succeeds, +confirm they are authenticated. If it fails, provide the error and suggest they +try again. + +Alternative: the user can set the `GITHITS_API_TOKEN` environment variable +instead of using browser login. diff --git a/plugins/claude/commands/logout.md b/plugins/claude/commands/logout.md new file mode 100644 index 00000000..94ce4443 --- /dev/null +++ b/plugins/claude/commands/logout.md @@ -0,0 +1,16 @@ +--- +description: Log out of your GitHits account +--- + +# Logout + +Sign the user out of GitHits by running the CLI logout command in the terminal: + +``` +npx -y githits logout +``` + +This removes the locally stored authentication tokens. + +Confirm to the user that they have been logged out successfully. If the logout +fails, provide the error details. diff --git a/plugins/claude/commands/search.md b/plugins/claude/commands/search.md new file mode 100644 index 00000000..833acb7f --- /dev/null +++ b/plugins/claude/commands/search.md @@ -0,0 +1,23 @@ +--- +description: Search for code examples from open source via GitHits +--- + +# Search + +Search for code examples using GitHits for the query: "$ARGUMENTS" + +Use the GitHits MCP `search` tool with the user's query. The tool requires two +parameters: + +- **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: + +- **license_mode**: `"strict"` (default, excludes copyleft), `"yolo"` (all + licenses), or `"custom"` (user's blocklist). + +Present the results clearly. After the user has reviewed the result, use the +`feedback` tool to report whether the example was helpful. diff --git a/plugins/claude/commands/status.md b/plugins/claude/commands/status.md new file mode 100644 index 00000000..2f207c9c --- /dev/null +++ b/plugins/claude/commands/status.md @@ -0,0 +1,22 @@ +--- +description: Show your GitHits authentication status +--- + +# Status + +Check the user's current GitHits authentication status by running the CLI +command in the terminal: + +``` +npx -y githits auth status +``` + +This shows whether the user is authenticated, where credentials are sourced +from, and token expiry details when available. + +After running the command, report the status clearly. If the user is not +authenticated, suggest running: + +``` +npx -y githits login +``` diff --git a/plugins/claude/skills/search/SKILL.md b/plugins/claude/skills/search/SKILL.md new file mode 100644 index 00000000..53b28d1b --- /dev/null +++ b/plugins/claude/skills/search/SKILL.md @@ -0,0 +1,36 @@ +--- +name: search +description: + Use GitHits MCP tools to find real-world code examples when model knowledge + is insufficient. +--- + +Use GitHits when: + +- You are blocked, uncertain about an API, or need up-to-date OSS usage. +- You have attempted a solution twice and it still fails - search for a working example before trying again. +- The user asks to search for examples or explicitly mentions GitHits. +- You are implementing non-trivial code in languages where confidence is lower. + +Authentication: + +- If any GitHits tool returns an authentication error, immediately run + `npx -y githits login` in the terminal. This opens the user's browser for + quick OAuth approval. Wait for the command to complete, then retry the + original tool call. +- If the environment has no display (SSH, containers), use + `npx -y githits login --no-browser` instead, which prints a URL for the + user to open on another device. +- Do NOT ask the user to run login manually -- handle it automatically. +- If login fails, inform the user of the error and suggest they set the + `GITHITS_API_TOKEN` environment variable as an alternative. + +Guidelines: + +- Prefer existing search context if it already answers the problem. +- Use `search_language` first when language naming is uncertain. +- Use `search` for one focused question at a time. +- After using results, send `feedback` with helpful/unhelpful outcome. + +Tool argument details and rich query guidance are provided directly in the MCP +tool descriptions; follow those descriptions as the source of truth. diff --git a/src/commands/init/agent-definitions.test.ts b/src/commands/init/agent-definitions.test.ts index e368e625..b13671b8 100644 --- a/src/commands/init/agent-definitions.test.ts +++ b/src/commands/init/agent-definitions.test.ts @@ -359,9 +359,7 @@ describe("getSetupConfig", () => { expect(config.commands[0]!.args).toContain("plugin"); expect(config.commands[0]!.args).toContain("marketplace"); expect(config.commands[0]!.args).toContain("add"); - expect(config.commands[0]!.args).toContain( - "githits-com/githits-claude-code-plugin", - ); + expect(config.commands[0]!.args).toContain("githits-com/githits-cli"); expect(config.commands[1]!.command).toBe("claude"); expect(config.commands[1]!.args).toContain("plugin"); expect(config.commands[1]!.args).toContain("install"); @@ -621,7 +619,7 @@ describe("getSetupConfig", () => { expect(config.commands[0]!.args).toContain("extensions"); expect(config.commands[0]!.args).toContain("install"); expect(config.commands[0]!.args).toContain( - "https://github.com/githits-com/githits-gemini-cli", + "https://github.com/githits-com/githits-cli", ); } }); @@ -1150,7 +1148,7 @@ describe("scanAgents", () => { }, "gemini extensions list": { exitCode: 0, - stdout: "githits-gemini-cli\n", + stdout: "githits-cli\n", stderr: "", }, [`${whichCmd} opencode`]: { diff --git a/src/commands/init/agent-definitions.ts b/src/commands/init/agent-definitions.ts index a7712206..66b05890 100644 --- a/src/commands/init/agent-definitions.ts +++ b/src/commands/init/agent-definitions.ts @@ -119,12 +119,7 @@ const claudeCode: AgentDefinition = { commands: [ { command: "claude", - args: [ - "plugin", - "marketplace", - "add", - "githits-com/githits-claude-code-plugin", - ], + args: ["plugin", "marketplace", "add", "githits-com/githits-cli"], }, { command: "claude", @@ -306,7 +301,7 @@ const geminiCli: AgentDefinition = { args: [ "extensions", "install", - "https://github.com/githits-com/githits-gemini-cli", + "https://github.com/githits-com/githits-cli", ], }, ], diff --git a/src/commands/init/setup-handlers.test.ts b/src/commands/init/setup-handlers.test.ts index 87cab998..d6da6864 100644 --- a/src/commands/init/setup-handlers.test.ts +++ b/src/commands/init/setup-handlers.test.ts @@ -393,12 +393,7 @@ describe("formatSetupPreview", () => { commands: [ { command: "claude", - args: [ - "plugin", - "marketplace", - "add", - "githits-com/githits-claude-code-plugin", - ], + args: ["plugin", "marketplace", "add", "githits-com/githits-cli"], }, { command: "claude", @@ -408,7 +403,7 @@ describe("formatSetupPreview", () => { }; const preview = formatSetupPreview(setup); expect(preview).toContain( - "Will run: claude plugin marketplace add githits-com/githits-claude-code-plugin", + "Will run: claude plugin marketplace add githits-com/githits-cli", ); expect(preview).toContain( "Will run: claude plugin install githits@githits-plugins", @@ -444,12 +439,7 @@ describe("executeCliSetup", () => { commands: [ { command: "claude", - args: [ - "plugin", - "marketplace", - "add", - "githits-com/githits-claude-code-plugin", - ], + args: ["plugin", "marketplace", "add", "githits-com/githits-cli"], }, { command: "claude", diff --git a/src/plugin-config.test.ts b/src/plugin-config.test.ts new file mode 100644 index 00000000..092052bb --- /dev/null +++ b/src/plugin-config.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from "bun:test"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +describe("plugin MCP configuration", () => { + it("defines githits MCP server in root Open Plugin package", async () => { + const rootMcpPath = join(import.meta.dir, "..", ".mcp.json"); + const contents = await readFile(rootMcpPath, "utf8"); + const parsed = JSON.parse(contents) as { + mcpServers?: Record; + }; + + expect(parsed.mcpServers).toBeDefined(); + expect(parsed.mcpServers?.githits).toBeDefined(); + expect(parsed.mcpServers?.githits?.command).toBe("npx"); + expect(parsed.mcpServers?.githits?.args).toEqual([ + "-y", + "githits@latest", + "mcp", + "start", + ]); + }); + + it("defines githits MCP server in Claude plugin payload", async () => { + const pluginMcpPath = join( + import.meta.dir, + "..", + "plugins", + "claude", + ".mcp.json", + ); + const contents = await readFile(pluginMcpPath, "utf8"); + const parsed = JSON.parse(contents) as { + mcpServers?: Record; + }; + + expect(parsed.mcpServers).toBeDefined(); + expect(parsed.mcpServers?.githits).toBeDefined(); + expect(parsed.mcpServers?.githits?.command).toBe("npx"); + expect(parsed.mcpServers?.githits?.args).toEqual([ + "-y", + "githits@latest", + "mcp", + "start", + ]); + }); +}); diff --git a/src/plugin-manifest.test.ts b/src/plugin-manifest.test.ts new file mode 100644 index 00000000..9c3bd0fe --- /dev/null +++ b/src/plugin-manifest.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "bun:test"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +describe("plugin manifest wiring", () => { + it("points Claude marketplace plugin source to plugins/claude payload", async () => { + const marketplacePath = join( + import.meta.dir, + "..", + ".claude-plugin", + "marketplace.json", + ); + const contents = await readFile(marketplacePath, "utf8"); + const parsed = JSON.parse(contents) as { + plugins?: Array<{ name?: string; source?: string }>; + }; + + const githitsPlugin = parsed.plugins?.find( + (plugin) => plugin.name === "githits", + ); + expect(githitsPlugin).toBeDefined(); + expect(githitsPlugin?.source).toBe("./plugins/claude"); + }); +}); diff --git a/src/plugin-version-consistency.test.ts b/src/plugin-version-consistency.test.ts new file mode 100644 index 00000000..b7b95a68 --- /dev/null +++ b/src/plugin-version-consistency.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from "bun:test"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +async function readJson(path: string): Promise { + const contents = await readFile(path, "utf8"); + return JSON.parse(contents) as T; +} + +describe("plugin version consistency", () => { + it("keeps package and plugin manifest versions aligned", async () => { + const root = join(import.meta.dir, ".."); + + const packageJson = await readJson<{ version: string }>( + join(root, "package.json"), + ); + + const openPlugin = await readJson<{ version: string }>( + join(root, ".plugin", "plugin.json"), + ); + + const claudePlugin = await readJson<{ version: string }>( + join(root, ".claude-plugin", "plugin.json"), + ); + + const claudePayloadPlugin = await readJson<{ version: string }>( + join(root, "plugins", "claude", ".claude-plugin", "plugin.json"), + ); + + const claudeMarketplace = await readJson<{ + metadata?: { version?: string }; + }>(join(root, ".claude-plugin", "marketplace.json")); + + const geminiExtension = await readJson<{ version: string }>( + join(root, "gemini-extension.json"), + ); + + const expected = packageJson.version; + + expect(openPlugin.version).toBe(expected); + expect(claudePlugin.version).toBe(expected); + expect(claudePayloadPlugin.version).toBe(expected); + expect(claudeMarketplace.metadata?.version).toBe(expected); + expect(geminiExtension.version).toBe(expected); + }); +});