githits 0.2.3 → 0.3.0

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