fix(pkg-deps): resolve runtime.version on MCP + reject silent no-op flag combos - #17
Merged
Conversation
…lag combos Follow-up to #16. Three user-facing issues from the final codex review round that didn't make it into the merged commit. ## Fixes - **MCP `runtime.items[].version` now always resolved.** The tool description promises `{name, version, constraint}` records with the backend resolving each constraint to a concrete version, but the merged code only resolved versions when the caller also set `include_transitive: true`. Now the handler always fetches the transitive DAG at depth 1 (mirroring the CLI path) so `runtime.items[].version` resolves regardless of whether the caller asked for the `transitive` block. The `transitive` block itself is still emitted only when `include_transitive: true`. One extra backend round-trip, in exchange for a contract that matches what the description says. - **Hard-error on silent no-op flag combinations.** Three inputs used to be silently ignored when paired incorrectly; each now returns `INVALID_ARGUMENT`: - MCP `max_depth` without `include_transitive` - MCP `include_importers` without `include_transitive` - CLI `--depth` without `--transitive` On an agent-facing tool, silently-ignored inputs are worse than a rejection: the caller believes they got the effect they asked for. - **Stale documentation from the UX-redesign rounds.** - `tools.md` tool-table row still claimed `package_dependencies` returned "an opaque DAG" — we dropped `transitive.dag` from the envelope mid-iteration. - `tools.md` MCP-schema note still said "CLI's `--depth` defaults to 3"; `--depth` has no default, backend full-graph traversal wins. - `cli-commands.md` two-views section mentioned NuGet TFMs in the `pkg deps` context, but NuGet is rejected client-side for this tool. ## Cleanup Envelope builder (`src/shared/package-dependencies-response.ts`) had ~155 lines of dead code left behind after the envelope took over provenance decoding from the terminal formatter: `DecodedConflict`, `decodeConflictEntry`, `decodeCycleEntry`, `ProvenanceEntry`, `buildProvenanceLookup`. Deleted. Module header rewritten to describe the current state (preprocessed transitive, typed conflicts/cycles with raw fallback, no `transitive.dag`, no `uniqueDependencies`). ## Tests - `bun test` — 962 pass, 0 fail (was 958; added 4 new tests). - `bun run typecheck` / `bun run build` clean. - Three new tests: - MCP: `runtime.items[].version` resolves even when `include_transitive` is absent (locks the wire behaviour). - MCP: `max_depth` without `include_transitive` returns `INVALID_ARGUMENT`. - MCP: `include_importers` without `include_transitive` returns `INVALID_ARGUMENT`. - CLI: `--depth` without `--transitive` errors with the expected hint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jlitola
added a commit
that referenced
this pull request
Apr 20, 2026
Fourth Wave 1 package-intelligence tool, following `package_summary` (#13), `package_vulnerabilities` (#14), `package_dependencies` (#16 + follow-up #17). Behind the existing `code_navigation` capability gate. Both surfaces share a single request builder, envelope builder, and error classifier; a parity test asserts `toEqual` JSON across the two surfaces for every service-sourced fixture. ## Surfaces - **`githits pkg changelog [spec]`** — default latest-mode output is a summary row (identity · source · mode · entry count) plus a one-line `version date url` per entry, newest-first. `--from <v>` switches to range mode (all entries between `--from` and `--to`/latest; `--limit` rejected). `--to <v>` / `--limit <n>` shape latest mode. `--verbose` expands each entry with its full markdown body, indented and dimmed. `--no-body` drops body fields from both terminal and JSON. `--git-ref` targets a specific branch/tag for CHANGELOG.md source. `--repo-url <url>` is an alternative addressing mode (mutually exclusive with `<spec>`). - **`package_changelog`** — MCP tool with the same envelope. Dual addressing: `registry` + `package_name` XOR `repo_url`. Permissive Zod schema (in-handler validation). `include_bodies` (default true) mirrors CLI's `--no-body`. Description is self-contained — no CLI-flag references. ## Design choices - **Dual addressing is unique to this tool.** P1 / P2 / P3 all accept only `registry` + `package_name` because their underlying backend queries are registry-metadata APIs. `packageChangelog` is intrinsically repo-level (its sources are GitHub Releases, CHANGELOG.md, HexDocs), so `repoUrl` is a peer addressing mode in the GraphQL signature, not a bolt-on. Exposing it on MCP was non-negotiable: `packageSummary` cannot resolve a repo URL → spec, so agents starting from a repo URL had no path in. Documented in `tools.md` so future tool authors don't cargo-cult the asymmetry. - **`<spec>@<version>` rejected.** `pkg vulns` / `pkg deps` treat `@version` as "for this exact version"; changelog has no single- version query. Silently remapping to `toVersion` would be a client-invented semantic shift. Rejected client-side with `INVALID_ARGUMENT` and a hint pointing to `--to` / `--from`. - **Data-first envelope.** `{registry|repoUrl, source, mode, entries: {count, items}, filter?}`. `source` always present here (null-source case is promoted to `NOT_FOUND` at the service boundary and never reaches the envelope). `mode` derived from request — `"range"` iff `fromVersion` non-null after normalisation, `"latest"` otherwise. `entries.count` computed client-side from `items.length`; the backend's count isn't selected on the wire so the invariant holds by construction. - **`version` kept when null, other per-entry nullables stripped.** `version` is the primary key agents index by, so the slot is always present (possibly null); stripping other nullables keeps the envelope lean. `body` additionally stripped under `include_bodies: false`. - **`filter.*` echo tracks explicit fields only.** Request builder tracks `explicitFilterFields` set; envelope emits `filter.*` only for caller-supplied inputs. Backend defaults (`limit: 10`, `toVersion: <latest>`) never round-trip as caller intent. - **`include_bodies` lever.** Release bodies on large packages can run 10 KB+ per entry; `include_bodies: false` drops `body` from all items explicitly (not silent truncation). Other fields preserved so agents still get the version / date / URL timeline. - **`metadata` dropped from envelope in v1.** Source-specific opaque `GenericJSON`; revisit via agent feedback with a `TODO(pkgseer-backend)` anchor if demand surfaces. - **No client-side registry restriction.** Unlike P3's 6-registry client-side gate, `packageChangelog` is source-pull rather than registry-query; the backend returns `NOT_FOUND` for registries without changelog sources. Live-smoke matrix documented in the plan to verify behaviour per registry. - **Mode mutual exclusion enforced client-side.** `--from` / `from_version` + `--limit` / `limit` together → `INVALID_ARGUMENT` with actionable hint. - **`source: null` promoted to NOT_FOUND.** Typed `PackageIntelligenceChangelogSourceNotFoundError` at the service boundary; shared classifier routes to `NOT_FOUND` with a message naming the sources tried (GitHub Releases, CHANGELOG.md, HexDocs). Empty `entries.items: []` with a valid `source` is success, not error. - **`--verbose` vs `--no-body` vs `--json` interaction.** `--verbose` is terminal-only (expands markdown bodies per entry); does not change `--json`. `--no-body` affects both terminal and `--json`. `--json` output shape is independent of `--verbose`. - **Shared `promoteGenericVersionNotFound` extended.** The helper now recognises `fromVersion` / `toVersion` in addition to `version`; preference order `version → fromVersion → toVersion`. `registry` / `packageName` made optional so repo-URL-addressed requests flow through without a spec. P2 / P3 regressions guarded by the existing tests plus 4 new helper tests. ## Tests - `bun test` — 1053 pass, 0 fail (was 966; +87 new tests). - `bun run typecheck` / `bun run build` / `bun run lint` — clean. - Unit tests: request-builder (addressing XOR, `<spec>@<version>` rejection, `--from`/`--limit` mutex, limit bounds, tag-style rejection, pre-release versions on `--from`/`--to`, explicit-flag tracking), envelope builder (shape, null handling, `include_bodies`, mode derivation, filter echo, empty entries), terminal formatter (default one-liners, `--verbose` expansion, missing fields), CLI action (13 cases incl. addressing, errors, flag interactions), MCP tool (15 cases incl. validation + service errors). - Parity test: 12 fixtures (happy latest, range mode, repo-URL addressing, `--no-body` / `include_bodies: false`, default bodies, empty entries, `NOT_FOUND` no-source, `TargetNotFoundError`, `VERSION_NOT_FOUND` with structured details, `BACKEND_ERROR`, `INVALID_ARGUMENT` for `<spec>@<version>` and `--from`+`--limit`). - Live-smoke deferred to review cycle (local non-interactive bash keychain prompt blocks the CLI startup handshake; shape correctness is locked in via the verbatim schema Zod parse + parity test). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 tasks
jlitola
added a commit
that referenced
this pull request
Apr 21, 2026
Fourth Wave 1 package-intelligence tool, following `package_summary` (#13), `package_vulnerabilities` (#14), `package_dependencies` (#16 + follow-up #17). Behind the existing `code_navigation` capability gate. Both surfaces share a single request builder, envelope builder, and error classifier; a parity test asserts `toEqual` JSON across the two surfaces for every service-sourced fixture. ## Surfaces - **`githits pkg changelog [spec]`** — default latest-mode output is a summary row (identity · source · mode · entry count) plus a one-line `version date url` per entry, newest-first. `--from <v>` switches to range mode (all entries between `--from` and `--to`/latest; `--limit` rejected). `--to <v>` / `--limit <n>` shape latest mode. `--verbose` expands each entry with its full markdown body, indented and dimmed. `--no-body` drops body fields from both terminal and JSON. `--git-ref` targets a specific branch/tag for CHANGELOG.md source. `--repo-url <url>` is an alternative addressing mode (mutually exclusive with `<spec>`). - **`package_changelog`** — MCP tool with the same envelope. Dual addressing: `registry` + `package_name` XOR `repo_url`. Permissive Zod schema (in-handler validation). `include_bodies` (default true) mirrors CLI's `--no-body`. Description is self-contained — no CLI-flag references. ## Design choices - **Dual addressing is unique to this tool.** P1 / P2 / P3 all accept only `registry` + `package_name` because their underlying backend queries are registry-metadata APIs. `packageChangelog` is intrinsically repo-level (its sources are GitHub Releases, CHANGELOG.md, HexDocs), so `repoUrl` is a peer addressing mode in the GraphQL signature, not a bolt-on. Exposing it on MCP was non-negotiable: `packageSummary` cannot resolve a repo URL → spec, so agents starting from a repo URL had no path in. Documented in `tools.md` so future tool authors don't cargo-cult the asymmetry. - **`<spec>@<version>` rejected.** `pkg vulns` / `pkg deps` treat `@version` as "for this exact version"; changelog has no single- version query. Silently remapping to `toVersion` would be a client-invented semantic shift. Rejected client-side with `INVALID_ARGUMENT` and a hint pointing to `--to` / `--from`. - **Data-first envelope.** `{registry|repoUrl, source, mode, entries: {count, items}, filter?}`. `source` always present here (null-source case is promoted to `NOT_FOUND` at the service boundary and never reaches the envelope). `mode` derived from request — `"range"` iff `fromVersion` non-null after normalisation, `"latest"` otherwise. `entries.count` computed client-side from `items.length`; the backend's count isn't selected on the wire so the invariant holds by construction. - **`version` kept when null, other per-entry nullables stripped.** `version` is the primary key agents index by, so the slot is always present (possibly null); stripping other nullables keeps the envelope lean. `body` additionally stripped under `include_bodies: false`. - **`filter.*` echo tracks explicit fields only.** Request builder tracks `explicitFilterFields` set; envelope emits `filter.*` only for caller-supplied inputs. Backend defaults (`limit: 10`, `toVersion: <latest>`) never round-trip as caller intent. - **`include_bodies` lever.** Release bodies on large packages can run 10 KB+ per entry; `include_bodies: false` drops `body` from all items explicitly (not silent truncation). Other fields preserved so agents still get the version / date / URL timeline. - **`metadata` dropped from envelope in v1.** Source-specific opaque `GenericJSON`; revisit via agent feedback with a `TODO(pkgseer-backend)` anchor if demand surfaces. - **No client-side registry restriction.** Unlike P3's 6-registry client-side gate, `packageChangelog` is source-pull rather than registry-query; the backend returns `NOT_FOUND` for registries without changelog sources. Live-smoke matrix documented in the plan to verify behaviour per registry. - **Mode mutual exclusion enforced client-side.** `--from` / `from_version` + `--limit` / `limit` together → `INVALID_ARGUMENT` with actionable hint. - **`source: null` promoted to NOT_FOUND.** Typed `PackageIntelligenceChangelogSourceNotFoundError` at the service boundary; shared classifier routes to `NOT_FOUND` with a message naming the sources tried (GitHub Releases, CHANGELOG.md, HexDocs). Empty `entries.items: []` with a valid `source` is success, not error. - **`--verbose` vs `--no-body` vs `--json` interaction.** `--verbose` is terminal-only (expands markdown bodies per entry); does not change `--json`. `--no-body` affects both terminal and `--json`. `--json` output shape is independent of `--verbose`. - **Shared `promoteGenericVersionNotFound` extended.** The helper now recognises `fromVersion` / `toVersion` in addition to `version`; preference order `version → fromVersion → toVersion`. `registry` / `packageName` made optional so repo-URL-addressed requests flow through without a spec. P2 / P3 regressions guarded by the existing tests plus 4 new helper tests. ## Tests - `bun test` — 1053 pass, 0 fail (was 966; +87 new tests). - `bun run typecheck` / `bun run build` / `bun run lint` — clean. - Unit tests: request-builder (addressing XOR, `<spec>@<version>` rejection, `--from`/`--limit` mutex, limit bounds, tag-style rejection, pre-release versions on `--from`/`--to`, explicit-flag tracking), envelope builder (shape, null handling, `include_bodies`, mode derivation, filter echo, empty entries), terminal formatter (default one-liners, `--verbose` expansion, missing fields), CLI action (13 cases incl. addressing, errors, flag interactions), MCP tool (15 cases incl. validation + service errors). - Parity test: 12 fixtures (happy latest, range mode, repo-URL addressing, `--no-body` / `include_bodies: false`, default bodies, empty entries, `NOT_FOUND` no-source, `TargetNotFoundError`, `VERSION_NOT_FOUND` with structured details, `BACKEND_ERROR`, `INVALID_ARGUMENT` for `<spec>@<version>` and `--from`+`--limit`). - Live-smoke deferred to review cycle (local non-interactive bash keychain prompt blocks the CLI startup handshake; shape correctness is locked in via the verbatim schema Zod parse + parity test). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jlitola
added a commit
that referenced
this pull request
Apr 21, 2026
Fourth Wave 1 package-intelligence tool, following `package_summary` (#13), `package_vulnerabilities` (#14), `package_dependencies` (#16 + follow-up #17). Behind the existing `code_navigation` capability gate. Both surfaces share a single request builder, envelope builder, and error classifier; a parity test asserts `toEqual` JSON across the two surfaces for every service-sourced fixture. ## Surfaces - **`githits pkg changelog [spec]`** — default latest-mode output is a summary row (identity · source · mode · entry count) plus, per entry, a `version date url` header followed by the first 10 lines of the markdown body (with a `… (+N more lines — use --verbose for the full body)` footer when the body is longer). `--verbose` uncaps the body preview; `--no-body` drops bodies entirely from both terminal and `--json`. `--from <v>` switches to range mode (all entries between `--from` and `--to`/latest; `--limit` rejected). `--to <v>` / `--limit <n>` shape latest mode. `--git-ref` targets a branch/tag for CHANGELOG.md source. `--repo-url <url>` is an alternative addressing mode, mutually exclusive with `<spec>`. - **`package_changelog`** — MCP tool with the same envelope. Dual addressing: `registry` + `package_name` XOR `repo_url`. Permissive Zod schema + in-handler validation. `include_bodies` (default true) mirrors CLI's `--no-body` and controls JSON envelope bodies. ## Design choices - **Dual addressing is unique to this tool.** P1 / P2 / P3 all accept only `registry` + `package_name` because their underlying backend queries are registry-metadata APIs. `packageChangelog` is intrinsically repo-level (sources: GitHub Releases, CHANGELOG.md, HexDocs), so `repoUrl` is a peer addressing mode in the GraphQL signature. Exposing it on MCP was non-negotiable: `packageSummary` cannot resolve repo-URL → spec, so agents starting from a repo URL had no path in. Documented in `tools.md` so future tool authors don't cargo-cult the asymmetry. - **`<spec>@<version>` rejected.** `pkg vulns` / `pkg deps` treat `@version` as "for this exact version"; changelog has no single-version query. Silently remapping to `toVersion` would be a client-invented semantic shift. Rejected with `INVALID_ARGUMENT` and a hint pointing to `--to` / `--from`. - **Body preview capped at 10 lines by default.** Release bodies routinely run 50-100+ lines — showing them unbounded would swamp the terminal; showing none would make the whole command useless for answering "what changed". 10-line cap shows the first one or two sections plus preamble, which is usually enough to answer the question; `--verbose` lifts the cap when full context is needed; `--no-body` drops bodies entirely for a pure timeline view. JSON envelope is always full-body unless `--no-body` / `include_bodies: false` is set — `--verbose` is terminal-only. - **Data-first envelope.** `{registry|repoUrl, source, mode, entries: {count, items}, filter?}`. `source` always present here (null-source case promoted to `NOT_FOUND` at the service boundary; envelope builder has a defence-in-depth throw if the invariant is ever violated upstream). `mode` derived from request — `"range"` iff `fromVersion` non-null, `"latest"` otherwise. `entries.count` computed client-side from `items.length`; backend count not selected on the wire. - **`version` kept when null, other per-entry nullables stripped.** `version` is the primary key agents index by. Empty-string values (e.g. `body: ""`) are preserved — distinct from absent — so agents can tell "empty release notes" from "no notes field". Terminal formatter renders `(empty release notes)` sentinel for empty-string bodies. - **`filter.*` tracks explicit fields only.** Builder returns an `explicitFilterFields` set; envelope emits `filter.*` only for caller-supplied inputs. Backend defaults (`limit: 10`, `toVersion: latest`) never round-trip as caller intent. - **`include_bodies` lever.** Default true. `false` drops `body` from every item explicitly; other fields preserved so agents still get the version / date / URL timeline. Measured 5.13× envelope size reduction on a 20-entry `npm:typescript` request (17.3 KB → 3.4 KB). - **`--no-body` + `--verbose` is an error.** Contradictory intents; CLI rejects with an actionable hint rather than silently ignoring `--verbose`. - **MCP `limit` validation in the shared builder, not Zod.** If Zod hard-rejected `limit: 51` at the SDK level, the agent would see a raw SDK error instead of our shared `{error, code, retryable}` envelope. Schema is permissive; builder enforces bounds. - **Registry coverage — all 9 on the wire, no client-side restriction.** `packageChangelog` is source-pull, not registry-query. Live-smoke confirmed npm / pypi / hex / crates / vcpkg / maven / packagist return useful data; nuget returns a generic `BACKEND_ERROR`; zig packages weren't in the backend index. All paths produce the shared envelope correctly. - **Mode mutex enforced client-side.** `--from` / `from_version` + `--limit` / `limit` → `INVALID_ARGUMENT` with actionable hint. - **`metadata` dropped in v1.** Source-specific opaque `GenericJSON`; revisit via agent feedback. - **Shared `promoteGenericVersionNotFound` extended.** Now recognises `fromVersion` / `toVersion` in addition to `version`. `registry` / `packageName` made optional so repo-URL-addressed requests flow through. Preference order `version → fromVersion → toVersion`. P2 / P3 regressions guarded by existing tests + 5 new helper tests. ## Tests - `bun test` — 1062 pass, 0 fail. - `bun run typecheck` / `bun run build` / `bun run lint` — clean. - Request builder, envelope builder, terminal formatter, MCP tool, CLI action, parity fixtures (12) — see docs for full matrix. - Live-smoke against production pkgseer: latest / range / repo-URL / `--no-body` / error paths / registry matrix / body-preview cap / `--verbose` uncap / truncation footer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jlitola
added a commit
that referenced
this pull request
Apr 21, 2026
Fourth Wave 1 package-intelligence tool, following `package_summary` (#13), `package_vulnerabilities` (#14), `package_dependencies` (#16 + follow-up #17). Behind the existing `code_navigation` capability gate. Both surfaces share a single request builder, envelope builder, and error classifier; a parity test asserts `toEqual` JSON across the two surfaces for every service-sourced fixture. ## Surfaces - **`githits pkg changelog [spec]`** — default latest-mode output is a summary row (identity · source · mode · entry count) plus, per entry, a `version date url` header followed by the first 10 lines of the markdown body (with a `… (+N more lines — use --verbose for the full body)` footer when the body is longer). `--verbose` uncaps the body preview; `--no-body` drops bodies entirely from both terminal and `--json`. `--from <v>` switches to range mode (all entries between `--from` and `--to`/latest; `--limit` rejected). `--to <v>` / `--limit <n>` shape latest mode. `--git-ref` targets a branch/tag for CHANGELOG.md source. `--repo-url <url>` is an alternative addressing mode, mutually exclusive with `<spec>`. - **`package_changelog`** — MCP tool with the same envelope. Dual addressing: `registry` + `package_name` XOR `repo_url`. Permissive Zod schema + in-handler validation. `include_bodies` (default true) mirrors CLI's `--no-body` and controls JSON envelope bodies. ## Design choices - **Dual addressing is unique to this tool.** P1 / P2 / P3 all accept only `registry` + `package_name` because their underlying backend queries are registry-metadata APIs. `packageChangelog` is intrinsically repo-level (sources: GitHub Releases, CHANGELOG.md, HexDocs), so `repoUrl` is a peer addressing mode in the GraphQL signature. Exposing it on MCP was non-negotiable: `packageSummary` cannot resolve repo-URL → spec, so agents starting from a repo URL had no path in. Documented in `tools.md` so future tool authors don't cargo-cult the asymmetry. - **`<spec>@<version>` rejected.** `pkg vulns` / `pkg deps` treat `@version` as "for this exact version"; changelog has no single-version query. Silently remapping to `toVersion` would be a client-invented semantic shift. Rejected with `INVALID_ARGUMENT` and a hint pointing to `--to` / `--from`. - **Body preview capped at 10 lines by default.** Release bodies routinely run 50-100+ lines — showing them unbounded would swamp the terminal; showing none would make the whole command useless for answering "what changed". 10-line cap shows the first one or two sections plus preamble, which is usually enough to answer the question; `--verbose` lifts the cap when full context is needed; `--no-body` drops bodies entirely for a pure timeline view. JSON envelope is always full-body unless `--no-body` / `include_bodies: false` is set — `--verbose` is terminal-only. - **Data-first envelope.** `{registry|repoUrl, source, mode, entries: {count, items}, filter?}`. `source` always present here (null-source case promoted to `NOT_FOUND` at the service boundary; envelope builder has a defence-in-depth throw if the invariant is ever violated upstream). `mode` derived from request — `"range"` iff `fromVersion` non-null, `"latest"` otherwise. `entries.count` computed client-side from `items.length`; backend count not selected on the wire. - **`version` kept when null, other per-entry nullables stripped.** `version` is the primary key agents index by. Empty-string values (e.g. `body: ""`) are preserved — distinct from absent — so agents can tell "empty release notes" from "no notes field". Terminal formatter renders `(empty release notes)` sentinel for empty-string bodies. - **`filter.*` tracks explicit fields only.** Builder returns an `explicitFilterFields` set; envelope emits `filter.*` only for caller-supplied inputs. Backend defaults (`limit: 10`, `toVersion: latest`) never round-trip as caller intent. - **`include_bodies` lever.** Default true. `false` drops `body` from every item explicitly; other fields preserved so agents still get the version / date / URL timeline. Measured 5.13× envelope size reduction on a 20-entry `npm:typescript` request (17.3 KB → 3.4 KB). - **`--no-body` + `--verbose` is an error.** Contradictory intents; CLI rejects with an actionable hint rather than silently ignoring `--verbose`. - **MCP `limit` validation in the shared builder, not Zod.** If Zod hard-rejected `limit: 51` at the SDK level, the agent would see a raw SDK error instead of our shared `{error, code, retryable}` envelope. Schema is permissive; builder enforces bounds. - **Registry coverage — all 9 on the wire, no client-side restriction.** `packageChangelog` is source-pull, not registry-query. Live-smoke confirmed npm / pypi / hex / crates / vcpkg / maven / packagist return useful data; nuget returns a generic `BACKEND_ERROR`; zig packages weren't in the backend index. All paths produce the shared envelope correctly. - **Mode mutex enforced client-side.** `--from` / `from_version` + `--limit` / `limit` → `INVALID_ARGUMENT` with actionable hint. - **`metadata` dropped in v1.** Source-specific opaque `GenericJSON`; revisit via agent feedback. - **Shared `promoteGenericVersionNotFound` extended.** Now recognises `fromVersion` / `toVersion` in addition to `version`. `registry` / `packageName` made optional so repo-URL-addressed requests flow through. Preference order `version → fromVersion → toVersion`. P2 / P3 regressions guarded by existing tests + 5 new helper tests. ## Tests - `bun test` — 1062 pass, 0 fail. - `bun run typecheck` / `bun run build` / `bun run lint` — clean. - Request builder, envelope builder, terminal formatter, MCP tool, CLI action, parity fixtures (12) — see docs for full matrix. - Live-smoke against production pkgseer: latest / range / repo-URL / `--no-body` / error paths / registry matrix / body-preview cap / `--verbose` uncap / truncation footer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #16. Three user-facing issues from the final codex review round that didn't make it into the merged commit.
Fixes
MCP
runtime.items[].versionnow always resolved. The tool description promises{name, version, constraint}records with the backend resolving each constraint to a concrete version, but the merged code only resolved versions when the caller also setinclude_transitive: true. Now the handler always fetches the transitive DAG at depth 1 (mirroring the CLI path) soruntime.items[].versionresolves regardless of whether the caller asked for thetransitiveblock. Thetransitiveblock itself is still emitted only wheninclude_transitive: true. One extra backend round-trip, in exchange for a contract that matches what the description says.Hard-error on silent no-op flag combinations. Three inputs used to be silently ignored when paired incorrectly; each now returns
INVALID_ARGUMENT:max_depthwithoutinclude_transitiveinclude_importerswithoutinclude_transitive--depthwithout--transitiveOn an agent-facing tool, silently-ignored inputs are worse than a rejection: the caller believes they got the effect they asked for.
Stale documentation from the UX-redesign rounds.
tools.mdtool-table row still claimedpackage_dependenciesreturned "an opaque DAG" — we droppedtransitive.dagfrom the envelope mid-iteration.tools.mdMCP-schema note still said "CLI's--depthdefaults to 3";--depthhas no default, backend full-graph traversal wins.cli-commands.mdtwo-views section mentioned NuGet TFMs in thepkg depscontext, but NuGet is rejected client-side for this tool.Cleanup
Envelope builder (
src/shared/package-dependencies-response.ts) had ~155 lines of dead code left behind after the envelope took over provenance decoding from the terminal formatter:DecodedConflict,decodeConflictEntry,decodeCycleEntry,ProvenanceEntry,buildProvenanceLookup. Deleted. Module header rewritten to describe the current state (preprocessed transitive, typed conflicts/cycles with raw fallback, notransitive.dag, nouniqueDependencies).Tests
bun test— 962 pass, 0 fail (was 958; added 4 new tests).bun run typecheck/bun run buildclean.runtime.items[].versionresolves even wheninclude_transitiveis absent (locks the wire behaviour).max_depthwithoutinclude_transitivereturnsINVALID_ARGUMENT.include_importerswithoutinclude_transitivereturnsINVALID_ARGUMENT.--depthwithout--transitiveerrors with the expected hint.Test plan
🤖 Generated with Claude Code