githits 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -49,11 +49,11 @@ import {
49
49
  shouldRunUpdateCheck,
50
50
  startTelemetrySpan,
51
51
  withTelemetrySpan
52
- } from "./shared/chunk-rgvmtzjt.js";
52
+ } from "./shared/chunk-15argn2z.js";
53
53
  import {
54
54
  __require,
55
55
  version
56
- } from "./shared/chunk-f2ah8xnh.js";
56
+ } from "./shared/chunk-q6d3ttgn.js";
57
57
 
58
58
  // src/cli.ts
59
59
  import { Command } from "commander";
@@ -411,6 +411,19 @@ function dim(text, useColors) {
411
411
  }
412
412
 
413
413
  // src/shared/pkgseer-registry.ts
414
+ var PKGSEER_REGISTRY_ARGS = [
415
+ "npm",
416
+ "pypi",
417
+ "hex",
418
+ "crates",
419
+ "nuget",
420
+ "maven",
421
+ "zig",
422
+ "vcpkg",
423
+ "packagist",
424
+ "rubygems",
425
+ "go"
426
+ ];
414
427
  var registryMap = {
415
428
  npm: "NPM",
416
429
  pypi: "PYPI",
@@ -420,8 +433,11 @@ var registryMap = {
420
433
  maven: "MAVEN",
421
434
  zig: "ZIG",
422
435
  vcpkg: "VCPKG",
423
- packagist: "PACKAGIST"
436
+ packagist: "PACKAGIST",
437
+ rubygems: "RUBYGEMS",
438
+ go: "GO"
424
439
  };
440
+ var PKGSEER_REGISTRY_LIST = PKGSEER_REGISTRY_ARGS.join(", ");
425
441
  function toPkgseerRegistry(registry) {
426
442
  return registryMap[registry];
427
443
  }
@@ -688,22 +704,12 @@ function isInvalidArgumentError(error2) {
688
704
  return error2.name.startsWith("Invalid") || error2.name.startsWith("Unsupported");
689
705
  }
690
706
  // src/shared/package-spec.ts
691
- var KNOWN_REGISTRIES = [
692
- "npm",
693
- "pypi",
694
- "hex",
695
- "crates",
696
- "nuget",
697
- "maven",
698
- "zig",
699
- "vcpkg",
700
- "packagist"
701
- ];
707
+ var KNOWN_REGISTRIES = PKGSEER_REGISTRY_ARGS;
702
708
 
703
709
  class UnsupportedRegistryError extends Error {
704
710
  attempted;
705
711
  constructor(attempted) {
706
- super(`Unsupported registry "${attempted}". Supported: ${KNOWN_REGISTRIES.join(", ")}.`);
712
+ super(`Unsupported registry "${attempted}". Supported: ${PKGSEER_REGISTRY_LIST}.`);
707
713
  this.attempted = attempted;
708
714
  this.name = "UnsupportedRegistryError";
709
715
  }
@@ -1764,7 +1770,7 @@ function buildListPackageDocsParams(input) {
1764
1770
  }
1765
1771
  const registry = input.registry?.trim().toLowerCase() ?? "";
1766
1772
  if (!isKnownPkgseerRegistryArg(registry)) {
1767
- throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: npm, pypi, hex, crates, nuget, maven, zig, vcpkg, packagist.`);
1773
+ throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: ${PKGSEER_REGISTRY_LIST}.`);
1768
1774
  }
1769
1775
  const params = {
1770
1776
  registry: toPkgseerRegistry(registry),
@@ -2012,9 +2018,11 @@ var SUPPORTED_DEPS_REGISTRIES = new Set([
2012
2018
  "HEX",
2013
2019
  "CRATES",
2014
2020
  "VCPKG",
2015
- "ZIG"
2021
+ "ZIG",
2022
+ "RUBYGEMS",
2023
+ "GO"
2016
2024
  ]);
2017
- var SUPPORTED_DEPS_REGISTRIES_HUMAN = "npm, pypi, hex, crates, vcpkg, and zig";
2025
+ var SUPPORTED_DEPS_REGISTRIES_LIST = PKGSEER_REGISTRY_ARGS.filter((arg) => SUPPORTED_DEPS_REGISTRIES.has(toPkgseerRegistry(arg))).join(", ");
2018
2026
  function supportsDependenciesRegistry(registry) {
2019
2027
  return SUPPORTED_DEPS_REGISTRIES.has(registry);
2020
2028
  }
@@ -2025,11 +2033,11 @@ function buildPackageDependenciesParams(input) {
2025
2033
  }
2026
2034
  const normalisedRegistryArg = input.registry?.trim().toLowerCase() ?? "";
2027
2035
  if (!isKnownPkgseerRegistryArg(normalisedRegistryArg)) {
2028
- throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: npm, pypi, hex, crates, nuget, maven, zig, vcpkg, packagist.`);
2036
+ throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: ${PKGSEER_REGISTRY_LIST}.`);
2029
2037
  }
2030
2038
  const registry = toPkgseerRegistry(normalisedRegistryArg);
2031
2039
  if (!supportsDependenciesRegistry(registry)) {
2032
- throw new UnsupportedDependenciesRegistryError(`pkg deps only supports ${SUPPORTED_DEPS_REGISTRIES_HUMAN}. Got: ${normalisedRegistryArg}.`);
2040
+ throw new UnsupportedDependenciesRegistryError(`pkg deps only supports ${SUPPORTED_DEPS_REGISTRIES_LIST}. Got: ${normalisedRegistryArg}.`);
2033
2041
  }
2034
2042
  const version2 = normaliseVersion(input.version);
2035
2043
  const canonicalLifecycles = resolveLifecycles(input.lifecycle);
@@ -2831,7 +2839,7 @@ function buildPackageSummaryParams(input) {
2831
2839
  }
2832
2840
  const normalisedRegistry = input.registry?.trim().toLowerCase() ?? "";
2833
2841
  if (!isKnownPkgseerRegistryArg(normalisedRegistry)) {
2834
- throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: npm, pypi, hex, crates, nuget, maven, zig, vcpkg, packagist.`);
2842
+ throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: ${PKGSEER_REGISTRY_LIST}.`);
2835
2843
  }
