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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.plugin/plugin.json +1 -1
- package/README.md +3 -3
- package/dist/cli.js +405 -229
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-rgvmtzjt.js → chunk-15argn2z.js} +7 -17
- package/dist/shared/{chunk-xzq0ecpa.js → chunk-kmka6wpx.js} +2 -2
- package/dist/shared/{chunk-f2ah8xnh.js → chunk-q6d3ttgn.js} +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +4 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -49,11 +49,11 @@ import {
|
|
|
49
49
|
shouldRunUpdateCheck,
|
|
50
50
|
startTelemetrySpan,
|
|
51
51
|
withTelemetrySpan
|
|
52
|
-
} from "./shared/chunk-
|
|
52
|
+
} from "./shared/chunk-15argn2z.js";
|
|
53
53
|
import {
|
|
54
54
|
__require,
|
|
55
55
|
version
|
|
56
|
-
} from "./shared/chunk-
|
|
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: ${
|
|
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:
|
|
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
|
|
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:
|
|
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 ${
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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}
|
|
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:
|
|
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.
|
|
3120
|
-
fields.push({
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
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
|
|
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(
|
|
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)}
|
|
3142
|
+
parts.push(`${formatCompactNumber(github.openIssues)} issues`);
|
|
3138
3143
|
}
|
|
3139
|
-
|
|
3140
|
-
parts.push("archived");
|
|
3141
|
-
}
|
|
3142
|
-
return parts.join(" · ");
|
|
3144
|
+
return parts.length > 0 ? parts.join(", ") : undefined;
|
|
3143
3145
|
}
|
|
3144
|
-
function
|
|
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
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
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.
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
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 (
|
|
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
|
|
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}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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("
|
|
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 =
|
|
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
|
|
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 =
|
|
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:
|
|
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-
|
|
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-
|
|
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
|
|
6958
|
+
var FEEDBACK_DESCRIPTION = `Submit feedback on a tool result or the GitHits experience.
|
|
6829
6959
|
|
|
6830
|
-
|
|
6831
|
-
|
|
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
|
|
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
|
|
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("
|
|
7904
|
-
accepted: z.boolean().describe("True
|
|
7905
|
-
feedback_text: z.string().optional().describe('Optional
|
|
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
|
|
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
|
-
|
|
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(
|
|
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. " +
|
|
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
|
|
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.
|
|
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(
|
|
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:
|
|
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("
|
|
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(`${
|
|
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(` ${
|
|
8612
|
+
lines.push(` ${bodyLine}`);
|
|
8481
8613
|
}
|
|
8482
8614
|
const hidden = bodyLines.length - visible.length;
|
|
8483
8615
|
if (hidden > 0) {
|
|
8484
|
-
lines.push(` ${dim(
|
|
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}
|
|
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("
|
|
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}
|
|
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(
|
|
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
|
-
|
|
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"`),
|
|
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 (
|
|
8726
|
+
if (textFormat) {
|
|
8591
8727
|
return textResult(formatPackageChangelogTerminal(payload, {
|
|
8592
8728
|
useColors: false,
|
|
8593
|
-
verbose: false,
|
|
8594
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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 = "
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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. " + "
|
|
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(
|
|
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("
|
|
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
|
|
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
|
|
9367
|
+
var CORE_BLOCK = `GitHits provides verified, canonical code examples from global open source for AI agents. Pick a path:
|
|
9207
9368
|
|
|
9208
|
-
|
|
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
|
|
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
|
|
9224
|
-
var
|
|
9225
|
-
var
|
|
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
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
const
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
`
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
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
|
|
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 ? `,
|
|
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:
|
|
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
|
-
|
|
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 = `
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
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:
|
|
9652
|
-
|
|
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
|
|
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 ? `,
|
|
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
|
-
|
|
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.
|
|
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-
|
|
10070
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-kmka6wpx.js");
|
|
9895
10071
|
return createContainer2();
|
|
9896
10072
|
}
|
|
9897
10073
|
function parseTargetSpecs(specs) {
|