Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/implementation/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The CLI exposes three primary commands (`search`, `languages`, `feedback`) that
| `code search <package> [query]` | package spec | `--keywords`, `--keyword`, `--match-mode`, `--category`, `--kind`, `--file`, `--intent`, `--limit`, `--wait`, `--json` | Search indexed dependency source code |
| `pkg info <spec>` | package spec | `--verbose`, `--json` | Show a package overview (latest version, downloads, license, vulnerabilities) |
| `pkg vulns <spec>` | package spec (optional `@version`) | `--severity`, `--include-withdrawn`, `--verbose`, `--json` | List known vulnerabilities for a package (npm/pypi/hex/crates) |
| `pkg deps <spec>` | package spec (optional `@version`) | `--groups`, `--lifecycle`, `--transitive`, `--depth`, `--verbose`, `--json` | Analyse dependencies: direct runtime deps, structured groups, optional transitive graph (npm/pypi/hex/crates/vcpkg/zig) |

### `githits init`

Expand Down Expand Up @@ -149,6 +150,42 @@ Lists known CVE / OSV advisories for a package: severity, affected version range

**Troubleshooting.** Same debug areas as `pkg info` (`GITHITS_DEBUG=pkg-intel` for classified errors; `GITHITS_DEBUG=pkg-graphql` for transport failures).

### `githits pkg deps`

```
githits pkg deps npm:express
githits pkg deps npm:express --groups
githits pkg deps crates:tokio --lifecycle optional
githits pkg deps npm:express --lifecycle runtime,development
githits pkg deps npm:express --transitive
githits pkg deps npm:express --transitive --depth 2
githits pkg deps npm:express --json
```

Analyses dependencies for a package on npm, PyPI, Hex, Crates, vcpkg, or Zig. Default terminal output is a flat list of direct runtime dependencies with a hint summarising hidden groups.

**Package spec.** `<registry>:<name>[@<version>]`. `@<version>` is accepted (same as `pkg vulns`); defaults to latest. Tag-style inputs such as `@v4.18.0` are rejected client-side with `INVALID_ARGUMENT` — callers must use the canonical version. Only `npm`, `pypi`, `hex`, `crates`, `vcpkg`, and `zig` are supported; other registries are rejected client-side with `pkg deps only supports npm, pypi, hex, crates, vcpkg, and zig. Got: ${registry}.`

**Two views.** The default runtime view collapses to a single-column list from `dependencies.direct` — the flat answer to "what does this pull in?". The structured groups view (`--groups`, or implicitly via `--lifecycle`) iterates `dependencyGroups.groups` and preserves registry-specific condition metadata (PyPI extras, Crates features, NuGet TFMs). Dev / peer / build / optional deps live only in the groups view — the wire's `direct[]` is always runtime-only.

**Lifecycle filter.** `-l, --lifecycle <phases>` accepts a comma-separated list of canonical lowercase tokens (`runtime`, `development`, `build`, `peer`, `optional`). Uppercase and whitespace are tolerated. Filters server-side via the backend's `lifecycle: [String!]` input, which only affects `dependencyGroups`; `direct[]` and `transitive[]` are returned regardless. Unknown tokens are rejected with `INVALID_ARGUMENT` and the canonical list.

**Groups view (`--groups` or any `--lifecycle`).** Headings collapse to `name` when `conditionType === "always"` (e.g. `runtime`, `development`). Feature / TFM groups render `name (lifecycle, conditionType[: conditionValue])` — `conditionValue` is omitted when it equals `name` (the common case on Crates features and PyPI extras). Within each group, entries sort alphabetically. Duplicate `{name, constraint}` tuples inside a group collapse in the terminal for scannability; the JSON envelope preserves every duplicate the backend emitted.

**Transitive view (`--transitive`).** Replaces the direct-deps list with the full unique transitive closure (alphabetical, `name@version`, one per line). Summary row carries the aggregate counts + conflict / cycle counts, and `(max depth N)` only when `--depth` was applied — otherwise the backend's full-graph traversal is shown. `--depth <n>` (1–10) caps traversal; there is **no client-side default cap** (matches `npm ls` / `cargo tree` ergonomics).

