githits 0.1.9 → 0.1.11

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,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  version
4
- } from "./shared/chunk-ey1zffpv.js";
4
+ } from "./shared/chunk-hyfjv9fg.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -616,6 +616,158 @@ function isAreaEnabled(area) {
616
616
  return scopes.includes(area) || scopes.includes("*");
617
617
  }
618
618
 
619
+ // src/shared/request-headers.ts
620
+ import { createHash as createHash2, randomUUID } from "node:crypto";
621
+ var MAX_HEADER_BYTES = 256;
622
+ var SESSION_ENV_VARS = [
623
+ "TERM_SESSION_ID",
624
+ "ITERM_SESSION_ID",
625
+ "WEZTERM_PANE",
626
+ "KITTY_PID",
627
+ "ALACRITTY_SOCKET",
628
+ "WT_SESSION",
629
+ "VSCODE_PID",
630
+ "SUPERSET_PANE_ID",
631
+ "SUPERSET_WORKSPACE_ID",
632
+ "STARSHIP_SESSION_KEY",
633
+ "SSH_CONNECTION"
634
+ ];
635
+ var cachedSessionId;
636
+ function resolveRawSessionId(env = process.env, ppid = process.ppid) {
637
+ for (const key of SESSION_ENV_VARS) {
638
+ const value = env[key];
639
+ if (value && value.trim().length > 0) {
640
+ return value.trim();
641
+ }
642
+ }
643
+ if (typeof ppid === "number" && !Number.isNaN(ppid) && ppid > 0) {
644
+ return String(ppid);
645
+ }
646
+ return randomUUID();
647
+ }
648
+ function getSessionId(env, ppid) {
649
+ if (cachedSessionId !== undefined && env === undefined && ppid === undefined) {
650
+ return cachedSessionId;
651
+ }
652
+ const raw = resolveRawSessionId(env, ppid);
653
+ const hashed = hashValue(raw);
654
+ if (env === undefined && ppid === undefined) {
655
+ cachedSessionId = hashed;
656
+ }
657
+ return hashed;
658
+ }
659
+ function hashValue(input) {
660
+ return createHash2("sha256").update(input).digest("hex").slice(0, 16);
661
+ }
662
+ var AGENT_PROBES = [
663
+ { envVar: "OPENCODE", name: "opencode" },
664
+ { envVar: "CLAUDECODE", name: "claude-code" },
665
+ { envVar: "CURSOR_TRACE_ID", name: "cursor" },
666
+ { envVar: "WINDSURF_CONFIG_DIR", name: "windsurf" },
667
+ { envVar: "ZED_TERM", name: "zed" },
668
+ { envVar: "VSCODE_PID", name: "vscode" }
669
+ ];
670
+ function parseAgentString(raw) {
671
+ const trimmed = raw.trim();
672
+ if (trimmed.length === 0)
673
+ return;
674
+ const slashIndex = trimmed.indexOf("/");
675
+ if (slashIndex === -1)
676
+ return { name: trimmed };
677
+ const name = trimmed.slice(0, slashIndex);
678
+ const ver = trimmed.slice(slashIndex + 1);
679
+ if (name.length === 0)
680
+ return;
681
+ return { name, version: ver || undefined };
682
+ }
683
+ function formatAgentInfo(info) {
684
+ return info.version ? `${info.name}/${info.version}` : info.name;
685
+ }
686
+ function resolveAgentInfo(env = process.env) {
687
+ const explicit = env.GITHITS_AGENT;
688
+ if (explicit && explicit.trim().length > 0) {
689
+ return parseAgentString(explicit);
690
+ }
691
+ for (const probe of AGENT_PROBES) {
692
+ const value = env[probe.envVar];
693
+ if (value && value.trim().length > 0) {
694
+ return { name: probe.name };
695
+ }
696
+ }
697
+ return;
698
+ }
699
+ var currentAgentInfo;
700
+ var agentInfoInitialized = false;
701
+ var agentInfoExplicitlySet = false;
702
+ var mcpClientVersionProvider;
703
+ function setMcpClientVersionProvider(provider) {
704
+ mcpClientVersionProvider = provider;
705
+ }
706
+ function getAgentInfo(env) {
707
+ if (agentInfoExplicitlySet) {
708
+ return currentAgentInfo;
709
+ }
710
+ if (mcpClientVersionProvider) {
711
+ try {
712
+ const fromProvider = mcpClientVersionProvider();
713
+ if (fromProvider && fromProvider.name.trim().length > 0) {
714
+ return fromProvider;
715
+ }
716
+ } catch {}
717
+ }
718
+ if (!agentInfoInitialized || env !== undefined) {
719
+ currentAgentInfo = resolveAgentInfo(env);
720
+ if (env === undefined) {
721
+ agentInfoInitialized = true;
722
+ }
723
+ }
724
+ return currentAgentInfo;
725
+ }
726
+ var CONTROL_CHARS = /[\x00-\x1f\x7f-\x9f]/g;
727
+ function sanitizeHeaderValue(value) {
728
+ if (value === undefined || value === null || typeof value !== "string") {
729
+ return;
730
+ }
731
+ const cleaned = value.replace(CONTROL_CHARS, "").trim();
732
+ if (cleaned.length === 0)
733
+ return;
734
+ if (Buffer.byteLength(cleaned, "utf8") > MAX_HEADER_BYTES)
735
+ return;
736
+ return cleaned;
737
+ }
738
+ var BASE_CLIENT_NAME = "githits-cli";
739
+ var clientName = BASE_CLIENT_NAME;
740
+ function setClientMode(mode) {
741
+ clientName = `${BASE_CLIENT_NAME}/${mode}`;
742
+ }
743
+ function buildClientHeaders(env, ppid) {
744
+ try {
745
+ const headers = {};
746
+ const name = sanitizeHeaderValue(clientName);
747
+ if (name) {
748
+ headers["x-githits-client-name"] = name;
749
+ }
750
+ const clientVersion = sanitizeHeaderValue(version);
751
+ if (clientVersion) {
752
+ headers["x-githits-client-version"] = clientVersion;
753
+ }
754
+ const agentInfo = getAgentInfo(env);
755
+ if (agentInfo) {
756
+ const agentValue = sanitizeHeaderValue(formatAgentInfo(agentInfo));
757
+ if (agentValue) {
758
+ headers["x-githits-agent"] = agentValue;
759
+ }
760
+ }
761
+ const sessionId = sanitizeHeaderValue(getSessionId(env, ppid));
762
+ if (sessionId) {
763
+ headers["x-githits-session-id"] = sessionId;
764
+ }
765
+ return headers;
766
+ } catch {
767
+ return {};
768
+ }
769
+ }
770
+
619
771
  // src/shared/pkgseer-graphql.ts
