githits 0.2.2 → 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 +1220 -203
- package/dist/index.js +1 -1
- package/dist/shared/chunk-1gqfte6e.js +11 -0
- package/dist/shared/{chunk-ns1j9dan.js → chunk-5mk9k2gv.js} +1269 -626
- package/dist/shared/{chunk-g6ay6x9v.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-4drb8s8v.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")
|
|
@@ -1146,12 +1276,280 @@ function mergeRanges(existing, incoming) {
|
|
|
1146
1276
|
function clampCharacterOffset(text, offset) {
|
|
1147
1277
|
return Math.max(0, Math.min(text.length, offset));
|
|
1148
1278
|
}
|
|
1279
|
+
// src/shared/grep-repo-text.ts
|
|
1280
|
+
var SEP = " | ";
|
|
1281
|
+
function renderGrepRepoText(envelope) {
|
|
1282
|
+
const lines = [];
|
|
1283
|
+
lines.push(buildHeader(envelope));
|
|
1284
|
+
lines.push("");
|
|
1285
|
+
if (envelope.matches.length === 0) {
|
|
1286
|
+
lines.push("No matches.");
|
|
1287
|
+
const trailer2 = buildTrailer(envelope);
|
|
1288
|
+
if (trailer2.length > 0) {
|
|
1289
|
+
lines.push("");
|
|
1290
|
+
for (const t of trailer2)
|
|
1291
|
+
lines.push(t);
|
|
1292
|
+
}
|
|
1293
|
+
return lines.join(`
|
|
1294
|
+
`);
|
|
1295
|
+
}
|
|
1296
|
+
const blocks = buildRenderBlocks2(envelope.matches);
|
|
1297
|
+
const blocksByFile = groupBlocksByFile2(blocks);
|
|
1298
|
+
const useContext = blocksHaveContext(blocks);
|
|
1299
|
+
const matchCountsByFile = countMatchesByFile(envelope.matches);
|
|
1300
|
+
let firstFile = true;
|
|
1301
|
+
for (const [filePath, fileBlocks] of blocksByFile) {
|
|
1302
|
+
if (!firstFile)
|
|
1303
|
+
lines.push("");
|
|
1304
|
+
firstFile = false;
|
|
1305
|
+
const matchCount = matchCountsByFile.get(filePath) ?? 0;
|
|
1306
|
+
lines.push(`${filePath} (${matchCount})`);
|
|
1307
|
+
fileBlocks.forEach((block, idx) => {
|
|
1308
|
+
if (useContext && idx > 0)
|
|
1309
|
+
lines.push(" --");
|
|
1310
|
+
const gutterWidth = widestLineNumberInBlock(block);
|
|
1311
|
+
for (const ln of block.lines) {
|
|
1312
|
+
lines.push(renderLine(ln, gutterWidth, useContext));
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
const trailer = buildTrailer(envelope);
|
|
1317
|
+
if (trailer.length > 0) {
|
|
1318
|
+
lines.push("");
|
|
1319
|
+
for (const t of trailer)
|
|
1320
|
+
lines.push(t);
|
|
1321
|
+
}
|
|
1322
|
+
return lines.join(`
|
|
1323
|
+
`);
|
|
1324
|
+
}
|
|
1325
|
+
function buildHeader(envelope) {
|
|
1326
|
+
const parts = [
|
|
1327
|
+
`code_grep${SEP}${envelope.totalMatches} match${envelope.totalMatches === 1 ? "" : "es"} in ${envelope.uniqueFilesMatched} file${envelope.uniqueFilesMatched === 1 ? "" : "s"}`
|
|
1328
|
+
];
|
|
1329
|
+
parts.push(`pattern=${quote(envelope.pattern)}`);
|
|
1330
|
+
const flags = [];
|
|
1331
|
+
if (envelope.patternType === "regex")
|
|
1332
|
+
flags.push("regex");
|
|
1333
|
+
if (envelope.caseSensitive)
|
|
1334
|
+
flags.push("case-sensitive");
|
|
1335
|
+
if (flags.length > 0)
|
|
1336
|
+
parts.push(flags.join(","));
|
|
1337
|
+
const filterEcho = buildFilterEcho(envelope);
|
|
1338
|
+
if (filterEcho)
|
|
1339
|
+
parts.push(filterEcho);
|
|
1340
|
+
return parts.join(SEP);
|
|
1341
|
+
}
|
|
1342
|
+
function buildFilterEcho(envelope) {
|
|
1343
|
+
const filter = envelope.filter;
|
|
1344
|
+
if (!filter)
|
|
1345
|
+
return "";
|
|
1346
|
+
const parts = [];
|
|
1347
|
+
if (filter.path)
|
|
1348
|
+
parts.push(`path=${quote(filter.path)}`);
|
|
1349
|
+
if (filter.pathPrefix)
|
|
1350
|
+
parts.push(`path_prefix=${quote(filter.pathPrefix)}`);
|
|
1351
|
+
if (filter.globs?.length)
|
|
1352
|
+
parts.push(`globs=${filter.globs.join(",")}`);
|
|
1353
|
+
if (filter.extensions?.length) {
|
|
1354
|
+
parts.push(`exts=${filter.extensions.join(",")}`);
|
|
1355
|
+
}
|
|
1356
|
+
if (typeof filter.maxMatches === "number") {
|
|
1357
|
+
parts.push(`max_matches=${filter.maxMatches}`);
|
|
1358
|
+
}
|
|
1359
|
+
if (typeof filter.maxMatchesPerFile === "number") {
|
|
1360
|
+
parts.push(`max_matches_per_file=${filter.maxMatchesPerFile}`);
|
|
1361
|
+
}
|
|
1362
|
+
return parts.join(" ");
|
|
1363
|
+
}
|
|
1364
|
+
function buildTrailer(envelope) {
|
|
1365
|
+
const lines = [];
|
|
1366
|
+
if (envelope.truncatedReason) {
|
|
1367
|
+
lines.push(`Truncated: ${envelope.truncatedReason}. Pass narrower path/path_prefix/globs or increase max_matches.`);
|
|
1368
|
+
}
|
|
1369
|
+
if (envelope.hasMore && envelope.nextCursor) {
|
|
1370
|
+
lines.push(`More matches available. Pass cursor=${envelope.nextCursor} for the next page.`);
|
|
1371
|
+
} else if (envelope.hasMore) {
|
|
1372
|
+
lines.push("More matches available.");
|
|
1373
|
+
}
|
|
1374
|
+
const skipNotes = [];
|
|
1375
|
+
if (envelope.binaryFilesSkipped) {
|
|
1376
|
+
skipNotes.push(`${envelope.binaryFilesSkipped} binary file(s) skipped`);
|
|
1377
|
+
}
|
|
1378
|
+
if (envelope.filesTooLargeSkipped) {
|
|
1379
|
+
skipNotes.push(`${envelope.filesTooLargeSkipped} oversized file(s) skipped`);
|
|
1380
|
+
}
|
|
1381
|
+
if (skipNotes.length > 0) {
|
|
1382
|
+
lines.push(`Note: ${skipNotes.join(", ")}.`);
|
|
1383
|
+
}
|
|
1384
|
+
return lines;
|
|
1385
|
+
}
|
|
1386
|
+
function buildRenderBlocks2(matches) {
|
|
1387
|
+
if (matches.length === 0)
|
|
1388
|
+
return [];
|
|
1389
|
+
const linesByFile = new Map;
|
|
1390
|
+
for (const match of matches) {
|
|
1391
|
+
let lineMap = linesByFile.get(match.filePath);
|
|
1392
|
+
if (!lineMap) {
|
|
1393
|
+
lineMap = new Map;
|
|
1394
|
+
linesByFile.set(match.filePath, lineMap);
|
|
1395
|
+
}
|
|
1396
|
+
const before = match.contextBefore ?? [];
|
|
1397
|
+
const beforeStart = match.line - before.length;
|
|
1398
|
+
for (let i = 0;i < before.length; i += 1) {
|
|
1399
|
+
const lineNumber = beforeStart + i;
|
|
1400
|
+
if (!lineMap.has(lineNumber)) {
|
|
1401
|
+
lineMap.set(lineNumber, {
|
|
1402
|
+
lineNumber,
|
|
1403
|
+
content: before[i] ?? "",
|
|
1404
|
+
isMatch: false
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
lineMap.set(match.line, {
|
|
1409
|
+
lineNumber: match.line,
|
|
1410
|
+
content: match.lineContent,
|
|
1411
|
+
isMatch: true
|
|
1412
|
+
});
|
|
1413
|
+
const after = match.contextAfter ?? [];
|
|
1414
|
+
for (let i = 0;i < after.length; i += 1) {
|
|
1415
|
+
const lineNumber = match.line + i + 1;
|
|
1416
|
+
if (!lineMap.has(lineNumber)) {
|
|
1417
|
+
lineMap.set(lineNumber, {
|
|
1418
|
+
lineNumber,
|
|
1419
|
+
content: after[i] ?? "",
|
|
1420
|
+
isMatch: false
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
const blocks = [];
|
|
1426
|
+
for (const [filePath, lineMap] of linesByFile) {
|
|
1427
|
+
const sorted = [...lineMap.values()].sort((a, b) => a.lineNumber - b.lineNumber);
|
|
1428
|
+
let current = [];
|
|
1429
|
+
for (const line of sorted) {
|
|
1430
|
+
const previous = current[current.length - 1];
|
|
1431
|
+
if (!previous || line.lineNumber === previous.lineNumber + 1) {
|
|
1432
|
+
current.push(line);
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1435
|
+
blocks.push({ filePath, lines: current });
|
|
1436
|
+
current = [line];
|
|
1437
|
+
}
|
|
1438
|
+
if (current.length > 0) {
|
|
1439
|
+
blocks.push({ filePath, lines: current });
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return blocks;
|
|
1443
|
+
}
|
|
1444
|
+
function groupBlocksByFile2(blocks) {
|
|
1445
|
+
const map = new Map;
|
|
1446
|
+
for (const block of blocks) {
|
|
1447
|
+
const list = map.get(block.filePath) ?? [];
|
|
1448
|
+
list.push(block);
|
|
1449
|
+
map.set(block.filePath, list);
|
|
1450
|
+
}
|
|
1451
|
+
return map;
|
|
1452
|
+
}
|
|
1453
|
+
function blocksHaveContext(blocks) {
|
|
1454
|
+
for (const block of blocks) {
|
|
1455
|
+
for (const line of block.lines) {
|
|
1456
|
+
if (!line.isMatch)
|
|
1457
|
+
return true;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
return false;
|
|
1461
|
+
}
|
|
1462
|
+
function widestLineNumberInBlock(block) {
|
|
1463
|
+
let max = 0;
|
|
1464
|
+
for (const line of block.lines) {
|
|
1465
|
+
const len = String(line.lineNumber).length;
|
|
1466
|
+
if (len > max)
|
|
1467
|
+
max = len;
|
|
1468
|
+
}
|
|
1469
|
+
return max;
|
|
1470
|
+
}
|
|
1471
|
+
function countMatchesByFile(matches) {
|
|
1472
|
+
const counts = new Map;
|
|
1473
|
+
for (const match of matches) {
|
|
1474
|
+
counts.set(match.filePath, (counts.get(match.filePath) ?? 0) + 1);
|
|
1475
|
+
}
|
|
1476
|
+
return counts;
|
|
1477
|
+
}
|
|
1478
|
+
function renderLine(line, gutterWidth, useContext) {
|
|
1479
|
+
const gutter = String(line.lineNumber).padStart(gutterWidth, " ");
|
|
1480
|
+
const sep = !useContext || line.isMatch ? ":" : "-";
|
|
1481
|
+
return ` ${gutter}${sep} ${line.content}`;
|
|
1482
|
+
}
|
|
1483
|
+
function quote(value) {
|
|
1484
|
+
return value.includes('"') ? `'${value}'` : `"${value}"`;
|
|
1485
|
+
}
|
|
1149
1486
|
// src/shared/language-filter.ts
|
|
1150
1487
|
var DEFAULT_LIMIT = 5;
|
|
1151
1488
|
function filterLanguages(languages, query, limit = DEFAULT_LIMIT) {
|
|
1152
1489
|
const lowerQuery = query.toLowerCase();
|
|
1153
1490
|
return languages.filter((lang) => lang.name.toLowerCase().includes(lowerQuery) || lang.display_name.toLowerCase().includes(lowerQuery) || lang.aliases.some((a) => a.toLowerCase().includes(lowerQuery))).slice(0, limit).map(({ name, display_name }) => ({ name, display_name }));
|
|
1154
1491
|
}
|
|
1492
|
+
// src/shared/list-files-text.ts
|
|
1493
|
+
var SEP2 = " | ";
|
|
1494
|
+
function renderListFilesText(envelope) {
|
|
1495
|
+
const lines = [];
|
|
1496
|
+
lines.push(buildHeader2(envelope));
|
|
1497
|
+
lines.push("");
|
|
1498
|
+
if (envelope.files.length === 0) {
|
|
1499
|
+
lines.push(envelope.hint ?? "No files match the requested filter.");
|
|
1500
|
+
return lines.join(`
|
|
1501
|
+
`);
|
|
1502
|
+
}
|
|
1503
|
+
for (const entry of envelope.files) {
|
|
1504
|
+
lines.push(entry.path);
|
|
1505
|
+
}
|
|
1506
|
+
if (envelope.hasMore) {
|
|
1507
|
+
lines.push("");
|
|
1508
|
+
lines.push("More files available. Pass limit=N to widen or refine path_prefix.");
|
|
1509
|
+
}
|
|
1510
|
+
if (envelope.hint) {
|
|
1511
|
+
lines.push("");
|
|
1512
|
+
lines.push(envelope.hint);
|
|
1513
|
+
}
|
|
1514
|
+
return lines.join(`
|
|
1515
|
+
`);
|
|
1516
|
+
}
|
|
1517
|
+
function buildHeader2(envelope) {
|
|
1518
|
+
const identity = buildIdentity(envelope);
|
|
1519
|
+
const countValue = envelope.hasMore ? `${envelope.files.length}+` : String(envelope.total);
|
|
1520
|
+
const parts = [
|
|
1521
|
+
`code_files${SEP2}${countValue} path${countValue === "1" ? "" : "s"}`
|
|
1522
|
+
];
|
|
1523
|
+
if (identity)
|
|
1524
|
+
parts.push(identity);
|
|
1525
|
+
const filter = buildFilterEcho2(envelope);
|
|
1526
|
+
if (filter)
|
|
1527
|
+
parts.push(filter);
|
|
1528
|
+
return parts.join(SEP2);
|
|
1529
|
+
}
|
|
1530
|
+
function buildIdentity(envelope) {
|
|
1531
|
+
if (envelope.registry && envelope.name) {
|
|
1532
|
+
const version2 = envelope.indexedVersion ?? envelope.resolution?.resolvedRef;
|
|
1533
|
+
return version2 ? `${envelope.registry}:${envelope.name}@${version2}` : `${envelope.registry}:${envelope.name}`;
|
|
1534
|
+
}
|
|
1535
|
+
if (envelope.repoUrl) {
|
|
1536
|
+
return envelope.gitRef ? `${envelope.repoUrl}@${envelope.gitRef}` : envelope.repoUrl;
|
|
1537
|
+
}
|
|
1538
|
+
return "";
|
|
1539
|
+
}
|
|
1540
|
+
function buildFilterEcho2(envelope) {
|
|
1541
|
+
const parts = [];
|
|
1542
|
+
if (envelope.filter?.pathPrefix) {
|
|
1543
|
+
parts.push(`path_prefix=${quote2(envelope.filter.pathPrefix)}`);
|
|
1544
|
+
}
|
|
1545
|
+
if (envelope.filter?.limit !== undefined) {
|
|
1546
|
+
parts.push(`limit=${envelope.filter.limit}`);
|
|
1547
|
+
}
|
|
1548
|
+
return parts.join(" ");
|
|
1549
|
+
}
|
|
1550
|
+
function quote2(value) {
|
|
1551
|
+
return value.includes('"') ? `'${value}'` : `"${value}"`;
|
|
1552
|
+
}
|
|
1155
1553
|
// src/shared/list-package-docs-request.ts
|
|
1156
1554
|
function buildListPackageDocsParams(input) {
|
|
1157
1555
|
const packageName = input.packageName?.trim() ?? "";
|
|
@@ -2037,6 +2435,9 @@ function mapPackageIntelligenceError(error2) {
|
|
|
2037
2435
|
return mapped;
|
|
2038
2436
|
}
|
|
2039
2437
|
function classify2(error2) {
|
|
2438
|
+
if (error2 instanceof ClientUpdateRequiredError) {
|
|
2439
|
+
return buildUpdateRequiredError(error2.reason, error2.currentVersion);
|
|
2440
|
+
}
|
|
2040
2441
|
if (error2 instanceof PackageIntelligenceTargetNotFoundError || error2 instanceof PackageIntelligenceChangelogSourceNotFoundError) {
|
|
2041
2442
|
return {
|
|
2042
2443
|
code: "NOT_FOUND",
|
|
@@ -2596,19 +2997,20 @@ function isSeverityLabel(value) {
|
|
|
2596
2997
|
function buildPackageVulnerabilitiesSuccessPayload(report, options = {}) {
|
|
2597
2998
|
const pkg = report.package;
|
|
2598
2999
|
const security = report.security;
|
|
2599
|
-
const
|
|
3000
|
+
const dedupedAdvisories = dedupAdvisoriesByAlias(security?.vulnerabilities ?? []);
|
|
3001
|
+
const total = security?.affectedVulnerabilityCount ?? dedupedAdvisories.length;
|
|
2600
3002
|
const payload = {
|
|
2601
3003
|
registry: lowerRegistry3(pkg.registry),
|
|
2602
3004
|
name: pkg.name,
|
|
2603
3005
|
version: pkg.version,
|
|
2604
|
-
summary: buildSummary(total, security)
|
|
3006
|
+
summary: buildSummary(total, security, dedupedAdvisories)
|
|
2605
3007
|
};
|
|
2606
3008
|
const requestedEcho = deriveRequestedVersion2(options.requestedVersion, pkg.version);
|
|
2607
3009
|
if (requestedEcho !== undefined) {
|
|
2608
3010
|
payload.requestedVersion = requestedEcho;
|
|
2609
3011
|
}
|
|
2610
3012
|
if (total > 0) {
|
|
2611
|
-
const sortedAdvisories = sortAdvisories(
|
|
3013
|
+
const sortedAdvisories = sortAdvisories(dedupedAdvisories.map(buildAdvisory));
|
|
2612
3014
|
if (sortedAdvisories.length > 0) {
|
|
2613
3015
|
payload.advisories = sortedAdvisories;
|
|
2614
3016
|
}
|
|
@@ -2647,14 +3049,19 @@ function parseVersionForSort(v) {
|
|
|
2647
3049
|
});
|
|
2648
3050
|
return { main, pre };
|
|
2649
3051
|
}
|
|
2650
|
-
function buildSummary(total, security) {
|
|
3052
|
+
function buildSummary(total, security, dedupedAdvisories) {
|
|
2651
3053
|
const summary = { total };
|
|
2652
|
-
if (
|
|
2653
|
-
|
|
3054
|
+
if (security) {
|
|
3055
|
+
summary.affectedVulnerabilityCount = security.affectedVulnerabilityCount;
|
|
3056
|
+
summary.nonAffectingVulnerabilityCount = security.nonAffectingVulnerabilityCount;
|
|
3057
|
+
summary.allVulnerabilityCount = security.allVulnerabilityCount;
|
|
3058
|
+
}
|
|
2654
3059
|
if (typeof security?.currentVersionAffected === "boolean") {
|
|
2655
3060
|
summary.affected = security.currentVersionAffected;
|
|
2656
3061
|
}
|
|
2657
|
-
|
|
3062
|
+
if (total === 0)
|
|
3063
|
+
return summary;
|
|
3064
|
+
const bySeverity = computeBySeverity(dedupedAdvisories);
|
|
2658
3065
|
const anyCounted = Object.values(bySeverity).some((n) => n > 0);
|
|
2659
3066
|
if (anyCounted) {
|
|
2660
3067
|
const trimmed = {};
|
|
@@ -2709,6 +3116,231 @@ function vulnSeverityLabel(score) {
|
|
|
2709
3116
|
return "medium";
|
|
2710
3117
|
return "low";
|
|
2711
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
|
+
}
|
|
2712
3344
|
function buildAdvisory(advisory) {
|
|
2713
3345
|
const lean = {};
|
|
2714
3346
|
if (advisory.osvId)
|
|
@@ -2728,6 +3360,21 @@ function buildAdvisory(advisory) {
|
|
|
2728
3360
|
if (advisory.affectedVersionRanges && advisory.affectedVersionRanges.length > 0) {
|
|
2729
3361
|
lean.affectedRanges = advisory.affectedVersionRanges.slice();
|
|
2730
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
|
+
}
|
|
2731
3378
|
if (advisory.fixedInVersions && advisory.fixedInVersions.length > 0) {
|
|
2732
3379
|
lean.fixedIn = advisory.fixedInVersions.slice();
|
|
2733
3380
|
}
|
|
@@ -2810,7 +3457,7 @@ function formatPackageVulnerabilitiesTerminal(report, options = {}) {
|
|
|
2810
3457
|
const lines = [headerLine];
|
|
2811
3458
|
if (requestedLine)
|
|
2812
3459
|
lines.push(requestedLine);
|
|
2813
|
-
lines.push(
|
|
3460
|
+
lines.push(formatNoAffectedVulnerabilitiesLine(payload));
|
|
2814
3461
|
return `${lines.join(`
|
|
2815
3462
|
`)}
|
|
2816
3463
|
`;
|
|
@@ -2844,15 +3491,25 @@ function formatHeader(payload, useColors) {
|
|
|
2844
3491
|
function formatSummaryLine(payload, useColors) {
|
|
2845
3492
|
const n = payload.summary.total;
|
|
2846
3493
|
const noun = n === 1 ? "vulnerability" : "vulnerabilities";
|
|
2847
|
-
const
|
|
3494
|
+
const verb = n === 1 ? "affects" : "affect";
|
|
3495
|
+
const base = `${n} ${noun} ${verb} this version`;
|
|
2848
3496
|
if (payload.summary.affected === true) {
|
|
2849
|
-
return colorize(
|
|
3497
|
+
return colorize(base, "yellow", useColors);
|
|
2850
3498
|
}
|
|
2851
3499
|
if (payload.summary.affected === false) {
|
|
2852
|
-
return
|
|
3500
|
+
return base;
|
|
2853
3501
|
}
|
|
2854
3502
|
return base;
|
|
2855
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
|
+
}
|
|
2856
3513
|
function formatBreakdownLine(summary, useColors) {
|
|
2857
3514
|
if (summary.total <= 1)
|
|
2858
3515
|
return;
|
|
@@ -2938,7 +3595,7 @@ function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimi
|
|
|
2938
3595
|
lines.push(` ${label.padEnd(detailWidth)} ${value}`);
|
|
2939
3596
|
};
|
|
2940
3597
|
if (advisory.affectedRanges && advisory.affectedRanges.length > 0) {
|
|
2941
|
-
pushRow("affected", formatRangeList(advisory.affectedRanges, verbose, useColors, rangeLimit));
|
|
3598
|
+
pushRow("affected", formatRangeList(advisory.affectedRanges, verbose, useColors, rangeLimit, advisory.affectedVersionRangesCount, advisory.affectedVersionRangesTruncated));
|
|
2942
3599
|
}
|
|
2943
3600
|
if (advisory.fixedIn && advisory.fixedIn.length > 0) {
|
|
2944
3601
|
pushRow("fixed in", advisory.fixedIn.join(", "));
|
|
@@ -2965,13 +3622,23 @@ function formatAdvisoryLines(advisory, labelWidth, verbose, useColors, rangeLimi
|
|
|
2965
3622
|
}
|
|
2966
3623
|
return lines;
|
|
2967
3624
|
}
|
|
2968
|
-
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
|
+
};
|
|
2969
3635
|
if (verbose || ranges.length <= limit) {
|
|
2970
|
-
return ranges.join(", ");
|
|
3636
|
+
return appendBackendHint(ranges.join(", "));
|
|
2971
3637
|
}
|
|
2972
3638
|
const shown = ranges.slice(0, limit).join(", ");
|
|
2973
|
-
const
|
|
2974
|
-
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);
|
|
2975
3642
|
return `${shown}, ${hint}`;
|
|
2976
3643
|
}
|
|
2977
3644
|
function resolveAffectedRangesLimit(terminalWidth) {
|
|
@@ -2986,8 +3653,8 @@ function formatUpgradeFooter(paths) {
|
|
|
2986
3653
|
if (!paths || paths.length === 0)
|
|
2987
3654
|
return;
|
|
2988
3655
|
if (paths.length === 1)
|
|
2989
|
-
return `
|
|
2990
|
-
return `
|
|
3656
|
+
return `Fix version: ${paths[0]}.`;
|
|
3657
|
+
return `Fix versions: ${paths.join(", ")}.`;
|
|
2991
3658
|
}
|
|
2992
3659
|
// src/shared/parse-lines-option.ts
|
|
2993
3660
|
function parseLinesOption(raw) {
|
|
@@ -3114,7 +3781,7 @@ function formatReadPackageDocTerminal(envelope, options) {
|
|
|
3114
3781
|
return envelope.content ?? "";
|
|
3115
3782
|
}
|
|
3116
3783
|
const lines = [];
|
|
3117
|
-
lines.push(
|
|
3784
|
+
lines.push(buildHeader3(envelope, options.useColors));
|
|
3118
3785
|
lines.push(`pageId: ${envelope.pageId}`);
|
|
3119
3786
|
if (envelope.sourceUrl)
|
|
3120
3787
|
lines.push(`source: ${envelope.sourceUrl}`);
|
|
@@ -3134,7 +3801,7 @@ function formatReadPackageDocTerminal(envelope, options) {
|
|
|
3134
3801
|
`)}
|
|
3135
3802
|
`;
|
|
3136
3803
|
}
|
|
3137
|
-
function
|
|
3804
|
+
function buildHeader3(envelope, useColors) {
|
|
3138
3805
|
const badge = envelope.sourceKind === "repo" ? "[repo]" : "[crawled]";
|
|
3139
3806
|
const title = envelope.title ?? envelope.pageId;
|
|
3140
3807
|
const prefix = envelope.registry && envelope.name ? `${envelope.registry}:${envelope.name}${envelope.version ? `@${envelope.version}` : ""}` : "documentation";
|
|
@@ -3167,10 +3834,11 @@ Need help? support@githits.com`);
|
|
|
3167
3834
|
throw new AuthRequiredError(`Authentication required${suffix}`);
|
|
3168
3835
|
}
|
|
3169
3836
|
// src/shared/unified-search-request.ts
|
|
3837
|
+
var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
|
|
3170
3838
|
function buildUnifiedSearchParams(input) {
|
|
3171
3839
|
const targets = resolveTargets(input.target, input.targets);
|
|
3172
3840
|
const rawQuery = normaliseRequiredQuery(input.query);
|
|
3173
|
-
const limit = input.limit ??
|
|
3841
|
+
const limit = input.limit ?? DEFAULT_UNIFIED_SEARCH_LIMIT;
|
|
3174
3842
|
const offset = input.offset ?? 0;
|
|
3175
3843
|
const waitTimeoutMs = input.waitTimeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
3176
3844
|
const qualifierClauses = buildQualifierClauses({
|
|
@@ -3276,7 +3944,7 @@ function buildFilters(input) {
|
|
|
3276
3944
|
return Object.keys(filters).length > 0 ? filters : undefined;
|
|
3277
3945
|
}
|
|
3278
3946
|
// src/shared/unified-search-response.ts
|
|
3279
|
-
var DEFAULT_LIMIT2 =
|
|
3947
|
+
var DEFAULT_LIMIT2 = DEFAULT_UNIFIED_SEARCH_LIMIT;
|
|
3280
3948
|
var DEFAULT_OFFSET = 0;
|
|
3281
3949
|
function buildUnifiedSearchSuccessPayload(params, rawQuery, compiledQuery, outcome) {
|
|
3282
3950
|
const warnings = outcome.state === "completed" ? outcome.result.queryWarnings : outcome.result?.queryWarnings ?? outcome.progress?.queryWarnings ?? [];
|
|
@@ -3299,6 +3967,9 @@ function buildUnifiedSearchSuccessPayload(params, rawQuery, compiledQuery, outco
|
|
|
3299
3967
|
const sourceStatus2 = compactSourceStatus(result?.sourceStatus);
|
|
3300
3968
|
if (sourceStatus2)
|
|
3301
3969
|
payload.sourceStatus = sourceStatus2;
|
|
3970
|
+
const combinedWarnings2 = combineWarnings(warnings, sourceStatus2);
|
|
3971
|
+
if (combinedWarnings2.length > 0)
|
|
3972
|
+
payload.warnings = combinedWarnings2;
|
|
3302
3973
|
return payload;
|
|
3303
3974
|
}
|
|
3304
3975
|
const completed = {
|
|
@@ -3315,8 +3986,18 @@ function buildUnifiedSearchSuccessPayload(params, rawQuery, compiledQuery, outco
|
|
|
3315
3986
|
const sourceStatus = compactSourceStatus(outcome.result.sourceStatus);
|
|
3316
3987
|
if (sourceStatus)
|
|
3317
3988
|
completed.sourceStatus = sourceStatus;
|
|
3989
|
+
const combinedWarnings = combineWarnings(warnings, sourceStatus);
|
|
3990
|
+
if (combinedWarnings.length > 0)
|
|
3991
|
+
completed.warnings = combinedWarnings;
|
|
3318
3992
|
return completed;
|
|
3319
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
|
+
}
|
|
3320
4001
|
function buildUnifiedSearchErrorPayload(error2) {
|
|
3321
4002
|
const mapped = mapCodeNavigationError(error2);
|
|
3322
4003
|
const payload = {
|
|
@@ -3361,15 +4042,16 @@ function buildUnifiedSearchStatusResultPayload(result) {
|
|
|
3361
4042
|
if (result.page.hasMore) {
|
|
3362
4043
|
payload.nextOffset = result.page.offset + result.page.returned;
|
|
3363
4044
|
}
|
|
3364
|
-
if (result.queryWarnings.length > 0) {
|
|
3365
|
-
payload.warnings = result.queryWarnings;
|
|
3366
|
-
}
|
|
3367
4045
|
if (result.sources.length > 0) {
|
|
3368
4046
|
payload.sources = result.sources.map((entry) => entry.toLowerCase());
|
|
3369
4047
|
}
|
|
3370
4048
|
const sourceStatus = compactSourceStatus(result.sourceStatus);
|
|
3371
4049
|
if (sourceStatus)
|
|
3372
4050
|
payload.sourceStatus = sourceStatus;
|
|
4051
|
+
const combinedWarnings = combineWarnings(result.queryWarnings, sourceStatus);
|
|
4052
|
+
if (combinedWarnings.length > 0) {
|
|
4053
|
+
payload.warnings = combinedWarnings;
|
|
4054
|
+
}
|
|
3373
4055
|
return payload;
|
|
3374
4056
|
}
|
|
3375
4057
|
function buildQueryEcho(params, rawQuery, compiledQuery, warnings) {
|
|
@@ -3495,6 +4177,46 @@ function compactProgress(progress) {
|
|
|
3495
4177
|
payload.expiresAt = progress.expiresAt;
|
|
3496
4178
|
return payload;
|
|
3497
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
|
+
}
|
|
3498
4220
|
function compactSourceStatus(sourceStatus) {
|
|
3499
4221
|
if (!sourceStatus || sourceStatus.length === 0)
|
|
3500
4222
|
return;
|
|
@@ -3581,6 +4303,194 @@ function parseRepoTarget(spec) {
|
|
|
3581
4303
|
}
|
|
3582
4304
|
return { repoUrl, gitRef };
|
|
3583
4305
|
}
|
|
4306
|
+
// src/shared/unified-search-text.ts
|
|
4307
|
+
var SUMMARY_WRAP_WIDTH = 76;
|
|
4308
|
+
var SEP3 = " | ";
|
|
4309
|
+
function renderUnifiedSearchSuccess(payload) {
|
|
4310
|
+
const lines = [];
|
|
4311
|
+
lines.push(buildHeader4(payload));
|
|
4312
|
+
lines.push("");
|
|
4313
|
+
if (payload.results.length === 0) {
|
|
4314
|
+
lines.push(payload.completed ? "No hits." : "No hits yet - indexing.");
|
|
4315
|
+
} else {
|
|
4316
|
+
payload.results.forEach((hit, idx) => {
|
|
4317
|
+
if (idx > 0)
|
|
4318
|
+
lines.push("");
|
|
4319
|
+
appendHit(lines, idx + 1, hit);
|
|
4320
|
+
});
|
|
4321
|
+
}
|
|
4322
|
+
const trailer = buildTrailer2(payload);
|
|
4323
|
+
if (trailer.length > 0) {
|
|
4324
|
+
lines.push("");
|
|
4325
|
+
for (const line of trailer)
|
|
4326
|
+
lines.push(line);
|
|
4327
|
+
}
|
|
4328
|
+
return lines.join(`
|
|
4329
|
+
`);
|
|
4330
|
+
}
|
|
4331
|
+
function renderUnifiedSearchError(payload) {
|
|
4332
|
+
const lines = [];
|
|
4333
|
+
const header = `search${SEP3}ERROR${SEP3}code=${payload.code}${payload.retryable ? `${SEP3}retryable` : ""}`;
|
|
4334
|
+
lines.push(header);
|
|
4335
|
+
lines.push(payload.error);
|
|
4336
|
+
if (payload.details && Object.keys(payload.details).length > 0) {
|
|
4337
|
+
lines.push("");
|
|
4338
|
+
lines.push("details:");
|
|
4339
|
+
for (const [key, value] of Object.entries(payload.details)) {
|
|
4340
|
+
lines.push(` ${key}: ${formatDetailValue(value)}`);
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
return lines.join(`
|
|
4344
|
+
`);
|
|
4345
|
+
}
|
|
4346
|
+
function buildHeader4(payload) {
|
|
4347
|
+
const count = payload.results.length;
|
|
4348
|
+
const status = payload.completed ? `${count} hit${count === 1 ? "" : "s"}` : `${count} partial`;
|
|
4349
|
+
const parts = [`search${SEP3}${status}`];
|
|
4350
|
+
parts.push(`query=${quote3(payload.query.raw)}`);
|
|
4351
|
+
if (!payload.completed) {
|
|
4352
|
+
parts.push(`searchRef=${payload.searchRef}`);
|
|
4353
|
+
}
|
|
4354
|
+
return parts.join(SEP3);
|
|
4355
|
+
}
|
|
4356
|
+
function appendHit(lines, index, hit) {
|
|
4357
|
+
const headerParts = [hit.target, shortType(hit.type)];
|
|
4358
|
+
if (typeof hit.score === "number") {
|
|
4359
|
+
headerParts.push(formatScore(hit.score));
|
|
4360
|
+
}
|
|
4361
|
+
lines.push(`[${index}] ${headerParts.join(" ")}`);
|
|
4362
|
+
const locator = buildLocatorLine(hit);
|
|
4363
|
+
if (locator)
|
|
4364
|
+
lines.push(` ${locator}`);
|
|
4365
|
+
if (hit.title && hit.title !== hit.locator.filePath) {
|
|
4366
|
+
lines.push(` ${hit.title}`);
|
|
4367
|
+
}
|
|
4368
|
+
if (hit.summary) {
|
|
4369
|
+
for (const wrapped of wrapText2(hit.summary, SUMMARY_WRAP_WIDTH)) {
|
|
4370
|
+
lines.push(` ${wrapped}`);
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
function shortType(type) {
|
|
4375
|
+
switch (type) {
|
|
4376
|
+
case "repository_code":
|
|
4377
|
+
return "code";
|
|
4378
|
+
case "repository_symbol":
|
|
4379
|
+
return "symbol";
|
|
4380
|
+
case "documentation_page":
|
|
4381
|
+
return "docs";
|
|
4382
|
+
case "repository_doc":
|
|
4383
|
+
return "repo-docs";
|
|
4384
|
+
default:
|
|
4385
|
+
return type;
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
function buildLocatorLine(hit) {
|
|
4389
|
+
const loc = hit.locator;
|
|
4390
|
+
if (loc.filePath) {
|
|
4391
|
+
let line = `${loc.filePath}${formatLineRange(loc.startLine, loc.endLine)}`;
|
|
4392
|
+
const tail = [];
|
|
4393
|
+
if (loc.qualifiedPath)
|
|
4394
|
+
tail.push(loc.qualifiedPath);
|
|
4395
|
+
if (loc.kind)
|
|
4396
|
+
tail.push(loc.kind);
|
|
4397
|
+
if (tail.length > 0)
|
|
4398
|
+
line += ` ${tail.join(SEP3)}`;
|
|
4399
|
+
return line;
|
|
4400
|
+
}
|
|
4401
|
+
if (loc.pageId)
|
|
4402
|
+
return `pageId: ${loc.pageId}`;
|
|
4403
|
+
if (loc.sourceUrl)
|
|
4404
|
+
return loc.sourceUrl;
|
|
4405
|
+
return "";
|
|
4406
|
+
}
|
|
4407
|
+
function formatLineRange(start, end) {
|
|
4408
|
+
if (typeof start !== "number")
|
|
4409
|
+
return "";
|
|
4410
|
+
if (typeof end !== "number" || end === start)
|
|
4411
|
+
return `:${start}`;
|
|
4412
|
+
return `:${start}-${end}`;
|
|
4413
|
+
}
|
|
4414
|
+
function formatScore(score) {
|
|
4415
|
+
return score.toFixed(2);
|
|
4416
|
+
}
|
|
4417
|
+
function buildTrailer2(payload) {
|
|
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
|
+
}
|
|
4425
|
+
if (payload.hasMore) {
|
|
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.";
|
|
4427
|
+
lines.push(`More hits available.${nextOffsetHint}`);
|
|
4428
|
+
}
|
|
4429
|
+
if (!payload.completed && payload.searchRef) {
|
|
4430
|
+
lines.push(`Indexing in progress. Call search_status with searchRef=${payload.searchRef} to follow up.`);
|
|
4431
|
+
}
|
|
4432
|
+
if (payload.sourceStatus && payload.sourceStatus.length > 0) {
|
|
4433
|
+
lines.push("source notes:");
|
|
4434
|
+
for (const entry of payload.sourceStatus) {
|
|
4435
|
+
lines.push(` - ${formatSourceStatus(entry)}`);
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
return lines;
|
|
4439
|
+
}
|
|
4440
|
+
function formatSourceStatus(entry) {
|
|
4441
|
+
const parts = [`${entry.source} (${entry.targetLabel})`];
|
|
4442
|
+
if (entry.indexingStatus)
|
|
4443
|
+
parts.push(`indexing=${entry.indexingStatus}`);
|
|
4444
|
+
if (entry.codeIndexState)
|
|
4445
|
+
parts.push(`codeIndex=${entry.codeIndexState}`);
|
|
4446
|
+
if (entry.ignoredFilters?.length) {
|
|
4447
|
+
parts.push(`ignored=${entry.ignoredFilters.join(",")}`);
|
|
4448
|
+
}
|
|
4449
|
+
if (entry.incompatibleFilters?.length) {
|
|
4450
|
+
parts.push(`incompatible=${entry.incompatibleFilters.join(",")}`);
|
|
4451
|
+
}
|
|
4452
|
+
if (entry.ignoredQueryFeatures?.length) {
|
|
4453
|
+
parts.push(`ignoredQuery=${entry.ignoredQueryFeatures.join(",")}`);
|
|
4454
|
+
}
|
|
4455
|
+
if (entry.incompatibleQueryFeatures?.length) {
|
|
4456
|
+
parts.push(`incompatibleQuery=${entry.incompatibleQueryFeatures.join(",")}`);
|
|
4457
|
+
}
|
|
4458
|
+
if (entry.note)
|
|
4459
|
+
parts.push(entry.note);
|
|
4460
|
+
return parts.join(SEP3);
|
|
4461
|
+
}
|
|
4462
|
+
function quote3(value) {
|
|
4463
|
+
return value.includes('"') ? `'${value}'` : `"${value}"`;
|
|
4464
|
+
}
|
|
4465
|
+
function formatDetailValue(value) {
|
|
4466
|
+
if (value === null || value === undefined)
|
|
4467
|
+
return "";
|
|
4468
|
+
if (typeof value === "string")
|
|
4469
|
+
return value;
|
|
4470
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
4471
|
+
return String(value);
|
|
4472
|
+
return JSON.stringify(value);
|
|
4473
|
+
}
|
|
4474
|
+
function wrapText2(text, width) {
|
|
4475
|
+
const lines = [];
|
|
4476
|
+
for (const paragraph of text.split(/\n/)) {
|
|
4477
|
+
if (paragraph.length === 0) {
|
|
4478
|
+
lines.push("");
|
|
4479
|
+
continue;
|
|
4480
|
+
}
|
|
4481
|
+
let remaining = paragraph.trim();
|
|
4482
|
+
while (remaining.length > width) {
|
|
4483
|
+
let breakAt = remaining.lastIndexOf(" ", width);
|
|
4484
|
+
if (breakAt <= 0)
|
|
4485
|
+
breakAt = width;
|
|
4486
|
+
lines.push(remaining.slice(0, breakAt).trimEnd());
|
|
4487
|
+
remaining = remaining.slice(breakAt).trimStart();
|
|
4488
|
+
}
|
|
4489
|
+
if (remaining.length > 0)
|
|
4490
|
+
lines.push(remaining);
|
|
4491
|
+
}
|
|
4492
|
+
return lines;
|
|
4493
|
+
}
|
|
3584
4494
|
// src/shared/list-files-request.ts
|
|
3585
4495
|
var LIMIT_MIN2 = 1;
|
|
3586
4496
|
var LIMIT_MAX2 = 1000;
|
|
@@ -3858,6 +4768,9 @@ function parseIntCliOption(raw, name, min, max) {
|
|
|
3858
4768
|
return parsed;
|
|
3859
4769
|
}
|
|
3860
4770
|
function formatIndexingError(mapped) {
|
|
4771
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
4772
|
+
return formatMappedErrorForTerminal(mapped);
|
|
4773
|
+
}
|
|
3861
4774
|
if (mapped.code !== "INDEXING")
|
|
3862
4775
|
return mapped.message;
|
|
3863
4776
|
const detail = mapped.details ?? {};
|
|
@@ -3875,6 +4788,9 @@ function formatIndexingError(mapped) {
|
|
|
3875
4788
|
`);
|
|
3876
4789
|
}
|
|
3877
4790
|
function formatFileErrorWithFilesHint(mapped) {
|
|
4791
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
4792
|
+
return formatMappedErrorForTerminal(mapped);
|
|
4793
|
+
}
|
|
3878
4794
|
if (mapped.code === "FILE_NOT_FOUND") {
|
|
3879
4795
|
return `${mapped.message}
|
|
3880
4796
|
Use \`code files\` to list available paths.`;
|
|
@@ -4140,7 +5056,7 @@ grep run. --symbol-field hydrates enclosing symbol metadata (appears under each
|
|
|
4140
5056
|
match in --verbose output; full payload in --json).`;
|
|
4141
5057
|
function registerCodeGrepCommand(pkgCommand) {
|
|
4142
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) => {
|
|
4143
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
5059
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-1gqfte6e.js");
|
|
4144
5060
|
const deps = await createContainer2();
|
|
4145
5061
|
await pkgGrepAction(arg1, arg2, arg3, options, {
|
|
4146
5062
|
codeNavigationService: deps.codeNavigationService,
|
|
@@ -4172,9 +5088,7 @@ function buildReadFileParams(input) {
|
|
|
4172
5088
|
startLine,
|
|
4173
5089
|
endLine,
|
|
4174
5090
|
waitTimeoutMs
|
|
4175
|
-
}
|
|
4176
|
-
startLineExplicit: input.startLine !== undefined,
|
|
4177
|
-
endLineExplicit: input.endLine !== undefined
|
|
5091
|
+
}
|
|
4178
5092
|
};
|
|
4179
5093
|
}
|
|
4180
5094
|
function normaliseLine(raw, name) {
|
|
@@ -4238,7 +5152,7 @@ function formatReadFileTerminal(envelope, options) {
|
|
|
4238
5152
|
function formatBinary(envelope, options, verbose) {
|
|
4239
5153
|
const sentinel = dim("Binary file — cannot display as text.", options.useColors);
|
|
4240
5154
|
if (verbose) {
|
|
4241
|
-
return `${
|
|
5155
|
+
return `${buildHeader5(envelope, options)}
|
|
4242
5156
|
|
|
4243
5157
|
${sentinel}
|
|
4244
5158
|
`;
|
|
@@ -4249,7 +5163,7 @@ ${sentinel}
|
|
|
4249
5163
|
function formatNoContent(envelope, options, verbose) {
|
|
4250
5164
|
const sentinel = dim("(no content returned)", options.useColors);
|
|
4251
5165
|
if (verbose) {
|
|
4252
|
-
return `${
|
|
5166
|
+
return `${buildHeader5(envelope, options)}
|
|
4253
5167
|
|
|
4254
5168
|
${sentinel}
|
|
4255
5169
|
`;
|
|
@@ -4259,7 +5173,7 @@ ${sentinel}
|
|
|
4259
5173
|
}
|
|
4260
5174
|
function formatVerboseBody(envelope, options) {
|
|
4261
5175
|
const lines = [];
|
|
4262
|
-
lines.push(
|
|
5176
|
+
lines.push(buildHeader5(envelope, options));
|
|
4263
5177
|
lines.push("");
|
|
4264
5178
|
const content = envelope.content ?? "";
|
|
4265
5179
|
const bodyLines = content.split(`
|
|
@@ -4275,11 +5189,15 @@ function formatVerboseBody(envelope, options) {
|
|
|
4275
5189
|
const gutter = dim(String(lineNumber).padStart(gutterWidth, " "), options.useColors);
|
|
4276
5190
|
lines.push(`${gutter} ${bodyLines[i]}`);
|
|
4277
5191
|
}
|
|
5192
|
+
if (envelope.hint) {
|
|
5193
|
+
lines.push("");
|
|
5194
|
+
lines.push(dim(envelope.hint, options.useColors));
|
|
5195
|
+
}
|
|
4278
5196
|
lines.push("");
|
|
4279
5197
|
return lines.join(`
|
|
4280
5198
|
`);
|
|
4281
5199
|
}
|
|
4282
|
-
function
|
|
5200
|
+
function buildHeader5(envelope, options) {
|
|
4283
5201
|
const parts = [envelope.path];
|
|
4284
5202
|
if (envelope.language)
|
|
4285
5203
|
parts.push(envelope.language);
|
|
@@ -4522,7 +5440,7 @@ function handleDocsListError(error2, json) {
|
|
|
4522
5440
|
...mapped.details ? { details: mapped.details } : {}
|
|
4523
5441
|
}));
|
|
4524
5442
|
} else {
|
|
4525
|
-
console.error(mapped
|
|
5443
|
+
console.error(formatMappedErrorForTerminal(mapped));
|
|
4526
5444
|
}
|
|
4527
5445
|
process.exit(1);
|
|
4528
5446
|
}
|
|
@@ -4578,7 +5496,7 @@ function handleDocsReadError(error2, json) {
|
|
|
4578
5496
|
...mapped.details ? { details: mapped.details } : {}
|
|
4579
5497
|
}));
|
|
4580
5498
|
} else {
|
|
4581
|
-
console.error(mapped
|
|
5499
|
+
console.error(formatMappedErrorForTerminal(mapped));
|
|
4582
5500
|
}
|
|
4583
5501
|
process.exit(1);
|
|
4584
5502
|
}
|
|
@@ -4657,7 +5575,7 @@ function registerExampleCommand(program) {
|
|
|
4657
5575
|
});
|
|
4658
5576
|
}
|
|
4659
5577
|
async function loadContainer() {
|
|
4660
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
5578
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-1gqfte6e.js");
|
|
4661
5579
|
return createContainer2();
|
|
4662
5580
|
}
|
|
4663
5581
|
// src/commands/feedback.ts
|
|
@@ -5431,6 +6349,36 @@ var TIMEOUT_MS = 5 * 60 * 1000;
|
|
|
5431
6349
|
function randomPort() {
|
|
5432
6350
|
return Math.floor(Math.random() * 2000) + 8000;
|
|
5433
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
|
+
}
|
|
5434
6382
|
async function loginFlow(options, deps) {
|
|
5435
6383
|
const { authService, authStorage, browserService, mcpUrl } = deps;
|
|
5436
6384
|
if (options.port !== undefined && (Number.isNaN(options.port) || options.port < 1 || options.port > 65535)) {
|
|
@@ -5454,6 +6402,9 @@ async function loginFlow(options, deps) {
|
|
|
5454
6402
|
if (!existing) {
|
|
5455
6403
|
await authStorage.clearClient(mcpUrl);
|
|
5456
6404
|
}
|
|
6405
|
+
const persistenceError = await preflightAuthPersistence(authStorage, mcpUrl);
|
|
6406
|
+
if (persistenceError)
|
|
6407
|
+
return persistenceError;
|
|
5457
6408
|
console.log("Discovering OAuth endpoints...");
|
|
5458
6409
|
const metadata = await authService.discoverEndpoints(mcpUrl);
|
|
5459
6410
|
let client = await authStorage.loadClient(mcpUrl);
|
|
@@ -5599,13 +6550,15 @@ You're ready to use githits with your AI assistant.`);
|
|
|
5599
6550
|
var LOGIN_DESCRIPTION = `Authenticate with your GitHits account via browser.
|
|
5600
6551
|
|
|
5601
6552
|
Opens your browser to complete authentication securely using OAuth.
|
|
5602
|
-
|
|
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.
|
|
5603
6556
|
|
|
5604
6557
|
Use --no-browser in environments without a display (CI, SSH sessions)
|
|
5605
6558
|
to get a URL you can open on another device.`;
|
|
5606
6559
|
function registerLoginCommand(program) {
|
|
5607
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) => {
|
|
5608
|
-
const deps = await
|
|
6561
|
+
const deps = await createAuthCommandDependencies();
|
|
5609
6562
|
await loginAction(options, deps);
|
|
5610
6563
|
});
|
|
5611
6564
|
}
|
|
@@ -5800,7 +6753,7 @@ function registerInitCommand(program) {
|
|
|
5800
6753
|
fileSystemService,
|
|
5801
6754
|
promptService,
|
|
5802
6755
|
execService,
|
|
5803
|
-
createLoginDeps: () =>
|
|
6756
|
+
createLoginDeps: () => createAuthCommandDependencies()
|
|
5804
6757
|
});
|
|
5805
6758
|
});
|
|
5806
6759
|
}
|
|
@@ -5877,10 +6830,11 @@ var LOGOUT_DESCRIPTION = `Remove stored credentials.
|
|
|
5877
6830
|
|
|
5878
6831
|
Clears all locally stored authentication data including tokens and
|
|
5879
6832
|
client registrations. OAuth tokens expire naturally; this
|
|
5880
|
-
removes
|
|
6833
|
+
removes local copies from the keychain, explicit file storage, and
|
|
6834
|
+
legacy auth file storage.`;
|
|
5881
6835
|
function registerLogoutCommand(program) {
|
|
5882
6836
|
program.command("logout").summary("Remove stored credentials").description(LOGOUT_DESCRIPTION).action(async () => {
|
|
5883
|
-
const deps = await
|
|
6837
|
+
const deps = await createAuthCommandDependencies();
|
|
5884
6838
|
await logoutAction(deps);
|
|
5885
6839
|
});
|
|
5886
6840
|
}
|
|
@@ -6066,9 +7020,10 @@ var schema3 = {
|
|
|
6066
7020
|
max_matches_per_file: z4.number().optional(),
|
|
6067
7021
|
cursor: z4.string().optional(),
|
|
6068
7022
|
symbol_fields: z4.array(z4.enum(GREP_REPO_SYMBOL_FIELDS)).optional().describe(GREP_REPO_SYMBOL_FIELDS_NOTE),
|
|
6069
|
-
wait_timeout_ms: z4.number().optional()
|
|
7023
|
+
wait_timeout_ms: z4.number().optional(),
|
|
7024
|
+
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.')
|
|
6070
7025
|
};
|
|
6071
|
-
var DESCRIPTION3 = "Deterministic text grep over indexed dependency and repository source files. " + "Use this when you know the text pattern you want; use `search` for discovery. " + "Whole-target grep is the default
|
|
7026
|
+
var DESCRIPTION3 = "Deterministic text grep over indexed dependency and repository source files. " + "Use this when you know the text pattern you want; use `search` for discovery. " + "Whole-target grep is the default — narrow with `path`, `path_prefix`, `globs`, or `extensions` to keep responses small. " + 'Default response is a compact line-oriented listing (`format: "text-v1"`); pass `format: "json"` for the structured envelope. ' + "Matches chain directly into `code_read` (the `path` and `line` from each match feed straight into `start_line` / `end_line`).";
|
|
6072
7027
|
function createGrepRepoTool(service) {
|
|
6073
7028
|
return {
|
|
6074
7029
|
name: "code_grep",
|
|
@@ -6124,6 +7079,9 @@ function createGrepRepoTool(service) {
|
|
|
6124
7079
|
excludeTestFiles: build.params.excludeTestFiles,
|
|
6125
7080
|
explicit: build.explicit
|
|
6126
7081
|
});
|
|
7082
|
+
if (isTextFormat(args.format)) {
|
|
7083
|
+
return textResult(renderGrepRepoText(payload));
|
|
7084
|
+
}
|
|
6127
7085
|
return textResult(JSON.stringify(payload));
|
|
6128
7086
|
} catch (error2) {
|
|
6129
7087
|
const mapped = mapCodeNavigationError(error2);
|
|
@@ -6137,15 +7095,19 @@ function createGrepRepoTool(service) {
|
|
|
6137
7095
|
}
|
|
6138
7096
|
};
|
|
6139
7097
|
}
|
|
7098
|
+
function isTextFormat(format) {
|
|
7099
|
+
return format === undefined || format === "text" || format === "text-v1";
|
|
7100
|
+
}
|
|
6140
7101
|
// src/tools/list-files.ts
|
|
6141
7102
|
import { z as z5 } from "zod";
|
|
6142
7103
|
var schema4 = {
|
|
6143
7104
|
target: codeTargetSchema,
|
|
6144
7105
|
path_prefix: z5.string().optional().describe("Literal directory prefix to filter by (e.g. `src/` or `lib/parser`). NOT a glob — `*.ts` and similar patterns won't match. Omit to list from the repository root."),
|
|
6145
7106
|
limit: z5.number().optional().describe("Max entries to return (1–1000, default 200). Out-of-range values return an `INVALID_ARGUMENT` envelope."),
|
|
6146
|
-
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`.")
|
|
7107
|
+
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`."),
|
|
7108
|
+
format: z5.enum(["json", "text", "text-v1"]).optional().describe('Response format. Default `text-v1` — compact paths-only listing tuned for agent context efficiency. Pass `format: "json"` for the structured envelope. `text` is an alias for `text-v1`. Errors stay JSON-formatted in either mode for now.')
|
|
6147
7109
|
};
|
|
6148
|
-
var DESCRIPTION4 = "List files in an indexed dependency.
|
|
7110
|
+
var DESCRIPTION4 = "List files in an indexed dependency. Default response is a compact " + 'paths-only listing (`format: "text-v1"`); pass `format: "json"` ' + "for the structured envelope `{total, hasMore, files: [{path, name, " + "language, fileType, byteSize}], resolution, indexedVersion}`. " + "Address via `target.registry` + `target.package_name` (package " + "scope) or `target.repo_url` + `target.git_ref` (repo scope), " + "mutually exclusive. `path_prefix` is a literal directory prefix — " + "it does NOT accept globs (`*.ts`) or extension filters. The " + "returned paths feed directly into `code_read` and help scope " + "`code_grep`. Returns an `INDEXING` error envelope when the " + "dependency is being indexed on-demand — retry with a longer " + "`wait_timeout_ms` or use a version from `details.availableVersions`.";
|
|
6149
7111
|
function createListFilesTool(service) {
|
|
6150
7112
|
return {
|
|
6151
7113
|
name: "code_files",
|
|
@@ -6174,6 +7136,9 @@ function createListFilesTool(service) {
|
|
|
6174
7136
|
pathPrefix: build.params.pathPrefix,
|
|
6175
7137
|
limit: build.params.limit
|
|
6176
7138
|
});
|
|
7139
|
+
if (isTextFormat2(args.format)) {
|
|
7140
|
+
return textResult(renderListFilesText(payload));
|
|
7141
|
+
}
|
|
6177
7142
|
return textResult(JSON.stringify(payload));
|
|
6178
7143
|
} catch (error2) {
|
|
6179
7144
|
const mapped = mapCodeNavigationError(error2);
|
|
@@ -6187,6 +7152,9 @@ function createListFilesTool(service) {
|
|
|
6187
7152
|
}
|
|
6188
7153
|
};
|
|
6189
7154
|
}
|
|
7155
|
+
function isTextFormat2(format) {
|
|
7156
|
+
return format === undefined || format === "text" || format === "text-v1";
|
|
7157
|
+
}
|
|
6190
7158
|
// src/tools/list-package-docs.ts
|
|
6191
7159
|
import { z as z6 } from "zod";
|
|
6192
7160
|
var schema5 = {
|
|
@@ -6675,7 +7643,7 @@ var schema9 = {
|
|
|
6675
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."),
|
|
6676
7644
|
include_withdrawn: z10.boolean().optional().describe("Include retracted advisories (default: false).")
|
|
6677
7645
|
};
|
|
6678
|
-
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.";
|
|
6679
7647
|
function createPackageVulnerabilitiesTool(service) {
|
|
6680
7648
|
return {
|
|
6681
7649
|
name: "pkg_vulns",
|
|
@@ -6718,14 +7686,34 @@ function createPackageVulnerabilitiesTool(service) {
|
|
|
6718
7686
|
}
|
|
6719
7687
|
// src/tools/read-file.ts
|
|
6720
7688
|
import { z as z11 } from "zod";
|
|
7689
|
+
var MCP_READ_MAX_SPAN = 150;
|
|
6721
7690
|
var schema10 = {
|
|
6722
7691
|
target: codeTargetSchema,
|
|
6723
7692
|
path: z11.string().describe("Path to the file. Package addressing: package-relative. Repo addressing: repo-relative. This is the same `path` key that `code_files` emits for each entry, so chaining needs no renaming."),
|
|
6724
|
-
start_line: z11.number().optional().describe(
|
|
6725
|
-
end_line: z11.number().optional().describe(
|
|
7693
|
+
start_line: z11.number().optional().describe(`Starting line (1-indexed). Omit to start at line 1. The MCP surface caps any single read at ${MCP_READ_MAX_SPAN} lines — pick a focused window from your prior \`search\` / \`code_grep\` hit.`),
|
|
7694
|
+
end_line: z11.number().optional().describe(`Ending line (inclusive). Must be ≥ \`start_line\` when both are set. Omitting it implies \`start_line + ${MCP_READ_MAX_SPAN - 1}\` because the MCP surface caps each read at ${MCP_READ_MAX_SPAN} lines.`),
|
|
6726
7695
|
wait_timeout_ms: z11.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`.")
|
|
6727
7696
|
};
|
|
6728
|
-
var DESCRIPTION10 = "Read a file from an indexed dependency.
|
|
7697
|
+
var DESCRIPTION10 = "Read a file from an indexed dependency. **MCP cap: " + `${MCP_READ_MAX_SPAN} lines per call** — broader requests (or no ` + `range) silently truncate to the first ${MCP_READ_MAX_SPAN} lines ` + "from your start, with a `hint` describing what was returned vs. " + "requested. Pick a focused window from a `search` / `code_grep` " + "match. Response: `{path, language, totalLines, startLine, endLine, " + "content, isBinary, hint?}`. Binary files set `isBinary: true` and " + "omit `content`. Pass the same `path` emitted by `code_files`. " + "Address via `target.registry` + `target.package_name` (package " + "scope) or `target.repo_url` + `target.git_ref` (repo scope), " + "mutually exclusive. On `INDEXING` retry with a longer " + "`wait_timeout_ms`. On `NOT_FOUND` / `FILE_NOT_FOUND` call " + "`code_files` to discover the actual path.";
|
|
7698
|
+
function deriveBoundedRange(startLine, endLine) {
|
|
7699
|
+
const start = startLine ?? 1;
|
|
7700
|
+
if (endLine === undefined) {
|
|
7701
|
+
return {
|
|
7702
|
+
startLine: start,
|
|
7703
|
+
endLine: start + MCP_READ_MAX_SPAN - 1,
|
|
7704
|
+
capped: true
|
|
7705
|
+
};
|
|
7706
|
+
}
|
|
7707
|
+
const span = endLine - start + 1;
|
|
7708
|
+
if (span > MCP_READ_MAX_SPAN) {
|
|
7709
|
+
return {
|
|
7710
|
+
startLine: start,
|
|
7711
|
+
endLine: start + MCP_READ_MAX_SPAN - 1,
|
|
7712
|
+
capped: true
|
|
7713
|
+
};
|
|
7714
|
+
}
|
|
7715
|
+
return { startLine: start, endLine, capped: false };
|
|
7716
|
+
}
|
|
6729
7717
|
function createReadFileTool(service) {
|
|
6730
7718
|
return {
|
|
6731
7719
|
name: "code_read",
|
|
@@ -6737,11 +7725,12 @@ function createReadFileTool(service) {
|
|
|
6737
7725
|
if ("content" in target)
|
|
6738
7726
|
return target;
|
|
6739
7727
|
try {
|
|
7728
|
+
const bounded = deriveBoundedRange(args.start_line, args.end_line);
|
|
6740
7729
|
const build = buildReadFileParams({
|
|
6741
7730
|
target,
|
|
6742
7731
|
filePath: args.path,
|
|
6743
|
-
startLine:
|
|
6744
|
-
endLine:
|
|
7732
|
+
startLine: bounded.startLine,
|
|
7733
|
+
endLine: bounded.endLine,
|
|
6745
7734
|
waitTimeoutMs: args.wait_timeout_ms
|
|
6746
7735
|
});
|
|
6747
7736
|
const result = await service.readFile(build.params);
|
|
@@ -6752,6 +7741,9 @@ function createReadFileTool(service) {
|
|
|
6752
7741
|
gitRef: target.gitRef,
|
|
6753
7742
|
requestedFilePath: build.params.filePath
|
|
6754
7743
|
});
|
|
7744
|
+
if (shouldEmitCappedHint(bounded, payload)) {
|
|
7745
|
+
payload.hint = buildCappedHint(payload, args.start_line, args.end_line);
|
|
7746
|
+
}
|
|
6755
7747
|
return textResult(JSON.stringify(payload));
|
|
6756
7748
|
} catch (error2) {
|
|
6757
7749
|
const mapped = mapCodeNavigationError(error2);
|
|
@@ -6765,6 +7757,33 @@ function createReadFileTool(service) {
|
|
|
6765
7757
|
}
|
|
6766
7758
|
};
|
|
6767
7759
|
}
|
|
7760
|
+
function shouldEmitCappedHint(bounded, payload) {
|
|
7761
|
+
if (!bounded.capped)
|
|
7762
|
+
return false;
|
|
7763
|
+
if (payload.isBinary)
|
|
7764
|
+
return false;
|
|
7765
|
+
if (payload.endLine === undefined)
|
|
7766
|
+
return false;
|
|
7767
|
+
if (payload.totalLines === undefined)
|
|
7768
|
+
return false;
|
|
7769
|
+
return payload.endLine < payload.totalLines;
|
|
7770
|
+
}
|
|
7771
|
+
function buildCappedHint(payload, originalStart, originalEnd) {
|
|
7772
|
+
const requested = describeRequest(originalStart, originalEnd);
|
|
7773
|
+
return `Returned lines ${payload.startLine}-${payload.endLine}/${payload.totalLines} ` + `(MCP cap: ${MCP_READ_MAX_SPAN} lines per call; you requested ${requested}). ` + `Pick a focused start_line/end_line window — typical 80-150 lines around a search/code_grep match. ` + `Each retry also costs context, so aim for one well-sized read.`;
|
|
7774
|
+
}
|
|
7775
|
+
function describeRequest(originalStart, originalEnd) {
|
|
7776
|
+
if (originalStart === undefined && originalEnd === undefined) {
|
|
7777
|
+
return "no range";
|
|
7778
|
+
}
|
|
7779
|
+
if (originalEnd === undefined) {
|
|
7780
|
+
return `start_line=${originalStart}, no end_line`;
|
|
7781
|
+
}
|
|
7782
|
+
if (originalStart === undefined) {
|
|
7783
|
+
return `end_line=${originalEnd}, no start_line`;
|
|
7784
|
+
}
|
|
7785
|
+
return `lines ${originalStart}-${originalEnd}`;
|
|
7786
|
+
}
|
|
6768
7787
|
// src/tools/read-package-doc.ts
|
|
6769
7788
|
import { z as z12 } from "zod";
|
|
6770
7789
|
var schema11 = {
|
|
@@ -6851,9 +7870,10 @@ var schema12 = {
|
|
|
6851
7870
|
name: z13.string().optional(),
|
|
6852
7871
|
language: z13.string().optional(),
|
|
6853
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."),
|
|
6854
|
-
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)."),
|
|
6855
7874
|
offset: z13.coerce.number().int().min(0).optional(),
|
|
6856
|
-
wait_timeout_ms: z13.coerce.number().int().min(0).max(60000).optional()
|
|
7875
|
+
wait_timeout_ms: z13.coerce.number().int().min(0).max(60000).optional(),
|
|
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.')
|
|
6857
7877
|
};
|
|
6858
7878
|
var DESCRIPTION12 = "Search indexed dependency and repository code, docs, and explicit symbols. " + "Provide either `target` for one target or `targets` for many; omit `sources` to use backend AUTO. " + "The query field uses GitHits discovery syntax (AND/OR/parens/qualifiers; see the parameter description). " + "Structured parameters combine with that query using AND semantics. " + "Results are complete by default — if indexing is still running, the response carries a `searchRef` and no hits; pass it to `search_status` to follow up. " + "Set `allow_partial_results: true` to opt into hits from sources that finished while others continue indexing. " + "Each hit's `type` tells you the follow-up tool: `documentation_page` and `repository_doc` → `docs_read` with `locator.pageId`; `repository_code` and `repository_symbol` → `code_read` with `locator.filePath` (and `locator.startLine`/`endLine` when present).";
|
|
6859
7879
|
function createSearchTool(service) {
|
|
@@ -6891,9 +7911,16 @@ function createSearchTool(service) {
|
|
|
6891
7911
|
});
|
|
6892
7912
|
const outcome = await service.search(built.params);
|
|
6893
7913
|
const payload = buildUnifiedSearchSuccessPayload(built.params, built.rawQuery, built.compiledQuery, outcome);
|
|
7914
|
+
if (isTextFormat3(args.format)) {
|
|
7915
|
+
return textResult(renderUnifiedSearchSuccess(payload));
|
|
7916
|
+
}
|
|
6894
7917
|
return textResult(JSON.stringify(payload));
|
|
6895
7918
|
} catch (error2) {
|
|
6896
|
-
|
|
7919
|
+
const payload = buildUnifiedSearchErrorPayload(error2);
|
|
7920
|
+
if (isTextFormat3(args.format)) {
|
|
7921
|
+
return errorResult(renderUnifiedSearchError(payload));
|
|
7922
|
+
}
|
|
7923
|
+
return errorResult(JSON.stringify(payload));
|
|
6897
7924
|
}
|
|
6898
7925
|
}
|
|
6899
7926
|
};
|
|
@@ -6901,6 +7928,9 @@ function createSearchTool(service) {
|
|
|
6901
7928
|
function isResolvedCodeTarget(target) {
|
|
6902
7929
|
return !("content" in target);
|
|
6903
7930
|
}
|
|
7931
|
+
function isTextFormat3(format) {
|
|
7932
|
+
return format === undefined || format === "text" || format === "text-v1";
|
|
7933
|
+
}
|
|
6904
7934
|
// src/tools/search-language.ts
|
|
6905
7935
|
import { z as z14 } from "zod";
|
|
6906
7936
|
var schema13 = {
|
|
@@ -6945,60 +7975,46 @@ function createSearchStatusTool(service) {
|
|
|
6945
7975
|
};
|
|
6946
7976
|
}
|
|
6947
7977
|
// src/commands/mcp-instructions.ts
|
|
6948
|
-
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.
|
|
6949
7979
|
|
|
6950
|
-
|
|
6951
|
-
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.
|
|
6952
7982
|
|
|
6953
7983
|
Package spec: \`registry:name[@version]\`.`;
|
|
6954
7984
|
var PKG_INFO_BULLET = "- `pkg_info` — instant package overview: latest version, license, downloads, quickstart, and active advisory count.";
|
|
6955
|
-
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.";
|
|
6956
7986
|
var DOCS_READ_BULLET = "- `docs_read` — read a documentation page by pageId. Works for both hosted docs and repo-backed docs.";
|
|
6957
|
-
var PKG_VULNS_BULLET = "- `pkg_vulns` — known CVE / OSV advisories for npm, PyPI, Hex, or Crates packages
|
|
6958
|
-
var PKG_DEPS_BULLET = "- `pkg_deps` — direct runtime deps plus
|
|
6959
|
-
var PKG_CHANGELOG_BULLET = "- `pkg_changelog` — release notes for a package or GitHub repo, newest-first. Default latest mode returns
|
|
6960
|
-
var SEARCH_BULLET =
|
|
6961
|
-
var SEARCH_STATUS_BULLET = "- `search_status` — follow up a prior
|
|
6962
|
-
var CODE_FILES_BULLET =
|
|
6963
|
-
var CODE_READ_BULLET = "- `code_read` —
|
|
6964
|
-
var CODE_GREP_BULLET = "- `code_grep` — deterministic text grep over indexed source
|
|
6965
|
-
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
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
function buildMcpInstructions(deps) {
|
|
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`.";
|
|
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.';
|
|
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) {
|
|
6970
7999
|
const sections = [CORE_BLOCK];
|
|
6971
|
-
if (!isPackageToolsCapabilityOpen(deps)) {
|
|
6972
|
-
return sections.join(`
|
|
6973
|
-
|
|
6974
|
-
`);
|
|
6975
|
-
}
|
|
6976
8000
|
const bullets = [];
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
`);
|
|
6996
|
-
}
|
|
6997
|
-
const parts = [PACKAGE_TOOLS_PREAMBLE, bullets.join(`
|
|
6998
|
-
`)];
|
|
6999
|
-
if (deps.codeNavigationService) {
|
|
7000
|
-
parts.push(SEARCH_VS_SYMBOLS_TIP);
|
|
7001
|
-
}
|
|
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);
|
|
8012
|
+
const parts = [PACKAGE_TOOLS_PREAMBLE];
|
|
8013
|
+
parts.push(MULTI_TURN_TIP);
|
|
8014
|
+
parts.push(bullets.join(`
|
|
8015
|
+
`));
|
|
8016
|
+
parts.push(REFERENCE_FIRST_TIP);
|
|
8017
|
+
parts.push(SEARCH_VS_SYMBOLS_TIP);
|
|
7002
8018
|
sections.push(parts.join(`
|
|
7003
8019
|
|
|
7004
8020
|
`));
|
|
@@ -7014,22 +8030,17 @@ function getMcpToolDefinitions(deps) {
|
|
|
7014
8030
|
eraseTool(createSearchLanguageTool(deps.githitsService)),
|
|
7015
8031
|
eraseTool(createFeedbackTool(deps.githitsService))
|
|
7016
8032
|
];
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
tools.push(eraseTool(createPackageSummaryTool(deps.packageIntelligenceService)));
|
|
7029
|
-
tools.push(eraseTool(createPackageVulnerabilitiesTool(deps.packageIntelligenceService)));
|
|
7030
|
-
tools.push(eraseTool(createPackageDependenciesTool(deps.packageIntelligenceService)));
|
|
7031
|
-
tools.push(eraseTool(createPackageChangelogTool(deps.packageIntelligenceService)));
|
|
7032
|
-
}
|
|
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)));
|
|
7033
8044
|
return tools;
|
|
7034
8045
|
}
|
|
7035
8046
|
function eraseTool(tool) {
|
|
@@ -7097,7 +8108,7 @@ function registerMcpCommand(program) {
|
|
|
7097
8108
|
When run interactively (TTY), shows setup instructions.
|
|
7098
8109
|
When run via stdio (non-TTY), starts the MCP server.
|
|
7099
8110
|
|
|
7100
|
-
|
|
8111
|
+
Authenticated tool calls require a valid GitHits token.`).action(async () => {
|
|
7101
8112
|
if (process.stdout.isTTY && process.stdin.isTTY) {
|
|
7102
8113
|
showMcpSetupInstructions();
|
|
7103
8114
|
return;
|
|
@@ -7186,6 +8197,9 @@ function handlePkgChangelogCommandError(error2, json) {
|
|
|
7186
8197
|
process.exit(1);
|
|
7187
8198
|
}
|
|
7188
8199
|
function formatChangelogTerminalError(mapped) {
|
|
8200
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
8201
|
+
return formatMappedErrorForTerminal(mapped);
|
|
8202
|
+
}
|
|
7189
8203
|
if (mapped.code !== "VERSION_NOT_FOUND")
|
|
7190
8204
|
return mapped.message;
|
|
7191
8205
|
const detail = mapped.details ?? {};
|
|
@@ -7309,6 +8323,9 @@ function handlePkgDepsCommandError(error2, json) {
|
|
|
7309
8323
|
process.exit(1);
|
|
7310
8324
|
}
|
|
7311
8325
|
function formatDepsTerminalError(mapped) {
|
|
8326
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
8327
|
+
return formatMappedErrorForTerminal(mapped);
|
|
8328
|
+
}
|
|
7312
8329
|
if (mapped.code !== "VERSION_NOT_FOUND")
|
|
7313
8330
|
return mapped.message;
|
|
7314
8331
|
const detail = mapped.details ?? {};
|
|
@@ -7395,7 +8412,7 @@ function handlePkgInfoCommandError(error2, json) {
|
|
|
7395
8412
|
}));
|
|
7396
8413
|
process.exit(1);
|
|
7397
8414
|
}
|
|
7398
|
-
console.error(mapped
|
|
8415
|
+
console.error(formatMappedErrorForTerminal(mapped));
|
|
7399
8416
|
process.exit(1);
|
|
7400
8417
|
}
|
|
7401
8418
|
var PKG_INFO_DESCRIPTION = `Get a package overview — latest version, license, description,
|
|
@@ -7468,6 +8485,9 @@ function handlePkgVulnsCommandError(error2, json) {
|
|
|
7468
8485
|
process.exit(1);
|
|
7469
8486
|
}
|
|
7470
8487
|
function formatVulnsTerminalError(mapped) {
|
|
8488
|
+
if (mapped.code === "UPDATE_REQUIRED") {
|
|
8489
|
+
return formatMappedErrorForTerminal(mapped);
|
|
8490
|
+
}
|
|
7471
8491
|
if (mapped.code !== "VERSION_NOT_FOUND")
|
|
7472
8492
|
return mapped.message;
|
|
7473
8493
|
const detail = mapped.details ?? {};
|
|
@@ -7492,8 +8512,8 @@ function formatVulnsTerminalError(mapped) {
|
|
|
7492
8512
|
`);
|
|
7493
8513
|
}
|
|
7494
8514
|
var PKG_VULNS_DESCRIPTION = `Show known vulnerabilities for a package. Lists CVE / OSV advisories
|
|
7495
|
-
with severity, affected version ranges, fix versions
|
|
7496
|
-
|
|
8515
|
+
with severity, affected version ranges, and fix versions.
|
|
8516
|
+
Malicious-package advisories are flagged prominently.
|
|
7497
8517
|
|
|
7498
8518
|
Package spec: <registry>:<name>[@<version>]. Supported registries:
|
|
7499
8519
|
npm, pypi, hex, crates. Omit @<version> to check the latest release.
|
|
@@ -7620,7 +8640,7 @@ function registerSearchCommand(program) {
|
|
|
7620
8640
|
"fixture",
|
|
7621
8641
|
"build",
|
|
7622
8642
|
"vendor"
|
|
7623
|
-
])).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) => {
|
|
7624
8644
|
const deps = await loadContainer2();
|
|
7625
8645
|
await searchAction(query, options, deps);
|
|
7626
8646
|
});
|
|
@@ -7634,14 +8654,6 @@ async function registerUnifiedSearchCommands(program, options = {}) {
|
|
|
7634
8654
|
if (!codeNavigationUrl) {
|
|
7635
8655
|
return;
|
|
7636
8656
|
}
|
|
7637
|
-
const overrideEnabled = options.overrideEnabled ?? isCodeNavigationCliOverrideEnabled();
|
|
7638
|
-
const registrationState = options.capability !== undefined || options.expiredStoredAuth !== undefined ? {
|
|
7639
|
-
capability: options.capability ?? "unknown",
|
|
7640
|
-
expiredStoredAuth: options.expiredStoredAuth ?? false
|
|
7641
|
-
} : await loadStartupCodeNavigationRegistrationState();
|
|
7642
|
-
if (!overrideEnabled && registrationState.capability !== "enabled" && !registrationState.expiredStoredAuth) {
|
|
7643
|
-
return;
|
|
7644
|
-
}
|
|
7645
8657
|
registerSearchCommand(program);
|
|
7646
8658
|
}
|
|
7647
8659
|
function requireSearchService(deps) {
|
|
@@ -7651,13 +8663,9 @@ function requireSearchService(deps) {
|
|
|
7651
8663
|
return deps.codeNavigationService;
|
|
7652
8664
|
}
|
|
7653
8665
|
async function loadContainer2() {
|
|
7654
|
-
const { createContainer: createContainer2 } = await import("./shared/chunk-
|
|
8666
|
+
const { createContainer: createContainer2 } = await import("./shared/chunk-1gqfte6e.js");
|
|
7655
8667
|
return createContainer2();
|
|
7656
8668
|
}
|
|
7657
|
-
async function loadStartupCodeNavigationRegistrationState() {
|
|
7658
|
-
const { resolveStartupCodeNavigationRegistrationState: resolveStartupCodeNavigationRegistrationState2 } = await import("./shared/chunk-4drb8s8v.js");
|
|
7659
|
-
return resolveStartupCodeNavigationRegistrationState2();
|
|
7660
|
-
}
|
|
7661
8669
|
function parseTargetSpecs(specs) {
|
|
7662
8670
|
if (!specs || specs.length === 0) {
|
|
7663
8671
|
throw new InvalidArgumentError("Provide at least one --in target.");
|
|
@@ -7990,7 +8998,32 @@ function formatUnifiedSearchMetadata(entry, useColors) {
|
|
|
7990
8998
|
}
|
|
7991
8999
|
// src/cli.ts
|
|
7992
9000
|
var program = new Command;
|
|
9001
|
+
var argv = process.argv.slice(2);
|
|
7993
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
|
+
});
|
|
7994
9027
|
if (isTelemetryEnabled()) {
|
|
7995
9028
|
process.once("exit", (exitCode) => {
|
|
7996
9029
|
flushTelemetry(exitCode);
|
|
@@ -8021,25 +9054,34 @@ registerMcpCommand(program);
|
|
|
8021
9054
|
registerExampleCommand(program);
|
|
8022
9055
|
registerLanguagesCommand(program);
|
|
8023
9056
|
registerFeedbackCommand(program);
|
|
8024
|
-
var argv = process.argv.slice(2);
|
|
8025
9057
|
var registrationArgv = stripRootRegistrationOptions(argv);
|
|
8026
|
-
var shouldLoadGatedHelpRegistration = needsGatedHelpRegistration(registrationArgv);
|
|
8027
|
-
var helpRegistrationOptions = shouldLoadGatedHelpRegistration ? await loadHelpRegistrationOptions(registrationArgv) : undefined;
|
|
8028
9058
|
if (shouldEagerLoadSearchCommands(registrationArgv)) {
|
|
8029
|
-
await withTelemetrySpan("cli.register.search", () => registerUnifiedSearchCommands(program
|
|
9059
|
+
await withTelemetrySpan("cli.register.search", () => registerUnifiedSearchCommands(program));
|
|
8030
9060
|
}
|
|
8031
9061
|
if (shouldEagerLoadGatedCommandGroup(registrationArgv, "code")) {
|
|
8032
|
-
await withTelemetrySpan("cli.register.code-group", () => registerCodeCommandGroup(program
|
|
9062
|
+
await withTelemetrySpan("cli.register.code-group", () => registerCodeCommandGroup(program));
|
|
8033
9063
|
}
|
|
8034
9064
|
if (shouldEagerLoadGatedCommandGroup(registrationArgv, "pkg")) {
|
|
8035
|
-
await withTelemetrySpan("cli.register.pkg-group", () => registerPkgCommandGroup(program
|
|
9065
|
+
await withTelemetrySpan("cli.register.pkg-group", () => registerPkgCommandGroup(program));
|
|
8036
9066
|
}
|
|
8037
9067
|
if (shouldEagerLoadGatedCommandGroup(registrationArgv, "docs")) {
|
|
8038
|
-
await withTelemetrySpan("cli.register.docs-group", () => registerDocsCommandGroup(program
|
|
9068
|
+
await withTelemetrySpan("cli.register.docs-group", () => registerDocsCommandGroup(program));
|
|
8039
9069
|
}
|
|
8040
9070
|
var authCommand = program.command("auth").summary("Manage authentication").description("Manage authentication with GitHits.");
|
|
8041
9071
|
registerAuthStatusCommand(authCommand);
|
|
8042
|
-
|
|
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
|
+
}
|
|
8043
9085
|
function stripRootRegistrationOptions(args) {
|
|
8044
9086
|
return args.filter((arg) => arg !== "--no-color");
|
|
8045
9087
|
}
|
|
@@ -8051,34 +9093,9 @@ function shouldEagerLoadSearchCommands(args) {
|
|
|
8051
9093
|
const [firstArg] = args;
|
|
8052
9094
|
return args.length === 0 || firstArg === "search" || firstArg === "search-status" || firstArg === "--help" || firstArg === "-h" || firstArg === "help" && (!args[1] || isSearchHelpTarget(args[1]));
|
|
8053
9095
|
}
|
|
8054
|
-
function isHelpInvocation(args) {
|
|
8055
|
-
return args.length === 0 || args[0] === "help" || args.includes("--help") || args.includes("-h");
|
|
8056
|
-
}
|
|
8057
|
-
function needsGatedHelpRegistration(args) {
|
|
8058
|
-
if (!isHelpInvocation(args)) {
|
|
8059
|
-
return false;
|
|
8060
|
-
}
|
|
8061
|
-
const [firstArg, secondArg] = args;
|
|
8062
|
-
if (firstArg === "help") {
|
|
8063
|
-
return isSearchHelpTarget(secondArg) || secondArg === "code" || secondArg === "pkg" || secondArg === "docs";
|
|
8064
|
-
}
|
|
8065
|
-
return isSearchHelpTarget(firstArg) || firstArg === "code" || firstArg === "pkg" || firstArg === "docs";
|
|
8066
|
-
}
|
|
8067
9096
|
function isSearchHelpTarget(value) {
|
|
8068
9097
|
return value === "search" || value === "search-status";
|
|
8069
9098
|
}
|
|
8070
|
-
async function loadHelpRegistrationOptions(args) {
|
|
8071
|
-
const { resolveStartupCodeNavigationRegistrationState: resolveStartupCodeNavigationRegistrationState2 } = await import("./shared/chunk-4drb8s8v.js");
|
|
8072
|
-
const registrationState = await resolveStartupCodeNavigationRegistrationState2();
|
|
8073
|
-
return {
|
|
8074
|
-
capability: registrationState.capability,
|
|
8075
|
-
expiredStoredAuth: shouldUseExpiredStoredAuthFallbackForHelp(args) ? registrationState.expiredStoredAuth : false
|
|
8076
|
-
};
|
|
8077
|
-
}
|
|
8078
|
-
function shouldUseExpiredStoredAuthFallbackForHelp(args) {
|
|
8079
|
-
const [firstArg, secondArg] = args;
|
|
8080
|
-
return firstArg === "search" || firstArg === "search-status" || firstArg === "code" || firstArg === "pkg" || firstArg === "docs" || firstArg === "help" && (isSearchHelpTarget(secondArg) || secondArg === "code" || secondArg === "pkg" || secondArg === "docs");
|
|
8081
|
-
}
|
|
8082
9099
|
function getTelemetryCommandName(command) {
|
|
8083
9100
|
const names = [];
|
|
8084
9101
|
let current = command;
|