**Verbose (`--verbose`).** In both plain and transitive modes, each dep expands to a multi-line block: the first line is `name@version`, followed by indented `- <constraint> required by <importer>@<importer-version>, …` bullets. Importers that share a constraint are collapsed onto one bullet with a comma-separated list. In plain mode each direct dep has exactly one importer (the root package itself); in transitive mode a popular leaf may list many importers grouped by constraint. Conflicts expand into a `Conflicts (N):` table (`name: range1, range2, …`, one row per package); circular dependencies expand into a `Circular dependencies (N):` list (`a → b → a` arrow chain).

**JSON envelope.** Preprocessed: `runtime.items[].version` surfaces the resolved version alongside the constraint. Under `--transitive`, `transitive.packages[]` carries `{name, version, importers[]}` records so agents get the same provenance signal as the verbose terminal output without decoding the raw DAG. `transitive.conflicts[]` and `transitive.circularDependencies[]` are typed (`{name, requiredVersions}` / `{cycle: string[]}`) when the observed backend shape decodes; raw passthrough otherwise. The raw DAG itself is deliberately **not** in the envelope — a future dedicated `pkg deps-dag` command will expose it under a typed contract for graph visualisation (mermaid / DOT / interactive viewer).

**Output envelope.** `{registry, name, version, requestedVersion?, runtime?, groups?, transitive?, filter?}`. Data-first: the `runtime` block emits whenever the backend returned `dependencies.direct` (including `{count: 0, items: []}` for zero-dep packages); the `groups` block emits whenever the backend returned `dependencyGroups` (including `{items: []}` when a lifecycle filter matched nothing, so agents distinguish "backend has no groups concept" from "filter excluded everything"). Each group carries its members under `items` (matches the top-level `runtime.items` naming so dependency lists share one key throughout the envelope). `filter.lifecycles` echoes the canonicalised, deduplicated, display-order-sorted list the backend received — not the raw CSV input.

**Exit codes.** 0 on success including zero-dep packages; 1 on any error. Under `--json`, the error envelope is written to **stderr**.

**Capability gate.** Same as `pkg info` / `pkg vulns` (inherits from the `code_navigation` token capability).

**Troubleshooting.** Same debug areas as `pkg info` / `pkg vulns` (`GITHITS_DEBUG=pkg-intel` for classified errors; `GITHITS_DEBUG=pkg-graphql` for transport failures).

## Architecture