620
772
  class PkgseerTransportError extends Error {
621
773
  constructor(message, options) {
@@ -634,6 +786,7 @@ async function postPkgseerGraphql(request) {
634
786
  response = await fetchFn(`${baseUrl(request.endpointUrl)}/api/graphql`, {
635
787
  method: "POST",
636
788
  headers: {
789
+ ...buildClientHeaders(),
637
790
  Authorization: `Bearer ${request.token}`,
638
791
  "Content-Type": "application/json",
639
792
  "User-Agent": userAgent
@@ -669,6 +822,157 @@ function parseJsonOrNull(body) {
669
822
  }
670
823
  }
671
824
 
825
+ // src/shared/telemetry.ts
826
+ import { writeSync } from "node:fs";
827
+ var ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
828
+ function isTelemetryEnabled(env = process.env) {
829
+ const raw = env.GITHITS_TELEMETRY?.trim().toLowerCase();
830
+ if (!raw)
831
+ return false;
832
+ return ENABLED_VALUES.has(raw);
833
+ }
834
+
835
+ class TelemetryCollector {
836
+ enabled;
837
+ now;
838
+ write;
839
+ sessionStartMs;
840
+ spans = [];
841
+ activeSpans = new Map;
842
+ nextId = 1;
843
+ flushed = false;
844
+ constructor(options = {}) {
845
+ this.enabled = isTelemetryEnabled(options.env);
846
+ this.now = options.now ?? (() => globalThis.performance.now());
847
+ this.write = options.write ?? ((text) => writeSync(process.stderr.fd, text));
848
+ this.sessionStartMs = this.now();
849
+ }
850
+ isEnabled() {
851
+ return this.enabled;
852
+ }
853
+ startSpan(name, attributes) {
854
+ if (!this.enabled)
855
+ return;
856
+ const span = {
857
+ id: this.nextId++,
858
+ name,
859
+ startMs: this.now(),
860
+ attributes: sanitiseAttributes(attributes)
861
+ };
862
+ this.spans.push(span);
863
+ this.activeSpans.set(span.id, span);
864
+ return { id: span.id };
865
+ }
866
+ endSpan(handle, attributes) {
867
+ if (!this.enabled || !handle)
868
+ return;
869
+ const span = this.activeSpans.get(handle.id);
870
+ if (!span || span.endMs !== undefined)
871
+ return;
872
+ span.endMs = this.now();
873
+ span.attributes = mergeAttributes(span.attributes, attributes);
874
+ this.activeSpans.delete(handle.id);
875
+ }
876
+ flush(exitCode = 0) {
877
+ if (!this.enabled || this.flushed)
878
+ return;
879
+ const nowMs = this.now();
880
+ for (const span of this.activeSpans.values()) {
881
+ if (span.endMs !== undefined)
882
+ continue;
883
+ span.endMs = nowMs;
884
+ span.endedAtExit = true;
885
+ }
886
+ this.activeSpans.clear();
887
+ this.write(formatTelemetryReport(this.spans, this.sessionStartMs, nowMs, exitCode));
888
+ this.flushed = true;
889
+ }
890
+ }
891
+ async function withTelemetrySpan(name, operation, attributes) {
892
+ const handle = telemetryCollector.startSpan(name, attributes);
893
+ try {
894
+ const result = await operation();
895
+ telemetryCollector.endSpan(handle);
896
+ return result;
897
+ } catch (error) {
898
+ telemetryCollector.endSpan(handle, { error: true });
899
+ throw error;
900
+ }
901
+ }
902
+ function withTelemetrySpanSync(name, operation, attributes) {
903
+ const handle = telemetryCollector.startSpan(name, attributes);
904
+ try {
905
+ const result = operation();
906
+ telemetryCollector.endSpan(handle);
907
+ return result;
908
+ } catch (error) {
909
+ telemetryCollector.endSpan(handle, { error: true });
910
+ throw error;
911
+ }
912
+ }
913
+ function startTelemetrySpan(name, attributes) {
914
+ return telemetryCollector.startSpan(name, attributes);
915
+ }
916
+ function endTelemetrySpan(handle, attributes) {
917
+ telemetryCollector.endSpan(handle, attributes);
918
+ }
919
+ function flushTelemetry(exitCode = 0) {
920
+ telemetryCollector.flush(exitCode);
921
+ }
922
+ var telemetryCollector = new TelemetryCollector;
923
+ function sanitiseAttributes(attributes) {
924
+ if (!attributes)
925
+ return;
926
+ const entries = Object.entries(attributes).filter(([, value]) => value !== undefined);
927
+ if (entries.length === 0)
928
+ return;
929
+ return Object.fromEntries(entries);
930
+ }
931
+ function mergeAttributes(initial, extra) {
932
+ if (!initial && !extra)
933
+ return;
934
+ return sanitiseAttributes({
935
+ ...initial ?? {},
936
+ ...extra ?? {}
937
+ });
938
+ }
939
+ function formatTelemetryReport(spans, sessionStartMs, sessionEndMs, exitCode) {
940
+ const lines = [
941
+ "[githits telemetry]",
942
+ `exit: ${exitCode}`,
943
+ `total: ${formatMs(sessionEndMs - sessionStartMs)}`
944
+ ];
945
+ const orderedSpans = [...spans].sort((left, right) => {
946
+ if (left.startMs !== right.startMs) {
947
+ return left.startMs - right.startMs;
948
+ }
949
+ return left.id - right.id;
950
+ });
951
+ for (const span of orderedSpans) {
952
+ const endMs = span.endMs ?? sessionEndMs;
953
+ const details = [`start +${formatMs(span.startMs - sessionStartMs)}`];
954
+ if (span.endedAtExit) {
955
+ details.push("ended-at-exit");
956
+ }
957
+ const attrs = formatAttributes(span.attributes);
958
+ if (attrs) {
959
+ details.push(attrs);
960
+ }
961
+ lines.push(`- ${span.name}: ${formatMs(endMs - span.startMs)} (${details.join(", ")})`);
962
+ }
963
+ return `${lines.join(`
964
+ `)}
965
+ `;
966
+ }
967
+ function formatAttributes(attributes) {
968
+ if (!attributes)
969
+ return "";
970
+ return Object.entries(attributes).map(([key, value]) => `${key}=${String(value)}`).join(" ");
971
+ }
972
+ function formatMs(value) {
973
+ return `${value.toFixed(1)}ms`;
974
+ }
975
+
672
976
  // src/services/githits-service.ts
673
977
  class AuthenticationError extends Error {
674
978
  constructor(message) {
@@ -697,47 +1001,54 @@ class GitHitsServiceImpl {
697
1001
  this.token = token;
698
1002
  }
699
1003
  async search(params) {
700
- const response = await fetch(`${this.apiUrl}/search`, {
701
- method: "POST",
702
- headers: this.headers(),
703
- body: JSON.stringify({
704
- query: params.query,
705
- language: params.language,
706
- license_mode: params.licenseMode ?? "strict",
707
- include_explanation: params.includeExplanation ?? false
708
- })
1004
+ return withTelemetrySpan("githits.search.request", async () => {
1005
+ const response = await fetch(`${this.apiUrl}/search`, {
1006
+ method: "POST",
1007
+ headers: this.headers(),
1008
+ body: JSON.stringify({
1009
+ query: params.query,
1010
+ language: params.language,
1011
+ license_mode: params.licenseMode ?? "strict",
1012
+ include_explanation: params.includeExplanation ?? false
1013
+ })
1014
+ });
1015
+ if (!response.ok) {
1016
+ throw await this.createError(response);
1017
+ }
1018
+ return response.text();
709
1019
  });
710
- if (!response.ok) {
711
- throw await this.createError(response);
712
- }
713
- return response.text();
714
1020
  }
715
1021
  async getLanguages() {
716
- const response = await fetch(`${this.apiUrl}/languages`, {
717
- headers: this.headers()
1022
+ return withTelemetrySpan("githits.languages.request", async () => {
1023
+ const response = await fetch(`${this.apiUrl}/languages`, {
1024
+ headers: this.headers()
1025
+ });
1026
+ if (!response.ok) {
1027
+ throw await this.createError(response);
1028
+ }
1029
+ return response.json();
718
1030
  });
719
- if (!response.ok) {
720
- throw await this.createError(response);
721
- }
722
- return response.json();
723
1031
  }
724
1032
  async submitFeedback(params) {
725
- const response = await fetch(`${this.apiUrl}/feedbacks`, {
726
- method: "POST",
727
- headers: this.headers(),
728
- body: JSON.stringify({
729
- solution_id: params.solutionId,
730
- accepted: params.accepted,
731
- feedback_text: params.feedbackText ?? null
732
- })
1033
+ return withTelemetrySpan("githits.feedback.request", async () => {
1034
+ const response = await fetch(`${this.apiUrl}/feedbacks`, {
1035
+ method: "POST",
1036
+ headers: this.headers(),
1037
+ body: JSON.stringify({
1038
+ solution_id: params.solutionId,
1039
+ accepted: params.accepted,
1040
+ feedback_text: params.feedbackText ?? null
1041
+ })
1042
+ });
1043
+ if (!response.ok) {
1044
+ throw await this.createError(response);
1045
+ }
1046
+ return { success: true, message: "Feedback submitted successfully" };
733
1047
  });
734
- if (!response.ok) {
735
- throw await this.createError(response);
736
- }
737
- return { success: true, message: "Feedback submitted successfully" };
738
1048
  }
739
1049
  headers() {
740
1050
  return {
1051
+ ...buildClientHeaders(),
741
1052
  Authorization: `Bearer ${this.token}`,
742
1053
  "Content-Type": "application/json",
743
1054
  "User-Agent": `githits-cli/${version}`
@@ -962,7 +1273,7 @@ query SearchSymbols(
962
1273
  hint
963
1274
  }
964
1275
  warning
965
- indexingStatus
1276
+ codeIndexState
966
1277
  indexingRef
967
1278
  availableVersions {
968
1279
  version
@@ -1005,7 +1316,7 @@ var searchSymbolsResponseSchema = z.object({
1005
1316
  hint: z.string().nullable().optional()
1006
1317
  }).nullable().optional(),
1007
1318
  warning: z.string().nullable().optional(),
1008
- indexingStatus: z.string(),
1319
+ codeIndexState: z.string(),
1009
1320
  indexingRef: z.string().nullable().optional(),
1010
1321
  availableVersions: z.array(availableVersionSchema).nullable().optional()
1011
1322
  });
@@ -1036,7 +1347,7 @@ var listRepoFilesResponseSchema = z.object({
1036
1347
  indexedVersion: z.string().nullable().optional(),
1037
1348
  resolution: navigationResolutionSchema,
1038
1349
  diagnostics: navigationDiagnosticsSchema,
1039
- indexingStatus: z.string(),
1350
+ codeIndexState: z.string(),
1040
1351
  indexingRef: z.string().nullable().optional(),
1041
1352
  availableVersions: z.array(availableVersionSchema).nullable().optional()
1042
1353
  });
@@ -1086,7 +1397,7 @@ query ListRepoFiles(
1086
1397
  diagnostics {
1087
1398
  hint
1088
1399
  }
1089
- indexingStatus
1400
+ codeIndexState
1090
1401
  indexingRef
1091
1402
  availableVersions {
1092
1403
  version
@@ -1104,7 +1415,7 @@ var codeContextResponseSchema = z.object({
1104
1415
  repoUrl: z.string().nullable().optional(),
1105
1416
  gitRef: z.string().nullable().optional(),
1106
1417
  isBinary: z.boolean().nullable().optional(),
1107
- indexingStatus: z.string(),
1418
+ codeIndexState: z.string(),
1108
1419
  indexingRef: z.string().nullable().optional()
1109
1420
  });
1110
1421
  var fetchCodeContextGraphQLResponseSchema = z.object({
@@ -1145,7 +1456,7 @@ query FetchCodeContext(
1145
1456
  repoUrl
1146
1457
  gitRef
1147
1458
  isBinary
1148
- indexingStatus
1459
+ codeIndexState
1149
1460
  indexingRef
1150
1461
  }
1151
1462
  }`;
@@ -1165,7 +1476,7 @@ var grepRepoFileResponseSchema = z.object({
1165
1476
  indexedVersion: z.string().nullable().optional(),
1166
1477
  resolution: navigationResolutionSchema,
1167
1478
  diagnostics: navigationDiagnosticsSchema,
1168
- indexingStatus: z.string(),
1479
+ codeIndexState: z.string(),
1169
1480
  indexingRef: z.string().nullable().optional(),
1170
1481
  availableVersions: z.array(availableVersionSchema).nullable().optional()
1171
1482
  });
@@ -1221,7 +1532,7 @@ query GrepRepoFile(
1221
1532
  diagnostics {
1222
1533
  hint
1223
1534
  }
1224
- indexingStatus
1535
+ codeIndexState
1225
1536
  indexingRef
1226
1537
  availableVersions {
1227
1538
  version
@@ -1301,13 +1612,13 @@ class CodeNavigationServiceImpl {
1301
1612
  if (!data) {
1302
1613
  throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
1303
1614
  }
1304
- if (data.indexingStatus === "INDEXING") {
1615
+ if (data.codeIndexState === "INDEXING") {
1305
1616
  throw new CodeNavigationIndexingError(this.createIndexingMessage(data.indexingRef ?? undefined), data.indexingRef ?? undefined, data.availableVersions?.map((entry) => ({
1306
1617
  version: entry.version ?? undefined,
1307
1618
  ref: entry.ref
1308
1619
  })));
1309
1620
  }
1310
- if (data.indexingStatus === "UNRESOLVABLE") {
1621
+ if (data.codeIndexState === "UNRESOLVABLE") {
1311
1622
  throw new CodeNavigationUnresolvableError("The requested target or version could not be resolved.");
1312
1623
  }
1313
1624
  return {
@@ -1410,7 +1721,7 @@ class CodeNavigationServiceImpl {
1410
1721
  return base;
1411
1722
  }
1412
1723
  throwIfIndexing(data) {
1413
- if (data.indexingStatus === "INDEXING") {
1724
+ if (data.codeIndexState === "INDEXING") {
1414
1725
  throw new CodeNavigationIndexingError(this.createIndexingMessage(data.indexingRef ?? undefined), data.indexingRef ?? undefined, data.availableVersions?.map((entry) => ({
1415
1726
  version: entry.version ?? undefined,
1416
1727
  ref: entry.ref
@@ -1534,7 +1845,7 @@ class CodeNavigationServiceImpl {
1534
1845
  throw new MalformedCodeNavigationResponseError("Malformed response from code navigation service.");
1535
1846
  }
1536
1847
  this.throwIfIndexing({
1537
- indexingStatus: data.indexingStatus,
1848
+ codeIndexState: data.codeIndexState,
1538
1849
  indexingRef: data.indexingRef
1539
1850
  });
1540
1851
  return {
@@ -1936,33 +2247,64 @@ class KeyringServiceImpl {
1936
2247
  class MigratingAuthStorage {
1937
2248
  primary;
1938
2249
  legacy;
1939
- constructor(primary, legacy) {
2250
+ onPrimaryUnavailable;
2251
+ primaryAvailable = true;
2252
+ warnedOnPrimaryFailure = false;
2253
+ constructor(primary, legacy, onPrimaryUnavailable = () => {}) {
1940
2254
  this.primary = primary;
1941
2255
  this.legacy = legacy;
2256
+ this.onPrimaryUnavailable = onPrimaryUnavailable;
1942
2257
  }
1943
2258
  async loadTokens(baseUrl2) {
1944
- const tokens = await this.primary.loadTokens(baseUrl2);
1945
- if (tokens)
1946
- return tokens;
2259
+ if (this.primaryAvailable) {
2260
+ try {
2261
+ const tokens = await this.primary.loadTokens(baseUrl2);
2262
+ if (tokens)
2263
+ return tokens;
2264
+ } catch (error) {
2265
+ if (!this.handlePrimaryFailure(error))
2266
+ throw error;
2267
+ }
2268
+ }
1947
2269
  const legacyTokens = await this.legacy.loadTokens(baseUrl2);
1948
- if (legacyTokens) {
2270
+ if (!legacyTokens)
2271
+ return null;
2272
+ if (!this.primaryAvailable)
2273
+ return legacyTokens;
2274
+ try {
1949
2275
  await this.primary.saveTokens(baseUrl2, legacyTokens);
1950
- try {
1951
- await this.legacy.clearTokens(baseUrl2);
1952
- } catch {}
2276
+ } catch (error) {
2277
+ if (!this.handlePrimaryFailure(error))
2278
+ throw error;
1953
2279
  return legacyTokens;
1954
2280
  }
1955
- return null;
2281
+ try {
2282
+ await this.legacy.clearTokens(baseUrl2);
2283
+ } catch {}
2284
+ return legacyTokens;
1956
2285
  }
1957
2286
  async saveTokens(baseUrl2, data) {
1958
- await this.primary.saveTokens(baseUrl2, data);
2287
+ if (this.primaryAvailable) {
2288
+ try {
2289
+ await this.primary.saveTokens(baseUrl2, data);
2290
+ return;
2291
+ } catch (error) {
2292
+ if (!this.handlePrimaryFailure(error))
2293
+ throw error;
2294
+ }
2295
+ }
2296
+ await this.legacy.saveTokens(baseUrl2, data);
1959
2297
  }
1960
2298
  async clearTokens(baseUrl2) {
1961
2299
  let primaryError;
1962
- try {
1963
- await this.primary.clearTokens(baseUrl2);
1964
- } catch (error) {
1965
- primaryError = error;
2300
+ if (this.primaryAvailable) {
2301
+ try {
2302
+ await this.primary.clearTokens(baseUrl2);
2303
+ } catch (error) {
2304
+ if (!this.handlePrimaryFailure(error)) {
2305
+ primaryError = error;
2306
+ }
2307
+ }
1966
2308
  }
1967
2309
  try {
1968
2310
  await this.legacy.clearTokens(baseUrl2);
@@ -1971,28 +2313,55 @@ class MigratingAuthStorage {
1971
2313
  throw primaryError;
1972
2314
  }
1973
2315
  async loadClient(baseUrl2) {
1974
- const client = await this.primary.loadClient(baseUrl2);
1975
- if (client)
1976
- return client;
2316
+ if (this.primaryAvailable) {
2317
+ try {
2318
+ const client = await this.primary.loadClient(baseUrl2);
2319
+ if (client)
2320
+ return client;
2321
+ } catch (error) {
2322
+ if (!this.handlePrimaryFailure(error))
2323
+ throw error;
2324
+ }
2325
+ }
1977
2326
  const legacyClient = await this.legacy.loadClient(baseUrl2);
1978
- if (legacyClient) {
2327
+ if (!legacyClient)
2328
+ return null;
2329
+ if (!this.primaryAvailable)
2330
+ return legacyClient;
2331
+ try {
1979
2332
  await this.primary.saveClient(baseUrl2, legacyClient);
1980
- try {
1981
- await this.legacy.clearClient(baseUrl2);
1982
- } catch {}
2333
+ } catch (error) {
2334
+ if (!this.handlePrimaryFailure(error))
2335
+ throw error;
1983
2336
  return legacyClient;
1984
2337
  }
1985
- return null;
2338
+ try {
2339
+ await this.legacy.clearClient(baseUrl2);
2340
+ } catch {}
2341
+ return legacyClient;
1986
2342
  }
1987
2343
  async saveClient(baseUrl2, data) {
1988
- await this.primary.saveClient(baseUrl2, data);
2344
+ if (this.primaryAvailable) {
2345
+ try {
2346
+ await this.primary.saveClient(baseUrl2, data);
2347
+ return;
2348
+ } catch (error) {
2349
+ if (!this.handlePrimaryFailure(error))
2350
+ throw error;
2351
+ }
2352
+ }
2353
+ await this.legacy.saveClient(baseUrl2, data);
1989
2354
  }
1990
2355
  async clearClient(baseUrl2) {
1991
2356
  let primaryError;
1992
- try {
1993
- await this.primary.clearClient(baseUrl2);
1994
- } catch (error) {
1995
- primaryError = error;
2357
+ if (this.primaryAvailable) {
2358
+ try {
2359
+ await this.primary.clearClient(baseUrl2);
2360
+ } catch (error) {
2361
+ if (!this.handlePrimaryFailure(error)) {
2362
+ primaryError = error;
2363
+ }
2364
+ }
1996
2365
  }
1997
2366
  try {
1998
2367
  await this.legacy.clearClient(baseUrl2);
@@ -2001,7 +2370,18 @@ class MigratingAuthStorage {
2001
2370
  throw primaryError;
2002
2371
  }
2003
2372
  getStorageLocation() {
2004
- return this.primary.getStorageLocation();
2373
+ return this.primaryAvailable ? this.primary.getStorageLocation() : this.legacy.getStorageLocation();
2374
+ }
2375
+ handlePrimaryFailure(error) {
2376
+ if (!(error instanceof KeychainUnavailableError)) {
2377
+ return false;
2378
+ }
2379
+ this.primaryAvailable = false;
2380
+ if (!this.warnedOnPrimaryFailure) {
2381
+ this.warnedOnPrimaryFailure = true;
2382
+ this.onPrimaryUnavailable(error);
2383
+ }
2384
+ return true;
2005
2385
  }
2006
2386
  }
2007
2387
  // src/services/package-intelligence-service.ts
@@ -2302,13 +2682,50 @@ var directDependencySchema = z2.object({
2302
2682
  versionConstraint: z2.string().nullable().optional(),
2303
2683
  type: z2.string().nullable().optional()
2304
2684
  });
2685
+ var dependencyGraphNodeSchema = z2.object({
2686
+ registry: z2.string(),
2687
+ name: z2.string(),
2688
+ version: z2.string().nullable().optional()
2689
+ });
2690
+ var dependencyGraphEdgeSchema = z2.object({
2691
+ fromIndex: z2.number().int().nullable().optional(),
2692
+ toIndex: z2.number().int(),
2693
+ constraint: z2.string().nullable().optional(),
2694
+ dependencyType: z2.string().nullable().optional()
2695
+ });
2696
+ var dependencyGraphSchema = z2.object({
2697
+ formatVersion: z2.number().int(),
2698
+ nodes: z2.array(dependencyGraphNodeSchema),
2699
+ edges: z2.array(dependencyGraphEdgeSchema)
2700
+ });
2701
+ var dependencyConflictEdgeSchema = z2.object({
2702
+ fromIndex: z2.number().int().nullable().optional(),
2703
+ toIndex: z2.number().int(),
2704
+ versionConstraint: z2.string(),
2705
+ dependencyType: z2.string()
2706
+ });
2707
+ var dependencyConflictSchema = z2.object({
2708
+ packageName: z2.string(),
2709
+ requiredVersions: z2.array(z2.string()),
2710
+ conflictingEdges: z2.array(dependencyConflictEdgeSchema)
2711
+ });
2712
+ var circularDependencyCycleSchema = z2.object({
2713
+ cycleStart: z2.string(),
2714
+ circularPath: z2.array(z2.string()),
2715
+ displayChain: z2.string()
2716
+ });
2717
+ var environmentMarkerSchema = z2.object({
2718
+ type: z2.string().nullable().optional(),
2719
+ value: z2.string().nullable().optional(),
2720
+ raw: z2.string().nullable().optional()
2721
+ });
2305
2722
  var transitiveDependencySchema = z2.object({
2306
2723
  totalEdges: z2.number().int().nullable().optional(),
2307
2724
  uniquePackagesCount: z2.number().int().nullable().optional(),
2308
2725
  uniqueDependencies: z2.array(z2.string()).nullable().optional(),
2309
- conflicts: z2.array(z2.unknown()).nullable().optional(),
2310
- circularDependencies: z2.array(z2.unknown()).nullable().optional(),
2311
- dag: z2.unknown().nullable().optional()
2726
+ dependencyConflicts: z2.array(dependencyConflictSchema).nullable().optional(),
2727
+ circularDependencyCycles: z2.array(circularDependencyCycleSchema).nullable().optional(),
2728
+ dependencyGraph: dependencyGraphSchema.nullable().optional()
2312
2729
  }).nullable().optional();
2313
2730
  var dependencyBundleSchema = z2.object({
2314
2731
  direct: z2.array(directDependencySchema).nullable().optional(),
@@ -2332,7 +2749,7 @@ var dependencyGroupSchema = z2.object({
2332
2749
  });
2333
2750
  var dependencyGroupsInfoSchema = z2.object({
2334
2751
  primaryGroup: z2.string().nullable().optional(),
2335
- environmentConstraints: z2.array(z2.unknown()).nullable().optional(),
2752
+ environmentMarkers: z2.array(environmentMarkerSchema).nullable().optional(),
2336
2753
  groups: z2.array(dependencyGroupSchema)
2337
2754
  }).nullable().optional();
2338
2755
  var dependencyReportResponseSchema = z2.object({
@@ -2382,14 +2799,44 @@ query PackageDependencies(
2382
2799
  totalEdges
2383
2800
  uniquePackagesCount
2384
2801
  uniqueDependencies
2385
- conflicts
2386
- circularDependencies
2387
- dag
2802
+ dependencyConflicts {
2803
+ packageName
2804
+ requiredVersions
2805
+ conflictingEdges {
2806
+ fromIndex
2807
+ toIndex
2808
+ versionConstraint
2809
+ dependencyType
2810
+ }
2811
+ }
2812
+ circularDependencyCycles {
2813
+ cycleStart
2814
+ circularPath
2815
+ displayChain
2816
+ }
2817
+ dependencyGraph {
2818
+ formatVersion
2819
+ nodes {
2820
+ registry
2821
+ name
2822
+ version
2823
+ }
2824
+ edges {
2825
+ fromIndex
2826
+ toIndex
2827
+ constraint
2828
+ dependencyType
2829
+ }
2830
+ }
2388
2831
  }
2389
2832
  }
2390
2833
  dependencyGroups {
2391
2834
  primaryGroup
2392
- environmentConstraints
2835
+ environmentMarkers {
2836
+ type
2837
+ value
2838
+ raw
2839
+ }
2393
2840
  groups {
2394
2841
  name
2395
2842
  lifecycle
@@ -2484,12 +2931,12 @@ class PackageIntelligenceServiceImpl {
2484
2931
  this.fetchFn = fetchFn;
2485
2932
  }
2486
2933
  async packageSummary(params) {
2487
- return executeWithTokenRefresh({
2934
+ return withTelemetrySpan("pkg-intel.summary.request", () => executeWithTokenRefresh({
2488
2935
  getToken: () => this.tokenProvider.getToken(),
2489
2936
  forceRefresh: () => this.tokenProvider.forceRefresh(),
2490
2937
  shouldRefresh: (error) => error instanceof AuthenticationError,
2491
2938
  executeWithToken: (token) => this.executePackageSummary(token, params)
2492
- });
2939
+ }));
2493
2940
  }
2494
2941
  async executePackageSummary(token, params) {
2495
2942
  let response;
@@ -2627,12 +3074,12 @@ class PackageIntelligenceServiceImpl {
2627
3074
  };
2628
3075
  }
2629
3076
  async packageVulnerabilities(params) {
2630
- return executeWithTokenRefresh({
3077
+ return withTelemetrySpan("pkg-intel.vulnerabilities.request", () => executeWithTokenRefresh({
2631
3078
  getToken: () => this.tokenProvider.getToken(),
2632
3079
  forceRefresh: () => this.tokenProvider.forceRefresh(),
2633
3080
  shouldRefresh: (error) => error instanceof AuthenticationError,
2634
3081
  executeWithToken: (token) => this.executePackageVulnerabilities(token, params)
2635
- });
3082
+ }));
2636
3083
  }
2637
3084
  async executePackageVulnerabilities(token, params) {
2638
3085
  let response;
@@ -2707,12 +3154,12 @@ class PackageIntelligenceServiceImpl {
2707
3154
  };
2708
3155
  }
2709
3156
  async packageDependencies(params) {
2710
- return executeWithTokenRefresh({
3157
+ return withTelemetrySpan("pkg-intel.dependencies.request", () => executeWithTokenRefresh({
2711
3158
  getToken: () => this.tokenProvider.getToken(),
2712
3159
  forceRefresh: () => this.tokenProvider.forceRefresh(),
2713
3160
  shouldRefresh: (error) => error instanceof AuthenticationError,
2714
3161
  executeWithToken: (token) => this.executePackageDependencies(token, params)
2715
- });
3162
+ }));
2716
3163
  }
2717
3164
  async executePackageDependencies(token, params) {
2718
3165
  let response;
@@ -2780,14 +3227,44 @@ class PackageIntelligenceServiceImpl {
2780
3227
  totalEdges: bundle.transitive.totalEdges ?? undefined,
2781
3228
  uniquePackagesCount: bundle.transitive.uniquePackagesCount ?? undefined,
2782
3229
  uniqueDependencies: bundle.transitive.uniqueDependencies ?? undefined,
2783
- conflicts: bundle.transitive.conflicts ?? undefined,
2784
- circularDependencies: bundle.transitive.circularDependencies ?? undefined,
2785
- dag: bundle.transitive.dag ?? undefined
3230
+ dependencyConflicts: bundle.transitive.dependencyConflicts?.map((c) => ({
3231
+ packageName: c.packageName,
3232
+ requiredVersions: c.requiredVersions,
3233
+ conflictingEdges: c.conflictingEdges.map((edge) => ({
3234
+ fromIndex: edge.fromIndex ?? undefined,
3235
+ toIndex: edge.toIndex,
3236
+ versionConstraint: edge.versionConstraint,
3237
+ dependencyType: edge.dependencyType
3238
+ }))
3239
+ })) ?? undefined,
3240
+ circularDependencyCycles: bundle.transitive.circularDependencyCycles?.map((cycle) => ({
3241
+ cycleStart: cycle.cycleStart,
3242
+ circularPath: cycle.circularPath,
3243
+ displayChain: cycle.displayChain
3244
+ })) ?? undefined,
3245
+ dependencyGraph: bundle.transitive.dependencyGraph ? {
3246
+ formatVersion: bundle.transitive.dependencyGraph.formatVersion,
3247
+ nodes: bundle.transitive.dependencyGraph.nodes.map((n) => ({
3248
+ registry: n.registry,
3249
+ name: n.name,
3250
+ version: n.version ?? undefined
3251
+ })),
3252
+ edges: bundle.transitive.dependencyGraph.edges.map((e) => ({
3253
+ fromIndex: e.fromIndex ?? undefined,
3254
+ toIndex: e.toIndex,
3255
+ constraint: e.constraint ?? undefined,
3256
+ dependencyType: e.dependencyType ?? undefined
3257
+ }))
3258
+ } : undefined
2786
3259
  } : undefined
2787
3260
  } : undefined;
2788
3261
  const dependencyGroups = data.dependencyGroups ? {
2789
3262
  primaryGroup: data.dependencyGroups.primaryGroup ?? undefined,
2790
- environmentConstraints: data.dependencyGroups.environmentConstraints ?? undefined,
3263
+ environmentMarkers: data.dependencyGroups.environmentMarkers?.map((m) => ({
3264
+ type: m.type ?? undefined,
3265
+ value: m.value ?? undefined,
3266
+ raw: m.raw ?? undefined
3267
+ })) ?? undefined,
2791
3268
  groups: data.dependencyGroups.groups.map((group) => ({
2792
3269
  name: group.name,
2793
3270
  lifecycle: group.lifecycle,
@@ -2811,12 +3288,12 @@ class PackageIntelligenceServiceImpl {
2811
3288
  };
2812
3289
  }
2813
3290
  async packageChangelog(params) {
2814
- return executeWithTokenRefresh({
3291
+ return withTelemetrySpan("pkg-intel.changelog.request", () => executeWithTokenRefresh({
2815
3292
  getToken: () => this.tokenProvider.getToken(),
2816
3293
  forceRefresh: () => this.tokenProvider.forceRefresh(),
2817
3294
  shouldRefresh: (error) => error instanceof AuthenticationError,
2818
3295
  executeWithToken: (token) => this.executePackageChangelog(token, params)
2819
- });
3296
+ }));
2820
3297
  }
2821
3298
  async executePackageChangelog(token, params) {
2822
3299
  let response;
@@ -3010,27 +3487,29 @@ class TokenManager {
3010
3487
  this.mcpUrl = deps.mcpUrl;
3011
3488
  }
3012
3489
  async getToken() {
3013
- if (!this.cachedToken) {
3014
- this.cachedToken = await this.authStorage.loadTokens(this.mcpUrl);
3015
- if (!this.cachedToken)
3016
- return;
3017
- }
3018
- const currentToken = this.cachedToken.accessToken;
3019
- const { expired, shouldRefresh } = shouldRefreshToken(this.cachedToken, PROACTIVE_REFRESH_RATIO, new Date);
3020
- if (!shouldRefresh) {
3021
- return currentToken;
3022
- }
3023
- const refreshedToken = await this.doRefresh();
3024
- if (refreshedToken) {
3025
- return refreshedToken;
3026
- }
3027
- if (!expired) {
3028
- return currentToken;
3029
- }
3030
- return;
3490
+ return withTelemetrySpan("token-manager.get-token", async () => {
3491
+ if (!this.cachedToken) {
3492
+ this.cachedToken = await withTelemetrySpan("token-manager.load-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
3493
+ if (!this.cachedToken)
3494
+ return;
3495
+ }
3496
+ const currentToken = this.cachedToken.accessToken;
3497
+ const { expired, shouldRefresh } = shouldRefreshToken(this.cachedToken, PROACTIVE_REFRESH_RATIO, new Date);
3498
+ if (!shouldRefresh) {
3499
+ return currentToken;
3500
+ }
3501
+ const refreshedToken = await this.doRefresh();
3502
+ if (refreshedToken) {
3503
+ return refreshedToken;
3504
+ }
3505
+ if (!expired) {
3506
+ return currentToken;
3507
+ }
3508
+ return;
3509
+ });
3031
3510
  }
3032
3511
  async forceRefresh() {
3033
- return this.doRefresh();
3512
+ return withTelemetrySpan("token-manager.force-refresh", () => this.doRefresh());
3034
3513
  }
3035
3514
  async doRefresh() {
3036
3515
  if (this.refreshPromise) {
@@ -3044,59 +3523,55 @@ class TokenManager {
3044
3523
  }
3045
3524
  }
3046
3525
  async executeRefresh() {
3047
- const tokens = this.cachedToken ?? await this.authStorage.loadTokens(this.mcpUrl);
3048
- if (!tokens)
3049
- return;
3050
- const client = await this.authStorage.loadClient(this.mcpUrl);
3051
- if (!client)
3052
- return;
3053
- let response;
3054
- try {
3055
- const metadata = await this.authService.discoverEndpoints(this.mcpUrl);
3056
- response = await this.authService.refreshAccessToken({
3057
- tokenEndpoint: metadata.tokenEndpoint,
3058
- clientId: client.clientId,
3059
- clientSecret: client.clientSecret,
3060
- refreshToken: tokens.refreshToken
3061
- });
3062
- } catch {
3063
- const isExpired = tokens.expiresAt ? new Date >= new Date(tokens.expiresAt) : false;
3064
- if (isExpired) {
3065
- this.cachedToken = null;
3066
- await this.authStorage.clearTokens(this.mcpUrl);
3526
+ return withTelemetrySpan("token-manager.refresh", async () => {
3527
+ const tokens = this.cachedToken ?? await withTelemetrySpan("token-manager.load-tokens", () => this.authStorage.loadTokens(this.mcpUrl));
3528
+ if (!tokens)
3529
+ return;
3530
+ const client = await withTelemetrySpan("token-manager.load-client", () => this.authStorage.loadClient(this.mcpUrl));
3531
+ if (!client)
3532
+ return;
3533
+ let response;
3534
+ try {
3535
+ const metadata = await withTelemetrySpan("token-manager.discover-endpoints", () => this.authService.discoverEndpoints(this.mcpUrl));
3536
+ response = await withTelemetrySpan("token-manager.refresh-access-token", () => this.authService.refreshAccessToken({
3537
+ tokenEndpoint: metadata.tokenEndpoint,
3538
+ clientId: client.clientId,
3539
+ clientSecret: client.clientSecret,
3540
+ refreshToken: tokens.refreshToken
3541
+ }));
3542
+ } catch {
3543
+ const isExpired = tokens.expiresAt ? new Date >= new Date(tokens.expiresAt) : false;
3544
+ if (isExpired) {
3545
+ this.cachedToken = null;
3546
+ await withTelemetrySpan("token-manager.clear-tokens", () => this.authStorage.clearTokens(this.mcpUrl));
3547
+ }
3548
+ return;
3067
3549
  }
3068
- return;
3069
- }
3070
- const newTokenData = {
3071
- accessToken: response.accessToken,
3072
- refreshToken: response.refreshToken,
3073
- expiresAt: new Date(Date.now() + response.expiresIn * 1000).toISOString(),
3074
- createdAt: tokens.createdAt
3075
- };
3076
- await this.authStorage.saveTokens(this.mcpUrl, newTokenData);
3077
- this.cachedToken = newTokenData;
3078
- return response.accessToken;
3550
+ const newTokenData = {
3551
+ accessToken: response.accessToken,
3552
+ refreshToken: response.refreshToken,
3553
+ expiresAt: new Date(Date.now() + response.expiresIn * 1000).toISOString(),
3554
+ createdAt: new Date().toISOString()
3555
+ };
3556
+ await withTelemetrySpan("token-manager.save-tokens", () => this.authStorage.saveTokens(this.mcpUrl, newTokenData));
3557
+ this.cachedToken = newTokenData;
3558
+ return response.accessToken;
3559
+ });
3079
3560
  }
3080
3561
  }
3081
3562
  // src/container.ts
3082
3563
  function createAuthStorage(fileSystemService) {
3083
- const fileStorage = new AuthStorageImpl(fileSystemService);
3084
- try {
3564
+ return withTelemetrySpanSync("container.create-auth-storage", () => {
3565
+ const fileStorage = new AuthStorageImpl(fileSystemService);
3085
3566
  const rawKeyring = new KeyringServiceImpl;
3086
3567
  const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
3087
- const probeKey = `__probe_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
3088
- keyring.setPassword("githits", probeKey, "probe");
3089
- try {
3090
- keyring.deletePassword("githits", probeKey);
3091
- } catch {}
3092
3568
  const keychainStorage = new KeychainAuthStorage(keyring);
3093
- return new MigratingAuthStorage(keychainStorage, fileStorage);
3094
- } catch (error) {
3095
- if (!(error instanceof KeychainUnavailableError))
3096
- throw error;
3097
- console.error("Warning: System keychain unavailable. Falling back to file-based credential storage.");
3098
- return fileStorage;
3099
- }
3569
+ return new MigratingAuthStorage(keychainStorage, fileStorage, (error) => {
3570
+ if (!(error instanceof KeychainUnavailableError))
3571
+ return;
3572
+ console.error("Warning: System keychain unavailable. Falling back to file-based credential storage.");
3573
+ });
3574
+ });
3100
3575
  }
3101
3576
  function createStaticTokenProvider(token) {
3102
3577
  return {
@@ -3107,19 +3582,42 @@ function createStaticTokenProvider(token) {
3107
3582
  };
3108
3583
  }
3109
3584
  async function createContainer() {
3110
- const mcpUrl = getMcpUrl();
3111
- const apiUrl = getApiUrl();
3112
- const codeNavigationUrl = getCodeNavigationUrl();
3113
- const codeNavigationCliOverrideEnabled = isCodeNavigationCliOverrideEnabled();
3114
- const fileSystemService = new FileSystemServiceImpl;
3115
- const authStorage = createAuthStorage(fileSystemService);
3116
- const authService = new AuthServiceImpl;
3117
- const browserService = new BrowserServiceImpl;
3118
- const envToken = getEnvApiToken();
3119
- if (envToken) {
3120
- const tokenProvider = createStaticTokenProvider(envToken);
3121
- const codeNavigationService2 = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
3122
- const packageIntelligenceService2 = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
3585
+ return withTelemetrySpan("container.create", async () => {
3586
+ const mcpUrl = getMcpUrl();
3587
+ const apiUrl = getApiUrl();
3588
+ const codeNavigationUrl = getCodeNavigationUrl();
3589
+ const codeNavigationCliOverrideEnabled = isCodeNavigationCliOverrideEnabled();
3590
+ const fileSystemService = new FileSystemServiceImpl;
3591
+ const authStorage = createAuthStorage(fileSystemService);
3592
+ const authService = new AuthServiceImpl;
3593
+ const browserService = new BrowserServiceImpl;
3594
+ const envToken = getEnvApiToken();
3595
+ if (envToken) {
3596
+ const tokenProvider = createStaticTokenProvider(envToken);
3597
+ const codeNavigationService2 = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
3598
+ const packageIntelligenceService2 = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenProvider) : undefined;
3599
+ return {
3600
+ authStorage,
3601
+ authService,
3602
+ browserService,
3603
+ fileSystemService,
3604
+ mcpUrl,
3605
+ apiUrl,
3606
+ apiToken: envToken,
3607
+ hasValidToken: true,
3608
+ envApiToken: envToken,
3609
+ codeNavigationCapability: getCodeNavigationCapability(envToken),
3610
+ codeNavigationCliOverrideEnabled,
3611
+ codeNavigationUrl,
3612
+ codeNavigationService: codeNavigationService2,
3613
+ packageIntelligenceService: packageIntelligenceService2,
3614
+ githitsService: new GitHitsServiceImpl(apiUrl, envToken)
3615
+ };
3616
+ }
3617
+ const tokenManager = new TokenManager({ authService, authStorage, mcpUrl });
3618
+ const apiToken = await withTelemetrySpan("container.token.get", () => tokenManager.getToken());
3619
+ const codeNavigationService = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager) : undefined;
3620
+ const packageIntelligenceService = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenManager) : undefined;
3123
3621
  return {
3124
3622
  authStorage,
3125
3623
  authService,
@@ -3127,73 +3625,56 @@ async function createContainer() {
3127
3625
  fileSystemService,
3128
3626
  mcpUrl,
3129
3627
  apiUrl,
3130
- apiToken: envToken,
3131
- hasValidToken: true,
3132
- envApiToken: envToken,
3133
- codeNavigationCapability: getCodeNavigationCapability(envToken),
3628
+ apiToken,
3629
+ hasValidToken: apiToken !== undefined,
3630
+ envApiToken: undefined,
3631
+ codeNavigationCapability: getCodeNavigationCapability(apiToken),
3134
3632
  codeNavigationCliOverrideEnabled,
3135
3633
  codeNavigationUrl,
3136
- codeNavigationService: codeNavigationService2,
3137
- packageIntelligenceService: packageIntelligenceService2,
3138
- githitsService: new GitHitsServiceImpl(apiUrl, envToken)
3634
+ codeNavigationService,
3635
+ packageIntelligenceService,
3636
+ githitsService: new RefreshingGitHitsService(apiUrl, tokenManager)
3139
3637
  };
3140
- }
3141
- const tokenManager = new TokenManager({ authService, authStorage, mcpUrl });
3142
- const apiToken = await tokenManager.getToken();
3143
- const codeNavigationService = codeNavigationUrl ? new CodeNavigationServiceImpl(codeNavigationUrl, tokenManager) : undefined;
3144
- const packageIntelligenceService = codeNavigationUrl ? new PackageIntelligenceServiceImpl(codeNavigationUrl, tokenManager) : undefined;
3145
- return {
3146
- authStorage,
3147
- authService,
3148
- browserService,
3149
- fileSystemService,
3150
- mcpUrl,
3151
- apiUrl,
3152
- apiToken,
3153
- hasValidToken: apiToken !== undefined,
3154
- envApiToken: undefined,
3155
- codeNavigationCapability: getCodeNavigationCapability(apiToken),
3156
- codeNavigationCliOverrideEnabled,
3157
- codeNavigationUrl,
3158
- codeNavigationService,
3159
- packageIntelligenceService,
3160
- githitsService: new RefreshingGitHitsService(apiUrl, tokenManager)
3161
- };
3638
+ });
3162
3639
  }
3163
3640
  async function resolveStartupCodeNavigationRegistrationState() {
3164
- const envToken = getEnvApiToken();
3165
- if (envToken) {
3641
+ return withTelemetrySpan("startup.resolve-code-nav-registration-state", async () => {
3642
+ const envToken = getEnvApiToken();
3643
+ if (envToken) {
3644
+ return {
3645
+ capability: getCodeNavigationCapability(envToken),
3646
+ expiredStoredAuth: false
3647
+ };
3648
+ }
3649
+ const tokens = await loadStartupTokens(getMcpUrl());
3650
+ if (tokens?.expiresAt && new Date(tokens.expiresAt) < new Date) {
3651
+ return { capability: "unknown", expiredStoredAuth: true };
3652
+ }
3166
3653
  return {
3167
- capability: getCodeNavigationCapability(envToken),
3654
+ capability: getCodeNavigationCapability(tokens?.accessToken),
3168
3655
  expiredStoredAuth: false
3169
3656
  };
3170
- }
3171
- const tokens = await loadStartupTokens(getMcpUrl());
3172
- if (tokens?.expiresAt && new Date(tokens.expiresAt) < new Date) {
3173
- return { capability: "unknown", expiredStoredAuth: true };
3174
- }
3175
- return {
3176
- capability: getCodeNavigationCapability(tokens?.accessToken),
3177
- expiredStoredAuth: false
3178
- };
3657
+ });
3179
3658
  }
3180
3659
  async function loadStartupTokens(mcpUrl) {
3181
- const fileSystemService = new FileSystemServiceImpl;
3182
- const fileStorage = new AuthStorageImpl(fileSystemService);
3183
- try {
3184
- const rawKeyring = new KeyringServiceImpl;
3185
- const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
3186
- const keychainStorage = new KeychainAuthStorage(keyring);
3187
- const keychainTokens = await keychainStorage.loadTokens(mcpUrl);
3188
- if (keychainTokens) {
3189
- return keychainTokens;
3190
- }
3191
- } catch (error) {
3192
- if (!(error instanceof KeychainUnavailableError)) {
3193
- throw error;
3660
+ return withTelemetrySpan("startup.load-tokens", async () => {
3661
+ const fileSystemService = new FileSystemServiceImpl;
3662
+ const fileStorage = new AuthStorageImpl(fileSystemService);
3663
+ try {
3664
+ const rawKeyring = new KeyringServiceImpl;
3665
+ const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
3666
+ const keychainStorage = new KeychainAuthStorage(keyring);
3667
+ const keychainTokens = await keychainStorage.loadTokens(mcpUrl);
3668
+ if (keychainTokens) {
3669
+ return keychainTokens;
3670
+ }
3671
+ } catch (error) {
3672
+ if (!(error instanceof KeychainUnavailableError)) {
3673
+ throw error;
3674
+ }
3194
3675
  }
3195
- }
3196
- return fileStorage.loadTokens(mcpUrl);
3676
+ return fileStorage.loadTokens(mcpUrl);
3677
+ });
3197
3678
  }
3198
3679
 
3199
3680
  // src/commands/auth-status.ts
@@ -3813,8 +4294,8 @@ function buildPackageDependenciesSuccessPayload(report, options = {}) {
3813
4294
  payload.requestedVersion = requestedEcho;
3814
4295
  }
3815
4296
  const bundle = report.dependencies;
3816
- const decodedDagForResolution = decodeDag(bundle?.transitive?.dag);
3817
- const directVersionByName = decodedDagForResolution ? buildDirectVersionLookup(decodedDagForResolution) : null;
4297
+ const graph = bundle?.transitive?.dependencyGraph ?? null;
4298
+ const directVersionByName = graph ? buildDirectVersionLookup(graph) : null;
3818
4299
  const directArray = bundle?.direct;
3819
4300
  if (directArray !== undefined) {
3820
4301
  const items = directArray.map((entry) => buildDirect(entry, directVersionByName));
@@ -3827,8 +4308,8 @@ function buildPackageDependenciesSuccessPayload(report, options = {}) {
3827
4308
  if (groupsInfo.primaryGroup) {
3828
4309
  groupsBlock.primaryGroup = groupsInfo.primaryGroup;
3829
4310
  }
3830
- if (groupsInfo.environmentConstraints && groupsInfo.environmentConstraints.length > 0) {
3831
- groupsBlock.environmentConstraints = groupsInfo.environmentConstraints.slice();
4311
+ if (groupsInfo.environmentMarkers && groupsInfo.environmentMarkers.length > 0) {
4312
+ groupsBlock.environmentMarkers = groupsInfo.environmentMarkers.map((m) => projectEnvironmentMarker(m));
3832
4313
  }
3833
4314
  payload.groups = groupsBlock;
3834
4315
  }
@@ -3845,15 +4326,18 @@ function buildPackageDependenciesSuccessPayload(report, options = {}) {
3845
4326
  if (options.maxDepth !== undefined) {
3846
4327
  block.depth = options.maxDepth;
3847
4328
  }
3848
- const packages = buildTransitivePackages(transitive.uniqueDependencies, decodedDagForResolution, options.includeImporters ?? false);
4329
+ const packages = buildTransitivePackages(transitive.uniqueDependencies, graph, options.includeImporters ?? false);
3849
4330
  if (packages && packages.length > 0) {
3850
4331
  block.packages = packages;
3851
4332
  }
3852
- if (transitive.conflicts && transitive.conflicts.length > 0) {
3853
- block.conflicts = buildTypedConflicts(transitive.conflicts);
4333
+ if (transitive.dependencyConflicts && transitive.dependencyConflicts.length > 0) {
4334
+ block.conflicts = transitive.dependencyConflicts.map((c) => ({
4335
+ name: c.packageName,
4336
+ requiredVersions: c.requiredVersions.slice().sort()
4337
+ }));
3854
4338
  }
3855
- if (transitive.circularDependencies && transitive.circularDependencies.length > 0) {
3856
- block.circularDependencies = buildTypedCycles(transitive.circularDependencies);
4339
+ if (transitive.circularDependencyCycles && transitive.circularDependencyCycles.length > 0) {
4340
+ block.circularDependencies = transitive.circularDependencyCycles.map((c) => ({ cycle: c.circularPath.slice() }));
3857
4341
  }
3858
4342
  payload.transitive = block;
3859
4343
  }
@@ -3863,6 +4347,16 @@ function buildPackageDependenciesSuccessPayload(report, options = {}) {
3863
4347
  }
3864
4348
  return payload;
3865
4349
  }
4350
+ function projectEnvironmentMarker(marker) {
4351
+ const out = {};
4352
+ if (marker.type !== undefined)
4353
+ out.type = marker.type;
4354
+ if (marker.value !== undefined)
4355
+ out.value = marker.value;
4356
+ if (marker.raw !== undefined)
4357
+ out.raw = marker.raw;
4358
+ return out;
4359
+ }
3866
4360
  function buildDirect(entry, directVersionByName) {
3867
4361
  const lean = { name: entry.name };
3868
4362
  if (entry.versionConstraint)
@@ -3872,15 +4366,14 @@ function buildDirect(entry, directVersionByName) {
3872
4366
  lean.version = resolved;
3873
4367
  return lean;
3874
4368
  }
3875
- function buildDirectVersionLookup(dag) {
3876
- const rootIdx = findRootNodeIdx(dag);
3877
- if (rootIdx === null)
3878
- return null;
4369
+ function buildDirectVersionLookup(graph) {
4370
+ const rootIdx = findRootNodeIdx(graph);
3879
4371
  const out = new Map;
3880
- for (const edge of dag.edges) {
3881
- if (edge.fromIdx !== rootIdx)
4372
+ for (const edge of graph.edges) {
4373
+ const fromRoot = edge.fromIndex === undefined || edge.fromIndex === null || edge.fromIndex === rootIdx;
4374
+ if (!fromRoot)
3882
4375
  continue;
3883
- const node = dag.nodes[edge.toIdx];
4376
+ const node = graph.nodes[edge.toIndex];
3884
4377
  if (!node || !node.version)
3885
4378
  continue;
3886
4379
  if (!out.has(node.name)) {
@@ -3889,12 +4382,12 @@ function buildDirectVersionLookup(dag) {
3889
4382
  }
3890
4383
  return out.size > 0 ? out : null;
3891
4384
  }
3892
- function findRootNodeIdx(dag) {
4385
+ function findRootNodeIdx(graph) {
3893
4386
  const incoming = new Set;
3894
- for (const e of dag.edges)
3895
- incoming.add(e.toIdx);
4387
+ for (const e of graph.edges)
4388
+ incoming.add(e.toIndex);
3896
4389
  let root = null;
3897
- for (let i = 0;i < dag.nodes.length; i++) {
4390
+ for (let i = 0;i < graph.nodes.length; i++) {
3898
4391
  if (!incoming.has(i)) {
3899
4392
  if (root !== null)
3900
4393
  return null;
@@ -3903,10 +4396,10 @@ function findRootNodeIdx(dag) {
3903
4396
  }
3904
4397
  return root;
3905
4398
  }
3906
- function buildTransitivePackages(uniqueDependencies, dag, includeImporters) {
4399
+ function buildTransitivePackages(uniqueDependencies, graph, includeImporters) {
3907
4400
  if (!uniqueDependencies || uniqueDependencies.length === 0)
3908
4401
  return null;
3909
- const incoming = includeImporters && dag ? buildIncomingEdgeMap(dag) : null;
4402
+ const incoming = includeImporters && graph ? buildIncomingEdgeMap(graph) : null;
3910
4403
  const out = [];
3911
4404
  for (const entry of uniqueDependencies) {
3912
4405
  const [name, version2] = parseNameAtVersion(entry);
@@ -3915,11 +4408,11 @@ function buildTransitivePackages(uniqueDependencies, dag, includeImporters) {
3915
4408
  const record = { name };
3916
4409
  if (version2)
3917
4410
  record.version = version2;
3918
- if (includeImporters && dag && incoming) {
3919
- const nodeIdx = findNodeIdx(dag, name, version2);
4411
+ if (includeImporters && graph && incoming) {
4412
+ const nodeIdx = findNodeIdx(graph, name, version2);
3920
4413
  if (nodeIdx !== null) {
3921
4414
  const edges = incoming.get(nodeIdx) ?? [];
3922
- const importers = buildImportersFromEdges(dag, edges);
4415
+ const importers = buildImportersFromEdges(graph, edges);
3923
4416
  if (importers.length > 0)
3924
4417
  record.importers = importers;
3925
4418
  }
@@ -3937,21 +4430,21 @@ function parseNameAtVersion(raw) {
3937
4430
  return [trimmed, undefined];
3938
4431
  return [trimmed.slice(0, atIdx), trimmed.slice(atIdx + 1)];
3939
4432
  }
3940
- function buildIncomingEdgeMap(dag) {
4433
+ function buildIncomingEdgeMap(graph) {
3941
4434
  const map = new Map;
3942
- for (const edge of dag.edges) {
3943
- const list = map.get(edge.toIdx);
4435
+ for (const edge of graph.edges) {
4436
+ const list = map.get(edge.toIndex);
3944
4437
  if (list)
3945
4438
  list.push(edge);
3946
4439
  else
3947
- map.set(edge.toIdx, [edge]);
4440
+ map.set(edge.toIndex, [edge]);
3948
4441
  }
3949
4442
  return map;
3950
4443
  }
3951
- function findNodeIdx(dag, name, version2) {
4444
+ function findNodeIdx(graph, name, version2) {
3952
4445
  let fallback = null;
3953
- for (let i = 0;i < dag.nodes.length; i++) {
3954
- const n = dag.nodes[i];
4446
+ for (let i = 0;i < graph.nodes.length; i++) {
4447
+ const n = graph.nodes[i];
3955
4448
  if (!n)
3956
4449
  continue;
3957
4450
  if (n.name !== name)
@@ -3965,11 +4458,13 @@ function findNodeIdx(dag, name, version2) {
3965
4458
  }
3966
4459
  return fallback;
3967
4460
  }
3968
- function buildImportersFromEdges(dag, edges) {
4461
+ function buildImportersFromEdges(graph, edges) {
3969
4462
  const seen = new Set;
3970
4463
  const out = [];
3971
4464
  for (const edge of edges) {
3972
- const from = dag.nodes[edge.fromIdx];
4465
+ if (edge.fromIndex === undefined || edge.fromIndex === null)
4466
+ continue;
4467
+ const from = graph.nodes[edge.fromIndex];
3973
4468
  if (!from)
3974
4469
  continue;
3975
4470
  const key = `${from.name}\x00${from.version ?? ""}\x00${edge.constraint ?? ""}`;
@@ -3996,60 +4491,6 @@ function buildImportersFromEdges(dag, edges) {
3996
4491
  });
3997
4492
  return out;
3998
4493
  }
3999
- function buildTypedConflicts(raw) {
4000
- const typed = [];
4001
- for (const entry of raw) {
4002
- const decoded = decodeConflictEntryForEnvelope(entry);
4003
- if (!decoded)
4004
- return raw.slice();
4005
- typed.push(decoded);
4006
- }
4007
- return typed;
4008
- }
4009
- function decodeConflictEntryForEnvelope(raw) {
4010
- if (!raw || typeof raw !== "object")
4011
- return null;
4012
- const obj = raw;
4013
- const name = typeof obj.package_name === "string" ? obj.package_name : typeof obj.packageName === "string" ? obj.packageName : null;
4014
- if (!name)
4015
- return null;
4016
- const rangesRaw = obj.required_versions ?? obj.requiredVersions;
4017
- if (!Array.isArray(rangesRaw))
4018
- return null;
4019
- const ranges = [];
4020
- for (const r of rangesRaw) {
4021
- if (typeof r === "string" && r.length > 0 && !ranges.includes(r)) {
4022
- ranges.push(r);
4023
- }
4024
- }
4025
- if (ranges.length === 0)
4026
- return null;
4027
- ranges.sort();
4028
- return { name, requiredVersions: ranges };
4029
- }
4030
- function buildTypedCycles(raw) {
4031
- const typed = [];
4032
- for (const entry of raw) {
4033
- const decoded = decodeCycleEntryForEnvelope(entry);
4034
- if (!decoded)
4035
- return raw.slice();
4036
- typed.push({ cycle: decoded });
4037
- }
4038
- return typed;
4039
- }
4040
- function decodeCycleEntryForEnvelope(raw) {
4041
- if (Array.isArray(raw) && raw.every((x) => typeof x === "string")) {
4042
- return raw;
4043
- }
4044
- if (!raw || typeof raw !== "object")
4045
- return null;
4046
- const obj = raw;
4047
- const source = obj.cycle ?? obj.packages ?? obj.path;
4048
- if (!Array.isArray(source))
4049
- return null;
4050
- const names = source.filter((x) => typeof x === "string");
4051
- return names.length > 0 ? names : null;
4052
- }
4053
4494
  function buildGroup(group) {
4054
4495
  const lean = {
4055
4496
  name: group.name,
@@ -4295,49 +4736,23 @@ function formatConflictsAndCycles(payload, verbose, useColors) {
4295
4736
  return "";
4296
4737
  if (conflicts.length > 0) {
4297
4738
  lines.push(colorize(`Conflicts (${conflicts.length}):`, "yellow", useColors));
4298
- const typed = isTypedConflictArray(conflicts) ? conflicts : null;
4299
- if (typed) {
4300
- const nameWidth = Math.max(...typed.map((c) => c.name.length));
4301
- const sorted = [...typed].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
4302
- for (const c of sorted) {
4303
- const padded = `${c.name}:`.padEnd(nameWidth + 2);
4304
- lines.push(` ${padded} ${c.requiredVersions.join(", ")}`);
4305
- }
4306
- } else {
4307
- for (const c of conflicts)
4308
- lines.push(` ${JSON.stringify(c)}`);
4739
+ const nameWidth = Math.max(...conflicts.map((c) => c.name.length));
4740
+ const sorted = [...conflicts].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
4741
+ for (const c of sorted) {
4742
+ const padded = `${c.name}:`.padEnd(nameWidth + 2);
4743
+ lines.push(` ${padded} ${c.requiredVersions.join(", ")}`);
4309
4744
  }
4310
4745
  }
4311
4746
  if (cycles.length > 0) {
4312
4747
  if (conflicts.length > 0)
4313
4748
  lines.push("");
4314
4749
  lines.push(colorize(`Circular dependencies (${cycles.length}):`, "red", useColors));
4315
- const typed = isTypedCycleArray(cycles) ? cycles : null;
4316
- if (typed) {
4317
- for (const c of typed)
4318
- lines.push(` ${c.cycle.join(" → ")}`);
4319
- } else {
4320
- for (const c of cycles)
4321
- lines.push(` ${JSON.stringify(c)}`);
4322
- }
4750
+ for (const c of cycles)
4751
+ lines.push(` ${c.cycle.join(" → ")}`);
4323
4752
  }
4324
4753
  return lines.join(`
4325
4754
  `);
4326
4755
  }
4327
- function isTypedConflictArray(arr) {
4328
- const first = arr[0];
4329
- if (!first || typeof first !== "object")
4330
- return false;
4331
- const obj = first;
4332
- return typeof obj.name === "string" && Array.isArray(obj.requiredVersions);
4333
- }
4334
- function isTypedCycleArray(arr) {
4335
- const first = arr[0];
4336
- if (!first || typeof first !== "object")
4337
- return false;
4338
- const obj = first;
4339
- return Array.isArray(obj.cycle);
4340
- }
4341
4756
  function formatGroupsBlock(payload, verbose, useColors) {
4342
4757
  const groups = payload.groups;
4343
4758
  const lines = [];
@@ -4350,10 +4765,10 @@ function formatGroupsBlock(payload, verbose, useColors) {
4350
4765
  const groupNoun = groups.items.length === 1 ? "group" : "groups";
4351
4766
  lines.push(colorize(`${groups.items.length} ${groupNoun} (${summary}):`, "bold", useColors));
4352
4767
  lines.push("");
4353
- if (verbose && groups.environmentConstraints && groups.environmentConstraints.length > 0) {
4354
- lines.push(dim(`environmentConstraints (${groups.environmentConstraints.length}):`, useColors));
4355
- for (const entry of groups.environmentConstraints) {
4356
- lines.push(dim(` ${JSON.stringify(entry)}`, useColors));
4768
+ if (verbose && groups.environmentMarkers && groups.environmentMarkers.length > 0) {
4769
+ lines.push(dim(`environmentMarkers (${groups.environmentMarkers.length}):`, useColors));
4770
+ for (const marker of groups.environmentMarkers) {
4771
+ lines.push(dim(` ${formatEnvironmentMarker(marker)}`, useColors));
4357
4772
  }
4358
4773
  lines.push("");
4359
4774
  }
@@ -4382,6 +4797,15 @@ function formatGroupsBlock(payload, verbose, useColors) {
4382
4797
  return lines.join(`
4383
4798
  `).trimEnd();
4384
4799
  }
4800
+ function formatEnvironmentMarker(marker) {
4801
+ if (marker.type && marker.value)
4802
+ return `${marker.type}: ${marker.value}`;
4803
+ if (marker.value)
4804
+ return marker.value;
4805
+ if (marker.type)
4806
+ return marker.type;
4807
+ return marker.raw ?? "(empty marker)";
4808
+ }
4385
4809
  function formatGroupHeading(group, registry) {
4386
4810
  if (group.conditionType === "always") {
4387
4811
  return group.name;
@@ -4447,101 +4871,6 @@ function sortAlphabetically(items, key) {
4447
4871
  return ka < kb ? -1 : ka > kb ? 1 : 0;
4448
4872
  });
4449
4873
  }
4450
- function decodeDag(raw) {
4451
- if (!raw || typeof raw !== "object")
4452
- return null;
4453
- const obj = raw;
4454
- const rawNodes = obj.n ?? obj.nodes;
4455
- const rawEdges = obj.e ?? obj.edges;
4456
- const nodes = decodeNodes(rawNodes);
4457
- if (!nodes)
4458
- return null;
4459
- const edges = decodeEdges(rawEdges);
4460
- if (!edges)
4461
- return null;
4462
- return { nodes, edges };
4463
- }
4464
- function decodeNodes(raw) {
4465
- if (Array.isArray(raw)) {
4466
- const result = [];
4467
- for (const entry of raw) {
4468
- if (!Array.isArray(entry)) {
4469
- if (typeof entry === "object" && entry !== null) {
4470
- const n = decodeObjectNode(entry);
4471
- if (!n)
4472
- return null;
4473
- result.push(n);
4474
- continue;
4475
- }
4476
- return null;
4477
- }
4478
- const [registry, name, version2] = entry;
4479
- if (typeof name !== "string")
4480
- return null;
4481
- result.push({
4482
- name,
4483
- version: typeof version2 === "string" ? version2 : undefined,
4484
- registry: typeof registry === "string" ? registry : undefined
4485
- });
4486
- }
4487
- return result;
4488
- }
4489
- if (raw && typeof raw === "object") {
4490
- const result = [];
4491
- for (const entry of Object.values(raw)) {
4492
- if (!entry || typeof entry !== "object")
4493
- return null;
4494
- const n = decodeObjectNode(entry);
4495
- if (!n)
4496
- return null;
4497
- result.push(n);
4498
- }
4499
- return result;
4500
- }
4501
- return null;
4502
- }
4503
- function decodeObjectNode(entry) {
4504
- const name = typeof entry.n === "string" ? entry.n : typeof entry.name === "string" ? entry.name : null;
4505
- if (!name)
4506
- return null;
4507
- const version2 = typeof entry.v === "string" ? entry.v : typeof entry.version === "string" ? entry.version : undefined;
4508
- return { name, version: version2 };
4509
- }
4510
- function decodeEdges(raw) {
4511
- if (!Array.isArray(raw))
4512
- return null;
4513
- const out = [];
4514
- for (const entry of raw) {
4515
- if (Array.isArray(entry)) {
4516
- const [from, to, constraint, lifecycle] = entry;
4517
- if (typeof from !== "number" || typeof to !== "number")
4518
- return null;
4519
- out.push({
4520
- fromIdx: from,
4521
- toIdx: to,
4522
- constraint: typeof constraint === "string" ? constraint : undefined,
4523
- lifecycle: typeof lifecycle === "string" ? lifecycle : undefined
4524
- });
4525
- continue;
4526
- }
4527
- if (entry && typeof entry === "object") {
4528
- const obj = entry;
4529
- const from = obj.f ?? obj.from;
4530
- const to = obj.t ?? obj.to;
4531
- if (typeof from !== "number" || typeof to !== "number")
4532
- return null;
4533
- out.push({
4534
- fromIdx: from,
4535
- toIdx: to,
4536
- constraint: typeof obj.c === "string" ? obj.c : undefined,
4537
- lifecycle: typeof obj.l === "string" ? obj.l : undefined
4538
- });
4539
- continue;
4540
- }
4541
- return null;
4542
- }
4543
- return out;
4544
- }
4545
4874
  // src/shared/package-intelligence-error-map.ts
4546
4875
  function mapPackageIntelligenceError(error2) {
4547
4876
  const mapped = classify2(error2);
@@ -7117,6 +7446,113 @@ function registerFeedbackCommand(program) {
7117
7446
  });
7118
7447
  }
7119
7448
  // src/commands/init/setup-handlers.ts
7449
+ function isPlainObject(value) {
7450
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7451
+ }
7452
+ function deepEqual(left, right) {
7453
+ if (Object.is(left, right)) {
7454
+ return true;
7455
+ }
7456
+ if (Array.isArray(left) && Array.isArray(right)) {
7457
+ if (left.length !== right.length) {
7458
+ return false;
7459
+ }
7460
+ for (let i = 0;i < left.length; i++) {
7461
+ if (!deepEqual(left[i], right[i])) {
7462
+ return false;
7463
+ }
7464
+ }
7465
+ return true;
7466
+ }
7467
+ if (isPlainObject(left) && isPlainObject(right)) {
7468
+ const leftKeys = Object.keys(left).sort();
7469
+ const rightKeys = Object.keys(right).sort();
7470
+ if (!deepEqual(leftKeys, rightKeys)) {
7471
+ return false;
7472
+ }
7473
+ for (const key of leftKeys) {
7474
+ if (!deepEqual(left[key], right[key])) {
7475
+ return false;
7476
+ }
7477
+ }
7478
+ return true;
7479
+ }
7480
+ return false;
7481
+ }
7482
+ function isStringArray2(value) {
7483
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
7484
+ }
7485
+ function isGitHitsPackageToken(token) {
7486
+ return token.toLowerCase() === "githits@latest";
7487
+ }
7488
+ function isLocalGitHitsInvocation(invocation) {
7489
+ if (invocation.length === 5) {
7490
+ const [command, yesFlag, packageToken, subcommand, action] = invocation;
7491
+ return command === "npx" && yesFlag === "-y" && typeof packageToken === "string" && isGitHitsPackageToken(packageToken) && subcommand === "mcp" && action === "start";
7492
+ }
7493
+ return false;
7494
+ }
7495
+ function extractInvocation(config) {
7496
+ if (!isPlainObject(config)) {
7497
+ return null;
7498
+ }
7499
+ const command = config.command;
7500
+ if (typeof command === "string") {
7501
+ const args = config.args;
7502
+ if (!isStringArray2(args)) {
7503
+ return null;
7504
+ }
7505
+ return [command, ...args];
7506
+ }
7507
+ if (isStringArray2(command)) {
7508
+ return [...command];
7509
+ }
7510
+ return null;
7511
+ }
7512
+ function nonCommandFieldsEqual(existing, expected) {
7513
+ for (const [key, value] of Object.entries(expected)) {
7514
+ if (key === "command" || key === "args") {
7515
+ continue;
7516
+ }
7517
+ if (!deepEqual(existing[key], value)) {
7518
+ return false;
7519
+ }
7520
+ }
7521
+ return true;
7522
+ }
7523
+ function hasLegacyRemoteIndicators(existing, expected) {
7524
+ if ("url" in existing || "serverUrl" in existing) {
7525
+ return true;
7526
+ }
7527
+ if ("type" in existing && !("type" in expected) && (existing.type === "http" || existing.type === "streamableHttp")) {
7528
+ return true;
7529
+ }
7530
+ return false;
7531
+ }
7532
+ function isEquivalentConfiguredValue(existing, expected) {
7533
+ if (deepEqual(existing, expected)) {
7534
+ return true;
7535
+ }
7536
+ if (!isPlainObject(existing)) {
7537
+ return false;
7538
+ }
7539
+ const expectedInvocation = extractInvocation(expected);
7540
+ const existingInvocation = extractInvocation(existing);
7541
+ if (!expectedInvocation || !existingInvocation) {
7542
+ return false;
7543
+ }
7544
+ if (!isLocalGitHitsInvocation(expectedInvocation) || !isLocalGitHitsInvocation(existingInvocation)) {
7545
+ return false;
7546
+ }
7547
+ if (hasLegacyRemoteIndicators(existing, expected)) {
7548
+ return false;
7549
+ }
7550
+ return nonCommandFieldsEqual(existing, expected);
7551
+ }
7552
+ function getMatchingServerKeys(servers, serverName) {
7553
+ const normalizedTarget = serverName.toLowerCase();
7554
+ return Object.keys(servers).filter((key) => key.toLowerCase() === normalizedTarget);
7555
+ }
7120
7556
  function mergeServerConfig(existingContent, serversKey, serverName, serverConfig) {
7121
7557
  let content = existingContent;
7122
7558
  if (content.charCodeAt(0) === 65279) {
@@ -7152,12 +7588,17 @@ function mergeServerConfig(existingContent, serversKey, serverName, serverConfig
7152
7588
  };
7153
7589
  }
7154
7590
  const serversObj = servers;
7155
- if (serverName in serversObj) {
7591
+ const matchingKeys = getMatchingServerKeys(serversObj, serverName);
7592
+ if (matchingKeys.length === 1 && matchingKeys[0] === serverName && isEquivalentConfiguredValue(serversObj[serverName], serverConfig)) {
7156
7593
  return { status: "already_configured" };
7157
7594
  }
7595
+ for (const key of matchingKeys) {
7596
+ delete serversObj[key];
7597
+ }
7598
+ const hadExisting = matchingKeys.length > 0;
7158
7599
  serversObj[serverName] = serverConfig;
7159
7600
  return {
7160
- status: "added",
7601
+ status: hadExisting ? "updated" : "added",
7161
7602
  content: `${JSON.stringify(config, null, 2)}
7162
7603
  `
7163
7604
  };
@@ -7195,7 +7636,12 @@ async function isAlreadyConfigured(config, fs) {
7195
7636
  if (typeof servers !== "object" || servers === null || Array.isArray(servers)) {
7196
7637
  return false;
7197
7638
  }
7198
- return config.serverName in servers;
7639
+ const serversObj = servers;
7640
+ const matchingKeys = getMatchingServerKeys(serversObj, config.serverName);
7641
+ if (matchingKeys.length !== 1 || matchingKeys[0] !== config.serverName) {
7642
+ return false;
7643
+ }
7644
+ return isEquivalentConfiguredValue(serversObj[config.serverName], config.serverConfig);
7199
7645
  } catch {
7200
7646
  return false;
7201
7647
  }
@@ -7325,6 +7771,13 @@ async function executeConfigFileSetup(setup, fs) {
7325
7771
  }
7326
7772
 
7327
7773
  // src/commands/init/agent-definitions.ts
7774
+ var GITHITS_SERVER_NAME = "GitHits";
7775
+ var GITHITS_MCP_COMMAND = "npx";
7776
+ var GITHITS_MCP_ARGS = ["-y", "githits@latest", "mcp", "start"];
7777
+ var GITHITS_MCP_INVOCATION = [
7778
+ GITHITS_MCP_COMMAND,
7779
+ ...GITHITS_MCP_ARGS
7780
+ ];
7328
7781
  function getAppDataPath(fs, appName) {
7329
7782
  const home = fs.getHomeDir();
7330
7783
  switch (process.platform) {
@@ -7380,8 +7833,11 @@ var cursor = {
7380
7833
  method: "config-file",
7381
7834
  configPath: fs.joinPath(fs.getHomeDir(), ".cursor", "mcp.json"),
7382
7835
  serversKey: "mcpServers",
7383
- serverName: "GitHits",
7384
- serverConfig: { url: getMcpUrl() }
7836
+ serverName: GITHITS_SERVER_NAME,
7837
+ serverConfig: {
7838
+ command: GITHITS_MCP_COMMAND,
7839
+ args: [...GITHITS_MCP_ARGS]
7840
+ }
7385
7841
  })
7386
7842
  };
7387
7843
  var windsurf = {
@@ -7394,8 +7850,11 @@ var windsurf = {
7394
7850
  method: "config-file",
7395
7851
  configPath: fs.joinPath(fs.getHomeDir(), ".codeium", "windsurf", "mcp_config.json"),
7396
7852
  serversKey: "mcpServers",
7397
- serverName: "GitHits",
7398
- serverConfig: { serverUrl: getMcpUrl() }
7853
+ serverName: GITHITS_SERVER_NAME,
7854
+ serverConfig: {
7855
+ command: GITHITS_MCP_COMMAND,
7856
+ args: [...GITHITS_MCP_ARGS]
7857
+ }
7399
7858
  })
7400
7859
  };
7401
7860
  var claudeDesktop = {
@@ -7422,10 +7881,10 @@ var claudeDesktop = {
7422
7881
  method: "config-file",
7423
7882
  configPath: fs.joinPath(appData, "claude_desktop_config.json"),
7424
7883
  serversKey: "mcpServers",
7425
- serverName: "GitHits",
7884
+ serverName: GITHITS_SERVER_NAME,
7426
7885
  serverConfig: {
7427
- command: "npx",
7428
- args: ["-y", "mcp-remote", getMcpUrl()]
7886
+ command: GITHITS_MCP_COMMAND,
7887
+ args: [...GITHITS_MCP_ARGS]
7429
7888
  }
7430
7889
  };
7431
7890
  }
@@ -7441,17 +7900,7 @@ var codexCli = {
7441
7900
  commands: [
7442
7901
  {
7443
7902
  command: "codex",
7444
- args: [
7445
- "mcp",
7446
- "add",
7447
- "githits",
7448
- "--",
7449
- "npx",
7450
- "-y",
7451
- "githits@latest",
7452
- "mcp",
7453
- "start"
7454
- ]
7903
+ args: ["mcp", "add", "githits", "--", ...GITHITS_MCP_INVOCATION]
7455
7904
  }
7456
7905
  ],
7457
7906
  checkCommand: {
@@ -7476,8 +7925,11 @@ var vscode = {
7476
7925
  method: "config-file",
7477
7926
  configPath: fs.joinPath(appData, "User", "mcp.json"),
7478
7927
  serversKey: "servers",
7479
- serverName: "GitHits",
7480
- serverConfig: { url: getMcpUrl(), type: "http" }
7928
+ serverName: GITHITS_SERVER_NAME,
7929
+ serverConfig: {
7930
+ command: GITHITS_MCP_COMMAND,
7931
+ args: [...GITHITS_MCP_ARGS]
7932
+ }
7481
7933
  };
7482
7934
  }
7483
7935
  };
@@ -7491,8 +7943,11 @@ var cline = {
7491
7943
  method: "config-file",
7492
7944
  configPath: fs.joinPath(fs.getHomeDir(), ".cline", "data", "settings", "cline_mcp_settings.json"),
7493
7945
  serversKey: "mcpServers",
7494
- serverName: "GitHits",
7495
- serverConfig: { url: getMcpUrl(), type: "streamableHttp" }
7946
+ serverName: GITHITS_SERVER_NAME,
7947
+ serverConfig: {
7948
+ command: GITHITS_MCP_COMMAND,
7949
+ args: [...GITHITS_MCP_ARGS]
7950
+ }
7496
7951
  })
7497
7952
  };
7498
7953
  var geminiCli = {
@@ -7536,8 +7991,11 @@ var googleAntigravity = {
7536
7991
  method: "config-file",
7537
7992
  configPath: fs.joinPath(fs.getHomeDir(), ".gemini", "antigravity", "mcp_config.json"),
7538
7993
  serversKey: "mcpServers",
7539
- serverName: "GitHits",
7540
- serverConfig: { serverUrl: getMcpUrl() }
7994
+ serverName: GITHITS_SERVER_NAME,
7995
+ serverConfig: {
7996
+ command: GITHITS_MCP_COMMAND,
7997
+ args: [...GITHITS_MCP_ARGS]
7998
+ }
7541
7999
  })
7542
8000
  };
7543
8001
  var openCode = {
@@ -7550,10 +8008,10 @@ var openCode = {
7550
8008
  method: "config-file",
7551
8009
  configPath: process.platform === "win32" ? fs.joinPath(process.env.APPDATA ?? fs.joinPath(fs.getHomeDir(), "AppData", "Roaming"), "opencode", "opencode.json") : fs.joinPath(fs.getHomeDir(), ".config", "opencode", "opencode.json"),
7552
8010
  serversKey: "mcp",
7553
- serverName: "GitHits",
8011
+ serverName: GITHITS_SERVER_NAME,
7554
8012
  serverConfig: {
7555
8013
  type: "local",
7556
- command: ["npx", "-y", "githits@latest", "mcp", "start"],
8014
+ command: [...GITHITS_MCP_INVOCATION],
7557
8015
  enabled: true
7558
8016
  }
7559
8017
  })
@@ -9142,8 +9600,23 @@ function createMcpServer(deps) {
9142
9600
  return server;
9143
9601
  }
9144
9602
  async function startMcpServer(deps) {
9603
+ setClientMode("mcp");
9145
9604
  const server = createMcpServer(deps);
9146
9605
  const transport = new StdioServerTransport;
9606
+ setMcpClientVersionProvider(() => {
9607
+ try {
9608
+ const clientVersion = server.server.getClientVersion();
9609
+ if (!clientVersion?.name || typeof clientVersion.name !== "string" || clientVersion.name.trim().length === 0) {
9610
+ return;
9611
+ }
9612
+ const name = clientVersion.name.trim();
9613
+ const rawVersion = clientVersion.version;
9614
+ const versionOut = typeof rawVersion === "string" && rawVersion.trim().length > 0 ? rawVersion.trim() : undefined;
9615
+ return { name, version: versionOut };
9616
+ } catch {
9617
+ return;
9618
+ }
9619
+ });
9147
9620
  await server.connect(transport);
9148
9621
  }
9149
9622
  function showMcpSetupInstructions() {
@@ -9652,10 +10125,20 @@ function registerSearchCommand(program) {
9652
10125
  }
9653
10126
  // src/cli.ts
9654
10127
  var program = new Command;
9655
- program.name("githits").description("Code examples from global open source for your AI assistant").version(version).option("--no-color", "Disable colored output").hook("preAction", (thisCommand) => {
10128
+ var commandSpans = new WeakMap;
10129
+ if (isTelemetryEnabled()) {
10130
+ process.once("exit", (exitCode) => {
10131
+ flushTelemetry(exitCode);
10132
+ });
10133
+ }
10134
+ program.name("githits").description("Code examples from global open source for your AI assistant").version(version).option("--no-color", "Disable colored output").hook("preAction", (thisCommand, actionCommand) => {
9656
10135
  if (thisCommand.opts().color === false) {
9657
10136
  process.env.NO_COLOR = "1";
9658
10137
  }
10138
+ const command = actionCommand ?? thisCommand;
10139
+ commandSpans.set(command, startTelemetrySpan(getTelemetryCommandName(command)));
10140
+ }).hook("postAction", (_thisCommand, actionCommand) => {
10141
+ endTelemetrySpan(commandSpans.get(actionCommand));
9659
10142
  }).addHelpText("after", `
9660
10143
  Getting started:
9661
10144
  githits init Set up MCP for your coding agents
@@ -9675,15 +10158,27 @@ registerLanguagesCommand(program);
9675
10158
  registerFeedbackCommand(program);
9676
10159
  var argv = process.argv.slice(2);
9677
10160
  if (shouldEagerLoadGatedCommandGroup(argv, "code")) {
9678
- await registerCodeCommandGroup(program);
10161
+ await withTelemetrySpan("cli.register.code-group", () => registerCodeCommandGroup(program));
9679
10162
  }
9680
10163
  if (shouldEagerLoadGatedCommandGroup(argv, "pkg")) {
9681
- await registerPkgCommandGroup(program);
10164
+ await withTelemetrySpan("cli.register.pkg-group", () => registerPkgCommandGroup(program));
9682
10165
  }
9683
10166
  var authCommand = program.command("auth").summary("Manage authentication").description("Manage authentication with GitHits.");
9684
10167
  registerAuthStatusCommand(authCommand);
9685
- await program.parseAsync();
10168
+ await withTelemetrySpan("cli.parse", () => program.parseAsync());
9686
10169
  function shouldEagerLoadGatedCommandGroup(args, groupName) {
9687
10170
  const [firstArg] = args;
9688
10171
  return firstArg === groupName || firstArg === "help" || firstArg === "--help" || firstArg === "-h";
9689
10172
  }
10173
+ function getTelemetryCommandName(command) {
10174
+ const names = [];
10175
+ let current = command;
10176
+ while (current) {
10177
+ const name = current.name();
10178
+ if (name && name !== "githits") {
10179
+ names.unshift(name);
10180
+ }
10181
+ current = current.parent ?? null;
10182
+ }
10183
+ return `command.${names.join(".")}`;
10184
+ }