From 65d8ea4605cb04191415a8119695e25b23c0d1d6 Mon Sep 17 00:00:00 2001 From: Juha Litola Date: Sat, 15 Aug 2026 10:27:02 +0300 Subject: [PATCH 1/4] feat: support standalone site target resolution Accept site as a resolve target preference and render standalone documentation-site candidates as a known target kind.\n\nUpdate CLI guidance and release metadata for the backend compatibility rollout. --- README.md | 2 +- ...ite-target-client-compatibility.changed.md | 7 ++++++ docs/implementation/cli-commands.md | 11 +++++---- .../src/services/resolve-target-service.ts | 2 +- .../src/shared/resolve-target-request.test.ts | 20 ++++++++++++++-- .../mcp/src/shared/resolve-target-request.ts | 3 ++- .../shared/resolve-target-response.test.ts | 23 +++++++++++++++++++ .../mcp/src/shared/resolve-target-response.ts | 2 +- src/cli.ts | 2 +- src/commands/resolve.test.ts | 1 + src/commands/resolve.ts | 14 +++++++---- 11 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 changes/site-target-client-compatibility.changed.md diff --git a/README.md b/README.md index f34f963..44075cc 100644 --- a/README.md +++ b/README.md @@ -350,7 +350,7 @@ githits example Find real-world implementations from open source githits languages List or filter supported programming languages githits feedback Submit feedback about GitHits results githits doctor Diagnose configuration and auth state -githits resolve Resolve a package or GitHub repository name to canonical targets +githits resolve Resolve a package, repository, or documentation-site name githits settings View and update preferences, privacy, and terms githits search Explore repository code, dependencies, docs, and symbols githits search-status Check the status of a previous indexed search diff --git a/changes/site-target-client-compatibility.changed.md b/changes/site-target-client-compatibility.changed.md new file mode 100644 index 0000000..a3f4f68 --- /dev/null +++ b/changes/site-target-client-compatibility.changed.md @@ -0,0 +1,7 @@ +--- +"githits": patch +"@githits/mcp": none +--- + +- **Resolve standalone documentation sites** - `githits resolve` accepts `site` + as a soft target-kind preference and renders site candidates explicitly. diff --git a/docs/implementation/cli-commands.md b/docs/implementation/cli-commands.md index 36d56be..f58744e 100644 --- a/docs/implementation/cli-commands.md +++ b/docs/implementation/cli-commands.md @@ -17,7 +17,7 @@ The CLI exposes setup/auth commands, `doctor`, `example`, `languages`, `feedback | `languages [query]` | — | `--json` | List or filter supported languages | | `feedback [solution_id]` | `--accept` or `--reject` | `-m, --message `, `--tool `, `--json` | Submit solution-tied or generic session feedback | | `doctor` | — | `--json` | Print redacted diagnostics for GitHits runtime, environment, service URLs, config, and auth storage | -| `resolve ` | package or GitHub repository name | `--query`, `--registry`, `--prefer-kind`, repeatable `--intent-hint`, `--limit`, `--json` | Resolve a human-provided name to ranked concrete targets for follow-up commands | +| `resolve ` | package, GitHub repository, or standalone documentation-site name | `--query`, `--registry`, `--prefer-kind`, repeatable `--intent-hint`, `--limit`, `--json` | Resolve a human-provided name to ranked concrete targets for follow-up commands | | `settings` | — | `--json` | Show canonical preferences, privacy and terms, and account limits | | `settings show` | — | `--json` | Explicit form of `settings` for showing all account settings | | `settings get ` | setting key | `--json` | Read one writable setting using its public CLI name | @@ -241,12 +241,15 @@ Prints redacted diagnostics for comparing GitHits behavior across terminals or a ```text githits resolve express githits resolve codex --prefer-kind repository +githits resolve "Apple human interface guidelines" --prefer-kind site githits resolve guava --registry maven --limit 3 githits resolve "pi agent" --query "coding agent CLI" --json ``` -Resolves a human-provided package or GitHub repository name to ranked canonical -targets such as `npm:express` or `github:openai/codex`. The default output is a +Resolves a human-provided package, GitHub repository, or standalone +documentation-site name to ranked canonical targets such as `npm:express`, +`github:openai/codex`, or +`site:developer.apple.com/design/human-interface-guidelines`. The default output is a compact numbered `Candidates` list with ambiguity guidance when needed and protected exact-name matches annotated inline. It does not label any terminal candidate as best or top. Ranked candidates include their available normalized @@ -268,7 +271,7 @@ candidates, so the terminal no-result message and exit status key off `best`. `--registry` accepts a comma-separated package-registry list and the command help enumerates every accepted value; repository candidates remain eligible. -`--prefer-kind package|repository` is a soft preference, not a filter. +`--prefer-kind package|repository|site` is a soft preference, not a filter. `--intent-hint` is repeatable. `--limit` controls the ranked list from 1-20 (default 8); protected exact-name matches can be additional. `--query` and `--intent-hint` are sent to the service as ranking context and must not contain diff --git a/packages/core-internal/src/services/resolve-target-service.ts b/packages/core-internal/src/services/resolve-target-service.ts index 8547e4f..36d912f 100644 --- a/packages/core-internal/src/services/resolve-target-service.ts +++ b/packages/core-internal/src/services/resolve-target-service.ts @@ -17,7 +17,7 @@ import { } from "./package-intelligence-service.js"; import type { TokenProvider } from "./token-provider.js"; -export type ResolveTargetKind = "PACKAGE" | "REPOSITORY"; +export type ResolveTargetKind = "PACKAGE" | "REPOSITORY" | "SITE"; export interface ResolveTargetParams { name: string; diff --git a/packages/mcp/src/shared/resolve-target-request.test.ts b/packages/mcp/src/shared/resolve-target-request.test.ts index a56ef57..24c7b54 100644 --- a/packages/mcp/src/shared/resolve-target-request.test.ts +++ b/packages/mcp/src/shared/resolve-target-request.test.ts @@ -57,6 +57,22 @@ describe("buildResolveTargetParams", () => { }); }); + it("accepts every resolver target kind", () => { + for (const [preferKind, expected] of [ + ["package", "PACKAGE"], + ["repository", "REPOSITORY"], + ["site", "SITE"], + ] as const) { + expect( + buildResolveTargetParams({ + name: "documentation", + preferKind, + includeDetailedFields: false, + }).preferredKinds, + ).toEqual([expected]); + } + }); + it("rejects empty names and unsupported enums", () => { expect(() => buildResolveTargetParams({ name: " ", includeDetailedFields: false }), @@ -78,10 +94,10 @@ describe("buildResolveTargetParams", () => { expect(() => buildResolveTargetParams({ name: "x", - preferKind: "site", + preferKind: "workspace", includeDetailedFields: false, }), - ).toThrow("prefer-kind expects package or repository"); + ).toThrow("prefer-kind expects package, repository, or site"); }); it("rejects non-integer and out-of-range limits", () => { diff --git a/packages/mcp/src/shared/resolve-target-request.ts b/packages/mcp/src/shared/resolve-target-request.ts index a2fa4fb..7f13284 100644 --- a/packages/mcp/src/shared/resolve-target-request.ts +++ b/packages/mcp/src/shared/resolve-target-request.ts @@ -82,8 +82,9 @@ function parsePreferredKind( if (!kind) return undefined; if (kind === "package") return "PACKAGE"; if (kind === "repository") return "REPOSITORY"; + if (kind === "site") return "SITE"; throw new InvalidPackageSpecError( - `prefer-kind expects package or repository. Got '${value}'.`, + `prefer-kind expects package, repository, or site. Got '${value}'.`, ); } diff --git a/packages/mcp/src/shared/resolve-target-response.test.ts b/packages/mcp/src/shared/resolve-target-response.test.ts index 5d0f2c0..6746b2c 100644 --- a/packages/mcp/src/shared/resolve-target-response.test.ts +++ b/packages/mcp/src/shared/resolve-target-response.test.ts @@ -172,6 +172,29 @@ describe("formatResolveTargetTerminal", () => { expect(output).toContain("githits search ''"); }); + it("renders standalone documentation sites as a known target kind", () => { + const site = candidate({ + kind: "SITE", + canonicalKey: + "site:developer.apple.com/design/human-interface-guidelines", + displayName: "Apple Human Interface Guidelines", + stars: undefined, + downloadsLastMonth: undefined, + documentationUrl: + "https://developer.apple.com/design/human-interface-guidelines", + codeAvailable: false, + }); + + const output = formatResolveTargetTerminal( + result({ best: site, candidates: [site], protectedMatches: [] }), + { name: "Apple human interface guidelines", useColors: false }, + ); + + expect(output).toContain( + "site:developer.apple.com/design/human-interface-guidelines [exact] · site · docs", + ); + }); + it("appends missing protected and best candidates after ranked candidates", () => { const protectedExtra = { kind: "PACKAGE", diff --git a/packages/mcp/src/shared/resolve-target-response.ts b/packages/mcp/src/shared/resolve-target-response.ts index 9b31281..2780d58 100644 --- a/packages/mcp/src/shared/resolve-target-response.ts +++ b/packages/mcp/src/shared/resolve-target-response.ts @@ -143,7 +143,7 @@ export function formatResolveTargetTerminal( } const KNOWN_CONFIDENCE_VALUES = new Set(["exact", "high", "medium", "low"]); -const KNOWN_KIND_VALUES = new Set(["package", "repository"]); +const KNOWN_KIND_VALUES = new Set(["package", "repository", "site"]); function formatCandidate( target: ResolveTargetReference, diff --git a/src/cli.ts b/src/cli.ts index 16f4357..8a06c5f 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -114,7 +114,7 @@ ${colorizeBrand("Getting started:", "primary", useColors, { bold: true })} githits login Sign in to your GitHits account githits mcp Show MCP setup instructions githits example "query" Find real-world implementations - githits resolve express Resolve a package or repository name + githits resolve express Resolve a package, repository, or site name Learn more at https://githits.com Docs: https://docs.githits.com diff --git a/src/commands/resolve.test.ts b/src/commands/resolve.test.ts index 9742fc3..113071c 100644 --- a/src/commands/resolve.test.ts +++ b/src/commands/resolve.test.ts @@ -388,6 +388,7 @@ describe("registerResolveCommand", () => { expect(resolveCommand?.description()).toContain( "include credentials, personal data, private code", ); + expect(help).toContain("package, repository, or site"); for (const registry of PKGSEER_REGISTRY_ARGS) { expect(help).toContain(registry); } diff --git a/src/commands/resolve.ts b/src/commands/resolve.ts index 9e2607c..684db70 100644 --- a/src/commands/resolve.ts +++ b/src/commands/resolve.ts @@ -101,7 +101,7 @@ function collectIntentHint(value: string, previous: string[] = []): string[] { return [...previous, value]; } -const DESCRIPTION = `Resolve a human-provided name to ranked package or GitHub repository targets. +const DESCRIPTION = `Resolve a human-provided name to ranked package, GitHub repository, or documentation-site targets. The optional --query and --intent-hint values are sent to the service as ranking context. Do not include credentials, personal data, private code, or proprietary @@ -110,15 +110,21 @@ content in either option.`; export function registerResolveCommand(program: Command): Command { return program .command("resolve") - .summary("Resolve a package or GitHub repository name") + .summary("Resolve a package, repository, or documentation-site name") .description(DESCRIPTION) - .argument("", "Package or GitHub repository name") + .argument( + "", + "Package, GitHub repository, or documentation-site name", + ) .option("-q, --query ", "Task context used as a soft ranking hint") .option( "--registry ", `Comma-separated package registries: ${PKGSEER_REGISTRY_LIST}`, ) - .option("--prefer-kind ", "Soft preference: package or repository") + .option( + "--prefer-kind ", + "Soft preference: package, repository, or site", + ) .option( "--intent-hint ", "Soft intent hint (repeatable)", From 4b350631e5ca57f4095607c2f95809cd932dddc1 Mon Sep 17 00:00:00 2001 From: Juha Litola Date: Sat, 15 Aug 2026 10:33:30 +0300 Subject: [PATCH 2/4] docs: keep site resolution compatibility dormant Retain package and repository product wording until backend site projection is activated.\n\nDescribe this release as forward compatibility while preserving SITE parsing and formatting. --- README.md | 2 +- .../site-target-client-compatibility.changed.md | 4 ++-- docs/implementation/cli-commands.md | 11 ++++------- src/cli.ts | 2 +- src/commands/resolve.test.ts | 1 - src/commands/resolve.ts | 14 ++++---------- 6 files changed, 12 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 44075cc..f34f963 100644 --- a/README.md +++ b/README.md @@ -350,7 +350,7 @@ githits example Find real-world implementations from open source githits languages List or filter supported programming languages githits feedback Submit feedback about GitHits results githits doctor Diagnose configuration and auth state -githits resolve Resolve a package, repository, or documentation-site name +githits resolve Resolve a package or GitHub repository name to canonical targets githits settings View and update preferences, privacy, and terms githits search Explore repository code, dependencies, docs, and symbols githits search-status Check the status of a previous indexed search diff --git a/changes/site-target-client-compatibility.changed.md b/changes/site-target-client-compatibility.changed.md index a3f4f68..a8de6db 100644 --- a/changes/site-target-client-compatibility.changed.md +++ b/changes/site-target-client-compatibility.changed.md @@ -3,5 +3,5 @@ "@githits/mcp": none --- -- **Resolve standalone documentation sites** - `githits resolve` accepts `site` - as a soft target-kind preference and renders site candidates explicitly. +- **Prepare standalone-site compatibility** - Resolve requests and responses + recognize the reserved site target kind ahead of backend activation. diff --git a/docs/implementation/cli-commands.md b/docs/implementation/cli-commands.md index f58744e..36d56be 100644 --- a/docs/implementation/cli-commands.md +++ b/docs/implementation/cli-commands.md @@ -17,7 +17,7 @@ The CLI exposes setup/auth commands, `doctor`, `example`, `languages`, `feedback | `languages [query]` | — | `--json` | List or filter supported languages | | `feedback [solution_id]` | `--accept` or `--reject` | `-m, --message `, `--tool `, `--json` | Submit solution-tied or generic session feedback | | `doctor` | — | `--json` | Print redacted diagnostics for GitHits runtime, environment, service URLs, config, and auth storage | -| `resolve ` | package, GitHub repository, or standalone documentation-site name | `--query`, `--registry`, `--prefer-kind`, repeatable `--intent-hint`, `--limit`, `--json` | Resolve a human-provided name to ranked concrete targets for follow-up commands | +| `resolve ` | package or GitHub repository name | `--query`, `--registry`, `--prefer-kind`, repeatable `--intent-hint`, `--limit`, `--json` | Resolve a human-provided name to ranked concrete targets for follow-up commands | | `settings` | — | `--json` | Show canonical preferences, privacy and terms, and account limits | | `settings show` | — | `--json` | Explicit form of `settings` for showing all account settings | | `settings get ` | setting key | `--json` | Read one writable setting using its public CLI name | @@ -241,15 +241,12 @@ Prints redacted diagnostics for comparing GitHits behavior across terminals or a ```text githits resolve express githits resolve codex --prefer-kind repository -githits resolve "Apple human interface guidelines" --prefer-kind site githits resolve guava --registry maven --limit 3 githits resolve "pi agent" --query "coding agent CLI" --json ``` -Resolves a human-provided package, GitHub repository, or standalone -documentation-site name to ranked canonical targets such as `npm:express`, -`github:openai/codex`, or -`site:developer.apple.com/design/human-interface-guidelines`. The default output is a +Resolves a human-provided package or GitHub repository name to ranked canonical +targets such as `npm:express` or `github:openai/codex`. The default output is a compact numbered `Candidates` list with ambiguity guidance when needed and protected exact-name matches annotated inline. It does not label any terminal candidate as best or top. Ranked candidates include their available normalized @@ -271,7 +268,7 @@ candidates, so the terminal no-result message and exit status key off `best`. `--registry` accepts a comma-separated package-registry list and the command help enumerates every accepted value; repository candidates remain eligible. -`--prefer-kind package|repository|site` is a soft preference, not a filter. +`--prefer-kind package|repository` is a soft preference, not a filter. `--intent-hint` is repeatable. `--limit` controls the ranked list from 1-20 (default 8); protected exact-name matches can be additional. `--query` and `--intent-hint` are sent to the service as ranking context and must not contain diff --git a/src/cli.ts b/src/cli.ts index 8a06c5f..16f4357 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -114,7 +114,7 @@ ${colorizeBrand("Getting started:", "primary", useColors, { bold: true })} githits login Sign in to your GitHits account githits mcp Show MCP setup instructions githits example "query" Find real-world implementations - githits resolve express Resolve a package, repository, or site name + githits resolve express Resolve a package or repository name Learn more at https://githits.com Docs: https://docs.githits.com diff --git a/src/commands/resolve.test.ts b/src/commands/resolve.test.ts index 113071c..9742fc3 100644 --- a/src/commands/resolve.test.ts +++ b/src/commands/resolve.test.ts @@ -388,7 +388,6 @@ describe("registerResolveCommand", () => { expect(resolveCommand?.description()).toContain( "include credentials, personal data, private code", ); - expect(help).toContain("package, repository, or site"); for (const registry of PKGSEER_REGISTRY_ARGS) { expect(help).toContain(registry); } diff --git a/src/commands/resolve.ts b/src/commands/resolve.ts index 684db70..9e2607c 100644 --- a/src/commands/resolve.ts +++ b/src/commands/resolve.ts @@ -101,7 +101,7 @@ function collectIntentHint(value: string, previous: string[] = []): string[] { return [...previous, value]; } -const DESCRIPTION = `Resolve a human-provided name to ranked package, GitHub repository, or documentation-site targets. +const DESCRIPTION = `Resolve a human-provided name to ranked package or GitHub repository targets. The optional --query and --intent-hint values are sent to the service as ranking context. Do not include credentials, personal data, private code, or proprietary @@ -110,21 +110,15 @@ content in either option.`; export function registerResolveCommand(program: Command): Command { return program .command("resolve") - .summary("Resolve a package, repository, or documentation-site name") + .summary("Resolve a package or GitHub repository name") .description(DESCRIPTION) - .argument( - "", - "Package, GitHub repository, or documentation-site name", - ) + .argument("", "Package or GitHub repository name") .option("-q, --query ", "Task context used as a soft ranking hint") .option( "--registry ", `Comma-separated package registries: ${PKGSEER_REGISTRY_LIST}`, ) - .option( - "--prefer-kind ", - "Soft preference: package, repository, or site", - ) + .option("--prefer-kind ", "Soft preference: package or repository") .option( "--intent-hint ", "Soft intent hint (repeatable)", From 3bb92b25c2422c6fde065b92cb6ace595f4d9746 Mon Sep 17 00:00:00 2001 From: Juha Litola Date: Sat, 15 Aug 2026 10:49:55 +0300 Subject: [PATCH 3/4] fix: keep dormant target errors generic Continue accepting the reserved SITE value without advertising it in validation guidance before backend activation. --- packages/mcp/src/shared/resolve-target-request.test.ts | 2 +- packages/mcp/src/shared/resolve-target-request.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mcp/src/shared/resolve-target-request.test.ts b/packages/mcp/src/shared/resolve-target-request.test.ts index 24c7b54..3570a1d 100644 --- a/packages/mcp/src/shared/resolve-target-request.test.ts +++ b/packages/mcp/src/shared/resolve-target-request.test.ts @@ -97,7 +97,7 @@ describe("buildResolveTargetParams", () => { preferKind: "workspace", includeDetailedFields: false, }), - ).toThrow("prefer-kind expects package, repository, or site"); + ).toThrow("Unsupported prefer-kind value"); }); it("rejects non-integer and out-of-range limits", () => { diff --git a/packages/mcp/src/shared/resolve-target-request.ts b/packages/mcp/src/shared/resolve-target-request.ts index 7f13284..e2256e6 100644 --- a/packages/mcp/src/shared/resolve-target-request.ts +++ b/packages/mcp/src/shared/resolve-target-request.ts @@ -84,7 +84,7 @@ function parsePreferredKind( if (kind === "repository") return "REPOSITORY"; if (kind === "site") return "SITE"; throw new InvalidPackageSpecError( - `prefer-kind expects package, repository, or site. Got '${value}'.`, + `Unsupported prefer-kind value '${value}'.`, ); } From 7cab7a3ebbb87a0f055918a5c7b2475a76a7ccb0 Mon Sep 17 00:00:00 2001 From: Juha Litola Date: Sat, 15 Aug 2026 12:01:52 +0300 Subject: [PATCH 4/4] fix: keep dormant target errors generic --- packages/mcp/src/shared/resolve-target-request.test.ts | 2 +- packages/mcp/src/shared/resolve-target-request.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mcp/src/shared/resolve-target-request.test.ts b/packages/mcp/src/shared/resolve-target-request.test.ts index 3570a1d..0a46a88 100644 --- a/packages/mcp/src/shared/resolve-target-request.test.ts +++ b/packages/mcp/src/shared/resolve-target-request.test.ts @@ -97,7 +97,7 @@ describe("buildResolveTargetParams", () => { preferKind: "workspace", includeDetailedFields: false, }), - ).toThrow("Unsupported prefer-kind value"); + ).toThrow("prefer-kind expects package or repository"); }); it("rejects non-integer and out-of-range limits", () => { diff --git a/packages/mcp/src/shared/resolve-target-request.ts b/packages/mcp/src/shared/resolve-target-request.ts index e2256e6..6e49e44 100644 --- a/packages/mcp/src/shared/resolve-target-request.ts +++ b/packages/mcp/src/shared/resolve-target-request.ts @@ -84,7 +84,7 @@ function parsePreferredKind( if (kind === "repository") return "REPOSITORY"; if (kind === "site") return "SITE"; throw new InvalidPackageSpecError( - `Unsupported prefer-kind value '${value}'.`, + `prefer-kind expects package or repository. Got '${value}'.`, ); }