githits 0.2.3 → 0.3.0
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/GEMINI.md +4 -6
- package/README.md +31 -17
- package/commands/help.md +4 -4
- package/dist/cli.js +668 -186
- package/dist/index.js +1 -1
- package/dist/shared/chunk-1gqfte6e.js +11 -0
- package/dist/shared/{chunk-a9js75mw.js → chunk-5mk9k2gv.js} +1269 -626
- package/dist/shared/{chunk-js72s35a.js → chunk-gxya097b.js} +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +4 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/commands/help.md +4 -4
- package/plugins/claude/skills/search/SKILL.md +2 -2
- package/skills/search/SKILL.md +2 -2
- package/dist/shared/chunk-9zze00cp.js +0 -11
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
AuthConfigError,
|
|
4
|
+
AuthStoragePolicyError,
|
|
3
5
|
AuthenticationError,
|
|
6
|
+
CLIENT_UPDATE_REQUIRED_REASON,
|
|
7
|
+
ClientUpdateRequiredError,
|
|
4
8
|
CodeNavigationAccessError,
|
|
5
9
|
CodeNavigationBackendError,
|
|
6
10
|
CodeNavigationFeatureFlagRequiredError,
|
|
@@ -16,6 +20,7 @@ import {
|
|
|
16
20
|
FileSystemServiceImpl,
|
|
17
21
|
MalformedCodeNavigationResponseError,
|
|
18
22
|
MalformedPackageIntelligenceResponseError,
|
|
23
|
+
NpmRegistryUpdateCheckService,
|
|
19
24
|
PackageIntelligenceAccessError,
|
|
20
25
|
PackageIntelligenceBackendError,
|
|
21
26
|
PackageIntelligenceChangelogSourceNotFoundError,
|
|
@@ -26,30 +31,155 @@ import {
|
|
|
26
31
|
PackageIntelligenceValidationError,
|
|
27
32
|
PackageIntelligenceVersionNotFoundError,
|
|
28
33
|
PromptServiceImpl,
|
|
34
|
+
createAuthCommandDependencies,
|
|
35
|
+
createAuthStatusDependencies,
|
|
29
36
|
createContainer,
|
|
30
37
|
debugLog,
|
|
31
38
|
endTelemetrySpan,
|
|
32
39
|
flushTelemetry,
|
|
33
|
-
|
|
40
|
+
formatRequiredUpdateNotice,
|
|
41
|
+
formatUpdateNotice,
|
|
34
42
|
getCodeNavigationUrl,
|
|
35
|
-
getEnvApiToken,
|
|
36
|
-
isCodeNavigationCliOverrideEnabled,
|
|
37
43
|
isTelemetryEnabled,
|
|
38
44
|
refreshExpiredToken,
|
|
39
|
-
resolveStartupCodeNavigationRegistrationState,
|
|
40
45
|
setClientMode,
|
|
41
46
|
setMcpClientVersionProvider,
|
|
47
|
+
shouldRunRequiredUpdateEnforcement,
|
|
48
|
+
shouldRunUpdateCheck,
|
|
42
49
|
startTelemetrySpan,
|
|
43
50
|
withTelemetrySpan
|
|
44
|
-
} from "./shared/chunk-
|
|
51
|
+
} from "./shared/chunk-5mk9k2gv.js";
|
|
45
52
|
import {
|
|
46
53
|
__require,
|
|
47
54
|
version
|
|
48
|
-
} from "./shared/chunk-
|
|
55
|
+
} from "./shared/chunk-gxya097b.js";
|
|
49
56
|
|
|
50
57
|
// src/cli.ts
|
|
51
58
|
import { Command } from "commander";
|
|
52
59
|
|
|
60
|
+
// src/cli/update-check.ts
|
|
61
|
+
function startUpdateCheckTask(service) {
|
|
62
|
+
const controller = new AbortController;
|
|
63
|
+
return {
|
|
64
|
+
promise: service.checkForUpdate(controller.signal),
|
|
65
|
+
abort: () => controller.abort()
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function startRequiredUpdateRefreshTask(service) {
|
|
69
|
+
const controller = new AbortController;
|
|
70
|
+
return {
|
|
71
|
+
promise: service.refreshRequiredUpdateStatus(controller.signal),
|
|
72
|
+
abort: () => controller.abort()
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function startUpdateCheckTaskForInvocation(options) {
|
|
76
|
+
if (!shouldRunUpdateCheck({
|
|
77
|
+
args: options.args,
|
|
78
|
+
env: options.env,
|
|
79
|
+
stderrIsTTY: options.stderrIsTTY,
|
|
80
|
+
stdinIsTTY: options.stdinIsTTY,
|
|
81
|
+
stdoutIsTTY: options.stdoutIsTTY
|
|
82
|
+
})) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
return startUpdateCheckTask(options.createService());
|
|
86
|
+
}
|
|
87
|
+
function startRequiredUpdateRefreshTaskForInvocation(options) {
|
|
88
|
+
if (!shouldRunRequiredUpdateEnforcement({
|
|
89
|
+
args: options.args,
|
|
90
|
+
env: options.env
|
|
91
|
+
})) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
return startRequiredUpdateRefreshTask(options.createService());
|
|
95
|
+
}
|
|
96
|
+
async function enforceCachedRequiredUpdateForInvocation(options) {
|
|
97
|
+
if (!shouldRunRequiredUpdateEnforcement({
|
|
98
|
+
args: options.args,
|
|
99
|
+
env: options.env
|
|
100
|
+
})) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const notice = await options.createService().getRequiredUpdateNotice();
|
|
104
|
+
if (!notice) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (isJsonInvocation(options.args)) {
|
|
108
|
+
options.stderr.write(`${JSON.stringify(requiredUpdateEnvelope(notice))}
|
|
109
|
+
`);
|
|
110
|
+
} else {
|
|
111
|
+
options.stderr.write(`${formatRequiredUpdateNotice(notice)}
|
|
112
|
+
`);
|
|
113
|
+
}
|
|
114
|
+
options.exit(1);
|
|
115
|
+
}
|
|
116
|
+
async function runWithUpdateCheckFlush(action, task, options) {
|
|
117
|
+
try {
|
|
118
|
+
return await action();
|
|
119
|
+
} finally {
|
|
120
|
+
await flushRequiredUpdateRefresh(options.requiredUpdateRefreshTask, {
|
|
121
|
+
timeoutMs: options.timeoutMs
|
|
122
|
+
});
|
|
123
|
+
await flushUpdateCheckNotice(task, options);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function flushRequiredUpdateRefresh(task, options = {}) {
|
|
127
|
+
if (!task) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const timeoutMs = options.timeoutMs ?? 50;
|
|
131
|
+
let timeout;
|
|
132
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
133
|
+
timeout = setTimeout(() => resolve("timeout"), timeoutMs);
|
|
134
|
+
});
|
|
135
|
+
const result = await Promise.race([task.promise, timeoutPromise]);
|
|
136
|
+
if (timeout) {
|
|
137
|
+
clearTimeout(timeout);
|
|
138
|
+
}
|
|
139
|
+
if (result === "timeout") {
|
|
140
|
+
task.abort();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async function flushUpdateCheckNotice(task, options) {
|
|
144
|
+
if (!task) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const timeoutMs = options.timeoutMs ?? 50;
|
|
148
|
+
let timeout;
|
|
149
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
150
|
+
timeout = setTimeout(() => resolve("timeout"), timeoutMs);
|
|
151
|
+
});
|
|
152
|
+
const result = await Promise.race([task.promise, timeoutPromise]);
|
|
153
|
+
if (timeout) {
|
|
154
|
+
clearTimeout(timeout);
|
|
155
|
+
}
|
|
156
|
+
if (result === "timeout") {
|
|
157
|
+
task.abort();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (!result) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
options.stderr.write(`${formatUpdateNotice(result)}
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
function isJsonInvocation(args) {
|
|
167
|
+
return args.includes("--json");
|
|
168
|
+
}
|
|
169
|
+
function requiredUpdateEnvelope(notice) {
|
|
170
|
+
return {
|
|
171
|
+
error: `Update required: ${notice.reason}`,
|
|
172
|
+
code: "UPDATE_REQUIRED",
|
|
173
|
+
retryable: false,
|
|
174
|
+
details: {
|
|
175
|
+
currentVersion: notice.currentVersion,
|
|
176
|
+
...notice.latestKnownVersion ? { latestKnownVersion: notice.latestKnownVersion } : {},
|
|
177
|
+
updateCommand: notice.updateCommand,
|
|
178
|
+
reason: notice.reason
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
53
183
|
// src/commands/auth-status.ts
|
|
54
184
|
function displayExpiry(expiresAt) {
|
|
55
185
|
if (!expiresAt) {
|
|
@@ -65,23 +195,12 @@ function displayExpiry(expiresAt) {
|
|
|
65
195
|
console.log(` Expires: in ${minutesLeft} minute${minutesLeft !== 1 ? "s" : ""}`);
|
|
66
196
|
}
|
|
67
197
|
}
|
|
68
|
-
function displayCodeNavigationStatus(capability, overrideEnabled) {
|
|
69
|
-
console.log(` Code navigation: ${capability}`);
|
|
70
|
-
console.log(` CLI override: ${overrideEnabled ? "enabled" : "disabled"}`);
|
|
71
|
-
}
|
|
72
198
|
async function authStatusAction(deps) {
|
|
73
|
-
const {
|
|
74
|
-
authStorage,
|
|
75
|
-
authService,
|
|
76
|
-
mcpUrl,
|
|
77
|
-
envApiToken,
|
|
78
|
-
codeNavigationCliOverrideEnabled
|
|
79
|
-
} = deps;
|
|
199
|
+
const { authStorage, authService, mcpUrl, envApiToken } = deps;
|
|
80
200
|
if (envApiToken) {
|
|
81
201
|
console.log(`Authenticated via environment variable.
|
|
82
202
|
`);
|
|
83
203
|
console.log(` Source: GITHITS_API_TOKEN`);
|
|
84
|
-
displayCodeNavigationStatus(getCodeNavigationCapability(envApiToken), codeNavigationCliOverrideEnabled);
|
|
85
204
|
return;
|
|
86
205
|
}
|
|
87
206
|
const auth = await authStorage.loadTokens(mcpUrl);
|
|
@@ -90,7 +209,6 @@ async function authStatusAction(deps) {
|
|
|
90
209
|
`);
|
|
91
210
|
console.log(` Environment: ${mcpUrl}
|
|
92
211
|
`);
|
|
93
|
-
displayCodeNavigationStatus("unknown", codeNavigationCliOverrideEnabled);
|
|
94
212
|
console.log("");
|
|
95
213
|
console.log("To authenticate:");
|
|
96
214
|
console.log(" githits login");
|
|
@@ -100,12 +218,10 @@ async function authStatusAction(deps) {
|
|
|
100
218
|
const refreshed = await refreshExpiredToken(authService, authStorage, mcpUrl);
|
|
101
219
|
if (refreshed) {
|
|
102
220
|
const refreshedAuth = await authStorage.loadTokens(mcpUrl);
|
|
103
|
-
const capability = getCodeNavigationCapability(refreshedAuth?.accessToken);
|
|
104
221
|
console.log(`Authenticated (token refreshed).
|
|
105
222
|
`);
|
|
106
223
|
console.log(` Environment: ${mcpUrl}`);
|
|
107
224
|
displayExpiry(refreshedAuth?.expiresAt ?? null);
|
|
108
|
-
displayCodeNavigationStatus(capability, codeNavigationCliOverrideEnabled);
|
|
109
225
|
console.log(`
|
|
110
226
|
Storage: ${authStorage.getStorageLocation()}`);
|
|
111
227
|
return;
|
|
@@ -115,7 +231,6 @@ async function authStatusAction(deps) {
|
|
|
115
231
|
console.log(` Environment: ${mcpUrl}`);
|
|
116
232
|
console.log(` Expired: ${new Date(auth.expiresAt).toLocaleDateString()}
|
|
117
233
|
`);
|
|
118
|
-
displayCodeNavigationStatus("unknown", codeNavigationCliOverrideEnabled);
|
|
119
234
|
console.log("");
|
|
120
235
|
console.log("Run `githits login` to re-authenticate.");
|
|
121
236
|
return;
|
|
@@ -124,35 +239,26 @@ async function authStatusAction(deps) {
|
|
|
124
239
|
`);
|
|
125
240
|
console.log(` Environment: ${mcpUrl}`);
|
|
126
241
|
displayExpiry(auth.expiresAt);
|
|
127
|
-
displayCodeNavigationStatus(getCodeNavigationCapability(auth.accessToken), codeNavigationCliOverrideEnabled);
|
|
128
242
|
console.log(`
|
|
129
243
|
Storage: ${authStorage.getStorageLocation()}`);
|
|
130
244
|
}
|
|
131
245
|
var STATUS_DESCRIPTION = `Show current authentication status.
|
|
132
246
|
|
|
133
247
|
Displays details about the stored token including environment
|
|
134
|
-
and expiration.
|
|
248
|
+
and expiration. If GITHITS_API_TOKEN is set, reports that source
|
|
249
|
+
without reading local OAuth storage. Useful for debugging authentication issues.`;
|
|
135
250
|
function registerAuthStatusCommand(program) {
|
|
136
251
|
program.command("status").summary("Show authentication status").description(STATUS_DESCRIPTION).action(async () => {
|
|
137
|
-
const deps = await
|
|
252
|
+
const deps = await createAuthStatusDependencies();
|
|
138
253
|
await authStatusAction(deps);
|
|
139
254
|
});
|
|
140
255
|
}
|
|
141
256
|
// src/commands/gated-command-group.ts
|
|
142
257
|
async function resolveGatedCommandGroupRegistrationState(options = {}) {
|
|
143
258
|
const codeNavigationUrl = options.codeNavigationUrl ?? getCodeNavigationUrl();
|
|
144
|
-
if (!codeNavigationUrl) {
|
|
145
|
-
return { codeNavigationUrl: undefined, shouldRegister: false };
|
|
146
|
-
}
|
|
147
|
-
const overrideEnabled = options.overrideEnabled ?? isCodeNavigationCliOverrideEnabled();
|
|
148
|
-
const registrationState = options.capability !== undefined || options.expiredStoredAuth !== undefined ? {
|
|
149
|
-
capability: options.capability ?? "unknown",
|
|
150
|
-
expiredStoredAuth: options.expiredStoredAuth ?? false
|
|
151
|
-
} : await resolveStartupCodeNavigationRegistrationState();
|
|
152
|
-
const envTokenPresent = options.envTokenPresent ?? Boolean(getEnvApiToken());
|
|
153
259
|
return {
|
|
154
260
|
codeNavigationUrl,
|
|
155
|
-
shouldRegister:
|
|
261
|
+
shouldRegister: codeNavigationUrl.length > 0
|
|
156
262
|
};
|
|
157
263
|
}
|
|
158
264
|
|
|
@@ -346,6 +452,9 @@ function mapCodeNavigationError(error2) {
|
|
|
346
452
|
return mapped;
|
|
347
453
|
}
|
|
348
454
|
function classify(error2) {
|
|
455
|
+
if (error2 instanceof ClientUpdateRequiredError) {
|
|
456
|
+
return buildUpdateRequiredError(error2.reason, error2.currentVersion);
|
|
457
|
+
}
|
|
349
458
|
if (error2 instanceof CodeNavigationVersionNotFoundError) {
|
|
350
459
|
const details = {};
|
|
351
460
|
if (error2.packageName)
|
|
@@ -456,6 +565,27 @@ function classify(error2) {
|
|
|
456
565
|
}
|
|
457
566
|
return { code: "UNKNOWN", message: "Unknown error", retryable: false };
|
|
458
567
|
}
|
|
568
|
+
function buildUpdateRequiredError(reason = CLIENT_UPDATE_REQUIRED_REASON, currentVersion) {
|
|
569
|
+
return {
|
|
570
|
+
code: "UPDATE_REQUIRED",
|
|
571
|
+
message: `Update required: ${reason}`,
|
|
572
|
+
retryable: false,
|
|
573
|
+
details: {
|
|
574
|
+
reason,
|
|
575
|
+
updateCommand: "npm i -g githits@latest",
|
|
576
|
+
...currentVersion ? { currentVersion } : {}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function formatMappedErrorForTerminal(mapped) {
|
|
581
|
+
if (mapped.code !== "UPDATE_REQUIRED") {
|
|
582
|
+
return mapped.message;
|
|
583
|
+
}
|
|
584
|
+
const detail = mapped.details ?? {};
|
|
585
|
+
const updateCommand = typeof detail.updateCommand === "string" ? detail.updateCommand : "npm i -g githits@latest";
|
|
586
|
+
return [mapped.message, "", "Update with:", ` ${updateCommand}`].join(`
|
|
587
|
+
`);
|
|
588
|
+
}
|
|
459
589
|
function classifyBackendError(error2) {
|
|
460
590
|
const details = {};
|
|
461
591
|
if (typeof error2.status === "number")
|
|
@@ -2305,6 +2435,9 @@ function mapPackageIntelligenceError(error2) {
|
|
|
2305
2435
|
return mapped;
|
|
2306
2436
|
}
|
|
2307
2437
|
function classify2(error2) {
|
|
2438
|
+
if (error2 instanceof ClientUpdateRequiredError) {
|
|
2439
|
+
return buildUpdateRequiredError(error2.reason, error2.currentVersion);
|
|
2440
|
+
}
|
|
2308
2441
|
if (error2 instanceof PackageIntelligenceTargetNotFoundError || error2 instanceof PackageIntelligenceChangelogSourceNotFoundError) {
|
|
2309
2442
|
return {
|
|
2310
2443
|
code: "NOT_FOUND",
|
|
@@ -2864,19 +2997,20 @@ function isSeverityLabel(value) {
|
|
|
2864
2997
|
function buildPackageVulnerabilitiesSuccessPayload(report, options = {}) {
|
|
2865
2998
|
const pkg = report.package;
|
|
2866
2999
|
const security = report.security;
|
|
2867
|
-
const
|
|
3000
|
+
const dedupedAdvisories = dedupAdvisoriesByAlias(security?.vulnerabilities ?? []);
|
|
3001
|
+
const total = security?.affectedVulnerabilityCount ?? dedupedAdvisories.length;
|
|
2868
3002
|
const payload = {
|
|
2869
3003
|
registry: lowerRegistry3(pkg.registry),
|
|
2870
3004
|
name: pkg.name,
|
|
2871
3005
|
version: pkg.version,
|
|
2872
|
-
summary: buildSummary(total, security)
|
|
3006
|
+
summary: buildSummary(total, security, dedupedAdvisories)
|
|
2873
3007
|
};
|
|
2874
3008
|
const requestedEcho = deriveRequestedVersion2(options.requestedVersion, pkg.version);
|
|
2875
3009
|
if (requestedEcho !== undefined) {
|
|
2876
3010
|
payload.requestedVersion = requestedEcho;
|
|
2877
3011
|
}
|
|
2878
3012
|
if (total > 0) {
|
|
2879
|
-
const sortedAdvisories = sortAdvisories(
|
|
3013
|
+
const sortedAdvisories = sortAdvisories(dedupedAdvisories.map(buildAdvisory));
|
|
2880
3014
|
if (sortedAdvisories.length > 0) {
|
|
2881
3015
|
payload.advisories = sortedAdvisories;
|
|
2882
3016
|
}
|
|
@@ -2915,14 +3049,19 @@ function parseVersionForSort(v) {
|
|
|
2915
3049
|
});
|
|
2916
3050
|
return { main, pre };
|
|
2917
3051
|
}
|
|
2918
|
-
function buildSummary(total, security) {
|
|
3052
|
+
function buildSummary(total, security, dedupedAdvisories) {
|
|
2919
3053
|
const summary = { total };
|
|
2920
|
-
if (
|
|
2921
|
-
|
|
3054
|
+
if (security) {
|
|
3055
|
+
summary.affectedVulnerabilityCount = security.affectedVulnerabilityCount;
|
|
3056
|
+
summary.nonAffectingVulnerabilityCount = security.nonAffectingVulnerabilityCount;
|
|
3057
|
+
summary.allVulnerabilityCount = security.allVulnerabilityCount;
|
|
3058
|
+
}
|
|
2922
3059
|
if (typeof security?.currentVersionAffected === "boolean") {
|
|
2923
3060
|
summary.affected = security.currentVersionAffected;
|
|
2924
3061
|
}
|
|
2925
|
-
|
|
3062
|
+
if (total === 0)
|
|
3063
|
+
return summary;
|
|
3064
|
+
const bySeverity = computeBySeverity(dedupedAdvisories);
|
|
2926
3065
|
const anyCounted = Object.values(bySeverity).some((n) => n > 0);
|
|
2927
3066
|
if (anyCounted) {
|
|
2928
3067
|
const trimmed = {};
|
|
@@ -2977,6 +3116,231 @@ function vulnSeverityLabel(score) {
|
|
|
2977
3116
|
return "medium";
|
|
2978
3117
|
return "low";
|
|
2979
3118
|
}
|
|
3119
|
+
function dedupAdvisoriesByAlias(advisories) {
|
|
3120
|
+
if (advisories.length === 0)
|
|
3121
|
+
return [];
|
|
3122
|
+
const parent = new Map;
|
|
3123
|
+
const find = (id) => {
|
|
3124
|
+
let root = id;
|
|
3125
|
+
let next = parent.get(root);
|
|
3126
|
+
while (next !== undefined && next !== root) {
|
|
3127
|
+
root = next;
|
|
3128
|
+
next = parent.get(root);
|
|
3129
|
+
}
|
|
3130
|
+
let cursor = id;
|
|
3131
|
+
while (cursor !== root) {
|
|
3132
|
+
const parentOfCursor = parent.get(cursor);
|
|
3133
|
+
if (parentOfCursor === undefined)
|
|
3134
|
+
break;
|
|
3135
|
+
parent.set(cursor, root);
|
|
3136
|
+
cursor = parentOfCursor;
|
|
3137
|
+
}
|
|
3138
|
+
return root;
|
|
3139
|
+
};
|
|
3140
|
+
const union = (a, b) => {
|
|
3141
|
+
const ra = find(a);
|
|
3142
|
+
const rb = find(b);
|
|
3143
|
+
if (ra !== rb)
|
|
3144
|
+
parent.set(ra, rb);
|
|
3145
|
+
};
|
|
3146
|
+
const ensure = (id) => {
|
|
3147
|
+
if (!parent.has(id))
|
|
3148
|
+
parent.set(id, id);
|
|
3149
|
+
};
|
|
3150
|
+
const advisoryKeys = [];
|
|
3151
|
+
for (let i = 0;i < advisories.length; i++) {
|
|
3152
|
+
const advisory = advisories[i];
|
|
3153
|
+
if (!advisory) {
|
|
3154
|
+
advisoryKeys.push(`__synthetic_${i}`);
|
|
3155
|
+
continue;
|
|
3156
|
+
}
|
|
3157
|
+
const ids = [];
|
|
3158
|
+
if (advisory.osvId)
|
|
3159
|
+
ids.push(advisory.osvId);
|
|
3160
|
+
for (const alias of advisory.aliases ?? []) {
|
|
3161
|
+
if (alias)
|
|
3162
|
+
ids.push(alias);
|
|
3163
|
+
}
|
|
3164
|
+
if (ids.length === 0) {
|
|
3165
|
+
const synthetic = `__synthetic_${i}`;
|
|
3166
|
+
ensure(synthetic);
|
|
3167
|
+
advisoryKeys.push(synthetic);
|
|
3168
|
+
continue;
|
|
3169
|
+
}
|
|
3170
|
+
for (const id of ids)
|
|
3171
|
+
ensure(id);
|
|
3172
|
+
for (let j = 1;j < ids.length; j++) {
|
|
3173
|
+
const a = ids[j - 1];
|
|
3174
|
+
const b = ids[j];
|
|
3175
|
+
if (a !== undefined && b !== undefined)
|
|
3176
|
+
union(a, b);
|
|
3177
|
+
}
|
|
3178
|
+
advisoryKeys.push(ids[0]);
|
|
3179
|
+
}
|
|
3180
|
+
const clusters = new Map;
|
|
3181
|
+
for (let i = 0;i < advisories.length; i++) {
|
|
3182
|
+
const advisory = advisories[i];
|
|
3183
|
+
if (!advisory)
|
|
3184
|
+
continue;
|
|
3185
|
+
const key = advisoryKeys[i];
|
|
3186
|
+
if (key === undefined)
|
|
3187
|
+
continue;
|
|
3188
|
+
const root = find(key);
|
|
3189
|
+
const bucket = clusters.get(root);
|
|
3190
|
+
if (bucket)
|
|
3191
|
+
bucket.push(advisory);
|
|
3192
|
+
else
|
|
3193
|
+
clusters.set(root, [advisory]);
|
|
3194
|
+
}
|
|
3195
|
+
const merged = [];
|
|
3196
|
+
for (const cluster of clusters.values()) {
|
|
3197
|
+
merged.push(mergeAdvisoryCluster(cluster));
|
|
3198
|
+
}
|
|
3199
|
+
return merged;
|
|
3200
|
+
}
|
|
3201
|
+
function mergeAdvisoryCluster(cluster) {
|
|
3202
|
+
if (cluster.length === 1) {
|
|
3203
|
+
const only = cluster[0];
|
|
3204
|
+
if (!only)
|
|
3205
|
+
throw new Error("empty advisory cluster");
|
|
3206
|
+
return only;
|
|
3207
|
+
}
|
|
3208
|
+
const canonical = pickCanonical(cluster);
|
|
3209
|
+
const aliasSet = new Set;
|
|
3210
|
+
for (const member of cluster) {
|
|
3211
|
+
if (member.osvId)
|
|
3212
|
+
aliasSet.add(member.osvId);
|
|
3213
|
+
for (const alias of member.aliases ?? []) {
|
|
3214
|
+
if (alias)
|
|
3215
|
+
aliasSet.add(alias);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
if (canonical.osvId)
|
|
3219
|
+
aliasSet.delete(canonical.osvId);
|
|
3220
|
+
const aliases = Array.from(aliasSet).sort();
|
|
3221
|
+
const affectedRangesSet = new Set;
|
|
3222
|
+
const matchedAffectedRangesSet = new Set;
|
|
3223
|
+
const fixedInSet = new Set;
|
|
3224
|
+
const duplicateIdsSet = new Set;
|
|
3225
|
+
let isMalicious = false;
|
|
3226
|
+
let affectsInspectedVersion = false;
|
|
3227
|
+
let affectedVersionRangesTruncated = false;
|
|
3228
|
+
let affectedVersionRangesCount = 0;
|
|
3229
|
+
let latestModifiedAt = canonical.modifiedAt;
|
|
3230
|
+
let withdrawnAt;
|
|
3231
|
+
let allWithdrawn = true;
|
|
3232
|
+
let maxSeverityScore = typeof canonical.severityScore === "number" ? canonical.severityScore : 0;
|
|
3233
|
+
for (const member of cluster) {
|
|
3234
|
+
for (const range of member.affectedVersionRanges ?? []) {
|
|
3235
|
+
affectedRangesSet.add(range);
|
|
3236
|
+
}
|
|
3237
|
+
for (const range of member.matchedAffectedVersionRanges ?? []) {
|
|
3238
|
+
matchedAffectedRangesSet.add(range);
|
|
3239
|
+
}
|
|
3240
|
+
for (const fix of member.fixedInVersions ?? []) {
|
|
3241
|
+
fixedInSet.add(fix);
|
|
3242
|
+
}
|
|
3243
|
+
for (const duplicateId of member.duplicateIds ?? []) {
|
|
3244
|
+
duplicateIdsSet.add(duplicateId);
|
|
3245
|
+
}
|
|
3246
|
+
if (member.isMalicious === true)
|
|
3247
|
+
isMalicious = true;
|
|
3248
|
+
if (member.affectsInspectedVersion === true)
|
|
3249
|
+
affectsInspectedVersion = true;
|
|
3250
|
+
if (member.affectedVersionRangesTruncated === true) {
|
|
3251
|
+
affectedVersionRangesTruncated = true;
|
|
3252
|
+
}
|
|
3253
|
+
if (typeof member.affectedVersionRangesCount === "number") {
|
|
3254
|
+
affectedVersionRangesCount = Math.max(affectedVersionRangesCount, member.affectedVersionRangesCount);
|
|
3255
|
+
}
|
|
3256
|
+
if (member.modifiedAt) {
|
|
3257
|
+
if (!latestModifiedAt || member.modifiedAt > latestModifiedAt) {
|
|
3258
|
+
latestModifiedAt = member.modifiedAt;
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
if (member.withdrawnAt) {
|
|
3262
|
+
if (!withdrawnAt || member.withdrawnAt > withdrawnAt) {
|
|
3263
|
+
withdrawnAt = member.withdrawnAt;
|
|
3264
|
+
}
|
|
3265
|
+
} else {
|
|
3266
|
+
allWithdrawn = false;
|
|
3267
|
+
}
|
|
3268
|
+
if (!member.withdrawnAt && typeof member.severityScore === "number" && member.severityScore > maxSeverityScore) {
|
|
3269
|
+
maxSeverityScore = member.severityScore;
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
const merged = {
|
|
3273
|
+
...canonical,
|
|
3274
|
+
aliases
|
|
3275
|
+
};
|
|
3276
|
+
if (maxSeverityScore > 0) {
|
|
3277
|
+
merged.severityScore = maxSeverityScore;
|
|
3278
|
+
}
|
|
3279
|
+
if (affectedRangesSet.size > 0) {
|
|
3280
|
+
merged.affectedVersionRanges = Array.from(affectedRangesSet);
|
|
3281
|
+
}
|
|
3282
|
+
const exactOrLowerBoundCount = Math.max(affectedVersionRangesCount, affectedRangesSet.size);
|
|
3283
|
+
if (exactOrLowerBoundCount > 0) {
|
|
3284
|
+
merged.affectedVersionRangesCount = exactOrLowerBoundCount;
|
|
3285
|
+
}
|
|
3286
|
+
if (affectedVersionRangesTruncated) {
|
|
3287
|
+
merged.affectedVersionRangesTruncated = true;
|
|
3288
|
+
}
|
|
3289
|
+
if (matchedAffectedRangesSet.size > 0) {
|
|
3290
|
+
merged.matchedAffectedVersionRanges = Array.from(matchedAffectedRangesSet);
|
|
3291
|
+
}
|
|
3292
|
+
if (affectsInspectedVersion)
|
|
3293
|
+
merged.affectsInspectedVersion = true;
|
|
3294
|
+
if (duplicateIdsSet.size > 0) {
|
|
3295
|
+
merged.duplicateIds = Array.from(duplicateIdsSet).sort();
|
|
3296
|
+
}
|
|
3297
|
+
if (fixedInSet.size > 0) {
|
|
3298
|
+
merged.fixedInVersions = Array.from(fixedInSet);
|
|
3299
|
+
}
|
|
3300
|
+
if (isMalicious)
|
|
3301
|
+
merged.isMalicious = true;
|
|
3302
|
+
if (latestModifiedAt) {
|
|
3303
|
+
merged.modifiedAt = latestModifiedAt;
|
|
3304
|
+
} else {
|
|
3305
|
+
delete merged.modifiedAt;
|
|
3306
|
+
}
|
|
3307
|
+
if (allWithdrawn && withdrawnAt) {
|
|
3308
|
+
merged.withdrawnAt = withdrawnAt;
|
|
3309
|
+
} else {
|
|
3310
|
+
delete merged.withdrawnAt;
|
|
3311
|
+
}
|
|
3312
|
+
return merged;
|
|
3313
|
+
}
|
|
3314
|
+
function pickCanonical(cluster) {
|
|
3315
|
+
const ranked = cluster.slice().sort((a, b) => {
|
|
3316
|
+
const aHasScore = typeof a.severityScore === "number" && a.severityScore > 0 ? 1 : 0;
|
|
3317
|
+
const bHasScore = typeof b.severityScore === "number" && b.severityScore > 0 ? 1 : 0;
|
|
3318
|
+
if (aHasScore !== bHasScore)
|
|
3319
|
+
return bHasScore - aHasScore;
|
|
3320
|
+
const aRank = idPrefixRank(a.osvId);
|
|
3321
|
+
const bRank = idPrefixRank(b.osvId);
|
|
3322
|
+
if (aRank !== bRank)
|
|
3323
|
+
return aRank - bRank;
|
|
3324
|
+
const aId = a.osvId ?? "";
|
|
3325
|
+
const bId = b.osvId ?? "";
|
|
3326
|
+
if (aId !== bId)
|
|
3327
|
+
return aId < bId ? -1 : 1;
|
|
3328
|
+
return 0;
|
|
3329
|
+
});
|
|
3330
|
+
const winner = ranked[0];
|
|
3331
|
+
if (!winner)
|
|
3332
|
+
throw new Error("empty cluster passed to pickCanonical");
|
|
3333
|
+
return winner;
|
|
3334
|
+
}
|
|
3335
|
+
function idPrefixRank(id) {
|
|
3336
|
+
if (!id)
|
|
3337
|
+
return 99;
|
|
3338
|
+
if (id.startsWith("GHSA-"))
|
|
3339
|
+
return 0;
|
|
3340
|
+
if (id.startsWith("RUSTSEC-"))
|
|
3341
|
+
return 2;
|
|
3342
|
+
return 1;
|
|
3343
|
+
}
|
|
2980
3344
|
function buildAdvisory(advisory) {
|
|
2981
3345
|
const lean = {};
|
|
2982
3346
|
if (advisory.osvId)
|
|
@@ -2996,6 +3360,21 @@ function buildAdvisory(advisory) {
|
|
|
2996
3360
|
if (advisory.affectedVersionRanges && advisory.affectedVersionRanges.length > 0) {
|
|
2997
3361
|
lean.affectedRanges = advisory.affectedVersionRanges.slice();
|
|
2998
3362
|
}
|
|
3363
|
+
if (typeof advisory.affectedVersionRangesCount === "number") {
|
|
3364
|
+
lean.affectedVersionRangesCount = advisory.affectedVersionRangesCount;
|
|
3365
|
+
}
|
|
3366
|
+
if (advisory.affectedVersionRangesTruncated === true) {
|
|
3367
|
+
lean.affectedVersionRangesTruncated = true;
|
|
3368
|
+
}
|
|
3369
|
+
if (typeof advisory.affectsInspectedVersion === "boolean") {
|
|
3370
|
+
lean.affectsInspectedVersion = advisory.affectsInspectedVersion;
|
|
3371
|
+
}
|
|
3372
|
+
if (advisory.matchedAffectedVersionRanges && advisory.matchedAffectedVersionRanges.length > 0) {
|
|
3373
|
+
lean.matchedAffectedVersionRanges = advisory.matchedAffectedVersionRanges.slice();
|
|
3374
|
+
}
|
|
3375
|
+
if (advisory.duplicateIds && advisory.duplicateIds.length > 0) {
|
|
3376
|
+
lean.duplicateIds = advisory.duplicateIds.slice();
|
|
3377
|
+
}
|
|
2999
3378
|
if (advisory.fixedInVersions && advisory.fixedInVersions.length > 0) {
|
|
3000
3379
|
lean.fixedIn = advisory.fixedInVersions.slice();
|
|
3001
3380
|
}
|
|
@@ -3078,7 +3457,7 @@ function formatPackageVulnerabilitiesTerminal(report, options = {}) {
|
|
|
3078
3457
|
const lines = [headerLine];
|
|
3079
3458
|
if (requestedLine)
|
|
3080
3459
|
lines.push(requestedLine);
|
|
3081
|
-
lines.push(
|
|
3460
|
+
lines.push(formatNoAffectedVulnerabilitiesLine(payload));
|
|
3082
3461
|
return `${lines.join(`
|
|
3083
3462
|
`)}
|
|
3084
3463
|
`;
|
|
@@ -3112,15 +3491,25 @@ function formatHeader(payload, useColors) {
|
|
|
3112
3491
|
function formatSummaryLine(payload, useColors) {
|
|
3113
3492
|
const n = payload.summary.total;
|
|
3114
3493
|
const noun = n === 1 ? "vulnerability" : "vulnerabilities";
|
|
3115
|
-
const
|
|
3494
|
+
const verb = n === 1 ? "affects" : "affect";
|
|
3495
|
+
const base = `${n} ${noun} ${verb} this version`;
|
|
3116
3496
|
if (payload.summary.affected === true) {
|
|
3117
|
-
return colorize(
|
|
3497
|
+
return colorize(base, "yellow", useColors);
|
|
3118
3498
|
}
|
|
3119
3499
|
if (payload.summary.affected === false) {
|
|
3120
|
-
return
|
|
3500
|
+
return base;
|
|
3121
3501
|
}
|
|
3122
3502
|
return base;
|
|
3123
3503
|
}
|
|
3504
|
+
function formatNoAffectedVulnerabilitiesLine(payload) {
|
|
3505
|
+
const historical = payload.summary.nonAffectingVulnerabilityCount ?? 0;
|
|
3506
|
+
if (historical > 0) {
|
|
3507
|
+
const noun = historical === 1 ? "historical advisory" : "historical advisories";
|
|
3508
|
+
const verb = historical === 1 ? "does" : "do";
|
|
3509
|
+
return `No vulnerabilities affect this version (${historical} ${noun} ${verb} not apply).`;
|
|
3510
|
+
}
|
|
3511
|
+
return "No known vulnerabilities affect this version.";
|
|
3512
|
+
}
|
|
3124
3513
|
function formatBreakdownLine(summary, useColors) {
|
|
3125
3514
|
if (summary.total <= 1)
|
|
3126
3515
|
return;
|
|
@@ -3206,7 +3595,7 @@ function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimi
|
|
|
3206
3595
|
lines.push(` ${label.padEnd(detailWidth)} ${value}`);
|
|
3207
3596
|
};
|
|
3208
3597
|
if (advisory.affectedRanges && advisory.affectedRanges.length > 0) {
|
|
3209
|
-
pushRow("affected", formatRangeList(advisory.affectedRanges, verbose, useColors, rangeLimit));
|
|
3598
|
+
pushRow("affected", formatRangeList(advisory.affectedRanges, verbose, useColors, rangeLimit, advisory.affectedVersionRangesCount, advisory.affectedVersionRangesTruncated));
|
|
3210
3599
|
}
|
|
3211
3600
|
if (advisory.fixedIn && advisory.fixedIn.length > 0) {
|
|
3212
3601
|
pushRow("fixed in", advisory.fixedIn.join(", "));
|
|
@@ -3233,13 +3622,23 @@ function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimi
|
|
|
3233
3622
|
}
|
|
3234
3623
|
return lines;
|
|
3235
3624
|
}
|
|
3236
|
-
function formatRangeList(ranges, verbose, useColors, limit) {
|
|
3625
|
+
function formatRangeList(ranges, verbose, useColors, limit, totalCount, backendTruncated) {
|
|
3626
|
+
const actualTotal = Math.max(totalCount ?? ranges.length, ranges.length);
|
|
3627
|
+
const backendHidden = backendTruncated === true ? actualTotal - ranges.length : 0;
|
|
3628
|
+
const appendBackendHint = (shown2) => {
|
|
3629
|
+
if (backendHidden > 0) {
|
|
3630
|
+
const hint2 = dim(`… (+${backendHidden} ranges omitted by service)`, useColors);
|
|
3631
|
+
return shown2.length > 0 ? `${shown2}, ${hint2}` : hint2;
|
|
3632
|
+
}
|
|
3633
|
+
return shown2;
|
|
3634
|
+
};
|
|
3237
3635
|
if (verbose || ranges.length <= limit) {
|
|
3238
|
-
return ranges.join(", ");
|
|
3636
|
+
return appendBackendHint(ranges.join(", "));
|
|
3239
3637
|
}
|
|
3240
3638
|
const shown = ranges.slice(0, limit).join(", ");
|
|
3241
|
-
const
|
|
3242
|
-
const
|
|
3639
|
+
const localHidden = ranges.length - limit;
|
|
3640
|
+
const hintText = backendHidden > 0 ? `… (+${localHidden} more with -v; +${backendHidden} omitted by service)` : `… (+${localHidden} more; use -v)`;
|
|
3641
|
+
const hint = dim(hintText, useColors);
|
|
3243
3642
|
return `${shown}, ${hint}`;
|
|
3244
3643
|
}
|
|
3245
3644
|
function resolveAffectedRangesLimit(terminalWidth) {
|
|
@@ -3254,8 +3653,8 @@ function formatUpgradeFooter(paths) {
|
|
|
3254
3653
|
if (!paths || paths.length === 0)
|
|
3255
3654
|
return;
|
|
3256
3655
|
if (paths.length === 1)
|
|
3257
|
-
return `
|
|
3258
|
-
return `
|
|
3656
|
+
return `Fix version: ${paths[0]}.`;
|
|
3657
|
+
return `Fix versions: ${paths.join(", ")}.`;
|
|
3259
3658
|
}
|
|
3260
3659
|
// src/shared/parse-lines-option.ts
|
|
3261
3660
|
function parseLinesOption(raw) {
|
|
@@ -3435,10 +3834,11 @@ Need help? support@githits.com`);
|
|
|
3435
3834
|
throw new AuthRequiredError(`Authentication required${suffix}`);
|
|
3436
3835
|
}
|
|
3437
3836
|
// src/shared/unified-search-request.ts
|
|
3837
|
+
var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
|
|
3438
3838
|
function buildUnifiedSearchParams(input) {
|
|
3439
3839
|
const targets = resolveTargets(input.target, input.targets);
|
|
3440
3840
|
const rawQuery = normaliseRequiredQuery(input.query);
|
|
3441
|
-
const limit = input.limit ??
|
|
3841
|
+
const limit = input.limit ?? DEFAULT_UNIFIED_SEARCH_LIMIT;
|
|
3442
3842
|
const offset = input.offset ?? 0;
|
|
3443
3843
|
const waitTimeoutMs = input.waitTimeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
3444
3844
|
const qualifierClauses = buildQualifierClauses({
|
|
@@ -3544,7 +3944,7 @@ function buildFilters(input) {
|
|
|
3544
3944
|
return Object.keys(filters).length > 0 ? filters : undefined;
|
|
3545
3945
|
}
|
|
3546
3946
|
// src/shared/unified-search-response.ts
|
|
3547
|
-
var DEFAULT_LIMIT2 =
|
|
3947
|
+
var DEFAULT_LIMIT2 = DEFAULT_UNIFIED_SEARCH_LIMIT;
|
|
3548
3948
|
var DEFAULT_OFFSET = 0;
|
|
3549
3949
|
function buildUnifiedSearchSuccessPayload(params, rawQuery, compiledQuery, outcome) {
|
|
3550
3950
|
const warnings = outcome.state === "completed" ? outcome.result.queryWarnings : outcome.result?.queryWarnings ?? outcome.progress?.queryWarnings ?? [];
|
|
@@ -3567,6 +3967,9 @@ function buildUnifiedSearchSuccessPayload(params, rawQuery, compiledQuery, outco
|
|
|
3567
3967
|
const sourceStatus2 = compactSourceStatus(result?.sourceStatus);
|
|
3568
3968
|
if (sourceStatus2)
|
|
3569
3969
|
payload.sourceStatus = sourceStatus2;
|
|
3970
|
+
const combinedWarnings2 = combineWarnings(warnings, sourceStatus2);
|
|
3971
|
+
if (combinedWarnings2.length > 0)
|
|
3972
|
+
payload.warnings = combinedWarnings2;
|
|
3570
3973
|
return payload;
|
|
3571
3974
|
}
|
|
3572
3975
|
const completed = {
|
|
@@ -3583,8 +3986,18 @@ function buildUnifiedSearchSuccessPayload(params, rawQuery, compiledQuery, outco
|
|
|
3583
3986
|
const sourceStatus = compactSourceStatus(outcome.result.sourceStatus);
|
|
3584
3987
|
if (sourceStatus)
|
|
3585
3988
|
completed.sourceStatus = sourceStatus;
|
|
3989
|
+
const combinedWarnings = combineWarnings(warnings, sourceStatus);
|
|
3990
|
+
if (combinedWarnings.length > 0)
|
|
3991
|
+
completed.warnings = combinedWarnings;
|
|
3586
3992
|
return completed;
|
|
3587
3993
|
}
|
|
3994
|
+
function combineWarnings(parserWarnings, sourceStatus) {
|
|
3995
|
+
const out = [];
|
|
3996
|
+
if (parserWarnings.length > 0)
|
|
3997
|
+
out.push(...parserWarnings);
|
|
3998
|
+
out.push(...buildSourceStatusWarnings(sourceStatus));
|
|
3999
|
+
return out;
|
|
4000
|
+
}
|
|
3588
4001
|
function buildUnifiedSearchErrorPayload(error2) {
|
|
3589
4002
|
const mapped = mapCodeNavigationError(error2);
|
|
3590
4003
|
const payload = {
|
|
@@ -3629,15 +4042,16 @@ function buildUnifiedSearchStatusResultPayload(result) {
|
|
|
3629
4042
|
if (result.page.hasMore) {
|
|
3630
4043
|
payload.nextOffset = result.page.offset + result.page.returned;
|
|
3631
4044
|
}
|
|
3632
|
-
if (result.queryWarnings.length > 0) {
|
|
3633
|
-
payload.warnings = result.queryWarnings;
|
|
3634
|
-
}
|
|
3635
4045
|
if (result.sources.length > 0) {
|
|
3636
4046
|
payload.sources = result.sources.map((entry) => entry.toLowerCase());
|
|
3637
4047
|
}
|
|
3638
4048
|
const sourceStatus = compactSourceStatus(result.sourceStatus);
|
|
3639
4049
|
if (sourceStatus)
|
|
3640
4050
|
payload.sourceStatus = sourceStatus;
|
|
4051
|
+
const combinedWarnings = combineWarnings(result.queryWarnings, sourceStatus);
|
|
4052
|
+
if (combinedWarnings.length > 0) {
|
|
4053
|
+
payload.warnings = combinedWarnings;
|
|
4054
|
+
}
|
|
3641
4055
|
return payload;
|
|
3642
4056
|
}
|
|
3643
4057
|
function buildQueryEcho(params, rawQuery, compiledQuery, warnings) {
|
|
@@ -3763,6 +4177,46 @@ function compactProgress(progress) {
|
|
|
3763
4177
|
payload.expiresAt = progress.expiresAt;
|
|
3764
4178
|
return payload;
|
|
3765
4179
|
}
|
|
4180
|
+
function buildSourceStatusWarnings(sourceStatus) {
|
|
4181
|
+
if (!sourceStatus || sourceStatus.length === 0)
|
|
4182
|
+
return [];
|
|
4183
|
+
const warnings = [];
|
|
4184
|
+
for (const entry of sourceStatus) {
|
|
4185
|
+
const message = warningForEntry(entry);
|
|
4186
|
+
if (message !== undefined)
|
|
4187
|
+
warnings.push(message);
|
|
4188
|
+
}
|
|
4189
|
+
return warnings;
|
|
4190
|
+
}
|
|
4191
|
+
function warningForEntry(entry) {
|
|
4192
|
+
const reasons = [];
|
|
4193
|
+
if (entry.incompatibleQueryFeatures?.length) {
|
|
4194
|
+
reasons.push(`incompatible query features [${entry.incompatibleQueryFeatures.join(", ")}]`);
|
|
4195
|
+
}
|
|
4196
|
+
if (entry.ignoredQueryFeatures?.length) {
|
|
4197
|
+
reasons.push(`ignored query features [${entry.ignoredQueryFeatures.join(", ")}]`);
|
|
4198
|
+
}
|
|
4199
|
+
if (entry.incompatibleFilters?.length) {
|
|
4200
|
+
reasons.push(`incompatible filters [${entry.incompatibleFilters.join(", ")}]`);
|
|
4201
|
+
}
|
|
4202
|
+
if (entry.ignoredFilters?.length) {
|
|
4203
|
+
reasons.push(`ignored filters [${entry.ignoredFilters.join(", ")}]`);
|
|
4204
|
+
}
|
|
4205
|
+
if (entry.indexingStatus) {
|
|
4206
|
+
reasons.push(`indexing status ${entry.indexingStatus}`);
|
|
4207
|
+
}
|
|
4208
|
+
if (entry.codeIndexState) {
|
|
4209
|
+
reasons.push(`code index state ${entry.codeIndexState}`);
|
|
4210
|
+
}
|
|
4211
|
+
const prefix = `Source '${entry.source}' for ${entry.targetLabel}`;
|
|
4212
|
+
if (reasons.length > 0) {
|
|
4213
|
+
return `${prefix}: ${reasons.join("; ")}`;
|
|
4214
|
+
}
|
|
4215
|
+
if (entry.note) {
|
|
4216
|
+
return `${prefix}: ${entry.note}`;
|
|
4217
|
+
}
|
|
4218
|
+
return;
|
|
4219
|
+
}
|
|
3766
4220
|
function compactSourceStatus(sourceStatus) {
|
|
3767
4221
|
if (!sourceStatus || sourceStatus.length === 0)
|
|
3768
4222
|
return;
|
|
@@ -3962,6 +4416,12 @@ function formatScore(score) {
|
|
|
3962
4416
|
}
|
|
3963
4417
|
function buildTrailer2(payload) {
|
|
3964
4418
|
const lines = [];
|
|
4419
|
+
if (payload.warnings && payload.warnings.length > 0) {
|
|
4420
|
+
lines.push("warnings:");
|
|
4421
|
+
for (const warning2 of payload.warnings) {
|
|
4422
|
+
lines.push(` - ${warning2}`);
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
3965
4425
|
if (payload.hasMore) {
|
|
3966
4426
|
const nextOffsetHint = typeof payload.nextOffset === "number" ? ` Pass offset=${payload.nextOffset} for the next page or limit=N to widen.` : " Pass limit=N to widen.";
|
|
3967
4427
|
lines.push(`More hits available.${nextOffsetHint}`);
|
|
@@ -4308,6 +4768,9 @@ function parseIntCliOption(raw, name, min, max) {
|
|
|
4308
4768
|
return parsed;
|
|
4309
4769
|
}
|
|
4310
4770
|
function formatIndexingError(mapped) {
|
|
4771
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
4772
|
+
return formatMappedErrorForTerminal(mapped);
|
|
4773
|
+
}
|
|
4311
4774
|
if (mapped.code !== "INDEXING")
|
|
4312
4775
|
return mapped.message;
|
|
4313
4776
|
const detail = mapped.details ?? {};
|
|
@@ -4325,6 +4788,9 @@ function formatIndexingError(mapped) {
|
|
|
4325
4788
|
`);
|
|
4326
4789
|
}
|
|
4327
4790
|
function formatFileErrorWithFilesHint(mapped) {
|
|
4791
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
4792
|
+
return formatMappedErrorForTerminal(mapped);
|
|
4793
|
+
}
|
|
4328
4794
|
if (mapped.code === "FILE_NOT_FOUND") {
|
|
4329
4795
|
return `${mapped.message}
|
|
4330
4796
|
Use \`code files\` to list available paths.`;
|
|
@@ -4590,7 +5056,7 @@ grep run. --symbol-field hydrates enclosing symbol metadata (appears under each
|
|
|
4590
5056
|
match in --verbose output; full payload in --json).`;
|
|
4591
5057
|
function registerCodeGrepCommand(pkgCommand) {
|
|
4592
5058
|
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)", collectRepeatable, []).option("--ext <ext>", "Extension filter without leading dot (repeatable)", collectRepeatable, []).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}`, collectRepeatable, []).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) => {
|
|
4593
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
5059
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-1gqfte6e.js");
|
|
4594
5060
|
const deps = await createContainer2();
|
|
4595
5061
|
await pkgGrepAction(arg1, arg2, arg3, options, {
|
|
4596
5062
|
codeNavigationService: deps.codeNavigationService,
|
|
@@ -4974,7 +5440,7 @@ function handleDocsListError(error2, json) {
|
|
|
4974
5440
|
...mapped.details ? { details: mapped.details } : {}
|
|
4975
5441
|
}));
|
|
4976
5442
|
} else {
|
|
4977
|
-
console.error(mapped
|
|
5443
|
+
console.error(formatMappedErrorForTerminal(mapped));
|
|
4978
5444
|
}
|
|
4979
5445
|
process.exit(1);
|
|
4980
5446
|
}
|
|
@@ -5030,7 +5496,7 @@ function handleDocsReadError(error2, json) {
|
|
|
5030
5496
|
...mapped.details ? { details: mapped.details } : {}
|
|
5031
5497
|
}));
|
|
5032
5498
|
} else {
|
|
5033
|
-
console.error(mapped
|
|
5499
|
+
console.error(formatMappedErrorForTerminal(mapped));
|
|
5034
5500
|
}
|
|
5035
5501
|
process.exit(1);
|
|
5036
5502
|
}
|
|
@@ -5109,7 +5575,7 @@ function registerExampleCommand(program) {
|
|
|
5109
5575
|
});
|
|
5110
5576
|
}
|
|
5111
5577
|
async function loadContainer() {
|
|
5112
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
5578
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-1gqfte6e.js");
|
|
5113
5579
|
return createContainer2();
|
|
5114
5580
|
}
|
|
5115
5581
|
// src/commands/feedback.ts
|
|
@@ -5883,6 +6349,36 @@ var TIMEOUT_MS = 5 * 60 * 1000;
|
|
|
5883
6349
|
function randomPort() {
|
|
5884
6350
|
return Math.floor(Math.random() * 2000) + 8000;
|
|
5885
6351
|
}
|
|
6352
|
+
async function preflightAuthPersistence(authStorage, mcpUrl) {
|
|
6353
|
+
const probeUrl = `${mcpUrl.replace(/\/+$/, "")}/__githits_storage_probe__`;
|
|
6354
|
+
const probeClient = {
|
|
6355
|
+
clientId: "__githits_storage_probe__",
|
|
6356
|
+
clientSecret: "__githits_storage_probe__",
|
|
6357
|
+
redirectUri: "http://127.0.0.1:1/callback",
|
|
6358
|
+
registeredAt: new Date(0).toISOString()
|
|
6359
|
+
};
|
|
6360
|
+
const probeTokens = {
|
|
6361
|
+
accessToken: "__githits_storage_probe__",
|
|
6362
|
+
refreshToken: "__githits_storage_probe__",
|
|
6363
|
+
expiresAt: new Date(0).toISOString(),
|
|
6364
|
+
createdAt: new Date(0).toISOString()
|
|
6365
|
+
};
|
|
6366
|
+
try {
|
|
6367
|
+
await authStorage.saveClient(probeUrl, probeClient);
|
|
6368
|
+
await authStorage.saveTokens(probeUrl, probeTokens);
|
|
6369
|
+
await authStorage.clearTokens(probeUrl);
|
|
6370
|
+
await authStorage.clearClient(probeUrl);
|
|
6371
|
+
return null;
|
|
6372
|
+
} catch (error2) {
|
|
6373
|
+
await authStorage.clearTokens(probeUrl).catch(() => {});
|
|
6374
|
+
await authStorage.clearClient(probeUrl).catch(() => {});
|
|
6375
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
6376
|
+
return {
|
|
6377
|
+
status: "failed",
|
|
6378
|
+
message: `Cannot persist OAuth credentials: ${message}`
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
}
|
|
5886
6382
|
async function loginFlow(options, deps) {
|
|
5887
6383
|
const { authService, authStorage, browserService, mcpUrl } = deps;
|
|
5888
6384
|
if (options.port !== undefined && (Number.isNaN(options.port) || options.port < 1 || options.port > 65535)) {
|
|
@@ -5906,6 +6402,9 @@ async function loginFlow(options, deps) {
|
|
|
5906
6402
|
if (!existing) {
|
|
5907
6403
|
await authStorage.clearClient(mcpUrl);
|
|
5908
6404
|
}
|
|
6405
|
+
const persistenceError = await preflightAuthPersistence(authStorage, mcpUrl);
|
|
6406
|
+
if (persistenceError)
|
|
6407
|
+
return persistenceError;
|
|
5909
6408
|
console.log("Discovering OAuth endpoints...");
|
|
5910
6409
|
const metadata = await authService.discoverEndpoints(mcpUrl);
|
|
5911
6410
|
let client = await authStorage.loadClient(mcpUrl);
|
|
@@ -6051,13 +6550,15 @@ You're ready to use githits with your AI assistant.`);
|
|
|
6051
6550
|
var LOGIN_DESCRIPTION = `Authenticate with your GitHits account via browser.
|
|
6052
6551
|
|
|
6053
6552
|
Opens your browser to complete authentication securely using OAuth.
|
|
6054
|
-
|
|
6553
|
+
OAuth credentials are stored in the system keychain by default. If your
|
|
6554
|
+
machine has no usable keychain, use GITHITS_API_TOKEN or explicitly configure
|
|
6555
|
+
auth.storage = "file". File storage is plaintext on disk.
|
|
6055
6556
|
|
|
6056
6557
|
Use --no-browser in environments without a display (CI, SSH sessions)
|
|
6057
6558
|
to get a URL you can open on another device.`;
|
|
6058
6559
|
function registerLoginCommand(program) {
|
|
6059
6560
|
program.command("login").summary("Authenticate with your GitHits account").description(LOGIN_DESCRIPTION).option("--no-browser", "Print URL instead of opening browser").option("--port <port>", "Port for local callback server", parseInt).option("--force", "Re-authenticate even if already logged in").action(async (options) => {
|
|
6060
|
-
const deps = await
|
|
6561
|
+
const deps = await createAuthCommandDependencies();
|
|
6061
6562
|
await loginAction(options, deps);
|
|
6062
6563
|
});
|
|
6063
6564
|
}
|
|
@@ -6252,7 +6753,7 @@ function registerInitCommand(program) {
|
|
|
6252
6753
|
fileSystemService,
|
|
6253
6754
|
promptService,
|
|
6254
6755
|
execService,
|
|
6255
|
-
createLoginDeps: () =>
|
|
6756
|
+
createLoginDeps: () => createAuthCommandDependencies()
|
|
6256
6757
|
});
|
|
6257
6758
|
});
|
|
6258
6759
|
}
|
|
@@ -6329,10 +6830,11 @@ var LOGOUT_DESCRIPTION = `Remove stored credentials.
|
|
|
6329
6830
|
|
|
6330
6831
|
Clears all locally stored authentication data including tokens and
|
|
6331
6832
|
client registrations. OAuth tokens expire naturally; this
|
|
6332
|
-
removes
|
|
6833
|
+
removes local copies from the keychain, explicit file storage, and
|
|
6834
|
+
legacy auth file storage.`;
|
|
6333
6835
|
function registerLogoutCommand(program) {
|
|
6334
6836
|
program.command("logout").summary("Remove stored credentials").description(LOGOUT_DESCRIPTION).action(async () => {
|
|
6335
|
-
const deps = await
|
|
6837
|
+
const deps = await createAuthCommandDependencies();
|
|
6336
6838
|
await logoutAction(deps);
|
|
6337
6839
|
});
|
|
6338
6840
|
}
|
|
@@ -7141,7 +7643,7 @@ var schema9 = {
|
|
|
7141
7643
|
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."),
|
|
7142
7644
|
include_withdrawn: z10.boolean().optional().describe("Include retracted advisories (default: false).")
|
|
7143
7645
|
};
|
|
7144
|
-
var DESCRIPTION9 = "Check known vulnerabilities for a package on npm, PyPI, Hex, or " + "Crates (other registries are not yet supported for vulnerability " + "data). Returns a count summary, each advisory with OSV ID, " + "severity, affected ranges and fix versions
|
|
7646
|
+
var DESCRIPTION9 = "Check known vulnerabilities for a package on npm, PyPI, Hex, or " + "Crates (other registries are not yet supported for vulnerability " + "data). Returns a count summary, each advisory with OSV ID, " + "severity, affected ranges, and fix versions. Malicious-package advisories surface in a separate " + "bucket. Pass `version` to inspect a specific release; otherwise " + "the latest is checked. Use `min_severity` to filter to a threshold " + "(`low`, `medium`, `high`, `critical`) and `include_withdrawn` to " + "also see retracted advisories.";
|
|
7145
7647
|
function createPackageVulnerabilitiesTool(service) {
|
|
7146
7648
|
return {
|
|
7147
7649
|
name: "pkg_vulns",
|
|
@@ -7368,7 +7870,7 @@ var schema12 = {
|
|
|
7368
7870
|
name: z13.string().optional(),
|
|
7369
7871
|
language: z13.string().optional(),
|
|
7370
7872
|
allow_partial_results: z13.boolean().optional().describe("Default false waits for all sources; if the wait window expires, returns only searchRef/progress. When true, includes hits from sources that finished so far and still returns searchRef for continuation. Partial payloads support normal pagination via nextOffset."),
|
|
7371
|
-
limit: z13.coerce.number().int().min(1).max(100).optional(),
|
|
7873
|
+
limit: z13.coerce.number().int().min(1).max(100).optional().describe("Maximum results to return (default 10, max 100)."),
|
|
7372
7874
|
offset: z13.coerce.number().int().min(0).optional(),
|
|
7373
7875
|
wait_timeout_ms: z13.coerce.number().int().min(0).max(60000).optional(),
|
|
7374
7876
|
format: z13.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact line-oriented output tuned for agent context efficiency. Pass `format: "json"` for the structured envelope (programmatic consumers, parity testing). `text` is an alias for `text-v1`. The text format is a public, snapshot-tested contract.')
|
|
@@ -7473,67 +7975,46 @@ function createSearchStatusTool(service) {
|
|
|
7473
7975
|
};
|
|
7474
7976
|
}
|
|
7475
7977
|
// src/commands/mcp-instructions.ts
|
|
7476
|
-
var CORE_BLOCK = `GitHits
|
|
7978
|
+
var CORE_BLOCK = `GitHits provides verified, canonical code examples from global open source. Use it when you're stuck, repeated attempts failed, you need up-to-date API usage, the question is comparative across OSS projects (e.g. "how does X vs Y handle Z"), the answer requires reading how a real codebase implements a feature, or the user mentions GitHits.
|
|
7477
7979
|
|
|
7478
|
-
|
|
7479
|
-
var PACKAGE_TOOLS_PREAMBLE = `
|
|
7980
|
+
Example workflow: call \`get_example\` with one focused question; pass \`language\` only when you know the exact language name, otherwise call \`search_language\` first. Send \`feedback\` on the returned solution_id. Reuse prior results before searching again.`;
|
|
7981
|
+
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.
|
|
7480
7982
|
|
|
7481
7983
|
Package spec: \`registry:name[@version]\`.`;
|
|
7482
7984
|
var PKG_INFO_BULLET = "- `pkg_info` — instant package overview: latest version, license, downloads, quickstart, and active advisory count.";
|
|
7483
|
-
var DOCS_LIST_BULLET = "- `docs_list` — browse
|
|
7985
|
+
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.";
|
|
7484
7986
|
var DOCS_READ_BULLET = "- `docs_read` — read a documentation page by pageId. Works for both hosted docs and repo-backed docs.";
|
|
7485
|
-
var PKG_VULNS_BULLET = "- `pkg_vulns` — known CVE / OSV advisories for npm, PyPI, Hex, or Crates packages
|
|
7486
|
-
var PKG_DEPS_BULLET = "- `pkg_deps` — direct runtime deps plus
|
|
7487
|
-
var PKG_CHANGELOG_BULLET = "- `pkg_changelog` — release notes for a package or GitHub repo, newest-first. Default latest mode returns
|
|
7488
|
-
var SEARCH_BULLET = '- `search` — unified search across indexed dependency code, docs, and
|
|
7489
|
-
var SEARCH_STATUS_BULLET = "- `search_status` — follow up a prior
|
|
7490
|
-
var CODE_FILES_BULLET = '- `code_files` —
|
|
7491
|
-
var CODE_READ_BULLET = "- `code_read` —
|
|
7492
|
-
var CODE_GREP_BULLET =
|
|
7987
|
+
var PKG_VULNS_BULLET = "- `pkg_vulns` — known CVE / OSV advisories for npm, PyPI, Hex, or Crates packages, optionally pinned to `@version`. Filter with `min_severity`; include retracted advisories with `include_withdrawn`.";
|
|
7988
|
+
var PKG_DEPS_BULLET = "- `pkg_deps` — direct runtime deps plus lifecycle/feature groups when available. Use `lifecycle` to filter groups, `include_transitive` for the full graph, and `include_importers` for provenance. Supports npm, PyPI, Hex, Crates, vcpkg, and Zig.";
|
|
7989
|
+
var PKG_CHANGELOG_BULLET = "- `pkg_changelog` — release notes for a package or GitHub repo, newest-first. Default latest mode returns recent entries with markdown bodies; `from_version` switches to range mode. Set `include_bodies: false` for a compact timeline.";
|
|
7990
|
+
var SEARCH_BULLET = '- `search` — unified search across indexed dependency code, docs, and symbols. Omit `sources` for AUTO. Default output is compact `text-v1`; pass `format: "json"` for locators, highlights, and source status. Complete by default; opt into partial hits with `allow_partial_results: true`. Incomplete responses carry a `searchRef`.';
|
|
7991
|
+
var SEARCH_STATUS_BULLET = "- `search_status` — follow up a prior `search` by `searchRef` to check progress, fetch partial hits, or fetch final results.";
|
|
7992
|
+
var CODE_FILES_BULLET = '- `code_files` — list files in an indexed dependency. Use `path_prefix` to scope to a directory. Returned paths feed into `code_read` and help scope `code_grep`; pass `format: "json"` for language/type/size metadata.';
|
|
7993
|
+
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.";
|
|
7994
|
+
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`.";
|
|
7493
7995
|
var SEARCH_VS_SYMBOLS_TIP = 'Prefer `get_example` for canonical example retrieval; prefer unified `search` for indexed dependency and repository discovery; use `sources:["symbol"]` when you want symbol-shaped results. Use `code_grep` for deterministic text matching and `code_read` for focused-window inspection of a known file.';
|
|
7494
|
-
var REFERENCE_FIRST_TIP = "Strategy — reference-first, content-second. Locate symbols and lines with `search` / `code_grep` first, then read only the
|
|
7495
|
-
var MULTI_TURN_TIP = '**Delegate multi-call work to a sub-agent.** Code-navigation
|
|
7496
|
-
function
|
|
7497
|
-
return deps.codeNavigationCliOverrideEnabled || deps.codeNavigationCapability === "enabled" || deps.codeNavigationCapability === "unknown" && deps.envApiToken !== undefined;
|
|
7498
|
-
}
|
|
7499
|
-
function buildMcpInstructions(deps) {
|
|
7996
|
+
var REFERENCE_FIRST_TIP = "Strategy — reference-first, content-second. Locate symbols and lines with `search` / `code_grep` first, then read only the needed window with `code_read` using explicit `start_line` / `end_line` values, typically 80-150 lines around the match. The MCP `code_read` surface caps each call at 150 lines.";
|
|
7997
|
+
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.';
|
|
7998
|
+
function buildMcpInstructions(_deps) {
|
|
7500
7999
|
const sections = [CORE_BLOCK];
|
|
7501
|
-
if (!isPackageToolsCapabilityOpen(deps)) {
|
|
7502
|
-
return sections.join(`
|
|
7503
|
-
|
|
7504
|
-
`);
|
|
7505
|
-
}
|
|
7506
8000
|
const bullets = [];
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
bullets.push(CODE_FILES_BULLET);
|
|
7519
|
-
bullets.push(CODE_READ_BULLET);
|
|
7520
|
-
bullets.push(CODE_GREP_BULLET);
|
|
7521
|
-
}
|
|
7522
|
-
if (bullets.length === 0) {
|
|
7523
|
-
return sections.join(`
|
|
7524
|
-
|
|
7525
|
-
`);
|
|
7526
|
-
}
|
|
8001
|
+
bullets.push(DOCS_LIST_BULLET);
|
|
8002
|
+
bullets.push(DOCS_READ_BULLET);
|
|
8003
|
+
bullets.push(PKG_INFO_BULLET);
|
|
8004
|
+
bullets.push(PKG_VULNS_BULLET);
|
|
8005
|
+
bullets.push(PKG_DEPS_BULLET);
|
|
8006
|
+
bullets.push(PKG_CHANGELOG_BULLET);
|
|
8007
|
+
bullets.push(SEARCH_BULLET);
|
|
8008
|
+
bullets.push(SEARCH_STATUS_BULLET);
|
|
8009
|
+
bullets.push(CODE_FILES_BULLET);
|
|
8010
|
+
bullets.push(CODE_READ_BULLET);
|
|
8011
|
+
bullets.push(CODE_GREP_BULLET);
|
|
7527
8012
|
const parts = [PACKAGE_TOOLS_PREAMBLE];
|
|
7528
|
-
|
|
7529
|
-
parts.push(MULTI_TURN_TIP);
|
|
7530
|
-
}
|
|
8013
|
+
parts.push(MULTI_TURN_TIP);
|
|
7531
8014
|
parts.push(bullets.join(`
|
|
7532
8015
|
`));
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
parts.push(SEARCH_VS_SYMBOLS_TIP);
|
|
7536
|
-
}
|
|
8016
|
+
parts.push(REFERENCE_FIRST_TIP);
|
|
8017
|
+
parts.push(SEARCH_VS_SYMBOLS_TIP);
|
|
7537
8018
|
sections.push(parts.join(`
|
|
7538
8019
|
|
|
7539
8020
|
`));
|
|
@@ -7549,22 +8030,17 @@ function getMcpToolDefinitions(deps) {
|
|
|
7549
8030
|
eraseTool(createSearchLanguageTool(deps.githitsService)),
|
|
7550
8031
|
eraseTool(createFeedbackTool(deps.githitsService))
|
|
7551
8032
|
];
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
tools.push(eraseTool(createPackageSummaryTool(deps.packageIntelligenceService)));
|
|
7564
|
-
tools.push(eraseTool(createPackageVulnerabilitiesTool(deps.packageIntelligenceService)));
|
|
7565
|
-
tools.push(eraseTool(createPackageDependenciesTool(deps.packageIntelligenceService)));
|
|
7566
|
-
tools.push(eraseTool(createPackageChangelogTool(deps.packageIntelligenceService)));
|
|
7567
|
-
}
|
|
8033
|
+
tools.push(eraseTool(createSearchTool(deps.codeNavigationService)));
|
|
8034
|
+
tools.push(eraseTool(createSearchStatusTool(deps.codeNavigationService)));
|
|
8035
|
+
tools.push(eraseTool(createListFilesTool(deps.codeNavigationService)));
|
|
8036
|
+
tools.push(eraseTool(createReadFileTool(deps.codeNavigationService)));
|
|
8037
|
+
tools.push(eraseTool(createGrepRepoTool(deps.codeNavigationService)));
|
|
8038
|
+
tools.push(eraseTool(createListPackageDocsTool(deps.packageIntelligenceService)));
|
|
8039
|
+
tools.push(eraseTool(createReadPackageDocTool(deps.packageIntelligenceService)));
|
|
8040
|
+
tools.push(eraseTool(createPackageSummaryTool(deps.packageIntelligenceService)));
|
|
8041
|
+
tools.push(eraseTool(createPackageVulnerabilitiesTool(deps.packageIntelligenceService)));
|
|
8042
|
+
tools.push(eraseTool(createPackageDependenciesTool(deps.packageIntelligenceService)));
|
|
8043
|
+
tools.push(eraseTool(createPackageChangelogTool(deps.packageIntelligenceService)));
|
|
7568
8044
|
return tools;
|
|
7569
8045
|
}
|
|
7570
8046
|
function eraseTool(tool) {
|
|
@@ -7632,7 +8108,7 @@ function registerMcpCommand(program) {
|
|
|
7632
8108
|
When run interactively (TTY), shows setup instructions.
|
|
7633
8109
|
When run via stdio (non-TTY), starts the MCP server.
|
|
7634
8110
|
|
|
7635
|
-
|
|
8111
|
+
Authenticated tool calls require a valid GitHits token.`).action(async () => {
|
|
7636
8112
|
if (process.stdout.isTTY && process.stdin.isTTY) {
|
|
7637
8113
|
showMcpSetupInstructions();
|
|
7638
8114
|
return;
|
|
@@ -7721,6 +8197,9 @@ function handlePkgChangelogCommandError(error2, json) {
|
|
|
7721
8197
|
process.exit(1);
|
|
7722
8198
|
}
|
|
7723
8199
|
function formatChangelogTerminalError(mapped) {
|
|
8200
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
8201
|
+
return formatMappedErrorForTerminal(mapped);
|
|
8202
|
+
}
|
|
7724
8203
|
if (mapped.code !== "VERSION_NOT_FOUND")
|
|
7725
8204
|
return mapped.message;
|
|
7726
8205
|
const detail = mapped.details ?? {};
|
|
@@ -7844,6 +8323,9 @@ function handlePkgDepsCommandError(error2, json) {
|
|
|
7844
8323
|
process.exit(1);
|
|
7845
8324
|
}
|
|
7846
8325
|
function formatDepsTerminalError(mapped) {
|
|
8326
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
8327
|
+
return formatMappedErrorForTerminal(mapped);
|
|
8328
|
+
}
|
|
7847
8329
|
if (mapped.code !== "VERSION_NOT_FOUND")
|
|
7848
8330
|
return mapped.message;
|
|
7849
8331
|
const detail = mapped.details ?? {};
|
|
@@ -7930,7 +8412,7 @@ function handlePkgInfoCommandError(error2, json) {
|
|
|
7930
8412
|
}));
|
|
7931
8413
|
process.exit(1);
|
|
7932
8414
|
}
|
|
7933
|
-
console.error(mapped
|
|
8415
|
+
console.error(formatMappedErrorForTerminal(mapped));
|
|
7934
8416
|
process.exit(1);
|
|
7935
8417
|
}
|
|
7936
8418
|
var PKG_INFO_DESCRIPTION = `Get a package overview — latest version, license, description,
|
|
@@ -8003,6 +8485,9 @@ function handlePkgVulnsCommandError(error2, json) {
|
|
|
8003
8485
|
process.exit(1);
|
|
8004
8486
|
}
|
|
8005
8487
|
function formatVulnsTerminalError(mapped) {
|
|
8488
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
8489
|
+
return formatMappedErrorForTerminal(mapped);
|
|
8490
|
+
}
|
|
8006
8491
|
if (mapped.code !== "VERSION_NOT_FOUND")
|
|
8007
8492
|
return mapped.message;
|
|
8008
8493
|
const detail = mapped.details ?? {};
|
|
@@ -8027,8 +8512,8 @@ function formatVulnsTerminalError(mapped) {
|
|
|
8027
8512
|
`);
|
|
8028
8513
|
}
|
|
8029
8514
|
var PKG_VULNS_DESCRIPTION = `Show known vulnerabilities for a package. Lists CVE / OSV advisories
|
|
8030
|
-
with severity, affected version ranges, fix versions
|
|
8031
|
-
|
|
8515
|
+
with severity, affected version ranges, and fix versions.
|
|
8516
|
+
Malicious-package advisories are flagged prominently.
|
|
8032
8517
|
|
|
8033
8518
|
Package spec: <registry>:<name>[@<version>]. Supported registries:
|
|
8034
8519
|
npm, pypi, hex, crates. Omit @<version> to check the latest release.
|
|
@@ -8155,7 +8640,7 @@ function registerSearchCommand(program) {
|
|
|
8155
8640
|
"fixture",
|
|
8156
8641
|
"build",
|
|
8157
8642
|
"vendor"
|
|
8158
|
-
])).option("--public", "Filter to public symbols when supported").option("--name <name>", "Structured name qualifier").option("--lang <language>", "Structured language qualifier").option("--allow-partial", "Include hits already available while indexing continues; a searchRef is still returned so search-status can fetch the rest").option("--limit <n>", "Max results (1-100)").option("--offset <n>", "Result offset").option("--wait <seconds>", "Max seconds to wait before returning a searchRef (0-60; default: 20)").option("--json", "Output as JSON").action(async (query, options) => {
|
|
8643
|
+
])).option("--public", "Filter to public symbols when supported").option("--name <name>", "Structured name qualifier").option("--lang <language>", "Structured language qualifier").option("--allow-partial", "Include hits already available while indexing continues; a searchRef is still returned so search-status can fetch the rest").option("--limit <n>", "Max results (1-100, default: 10)").option("--offset <n>", "Result offset").option("--wait <seconds>", "Max seconds to wait before returning a searchRef (0-60; default: 20)").option("--json", "Output as JSON").action(async (query, options) => {
|
|
8159
8644
|
const deps = await loadContainer2();
|
|
8160
8645
|
await searchAction(query, options, deps);
|
|
8161
8646
|
});
|
|
@@ -8169,14 +8654,6 @@ async function registerUnifiedSearchCommands(program, options = {}) {
|
|
|
8169
8654
|
if (!codeNavigationUrl) {
|
|
8170
8655
|
return;
|
|
8171
8656
|
}
|
|
8172
|
-
const overrideEnabled = options.overrideEnabled ?? isCodeNavigationCliOverrideEnabled();
|
|
8173
|
-
const registrationState = options.capability !== undefined || options.expiredStoredAuth !== undefined ? {
|
|
8174
|
-
capability: options.capability ?? "unknown",
|
|
8175
|
-
expiredStoredAuth: options.expiredStoredAuth ?? false
|
|
8176
|
-
} : await loadStartupCodeNavigationRegistrationState();
|
|
8177
|
-
if (!overrideEnabled && registrationState.capability !== "enabled" && !registrationState.expiredStoredAuth) {
|
|
8178
|
-
return;
|
|
8179
|
-
}
|
|
8180
8657
|
registerSearchCommand(program);
|
|
8181
8658
|
}
|
|
8182
8659
|
function requireSearchService(deps) {
|
|
@@ -8186,13 +8663,9 @@ function requireSearchService(deps) {
|
|
|
8186
8663
|
return deps.codeNavigationService;
|
|
8187
8664
|
}
|
|
8188
8665
|
async function loadContainer2() {
|
|
8189
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
8666
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-1gqfte6e.js");
|
|
8190
8667
|
return createContainer2();
|
|
8191
8668
|
}
|
|
8192
|
-
async function loadStartupCodeNavigationRegistrationState() {
|
|
8193
|
-
const { resolveStartupCodeNavigationRegistrationState: resolveStartupCodeNavigationRegistrationState2 } = await import("./shared/chunk-9zze00cp.js");
|
|
8194
|
-
return resolveStartupCodeNavigationRegistrationState2();
|
|
8195
|
-
}
|
|
8196
8669
|
function parseTargetSpecs(specs) {
|
|
8197
8670
|
if (!specs || specs.length === 0) {
|
|
8198
8671
|
throw new InvalidArgumentError("Provide at least one --in target.");
|
|
@@ -8525,7 +8998,32 @@ function formatUnifiedSearchMetadata(entry, useColors) {
|
|
|
8525
8998
|
}
|
|
8526
8999
|
// src/cli.ts
|
|
8527
9000
|
var program = new Command;
|
|
9001
|
+
var argv = process.argv.slice(2);
|
|
8528
9002
|
var commandSpans = new WeakMap;
|
|
9003
|
+
var createUpdateCheckService = () => new NpmRegistryUpdateCheckService({
|
|
9004
|
+
currentVersion: version,
|
|
9005
|
+
fileSystemService: new FileSystemServiceImpl
|
|
9006
|
+
});
|
|
9007
|
+
await enforceCachedRequiredUpdateForInvocation({
|
|
9008
|
+
args: argv,
|
|
9009
|
+
env: process.env,
|
|
9010
|
+
createService: createUpdateCheckService,
|
|
9011
|
+
stderr: process.stderr,
|
|
9012
|
+
exit: process.exit
|
|
9013
|
+
});
|
|
9014
|
+
var updateCheckTask = startUpdateCheckTaskForInvocation({
|
|
9015
|
+
args: argv,
|
|
9016
|
+
env: process.env,
|
|
9017
|
+
stderrIsTTY: process.stderr.isTTY === true,
|
|
9018
|
+
stdinIsTTY: process.stdin.isTTY === true,
|
|
9019
|
+
stdoutIsTTY: process.stdout.isTTY === true,
|
|
9020
|
+
createService: createUpdateCheckService
|
|
9021
|
+
});
|
|
9022
|
+
var requiredUpdateRefreshTask = startRequiredUpdateRefreshTaskForInvocation({
|
|
9023
|
+
args: argv,
|
|
9024
|
+
env: process.env,
|
|
9025
|
+
createService: createUpdateCheckService
|
|
9026
|
+
});
|
|
8529
9027
|
if (isTelemetryEnabled()) {
|
|
8530
9028
|
process.once("exit", (exitCode) => {
|
|
8531
9029
|
flushTelemetry(exitCode);
|
|
@@ -8556,25 +9054,34 @@ registerMcpCommand(program);
|
|
|
8556
9054
|
registerExampleCommand(program);
|
|
8557
9055
|
registerLanguagesCommand(program);
|
|
8558
9056
|
registerFeedbackCommand(program);
|
|
8559
|
-
var argv = process.argv.slice(2);
|
|
8560
9057
|
var registrationArgv = stripRootRegistrationOptions(argv);
|
|
8561
|
-
var shouldLoadGatedHelpRegistration = needsGatedHelpRegistration(registrationArgv);
|
|
8562
|
-
var helpRegistrationOptions = shouldLoadGatedHelpRegistration ? await loadHelpRegistrationOptions(registrationArgv) : undefined;
|
|
8563
9058
|
if (shouldEagerLoadSearchCommands(registrationArgv)) {
|
|
8564
|
-
await withTelemetrySpan("cli.register.search", () => registerUnifiedSearchCommands(program
|
|
9059
|
+
await withTelemetrySpan("cli.register.search", () => registerUnifiedSearchCommands(program));
|
|
8565
9060
|
}
|
|
8566
9061
|
if (shouldEagerLoadGatedCommandGroup(registrationArgv, "code")) {
|
|
8567
|
-
await withTelemetrySpan("cli.register.code-group", () => registerCodeCommandGroup(program
|
|
9062
|
+
await withTelemetrySpan("cli.register.code-group", () => registerCodeCommandGroup(program));
|
|
8568
9063
|
}
|
|
8569
9064
|
if (shouldEagerLoadGatedCommandGroup(registrationArgv, "pkg")) {
|
|
8570
|
-
await withTelemetrySpan("cli.register.pkg-group", () => registerPkgCommandGroup(program
|
|
9065
|
+
await withTelemetrySpan("cli.register.pkg-group", () => registerPkgCommandGroup(program));
|
|
8571
9066
|
}
|
|
8572
9067
|
if (shouldEagerLoadGatedCommandGroup(registrationArgv, "docs")) {
|
|
8573
|
-
await withTelemetrySpan("cli.register.docs-group", () => registerDocsCommandGroup(program
|
|
9068
|
+
await withTelemetrySpan("cli.register.docs-group", () => registerDocsCommandGroup(program));
|
|
8574
9069
|
}
|
|
8575
9070
|
var authCommand = program.command("auth").summary("Manage authentication").description("Manage authentication with GitHits.");
|
|
8576
9071
|
registerAuthStatusCommand(authCommand);
|
|
8577
|
-
|
|
9072
|
+
try {
|
|
9073
|
+
await runWithUpdateCheckFlush(() => withTelemetrySpan("cli.parse", () => program.parseAsync()), updateCheckTask, { stderr: process.stderr, requiredUpdateRefreshTask });
|
|
9074
|
+
} catch (error2) {
|
|
9075
|
+
if (isUserFacingError(error2)) {
|
|
9076
|
+
console.error(`${error2.message}
|
|
9077
|
+
`);
|
|
9078
|
+
process.exit(1);
|
|
9079
|
+
}
|
|
9080
|
+
throw error2;
|
|
9081
|
+
}
|
|
9082
|
+
function isUserFacingError(error2) {
|
|
9083
|
+
return error2 instanceof AuthConfigError || error2 instanceof AuthStoragePolicyError;
|
|
9084
|
+
}
|
|
8578
9085
|
function stripRootRegistrationOptions(args) {
|
|
8579
9086
|
return args.filter((arg) => arg !== "--no-color");
|
|
8580
9087
|
}
|
|
@@ -8586,34 +9093,9 @@ function shouldEagerLoadSearchCommands(args) {
|
|
|
8586
9093
|
const [firstArg] = args;
|
|
8587
9094
|
return args.length === 0 || firstArg === "search" || firstArg === "search-status" || firstArg === "--help" || firstArg === "-h" || firstArg === "help" && (!args[1] || isSearchHelpTarget(args[1]));
|
|
8588
9095
|
}
|
|
8589
|
-
function isHelpInvocation(args) {
|
|
8590
|
-
return args.length === 0 || args[0] === "help" || args.includes("--help") || args.includes("-h");
|
|
8591
|
-
}
|
|
8592
|
-
function needsGatedHelpRegistration(args) {
|
|
8593
|
-
if (!isHelpInvocation(args)) {
|
|
8594
|
-
return false;
|
|
8595
|
-
}
|
|
8596
|
-
const [firstArg, secondArg] = args;
|
|
8597
|
-
if (firstArg === "help") {
|
|
8598
|
-
return isSearchHelpTarget(secondArg) || secondArg === "code" || secondArg === "pkg" || secondArg === "docs";
|
|
8599
|
-
}
|
|
8600
|
-
return isSearchHelpTarget(firstArg) || firstArg === "code" || firstArg === "pkg" || firstArg === "docs";
|
|
8601
|
-
}
|
|
8602
9096
|
function isSearchHelpTarget(value) {
|
|
8603
9097
|
return value === "search" || value === "search-status";
|
|
8604
9098
|
}
|
|
8605
|
-
async function loadHelpRegistrationOptions(args) {
|
|
8606
|
-
const { resolveStartupCodeNavigationRegistrationState: resolveStartupCodeNavigationRegistrationState2 } = await import("./shared/chunk-9zze00cp.js");
|
|
8607
|
-
const registrationState = await resolveStartupCodeNavigationRegistrationState2();
|
|
8608
|
-
return {
|
|
8609
|
-
capability: registrationState.capability,
|
|
8610
|
-
expiredStoredAuth: shouldUseExpiredStoredAuthFallbackForHelp(args) ? registrationState.expiredStoredAuth : false
|
|
8611
|
-
};
|
|
8612
|
-
}
|
|
8613
|
-
function shouldUseExpiredStoredAuthFallbackForHelp(args) {
|
|
8614
|
-
const [firstArg, secondArg] = args;
|
|
8615
|
-
return firstArg === "search" || firstArg === "search-status" || firstArg === "code" || firstArg === "pkg" || firstArg === "docs" || firstArg === "help" && (isSearchHelpTarget(secondArg) || secondArg === "code" || secondArg === "pkg" || secondArg === "docs");
|
|
8616
|
-
}
|
|
8617
9099
|
function getTelemetryCommandName(command) {
|
|
8618
9100
|
const names = [];
|
|
8619
9101
|
let current = command;
|