2836
2844
  return {
2837
2845
  params: {
@@ -2892,12 +2900,6 @@ function buildPackageSummarySuccessPayload(summary) {
2892
2900
  const github = buildGithub(pkg.githubRepository);
2893
2901
  if (github)
2894
2902
  payload.github = github;
2895
- if (summary.quickstart?.installCommand) {
2896
- payload.install = summary.quickstart.installCommand;
2897
- }
2898
- const usage = normaliseUsage(summary.quickstart?.usageExample);
2899
- if (usage)
2900
- payload.usage = usage;
2901
2903
  const vulns = buildVulnerabilities(summary.security);
2902
2904
  if (vulns)
2903
2905
  payload.vulnerabilities = vulns;
@@ -2956,20 +2958,12 @@ function buildGithub(github) {
2956
2958
  result.lastPushedAt = lastPushedAt;
2957
2959
  return Object.keys(result).length > 0 ? result : undefined;
2958
2960
  }
2959
- function normaliseUsage(usage) {
2960
- if (!usage)
2961
- return;
2962
- const cleaned = usage.replace(/\r\n/g, `
2963
- `).replace(/\r/g, `
2964
- `);
2965
- return cleaned.length > 0 ? cleaned : undefined;
2966
- }
2967
2961
  function buildVulnerabilities(security) {
2968
2962
  if (!security)
2969
2963
  return;
2970
- const total = security.vulnerabilityCount ?? 0;
2971
- if (total === 0)
2964
+ if (typeof security.vulnerabilityCount !== "number")
2972
2965
  return;
2966
+ const total = security.vulnerabilityCount;
2973
2967
  const result = {
2974
2968
  total,
2975
2969
  affectsLatest: security.hasCurrentVulnerabilities ?? false
@@ -3025,7 +3019,7 @@ function pickChangelogSummary(entry) {
3025
3019
  const firstLine = entry.body.split(/\r?\n/).map((line) => stripMarkdownHeading(line.trim())).find((line) => line.length > 0);
3026
3020
  if (!firstLine)
3027
3021
  return;
3028
- return firstLine.length > 120 ? `${firstLine.slice(0, 120).trimEnd()}…` : firstLine;
3022
+ return firstLine.length > 120 ? `${firstLine.slice(0, 117).trimEnd()}...` : firstLine;
3029
3023
  }
3030
3024
  function stripMarkdownHeading(line) {
3031
3025
  return line.replace(/^#{1,6}\s+/, "").trim();
@@ -3036,7 +3030,7 @@ function formatPackageSummaryTerminal(summary, options = {}) {
3036
3030
  const width = resolveWidth(options.terminalWidth);
3037
3031
  const now = options.now ?? new Date;
3038
3032
  const sections = [];
3039
- const header = lean.license ? `${colorize(lean.name, "bold", useColors)} @ ${lean.version} · ${lean.license}` : `${colorize(lean.name, "bold", useColors)} @ ${lean.version}`;
3033
+ const header = lean.license ? `${colorize(lean.name, "bold", useColors)} @ ${lean.version} | ${lean.license}` : `${colorize(lean.name, "bold", useColors)} @ ${lean.version}`;
3040
3034
  sections.push(header);
3041
3035
  if (lean.description) {
3042
3036
  sections.push(wrapText(lean.description, width));
@@ -3046,9 +3040,6 @@ function formatPackageSummaryTerminal(summary, options = {}) {
3046
3040
  sections.push(fields.join(`
3047
3041
  `));
3048
3042
  }
3049
- if (lean.vulnerabilities) {
3050
- sections.push(formatVulnFooter(lean.vulnerabilities, useColors));
3051
- }
3052
3043
  if (options.verbose) {
3053
3044
  const verbose = buildVerboseSections(lean, useColors);
3054
3045
  if (verbose.length > 0)
@@ -3093,10 +3084,19 @@ function wrapText(text, width) {
3093
3084
  function buildFieldList(lean, summary, useColors, now) {
3094
3085
  const fields = [];
3095
3086
  if (lean.repository) {
3087
+ const repositoryParts = [dim(lean.repository, useColors)];
3088
+ const githubPopularity = formatGithubPopularity(lean.github);
3089
+ if (githubPopularity)
3090
+ repositoryParts.push(`(${githubPopularity})`);
3096
3091
  fields.push({
3097
3092
  label: "Repository",
3098
- value: dim(lean.repository, useColors)
3093
+ value: repositoryParts.join(" ")
3099
3094
  });
3095
+ } else {
3096
+ const githubPopularity = formatGithubPopularity(lean.github);
3097
+ if (githubPopularity) {
3098
+ fields.push({ label: "GitHub", value: githubPopularity });
3099
+ }
3100
3100
  }
3101
3101
  if (lean.homepage) {
3102
3102
  fields.push({ label: "Homepage", value: dim(lean.homepage, useColors) });
@@ -3116,54 +3116,53 @@ function buildFieldList(lean, summary, useColors, now) {
3116
3116
  value: `${formatCompactNumber(lean.downloads.total)} total`
3117
3117
  });
3118
3118
  }
3119
- if (lean.github) {
3120
- fields.push({ label: "GitHub", value: formatGithubLine(lean.github) });
3121
- }
3122
- if (lean.install) {
3123
- fields.push({ label: "Install", value: lean.install });
3119
+ if (lean.vulnerabilities) {
3120
+ fields.push({
3121
+ label: "Vulnerabilities",
3122
+ value: formatVulnerabilityStatus(lean.vulnerabilities)
3123
+ });
3124
3124
  }
3125
3125
  const labelWidth = Math.max(10, ...fields.map((field) => field.label.length));
3126
3126
  return fields.map((field) => `${field.label.padEnd(labelWidth)} ${field.value}`);
3127
3127
  }
3128
- function formatGithubLine(github) {
3128
+ function formatGithubPopularity(github) {
3129
+ if (!github)
3130
+ return;
3129
3131
  const parts = [];
3132
+ if (github.archived) {
3133
+ parts.push("[ARCHIVED]");
3134
+ }
3130
3135
  if (github.stars !== undefined) {
3131
- parts.push(`★ ${formatCompactNumber(github.stars)}`);
3136
+ parts.push(`${formatCompactNumber(github.stars)} stars`);
3132
3137
  }
3133
3138
  if (github.forks !== undefined) {
3134
3139
  parts.push(`${formatCompactNumber(github.forks)} forks`);
3135
3140
  }
3136
3141
  if (github.openIssues !== undefined) {
3137
- parts.push(`${formatCompactNumber(github.openIssues)} open issues`);
3142
+ parts.push(`${formatCompactNumber(github.openIssues)} issues`);
3138
3143
  }
3139
- if (github.archived) {
3140
- parts.push("archived");
3141
- }
3142
- return parts.join(" · ");
3144
+ return parts.length > 0 ? parts.join(", ") : undefined;
3143
3145
  }
3144
- function formatVulnFooter(vulns, useColors) {
3146
+ function formatVulnerabilityStatus(vulns) {
3147
+ if (vulns.total === 0) {
3148
+ return "No active vulnerabilities in latest published version";
3149
+ }
3145
3150
  const noun = vulns.total === 1 ? "vulnerability" : "vulnerabilities";
3146
- const base = `${vulns.total} known ${noun}`;
3147
- const full = vulns.affectsLatest ? `${base} · latest affected` : base;
3148
- return colorize(full, "yellow", useColors);
3151
+ if (vulns.affectsLatest) {
3152
+ return `${vulns.total} active ${noun}; latest affected`;
3153
+ }
3154
+ return `${vulns.total} known ${noun}; latest not affected`;
3149
3155
  }
3150
3156
  function buildVerboseSections(lean, useColors) {
3151
3157
  const blocks = [];
3152
- if (lean.usage) {
3153
- blocks.push([
3154
- highlight("Usage", useColors),
3155
- lean.usage.split(`
3156
- `).map((line) => ` ${line}`).join(`
3157
- `)
3158
- ].join(`
3159
- `));
3158
+ if (lean.github) {
3159
+ const github = formatVerboseGithub(lean.github, useColors);
3160
+ if (github)
3161
+ blocks.push(github);
3160
3162
  }
3161
3163
  if (lean.vulnerabilities?.recent && lean.vulnerabilities.recent.length > 0) {
3162
3164
  blocks.push(formatVerboseAdvisories(lean.vulnerabilities.recent, useColors));
3163
3165
  }
3164
- if (lean.github?.topics && lean.github.topics.length > 0) {
3165
- blocks.push(`${"Topics".padEnd(10)} ${lean.github.topics.join(", ")}`);
3166
- }
3167
3166
  if (lean.recentChanges && lean.recentChanges.length > 0) {
3168
3167
  blocks.push(formatVerboseChanges(lean.recentChanges, useColors));
3169
3168
  }
@@ -3171,6 +3170,23 @@ function buildVerboseSections(lean, useColors) {
3171
3170
 
3172
3171
  `);
3173
3172
  }
3173
+ function formatVerboseGithub(github, useColors) {
3174
+ const fields = [];
3175
+ if (github.language)
3176
+ fields.push({ label: "Language", value: github.language });
3177
+ if (github.lastPushedAt) {
3178
+ fields.push({ label: "Last pushed", value: github.lastPushedAt });
3179
+ }
3180
+ if (github.topics && github.topics.length > 0) {
3181
+ fields.push({ label: "Topics", value: github.topics.join(", ") });
3182
+ }
3183
+ const labelWidth = Math.max(10, ...fields.map((field) => field.label.length));
3184
+ return fields.length > 0 ? [
3185
+ highlight("GitHub", useColors),
3186
+ ...fields.map((field) => ` ${field.label.padEnd(labelWidth)} ${field.value}`)
3187
+ ].join(`
3188
+ `) : undefined;
3189
+ }
3174
3190
  function formatVerboseAdvisories(advisories, useColors) {
3175
3191
  const labelWidth = Math.max(...advisories.map((a) => (a.severityLabel ?? "").length));
3176
3192
  const lines = [highlight("Recent advisories", useColors)];
@@ -3220,12 +3236,22 @@ var SUPPORTED_VULN_REGISTRIES = new Set([
3220
3236
  "NPM",
3221
3237
  "PYPI",
3222
3238
  "HEX",
3223
- "CRATES"
3239
+ "CRATES",
3240
+ "NUGET",
3241
+ "MAVEN",
3242
+ "PACKAGIST",
3243
+ "RUBYGEMS",
3244
+ "GO"
3224
3245
  ]);
3225
- var SUPPORTED_VULN_REGISTRIES_HUMAN = "npm, pypi, hex, and crates";
3246
+ var SUPPORTED_VULN_REGISTRIES_HUMAN = "npm, pypi, hex, crates, nuget, maven, packagist, rubygems, and go";
3226
3247
  function supportsVulnerabilitiesRegistry(registry) {
3227
3248
  return SUPPORTED_VULN_REGISTRIES.has(registry);
3228
3249
  }
3250
+ var ADVISORY_SCOPE_TO_GRAPHQL = {
3251
+ affected: "AFFECTED",
3252
+ non_affecting: "NON_AFFECTING",
3253
+ all: "ALL"
3254
+ };
3229
3255
  function buildPackageVulnerabilitiesParams(input) {
3230
3256
  const trimmedName = input.packageName?.trim() ?? "";
3231
3257
  if (!trimmedName) {
@@ -3233,28 +3259,33 @@ function buildPackageVulnerabilitiesParams(input) {
3233
3259
  }
3234
3260
  const normalisedRegistryArg = input.registry?.trim().toLowerCase() ?? "";
3235
3261
  if (!isKnownPkgseerRegistryArg(normalisedRegistryArg)) {
3236
- throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: npm, pypi, hex, crates, nuget, maven, zig, vcpkg, packagist.`);
3262
+ throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: ${PKGSEER_REGISTRY_LIST}.`);
3237
3263
  }
3238
3264
  const registry = toPkgseerRegistry(normalisedRegistryArg);
3239
3265
  if (!supportsVulnerabilitiesRegistry(registry)) {
3240
3266
  throw new UnsupportedVulnerabilitiesRegistryError(`pkg vulns only supports ${SUPPORTED_VULN_REGISTRIES_HUMAN}. Got: ${normalisedRegistryArg}.`);
3241
3267
  }
3242
- const minSeverity = resolveMinSeverity(input.minSeverity);
3268
+ const severityLabel2 = resolveMinSeverityLabel(input.minSeverity);
3269
+ const minSeverity = severityLabel2 !== undefined ? SEVERITY_LABEL_TO_CVSS[severityLabel2] : undefined;
3243
3270
  const trimmedVersion = input.version?.trim();
3244
3271
  if (trimmedVersion && /^v\d/i.test(trimmedVersion)) {
3245
3272
  throw new InvalidPackageSpecError(`Invalid version '${trimmedVersion}'. Use the canonical package version without a leading 'v' (for example '4.18.0', not 'v4.18.0').`);
3246
3273
  }
3274
+ const advisoryScope = resolveAdvisoryScope(input.advisoryScope);
3275
+ const filterWithScope = buildFilterEcho3(severityLabel2, input.includeWithdrawn, advisoryScope);
3247
3276
  return {
3248
3277
  params: {
3249
3278
  registry,
3250
3279
  packageName: trimmedName,
3251
3280
  version: trimmedVersion && trimmedVersion.length > 0 ? trimmedVersion : undefined,
3252
3281
  minSeverity,
3253
- includeWithdrawn: input.includeWithdrawn
3254
- }
3282
+ includeWithdrawn: input.includeWithdrawn,
3283
+ advisoryScope: advisoryScope ? ADVISORY_SCOPE_TO_GRAPHQL[advisoryScope] : undefined
3284
+ },
3285
+ ...filterWithScope ? { filter: filterWithScope } : {}
3255
3286
  };
3256
3287
  }
3257
- function resolveMinSeverity(raw) {
3288
+ function resolveMinSeverityLabel(raw) {
3258
3289
  if (raw === undefined)
3259
3290
  return;
3260
3291
  const trimmed = raw.trim();
@@ -3264,12 +3295,36 @@ function resolveMinSeverity(raw) {
3264
3295
  if (!isSeverityLabel(lower)) {
3265
3296
  throw new InvalidPackageSpecError(`Unsupported severity '${raw}'. Expected one of: low, medium, high, critical.`);
3266
3297
  }
3267
- return SEVERITY_LABEL_TO_CVSS[lower];
3298
+ return lower;
3299
+ }
3300
+ function buildFilterEcho3(minSeverity, includeWithdrawn, advisoryScope) {
3301
+ const filter = {};
3302
+ if (minSeverity !== undefined)
3303
+ filter.minSeverity = minSeverity;
3304
+ if (includeWithdrawn === true)
3305
+ filter.includeWithdrawn = true;
3306
+ if (advisoryScope !== undefined && advisoryScope !== "affected") {
3307
+ filter.advisoryScope = advisoryScope;
3308
+ }
3309
+ return Object.keys(filter).length > 0 ? filter : undefined;
3310
+ }
3311
+ function resolveAdvisoryScope(raw) {
3312
+ if (raw === undefined)
3313
+ return;
3314
+ const trimmed = raw.trim();
3315
+ if (trimmed.length === 0)
3316
+ return;
3317
+ const lower = trimmed.toLowerCase().replace(/-/g, "_");
3318
+ if (lower === "affected" || lower === "non_affecting" || lower === "all") {
3319
+ return lower;
3320
+ }
3321
+ throw new InvalidPackageSpecError(`Unsupported advisory scope '${raw}'. Expected one of: affected, non_affecting, all.`);
3268
3322
  }
3269
3323
  function isSeverityLabel(value) {
3270
3324
  return value === "low" || value === "medium" || value === "high" || value === "critical";
3271
3325
  }
3272
3326
  // src/shared/package-vulnerabilities-response.ts
3327
+ var DEFAULT_ADVISORY_CAP = 5;
3273
3328
  function buildPackageVulnerabilitiesSuccessPayload(report, options = {}) {
3274
3329
  const pkg = report.package;
3275
3330
  const security = report.security;
@@ -3285,11 +3340,12 @@ function buildPackageVulnerabilitiesSuccessPayload(report, options = {}) {
3285
3340
  if (requestedEcho !== undefined) {
3286
3341
  payload.requestedVersion = requestedEcho;
3287
3342
  }
3288
- if (total > 0) {
3289
- const sortedAdvisories = sortAdvisories(dedupedAdvisories.map(buildAdvisory));
3290
- if (sortedAdvisories.length > 0) {
3291
- payload.advisories = sortedAdvisories;
3292
- }
3343
+ if (options.filter !== undefined) {
3344
+ payload.filter = options.filter;
3345
+ }
3346
+ const sortedAdvisories = sortAdvisories(dedupedAdvisories.map(buildAdvisory));
3347
+ if (sortedAdvisories.length > 0) {
3348
+ payload.advisories = sortedAdvisories;
3293
3349
  }
3294
3350
  const upgradePaths = security?.upgradePaths;
3295
3351
  if (upgradePaths && upgradePaths.length > 0) {
@@ -3335,7 +3391,7 @@ function buildSummary(total, security, dedupedAdvisories) {
3335
3391
  if (typeof security?.currentVersionAffected === "boolean") {
3336
3392
  summary.affected = security.currentVersionAffected;
3337
3393
  }
3338
- if (total === 0)
3394
+ if (dedupedAdvisories.length === 0)
3339
3395
  return summary;
3340
3396
  const bySeverity = computeBySeverity(dedupedAdvisories);
3341
3397
  const anyCounted = Object.values(bySeverity).some((n) => n > 0);
@@ -3723,17 +3779,25 @@ function lowerRegistry3(value) {
3723
3779
  }
3724
3780
  function formatPackageVulnerabilitiesTerminal(report, options = {}) {
3725
3781
  const payload = buildPackageVulnerabilitiesSuccessPayload(report, {
3726
- requestedVersion: options.requestedVersion
3782
+ requestedVersion: options.requestedVersion,
3783
+ filter: options.filter
3727
3784
  });
3728
3785
  const useColors = options.useColors ?? false;
3729
3786
  const verbose = options.verbose ?? false;
3787
+ const surface = options.surface ?? "cli";
3730
3788
  const headerLine = formatHeader(payload, useColors);
3731
3789
  const requestedLine = payload.requestedVersion ? dim(`(requested ${payload.requestedVersion})`, useColors) : undefined;
3790
+ const filterLines = formatFilterLines(payload.filter);
3732
3791
  if (payload.summary.total === 0) {
3733
3792
  const lines = [headerLine];
3734
3793
  if (requestedLine)
3735
3794
  lines.push(requestedLine);
3795
+ lines.push(...filterLines);
3736
3796
  lines.push(formatNoAffectedVulnerabilitiesLine(payload));
3797
+ if (payload.advisories && payload.advisories.length > 0) {
3798
+ const rangeLimit = resolveAffectedRangesLimit(options.terminalWidth);
3799
+ lines.push("", formatAdvisoryList(payload.advisories, verbose, useColors, rangeLimit, surface));
3800
+ }
3737
3801
  return `${lines.join(`
3738
3802
  `)}
3739
3803
  `;
@@ -3742,15 +3806,21 @@ function formatPackageVulnerabilitiesTerminal(report, options = {}) {
3742
3806
  const headerBlock = [headerLine];
3743
3807
  if (requestedLine)
3744
3808
  headerBlock.push(requestedLine);
3809
+ headerBlock.push(...filterLines);
3745
3810
  headerBlock.push(formatSummaryLine(payload, useColors));
3746
- const breakdown = formatBreakdownLine(payload.summary, useColors);
3811
+ const selectedAdvisoryCount = payload.advisories?.length ?? 0;
3812
+ const scope = payload.filter?.advisoryScope;
3813
+ const selectedCountLine = formatSelectedAdvisoryCountLine(selectedAdvisoryCount, scope);
3814
+ if (selectedCountLine)
3815
+ headerBlock.push(selectedCountLine);
3816
+ const breakdown = scope === undefined ? formatBreakdownLine(payload.summary, useColors) : undefined;
3747
3817
  if (breakdown)
3748
3818
  headerBlock.push(breakdown);
3749
3819
  blocks.push(headerBlock.join(`
3750
3820
  `));
3751
3821
  if (payload.advisories && payload.advisories.length > 0) {
3752
3822
  const rangeLimit = resolveAffectedRangesLimit(options.terminalWidth);
3753
- blocks.push(formatAdvisoryList(payload.advisories, verbose, useColors, rangeLimit));
3823
+ blocks.push(formatAdvisoryList(payload.advisories, verbose, useColors, rangeLimit, surface));
3754
3824
  }
3755
3825
  const upgradeFooter = formatUpgradeFooter(payload.upgradePaths);
3756
3826
  if (upgradeFooter)
@@ -3762,7 +3832,29 @@ function formatPackageVulnerabilitiesTerminal(report, options = {}) {
3762
3832
  }
3763
3833
  function formatHeader(payload, useColors) {
3764
3834
  const name = colorize(payload.name, "bold", useColors);
3765
- return `${name} @ ${payload.version} · ${payload.registry}`;
3835
+ return `${name} @ ${payload.version} | ${payload.registry}`;
3836
+ }
3837
+ function formatFilterLines(filter) {
3838
+ if (!filter)
3839
+ return [];
3840
+ const lines = [];
3841
+ if (filter.advisoryScope) {
3842
+ lines.push(`Scope ${formatAdvisoryScope(filter.advisoryScope)}`);
3843
+ }
3844
+ if (filter.minSeverity) {
3845
+ lines.push(`Filter severity >= ${filter.minSeverity}`);
3846
+ }
3847
+ if (filter.includeWithdrawn === true) {
3848
+ lines.push("Filter include withdrawn");
3849
+ }
3850
+ return lines;
3851
+ }
3852
+ function formatAdvisoryScope(scope) {
3853
+ if (scope === "non_affecting")
3854
+ return "historical advisories only";
3855
+ if (scope === "all")
3856
+ return "all package advisories";
3857
+ return scope;
3766
3858
  }
3767
3859
  function formatSummaryLine(payload, useColors) {
3768
3860
  const n = payload.summary.total;
@@ -3778,13 +3870,41 @@ function formatSummaryLine(payload, useColors) {
3778
3870
  return base;
3779
3871
  }
3780
3872
  function formatNoAffectedVulnerabilitiesLine(payload) {
3873
+ const selectedAdvisoryCount = payload.advisories?.length ?? 0;
3874
+ if (payload.filter?.advisoryScope === "non_affecting") {
3875
+ if (selectedAdvisoryCount > 0) {
3876
+ return "No active vulnerabilities affect this version; historical advisories are listed below.";
3877
+ }
3878
+ return "No active vulnerabilities affect this version; no historical advisories match the current filter.";
3879
+ }
3880
+ if (payload.filter?.advisoryScope === "all") {
3881
+ if (selectedAdvisoryCount > 0) {
3882
+ return "No active vulnerabilities affect this version; package advisories are listed below.";
3883
+ }
3884
+ return "No active vulnerabilities affect this version; no package advisories match the current filter.";
3885
+ }
3886
+ if (payload.filter !== undefined) {
3887
+ return "No vulnerabilities matching the filter affect this version.";
3888
+ }
3781
3889
  const historical = payload.summary.nonAffectingVulnerabilityCount ?? 0;
3782
3890
  if (historical > 0) {
3783
3891
  const noun = historical === 1 ? "historical advisory" : "historical advisories";
3784
3892
  const verb = historical === 1 ? "does" : "do";
3785
- return `No vulnerabilities affect this version (${historical} ${noun} ${verb} not apply).`;
3893
+ return `No active vulnerabilities affect this version (${historical} ${noun} ${verb} not apply).`;
3786
3894
  }
3787
- return "No known vulnerabilities affect this version.";
3895
+ return "No active vulnerabilities affect this version.";
3896
+ }
3897
+ function formatSelectedAdvisoryCountLine(count, scope) {
3898
+ if (scope === undefined)
3899
+ return;
3900
+ const noun = count === 1 ? "advisory" : "advisories";
3901
+ if (scope === "non_affecting") {
3902
+ return ` showing ${count} historical ${noun} that do not affect this version`;
3903
+ }
3904
+ if (scope === "all") {
3905
+ return ` showing ${count} package ${noun} across affected and historical scopes`;
3906
+ }
3907
+ return;
3788
3908
  }
3789
3909
  function formatBreakdownLine(summary, useColors) {
3790
3910
  if (summary.total <= 1)
@@ -3810,22 +3930,31 @@ function formatBreakdownLine(summary, useColors) {
3810
3930
  }
3811
3931
  if (parts.length === 0)
3812
3932
  return;
3813
- return ` ${parts.join(" · ")}`;
3933
+ return ` ${parts.join(" | ")}`;
3814
3934
  }
3815
- function formatAdvisoryList(advisories, verbose, useColors, rangeLimit) {
3816
- const labelWidth = Math.max(...advisories.map((a) => severityColumnLabel(a).length));
3935
+ function formatAdvisoryList(advisories, verbose, useColors, rangeLimit, surface) {
3936
+ const renderedAdvisories = verbose ? advisories : advisories.slice(0, DEFAULT_ADVISORY_CAP);
3937
+ const labelWidth = Math.max(...renderedAdvisories.map((a) => severityColumnLabel(a).length));
3817
3938
  const lines = [];
3818
- for (const advisory of advisories) {
3819
- lines.push(...formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimit));
3939
+ for (const advisory of renderedAdvisories) {
3940
+ lines.push(...formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimit, surface));
3820
3941
  lines.push("");
3821
3942
  }
3943
+ const hidden = advisories.length - renderedAdvisories.length;
3944
+ if (hidden > 0) {
3945
+ lines.push(dim(formatAdvisoryCapHint(hidden, surface), useColors));
3946
+ }
3822
3947
  return lines.join(`
3823
3948
  `).trimEnd();
3824
3949
  }
3950
+ function formatAdvisoryCapHint(hidden, surface) {
3951
+ const hint = surface === "mcp" ? "use verbose=true or format=json" : "use -v";
3952
+ return `... (+${hidden} more; ${hint})`;
3953
+ }
3825
3954
  function severityColumnLabel(advisory) {
3826
3955
  if (advisory.isMalicious === true) {
3827
3956
  if (advisory.severityLabel)
3828
- return `MALWARE · ${advisory.severityLabel}`;
3957
+ return `MALWARE | ${advisory.severityLabel}`;
3829
3958
  return "MALWARE";
3830
3959
  }
3831
3960
  return advisory.severityLabel ?? "unrated";
@@ -3854,7 +3983,7 @@ function severityColumnColor(advisory, useColors, padded) {
3854
3983
  return dim(padded, useColors);
3855
3984
  }
3856
3985
  }
3857
- function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimit) {
3986
+ function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimit, surface) {
3858
3987
  const rawLabel = severityColumnLabel(advisory);
3859
3988
  const padded = rawLabel.padEnd(labelWidth);
3860
3989
  const colouredLabel = severityColumnColor(advisory, useColors, padded);
@@ -3871,7 +4000,7 @@ function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimi
3871
4000
  lines.push(` ${label.padEnd(detailWidth)} ${value}`);
3872
4001
  };
3873
4002
  if (advisory.affectedRanges && advisory.affectedRanges.length > 0) {
3874
- pushRow("affected", formatRangeList(advisory.affectedRanges, verbose, useColors, rangeLimit, advisory.affectedVersionRangesCount, advisory.affectedVersionRangesTruncated));
4003
+ pushRow("affected", formatRangeList(advisory.affectedRanges, verbose, useColors, rangeLimit, surface, advisory.affectedVersionRangesCount, advisory.affectedVersionRangesTruncated));
3875
4004
  }
3876
4005
  if (advisory.fixedIn && advisory.fixedIn.length > 0) {
3877
4006
  pushRow("fixed in", advisory.fixedIn.join(", "));
@@ -3898,12 +4027,12 @@ function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimi
3898
4027
  }
3899
4028
  return lines;
3900
4029
  }
3901
- function formatRangeList(ranges, verbose, useColors, limit, totalCount, backendTruncated) {
4030
+ function formatRangeList(ranges, verbose, useColors, limit, surface, totalCount, backendTruncated) {
3902
4031
  const actualTotal = Math.max(totalCount ?? ranges.length, ranges.length);
3903
4032
  const backendHidden = backendTruncated === true ? actualTotal - ranges.length : 0;
3904
4033
  const appendBackendHint = (shown2) => {
3905
4034
  if (backendHidden > 0) {
3906
- const hint2 = dim(`… (+${backendHidden} ranges omitted by service)`, useColors);
4035
+ const hint2 = dim(`... (+${backendHidden} ranges omitted by service)`, useColors);
3907
4036
  return shown2.length > 0 ? `${shown2}, ${hint2}` : hint2;
3908
4037
  }
3909
4038
  return shown2;
@@ -3913,7 +4042,8 @@ function formatRangeList(ranges, verbose, useColors, limit, totalCount, backendT
3913
4042
  }
3914
4043
  const shown = ranges.slice(0, limit).join(", ");
3915
4044
  const localHidden = ranges.length - limit;
3916
- const hintText = backendHidden > 0 ? `… (+${localHidden} more with -v; +${backendHidden} omitted by service)` : `… (+${localHidden} more; use -v)`;
4045
+ const localHint = surface === "mcp" ? "use verbose=true" : "use -v";
4046
+ const hintText = backendHidden > 0 ? `... (+${localHidden} more with ${localHint}; +${backendHidden} omitted by service)` : `... (+${localHidden} more; ${localHint})`;
3917
4047
  const hint = dim(hintText, useColors);
3918
4048
  return `${shown}, ${hint}`;
3919
4049
  }
@@ -4328,6 +4458,7 @@ async function loginFlow(options, deps, output = stdoutLoginOutput) {
4328
4458
  callback = await Promise.race([callbackServer.result, timeoutPromise]);
4329
4459
  if (timeoutId)
4330
4460
  clearTimeout(timeoutId);
4461
+ await callbackServer.close().catch(() => {});
4331
4462
  } catch (error2) {
4332
4463
  if (timeoutId)
4333
4464
  clearTimeout(timeoutId);
@@ -4606,11 +4737,11 @@ function buildUnifiedSearchParams(input) {
4606
4737
  }
4607
4738
  function resolveTargets(target, targets) {
4608
4739
  if (target && targets) {
4609
- throw new InvalidArgumentError("Provide either target or targets, not both.");
4740
+ throw new InvalidArgumentError("Provide either `target` for one search target or `targets` for multiple, not both.");
4610
4741
  }
4611
4742
  const resolved = target ? [target] : targets ?? [];
4612
4743
  if (resolved.length === 0) {
4613
- throw new InvalidArgumentError("At least one target is required.");
4744
+ throw new InvalidArgumentError("Provide either `target` for one search target or `targets` for multiple; neither was set.");
4614
4745
  }
4615
4746
  const deduped = [];
4616
4747
  const seen = new Set;
@@ -4729,7 +4860,7 @@ function combineWarnings(parserWarnings, sourceStatus, hits = [], progress) {
4729
4860
  out.push(...buildHitFreshnessWarnings(hits));
4730
4861
  out.push(...buildProgressFreshnessWarnings(progress));
4731
4862
  out.push(...buildSourceStatusWarnings(sourceStatus));
4732
- return out;
4863
+ return Array.from(new Set(out));
4733
4864
  }
4734
4865
  function buildUnifiedSearchErrorPayload(error2) {
4735
4866
  const mapped = mapCodeNavigationError(error2);
@@ -4940,7 +5071,7 @@ function compactProgress(progress) {
4940
5071
  payload.requestedTargets = progress.requestedTargets;
4941
5072
  }
4942
5073
  if (progress.filters)
4943
- payload.filters = buildFilterEcho3(progress.filters);
5074
+ payload.filters = buildFilterEcho4(progress.filters);
4944
5075
  if (typeof progress.limit === "number")
4945
5076
  payload.limit = progress.limit;
4946
5077
  if (typeof progress.offset === "number")
@@ -4987,7 +5118,7 @@ function compactProgressTarget(target) {
4987
5118
  payload.requestedRefKind = target.requestedRefKind;
4988
5119
  return Object.keys(payload).length > 0 ? payload : undefined;
4989
5120
  }
4990
- function buildFilterEcho3(filters) {
5121
+ function buildFilterEcho4(filters) {
4991
5122
  const echo = {};
4992
5123
  if (filters.kind)
4993
5124
  echo.kind = filters.kind.toLowerCase();
@@ -6084,7 +6215,7 @@ function buildCliListFilesParams(input) {
6084
6215
  throw new InvalidPackageSpecError(rewritten);
6085
6216
  }
6086
6217
  }
6087
- var REGISTRY_SPEC_HINT = /^(npm|pypi|hex|crates|nuget|maven|zig|vcpkg|packagist):/i;
6218
+ var REGISTRY_SPEC_HINT = new RegExp(`^(${PKGSEER_REGISTRY_ARGS.join("|")}):`, "i");
6088
6219
  function resolvePositionals(firstArg, secondArg, hasRepoUrl) {
6089
6220
  if (hasRepoUrl) {
6090
6221
  if (secondArg !== undefined) {
@@ -6110,8 +6241,7 @@ and \`githits code grep\`.
6110
6241
  filters intersect on top.
6111
6242
 
6112
6243
  Addressing: <spec> (registry:name[@version]) OR --repo-url <url>
6113
- --git-ref <ref>. Supported registries: npm, pypi, hex, crates,
6114
- vcpkg, zig, nuget, maven, packagist.
6244
+ --git-ref <ref>. Supported registries: ${PKGSEER_REGISTRY_LIST}.
6115
6245
 
6116
6246
  By default each result is a bare path for easy piping; pass
6117
6247
  --verbose to include language / file-type / size annotations.
@@ -6263,7 +6393,7 @@ grep run. --symbol-field hydrates enclosing symbol metadata (appears under each
6263
6393
  match in --verbose output; full payload in --json).`;
6264
6394
  function registerCodeGrepCommand(pkgCommand) {
6265
6395
  return pkgCommand.command("grep").summary("Deterministic text grep over indexed dependency source").description(PKG_GREP_DESCRIPTION).argument("[spec-or-pattern]", "Spec mode: package spec (e.g. npm:express). Repo mode (with --repo-url): the pattern.").argument("[pattern-or-prefix]", "Spec mode: the pattern. Repo mode: optional path-prefix.").argument("[path-prefix]", "Spec mode only: optional path-prefix. Ignored with --repo-url.").option("--repo-url <url>", "Repository URL addressing (requires --git-ref)").option("--git-ref <ref>", "Tag, commit, branch, or HEAD. Required with --repo-url.").option("--path <path>", "Exact file path to grep").option("--glob <glob>", "Glob scope (repeatable)", collectRepeatable2, []).option("--ext <ext>", "Extension filter without leading dot (repeatable)", collectRepeatable2, []).option("--regex", "Interpret the pattern as RE2 regex").option("--case-sensitive", "Enable ASCII case-sensitive matching").option("-C, --context <n>", "Context lines before and after each match (0-10)").option("-B, --before-context <n>", "Context lines before each match (0-10)").option("-A, --after-context <n>", "Context lines after each match (0-10)").option("--exclude-docs", "Skip files classified as documentation").option("--exclude-tests", "Skip files classified as tests").option("--limit <n>", "Max matches to return on this page (1-1000, default 50)").option("--per-file-limit <n>", "Cap matches per file within this page (0-1000, 0 = unlimited)").option("--cursor <cursor>", "Opaque nextCursor from a previous grep result").option("--symbol-field <field>", `Repeatable; surfaces in --json and under each --verbose match. ${GREP_REPO_SYMBOL_FIELDS_NOTE}`, collectRepeatable2, []).option("--wait <ms>", `Indexing wait timeout (0-${MAX_WAIT_TIMEOUT_MS}, default ${DEFAULT_WAIT_TIMEOUT_MS})`).option("-v, --verbose", "Render grouped output with file headers").option("--json", "Emit the JSON envelope").action(async (arg1, arg2, arg3, options) => {
6266
- const { createContainer: createContainer2 } = await import("./shared/chunk-xzq0ecpa.js");
6396
+ const { createContainer: createContainer2 } = await import("./shared/chunk-kmka6wpx.js");
6267
6397
  const deps = await createContainer2();
6268
6398
  await pkgGrepAction(arg1, arg2, arg3, options, {
6269
6399
  codeNavigationService: deps.codeNavigationService,
@@ -6797,7 +6927,7 @@ function registerExampleCommand(program) {
6797
6927
  });
6798
6928
  }
6799
6929
  async function loadContainer() {
6800
- const { createContainer: createContainer2 } = await import("./shared/chunk-xzq0ecpa.js");
6930
+ const { createContainer: createContainer2 } = await import("./shared/chunk-kmka6wpx.js");
6801
6931
  return createContainer2();
6802
6932
  }
6803
6933
  // src/commands/feedback.ts
@@ -6825,17 +6955,25 @@ async function feedbackAction(solutionId, options, deps) {
6825
6955
  process.exit(1);
6826
6956
  }
6827
6957
  }
6828
- var FEEDBACK_DESCRIPTION = `Submit feedback on a search result.
6958
+ var FEEDBACK_DESCRIPTION = `Submit feedback on a tool result or the GitHits experience.
6829
6959
 
6830
- Rate whether a code example was helpful. Use --accept for positive
6831
- feedback or --reject for negative. Optionally add a message.
6960
+ Two modes:
6961
+ - Solution-tied: pass the [solution_id] from a prior 'githits example'
6962
+ result (shown at the bottom of the markdown / under 'solution_id'
6963
+ in --json) to anchor feedback to that specific result.
6964
+ - Generic: omit [solution_id] to send feedback about any command
6965
+ (search, pkg, docs, code) or the overall experience. A --message
6966
+ is strongly recommended here.
6967
+
6968
+ Use --accept for positive feedback or --reject for negative.
6832
6969
 
6833
6970
  Examples:
6834
6971
  githits feedback abc123 --accept
6835
6972
  githits feedback abc123 --reject -m "Example was outdated"
6836
- githits feedback abc123 --accept --message "Solved my problem" --json`;
6973
+ githits feedback --accept -m "code_grep regex is fast on npm:lodash"
6974
+ githits feedback --reject -m "search missing kotlin support"`;
6837
6975
  function registerFeedbackCommand(program) {
6838
- program.command("feedback").summary("Submit feedback on a search result").description(FEEDBACK_DESCRIPTION).argument("<solution_id>", "Solution ID from search result").addOption(new Option2("--accept", "Mark as helpful").conflicts("reject")).addOption(new Option2("--reject", "Mark as unhelpful").conflicts("accept")).option("-m, --message <text>", "Feedback explanation").option("--json", "Output as JSON for piping").action(async (solutionId, options) => {
6976
+ program.command("feedback").summary("Submit feedback on a tool result or the GitHits experience").description(FEEDBACK_DESCRIPTION).argument("[solution_id]", "Solution ID from a prior 'githits example' result (omit for generic feedback)").addOption(new Option2("--accept", "Mark as helpful").conflicts("reject")).addOption(new Option2("--reject", "Mark as unhelpful").conflicts("accept")).option("-m, --message <text>", "Feedback explanation").option("--json", "Output as JSON for piping").action(async (solutionId, options) => {
6839
6977
  try {
6840
6978
  const deps = await createContainer();
6841
6979
  await feedbackAction(solutionId, options, deps);
@@ -7900,13 +8038,17 @@ function classify3(operation, error2) {
7900
8038
 
7901
8039
  // src/tools/feedback.ts
7902
8040
  var schema = {
7903
- solution_id: z.string().min(1).describe("The solution ID from a previous search result (shown in the result)"),
7904
- accepted: z.boolean().describe("True if the example was helpful/good, False if unhelpful/bad"),
7905
- feedback_text: z.string().optional().describe('Optional text explaining why (e.g., "This solved problem X" or "Example was outdated")')
8041
+ solution_id: z.string().min(1).optional().describe("Optional. Pass the `solution_id` from a prior `get_example` response (shown on the trailing line of the markdown result, or under the `solution_id` key in JSON mode) to anchor feedback to that specific result. Omit for generic feedback about any tool (code/package navigation, search, docs) or the overall experience."),
8042
+ accepted: z.boolean().describe("True for positive feedback (helpful/good), False for negative (unhelpful/bad). Always required."),
8043
+ feedback_text: z.string().optional().describe('Optional context (e.g., "This solved problem X" or "code_grep regex over npm:lodash missed Foo function"). Strongly recommended when `solution_id` is omitted, since there is no specific result to anchor to.')
7906
8044
  };
7907
- var DESCRIPTION = `Submit feedback on a GitHits example result.
8045
+ var DESCRIPTION = `Submit feedback on a tool result or the GitHits experience.
8046
+
8047
+ Two modes:
8048
+ 1. **Solution-tied** — pass the \`solution_id\` from a prior \`get_example\` response to rate that specific result.
8049
+ 2. **Generic** — omit \`solution_id\` to send feedback about any tool (\`search\`, \`code_grep\`, \`code_read\`, \`code_files\`, \`docs_*\`, \`pkg_*\`) or the overall experience.
7908
8050
 
7909
- Call after \`get_example\` to record whether the returned example was used. \`accepted=true\` when it solved the problem or was useful; \`accepted=false\` when it was irrelevant or wrong. Use \`feedback_text\` to add a short reason. Feeds back into ranking quality.`;
8051
+ \`accepted\` is always required (true = positive, false = negative). Add \`feedback_text\` for context strongly recommended in generic mode. Feeds ranking and product quality.`;
7910
8052
  function createFeedbackTool(service) {
7911
8053
  return {
7912
8054
  name: "feedback",
@@ -7930,7 +8072,7 @@ var schema2 = {
7930
8072
  query: z2.string().min(1).describe("Natural-language example-search query for canonical code examples."),
7931
8073
  language: z2.string().min(1).optional().describe("Optional programming language. If omitted, GitHits tries to infer it automatically. Use search_language first only when you need to force a specific language and the exact name is uncertain."),
7932
8074
  license_mode: z2.enum(["strict", "yolo", "custom"]).optional().describe("License filtering mode: strict (default), yolo, or custom."),
7933
- format: z2.enum(["json", "text", "text-v1"]).optional().describe("Response format. Default `text-v1` returns markdown directly with a trailing `solution_id` line when available. Pass `json` for `{result, solution_id?}`.")
8075
+ format: z2.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` returns markdown directly with a trailing `solution_id` line when available. Pass `format: "json"` for `{result, solution_id?}`.')
7934
8076
  };
7935
8077
  var DESCRIPTION2 = `Get verified, canonical code examples from global open source.
7936
8078
 
@@ -7969,17 +8111,7 @@ import { z as z4 } from "zod";
7969
8111
  // src/tools/code-navigation-shared.ts
7970
8112
  import { z as z3 } from "zod";
7971
8113
  var structuredCodeTargetSchema = z3.object({
7972
- registry: z3.enum([
7973
- "npm",
7974
- "pypi",
7975
- "hex",
7976
- "crates",
7977
- "nuget",
7978
- "maven",
7979
- "zig",
7980
- "vcpkg",
7981
- "packagist"
7982
- ]).optional().describe("Package registry (npm, pypi, hex, etc.). Required for package scope."),
8114
+ registry: z3.enum(PKGSEER_REGISTRY_ARGS).optional().describe(`Package registry (${PKGSEER_REGISTRY_LIST}). Required for package scope.`),
7983
8115
  package_name: z3.string().max(255).optional().describe("Package name. Required for package scope."),
7984
8116
  version: z3.string().max(100).optional().describe("Package version, e.g. '4.18.2' (defaults to latest). For package scope only."),
7985
8117
  repo_url: z3.string().optional().describe("Repository URL (GitHub). Required for repo scope. Example: https://github.com/expressjs/express"),
@@ -8065,7 +8197,7 @@ var schema3 = {
8065
8197
  wait_timeout_ms: z4.number().optional(),
8066
8198
  format: z4.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact line-oriented output (matches grouped by file with grep -A/-B notation for context). Pass `format: "json"` for the structured envelope. `text` is an alias for `text-v1`. Errors stay JSON-formatted in either mode for now.')
8067
8199
  };
8068
- var DESCRIPTION3 = "Deterministic text grep over indexed dependency and repository source files. " + "Use this when you know the text pattern you want; use `search` for discovery. " + "Whole-target grep is the default — narrow with `path`, `path_prefix`, `globs`, or `extensions` to keep responses small. " + 'Default response is a compact line-oriented listing (`format: "text-v1"`); pass `format: "json"` for the structured envelope. ' + "Matches chain directly into `code_read` (the `path` and `line` from each match feed straight into `start_line` / `end_line`).";
8200
+ var DESCRIPTION3 = "Deterministic text or regex grep over indexed dependency and repository source files. " + 'Use this when you know the pattern (literal by default; pass `pattern_type: "regex"` for RE2). ' + "Use `search` for discovery instead. " + "Whole-target grep is the default — narrow with `path`, `path_prefix`, `globs`, or `extensions` to keep responses small. " + "Each match's `path` chains into `code_read.path`; pick a window around `match.line` for `code_read.start_line` / `end_line`.";
8069
8201
  function createGrepRepoTool(service) {
8070
8202
  return {
8071
8203
  name: "code_grep",
@@ -8158,9 +8290,9 @@ var schema4 = {
8158
8290
  include_hidden: z5.boolean().optional(),
8159
8291
  limit: z5.number().optional().describe("Max entries to return (1–1000, default 200). Out-of-range values return an `INVALID_ARGUMENT` envelope."),
8160
8292
  wait_timeout_ms: z5.number().optional().describe("Max milliseconds to wait for indexing (0–60000, default 20000). On an `INDEXING` error envelope, retry with a longer timeout or pass a version from `details.availableVersions`."),
8161
- format: z5.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact paths-only listing tuned for agent context efficiency. Pass `format: "json"` for the structured envelope. `text` is an alias for `text-v1`. Errors stay JSON-formatted in either mode for now.')
8293
+ format: z5.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact paths-only listing. Pass `format: "json"` for the structured envelope. `text` is an alias for `text-v1`. Errors stay JSON-formatted in either mode for now.')
8162
8294
  };
8163
- var DESCRIPTION4 = "List files in an indexed dependency. Default response is a compact " + 'paths-only listing (`format: "text-v1"`); pass `format: "json"` ' + "for the structured envelope `{total, hasMore, files: [{path, name, " + "language, fileType, byteSize}], resolution, indexedVersion}`. " + "Address via `target.registry` + `target.package_name` (package " + "scope) or `target.repo_url` + `target.git_ref` (repo scope), " + "mutually exclusive. Narrow with `path`, `path_prefix`, `globs`, " + "`extensions`, `file_types`, `languages`, or file-intent filters. " + "The returned paths feed directly into `code_read` and help scope " + "`code_grep`. Returns an `INDEXING` error envelope when the " + "dependency is being indexed on-demand — retry with a longer " + "`wait_timeout_ms` or use a version from `details.availableVersions`.";
8295
+ var DESCRIPTION4 = "List files in an indexed dependency. Use this to discover paths " + "before `code_read` (when `code_read` returns `FILE_NOT_FOUND` or " + "you don't yet know the path) and to scope `code_grep`. Address " + "via `target.registry` + `target.package_name` (package scope) or " + "`target.repo_url` + `target.git_ref` (repo scope), mutually " + "exclusive. Narrow with `path`, `path_prefix`, `globs`, " + "`extensions`, `file_types`, `languages`, or file-intent filters. " + "JSON envelope shape: `{total, hasMore, files: [{path, name, " + "language, fileType, byteSize}], resolution, indexedVersion}`. " + "Returns an `INDEXING` error envelope when the dependency is being " + "indexed on-demand — retry with a longer `wait_timeout_ms` or use " + "a version from `details.availableVersions`.";
8164
8296
  function createListFilesTool(service) {
8165
8297
  return {
8166
8298
  name: "code_files",
@@ -8232,7 +8364,7 @@ function isTextFormat3(format) {
8232
8364
  // src/tools/list-package-docs.ts
8233
8365
  import { z as z6 } from "zod";
8234
8366
  var schema5 = {
8235
- registry: z6.string().describe("Package registry. One of: npm, pypi, hex, crates, nuget, maven, zig, vcpkg, packagist."),
8367
+ registry: z6.string().describe(`Package registry. One of: ${PKGSEER_REGISTRY_LIST}.`),
8236
8368
  package_name: z6.string().describe("Package name (scoped names ok: @types/node)."),
8237
8369
  version: z6.string().optional().describe("Optional package version."),
8238
8370
  limit: z6.number().optional().describe("Max pages to return (1-500, default 100)."),
@@ -8339,7 +8471,7 @@ function resolveAddressing(input) {
8339
8471
  }
8340
8472
  const normalisedRegistryArg = input.registry?.trim().toLowerCase() ?? "";
8341
8473
  if (!isKnownPkgseerRegistryArg(normalisedRegistryArg)) {
8342
- throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: npm, pypi, hex, crates, nuget, maven, zig, vcpkg, packagist.`);
8474
+ throw new UnsupportedRegistryError(`Unsupported registry '${input.registry}'. Supported: ${PKGSEER_REGISTRY_LIST}.`);
8343
8475
  }
8344
8476
  const registry = toPkgseerRegistry(normalisedRegistryArg);
8345
8477
  return { registry, packageName };
@@ -8453,11 +8585,11 @@ function formatPackageChangelogTerminal(envelope, options) {
8453
8585
  const dateWidth = 10;
8454
8586
  for (const entry of envelope.entries.items) {
8455
8587
  const version2 = entry.version ?? "(unversioned)";
8456
- const date = entry.publishedAt ? formatDate(entry.publishedAt) : dim("", options.useColors);
8588
+ const date = entry.publishedAt ? formatDate(entry.publishedAt) : dim("-", options.useColors);
8457
8589
  const url = entry.htmlUrl ? dim(entry.htmlUrl, options.useColors) : dim("(no link)", options.useColors);
8458
8590
  const padded = padColumn(version2, versionWidth);
8459
8591
  const datePadded = padColumn(date, dateWidth);
8460
- lines.push(`${colorize(padded, "bold", options.useColors)} ${datePadded} ${url}`);
8592
+ lines.push(`${highlight(`${padded} ${datePadded}`, options.useColors)} ${url}`);
8461
8593
  if (entry.body != null) {
8462
8594
  appendBodyLines(lines, entry.body, options);
8463
8595
  }
@@ -8474,28 +8606,28 @@ function appendBodyLines(lines, body, options) {
8474
8606
  }
8475
8607
  const bodyLines = body.split(`
8476
8608
  `);
8477
- const cap = options.verbose ? bodyLines.length : DEFAULT_BODY_PREVIEW_LINES;
8609
+ const cap = options.verbose ? bodyLines.length : options.bodyPreviewLines ?? DEFAULT_BODY_PREVIEW_LINES;
8478
8610
  const visible = bodyLines.slice(0, cap);
8479
8611
  for (const bodyLine of visible) {
8480
- lines.push(` ${dim(bodyLine, options.useColors)}`);
8612
+ lines.push(` ${bodyLine}`);
8481
8613
  }
8482
8614
  const hidden = bodyLines.length - visible.length;
8483
8615
  if (hidden > 0) {
8484
- lines.push(` ${dim(`… (+${hidden} more line${hidden === 1 ? "" : "s"} ${options.fullBodyHint ?? "use --verbose for the full body"})`, options.useColors)}`);
8616
+ lines.push(` ${dim(`... (+${hidden} more line${hidden === 1 ? "" : "s"} - ${options.fullBodyHint ?? "use --verbose for the full body"})`, options.useColors)}`);
8485
8617
  }
8486
8618
  }
8487
8619
  function buildSummaryLine(envelope, options) {
8488
- const identity = envelope.registry && envelope.name ? `${envelope.name} · ${envelope.registry}` : envelope.repoUrl ?? "(unknown)";
8620
+ const identity = envelope.registry && envelope.name ? `${envelope.name} | ${envelope.registry}` : envelope.repoUrl ?? "(unknown)";
8489
8621
  const sourceLabel = envelope.source ? humanizeSource(envelope.source) : "package versions";
8490
8622
  const modeLabel = envelope.mode === "range" ? rangeLabel(envelope) : latestLabel(envelope);
8491
8623
  const countLabel = `${envelope.entries.count} ${plural2("entry", "entries", envelope.entries.count)}`;
8492
8624
  const parts = [identity, `source: ${sourceLabel}`, modeLabel, countLabel];
8493
- return colorize(parts.join(" · "), "bold", options.useColors);
8625
+ return colorize(parts.join(" | "), "bold", options.useColors);
8494
8626
  }
8495
8627
  function rangeLabel(envelope) {
8496
8628
  const from = envelope.filter?.fromVersion ?? "earliest";
8497
8629
  const to = envelope.filter?.toVersion ?? "latest";
8498
- return `range ${from} ${to}`;
8630
+ return `range ${from} -> ${to}`;
8499
8631
  }
8500
8632
  function latestLabel(envelope) {
8501
8633
  if (envelope.filter?.toVersion) {
@@ -8546,7 +8678,7 @@ function stripAnsi(text) {
8546
8678
 
8547
8679
  // src/tools/package-changelog.ts
8548
8680
  var schema6 = {
8549
- registry: z7.string().optional().describe("Package registry (with `package_name`). Mutually exclusive with `repo_url`. Supported: npm, pypi, hex, crates, vcpkg, zig, nuget, maven, packagist."),
8681
+ registry: z7.string().optional().describe(`Package registry (with \`package_name\`). Mutually exclusive with \`repo_url\`. Supported: ${PKGSEER_REGISTRY_LIST}.`),
8550
8682
  package_name: z7.string().optional().describe("Package name (with `registry`). Scoped names ok (`@types/node`). Mutually exclusive with `repo_url`."),
8551
8683
  repo_url: z7.string().optional().describe("GitHub repository URL (https://…). Mutually exclusive with `registry` + `package_name`. Use when agents have a repo URL without a registry mapping."),
8552
8684
  from_version: z7.string().optional().describe("Start of version range. When set, the response returns every entry between `from_version` and `to_version` (or latest) with no count cap — range mode. Mutually exclusive with `limit`. Tag-style `v`-prefixed inputs are rejected."),
@@ -8554,9 +8686,11 @@ var schema6 = {
8554
8686
  limit: z7.number().optional().describe("Latest-mode cap on entry count (1–50, default 10). Rejected with `INVALID_ARGUMENT` when `from_version` is also set or when out of range."),
8555
8687
  git_ref: z7.string().optional().describe("Git branch or tag for CHANGELOG.md source (no effect on GitHub Releases or HexDocs). Defaults to the repository's default branch."),
8556
8688
  include_bodies: z7.boolean().optional().describe("When false, each entry in `entries.items[]` omits its `body` field. Default true. Set false when you only need the version / date / URL timeline — drops 10 KB+ per entry on large release notes."),
8557
- format: z7.enum(["json", "text", "text-v1"]).optional().describe("Response format. Default `text-v1` is compact for agents. Pass `json` for the structured envelope.")
8689
+ verbose: z7.boolean().optional().describe("Text output only. Show full body previews. Mutually exclusive with include_bodies:false and body_lines."),
8690
+ body_lines: z7.number().optional().describe("Text output only. Number of body lines to preview per entry (1-50, default 10). Ignored for format=json and include_bodies:false. Mutually exclusive with verbose:true."),
8691
+ format: z7.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact entry timeline with body previews. Pass `format: "json"` for the structured envelope with full markdown bodies.')
8558
8692
  };
8559
- var DESCRIPTION6 = "Release notes for a package or GitHub repo, newest-first. Default " + "latest mode returns the ten most recent entries (`limit` 1–50). " + "With `from_version`, returns every entry in the " + "`[from_version, to_version]` range (range mode, no count cap). " + "Address via `registry` + `package_name` or `repo_url` (mutually " + 'exclusive). Response includes optional `source` (`"releases"` / ' + '`"changelog_file"` / `"hexdocs"`) when a concrete changelog source ' + 'exists, `mode` (`"latest"` or `"range"`), ' + 'and entries with markdown body previews. Pass `format: "json"` ' + "for the structured envelope with full markdown bodies. Set " + "`include_bodies: false` for a version / date / URL timeline only. " + "Package-version entries without changelog text succeed with `source` " + "omitted; no-source plus no entries returns `NOT_FOUND`. Supports npm, " + "PyPI, Hex, Crates, vcpkg, Zig, NuGet, Maven, Packagist.";
8693
+ var DESCRIPTION6 = "Release notes for a package or GitHub repo, newest-first. Default " + "latest mode returns the ten most recent entries (`limit` 1–50). " + "With `from_version`, returns every entry in the " + "`[from_version, to_version]` range (range mode, no count cap). " + "Address via `registry` + `package_name` or `repo_url` (mutually " + 'exclusive). Response includes optional `source` (`"releases"` / ' + '`"changelog_file"` / `"hexdocs"`) when a concrete changelog source ' + 'exists, `mode` (`"latest"` or `"range"`), and entries with ' + "markdown body previews. Example: " + '`{"registry":"npm","package_name":"express","limit":2}`. ' + "Text output previews 10 body lines by default; use `body_lines` " + "to tune the preview or `verbose:true` for full text bodies. Set " + "`include_bodies: false` for a version / date / URL timeline only; " + 'pass `format: "json"` for the complete structured envelope. ' + "Package-version entries without changelog " + "text succeed with `source` omitted; no-source plus no entries " + "returns `NOT_FOUND`. Supports npm, PyPI, Hex, Crates, NuGet, " + "Maven, Zig, vcpkg, Packagist, RubyGems, and Go.";
8560
8694
  function createPackageChangelogTool(service) {
8561
8695
  return {
8562
8696
  name: "pkg_changelog",
@@ -8565,6 +8699,8 @@ function createPackageChangelogTool(service) {
8565
8699
  annotations: { readOnlyHint: true },
8566
8700
  handler: async (args) => {
8567
8701
  try {
8702
+ const textFormat = isTextFormat5(args.format);
8703
+ const bodyPreviewLines = textFormat ? validateTextOptions(args) : undefined;
8568
8704
  const { params, explicitFilterFields } = buildPackageChangelogParams({
8569
8705
  registry: args.registry,
8570
8706
  packageName: args.package_name,
@@ -8587,11 +8723,12 @@ function createPackageChangelogTool(service) {
8587
8723
  limit: params.limit,
8588
8724
  gitRef: params.gitRef
8589
8725
  });
8590
- if (isTextFormat5(args.format)) {
8726
+ if (textFormat) {
8591
8727
  return textResult(formatPackageChangelogTerminal(payload, {
8592
8728
  useColors: false,
8593
- verbose: false,
8594
- fullBodyHint: 'pass format="json" for full bodies'
8729
+ verbose: args.verbose ?? false,
8730
+ bodyPreviewLines,
8731
+ fullBodyHint: 'pass verbose=true, body_lines=<n>, or format="json" for full bodies'
8595
8732
  }).trimEnd());
8596
8733
  }
8597
8734
  return textResult(JSON.stringify(payload));
@@ -8615,22 +8752,36 @@ function createPackageChangelogTool(service) {
8615
8752
  }
8616
8753
  };
8617
8754
  }
8755
+ function validateTextOptions(args) {
8756
+ if (args.include_bodies === false && args.verbose === true) {
8757
+ throw new InvalidPackageSpecError("verbose:true conflicts with include_bodies:false because bodies are omitted. Drop one of the two options.");
8758
+ }
8759
+ if (args.verbose === true && args.body_lines !== undefined) {
8760
+ throw new InvalidPackageSpecError("body_lines conflicts with verbose:true because verbose already shows full bodies. Drop one of the two options.");
8761
+ }
8762
+ if (args.body_lines === undefined)
8763
+ return;
8764
+ if (!Number.isInteger(args.body_lines) || args.body_lines < 1 || args.body_lines > 50) {
8765
+ throw new InvalidPackageSpecError(`body_lines must be an integer between 1 and 50. Got ${args.body_lines}.`);
8766
+ }
8767
+ return args.body_lines;
8768
+ }
8618
8769
  function isTextFormat5(format) {
8619
8770
  return format === undefined || format === "text" || format === "text-v1";
8620
8771
  }
8621
8772
  // src/tools/package-dependencies.ts
8622
8773
  import { z as z8 } from "zod";
8623
8774
  var schema7 = {
8624
- registry: z8.string().describe("Package registry. Dependency data is available on npm, pypi, hex, crates, vcpkg, and zig."),
8775
+ registry: z8.string().describe(`Package registry. Dependency data is available on ${SUPPORTED_DEPS_REGISTRIES_LIST}.`),
8625
8776
  package_name: z8.string().describe("Package name (scoped names ok: @types/node)."),
8626
8777
  version: z8.string().optional().describe("Specific version to inspect. Defaults to latest when omitted. Tag-style inputs with a leading `v` (for example `v4.18.0`) are rejected — pass the canonical version (`4.18.0`)."),
8627
8778
  lifecycle: z8.union([z8.string(), z8.array(z8.string())]).optional().describe("Lifecycle breadth. Omit for runtime-only. Use `runtime` for explicit runtime-only, a concrete non-runtime lifecycle (`development`, `build`, `peer`, `optional`) for runtime plus matching groups, or `all` for runtime plus all available groups. Accepts a single value, a comma-separated string, or an array; `all` cannot be combined with other values. Uppercase is tolerated."),
8628
8779
  include_transitive: z8.boolean().optional().describe("When true the response gains a `transitive` block with aggregate counts (`edges`, `uniquePackages`), the preprocessed `packages[]` list (each `{name, version}` — the complete install footprint), plus typed `conflicts[]` (`{name, requiredVersions}`) and `circularDependencies[]` (`{cycle: string[]}`) when the backend reported any. Off by default."),
8629
8780
  include_importers: z8.boolean().optional().describe("Requires `include_transitive: true`. When true, each entry in `transitive.packages[]` also carries an `importers` array — every upstream package that pulls it in, with that importer's own resolved version and the constraint it declared. Off by default because adding provenance roughly quadruples the envelope size on heavy graphs. Turn on when you need to trace why a specific transitive dep is present."),
8630
8781
  max_depth: z8.number().int().min(1).max(10).optional().describe("Cap the transitive traversal at this depth (1–10). Omit to get the backend's full graph. Requires `include_transitive: true` — passing `max_depth` without the transitive flag is rejected with `INVALID_ARGUMENT`."),
8631
- format: z8.enum(["json", "text", "text-v1"]).optional().describe("Response format. Default `text-v1` is compact for agents. Pass `json` for the structured envelope.")
8782
+ format: z8.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` compact dependency listing. Pass `format: "json"` for the structured envelope.')
8632
8783
  };
8633
- var DESCRIPTION7 = "Analyze a package's dependency graph. Default output is compact " + "text listing direct runtime dependencies with resolved versions; " + 'pass `format: "json"` for the structured envelope. Non-runtime ' + "groups are omitted by default for token efficiency. Use `lifecycle` " + "with a concrete value for runtime plus matching groups, or `all` " + "for runtime plus all available groups. Set " + "`include_transitive: true` to add a `transitive` block with the " + "full install footprint, conflict detection, and circular-" + "dependency flags; layer `include_importers: true` on top when you " + "also need per-package provenance. Supports npm, PyPI, Hex, Crates, " + "vcpkg, and Zig.";
8784
+ var DESCRIPTION7 = "Analyze a package's dependency graph. Lists direct runtime " + "dependencies with resolved versions; non-runtime groups are " + "omitted by default. Use `lifecycle` with a concrete value for " + "runtime plus matching groups, or `all` for runtime plus every " + "available group. Set `include_transitive: true` to add a " + "`transitive` block with the full install footprint, conflict " + "detection, and circular-dependency flags; layer " + "`include_importers: true` on top when you also need per-package " + "provenance. Supports npm, PyPI, Hex, Crates, Zig, vcpkg, RubyGems, " + "and Go.";
8634
8785
  function createPackageDependenciesTool(service) {
8635
8786
  return {
8636
8787
  name: "pkg_deps",
@@ -8701,11 +8852,12 @@ function isTextFormat6(format) {
8701
8852
  // src/tools/package-summary.ts
8702
8853
  import { z as z9 } from "zod";
8703
8854
  var schema8 = {
8704
- registry: z9.string().describe("Package registry. One of: npm, pypi, hex, crates, nuget, maven, zig, vcpkg, packagist."),
8855
+ registry: z9.string().describe(`Package registry. One of: ${PKGSEER_REGISTRY_LIST}.`),
8705
8856
  package_name: z9.string().describe("Package name (scoped names ok: @types/node)."),
8706
- format: z9.enum(["json", "text", "text-v1"]).optional().describe("Response format. Default `text-v1` is compact for agents. Pass `json` for the structured envelope.")
8857
+ verbose: z9.boolean().optional().describe("Text only. Adds GitHub language/topics/last-pushed, recent advisories, and recent changes. Ignored for format=json."),
8858
+ format: z9.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact package overview. Pass `format: "json"` for the structured envelope.')
8707
8859
  };
8708
- var DESCRIPTION8 = "Get a package overview latest version, license, description, " + "repository, downloads, GitHub stars, install command, recent " + "changes, and a count of known vulnerabilities. Use before " + "recommending a package or to orient on what a dependency is. " + 'Default output is compact text; pass `format: "json"` for the ' + "structured envelope. " + "Works across npm, PyPI, Hex, Crates, NuGet, Maven, Packagist, " + "vcpkg, and Zig. Always returns data for the latest published " + "version.";
8860
+ var DESCRIPTION8 = "Latest-version package overview for dependency triage. Provide " + "`registry` and `package_name` (for example `npm` + `express`). " + "Default text returns license, description, repository popularity " + "(stars/forks/issues and [ARCHIVED] when applicable), downloads, " + "publish age, and vulnerability status. Set `verbose: true` for " + "GitHub language/topics/last-pushed, recent advisories, and recent " + 'changes. Pass `format: "json"` for structured fields. Use ' + "`pkg_vulns` for version-specific vulnerability details.";
8709
8861
  function createPackageSummaryTool(service) {
8710
8862
  return {
8711
8863
  name: "pkg_info",
@@ -8722,6 +8874,7 @@ function createPackageSummaryTool(service) {
8722
8874
  const payload = buildPackageSummarySuccessPayload(summary);
8723
8875
  if (isTextFormat7(args.format)) {
8724
8876
  return textResult(formatPackageSummaryTerminal(summary, {
8877
+ verbose: args.verbose,
8725
8878
  useColors: false
8726
8879
  }).trimEnd());
8727
8880
  }
@@ -8752,14 +8905,16 @@ function isTextFormat7(format) {
8752
8905
  // src/tools/package-vulnerabilities.ts
8753
8906
  import { z as z10 } from "zod";
8754
8907
  var schema9 = {
8755
- registry: z10.string().describe("Package registry. Vulnerability data available on npm, pypi, hex, and crates only."),
8908
+ registry: z10.string().describe("Package registry. Vulnerability data is unavailable for vcpkg and zig."),
8756
8909
  package_name: z10.string().describe("Package name (scoped names ok: @types/node)."),
8757
8910
  version: z10.string().optional().describe("Specific version to check. Defaults to latest when omitted."),
8758
8911
  min_severity: z10.string().optional().describe("Only return advisories at or above this severity (`low`, `medium`, `high`, `critical`; uppercase tolerated). Omit to see all, including null-severity advisories."),
8759
8912
  include_withdrawn: z10.boolean().optional().describe("Include retracted advisories (default: false)."),
8760
- format: z10.enum(["json", "text", "text-v1"]).optional().describe("Response format. Default `text-v1` is compact for agents. Pass `json` for the structured envelope.")
8913
+ advisory_scope: z10.string().optional().describe("Advisory rows to return: `affected` (default), `non_affecting` for historical advisories that do not affect the inspected version, or `all` for both affected and historical advisories. Counts always include affected/non-affecting/all totals."),
8914
+ verbose: z10.boolean().optional().describe("Text output only. Show every advisory and full detail rows; format=json always returns the complete structured envelope."),
8915
+ format: z10.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact advisory summary. Pass `format: "json"` for the structured envelope.')
8761
8916
  };
8762
- var DESCRIPTION9 = "Check known vulnerabilities for a package on npm, PyPI, Hex, or " + "Crates (other registries are not yet supported for vulnerability " + "data). Returns a count summary, each advisory with OSV ID, " + "severity, affected ranges, and fix versions. Malicious-package advisories surface in a separate " + "bucket. Pass `version` to inspect a specific release; otherwise " + "the latest is checked. Use `min_severity` to filter to a threshold " + "(`low`, `medium`, `high`, `critical`) and `include_withdrawn` to " + "also see retracted advisories. Default output is compact text; " + 'pass `format: "json"` for the structured envelope.';
8917
+ var DESCRIPTION9 = "Check known vulnerabilities for a package on npm, PyPI, Hex, " + "Crates, NuGet, Maven, Packagist, RubyGems, or Go (vcpkg and Zig " + "are not supported for vulnerability data). Returns a count summary, each advisory with OSV ID, " + "severity, affected ranges, and fix versions. Malicious-package " + "advisories surface in a separate bucket. Example: " + '`{"registry":"npm","package_name":"lodash","version":"4.17.20","min_severity":"high"}`. ' + "Pass `version` to inspect " + "a pinned release; omit it for latest. Default text is capped for " + "readability; use `verbose:true` for all selected advisory rows or " + '`format:"json"` for the complete envelope. Use ' + "`min_severity` to filter to a threshold (`low`, `medium`, `high`, " + "`critical`) and `include_withdrawn` to also see retracted " + 'advisories. Use `advisory_scope:"non_affecting"` to list ' + "historical advisories that do not affect the inspected version, or " + '`advisory_scope:"all"` to list affected and historical advisories together.';
8763
8918
  function createPackageVulnerabilitiesTool(service) {
8764
8919
  return {
8765
8920
  name: "pkg_vulns",
@@ -8768,21 +8923,26 @@ function createPackageVulnerabilitiesTool(service) {
8768
8923
  annotations: { readOnlyHint: true },
8769
8924
  handler: async (args) => {
8770
8925
  try {
8771
- const { params } = buildPackageVulnerabilitiesParams({
8926
+ const { params, filter } = buildPackageVulnerabilitiesParams({
8772
8927
  registry: args.registry,
8773
8928
  packageName: args.package_name,
8774
8929
  version: args.version,
8775
8930
  minSeverity: args.min_severity,
8776
- includeWithdrawn: args.include_withdrawn
8931
+ includeWithdrawn: args.include_withdrawn,
8932
+ advisoryScope: args.advisory_scope
8777
8933
  });
8778
8934
  const report = await service.packageVulnerabilities(params);
8779
8935
  const payload = buildPackageVulnerabilitiesSuccessPayload(report, {
8780
- requestedVersion: args.version
8936
+ requestedVersion: args.version,
8937
+ filter
8781
8938
  });
8782
8939
  if (isTextFormat8(args.format)) {
8783
8940
  return textResult(formatPackageVulnerabilitiesTerminal(report, {
8784
8941
  useColors: false,
8785
- requestedVersion: args.version
8942
+ requestedVersion: args.version,
8943
+ filter,
8944
+ verbose: args.verbose,
8945
+ surface: "mcp"
8786
8946
  }).trimEnd());
8787
8947
  }
8788
8948
  return textResult(JSON.stringify(payload));
@@ -8902,7 +9062,8 @@ function shouldEmitCappedHint(bounded, payload) {
8902
9062
  }
8903
9063
  function buildCappedHint(payload, originalStart, originalEnd) {
8904
9064
  const requested = describeRequest(originalStart, originalEnd);
8905
- return `Returned lines ${payload.startLine}-${payload.endLine}/${payload.totalLines} ` + `(MCP cap: ${MCP_READ_MAX_SPAN} lines per call; you requested ${requested}). ` + `Pick a focused start_line/end_line window — typical 80-150 lines around a search/code_grep match. ` + `Each retry also costs context, so aim for one well-sized read.`;
9065
+ const continuation = payload.endLine !== undefined ? ` To continue, retry with start_line=${payload.endLine + 1}.` : "";
9066
+ return `Returned lines ${payload.startLine}-${payload.endLine}/${payload.totalLines} ` + `(MCP cap: ${MCP_READ_MAX_SPAN} lines per call; you requested ${requested}).` + `${continuation} ` + `Pick a focused start_line/end_line window — typical 80-150 lines around a search/code_grep match. ` + `Each retry also costs context, so aim for one well-sized read.`;
8906
9067
  }
8907
9068
  function describeRequest(originalStart, originalEnd) {
8908
9069
  if (originalStart === undefined && originalEnd === undefined) {
@@ -9033,9 +9194,9 @@ var schema12 = {
9033
9194
  limit: z13.coerce.number().int().min(1).max(100).optional().describe("Maximum results to return (default 10, max 100)."),
9034
9195
  offset: z13.coerce.number().int().min(0).optional(),
9035
9196
  wait_timeout_ms: z13.coerce.number().int().min(0).max(60000).optional(),
9036
- format: z13.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact line-oriented output tuned for agent context efficiency. Pass `format: "json"` for the structured envelope (programmatic consumers, parity testing). `text` is an alias for `text-v1`. The text format is a public, snapshot-tested contract.')
9197
+ format: z13.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact line-oriented output. Pass `format: "json"` for the structured envelope. `text` is an alias for `text-v1`. The text format is a public, snapshot-tested contract.')
9037
9198
  };
9038
- var DESCRIPTION12 = "Search indexed dependency and repository code, docs, and explicit symbols. " + "Provide either `target` for one target or `targets` for many; omit `sources` to use backend AUTO. " + "The query field uses GitHits discovery syntax (AND/OR/parens/qualifiers; see the parameter description). " + "Structured parameters combine with that query using AND semantics. " + "Results are complete by default — if indexing is still running, the response carries a `searchRef` and no hits; pass it to `search_status` to follow up. " + "Set `allow_partial_results: true` to opt into hits from sources that finished while others continue indexing. " + "Each hit's `type` tells you the follow-up tool: `documentation_page` and `repository_doc` → `docs_read` with `locator.pageId`; `repository_code` and `repository_symbol` → `code_read` with `locator.filePath` (and `locator.startLine`/`endLine` when present).";
9199
+ var DESCRIPTION12 = "Search indexed dependency and repository code, docs, and explicit symbols. " + "Provide either `target` for one target or `targets` for many; omit `sources` to use backend AUTO. " + "Structured parameters combine with the `query` using AND semantics. " + "Complete by default — if indexing is still running, the response carries a `searchRef` and no hits; pass it to `search_status` to follow up. " + "Set `allow_partial_results: true` to opt into hits from sources that finished while others continue indexing. " + "Each hit's `type` tells you the follow-up tool: `documentation_page` and `repository_doc` → `docs_read` with `locator.pageId`; `repository_code` and `repository_symbol` → `code_read` with `locator.filePath` (and `locator.startLine`/`endLine` when present).";
9039
9200
  function createSearchTool(service) {
9040
9201
  return {
9041
9202
  name: "search",
@@ -9139,7 +9300,7 @@ function isTextFormat11(format) {
9139
9300
  import { z as z14 } from "zod";
9140
9301
  var schema13 = {
9141
9302
  query: z14.string().min(1).describe('Language name or partial name to search for (e.g., "python", "type", "java")'),
9142
- format: z14.enum(["json", "text", "text-v1"]).optional().describe("Response format. Default `text-v1` returns one language per line. Pass `json` for the structured array.")
9303
+ format: z14.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` returns one language per line. Pass `format: "json"` for the structured array.')
9143
9304
  };
9144
9305
  var DESCRIPTION13 = `Find the correct language name for \`get_example\` when it is uncertain. Returns up to 5 matching languages by name, display name, or alias. Default output is one language per line; pass \`format: "json"\` for the structured array.`;
9145
9306
  function createSearchLanguageTool(service) {
@@ -9175,10 +9336,10 @@ function renderLanguageMatches(matches) {
9175
9336
  // src/tools/search-status.ts
9176
9337
  import { z as z15 } from "zod";
9177
9338
  var schema14 = {
9178
- search_ref: z15.string().min(1).describe("Search reference returned by search."),
9339
+ search_ref: z15.string().min(1).describe("The `searchRef` field from a prior `search` response (camelCase in the response, snake_case as this parameter). Pass it through unchanged."),
9179
9340
  format: z15.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact line-oriented output matching `search`. Pass `format: "json"` for the structured envelope.')
9180
9341
  };
9181
- var DESCRIPTION14 = "Check progress, fetch partial hits when the original request used allow_partial_results: true, or fetch final results for a prior unified search. " + "Pass the search_ref returned by `search` when the original request did not complete within the wait window.";
9342
+ var DESCRIPTION14 = "Check progress, fetch partial hits (when the original request used `allow_partial_results: true`), or fetch final results for a prior `search` that returned a `searchRef`. " + "Pass the `searchRef` from that response as `search_ref` here (response field is camelCase; this parameter is snake_case).";
9182
9343
  function createSearchStatusTool(service) {
9183
9344
  return {
9184
9345
  name: "search_status",
@@ -9203,50 +9364,54 @@ function isTextFormat13(format) {
9203
9364
  return format === undefined || format === "text" || format === "text-v1";
9204
9365
  }
9205
9366
  // src/commands/mcp-instructions.ts
9206
- var CORE_BLOCK = `GitHits provides verified, canonical code examples from global open source. Use it for global solution synthesis and canonical examples when you're stuck, repeated attempts failed, you need up-to-date API usage, the question is comparative across OSS projects (e.g. "how does X vs Y handle Z"), the answer requires reading how a real codebase implements a feature, or the user mentions GitHits.
9367
+ var CORE_BLOCK = `GitHits provides verified, canonical code examples from global open source for AI agents. Pick a path:
9207
9368
 
9208
- Example workflow: call \`get_example\` with one focused question; pass \`language\` only when you know the exact language name, otherwise call \`search_language\` first. Default output is markdown with a trailing \`solution_id\`; send \`feedback\` on that solution_id. Reuse prior results before searching again. For dependency-specific grounding, use package-scoped \`search\` before global \`get_example\`.`;
9369
+ - Need a canonical example or cross-project pattern with no specific package pinned (you're stuck, repeated attempts failed, the user wants up-to-date API usage, or the user mentioned GitHits) call \`get_example\` for global solution synthesis.
9370
+ - Inspecting a specific known dependency or repository (stack trace points there, verifying how a particular library works, evaluating an upgrade) — call \`search\` plus the indexed code, docs, and package tools listed below.
9371
+ - Question is comparative across OSS projects (e.g. "how does X vs Y handle Z") or requires reading how a real codebase implements a feature — combine the two: \`search\` for known targets, \`get_example\` for cross-project synthesis.
9372
+ - Rate a result or share tool/UX feedback — call \`feedback\` with a \`solution_id\` from a prior \`get_example\` response (solution-tied) or omit it for generic feedback about any tool (\`search\`, \`code_*\`, \`docs_*\`, \`pkg_*\`) or the overall experience.
9373
+
9374
+ \`get_example\` workflow: pass \`language\` only when you know the exact name; otherwise call \`search_language\` first. Default output is markdown with a trailing \`solution_id\`. Reuse prior results before searching again. For dependency-specific grounding, prefer package-scoped \`search\` before global \`get_example\`.`;
9209
9375
  var PACKAGE_TOOLS_PREAMBLE = `Indexed package/source tools inspect third-party dependency source, docs, and registry metadata. Use them when a stack trace points into a dependency, you need to verify how a library works, or you're evaluating whether to add or upgrade a package.
9210
9376
 
9211
9377
  Package spec: \`registry:name[@version]\`. Default outputs are compact \`text-v1\` for agent context efficiency; pass \`format: "json"\` only when you need structured fields for programmatic parsing.`;
9212
- var PKG_INFO_BULLET = '- `pkg_info` compact package overview: latest version, license, downloads, quickstart, and active advisory count. Pass `format: "json"` for the structured envelope.';
9213
- var DOCS_LIST_BULLET = "- `docs_list` — browse hosted and repository-backed package docs. Entries include stable pageIds, source URLs, and repo-file follow-up metadata when available.";
9214
- var DOCS_READ_BULLET = "- `docs_read` — read a documentation page by pageId. Works for both hosted docs and repo-backed docs.";
9215
- var PKG_VULNS_BULLET = '- `pkg_vulns` — compact known CVE / OSV advisory summary for npm, PyPI, Hex, or Crates packages, optionally pinned to `version`. Filter with `min_severity`; include retracted advisories with `include_withdrawn`. Pass `format: "json"` for per-advisory structured fields.';
9216
- var PKG_DEPS_BULLET = '- `pkg_deps` — compact direct runtime deps by default. Use `lifecycle: "runtime"` for explicit runtime-only, a concrete lifecycle for runtime plus matching non-runtime deps, or `lifecycle: "all"` for all groups. Use `include_transitive` for the full graph and `include_importers` for provenance. Pass `format: "json"` for the structured envelope.';
9217
- var PKG_CHANGELOG_BULLET = '- `pkg_changelog` — compact release notes for a package or GitHub repo, newest-first. Default latest mode returns recent entries with markdown body previews; `from_version` switches to range mode. Set `include_bodies: false` for a compact timeline or pass `format: "json"` for full bodies.';
9378
+ var MULTI_TURN_TIP = '**Delegate multi-call work to a sub-agent.** Code-navigation work (`search`, `code_grep`, `code_read`, `code_files`) often takes 3-10 calls. For cross-project comparisons, codebase mapping, pattern surveys, or "how does X actually work" investigations, spawn a sub-task/sub-agent and ask for a compact synthesis. Do it inline only when the raw snippet belongs in the main conversation.';
9218
9379
  var SEARCH_BULLET = '- `search` — unified search across indexed dependency code, docs, and symbols. Omit `sources` for AUTO. Use `sources:["code"]` for implementation/examples/tests text, `sources:["symbol"]` for precise API/entity lookup, and `sources:["docs"]` for guides/reference/changelogs. Default output is compact `text-v1` with ready-to-call follow-up arguments; pass `format: "json"` for structured locators, highlights, and source status. Complete by default; opt into partial hits with `allow_partial_results: true`. Incomplete responses carry a `searchRef`.';
9219
9380
  var SEARCH_STATUS_BULLET = "- `search_status` — follow up a prior `search` by `searchRef` to check progress, fetch partial hits, or fetch final results.";
9220
- var CODE_FILES_BULLET = '- `code_files` — list files in an indexed dependency. `path`, `path_prefix`, and `globs` are OR-ed selectors; extensions, language, file type, and file-intent filters intersect on top. Returned paths feed into `code_read` and help scope `code_grep`; pass `format: "json"` for language/type/size metadata.';
9221
- var CODE_READ_BULLET = "- `code_read` — read a dependency file by `path`. **MCP cap: 150 lines per call**; choose focused `start_line` / `end_line` windows from `search` or `code_grep`. Binary files set `isBinary: true` and omit `content`. On `FILE_NOT_FOUND` / `NOT_FOUND`, call `code_files` for the actual path.";
9222
9381
  var CODE_GREP_BULLET = "- `code_grep` — deterministic text or regex grep over indexed source. Use it when you know the pattern; use `search` for discovery. Narrow with `path`, `path_prefix`, `globs`, or `extensions`. Each match's `path:line` chains into `code_read`.";
9223
- var SEARCH_VS_SYMBOLS_TIP = 'Use code-first grounding for behavioral claims: source, symbols, tests, examples, and call sites beat docs prose. Prefer unified `search` for indexed dependency and repository discovery; use `sources:["symbol"]` when you want symbol-shaped results. Prefer `get_example` for global canonical example retrieval after package-scoped grounding. Use `code_grep` for deterministic text matching and `code_read` for focused-window inspection of a known file.';
9224
- var REFERENCE_FIRST_TIP = "Strategy — reference-first, content-second. Locate symbols and lines with `search` / `code_grep` first, then read only the needed window with `code_read` using explicit `start_line` / `end_line` values, typically 80-150 lines around the match. The MCP `code_read` surface caps each call at 150 lines.";
9225
- var MULTI_TURN_TIP = '**Delegate multi-call work to a sub-agent.** Code-navigation work (`search`, `code_grep`, `code_read`, `code_files`) often takes 3-10 calls. For cross-project comparisons, codebase mapping, pattern surveys, or "how does X actually work" investigations, spawn a sub-task/sub-agent and ask for a compact synthesis. Do it inline only when the raw snippet belongs in the main conversation.';
9382
+ var CODE_READ_BULLET = "- `code_read` read a dependency file by `path`. **MCP cap: 150 lines per call**. When you already have an exact path (e.g. from a stack trace), call this directly; otherwise locate it first with `search` or `code_grep` and pick a focused `start_line` / `end_line` window. Binary files set `isBinary: true` and omit `content`. On `FILE_NOT_FOUND` / `NOT_FOUND`, call `code_files` for the actual path.";
9383
+ var CODE_FILES_BULLET = '- `code_files` list files in an indexed dependency. `path`, `path_prefix`, and `globs` are OR-ed selectors; extensions, language, file type, and file-intent filters intersect on top. Returned paths feed into `code_read` and help scope `code_grep`; pass `format: "json"` for language/type/size metadata.';
9384
+ var DOCS_LIST_BULLET = "- `docs_list` browse hosted and repository-backed package docs. Entries include stable pageIds, source URLs, and repo-file follow-up metadata when available.";
9385
+ var DOCS_READ_BULLET = "- `docs_read` — read a documentation page by pageId. Works for both hosted docs and repo-backed docs.";
9386
+ var PKG_INFO_BULLET = '- `pkg_info` — latest-version package triage by `registry` + `package_name` (e.g. `npm` + `express`): license, repository popularity, downloads, publish age, and vulnerability status. Set `verbose: true` for GitHub language/topics/last-pushed, recent advisories, and recent changes; pass `format: "json"` for structured fields.';
9387
+ var PKG_VULNS_BULLET = '- `pkg_vulns` — compact known CVE / OSV advisory summary for npm, PyPI, Hex, Crates, NuGet, Maven, Packagist, RubyGems, or Go packages; vcpkg and Zig are not supported. Optionally pin `version` (e.g. `npm` + `lodash` + `4.17.20`). Filter with `min_severity`; include retracted advisories with `include_withdrawn`; set `advisory_scope: "non_affecting"` for historical advisories or `"all"` for affected + historical rows. Default text is capped; use `verbose: true` for all selected rows or `format: "json"` for the complete per-advisory envelope.';
9388
+ var PKG_DEPS_BULLET = '- `pkg_deps` — compact direct runtime deps by default. Use `lifecycle: "runtime"` for explicit runtime-only, a concrete lifecycle for runtime plus matching non-runtime deps, or `lifecycle: "all"` for all groups. Use `include_transitive` for the full graph and `include_importers` for provenance. Pass `format: "json"` for the structured envelope.';
9389
+ var PKG_CHANGELOG_BULLET = '- `pkg_changelog` — compact release notes for a package or GitHub repo, newest-first (e.g. `npm` + `express` + `limit: 2`). Default latest mode returns recent entries with 10-line markdown previews; `from_version` switches to range mode. Set `body_lines` to tune text previews, `verbose: true` for full text bodies, `include_bodies: false` for a compact timeline, or `format: "json"` for the complete envelope.';
9390
+ var STRATEGY_TIP = 'Strategy — reference-first. Locate symbols and lines with `search` or `code_grep` first, then read the needed window with `code_read` using explicit `start_line` / `end_line` (typical 80-150 lines around the match; the MCP `code_read` surface caps each call at 150 lines). Source, symbols, tests, and call sites beat docs prose for behavioral claims; use `sources:["symbol"]` when you want symbol-shaped results, `code_grep` for deterministic text matching, and `get_example` for global canonical examples after package-scoped grounding.';
9226
9391
  function buildMcpInstructions(_deps) {
9227
- const sections = [CORE_BLOCK];
9228
- const bullets = [];
9229
- bullets.push(DOCS_LIST_BULLET);
9230
- bullets.push(DOCS_READ_BULLET);
9231
- bullets.push(PKG_INFO_BULLET);
9232
- bullets.push(PKG_VULNS_BULLET);
9233
- bullets.push(PKG_DEPS_BULLET);
9234
- bullets.push(PKG_CHANGELOG_BULLET);
9235
- bullets.push(SEARCH_BULLET);
9236
- bullets.push(SEARCH_STATUS_BULLET);
9237
- bullets.push(CODE_FILES_BULLET);
9238
- bullets.push(CODE_READ_BULLET);
9239
- bullets.push(CODE_GREP_BULLET);
9240
- const parts = [PACKAGE_TOOLS_PREAMBLE];
9241
- parts.push(MULTI_TURN_TIP);
9242
- parts.push(bullets.join(`
9243
- `));
9244
- parts.push(REFERENCE_FIRST_TIP);
9245
- parts.push(SEARCH_VS_SYMBOLS_TIP);
9246
- sections.push(parts.join(`
9392
+ const bullets = [
9393
+ SEARCH_BULLET,
9394
+ SEARCH_STATUS_BULLET,
9395
+ CODE_GREP_BULLET,
9396
+ CODE_READ_BULLET,
9397
+ CODE_FILES_BULLET,
9398
+ DOCS_LIST_BULLET,
9399
+ DOCS_READ_BULLET,
9400
+ PKG_INFO_BULLET,
9401
+ PKG_VULNS_BULLET,
9402
+ PKG_DEPS_BULLET,
9403
+ PKG_CHANGELOG_BULLET
9404
+ ];
9405
+ const packageSection = [
9406
+ PACKAGE_TOOLS_PREAMBLE,
9407
+ MULTI_TURN_TIP,
9408
+ bullets.join(`
9409
+ `),
9410
+ STRATEGY_TIP
9411
+ ].join(`
9247
9412
 
9248
- `));
9249
- return sections.join(`
9413
+ `);
9414
+ return [CORE_BLOCK, packageSection].join(`
9250
9415
 
9251
9416
  `);
9252
9417
  }
@@ -9445,7 +9610,7 @@ function formatChangelogTerminalError(mapped) {
9445
9610
  if (available && available.length > 0) {
9446
9611
  const sample = available.slice(0, 5).join(", ");
9447
9612
  const more = available.length - 5;
9448
- const suffix = more > 0 ? `, (+${more} more)` : "";
9613
+ const suffix = more > 0 ? `, ... (+${more} more)` : "";
9449
9614
  lines.push(` available: ${sample}${suffix}`);
9450
9615
  }
9451
9616
  return lines.join(`
@@ -9461,8 +9626,7 @@ Addressing: <spec> (registry:name) OR --repo-url <url>. Source
9461
9626
  (GitHub Releases, CHANGELOG.md, or HexDocs) is shown on the summary
9462
9627
  line.
9463
9628
 
9464
- Package spec: <registry>:<name>. Supported registries: npm, pypi,
9465
- hex, crates, vcpkg, zig, nuget, maven, packagist. \`<spec>@<version>\`
9629
+ Package spec: <registry>:<name>. Supported registries: ${PKGSEER_REGISTRY_LIST}. \`<spec>@<version>\`
9466
9630
  is NOT accepted here — use --to <version> for "entries up to this
9467
9631
  version".`;
9468
9632
  function registerPkgChangelogCommand(pkgCommand) {
@@ -9585,8 +9749,7 @@ groups). Concrete --lifecycle values include runtime plus matching groups.
9585
9749
  conflict detection, and circular-dependency flags.
9586
9750
 
9587
9751
  Package spec: <registry>:<name>[@<version>]. Supported registries:
9588
- npm, pypi, hex, crates, vcpkg, zig. Omit @<version> for the latest
9589
- release.`;
9752
+ ${SUPPORTED_DEPS_REGISTRIES_LIST}. Omit @<version> for the latest release.`;
9590
9753
  function registerPkgDepsCommand(pkgCommand) {
9591
9754
  return pkgCommand.command("deps").summary("Analyze dependencies for a package").description(PKG_DEPS_DESCRIPTION).argument("<spec>", "Package spec, e.g. npm:express or npm:express@4.18.0").option("-l, --lifecycle <phases>", "Dependency lifecycle breadth (runtime, development, build, peer, optional, all; comma-separated for multi-select except all).").option("-t, --transitive", "Include aggregate transitive counts, conflicts, and circular dependencies").option("--depth <n>", "Cap transitive traversal depth (1-10). Omit for the full graph.").option("-v, --verbose", "Show conditionType / selectionMode / environmentConstraints metadata in the groups view").option("--json", "Emit the lean JSON envelope").action(async (spec, options) => {
9592
9755
  const deps = await createContainer();
@@ -9643,17 +9806,20 @@ function handlePkgInfoCommandError(error2, json) {
9643
9806
  console.error(formatMappedErrorForTerminal(mapped));
9644
9807
  process.exit(1);
9645
9808
  }
9646
- var PKG_INFO_DESCRIPTION = `Get a package overview latest version, license, description,
9647
- repository, downloads, GitHub stars, install command, and known
9648
- vulnerabilities. Use before picking a dependency or to orient on what
9649
- a package is.
9809
+ var PKG_INFO_DESCRIPTION = `Latest-version package overview for dependency triage.
9810
+
9811
+ Default output shows license, description, repository popularity
9812
+ (stars/forks/issues and [ARCHIVED] when applicable), downloads,
9813
+ publish age, and vulnerability status. --verbose adds GitHub
9814
+ language/topics/last-pushed, recent advisories, and recent changes.
9650
9815
 
9651
- Package spec: <registry>:<name>. Supported registries: npm, pypi, hex,
9652
- crates, nuget, maven, zig, vcpkg, packagist.
9816
+ Package spec: <registry>:<name>. Supported registries: ${PKGSEER_REGISTRY_LIST}.
9817
+
9818
+ Example: githits pkg info npm:express
9653
9819
 
9654
9820
  Always returns data for the latest published version.`;
9655
9821
  function registerPkgInfoCommand(pkgCommand) {
9656
- return pkgCommand.command("info").summary("Show a package overview").description(PKG_INFO_DESCRIPTION).argument("<spec>", "Package spec, e.g. npm:express or pypi:requests").option("-v, --verbose", "Show advisories, install usage, topics, and recent changes").option("--json", "Emit the lean JSON envelope").action(async (spec, options) => {
9822
+ return pkgCommand.command("info").summary("Show a package overview").description(PKG_INFO_DESCRIPTION).argument("<spec>", "Package spec, e.g. npm:express or pypi:requests").option("-v, --verbose", "Show GitHub language/topics/last-pushed, recent advisories, and recent changes").option("--json", "Emit the lean JSON envelope").action(async (spec, options) => {
9657
9823
  const deps = await createContainer();
9658
9824
  await pkgInfoAction(spec, options, {
9659
9825
  packageIntelligenceService: deps.packageIntelligenceService,
@@ -9672,17 +9838,19 @@ async function pkgVulnsAction(spec, options, deps) {
9672
9838
  throw new InvalidPackageSpecError("Package intelligence is not configured for this environment.");
9673
9839
  }
9674
9840
  const parsed = parsePackageSpec(spec);
9675
- const { params } = buildPackageVulnerabilitiesParams({
9841
+ const { params, filter } = buildPackageVulnerabilitiesParams({
9676
9842
  registry: parsed.registry,
9677
9843
  packageName: parsed.name,
9678
9844
  version: parsed.version,
9679
9845
  minSeverity: options.severity,
9680
- includeWithdrawn: options.includeWithdrawn
9846
+ includeWithdrawn: options.includeWithdrawn,
9847
+ advisoryScope: options.scope
9681
9848
  });
9682
9849
  const report = await deps.packageIntelligenceService.packageVulnerabilities(params);
9683
9850
  if (options.json) {
9684
9851
  const payload = buildPackageVulnerabilitiesSuccessPayload(report, {
9685
- requestedVersion: parsed.version
9852
+ requestedVersion: parsed.version,
9853
+ filter
9686
9854
  });
9687
9855
  console.log(JSON.stringify(payload));
9688
9856
  return;
@@ -9691,6 +9859,8 @@ async function pkgVulnsAction(spec, options, deps) {
9691
9859
  verbose: options.verbose,
9692
9860
  useColors: shouldUseColors(),
9693
9861
  requestedVersion: parsed.version,
9862
+ filter,
9863
+ surface: "cli",
9694
9864
  terminalWidth: process.stdout.columns
9695
9865
  });
9696
9866
  process.stdout.write(output);
@@ -9733,24 +9903,30 @@ function formatVulnsTerminalError(mapped) {
9733
9903
  if (available && available.length > 0) {
9734
9904
  const sample = available.slice(0, 5).join(", ");
9735
9905
  const more = available.length - 5;
9736
- const suffix = more > 0 ? `, (+${more} more)` : "";
9906
+ const suffix = more > 0 ? `, ... (+${more} more)` : "";
9737
9907
  lines.push(` available: ${sample}${suffix}`);
9738
9908
  }
9739
9909
  return lines.join(`
9740
9910
  `);
9741
9911
  }
9742
9912
  var PKG_VULNS_DESCRIPTION = `Show known vulnerabilities for a package. Lists CVE / OSV advisories
9743
- with severity, affected version ranges, and fix versions.
9744
- Malicious-package advisories are flagged prominently.
9913
+ with severity, affected version ranges, and fix versions. Default text is
9914
+ capped for readability; use --verbose for all selected advisory rows or --json
9915
+ for the complete structured envelope.
9745
9916
 
9746
9917
  Package spec: <registry>:<name>[@<version>]. Supported registries:
9747
- npm, pypi, hex, crates. Omit @<version> to check the latest release.
9918
+ npm, pypi, hex, crates, nuget, maven, packagist, rubygems, go. vcpkg and zig are not supported.
9919
+ Omit @<version> to check the latest release.
9920
+ Example: githits pkg vulns npm:lodash@4.17.20 --severity high
9748
9921
 
9749
9922
  Severity filter (--severity) and withdrawn-advisory visibility
9750
9923
  (--include-withdrawn) are passed through to the backend; the
9751
- returned count reflects whatever survived the filter.`;
9924
+ returned count reflects whatever survived the filter and active filters
9925
+ are echoed in text and JSON output. Use --scope non_affecting to list
9926
+ historical advisories that do not affect the inspected version, or --scope all
9927
+ to list affected and historical package advisories together.`;
9752
9928
  function registerPkgVulnsCommand(pkgCommand) {
9753
- return pkgCommand.command("vulns").summary("List known vulnerabilities for a package").description(PKG_VULNS_DESCRIPTION).argument("<spec>", "Package spec, e.g. npm:express or npm:express@4.18.0").option("-s, --severity <level>", "Only show advisories at or above this severity (low, medium, high, critical). Omit to see all.").option("--include-withdrawn", "Include retracted advisories (default: off)").option("-v, --verbose", "Show aliases, modified/withdrawn dates, and malicious-advisory markers").option("--json", "Emit the lean JSON envelope").action(async (spec, options) => {
9929
+ return pkgCommand.command("vulns").summary("List known vulnerabilities for a package").description(PKG_VULNS_DESCRIPTION).argument("<spec>", "Package spec, e.g. npm:express or npm:express@4.18.0").option("-s, --severity <level>", "Only show advisories at or above this severity (low, medium, high, critical). Omit to see all.").option("--scope <scope>", "Advisory rows to return: affected, non_affecting, all (default: affected)").option("--include-withdrawn", "Include retracted advisories (default: off)").option("-v, --verbose", "Show aliases, modified/withdrawn dates, and malicious-advisory markers").option("--json", "Emit the lean JSON envelope").action(async (spec, options) => {
9754
9930
  const deps = await createContainer();
9755
9931
  await pkgVulnsAction(spec, options, {
9756
9932
  packageIntelligenceService: deps.packageIntelligenceService,
@@ -9767,7 +9943,7 @@ async function registerPkgCommandGroup(program, options = {}) {
9767
9943
  if (!registration.shouldRegister) {
9768
9944
  return;
9769
9945
  }
9770
- const pkgCommand = program.command("pkg").summary("Package metadata: info, vulnerabilities, dependencies, changelog").description("Inspect package metadata from npm, PyPI, Hex, Crates, NuGet, Maven, Packagist, vcpkg, and Zig: overviews, advisories, dependency graphs, and changelogs. For source-level operations inside a dependency, use `githits code`.");
9946
+ const pkgCommand = program.command("pkg").summary("Package metadata: info, vulnerabilities, dependencies, changelog").description("Inspect package metadata from npm, PyPI, Hex, Crates, NuGet, Maven, Packagist, RubyGems, Go, vcpkg, and Zig: overviews, advisories, dependency graphs, and changelogs. Advisory data is unavailable for vcpkg and Zig. For source-level operations inside a dependency, use `githits code`.");
9771
9947
  registerPkgInfoCommand(pkgCommand);
9772
9948
  registerPkgVulnsCommand(pkgCommand);
9773
9949
  registerPkgDepsCommand(pkgCommand);
@@ -9891,7 +10067,7 @@ function requireSearchService(deps) {
9891
10067
  return deps.codeNavigationService;
9892
10068
  }
9893
10069
  async function loadContainer2() {
9894
- const { createContainer: createContainer2 } = await import("./shared/chunk-xzq0ecpa.js");
10070
+ const { createContainer: createContainer2 } = await import("./shared/chunk-kmka6wpx.js");
9895
10071
  return createContainer2();
9896
10072
  }
9897
10073
  function parseTargetSpecs(specs) {