```
Expand Down
62 changes: 62 additions & 0 deletions docs/implementation/mcp-cli-parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,22 @@ When a new tool lands with both MCP and CLI surfaces:
| `src/shared/package-summary-response.ts` | Lean JSON envelope builder and terminal formatter for `package_summary`. |
| `src/shared/package-vulnerabilities-request.ts` | Shared request builder for `package_vulnerabilities`; owns the tool-local `supportsVulnerabilitiesRegistry` predicate and the severity-label → CVSS float map. |
| `src/shared/package-vulnerabilities-response.ts` | Lean JSON envelope builder for `package_vulnerabilities` (shared); terminal formatter (CLI-only). |
| `src/shared/package-dependencies-request.ts` | Shared request builder for `package_dependencies`; owns `supportsDependenciesRegistry` + lifecycle / depth validation. |
| `src/shared/package-dependencies-response.ts` | Lean JSON envelope builder for `package_dependencies` (shared); terminal formatter (CLI-only). |
| `src/shared/package-intelligence-error-map.ts` | `mapPackageIntelligenceError` classifier (reuses `MappedError` from the code-nav map). |
| `src/services/promote-version-not-found.ts` | Shared helper that promotes generic backend errors with "no matching version" messages into typed `VERSION_NOT_FOUND`. Used by `packageVulnerabilities` and `packageDependencies` executors. |
| `src/tools/search-symbols.ts` | MCP tool definition for `search_symbols`. |
| `src/tools/package-summary.ts` | MCP tool definition for `package_summary`. |
| `src/tools/package-vulnerabilities.ts` | MCP tool definition for `package_vulnerabilities`. |
| `src/tools/package-dependencies.ts` | MCP tool definition for `package_dependencies`. |
| `src/commands/code/search-symbols.ts` | CLI command. |
| `src/commands/pkg/info.ts` | CLI command for `pkg info`. |
| `src/commands/pkg/vulns.ts` | CLI command for `pkg vulns`. |
| `src/commands/pkg/deps.ts` | CLI command for `pkg deps`. |
| `src/tools/search-symbols-parity.test.ts` | Parity tests (cite rule IDs). |
| `src/tools/package-summary-parity.test.ts` | Parity tests for `package_summary` (cite rule IDs). |
| `src/tools/package-vulnerabilities-parity.test.ts` | Parity tests for `package_vulnerabilities` (cite rule IDs). |
| `src/tools/package-dependencies-parity.test.ts` | Parity tests for `package_dependencies` (cite rule IDs). |

## Per-tool notes

Expand Down Expand Up @@ -230,3 +236,59 @@ When a new tool lands with both MCP and CLI surfaces:
inputs like `v4.18.0` are rejected as `INVALID_ARGUMENT` with an
actionable message instead of relying on the current production
backend, which returns a generic error for that input.

### `package_dependencies`

- **Data-first envelope.** `runtime`, `groups`, and `transitive` are
three independent blocks emitted based on what the backend
returned and what the caller asked for, not on additional caller
flags. An MCP agent decides what to read based on what's in the
envelope — no branching on invocation inputs.
- **No `include_groups` input.** The data-first envelope emits the
`groups` block unconditionally when the backend returned
`dependencyGroups`, so an `include_groups: true` input would be a
silently ignored no-op. Deliberately absent from the MCP schema.
- **Dependency list naming.** Every list of dependencies in the
envelope uses the `items` key: `runtime.items`, `groups.items`
(array of groups), each group's nested `items` (array of member
deps). Symmetric and easy to parse.
- **Lifecycle filter echo.** `filter.lifecycles` is the
canonicalised, deduplicated, display-order-sorted array the
backend actually received (never the raw CSV). Emitted only when
the caller supplied a non-empty input.
- **Null vs empty matters.** `groups` is omitted entirely when the
backend returned `dependencyGroups: null` (zero-dep packages);
emitted with `items: []` when the backend returned a non-null
`dependencyGroups` with zero groups (filter matched nothing).
`runtime` is omitted when `dependencies: null` or `direct: null`;
emitted with `count: 0, items: []` when `direct: []`.
- **Terminal-only dedup.** Crates feature groups can contain
duplicate `{name, constraint}` tuples (target-cfg branching). The
terminal formatter collapses them; the JSON envelope preserves
every duplicate the backend emitted. A parity fixture exercises
the round-trip.
- **Preprocessed transitive.** Backend declares `transitive.conflicts`,
`transitive.circularDependencies`, and the DAG as `GenericJSON`,
but the envelope builder decodes them using best-effort shape
detectors so agents see typed data. `transitive.packages[]` carries
`{name, version, importers[]}` records (importer name / version /
constraint pulled from the DAG); `conflicts[]` is typed
`{name, requiredVersions}` when decodable; `circularDependencies[]`
is typed `{cycle: string[]}` when decodable. When a decoder can't
match, that field falls back to raw `GenericJSON[]` so no data is
lost. The raw DAG itself is deliberately dropped from this tool's
envelope — a future `pkg deps-dag` command will expose it under a
typed contract. `groups.environmentConstraints` remains raw
`GenericJSON[]` (no live shape observed yet).
- **Parity assertion policy** (coded in
`src/tools/package-dependencies-parity.test.ts`):
- `toEqual` across the service-sourced success fixtures: happy
flat-runtime, zero-dep (omits `groups`), full-view, optional-
lifecycle (tokio features), multi-lifecycle filter,
filter-matched-nothing (`groups: {items: []}`),
Crates-target-cfg dedup round-trip, versioned match / diff,
`NOT_FOUND`, `VERSION_NOT_FOUND` with structured details,
`BACKEND_ERROR`.
- `toMatchObject` for builder-sourced `INVALID_ARGUMENT` cases:
unsupported registry (`nuget`), tag-style version (`v4.18.0`),
unknown lifecycle token (`dev`).
Loading
Loading