@pkgseer/cli 0.4.3 → 0.4.8

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-d3mfgdr9.js";
4
+ } from "./shared/chunk-edcjw6wf.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -670,7 +670,7 @@ var FindSymbolDocument = gql`
670
670
  waitTimeoutMs: $waitTimeoutMs
671
671
  ) {
672
672
  symbols {
673
- symbolId
673
+ symbolRef
674
674
  name
675
675
  qualifiedPath
676
676
  kind
@@ -720,7 +720,7 @@ var SearchSymbolsDocument = gql`
720
720
  contentPreview
721
721
  code
722
722
  language
723
- symbolId
723
+ symbolRef
724
724
  qualifiedPath
725
725
  kind
726
726
  arity
@@ -757,7 +757,7 @@ var ListSymbolsDocument = gql`
757
757
  waitTimeoutMs: $waitTimeoutMs
758
758
  ) {
759
759
  symbols {
760
- symbolId
760
+ symbolRef
761
761
  name
762
762
  qualifiedPath
763
763
  kind
@@ -779,7 +779,7 @@ var ListSymbolsDocument = gql`
779
779
  }
780
780
  `;
781
781
  var SymbolDependenciesDocument = gql`
782
- query SymbolDependencies($symbol: SymbolReferenceInput!, $maxDepth: Int, $maxResults: Int, $includeExternal: Boolean, $includeBuiltins: Boolean, $mode: NavigationMode, $waitTimeoutMs: Int) {
782
+ query SymbolDependencies($symbol: SymbolReferenceInput!, $maxDepth: Int, $maxResults: Int, $includeExternal: Boolean, $includeBuiltins: Boolean, $mode: NavigationMode, $waitTimeoutMs: Int, $version: String) {
783
783
  symbolDependencies(
784
784
  symbol: $symbol
785
785
  maxDepth: $maxDepth
@@ -788,14 +788,15 @@ var SymbolDependenciesDocument = gql`
788
788
  includeBuiltins: $includeBuiltins
789
789
  mode: $mode
790
790
  waitTimeoutMs: $waitTimeoutMs
791
+ version: $version
791
792
  ) {
792
793
  root {
793
- symbolId
794
+ symbolRef
794
795
  name
795
796
  qualifiedPath
796
797
  }
797
798
  dependencies {
798
- symbolId
799
+ symbolRef
799
800
  name
800
801
  qualifiedPath
801
802
  kind
@@ -817,21 +818,22 @@ var SymbolDependenciesDocument = gql`
817
818
  }
818
819
  `;
819
820
  var SymbolDependentsDocument = gql`
820
- query SymbolDependents($symbol: SymbolReferenceInput!, $samePackageOnly: Boolean, $maxResults: Int, $mode: NavigationMode, $waitTimeoutMs: Int) {
821
+ query SymbolDependents($symbol: SymbolReferenceInput!, $samePackageOnly: Boolean, $maxResults: Int, $maxDepth: Int, $mode: NavigationMode, $waitTimeoutMs: Int) {
821
822
  symbolDependents(
822
823
  symbol: $symbol
823
824
  samePackageOnly: $samePackageOnly
824
825
  maxResults: $maxResults
826
+ maxDepth: $maxDepth
825
827
  mode: $mode
826
828
  waitTimeoutMs: $waitTimeoutMs
827
829
  ) {
828
830
  target {
829
- symbolId
831
+ symbolRef
830
832
  name
831
833
  qualifiedPath
832
834
  }
833
835
  dependents {
834
- symbolId
836
+ symbolRef
835
837
  name
836
838
  qualifiedPath
837
839
  kind
@@ -887,19 +889,19 @@ var CallPathDocument = gql`
887
889
  ) {
888
890
  pathFound
889
891
  fromSymbol {
890
- symbolId
892
+ symbolRef
891
893
  name
892
894
  qualifiedPath
893
895
  }
894
896
  toSymbol {
895
- symbolId
897
+ symbolRef
896
898
  name
897
899
  qualifiedPath
898
900
  }
899
901
  paths {
900
902
  length
901
903
  hops {
902
- symbolId
904
+ symbolRef
903
905
  name
904
906
  qualifiedPath
905
907
  filePath
@@ -1040,7 +1042,7 @@ var VersionDiffDocument = gql`
1040
1042
  endLine
1041
1043
  isPublic
1042
1044
  minArity
1043
- symbolId
1045
+ symbolRef
1044
1046
  }
1045
1047
  to {
1046
1048
  contentHash
@@ -1050,7 +1052,7 @@ var VersionDiffDocument = gql`
1050
1052
  endLine
1051
1053
  isPublic
1052
1054
  minArity
1053
- symbolId
1055
+ symbolRef
1054
1056
  }
1055
1057
  }
1056
1058
  hasMore
@@ -1219,7 +1221,7 @@ function createClient(apiToken) {
1219
1221
  if (apiToken) {
1220
1222
  headers.Authorization = `Bearer ${apiToken}`;
1221
1223
  }
1222
- const client = new GraphQLClient(apiUrl, { headers });
1224
+ const client = new GraphQLClient(apiUrl, { headers, errorPolicy: "all" });
1223
1225
  return getSdk(client);
1224
1226
  }
1225
1227
 
@@ -1392,7 +1394,7 @@ class AuthStorageImpl {
1392
1394
  this.configDir = configDir ?? fs.joinPath(fs.getHomeDir(), CONFIG_DIR);
1393
1395
  this.authPath = fs.joinPath(this.configDir, AUTH_FILE);
1394
1396
  }
1395
- getPath() {
1397
+ getStorageLocation() {
1396
1398
  return this.authPath;
1397
1399
  }
1398
1400
  async load(baseUrl) {
@@ -1512,6 +1514,117 @@ class BrowserServiceImpl {
1512
1514
  await open(url);
1513
1515
  }
1514
1516
  }
1517
+ // src/services/chunking-keyring-service.ts
1518
+ var WINDOWS_MAX_ENTRY_SIZE = 1200;
1519
+ var CHUNKED_PREFIX = "CHUNKED:";
1520
+ var MAX_CHUNK_COUNT = 100;
1521
+ function chunkKey(account, writeId, index) {
1522
+ return `${account}:chunk:${writeId}:${index}`;
1523
+ }
1524
+ function parseChunkedSentinel(value) {
1525
+ if (!value.startsWith(CHUNKED_PREFIX))
1526
+ return null;
1527
+ const rest = value.slice(CHUNKED_PREFIX.length);
1528
+ const colonIndex = rest.indexOf(":");
1529
+ if (colonIndex === -1)
1530
+ return null;
1531
+ const writeId = rest.slice(0, colonIndex);
1532
+ if (writeId.length === 0)
1533
+ return null;
1534
+ const countStr = rest.slice(colonIndex + 1);
1535
+ const count = Number(countStr);
1536
+ if (!Number.isInteger(count) || count <= 0)
1537
+ return null;
1538
+ return { writeId, count };
1539
+ }
1540
+ function splitIntoChunks(value, maxSize) {
1541
+ if (value.length === 0)
1542
+ return [""];
1543
+ const chunks = [];
1544
+ for (let offset = 0;offset < value.length; offset += maxSize) {
1545
+ chunks.push(value.slice(offset, offset + maxSize));
1546
+ }
1547
+ return chunks;
1548
+ }
1549
+ function generateWriteId() {
1550
+ let id;
1551
+ do {
1552
+ id = Math.random().toString(36).slice(2, 8);
1553
+ } while (id.length < 6);
1554
+ return id;
1555
+ }
1556
+
1557
+ class ChunkingKeyringService {
1558
+ inner;
1559
+ maxEntrySize;
1560
+ constructor(inner, maxEntrySize = WINDOWS_MAX_ENTRY_SIZE) {
1561
+ this.inner = inner;
1562
+ this.maxEntrySize = maxEntrySize;
1563
+ }
1564
+ getPassword(service, account) {
1565
+ const value = this.inner.getPassword(service, account);
1566
+ if (value === null)
1567
+ return null;
1568
+ if (!value.startsWith(CHUNKED_PREFIX))
1569
+ return value;
1570
+ const sentinel = parseChunkedSentinel(value);
1571
+ if (sentinel === null)
1572
+ return null;
1573
+ const chunks = [];
1574
+ for (let i = 0;i < sentinel.count; i++) {
1575
+ const chunk = this.inner.getPassword(service, chunkKey(account, sentinel.writeId, i));
1576
+ if (chunk === null) {
1577
+ console.error(`Warning: Incomplete chunked keychain entry for "${account}" (missing chunk ${i} of ${sentinel.count}). Treating as missing.`);
1578
+ return null;
1579
+ }
1580
+ chunks.push(chunk);
1581
+ }
1582
+ return chunks.join("");
1583
+ }
1584
+ setPassword(service, account, password) {
1585
+ const oldValue = this.readOldSentinel(service, account);
1586
+ if (password.length <= this.maxEntrySize) {
1587
+ this.inner.setPassword(service, account, password);
1588
+ } else {
1589
+ const chunks = splitIntoChunks(password, this.maxEntrySize);
1590
+ if (chunks.length > MAX_CHUNK_COUNT) {
1591
+ throw new Error(`Value requires ${chunks.length} chunks, exceeding maximum of ${MAX_CHUNK_COUNT}. ` + `This likely indicates a bug — credential data should not be this large.`);
1592
+ }
1593
+ const writeId = generateWriteId();
1594
+ for (const [i, chunk] of chunks.entries()) {
1595
+ this.inner.setPassword(service, chunkKey(account, writeId, i), chunk);
1596
+ }
1597
+ this.inner.setPassword(service, account, `${CHUNKED_PREFIX}${writeId}:${chunks.length}`);
1598
+ }
1599
+ if (oldValue !== null) {
1600
+ this.deleteChunkEntries(service, account, oldValue);
1601
+ }
1602
+ }
1603
+ deletePassword(service, account) {
1604
+ const oldValue = this.readOldSentinel(service, account);
1605
+ if (oldValue !== null) {
1606
+ this.deleteChunkEntries(service, account, oldValue);
1607
+ }
1608
+ return this.inner.deletePassword(service, account);
1609
+ }
1610
+ readOldSentinel(service, account) {
1611
+ try {
1612
+ const value = this.inner.getPassword(service, account);
1613
+ if (value === null)
1614
+ return null;
1615
+ return parseChunkedSentinel(value);
1616
+ } catch {
1617
+ return null;
1618
+ }
1619
+ }
1620
+ deleteChunkEntries(service, account, sentinel) {
1621
+ for (let i = 0;i < sentinel.count; i++) {
1622
+ try {
1623
+ this.inner.deletePassword(service, chunkKey(account, sentinel.writeId, i));
1624
+ } catch {}
1625
+ }
1626
+ }
1627
+ }
1515
1628
  // src/services/config-service.ts
1516
1629
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
1517
1630
  import { z } from "zod";
@@ -1770,6 +1883,143 @@ class GitServiceImpl {
1770
1883
  return existsSync(gitPath);
1771
1884
  }
1772
1885
  }
1886
+ // src/services/keychain-auth-storage.ts
1887
+ var SERVICE_NAME = "pkgseer";
1888
+ var TOKEN_PREFIX = "v1:tokens:";
1889
+ function normalizeBaseUrl2(url) {
1890
+ return url.replace(/\/+$/, "");
1891
+ }
1892
+ function parseJsonOrNull(json) {
1893
+ if (json === null)
1894
+ return null;
1895
+ try {
1896
+ const parsed = JSON.parse(json);
1897
+ if (typeof parsed !== "object" || parsed === null)
1898
+ return null;
1899
+ return parsed;
1900
+ } catch {
1901
+ return null;
1902
+ }
1903
+ }
1904
+ function isValidTokenData(data) {
1905
+ if (typeof data !== "object" || data === null)
1906
+ return false;
1907
+ const d = data;
1908
+ return typeof d.token === "string" && d.token.length > 0 && typeof d.tokenName === "string" && d.tokenName.length > 0 && Array.isArray(d.scopes) && typeof d.createdAt === "string" && d.createdAt.length > 0 && (d.expiresAt === null || typeof d.expiresAt === "string" && d.expiresAt.length > 0) && typeof d.apiKeyId === "number";
1909
+ }
1910
+
1911
+ class KeychainAuthStorage {
1912
+ keyring;
1913
+ constructor(keyring) {
1914
+ this.keyring = keyring;
1915
+ }
1916
+ async load(baseUrl) {
1917
+ const key = `${TOKEN_PREFIX}${normalizeBaseUrl2(baseUrl)}`;
1918
+ const json = this.keyring.getPassword(SERVICE_NAME, key);
1919
+ const data = parseJsonOrNull(json);
1920
+ if (data !== null && !isValidTokenData(data))
1921
+ return null;
1922
+ return data;
1923
+ }
1924
+ async save(baseUrl, data) {
1925
+ const key = `${TOKEN_PREFIX}${normalizeBaseUrl2(baseUrl)}`;
1926
+ this.keyring.setPassword(SERVICE_NAME, key, JSON.stringify(data));
1927
+ }
1928
+ async clear(baseUrl) {
1929
+ const key = `${TOKEN_PREFIX}${normalizeBaseUrl2(baseUrl)}`;
1930
+ this.keyring.deletePassword(SERVICE_NAME, key);
1931
+ }
1932
+ getStorageLocation() {
1933
+ switch (process.platform) {
1934
+ case "darwin":
1935
+ return "macOS Keychain (pkgseer)";
1936
+ case "win32":
1937
+ return "Windows Credential Manager (pkgseer)";
1938
+ default:
1939
+ return "System keychain (pkgseer)";
1940
+ }
1941
+ }
1942
+ }
1943
+ // src/services/keyring-service.ts
1944
+ import { Entry } from "@napi-rs/keyring";
1945
+
1946
+ class KeychainUnavailableError extends Error {
1947
+ constructor(message, cause) {
1948
+ super(message);
1949
+ this.name = "KeychainUnavailableError";
1950
+ this.cause = cause;
1951
+ }
1952
+ }
1953
+ function wrapKeyringError(error) {
1954
+ const message = error instanceof Error ? error.message : String(error);
1955
+ throw new KeychainUnavailableError(`System keychain unavailable: ${message}`, error);
1956
+ }
1957
+
1958
+ class KeyringServiceImpl {
1959
+ getPassword(service, account) {
1960
+ try {
1961
+ return new Entry(service, account).getPassword();
1962
+ } catch (error) {
1963
+ wrapKeyringError(error);
1964
+ }
1965
+ }
1966
+ setPassword(service, account, password) {
1967
+ try {
1968
+ new Entry(service, account).setPassword(password);
1969
+ } catch (error) {
1970
+ wrapKeyringError(error);
1971
+ }
1972
+ }
1973
+ deletePassword(service, account) {
1974
+ try {
1975
+ return new Entry(service, account).deleteCredential();
1976
+ } catch (error) {
1977
+ wrapKeyringError(error);
1978
+ }
1979
+ }
1980
+ }
1981
+ // src/services/migrating-auth-storage.ts
1982
+ class MigratingAuthStorage {
1983
+ primary;
1984
+ legacy;
1985
+ constructor(primary, legacy) {
1986
+ this.primary = primary;
1987
+ this.legacy = legacy;
1988
+ }
1989
+ async load(baseUrl) {
1990
+ const tokens = await this.primary.load(baseUrl);
1991
+ if (tokens)
1992
+ return tokens;
1993
+ const legacyTokens = await this.legacy.load(baseUrl);
1994
+ if (legacyTokens) {
1995
+ await this.primary.save(baseUrl, legacyTokens);
1996
+ try {
1997
+ await this.legacy.clear(baseUrl);
1998
+ } catch {}
1999
+ return legacyTokens;
2000
+ }
2001
+ return null;
2002
+ }
2003
+ async save(baseUrl, data) {
2004
+ await this.primary.save(baseUrl, data);
2005
+ }
2006
+ async clear(baseUrl) {
2007
+ let primaryError;
2008
+ try {
2009
+ await this.primary.clear(baseUrl);
2010
+ } catch (error) {
2011
+ primaryError = error;
2012
+ }
2013
+ try {
2014
+ await this.legacy.clear(baseUrl);
2015
+ } catch {}
2016
+ if (primaryError)
2017
+ throw primaryError;
2018
+ }
2019
+ getStorageLocation() {
2020
+ return this.primary.getStorageLocation();
2021
+ }
2022
+ }
1773
2023
  // src/services/pkgseer-service.ts
1774
2024
  class PkgseerServiceImpl {
1775
2025
  client;
@@ -2019,7 +2269,8 @@ class PkgseerServiceImpl {
2019
2269
  includeExternal: options?.includeExternal,
2020
2270
  includeBuiltins: options?.includeBuiltins,
2021
2271
  mode: options?.mode,
2022
- waitTimeoutMs: options?.waitTimeoutMs
2272
+ waitTimeoutMs: options?.waitTimeoutMs,
2273
+ version: options?.version
2023
2274
  });
2024
2275
  return { data: result.data, errors: result.errors };
2025
2276
  }
@@ -2027,6 +2278,7 @@ class PkgseerServiceImpl {
2027
2278
  const result = await this.client.SymbolDependents({
2028
2279
  symbol,
2029
2280
  samePackageOnly: options?.samePackageOnly,
2281
+ maxDepth: options?.maxDepth,
2030
2282
  maxResults: options?.maxResults,
2031
2283
  mode: options?.mode,
2032
2284
  waitTimeoutMs: options?.waitTimeoutMs
@@ -2317,10 +2569,29 @@ async function resolveApiToken(configService, baseUrl) {
2317
2569
  const tokenData = await configService.getApiToken(baseUrl);
2318
2570
  return tokenData?.token;
2319
2571
  }
2572
+ function createAuthStorage(fileSystemService) {
2573
+ const fileStorage = new AuthStorageImpl(fileSystemService);
2574
+ try {
2575
+ const rawKeyring = new KeyringServiceImpl;
2576
+ const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
2577
+ const probeKey = `__probe_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2578
+ keyring.setPassword("pkgseer", probeKey, "probe");
2579
+ try {
2580
+ keyring.deletePassword("pkgseer", probeKey);
2581
+ } catch {}
2582
+ const keychainStorage = new KeychainAuthStorage(keyring);
2583
+ return new MigratingAuthStorage(keychainStorage, fileStorage);
2584
+ } catch (error) {
2585
+ if (!(error instanceof KeychainUnavailableError))
2586
+ throw error;
2587
+ console.error("Warning: System keychain unavailable. Falling back to file-based credential storage.");
2588
+ return fileStorage;
2589
+ }
2590
+ }
2320
2591
  async function createContainer() {
2321
2592
  const baseUrl = getBaseUrl();
2322
2593
  const fileSystemService = new FileSystemServiceImpl;
2323
- const authStorage = new AuthStorageImpl(fileSystemService);
2594
+ const authStorage = createAuthStorage(fileSystemService);
2324
2595
  const configService = new ConfigServiceImpl(fileSystemService, authStorage);
2325
2596
  const [apiToken, configResult] = await Promise.all([
2326
2597
  resolveApiToken(configService, baseUrl),
@@ -2381,7 +2652,7 @@ async function authStatusAction(deps) {
2381
2652
  console.log(" Expires: never");
2382
2653
  }
2383
2654
  console.log(`
2384
- Stored at: ${authStorage.getPath()}`);
2655
+ Storage: ${authStorage.getStorageLocation()}`);
2385
2656
  }
2386
2657
  var STATUS_DESCRIPTION = `Show current authentication status.
2387
2658
 
@@ -2394,6 +2665,42 @@ function registerAuthStatusCommand(program) {
2394
2665
  });
2395
2666
  }
2396
2667
 
2668
+ // src/lib/error-classification.ts
2669
+ function classifyError(message) {
2670
+ const lower = message.toLowerCase();
2671
+ if (lower.includes("being indexed") || lower.includes("retry with waittimeoutms")) {
2672
+ return "being_indexed";
2673
+ }
2674
+ if (lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out")) {
2675
+ return "timeout";
2676
+ }
2677
+ if (lower.includes("not found") || lower.includes("does not exist")) {
2678
+ return "not_found";
2679
+ }
2680
+ if (lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("token") || lower.includes("authentication") || lower.includes("permission")) {
2681
+ return "auth";
2682
+ }
2683
+ if (lower.includes("rate limit") || lower.includes("too many requests")) {
2684
+ return "rate_limit";
2685
+ }
2686
+ return "unknown";
2687
+ }
2688
+
2689
+ // src/lib/registry.ts
2690
+ var REGISTRY_MAP = {
2691
+ npm: "NPM",
2692
+ pypi: "PYPI",
2693
+ hex: "HEX",
2694
+ crates: "CRATES",
2695
+ nuget: "NUGET",
2696
+ maven: "MAVEN",
2697
+ zig: "ZIG",
2698
+ vcpkg: "VCPKG"
2699
+ };
2700
+ function toGraphQLRegistry(registry) {
2701
+ return REGISTRY_MAP[registry.toLowerCase()] || "NPM";
2702
+ }
2703
+
2397
2704
  // src/commands/shared.ts
2398
2705
  function parsePackageSpec(spec) {
2399
2706
  let registry = "npm";
@@ -2425,19 +2732,6 @@ function parsePackageSpec(spec) {
2425
2732
  }
2426
2733
  return { registry, name: rest };
2427
2734
  }
2428
- function toGraphQLRegistry(registry) {
2429
- const map = {
2430
- npm: "NPM",
2431
- pypi: "PYPI",
2432
- hex: "HEX",
2433
- crates: "CRATES",
2434
- nuget: "NUGET",
2435
- maven: "MAVEN",
2436
- zig: "ZIG",
2437
- vcpkg: "VCPKG"
2438
- };
2439
- return map[registry.toLowerCase()] || "NPM";
2440
- }
2441
2735
  function output(data, json) {
2442
2736
  if (json) {
2443
2737
  console.log(JSON.stringify(data));
@@ -2453,30 +2747,20 @@ function outputError(message, json) {
2453
2747
  }
2454
2748
  process.exit(1);
2455
2749
  }
2750
+ var CLI_HINTS = {
2751
+ being_indexed: " Hint: package is being indexed. Try again with a longer --wait value (e.g., --wait 30000).",
2752
+ timeout: " Hint: lower the limit or narrow the scope, then retry.",
2753
+ not_found: " Hint: verify the name/registry and that the resource exists.",
2754
+ auth: " Hint: check login or token validity (pkgseer auth-status).",
2755
+ rate_limit: " Hint: wait and retry, or reduce request frequency.",
2756
+ unknown: ""
2757
+ };
2456
2758
  function handleErrors(errors, json) {
2457
2759
  if (!errors || errors.length === 0)
2458
2760
  return;
2459
2761
  const combined = errors.map((e) => e.message).filter(Boolean).join(", ");
2460
- const lower = combined.toLowerCase();
2461
- const isTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
2462
- const isNotFound = lower.includes("not found") || lower.includes("does not exist") || lower.includes("unknown");
2463
- const isAuth = lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("token") || lower.includes("authentication") || lower.includes("permission");
2464
- const isRateLimit = lower.includes("rate limit") || lower.includes("too many requests");
2465
- const isBeingIndexed = lower.includes("being indexed") || lower.includes("retry with waittimeoutms");
2466
- let hint = "";
2467
- if (isBeingIndexed) {
2468
- hint = " Hint: package is being indexed. Try again with a longer --wait value (e.g., --wait 30000).";
2469
- } else if (isTimeout) {
2470
- hint = " Hint: lower the limit or narrow the scope, then retry.";
2471
- } else if (isNotFound) {
2472
- hint = " Hint: verify the name/registry and that the resource exists.";
2473
- } else if (isAuth) {
2474
- hint = " Hint: check login or token validity (pkgseer auth-status).";
2475
- } else if (isRateLimit) {
2476
- hint = " Hint: wait and retry, or reduce request frequency.";
2477
- }
2478
- const message = `${combined}${hint}`;
2479
- outputError(message, json);
2762
+ const hint = CLI_HINTS[classifyError(combined)];
2763
+ outputError(`${combined}${hint}`, json);
2480
2764
  }
2481
2765
  function formatNumber(num) {
2482
2766
  if (num == null)
@@ -2565,21 +2849,10 @@ async function withCliErrorHandling(json, fn) {
2565
2849
  return;
2566
2850
  }
2567
2851
  const message = error instanceof Error ? error.message : "Unknown error";
2568
- const lower = message.toLowerCase();
2569
- if (lower.includes("being indexed") || lower.includes("retry with waittimeoutms")) {
2570
- outputError(`${message}. Hint: package is being indexed. Try again with a longer --wait value (e.g., --wait 30000).`, json);
2571
- return;
2572
- }
2573
- if (lower.includes("-32001") || lower.includes("timeout")) {
2574
- outputError(`${message}. Hint: lower the limit or narrow the scope, then retry.`, json);
2575
- return;
2576
- }
2577
- if (lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("token") || lower.includes("authentication") || lower.includes("permission")) {
2578
- outputError(`${message}. Hint: check login or token validity.`, json);
2579
- return;
2580
- }
2581
- if (lower.includes("rate limit") || lower.includes("too many requests")) {
2582
- outputError(`${message}. Hint: wait and retry.`, json);
2852
+ const kind = classifyError(message);
2853
+ const hint = CLI_HINTS[kind];
2854
+ if (hint) {
2855
+ outputError(`${message}.${hint}`, json);
2583
2856
  return;
2584
2857
  }
2585
2858
  const colonMatch = message.match(/^([^:]+):/);
@@ -2615,12 +2888,12 @@ function parseSymbolRef(spec) {
2615
2888
  packageName: parsed.name
2616
2889
  };
2617
2890
  }
2618
- function buildSymbolReference(spec, id) {
2619
- if (id) {
2620
- return { symbolId: Number.parseInt(id, 10) };
2891
+ function buildSymbolReference(spec, ref) {
2892
+ if (ref) {
2893
+ return { symbolRef: ref };
2621
2894
  }
2622
2895
  if (!spec) {
2623
- throw new Error("Symbol reference required. Use registry:package#symbolName or --id <number>");
2896
+ throw new Error("Symbol reference required. Use registry:package#symbolName or --ref <string>");
2624
2897
  }
2625
2898
  return parseSymbolRef(spec);
2626
2899
  }
@@ -2663,7 +2936,7 @@ function formatSymbol(symbol) {
2663
2936
  const loc = symbol.startLine ? `${symbol.filePath}:${symbol.startLine}` : symbol.filePath;
2664
2937
  parts.push(` File: ${loc}`);
2665
2938
  }
2666
- const meta = [`id:${symbol.symbolId}`];
2939
+ const meta = [`ref:${symbol.symbolRef}`];
2667
2940
  if (symbol.callerCount != null) {
2668
2941
  meta.push(`callers:${symbol.callerCount}`);
2669
2942
  }
@@ -2693,8 +2966,8 @@ function formatDependencyEntry(entry) {
2693
2966
  if (entry.package) {
2694
2967
  parts.push(` Package: ${entry.package.registry}:${entry.package.name}`);
2695
2968
  }
2696
- if (entry.symbolId) {
2697
- parts.push(` id:${entry.symbolId}`);
2969
+ if (entry.symbolRef) {
2970
+ parts.push(` ref:${entry.symbolRef}`);
2698
2971
  }
2699
2972
  return parts.join(`
2700
2973
  `);
@@ -2703,7 +2976,7 @@ function formatDependencyEntry(entry) {
2703
2976
  // src/commands/code/callees.ts
2704
2977
  function formatCalleesResult(data) {
2705
2978
  const lines = [];
2706
- lines.push(`Callees of ${data.root.qualifiedPath ?? data.root.name} (id:${data.root.symbolId})`);
2979
+ lines.push(`Callees of ${data.root.qualifiedPath ?? data.root.name} (ref:${data.root.symbolRef})`);
2707
2980
  lines.push(`${data.total} callee(s)${data.hasMore ? " (more available)" : ""}`);
2708
2981
  if (data.externalPackages && data.externalPackages.length > 0) {
2709
2982
  lines.push(`External packages: ${data.externalPackages.join(", ")}`);
@@ -2724,7 +2997,7 @@ function formatCalleesResult(data) {
2724
2997
  }
2725
2998
  async function codeCalleesAction(symbolArg, options, deps) {
2726
2999
  const { pkgseerService } = deps;
2727
- const symbolRef = buildSymbolReference(symbolArg, options.id);
3000
+ const symbolRef = buildSymbolReference(symbolArg, options.ref);
2728
3001
  const result = await pkgseerService.symbolDependencies(symbolRef, {
2729
3002
  maxDepth: parseIntOption(options.maxDepth, "--max-depth"),
2730
3003
  maxResults: parseIntOption(options.limit, "--limit"),
@@ -2734,27 +3007,27 @@ async function codeCalleesAction(symbolArg, options, deps) {
2734
3007
  waitTimeoutMs: parseWaitTimeout(options.wait)
2735
3008
  });
2736
3009
  handleErrors(result.errors, options.json ?? false);
2737
- if (!result.data.symbolDependencies) {
2738
- outputError("Symbol not found. Verify the symbol reference or use --id.", options.json ?? false);
3010
+ if (!result.data?.symbolDependencies) {
3011
+ outputError("Symbol not found. Verify the symbol reference or use --ref.", options.json ?? false);
2739
3012
  return;
2740
3013
  }
2741
3014
  if (options.json) {
2742
- output(result.data.symbolDependencies, true);
3015
+ output(result.data?.symbolDependencies, true);
2743
3016
  } else {
2744
- console.log(formatCalleesResult(result.data.symbolDependencies));
3017
+ console.log(formatCalleesResult(result.data?.symbolDependencies));
2745
3018
  }
2746
3019
  }
2747
3020
  var CALLEES_DESCRIPTION = `Find what a symbol calls.
2748
3021
 
2749
3022
  Shows functions and methods that the specified symbol depends on.
2750
- Use registry:package#symbolName format or --id for direct lookup.
3023
+ Use registry:package#symbolName format or --ref for direct lookup.
2751
3024
 
2752
3025
  Examples:
2753
3026
  pkgseer code callees npm:express#Router
2754
- pkgseer code callees --id 12345
3027
+ pkgseer code callees --ref npm:express:4.18.2:42
2755
3028
  pkgseer code callees npm:lodash#merge --max-depth 2 --include-external`;
2756
3029
  function registerCodeCalleesCommand(program) {
2757
- program.command("callees [symbol]").summary("Find what a symbol calls").description(CALLEES_DESCRIPTION).option("--id <id>", "Direct symbol ID").option("--max-depth <n>", "Max traversal depth").option("--limit <n>", "Max results").option("--include-external", "Include external package calls").option("--include-builtins", "Include builtin function calls").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (symbolArg, options) => {
3030
+ program.command("callees [symbol]").summary("Find what a symbol calls").description(CALLEES_DESCRIPTION).option("--ref <ref>", "Direct symbol reference (registry:package:version:id)").option("--max-depth <n>", "Max traversal depth").option("--limit <n>", "Max results").option("--include-external", "Include external package calls").option("--include-builtins", "Include builtin function calls").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (symbolArg, options) => {
2758
3031
  await withCliErrorHandling(options.json ?? false, async () => {
2759
3032
  const deps = await createContainer();
2760
3033
  await codeCalleesAction(symbolArg, options, deps);
@@ -2764,7 +3037,7 @@ function registerCodeCalleesCommand(program) {
2764
3037
  // src/commands/code/callers.ts
2765
3038
  function formatCallersResult(data) {
2766
3039
  const lines = [];
2767
- lines.push(`Callers of ${data.target.qualifiedPath ?? data.target.name} (id:${data.target.symbolId})`);
3040
+ lines.push(`Callers of ${data.target.qualifiedPath ?? data.target.name} (ref:${data.target.symbolRef})`);
2768
3041
  lines.push(`${data.total} caller(s)${data.hasMore ? " (more available)" : ""}`);
2769
3042
  lines.push("");
2770
3043
  for (const dep of data.dependents) {
@@ -2776,35 +3049,36 @@ function formatCallersResult(data) {
2776
3049
  }
2777
3050
  async function codeCallersAction(symbolArg, options, deps) {
2778
3051
  const { pkgseerService } = deps;
2779
- const symbolRef = buildSymbolReference(symbolArg, options.id);
3052
+ const symbolRef = buildSymbolReference(symbolArg, options.ref);
2780
3053
  const result = await pkgseerService.symbolDependents(symbolRef, {
2781
3054
  samePackageOnly: options.samePackageOnly,
3055
+ maxDepth: parseIntOption(options.maxDepth, "--max-depth"),
2782
3056
  maxResults: parseIntOption(options.limit, "--limit"),
2783
3057
  mode: parseNavigationMode(options.mode),
2784
3058
  waitTimeoutMs: parseWaitTimeout(options.wait)
2785
3059
  });
2786
3060
  handleErrors(result.errors, options.json ?? false);
2787
- if (!result.data.symbolDependents) {
2788
- outputError("Symbol not found. Verify the symbol reference or use --id.", options.json ?? false);
3061
+ if (!result.data?.symbolDependents) {
3062
+ outputError("Symbol not found. Verify the symbol reference or use --ref.", options.json ?? false);
2789
3063
  return;
2790
3064
  }
2791
3065
  if (options.json) {
2792
- output(result.data.symbolDependents, true);
3066
+ output(result.data?.symbolDependents, true);
2793
3067
  } else {
2794
- console.log(formatCallersResult(result.data.symbolDependents));
3068
+ console.log(formatCallersResult(result.data?.symbolDependents));
2795
3069
  }
2796
3070
  }
2797
3071
  var CALLERS_DESCRIPTION = `Find what calls a symbol.
2798
3072
 
2799
3073
  Shows functions and methods that call the specified symbol.
2800
- Use registry:package#symbolName format or --id for direct lookup.
3074
+ Use registry:package#symbolName format or --ref for direct lookup.
2801
3075
 
2802
3076
  Examples:
2803
3077
  pkgseer code callers npm:express#Router
2804
- pkgseer code callers --id 12345
3078
+ pkgseer code callers --ref npm:express:4.18.2:42
2805
3079
  pkgseer code callers npm:lodash#debounce --same-package-only`;
2806
3080
  function registerCodeCallersCommand(program) {
2807
- program.command("callers [symbol]").summary("Find what calls a symbol").description(CALLERS_DESCRIPTION).option("--id <id>", "Direct symbol ID").option("--same-package-only", "Only show callers from the same package").option("--limit <n>", "Max results").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (symbolArg, options) => {
3081
+ program.command("callers [symbol]").summary("Find what calls a symbol").description(CALLERS_DESCRIPTION).option("--ref <ref>", "Direct symbol reference (registry:package:version:id)").option("--same-package-only", "Only show callers from the same package").option("--max-depth <n>", "Max traversal depth").option("--limit <n>", "Max results").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (symbolArg, options) => {
2808
3082
  await withCliErrorHandling(options.json ?? false, async () => {
2809
3083
  const deps = await createContainer();
2810
3084
  await codeCallersAction(symbolArg, options, deps);
@@ -2856,14 +3130,14 @@ async function codeDiffAction(packageArg, fromVersion, toVersion, options, deps)
2856
3130
  waitTimeoutMs: parseWaitTimeout(options.wait)
2857
3131
  });
2858
3132
  handleErrors(result.errors, options.json ?? false);
2859
- if (!result.data.versionDiff) {
3133
+ if (!result.data?.versionDiff) {
2860
3134
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2861
3135
  return;
2862
3136
  }
2863
3137
  if (options.json) {
2864
- output(result.data.versionDiff, true);
3138
+ output(result.data?.versionDiff, true);
2865
3139
  } else {
2866
- console.log(formatDiffResult(result.data.versionDiff));
3140
+ console.log(formatDiffResult(result.data?.versionDiff));
2867
3141
  }
2868
3142
  }
2869
3143
  var DIFF_DESCRIPTION = `Compare symbols between two package versions.
@@ -2916,11 +3190,11 @@ async function codeFilesAction(packageArg, options, deps) {
2916
3190
  waitTimeoutMs: parseWaitTimeout(options.wait)
2917
3191
  });
2918
3192
  handleErrors(result.errors, options.json ?? false);
2919
- if (!result.data.listRepoFiles) {
3193
+ if (!result.data?.listRepoFiles) {
2920
3194
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2921
3195
  return;
2922
3196
  }
2923
- const data = result.data.listRepoFiles;
3197
+ const data = result.data?.listRepoFiles;
2924
3198
  if (options.json) {
2925
3199
  output(data, true);
2926
3200
  } else {
@@ -2981,11 +3255,11 @@ async function codeFindAction(packageArg, nameArg, options, deps) {
2981
3255
  waitTimeoutMs: parseWaitTimeout(options.wait)
2982
3256
  });
2983
3257
  handleErrors(result.errors, options.json ?? false);
2984
- if (!result.data.findSymbol) {
3258
+ if (!result.data?.findSymbol) {
2985
3259
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2986
3260
  return;
2987
3261
  }
2988
- const data = result.data.findSymbol;
3262
+ const data = result.data?.findSymbol;
2989
3263
  if (options.json) {
2990
3264
  output(data, true);
2991
3265
  } else {
@@ -3055,11 +3329,11 @@ async function codeGrepAction(packageArg, filePath, pattern, options, deps) {
3055
3329
  waitTimeoutMs: parseWaitTimeout(options.wait)
3056
3330
  });
3057
3331
  handleErrors(result.errors, options.json ?? false);
3058
- if (!result.data.grepRepoFile) {
3332
+ if (!result.data?.grepRepoFile) {
3059
3333
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
3060
3334
  return;
3061
3335
  }
3062
- const data = result.data.grepRepoFile;
3336
+ const data = result.data?.grepRepoFile;
3063
3337
  if (options.json) {
3064
3338
  output(data, true);
3065
3339
  } else {
@@ -3142,14 +3416,14 @@ async function codeImportsAction(packageArg, options, deps) {
3142
3416
  waitTimeoutMs: parseWaitTimeout(options.wait)
3143
3417
  });
3144
3418
  handleErrors(result.errors, options.json ?? false);
3145
- if (!result.data.packageImports) {
3419
+ if (!result.data?.packageImports) {
3146
3420
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
3147
3421
  return;
3148
3422
  }
3149
3423
  if (options.json) {
3150
- output(result.data.packageImports, true);
3424
+ output(result.data?.packageImports, true);
3151
3425
  } else {
3152
- console.log(formatImportsResult(result.data.packageImports));
3426
+ console.log(formatImportsResult(result.data?.packageImports));
3153
3427
  }
3154
3428
  }
3155
3429
  var IMPORTS_DESCRIPTION = `List import statements in a package.
@@ -3211,14 +3485,14 @@ async function codeListAction(packageArg, options, deps) {
3211
3485
  waitTimeoutMs: parseWaitTimeout(options.wait)
3212
3486
  });
3213
3487
  handleErrors(result.errors, options.json ?? false);
3214
- if (!result.data.listSymbols) {
3488
+ if (!result.data?.listSymbols) {
3215
3489
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
3216
3490
  return;
3217
3491
  }
3218
3492
  if (options.json) {
3219
- output(result.data.listSymbols, true);
3493
+ output(result.data?.listSymbols, true);
3220
3494
  } else {
3221
- console.log(formatListResult(result.data.listSymbols));
3495
+ console.log(formatListResult(result.data?.listSymbols));
3222
3496
  }
3223
3497
  }
3224
3498
  var LIST_DESCRIPTION = `Browse symbols in a package.
@@ -3257,9 +3531,9 @@ function formatPathResult(data) {
3257
3531
  for (const [j, hop] of callPath.hops.entries()) {
3258
3532
  const name = hop.qualifiedPath ?? hop.name ?? "unknown";
3259
3533
  const loc = hop.filePath ? hop.callLine ? ` ${hop.filePath}:${hop.callLine}` : ` ${hop.filePath}` : "";
3260
- const idPart = hop.symbolId ? ` (id:${hop.symbolId})` : "";
3534
+ const refPart = hop.symbolRef ? ` (ref:${hop.symbolRef})` : "";
3261
3535
  const arrow = j < callPath.hops.length - 1 ? " ->" : "";
3262
- lines.push(` ${j + 1}. ${name}${idPart}${loc}${arrow}`);
3536
+ lines.push(` ${j + 1}. ${name}${refPart}${loc}${arrow}`);
3263
3537
  }
3264
3538
  lines.push("");
3265
3539
  }
@@ -3268,35 +3542,35 @@ function formatPathResult(data) {
3268
3542
  }
3269
3543
  async function codePathAction(fromArg, toArg, options, deps) {
3270
3544
  const { pkgseerService } = deps;
3271
- const fromRef = buildSymbolReference(fromArg, options.fromId);
3272
- const toRef = buildSymbolReference(toArg, options.toId);
3545
+ const fromRef = buildSymbolReference(fromArg, options.fromRef);
3546
+ const toRef = buildSymbolReference(toArg, options.toRef);
3273
3547
  const result = await pkgseerService.callPath(fromRef, toRef, {
3274
3548
  maxDepth: parseIntOption(options.maxDepth, "--max-depth"),
3275
3549
  mode: parseNavigationMode(options.mode),
3276
3550
  waitTimeoutMs: parseWaitTimeout(options.wait)
3277
3551
  });
3278
3552
  handleErrors(result.errors, options.json ?? false);
3279
- if (!result.data.callPath) {
3553
+ if (!result.data?.callPath) {
3280
3554
  outputError("Could not find call path. Verify the symbol references.", options.json ?? false);
3281
3555
  return;
3282
3556
  }
3283
3557
  if (options.json) {
3284
- output(result.data.callPath, true);
3558
+ output(result.data?.callPath, true);
3285
3559
  } else {
3286
- console.log(formatPathResult(result.data.callPath));
3560
+ console.log(formatPathResult(result.data?.callPath));
3287
3561
  }
3288
3562
  }
3289
3563
  var PATH_DESCRIPTION = `Find call path between two symbols.
3290
3564
 
3291
3565
  Discovers the shortest call path from one symbol to another.
3292
- Use registry:package#symbolName format or --from-id/--to-id for direct lookup.
3566
+ Use registry:package#symbolName format or --from-ref/--to-ref for direct lookup.
3293
3567
 
3294
3568
  Examples:
3295
3569
  pkgseer code path npm:express#app npm:express#Router
3296
- pkgseer code path --from-id 123 --to-id 456
3570
+ pkgseer code path --from-ref npm:express:4.18.2:123 --to-ref npm:express:4.18.2:456
3297
3571
  pkgseer code path npm:lodash#merge npm:lodash#cloneDeep --max-depth 5`;
3298
3572
  function registerCodePathCommand(program) {
3299
- program.command("path [from] [to]").summary("Find call path between symbols").description(PATH_DESCRIPTION).option("--from-id <id>", "Source symbol ID").option("--to-id <id>", "Target symbol ID").option("--max-depth <n>", "Max path depth").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (fromArg, toArg, options) => {
3573
+ program.command("path [from] [to]").summary("Find call path between symbols").description(PATH_DESCRIPTION).option("--from-ref <ref>", "Source symbol reference (registry:package:version:id)").option("--to-ref <ref>", "Target symbol reference (registry:package:version:id)").option("--max-depth <n>", "Max path depth").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (fromArg, toArg, options) => {
3300
3574
  await withCliErrorHandling(options.json ?? false, async () => {
3301
3575
  const deps = await createContainer();
3302
3576
  await codePathAction(fromArg, toArg, options, deps);
@@ -3356,11 +3630,11 @@ async function codeSearchAction(packageArg, query, options, deps) {
3356
3630
  waitTimeoutMs: parseWaitTimeout(options.wait)
3357
3631
  });
3358
3632
  handleErrors(result.errors, options.json ?? false);
3359
- if (!result.data.searchSymbols) {
3633
+ if (!result.data?.searchSymbols) {
3360
3634
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
3361
3635
  return;
3362
3636
  }
3363
- const data = result.data.searchSymbols;
3637
+ const data = result.data?.searchSymbols;
3364
3638
  if (options.json) {
3365
3639
  output(data, true);
3366
3640
  } else {
@@ -3598,7 +3872,7 @@ async function fetchPage(ref, defaultRegistry, defaultVersion, pkgseerService) {
3598
3872
  }).join("; ");
3599
3873
  return { ref: ref.originalRef, result: null, error: errorMsg };
3600
3874
  }
3601
- if (!result2.data.getDocPage) {
3875
+ if (!result2.data?.getDocPage) {
3602
3876
  return {
3603
3877
  ref: ref.originalRef,
3604
3878
  result: null,
@@ -3608,8 +3882,8 @@ async function fetchPage(ref, defaultRegistry, defaultVersion, pkgseerService) {
3608
3882
  return {
3609
3883
  ref: ref.originalRef,
3610
3884
  result: {
3611
- schemaVersion: result2.data.getDocPage?.schemaVersion ?? null,
3612
- page: result2.data.getDocPage?.page ?? null
3885
+ schemaVersion: result2.data?.getDocPage?.schemaVersion ?? null,
3886
+ page: result2.data?.getDocPage?.page ?? null
3613
3887
  }
3614
3888
  };
3615
3889
  }
@@ -3642,7 +3916,7 @@ async function fetchPage(ref, defaultRegistry, defaultVersion, pkgseerService) {
3642
3916
  }).join("; ");
3643
3917
  return { ref: ref.originalRef, result: null, error: errorMsg };
3644
3918
  }
3645
- if (!result.data.fetchPackageDoc) {
3919
+ if (!result.data?.fetchPackageDoc) {
3646
3920
  return {
3647
3921
  ref: ref.originalRef,
3648
3922
  result: null,
@@ -3651,8 +3925,8 @@ async function fetchPage(ref, defaultRegistry, defaultVersion, pkgseerService) {
3651
3925
  }
3652
3926
  return {
3653
3927
  ref: ref.originalRef,
3654
- result: result.data.fetchPackageDoc ? {
3655
- page: result.data.fetchPackageDoc.page ?? null
3928
+ result: result.data?.fetchPackageDoc ? {
3929
+ page: result.data?.fetchPackageDoc.page ?? null
3656
3930
  } : null
3657
3931
  };
3658
3932
  } catch (error) {
@@ -3797,12 +4071,12 @@ async function docsListAction(packageArg, options, deps) {
3797
4071
  const version2 = parsed.version ?? options.pkgVersion;
3798
4072
  const result = await pkgseerService.cliDocsList(registry, parsed.name, version2);
3799
4073
  handleErrors(result.errors, options.json ?? false);
3800
- if (!result.data.listPackageDocs) {
4074
+ if (!result.data?.listPackageDocs) {
3801
4075
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
3802
4076
  return;
3803
4077
  }
3804
4078
  if (options.json) {
3805
- const pages = result.data.listPackageDocs.pages?.filter((p) => p) ?? [];
4079
+ const pages = result.data?.listPackageDocs.pages?.filter((p) => p) ?? [];
3806
4080
  const slim = pages.map((p) => {
3807
4081
  if (!p || !p.id)
3808
4082
  return null;
@@ -3813,7 +4087,7 @@ async function docsListAction(packageArg, options, deps) {
3813
4087
  }).filter((p) => p !== null);
3814
4088
  output(slim, true);
3815
4089
  } else {
3816
- console.log(formatDocsList(result.data.listPackageDocs));
4090
+ console.log(formatDocsList(result.data?.listPackageDocs));
3817
4091
  }
3818
4092
  }
3819
4093
  var LIST_DESCRIPTION2 = `List available documentation pages for a package.
@@ -4292,7 +4566,7 @@ async function pollSearchProgress(searchRef, pkgseerService, useColors) {
4292
4566
  const result = await pollUntilDone({
4293
4567
  fetch: async () => {
4294
4568
  const res = await pkgseerService.getSearchProgress(searchRef);
4295
- return res.data.searchProgress ?? null;
4569
+ return res.data?.searchProgress ?? null;
4296
4570
  },
4297
4571
  isDone: (progress) => {
4298
4572
  if (!progress)
@@ -4325,7 +4599,7 @@ async function handleResume(searchRef, pkgseerService, options, useColors) {
4325
4599
  handleErrors(progressResult.errors, options.json ?? false);
4326
4600
  return true;
4327
4601
  }
4328
- const progress = progressResult.data.searchProgress;
4602
+ const progress = progressResult.data?.searchProgress;
4329
4603
  if (!progress) {
4330
4604
  outputError("Search session not found. It may have expired (sessions last 1 hour).", options.json ?? false);
4331
4605
  return true;
@@ -4336,7 +4610,7 @@ async function handleResume(searchRef, pkgseerService, options, useColors) {
4336
4610
  handleErrors(resultsResponse.errors, options.json ?? false);
4337
4611
  return true;
4338
4612
  }
4339
- const searchResults = resultsResponse.data.searchResults;
4613
+ const searchResults = resultsResponse.data?.searchResults;
4340
4614
  if (!searchResults) {
4341
4615
  outputError("Search completed but no results returned.", options.json ?? false);
4342
4616
  return true;
@@ -4379,8 +4653,8 @@ Ref: ${searchRef}`, options.json ?? false);
4379
4653
  }
4380
4654
  if (pollResult.status === "COMPLETED") {
4381
4655
  const resultsResponse = await pkgseerService.getSearchResults(searchRef);
4382
- if (resultsResponse.data.searchResults) {
4383
- outputResults(resultsResponse.data.searchResults, options, useColors);
4656
+ if (resultsResponse.data?.searchResults) {
4657
+ outputResults(resultsResponse.data?.searchResults, options, useColors);
4384
4658
  }
4385
4659
  } else {
4386
4660
  console.log(`Search ended with status: ${pollResult.status.toLowerCase()}`);
@@ -4421,7 +4695,7 @@ async function searchAction(queryArg, options, deps, defaultMode = "ALL") {
4421
4695
  waitTimeoutMs
4422
4696
  });
4423
4697
  handleErrors(result.errors, options.json ?? false);
4424
- const asyncResult = result.data.combinedSearch;
4698
+ const asyncResult = result.data?.combinedSearch;
4425
4699
  if (!asyncResult) {
4426
4700
  outputError("No results returned. Check package names and registries.", options.json ?? false);
4427
4701
  return;
@@ -4442,9 +4716,9 @@ async function searchAction(queryArg, options, deps, defaultMode = "ALL") {
4442
4716
  const pollResult = await pollSearchProgress(searchRef, pkgseerService, useColors);
4443
4717
  if (pollResult.success && pollResult.status === "COMPLETED") {
4444
4718
  const resultsResponse = await pkgseerService.getSearchResults(searchRef);
4445
- if (resultsResponse.data.searchResults) {
4719
+ if (resultsResponse.data?.searchResults) {
4446
4720
  console.log("");
4447
- outputResults(resultsResponse.data.searchResults, options, useColors);
4721
+ outputResults(resultsResponse.data?.searchResults, options, useColors);
4448
4722
  return;
4449
4723
  }
4450
4724
  }
@@ -4598,7 +4872,7 @@ async function indexAction(packages, options, deps) {
4598
4872
  }
4599
4873
  const result = await pkgseerService.triggerIndexing(input2);
4600
4874
  handleErrors(result.errors, options.json ?? false);
4601
- const data = result.data.triggerIndexing;
4875
+ const data = result.data?.triggerIndexing;
4602
4876
  if (options.json) {
4603
4877
  output(data, true);
4604
4878
  } else {
@@ -4843,16 +5117,39 @@ async function setupCodexViaCli(shellService) {
4843
5117
  }
4844
5118
  function showAvailableTools(hasProject, useColors) {
4845
5119
  console.log(`
4846
- Available MCP tools:`);
4847
- console.log(" • package_summary - Get package overview");
4848
- console.log(" package_vulnerabilities - Check for security issues");
4849
- console.log(" package_quality - Get quality score");
4850
- console.log(" package_dependencies - List dependencies");
4851
- console.log(" compare_packages - Compare multiple packages");
4852
- console.log(" list_package_docs - List documentation pages");
4853
- console.log(" fetch_package_doc - Fetch documentation content");
4854
- console.log(" search_package_docs - Search package documentation");
4855
- console.log(" • search_project_docs - Search docs across project dependencies");
5120
+ Available MCP tools (21):`);
5121
+ console.log("");
5122
+ console.log(" Navigate source:");
5123
+ console.log(" find_symbol - Read source code of functions/classes");
5124
+ console.log(" list_symbols - Browse public API or file symbols");
5125
+ console.log(" search_symbols - Full-text search within package code");
5126
+ console.log(" symbol_callers - Trace what calls a function");
5127
+ console.log(" symbol_callees - Trace what a function calls");
5128
+ console.log(" call_path - Find call path between two functions");
5129
+ console.log("");
5130
+ console.log(" Browse files:");
5131
+ console.log(" • list_repo_files - Explore package file structure");
5132
+ console.log(" • grep_repo_file - Search for patterns in source files");
5133
+ console.log(" • fetch_code_context - Read source by file path and line range");
5134
+ console.log("");
5135
+ console.log(" Search & docs:");
5136
+ console.log(" • search - Search code and docs across packages");
5137
+ console.log(" • search_project_docs - Search docs across project dependencies");
5138
+ console.log(" • list_package_docs - List documentation pages");
5139
+ console.log(" • fetch_package_doc - Fetch documentation content");
5140
+ console.log("");
5141
+ console.log(" Evaluate:");
5142
+ console.log(" • package_summary - Get package overview");
5143
+ console.log(" • package_quality - Get quality score");
5144
+ console.log(" • package_vulnerabilities - Check for security issues");
5145
+ console.log(" • compare_packages - Compare multiple packages");
5146
+ console.log(" • package_dependencies - List dependencies");
5147
+ console.log(" • version_diff - Compare changes between versions");
5148
+ console.log("");
5149
+ console.log(" Utility:");
5150
+ console.log(" • trigger_indexing - Pre-warm package indexes");
5151
+ console.log(" • search_status - Check async search progress");
5152
+ console.log(" • package_imports - See what a package imports");
4856
5153
  if (!hasProject) {
4857
5154
  console.log(dim(`
4858
5155
  Tip: Create pkgseer.yml to enable automatic project detection for search_project_docs.`, useColors));
@@ -5748,27 +6045,51 @@ function generateSkillContent(invocation = "pkgseer") {
5748
6045
  return `---
5749
6046
  name: pkgseer
5750
6047
  version: ${version}
5751
- description: Search code/docs across npm, PyPI, Hex packages. Provides quality scores, security vulnerabilities, dependencies, comparisons. Use when user asks about package security, CVEs, vulnerabilities, code examples, API usage, package comparison, dependency analysis, or which package to use.
6048
+ description: Navigate source code, trace call graphs, and search docs across npm/PyPI/Hex packages. Quality scores, vulnerability checks, dependency analysis, and cross-package comparison. Use when debugging third-party dependencies, understanding how a library works internally, tracing a stack trace into dependency code, evaluating or comparing packages, checking security vulnerabilities, or understanding what changed between versions.
5752
6049
  ---
5753
6050
 
5754
- # PkgSeer - Package Intelligence
6051
+ # PkgSeer - Package Source Code & Intelligence
6052
+
6053
+ Navigate dependency source code and analyze packages across npm, PyPI, and Hex. All commands support \`--json\`.
5755
6054
 
5756
- Search and analyze packages across npm, PyPI, and Hex. All commands support \`--json\`.
6055
+ Use PkgSeer instead of guessing library behavior, cloning repos, or browsing GitHub.
5757
6056
 
5758
- ## Search (Primary Use Case)
6057
+ ## Code Navigation (Debug & Understand Dependencies)
6058
+
6059
+ \`\`\`bash
6060
+ # Read source code of a function/class — use instead of guessing from training data
6061
+ ${invocation} code find express Router --include-code
6062
+ ${invocation} code find pypi:requests Session --include-code
6063
+
6064
+ # Trace what calls a function (find entry points, debug triggers)
6065
+ ${invocation} code callers -r npm -p express -n Router
6066
+
6067
+ # Trace what a function calls (follow execution path)
6068
+ ${invocation} code callees -r npm -p express -n Router
6069
+
6070
+ # Find how function A reaches function B
6071
+ ${invocation} code path -r npm -p express --from-name listen --to-name createServer
6072
+
6073
+ # Search for error messages or patterns in dependency source
6074
+ ${invocation} code search express "ERR_HTTP_HEADERS_SENT"
6075
+ ${invocation} code grep express src/router/index.js "handle"
6076
+
6077
+ # Browse package structure, API, and imports
6078
+ ${invocation} code files express --path-prefix src/
6079
+ ${invocation} code list express --scope exports
6080
+ ${invocation} code imports express --resolved-only
6081
+
6082
+ # Compare versions (detect breaking changes after upgrades)
6083
+ ${invocation} code diff express v4.18.2 v5.0.0
6084
+ \`\`\`
6085
+
6086
+ ## Search Across Packages
5759
6087
 
5760
6088
  \`\`\`bash
5761
- # Search code and docs across packages
5762
6089
  ${invocation} search "<query>" -P lodash,express # npm packages
5763
6090
  ${invocation} search "<query>" -P pypi:requests # PyPI packages
5764
- ${invocation} search "authentication" -P hex:phoenix,hex:plug
5765
-
5766
- # Search modes
5767
6091
  ${invocation} search "<query>" -P <packages> --mode code # Code only
5768
6092
  ${invocation} search "<query>" -P <packages> --mode docs # Docs only
5769
-
5770
- # Docs-only search (shorthand)
5771
- ${invocation} docs search "<query>" -P <packages>
5772
6093
  \`\`\`
5773
6094
 
5774
6095
  ## Package Analysis
@@ -5776,23 +6097,11 @@ ${invocation} docs search "<query>" -P <packages>
5776
6097
  Package format: \`[registry:]name[@version]\`
5777
6098
 
5778
6099
  \`\`\`bash
5779
- # Overview: metadata, versions, quickstart
5780
- ${invocation} pkg info lodash
5781
- ${invocation} pkg info pypi:requests
5782
-
5783
- # Quality score (0-100) with category breakdown
5784
- ${invocation} pkg quality express@4.18.0
5785
- ${invocation} pkg quality pypi:django@4.2
5786
-
5787
- # Security: CVEs, severity, upgrade paths
5788
- ${invocation} pkg vulns lodash@4.17.21
5789
-
5790
- # Dependencies: direct, transitive, tree view
5791
- ${invocation} pkg deps express --transitive
5792
-
5793
- # Compare up to 10 packages
5794
- ${invocation} pkg compare lodash underscore ramda
5795
- ${invocation} pkg compare axios pypi:httpx # cross-registry
6100
+ ${invocation} pkg info lodash # Overview, versions, quickstart
6101
+ ${invocation} pkg quality express@4.18.0 # Quality score (0-100)
6102
+ ${invocation} pkg vulns lodash@4.17.21 # CVEs, severity, upgrade paths
6103
+ ${invocation} pkg deps express --transitive # Dependency tree
6104
+ ${invocation} pkg compare lodash underscore ramda # Side-by-side comparison
5796
6105
  \`\`\`
5797
6106
 
5798
6107
  ## Documentation
@@ -5808,6 +6117,7 @@ ${invocation} docs search "<query>" -P <packages> # Search docs only
5808
6117
  - Package format: \`[registry:]name[@version]\` (e.g., \`pypi:django@4.2\`)
5809
6118
  - Default registry: npm
5810
6119
  - Use \`--json\` for structured output when parsing
6120
+ - Quick start: \`${invocation} code find <package> <function> --include-code\`
5811
6121
  `;
5812
6122
  }
5813
6123
  function extractSkillVersion(content) {
@@ -5940,7 +6250,8 @@ PkgSeer skill ${action} for ${toolName}!`, useColors));
5940
6250
  console.log(`Location: ${highlight(paths.skillPath, useColors)}`);
5941
6251
  console.log(dim(`
5942
6252
  The skill is now available. Try asking:
5943
- ` + ` "Search for authentication examples in express"
6253
+ ` + ` "How does express Router work internally?"
6254
+ ` + ` "What changed between express v4 and v5?"
5944
6255
  ` + ` "Is lodash secure? Check for vulnerabilities"
5945
6256
  ` + ' "Compare axios vs fetch vs got"', useColors));
5946
6257
  }
@@ -6426,19 +6737,6 @@ function errorResult(message) {
6426
6737
  }
6427
6738
 
6428
6739
  // src/tools/shared.ts
6429
- function toGraphQLRegistry2(registry) {
6430
- const map = {
6431
- npm: "NPM",
6432
- pypi: "PYPI",
6433
- hex: "HEX",
6434
- crates: "CRATES",
6435
- nuget: "NUGET",
6436
- maven: "MAVEN",
6437
- zig: "ZIG",
6438
- vcpkg: "VCPKG"
6439
- };
6440
- return map[registry.toLowerCase()] || "NPM";
6441
- }
6442
6740
  function toNavigationMode(mode) {
6443
6741
  if (!mode)
6444
6742
  return;
@@ -6487,7 +6785,7 @@ var schemas = {
6487
6785
  navigationMode: z2.enum(["summary", "detailed"]).optional().describe("Response detail level. summary (default) returns core fields; detailed adds all optional fields."),
6488
6786
  waitTimeoutMs: z2.number().int().min(0).max(30000).optional().describe("Max milliseconds to wait for package indexing (0-30000). 0 = error immediately if not indexed."),
6489
6787
  symbolReference: z2.object({
6490
- symbol_id: z2.number().int().optional().describe("Direct symbol ID from find_symbol or list_symbols results (preferred, avoids ambiguity)"),
6788
+ symbol_ref: z2.string().optional().describe("Compound symbol reference (registry:package:version:id) from find_symbol or list_symbols results. Self-contained no additional fields needed."),
6491
6789
  registry: z2.enum(["npm", "pypi", "hex", "crates", "nuget", "maven", "zig", "vcpkg"]).optional().describe("Package registry (required for name-based lookup)"),
6492
6790
  package_name: z2.string().max(255).optional().describe("Package name (required for name-based lookup)"),
6493
6791
  symbol_name: z2.string().max(500).optional().describe("Symbol name or qualified path (required for name-based lookup). Matches both short name and qualifiedPath.")
@@ -6495,22 +6793,23 @@ var schemas = {
6495
6793
  };
6496
6794
  function toSymbolReferenceInput(ref) {
6497
6795
  return {
6498
- symbolId: ref.symbol_id,
6499
- registry: ref.registry ? toGraphQLRegistry2(ref.registry) : undefined,
6796
+ symbolRef: ref.symbol_ref,
6797
+ registry: ref.registry ? toGraphQLRegistry(ref.registry) : undefined,
6500
6798
  packageName: ref.package_name,
6501
6799
  symbolName: ref.symbol_name
6502
6800
  };
6503
6801
  }
6802
+ var MCP_HINTS = {
6803
+ being_indexed: "Hint: package is being indexed. Try again with a longer wait_timeout_ms (e.g., 30000).",
6804
+ timeout: "Hint: try lowering the limit or narrowing the scope, then retry."
6805
+ };
6504
6806
  function buildHintedMessage(operation, message) {
6505
- const lower = message.toLowerCase();
6506
- const isBeingIndexed = lower.includes("being indexed") || lower.includes("retry with waittimeoutms");
6507
- if (isBeingIndexed) {
6508
- return `Failed to ${operation}: ${message}. ` + "Hint: package is being indexed. Try again with a longer wait_timeout_ms (e.g., 30000).";
6509
- }
6510
- const isTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
6511
- if (isTimeout) {
6512
- return `Failed to ${operation}: ${message}. ` + "Hint: try lowering the limit or narrowing the scope, then retry.";
6807
+ const kind = classifyError(message);
6808
+ const hint = MCP_HINTS[kind];
6809
+ if (hint) {
6810
+ return `Failed to ${operation}: ${message}. ${hint}`;
6513
6811
  }
6812
+ const lower = message.toLowerCase();
6514
6813
  if (lower.includes("graphql error (code: 500)")) {
6515
6814
  try {
6516
6815
  const jsonMatch = message.match(/\{.*\}/s);
@@ -6529,24 +6828,17 @@ function buildHintedMessage(operation, message) {
6529
6828
  }
6530
6829
  return `Failed to ${operation}: ${message}`;
6531
6830
  }
6831
+ var MCP_GRAPHQL_HINTS = {
6832
+ not_found: "Hint: verify the name/registry and that the resource exists.",
6833
+ being_indexed: "Hint: package is being indexed. Try again with a longer wait_timeout_ms (e.g., 30000).",
6834
+ timeout: "Hint: try lowering the limit or narrowing the scope, then retry."
6835
+ };
6532
6836
  function handleGraphQLErrors(errors) {
6533
6837
  if (errors && errors.length > 0) {
6534
- const messages = errors.map((e) => e.message).filter(Boolean);
6535
- const combined = messages.join(", ");
6536
- const lower = combined.toLowerCase();
6537
- const hasNotFound = lower.includes("not found") || lower.includes("does not exist");
6538
- const hasBeingIndexed = lower.includes("being indexed") || lower.includes("retry with waittimeoutms");
6539
- const hasTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
6540
- if (hasNotFound) {
6541
- return errorResult(`Error: ${combined}. Hint: verify the name/registry and that the resource exists.`);
6542
- }
6543
- if (hasBeingIndexed) {
6544
- return errorResult(`Error: ${combined}. Hint: package is being indexed. Try again with a longer wait_timeout_ms (e.g., 30000).`);
6545
- }
6546
- if (hasTimeout) {
6547
- return errorResult(`Error: ${combined}. Hint: try lowering the limit or narrowing the scope, then retry.`);
6548
- }
6549
- return errorResult(`Error: ${combined}`);
6838
+ const combined = errors.map((e) => e.message).filter(Boolean).join(", ");
6839
+ const hint = MCP_GRAPHQL_HINTS[classifyError(combined)];
6840
+ const suffix = hint ? `. ${hint}` : "";
6841
+ return errorResult(`Error: ${combined}${suffix}`);
6550
6842
  }
6551
6843
  return null;
6552
6844
  }
@@ -6564,8 +6856,8 @@ function notFoundError(packageName, registry) {
6564
6856
 
6565
6857
  // src/tools/call-path.ts
6566
6858
  var argsSchema = {
6567
- from: schemas.symbolReference.describe("Source symbol. Provide either symbol_id or registry + package_name + symbol_name."),
6568
- to: schemas.symbolReference.describe("Destination symbol. Provide either symbol_id or registry + package_name + symbol_name."),
6859
+ from: schemas.symbolReference.describe("Source symbol. Provide either symbol_ref or registry + package_name + symbol_name."),
6860
+ to: schemas.symbolReference.describe("Destination symbol. Provide either symbol_ref or registry + package_name + symbol_name."),
6569
6861
  max_depth: z3.number().int().min(1).max(10).optional().describe("Maximum path length to search (max hops between from and to)"),
6570
6862
  mode: schemas.navigationMode,
6571
6863
  wait_timeout_ms: schemas.waitTimeoutMs
@@ -6573,17 +6865,18 @@ var argsSchema = {
6573
6865
  function createCallPathTool(pkgseerService) {
6574
6866
  return {
6575
6867
  name: "call_path",
6576
- description: "Find the shortest call path between two symbols in the same package. " + "Uses BFS to trace how function A eventually calls function B through intermediate calls. " + "Returns whether a path was found, with ordered call paths (shortest first) and hop details. " + "For direct callers/callees only, use symbol_callers or symbol_callees instead.",
6868
+ description: "Trace how execution flows from one function to another in a dependency " + "debug how a public API reaches an internal function or error handler. " + "Uses BFS through intermediate calls. " + "Returns ordered call paths (shortest first) with hop details. " + "For direct callers/callees only, use symbol_callers or symbol_callees instead.",
6577
6869
  schema: argsSchema,
6870
+ annotations: { readOnlyHint: true },
6578
6871
  handler: async (args, _extra) => {
6579
6872
  return withErrorHandling("find call path", async () => {
6580
6873
  const fromInput = toSymbolReferenceInput(args.from);
6581
6874
  const toInput = toSymbolReferenceInput(args.to);
6582
- if (!fromInput.symbolId && (!fromInput.symbolName || !fromInput.registry || !fromInput.packageName)) {
6583
- return errorResult("Invalid 'from' symbol: provide either symbol_id or symbol_name with registry and package_name.");
6875
+ if (!fromInput.symbolRef && (!fromInput.symbolName || !fromInput.registry || !fromInput.packageName)) {
6876
+ return errorResult("Invalid 'from' symbol: provide either symbol_ref or symbol_name with registry and package_name.");
6584
6877
  }
6585
- if (!toInput.symbolId && (!toInput.symbolName || !toInput.registry || !toInput.packageName)) {
6586
- return errorResult("Invalid 'to' symbol: provide either symbol_id or symbol_name with registry and package_name.");
6878
+ if (!toInput.symbolRef && (!toInput.symbolName || !toInput.registry || !toInput.packageName)) {
6879
+ return errorResult("Invalid 'to' symbol: provide either symbol_ref or symbol_name with registry and package_name.");
6587
6880
  }
6588
6881
  const result = await pkgseerService.callPath(fromInput, toInput, {
6589
6882
  maxDepth: args.max_depth,
@@ -6593,10 +6886,10 @@ function createCallPathTool(pkgseerService) {
6593
6886
  const graphqlError = handleGraphQLErrors(result.errors);
6594
6887
  if (graphqlError)
6595
6888
  return graphqlError;
6596
- if (!result.data.callPath) {
6889
+ if (!result.data?.callPath) {
6597
6890
  return errorResult("Could not find call path. Verify the symbols exist and the package is indexed.");
6598
6891
  }
6599
- return textResult(JSON.stringify(result.data.callPath, null, 2));
6892
+ return textResult(JSON.stringify(result.data?.callPath, null, 2));
6600
6893
  });
6601
6894
  }
6602
6895
  };
@@ -6616,10 +6909,11 @@ function createComparePackagesTool(pkgseerService) {
6616
6909
  name: "compare_packages",
6617
6910
  description: "Compare 2-10 packages side-by-side. Use this when evaluating alternatives (e.g., react vs preact vs solid-js). " + "Returns for each package: quality score, download counts, vulnerability count, license, and latest version. " + "Supports cross-registry comparison (npm, pypi, hex, crates). " + 'Format: [{"registry":"npm","name":"lodash"},{"registry":"npm","name":"underscore"}]',
6618
6911
  schema: argsSchema2,
6912
+ annotations: { readOnlyHint: true },
6619
6913
  handler: async ({ packages }, _extra) => {
6620
6914
  return withErrorHandling("compare packages", async () => {
6621
6915
  const input2 = packages.map((pkg) => ({
6622
- registry: toGraphQLRegistry2(pkg.registry),
6916
+ registry: toGraphQLRegistry(pkg.registry),
6623
6917
  name: pkg.name,
6624
6918
  version: pkg.version
6625
6919
  }));
@@ -6627,10 +6921,10 @@ function createComparePackagesTool(pkgseerService) {
6627
6921
  const graphqlError = handleGraphQLErrors(result.errors);
6628
6922
  if (graphqlError)
6629
6923
  return graphqlError;
6630
- if (!result.data.comparePackages) {
6924
+ if (!result.data?.comparePackages) {
6631
6925
  return errorResult("Comparison failed: no results returned");
6632
6926
  }
6633
- return textResult(JSON.stringify(result.data.comparePackages, null, 2));
6927
+ return textResult(JSON.stringify(result.data?.comparePackages, null, 2));
6634
6928
  });
6635
6929
  }
6636
6930
  };
@@ -6647,8 +6941,9 @@ var argsSchema3 = {
6647
6941
  function createFetchCodeContextTool(pkgseerService) {
6648
6942
  return {
6649
6943
  name: "fetch_code_context",
6650
- description: "Fetch code content from a repository by file path and line range. " + "Returns full file by default, or a specific line range. " + "For symbol-based lookup, use find_symbol(include_code=true) instead. " + "Requires repo_url, git_ref (tag/commit/branch), and file_path.",
6944
+ description: "Read source code from a dependency's repository by file path and line range — " + "use when you have a file path from a stack trace or symbol lookup. " + "Returns full file or a specific line range. " + "Prefer find_symbol(include_code=true) for function/class lookup. " + "Requires repo_url, git_ref, and file_path.",
6651
6945
  schema: argsSchema3,
6946
+ annotations: { readOnlyHint: true },
6652
6947
  handler: async ({ repo_url, git_ref, file_path, start_line, end_line }, _extra) => {
6653
6948
  return withErrorHandling("fetch code context", async () => {
6654
6949
  const result = await pkgseerService.fetchCodeContext(repo_url, git_ref, file_path, {
@@ -6658,10 +6953,10 @@ function createFetchCodeContextTool(pkgseerService) {
6658
6953
  const graphqlError = handleGraphQLErrors(result.errors);
6659
6954
  if (graphqlError)
6660
6955
  return graphqlError;
6661
- if (!result.data.fetchCodeContext) {
6956
+ if (!result.data?.fetchCodeContext) {
6662
6957
  return errorResult(`Code not found: ${file_path} at ${git_ref} in ${repo_url}. ` + "Check that the repository, file path, and git ref are correct.");
6663
6958
  }
6664
- return textResult(JSON.stringify(result.data.fetchCodeContext, null, 2));
6959
+ return textResult(JSON.stringify(result.data?.fetchCodeContext, null, 2));
6665
6960
  });
6666
6961
  }
6667
6962
  };
@@ -6676,16 +6971,17 @@ function createFetchPackageDocTool(pkgseerService) {
6676
6971
  name: "fetch_package_doc",
6677
6972
  description: "Get full content of a documentation page. Requires page_id from list_package_docs. " + "Returns: title, full content (markdown/HTML), format type, navigation breadcrumbs, " + "source URL, and last updated timestamp. Use this when you need complete documentation, " + "not just search snippets.",
6678
6973
  schema: argsSchema4,
6974
+ annotations: { readOnlyHint: true },
6679
6975
  handler: async ({ page_id }, _extra) => {
6680
6976
  return withErrorHandling("fetch documentation page", async () => {
6681
6977
  const result = await pkgseerService.getDocPage(page_id);
6682
6978
  const graphqlError = handleGraphQLErrors(result.errors);
6683
6979
  if (graphqlError)
6684
6980
  return graphqlError;
6685
- if (!result.data.getDocPage) {
6981
+ if (!result.data?.getDocPage) {
6686
6982
  return errorResult(`Documentation page not found: ${page_id}`);
6687
6983
  }
6688
- return textResult(JSON.stringify(result.data.getDocPage, null, 2));
6984
+ return textResult(JSON.stringify(result.data?.getDocPage, null, 2));
6689
6985
  });
6690
6986
  }
6691
6987
  };
@@ -6716,11 +7012,12 @@ var argsSchema5 = {
6716
7012
  function createFindSymbolTool(pkgseerService) {
6717
7013
  return {
6718
7014
  name: "find_symbol",
6719
- description: "Find functions, methods, or classes by name in a package. " + "Set include_code=true to get full source code inline, avoiding separate fetch_code_context calls. " + "Returns: symbol ID, name, qualified path, kind, file location, and optionally source code. " + "Omit name to get all public exports. Use namespace to filter by module path.",
7015
+ description: "Read source code of any function, class, or method in a third-party package — " + "use instead of guessing implementations from training data. " + "Set include_code=true for full source inline. " + "Returns: symbol ref, name, qualified path, kind, file location, and optionally source code. " + "Omit name to get all public exports. Use namespace to filter by module path.",
6720
7016
  schema: argsSchema5,
7017
+ annotations: { readOnlyHint: true },
6721
7018
  handler: async (args, _extra) => {
6722
7019
  return withErrorHandling("find symbol", async () => {
6723
- const result = await pkgseerService.findSymbol(toGraphQLRegistry2(args.registry), args.package_name, {
7020
+ const result = await pkgseerService.findSymbol(toGraphQLRegistry(args.registry), args.package_name, {
6724
7021
  name: args.name,
6725
7022
  namespace: args.namespace,
6726
7023
  kind: toSymbolKind(args.kind),
@@ -6734,10 +7031,10 @@ function createFindSymbolTool(pkgseerService) {
6734
7031
  const graphqlError = handleGraphQLErrors(result.errors);
6735
7032
  if (graphqlError)
6736
7033
  return graphqlError;
6737
- if (!result.data.findSymbol) {
7034
+ if (!result.data?.findSymbol) {
6738
7035
  return notFoundError(args.package_name, args.registry);
6739
7036
  }
6740
- const data = result.data.findSymbol;
7037
+ const data = result.data?.findSymbol;
6741
7038
  if (data.symbols.length === 0 && data.diagnostics?.hint) {
6742
7039
  return textResult(JSON.stringify({ ...data, _hint: data.diagnostics.hint }, null, 2));
6743
7040
  }
@@ -6761,11 +7058,12 @@ var argsSchema6 = {
6761
7058
  function createGrepRepoFileTool(pkgseerService) {
6762
7059
  return {
6763
7060
  name: "grep_repo_file",
6764
- description: "Search for a pattern within a file in a package's repository. " + "Case-insensitive substring matching. Returns matching lines with context. " + "Faster than fetch_code_context for large files. " + "Use list_repo_files to discover available file paths.",
7061
+ description: "Search for a pattern within a dependency's source file " + "find error messages, config keys, or specific patterns without cloning the repo. " + "Case-insensitive substring matching with context lines. " + "Faster than fetch_code_context for large files. " + "Use list_repo_files to discover file paths.",
6765
7062
  schema: argsSchema6,
7063
+ annotations: { readOnlyHint: true },
6766
7064
  handler: async (args, _extra) => {
6767
7065
  return withErrorHandling("grep repo file", async () => {
6768
- const result = await pkgseerService.grepRepoFile(toGraphQLRegistry2(args.registry), args.package_name, args.file_path, args.pattern, {
7066
+ const result = await pkgseerService.grepRepoFile(toGraphQLRegistry(args.registry), args.package_name, args.file_path, args.pattern, {
6769
7067
  contextLines: args.context_lines,
6770
7068
  maxMatches: args.max_matches,
6771
7069
  version: args.version,
@@ -6774,10 +7072,10 @@ function createGrepRepoFileTool(pkgseerService) {
6774
7072
  const graphqlError = handleGraphQLErrors(result.errors);
6775
7073
  if (graphqlError)
6776
7074
  return graphqlError;
6777
- if (!result.data.grepRepoFile) {
7075
+ if (!result.data?.grepRepoFile) {
6778
7076
  return notFoundError(args.package_name, args.registry);
6779
7077
  }
6780
- const data = result.data.grepRepoFile;
7078
+ const data = result.data?.grepRepoFile;
6781
7079
  if (data.matches.length === 0 && data.diagnostics?.hint) {
6782
7080
  return textResult(JSON.stringify({ ...data, _hint: data.diagnostics.hint }, null, 2));
6783
7081
  }
@@ -6795,18 +7093,19 @@ var argsSchema7 = {
6795
7093
  function createListPackageDocsTool(pkgseerService) {
6796
7094
  return {
6797
7095
  name: "list_package_docs",
6798
- description: "Discover available documentation pages for a package. Start here before fetching or searching docs. " + "Returns: page titles, unique IDs (needed for fetch_package_doc), word counts, and update timestamps. " + "Workflow: list_package_docs → fetch_package_doc for full content, or search_package_docs to find specific topics.",
7096
+ description: "Discover available documentation pages for a package. Start here before fetching or searching docs. " + "Returns: page titles, unique IDs (needed for fetch_package_doc), word counts, and update timestamps. " + "Workflow: list_package_docs → fetch_package_doc for full content, or search(mode='docs') to find specific topics.",
6799
7097
  schema: argsSchema7,
7098
+ annotations: { readOnlyHint: true },
6800
7099
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
6801
7100
  return withErrorHandling("list package documentation", async () => {
6802
- const result = await pkgseerService.listPackageDocs(toGraphQLRegistry2(registry), package_name, version2);
7101
+ const result = await pkgseerService.listPackageDocs(toGraphQLRegistry(registry), package_name, version2);
6803
7102
  const graphqlError = handleGraphQLErrors(result.errors);
6804
7103
  if (graphqlError)
6805
7104
  return graphqlError;
6806
- if (!result.data.listPackageDocs) {
7105
+ if (!result.data?.listPackageDocs) {
6807
7106
  return notFoundError(package_name, registry);
6808
7107
  }
6809
- return textResult(JSON.stringify(result.data.listPackageDocs, null, 2));
7108
+ return textResult(JSON.stringify(result.data?.listPackageDocs, null, 2));
6810
7109
  });
6811
7110
  }
6812
7111
  };
@@ -6824,11 +7123,12 @@ var argsSchema8 = {
6824
7123
  function createListRepoFilesTool(pkgseerService) {
6825
7124
  return {
6826
7125
  name: "list_repo_files",
6827
- description: "List files in a package's indexed repository. " + "Returns file paths, names, languages, types (source/doc/config), and sizes. " + "Use path_prefix to filter by directory (e.g., 'src/'). " + "Useful for exploring package structure before searching or reading code.",
7126
+ description: "Explore a dependency's file structure — " + "see what source files, configs, and docs exist. " + "Returns file paths, names, languages, types (source/doc/config), and sizes. " + "Use path_prefix to filter by directory (e.g., 'src/'). " + "Good first step when investigating unfamiliar package internals.",
6828
7127
  schema: argsSchema8,
7128
+ annotations: { readOnlyHint: true },
6829
7129
  handler: async (args, _extra) => {
6830
7130
  return withErrorHandling("list repo files", async () => {
6831
- const result = await pkgseerService.listRepoFiles(toGraphQLRegistry2(args.registry), args.package_name, {
7131
+ const result = await pkgseerService.listRepoFiles(toGraphQLRegistry(args.registry), args.package_name, {
6832
7132
  version: args.version,
6833
7133
  pathPrefix: args.path_prefix,
6834
7134
  limit: args.limit,
@@ -6837,10 +7137,10 @@ function createListRepoFilesTool(pkgseerService) {
6837
7137
  const graphqlError = handleGraphQLErrors(result.errors);
6838
7138
  if (graphqlError)
6839
7139
  return graphqlError;
6840
- if (!result.data.listRepoFiles) {
7140
+ if (!result.data?.listRepoFiles) {
6841
7141
  return notFoundError(args.package_name, args.registry);
6842
7142
  }
6843
- const data = result.data.listRepoFiles;
7143
+ const data = result.data?.listRepoFiles;
6844
7144
  if (data.files.length === 0 && data.diagnostics?.hint) {
6845
7145
  return textResult(JSON.stringify({ ...data, _hint: data.diagnostics.hint }, null, 2));
6846
7146
  }
@@ -6867,8 +7167,9 @@ var argsSchema9 = {
6867
7167
  function createListSymbolsTool(pkgseerService) {
6868
7168
  return {
6869
7169
  name: "list_symbols",
6870
- description: "Browse symbols in a package by scope. " + "Use scope='exports' for public API with namespace grouping and optional popularity. " + "Use scope='file' with file_path for all symbols in a specific file. " + "Returns symbols with IDs, names, qualified paths, kinds, and file locations. " + "Use find_symbol(name=<symbol>, include_code=true) to get source for any listed symbol.",
7170
+ description: "Browse a dependency's public API or file-level symbols. " + "Use scope='exports' for the public API with optional popularity ranking. " + "Use scope='file' with file_path for all symbols in a specific file. " + "Returns symbol refs, names, qualified paths, kinds, and file locations. " + "Use find_symbol(name=<symbol>, include_code=true) to read source for any result.",
6871
7171
  schema: argsSchema9,
7172
+ annotations: { readOnlyHint: true },
6872
7173
  handler: async (args, _extra) => {
6873
7174
  return withErrorHandling("list symbols", async () => {
6874
7175
  const scope = toListScope(args.scope);
@@ -6883,7 +7184,7 @@ function createListSymbolsTool(pkgseerService) {
6883
7184
  isError: true
6884
7185
  };
6885
7186
  }
6886
- const result = await pkgseerService.listSymbols(toGraphQLRegistry2(args.registry), args.package_name, scope, {
7187
+ const result = await pkgseerService.listSymbols(toGraphQLRegistry(args.registry), args.package_name, scope, {
6887
7188
  filePath: args.file_path,
6888
7189
  namespace: args.namespace,
6889
7190
  publicOnly: args.public_only,
@@ -6896,10 +7197,10 @@ function createListSymbolsTool(pkgseerService) {
6896
7197
  const graphqlError = handleGraphQLErrors(result.errors);
6897
7198
  if (graphqlError)
6898
7199
  return graphqlError;
6899
- if (!result.data.listSymbols) {
7200
+ if (!result.data?.listSymbols) {
6900
7201
  return notFoundError(args.package_name, args.registry);
6901
7202
  }
6902
- return textResult(JSON.stringify(result.data.listSymbols, null, 2));
7203
+ return textResult(JSON.stringify(result.data?.listSymbols, null, 2));
6903
7204
  });
6904
7205
  }
6905
7206
  };
@@ -6990,22 +7291,23 @@ function createPackageDependenciesTool(pkgseerService) {
6990
7291
  name: "package_dependencies",
6991
7292
  description: "Analyze package dependencies. By default returns direct dependencies only. " + "Set include_transitive=true to get the full dependency tree (use max_depth to limit large trees). " + "Returns: dependency names, version constraints, and for transitive deps, a graph with depth levels. " + "Use this to understand complexity before adding a package, or to find nested dependencies.",
6992
7293
  schema: argsSchema10,
7294
+ annotations: { readOnlyHint: true },
6993
7295
  handler: async ({ registry, package_name, version: version2, include_transitive, max_depth }, _extra) => {
6994
7296
  return withErrorHandling("fetch package dependencies", async () => {
6995
- const result = await pkgseerService.getPackageDependencies(toGraphQLRegistry2(registry), package_name, version2, include_transitive, max_depth);
7297
+ const result = await pkgseerService.getPackageDependencies(toGraphQLRegistry(registry), package_name, version2, include_transitive, max_depth);
6996
7298
  const graphqlError = handleGraphQLErrors(result.errors);
6997
7299
  if (graphqlError)
6998
7300
  return graphqlError;
6999
- if (!result.data.packageDependencies) {
7301
+ if (!result.data?.packageDependencies) {
7000
7302
  return notFoundError(package_name, registry);
7001
7303
  }
7002
- const deps = result.data.packageDependencies.dependencies;
7304
+ const deps = result.data?.packageDependencies.dependencies;
7003
7305
  const decodedDag = decodeDag(deps?.transitive?.dag);
7004
7306
  const transitiveDag = deps?.transitive?.dag;
7005
7307
  const edgesWithDepth = buildEdgeDepths(decodedDag, typeof transitiveDag?.root === "string" ? transitiveDag.root : undefined);
7006
7308
  const output2 = {
7007
7309
  summary: deps?.summary,
7008
- package: result.data.packageDependencies.package,
7310
+ package: result.data?.packageDependencies.package,
7009
7311
  direct: deps?.direct,
7010
7312
  transitive: deps?.transitive ? {
7011
7313
  totalEdges: deps.transitive.totalEdges,
@@ -7019,7 +7321,7 @@ function createPackageDependenciesTool(pkgseerService) {
7019
7321
  raw: deps.transitive.dag
7020
7322
  }
7021
7323
  } : undefined,
7022
- raw: result.data.packageDependencies
7324
+ raw: result.data?.packageDependencies
7023
7325
  };
7024
7326
  return textResult(JSON.stringify(output2, null, 2));
7025
7327
  });
@@ -7041,11 +7343,12 @@ var argsSchema11 = {
7041
7343
  function createPackageImportsTool(pkgseerService) {
7042
7344
  return {
7043
7345
  name: "package_imports",
7044
- description: "Get import statements in a package. " + "Returns parsed import/require/use statements with optional resolution to known packages. " + "Use resolved_only=true to see only imports that map to packages in the PkgSeer registry. " + "Includes: source path, imported names, and de-duplicated dependency list.",
7346
+ description: "See what a dependency imports — understand its internal dependencies and external package usage. " + "Returns parsed import/require/use statements with optional resolution to known packages. " + "Use resolved_only=true to see only imports that map to packages in the PkgSeer registry. " + "Includes: source path, imported names, and de-duplicated dependency list.",
7045
7347
  schema: argsSchema11,
7348
+ annotations: { readOnlyHint: true },
7046
7349
  handler: async (args, _extra) => {
7047
7350
  return withErrorHandling("fetch package imports", async () => {
7048
- const result = await pkgseerService.packageImports(toGraphQLRegistry2(args.registry), args.package_name, {
7351
+ const result = await pkgseerService.packageImports(toGraphQLRegistry(args.registry), args.package_name, {
7049
7352
  filePath: args.file_path,
7050
7353
  resolvedOnly: args.resolved_only,
7051
7354
  limit: args.limit,
@@ -7056,10 +7359,10 @@ function createPackageImportsTool(pkgseerService) {
7056
7359
  const graphqlError = handleGraphQLErrors(result.errors);
7057
7360
  if (graphqlError)
7058
7361
  return graphqlError;
7059
- if (!result.data.packageImports) {
7362
+ if (!result.data?.packageImports) {
7060
7363
  return notFoundError(args.package_name, args.registry);
7061
7364
  }
7062
- return textResult(JSON.stringify(result.data.packageImports, null, 2));
7365
+ return textResult(JSON.stringify(result.data?.packageImports, null, 2));
7063
7366
  });
7064
7367
  }
7065
7368
  };
@@ -7075,16 +7378,17 @@ function createPackageQualityTool(pkgseerService) {
7075
7378
  name: "package_quality",
7076
7379
  description: "Evaluate package maintenance health and code quality. Use this to assess if a package is well-maintained " + "before adding it as a dependency. Returns: overall score (0-100), category scores (documentation, " + "testing, community, maintenance), and individual rule results with pass/fail status. " + "Useful for comparing quality between alternative packages.",
7077
7380
  schema: argsSchema12,
7381
+ annotations: { readOnlyHint: true },
7078
7382
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
7079
7383
  return withErrorHandling("fetch package quality", async () => {
7080
- const result = await pkgseerService.getPackageQuality(toGraphQLRegistry2(registry), package_name, version2);
7384
+ const result = await pkgseerService.getPackageQuality(toGraphQLRegistry(registry), package_name, version2);
7081
7385
  const graphqlError = handleGraphQLErrors(result.errors);
7082
7386
  if (graphqlError)
7083
7387
  return graphqlError;
7084
- if (!result.data.packageQuality) {
7388
+ if (!result.data?.packageQuality) {
7085
7389
  return notFoundError(package_name, registry);
7086
7390
  }
7087
- return textResult(JSON.stringify(result.data.packageQuality, null, 2));
7391
+ return textResult(JSON.stringify(result.data?.packageQuality, null, 2));
7088
7392
  });
7089
7393
  }
7090
7394
  };
@@ -7099,16 +7403,17 @@ function createPackageSummaryTool(pkgseerService) {
7099
7403
  name: "package_summary",
7100
7404
  description: "Get a comprehensive overview of a package. Use this as your first tool when researching a package. " + "Returns: description, latest version, license, repository URL, download stats, " + "active security advisories count, and quickstart/installation instructions. " + "For deeper analysis, follow up with package_quality (maintenance health) or " + "package_vulnerabilities (security details).",
7101
7405
  schema: argsSchema13,
7406
+ annotations: { readOnlyHint: true },
7102
7407
  handler: async ({ registry, package_name }, _extra) => {
7103
7408
  return withErrorHandling("fetch package summary", async () => {
7104
- const result = await pkgseerService.getPackageSummary(toGraphQLRegistry2(registry), package_name);
7409
+ const result = await pkgseerService.getPackageSummary(toGraphQLRegistry(registry), package_name);
7105
7410
  const graphqlError = handleGraphQLErrors(result.errors);
7106
7411
  if (graphqlError)
7107
7412
  return graphqlError;
7108
- if (!result.data.packageSummary) {
7413
+ if (!result.data?.packageSummary) {
7109
7414
  return notFoundError(package_name, registry);
7110
7415
  }
7111
- return textResult(JSON.stringify(result.data.packageSummary, null, 2));
7416
+ return textResult(JSON.stringify(result.data?.packageSummary, null, 2));
7112
7417
  });
7113
7418
  }
7114
7419
  };
@@ -7124,16 +7429,17 @@ function createPackageVulnerabilitiesTool(pkgseerService) {
7124
7429
  name: "package_vulnerabilities",
7125
7430
  description: "Check security vulnerabilities for a package. Use this before adding dependencies or when auditing existing ones. " + "Returns: list of CVEs/advisories with severity levels, affected version ranges, fixed versions, " + "and upgrade recommendations. If version is specified, shows only vulnerabilities affecting that version.",
7126
7431
  schema: argsSchema14,
7432
+ annotations: { readOnlyHint: true },
7127
7433
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
7128
7434
  return withErrorHandling("fetch package vulnerabilities", async () => {
7129
- const result = await pkgseerService.getPackageVulnerabilities(toGraphQLRegistry2(registry), package_name, version2);
7435
+ const result = await pkgseerService.getPackageVulnerabilities(toGraphQLRegistry(registry), package_name, version2);
7130
7436
  const graphqlError = handleGraphQLErrors(result.errors);
7131
7437
  if (graphqlError)
7132
7438
  return graphqlError;
7133
- if (!result.data.packageVulnerabilities) {
7439
+ if (!result.data?.packageVulnerabilities) {
7134
7440
  return notFoundError(package_name, registry);
7135
7441
  }
7136
- return textResult(JSON.stringify(result.data.packageVulnerabilities, null, 2));
7442
+ return textResult(JSON.stringify(result.data?.packageVulnerabilities, null, 2));
7137
7443
  });
7138
7444
  }
7139
7445
  };
@@ -7191,6 +7497,7 @@ function createSearchTool(pkgseerService) {
7191
7497
  name: "search",
7192
7498
  description: "Search code and documentation across packages. Returns functions, classes, and doc pages " + "matching your query. Use mode='code' for code only, mode='docs' for docs only, or " + "mode='all' (default). Provide 1-20 packages. Results include relevance scores and snippets. " + "For full source of code results, use find_symbol(name=<title>, include_code=true). " + "If packages need indexing, waits up to waitTimeoutMs (default 10s) or returns searchRef — use search_status to poll.",
7193
7499
  schema: argsSchema15,
7500
+ annotations: { readOnlyHint: true },
7194
7501
  handler: async ({ packages, query, mode, limit, waitTimeoutMs }, _extra) => {
7195
7502
  return withErrorHandling("search packages", async () => {
7196
7503
  const normalizedQuery = query.trim();
@@ -7198,7 +7505,7 @@ function createSearchTool(pkgseerService) {
7198
7505
  return errorResult("Search query is required.");
7199
7506
  }
7200
7507
  const graphqlPackages = packages.map((pkg) => ({
7201
- registry: toGraphQLRegistry2(pkg.registry),
7508
+ registry: toGraphQLRegistry(pkg.registry),
7202
7509
  name: pkg.name,
7203
7510
  version: pkg.version
7204
7511
  }));
@@ -7210,7 +7517,7 @@ function createSearchTool(pkgseerService) {
7210
7517
  const graphqlError = handleGraphQLErrors(result.errors);
7211
7518
  if (graphqlError)
7212
7519
  return graphqlError;
7213
- const asyncResult = result.data.combinedSearch;
7520
+ const asyncResult = result.data?.combinedSearch;
7214
7521
  if (!asyncResult) {
7215
7522
  return errorResult("No search results returned. Check package names and registries.");
7216
7523
  }
@@ -7255,8 +7562,9 @@ function createSearchProjectDocsTool(deps) {
7255
7562
  const { pkgseerService, configService, defaultProjectDir } = deps;
7256
7563
  return {
7257
7564
  name: "search_project_docs",
7258
- description: "Searches documentation across all dependencies in a PkgSeer project using search terms and match modes (any/all). Returns ranked results from multiple packages. Use project_directory to specify which project's context to use, or project to search a specific project directly.",
7565
+ description: "Search documentation across all dependencies in a PkgSeer project " + "use when you need to find information but don't know which dependency has it. " + "Requires a pkgseer.yml project config (or pass project name directly). " + "Returns ranked results with snippets from multiple packages. " + "Use project_directory to specify which project's context to use. " + "For single-package doc search, use search(mode='docs') instead.",
7259
7566
  schema: argsSchema16,
7567
+ annotations: { readOnlyHint: true },
7260
7568
  handler: async ({
7261
7569
  project_directory,
7262
7570
  project,
@@ -7290,13 +7598,13 @@ function createSearchProjectDocsTool(deps) {
7290
7598
  const graphqlError = handleGraphQLErrors(result.errors);
7291
7599
  if (graphqlError)
7292
7600
  return graphqlError;
7293
- if (!result.data.searchProjectDocs) {
7601
+ if (!result.data?.searchProjectDocs) {
7294
7602
  return errorResult(`Project not found: ${resolvedProject}`);
7295
7603
  }
7296
- if ((result.data.searchProjectDocs.entries ?? []).length === 0 && normalizedTerms.length > 0) {
7604
+ if ((result.data?.searchProjectDocs.entries ?? []).length === 0 && normalizedTerms.length > 0) {
7297
7605
  return errorResult(`No documentation matched: ${normalizedTerms.join(", ")}. ` + "Try fewer or broader terms, or reduce match constraints.");
7298
7606
  }
7299
- return textResult(JSON.stringify(result.data.searchProjectDocs, null, 2));
7607
+ return textResult(JSON.stringify(result.data?.searchProjectDocs, null, 2));
7300
7608
  });
7301
7609
  }
7302
7610
  };
@@ -7311,13 +7619,14 @@ function createSearchStatusTool(pkgseerService) {
7311
7619
  name: "search_status",
7312
7620
  description: "Check the status of an async search and get results if complete. " + "Use after search returns completed=false with a searchRef. Sessions expire after 1 hour. " + "Returns status (PENDING, INDEXING, SEARCHING, COMPLETED, TIMEOUT, FAILED), " + "progress info (packagesReady/packagesTotal), and results when COMPLETED.",
7313
7621
  schema: argsSchema17,
7622
+ annotations: { readOnlyHint: true },
7314
7623
  handler: async ({ searchRef }, _extra) => {
7315
7624
  return withErrorHandling("check search status", async () => {
7316
7625
  const progressResult = await pkgseerService.getSearchProgress(searchRef);
7317
7626
  const graphqlError = handleGraphQLErrors(progressResult.errors);
7318
7627
  if (graphqlError)
7319
7628
  return graphqlError;
7320
- const progress = progressResult.data.searchProgress;
7629
+ const progress = progressResult.data?.searchProgress;
7321
7630
  if (!progress) {
7322
7631
  return errorResult("Search session not found. It may have expired (sessions last 1 hour).");
7323
7632
  }
@@ -7326,7 +7635,7 @@ function createSearchStatusTool(pkgseerService) {
7326
7635
  const resultsError = handleGraphQLErrors(resultsResponse.errors);
7327
7636
  if (resultsError)
7328
7637
  return resultsError;
7329
- const searchResults = resultsResponse.data.searchResults;
7638
+ const searchResults = resultsResponse.data?.searchResults;
7330
7639
  if (!searchResults) {
7331
7640
  return errorResult("Search completed but results are no longer available. " + "The session may have expired (sessions last 1 hour).");
7332
7641
  }
@@ -7376,11 +7685,12 @@ var argsSchema18 = {
7376
7685
  function createSearchSymbolsTool(pkgseerService) {
7377
7686
  return {
7378
7687
  name: "search_symbols",
7379
- description: "Full-text search within package code. " + "Searches across functions, classes, modules, and doc sections. " + "Supports query string or keyword list with match_mode. " + "Use file_path to filter by directory (e.g., 'src/'). " + "Returns matching code chunks with name, type, file path, line numbers, and content previews. " + "Follow-up: use find_symbol(name=<result.name>, include_code=true) to get full source.",
7688
+ description: "Search a dependency's source code by text — " + "find where error messages, exceptions, or patterns appear across functions, classes, and modules. " + "Supports query string or keyword list with match_mode. " + "Use file_path to filter by directory (e.g., 'src/'). " + "Returns matching code chunks with name, type, file path, line numbers, and content previews. " + "Follow up with find_symbol(name=<result.name>, include_code=true) for full source.",
7380
7689
  schema: argsSchema18,
7690
+ annotations: { readOnlyHint: true },
7381
7691
  handler: async (args, _extra) => {
7382
7692
  return withErrorHandling("search symbols", async () => {
7383
- const result = await pkgseerService.searchSymbols(toGraphQLRegistry2(args.registry), args.package_name, {
7693
+ const result = await pkgseerService.searchSymbols(toGraphQLRegistry(args.registry), args.package_name, {
7384
7694
  query: args.query,
7385
7695
  keywords: args.keywords,
7386
7696
  matchMode: toMatchMode(args.match_mode),
@@ -7394,10 +7704,10 @@ function createSearchSymbolsTool(pkgseerService) {
7394
7704
  const graphqlError = handleGraphQLErrors(result.errors);
7395
7705
  if (graphqlError)
7396
7706
  return graphqlError;
7397
- if (!result.data.searchSymbols) {
7707
+ if (!result.data?.searchSymbols) {
7398
7708
  return notFoundError(args.package_name, args.registry);
7399
7709
  }
7400
- const data = result.data.searchSymbols;
7710
+ const data = result.data?.searchSymbols;
7401
7711
  const extra = {};
7402
7712
  if (data.results.length === 0 && data.diagnostics?.hint) {
7403
7713
  extra._hint = data.diagnostics.hint;
@@ -7416,7 +7726,7 @@ function createSearchSymbolsTool(pkgseerService) {
7416
7726
  // src/tools/symbol-callees.ts
7417
7727
  import { z as z17 } from "zod";
7418
7728
  var argsSchema19 = {
7419
- symbol: schemas.symbolReference.describe("Symbol to find callees for. Provide either symbol_id or registry + package_name + symbol_name."),
7729
+ symbol: schemas.symbolReference.describe("Symbol to find callees for. Provide either symbol_ref or registry + package_name + symbol_name."),
7420
7730
  max_depth: z17.number().int().min(1).max(5).optional().describe("BFS traversal depth (1 = direct callees only, 2+ = transitive, max 5)"),
7421
7731
  limit: z17.number().int().min(1).max(100).optional().describe("Maximum dependency entries to return"),
7422
7732
  include_external: z17.boolean().optional().describe("Include calls to symbols in other packages"),
@@ -7427,13 +7737,14 @@ var argsSchema19 = {
7427
7737
  function createSymbolCalleesTool(pkgseerService) {
7428
7738
  return {
7429
7739
  name: "symbol_callees",
7430
- description: "Find what a symbol calls (its dependencies). " + "Returns direct callees at max_depth=1, or transitive call chains at higher depths. " + "Includes callee names, qualified paths, file locations, call lines, and external package references. " + "Get symbol_id from find_symbol or list_symbols results. See symbol_callers for the reverse.",
7740
+ description: "Trace what a function calls internally — " + "understand dependency behavior by following its execution path. " + "Returns direct callees at max_depth=1, or transitive call chains at higher depths. " + "Includes callee names, qualified paths, file locations, call lines, and external package references. " + "Get symbol_ref from find_symbol or list_symbols. See symbol_callers for the reverse.",
7431
7741
  schema: argsSchema19,
7742
+ annotations: { readOnlyHint: true },
7432
7743
  handler: async (args, _extra) => {
7433
7744
  return withErrorHandling("find symbol callees", async () => {
7434
7745
  const symbolInput = toSymbolReferenceInput(args.symbol);
7435
- if (!symbolInput.symbolId && (!symbolInput.symbolName || !symbolInput.registry || !symbolInput.packageName)) {
7436
- return errorResult("Provide either symbol_id or symbol_name with registry and package_name to identify the symbol.");
7746
+ if (!symbolInput.symbolRef && (!symbolInput.symbolName || !symbolInput.registry || !symbolInput.packageName)) {
7747
+ return errorResult("Provide either symbol_ref or symbol_name with registry and package_name to identify the symbol.");
7437
7748
  }
7438
7749
  const result = await pkgseerService.symbolDependencies(symbolInput, {
7439
7750
  maxDepth: args.max_depth,
@@ -7446,10 +7757,10 @@ function createSymbolCalleesTool(pkgseerService) {
7446
7757
  const graphqlError = handleGraphQLErrors(result.errors);
7447
7758
  if (graphqlError)
7448
7759
  return graphqlError;
7449
- if (!result.data.symbolDependencies) {
7760
+ if (!result.data?.symbolDependencies) {
7450
7761
  return errorResult("Symbol not found. Verify the symbol reference and that the package is indexed.");
7451
7762
  }
7452
- return textResult(JSON.stringify(result.data.symbolDependencies, null, 2));
7763
+ return textResult(JSON.stringify(result.data?.symbolDependencies, null, 2));
7453
7764
  });
7454
7765
  }
7455
7766
  };
@@ -7457,8 +7768,9 @@ function createSymbolCalleesTool(pkgseerService) {
7457
7768
  // src/tools/symbol-callers.ts
7458
7769
  import { z as z18 } from "zod";
7459
7770
  var argsSchema20 = {
7460
- symbol: schemas.symbolReference.describe("Symbol to find callers for. Provide either symbol_id or registry + package_name + symbol_name."),
7771
+ symbol: schemas.symbolReference.describe("Symbol to find callers for. Provide either symbol_ref or registry + package_name + symbol_name."),
7461
7772
  same_package_only: z18.boolean().optional().describe("Only return callers from the same package"),
7773
+ max_depth: z18.number().int().min(1).max(5).optional().describe("BFS traversal depth (1 = direct callers only, 2+ = transitive, max 5)"),
7462
7774
  limit: z18.number().int().min(1).max(100).optional().describe("Maximum entries to return"),
7463
7775
  mode: schemas.navigationMode,
7464
7776
  wait_timeout_ms: schemas.waitTimeoutMs
@@ -7466,16 +7778,18 @@ var argsSchema20 = {
7466
7778
  function createSymbolCallersTool(pkgseerService) {
7467
7779
  return {
7468
7780
  name: "symbol_callers",
7469
- description: "Find what calls a symbol (its dependents/callers). " + "Returns the target symbol and a list of callers with file locations and call line numbers. " + "Use same_package_only to limit to callers within the same package. " + "Get symbol_id from find_symbol or list_symbols results. See symbol_callees for the reverse.",
7781
+ description: "Trace what calls a function in a dependency's source code — " + "find entry points or debug why behavior triggers. " + "Returns callers with file locations and call line numbers. " + "Use same_package_only for within-package callers only. " + "Get symbol_ref from find_symbol or list_symbols. See symbol_callees for the reverse.",
7470
7782
  schema: argsSchema20,
7783
+ annotations: { readOnlyHint: true },
7471
7784
  handler: async (args, _extra) => {
7472
7785
  return withErrorHandling("find symbol callers", async () => {
7473
7786
  const symbolInput = toSymbolReferenceInput(args.symbol);
7474
- if (!symbolInput.symbolId && (!symbolInput.symbolName || !symbolInput.registry || !symbolInput.packageName)) {
7475
- return errorResult("Provide either symbol_id or symbol_name with registry and package_name to identify the symbol.");
7787
+ if (!symbolInput.symbolRef && (!symbolInput.symbolName || !symbolInput.registry || !symbolInput.packageName)) {
7788
+ return errorResult("Provide either symbol_ref or symbol_name with registry and package_name to identify the symbol.");
7476
7789
  }
7477
7790
  const result = await pkgseerService.symbolDependents(symbolInput, {
7478
7791
  samePackageOnly: args.same_package_only,
7792
+ maxDepth: args.max_depth,
7479
7793
  maxResults: args.limit,
7480
7794
  mode: toNavigationMode(args.mode),
7481
7795
  waitTimeoutMs: args.wait_timeout_ms
@@ -7483,10 +7797,10 @@ function createSymbolCallersTool(pkgseerService) {
7483
7797
  const graphqlError = handleGraphQLErrors(result.errors);
7484
7798
  if (graphqlError)
7485
7799
  return graphqlError;
7486
- if (!result.data.symbolDependents) {
7800
+ if (!result.data?.symbolDependents) {
7487
7801
  return errorResult("Symbol not found. Verify the symbol reference and that the package is indexed.");
7488
7802
  }
7489
- return textResult(JSON.stringify(result.data.symbolDependents, null, 2));
7803
+ return textResult(JSON.stringify(result.data?.symbolDependents, null, 2));
7490
7804
  });
7491
7805
  }
7492
7806
  };
@@ -7512,10 +7826,11 @@ function createTriggerIndexingTool(pkgseerService) {
7512
7826
  name: "trigger_indexing",
7513
7827
  description: "Pre-warm PkgSeer indexes by triggering indexing for packages and/or repositories. " + "Fire-and-forget: triggers jobs and returns immediately with accepted/skipped counts. " + "Use before search requests to ensure packages are indexed. " + "Rate limited to 10 requests per minute.",
7514
7828
  schema: argsSchema21,
7829
+ annotations: { readOnlyHint: false, idempotentHint: true },
7515
7830
  handler: async (args, _extra) => {
7516
7831
  return withErrorHandling("trigger indexing", async () => {
7517
7832
  const packages = args.packages?.map((p) => ({
7518
- registry: toGraphQLRegistry2(p.registry),
7833
+ registry: toGraphQLRegistry(p.registry),
7519
7834
  name: p.name,
7520
7835
  version: p.version
7521
7836
  }));
@@ -7531,7 +7846,7 @@ function createTriggerIndexingTool(pkgseerService) {
7531
7846
  const graphqlError = handleGraphQLErrors(result.errors);
7532
7847
  if (graphqlError)
7533
7848
  return graphqlError;
7534
- if (!result.data.triggerIndexing) {
7849
+ if (!result.data?.triggerIndexing) {
7535
7850
  return {
7536
7851
  content: [
7537
7852
  {
@@ -7542,7 +7857,7 @@ function createTriggerIndexingTool(pkgseerService) {
7542
7857
  isError: true
7543
7858
  };
7544
7859
  }
7545
- return textResult(JSON.stringify(result.data.triggerIndexing, null, 2));
7860
+ return textResult(JSON.stringify(result.data?.triggerIndexing, null, 2));
7546
7861
  });
7547
7862
  }
7548
7863
  };
@@ -7570,11 +7885,12 @@ var argsSchema22 = {
7570
7885
  function createVersionDiffTool(pkgseerService) {
7571
7886
  return {
7572
7887
  name: "version_diff",
7573
- description: "Compare symbols between two package versions. " + "Detects added, removed, moved, and modified symbols with severity " + "(breaking, behavior_change, non_breaking, internal). " + "Returns summary counts and per-symbol changes with file locations.",
7888
+ description: "Compare two versions of a dependency to understand what changed — " + "use when debugging issues after upgrades or planning migrations. " + "Detects added, removed, moved, and modified symbols with severity " + "(breaking, behavior_change, non_breaking, internal). " + "Returns summary counts and per-symbol changes with file locations.",
7574
7889
  schema: argsSchema22,
7890
+ annotations: { readOnlyHint: true },
7575
7891
  handler: async (args, _extra) => {
7576
7892
  return withErrorHandling("version diff", async () => {
7577
- const result = await pkgseerService.versionDiff(toGraphQLRegistry2(args.registry), args.package_name, args.from_version, args.to_version, {
7893
+ const result = await pkgseerService.versionDiff(toGraphQLRegistry(args.registry), args.package_name, args.from_version, args.to_version, {
7578
7894
  includePrivate: args.include_private,
7579
7895
  kind: toSymbolKind(args.kind),
7580
7896
  limit: args.limit,
@@ -7583,15 +7899,37 @@ function createVersionDiffTool(pkgseerService) {
7583
7899
  const graphqlError = handleGraphQLErrors(result.errors);
7584
7900
  if (graphqlError)
7585
7901
  return graphqlError;
7586
- if (!result.data.versionDiff) {
7902
+ if (!result.data?.versionDiff) {
7587
7903
  return notFoundError(args.package_name, args.registry);
7588
7904
  }
7589
- return textResult(JSON.stringify(result.data.versionDiff, null, 2));
7905
+ return textResult(JSON.stringify(result.data?.versionDiff, null, 2));
7590
7906
  });
7591
7907
  }
7592
7908
  };
7593
7909
  }
7594
7910
  // src/commands/mcp.ts
7911
+ var PKGSEER_INSTRUCTIONS = `PkgSeer provides source code intelligence for third-party packages across npm, PyPI, Hex, and other registries. All tools are read-only except trigger_indexing (which only queues background work).
7912
+
7913
+ Use PkgSeer when:
7914
+ - A stack trace or error originates in a third-party dependency
7915
+ - You need to understand how a library function works internally
7916
+ - You want to trace call chains through dependency source code
7917
+ - You're evaluating, comparing, or auditing packages
7918
+ - You need to understand what changed between package versions
7919
+
7920
+ Instead of guessing library behavior from training data, use find_symbol(include_code=true) to read actual source.
7921
+ Instead of cloning repos to search code, use search_symbols or grep_repo_file.
7922
+ Instead of manually tracing call chains on GitHub, use symbol_callers and symbol_callees.
7923
+
7924
+ Tool groups:
7925
+ - Navigate source: find_symbol, list_symbols, search_symbols, symbol_callers, symbol_callees, call_path
7926
+ - Browse files: list_repo_files, grep_repo_file, fetch_code_context
7927
+ - Search across packages: search, search_project_docs
7928
+ - Browse docs: list_package_docs, fetch_package_doc
7929
+ - Evaluate: package_summary, package_quality, package_vulnerabilities, compare_packages, package_dependencies, version_diff
7930
+ - Utility: trigger_indexing, search_status, package_imports
7931
+
7932
+ Quick start: find_symbol(registry="npm", name="<function>", package_name="<pkg>", include_code=true) often answers the question in one call.`;
7595
7933
  var TOOL_FACTORIES = {
7596
7934
  package_summary: ({ pkgseerService }) => createPackageSummaryTool(pkgseerService),
7597
7935
  package_vulnerabilities: ({ pkgseerService }) => createPackageVulnerabilitiesTool(pkgseerService),
@@ -7647,10 +7985,7 @@ var PROJECT_READ_TOOLS = ["search_project_docs"];
7647
7985
  var ALL_TOOLS = [...PUBLIC_READ_TOOLS, ...PROJECT_READ_TOOLS];
7648
7986
  function createMcpServer(deps) {
7649
7987
  const { pkgseerService, configService, config, fileSystemService } = deps;
7650
- const server = new McpServer({
7651
- name: "pkgseer",
7652
- version: "0.1.0"
7653
- });
7988
+ const server = new McpServer({ name: "pkgseer", version: "0.1.0" }, { instructions: PKGSEER_INSTRUCTIONS });
7654
7989
  const defaultProjectDir = fileSystemService.getCwd();
7655
7990
  const enabledToolNames = config.enabled_tools ?? ALL_TOOLS;
7656
7991
  const toolsToRegister = enabledToolNames.filter((name) => ALL_TOOLS.includes(name));
@@ -7663,7 +7998,11 @@ function createMcpServer(deps) {
7663
7998
  for (const toolName of toolsToRegister) {
7664
7999
  const factory = TOOL_FACTORIES[toolName];
7665
8000
  const tool = factory(factoryDeps);
7666
- server.registerTool(tool.name, { description: tool.description, inputSchema: tool.schema }, tool.handler);
8001
+ server.registerTool(tool.name, {
8002
+ description: tool.description,
8003
+ inputSchema: tool.schema,
8004
+ annotations: tool.annotations
8005
+ }, tool.handler);
7667
8006
  }
7668
8007
  return server;
7669
8008
  }
@@ -7712,10 +8051,13 @@ function registerMcpCommand(program) {
7712
8051
  When run interactively (TTY), shows setup instructions.
7713
8052
  When run via stdio (non-TTY), starts the MCP server.
7714
8053
 
7715
- Available tools: package_summary, package_vulnerabilities,
7716
- package_dependencies, package_quality, compare_packages,
7717
- list_package_docs, fetch_package_doc, search_package_docs,
7718
- search_project_docs`).action(async () => {
8054
+ Available tools (21):
8055
+ Navigate source: find_symbol, list_symbols, search_symbols, symbol_callers, symbol_callees, call_path
8056
+ Browse files: list_repo_files, grep_repo_file, fetch_code_context
8057
+ Search: search, search_project_docs
8058
+ Docs: list_package_docs, fetch_package_doc
8059
+ Evaluate: package_summary, package_quality, package_vulnerabilities, compare_packages, package_dependencies, version_diff
8060
+ Utility: trigger_indexing, search_status, package_imports`).action(async () => {
7719
8061
  const deps = await createContainer();
7720
8062
  if (process.stdout.isTTY && process.stdin.isTTY) {
7721
8063
  showMcpSetupInstructions(deps);
@@ -7779,12 +8121,12 @@ async function pkgCompareAction(packages, options, deps) {
7779
8121
  });
7780
8122
  const result = await pkgseerService.cliComparePackages(input2);
7781
8123
  handleErrors(result.errors, options.json ?? false);
7782
- if (!result.data.comparePackages) {
8124
+ if (!result.data?.comparePackages) {
7783
8125
  outputError("Comparison failed", options.json ?? false);
7784
8126
  return;
7785
8127
  }
7786
8128
  if (options.json) {
7787
- const pkgs = result.data.comparePackages.packages?.filter((p) => p) ?? [];
8129
+ const pkgs = result.data?.comparePackages.packages?.filter((p) => p) ?? [];
7788
8130
  const slim = pkgs.map((p) => ({
7789
8131
  package: `${p?.packageName}@${p?.version}`,
7790
8132
  quality: p?.quality?.score,
@@ -7793,7 +8135,7 @@ async function pkgCompareAction(packages, options, deps) {
7793
8135
  }));
7794
8136
  output(slim, true);
7795
8137
  } else {
7796
- console.log(formatPackageComparison(result.data.comparePackages));
8138
+ console.log(formatPackageComparison(result.data?.comparePackages));
7797
8139
  }
7798
8140
  }
7799
8141
  var COMPARE_DESCRIPTION = `Compare multiple packages.
@@ -7862,13 +8204,13 @@ async function pkgDepsAction(packageArg, options, deps) {
7862
8204
  const transitiveRequested = options.transitive ?? false;
7863
8205
  const result = await pkgseerService.cliPackageDeps(registry, parsed.name, version2, options.transitive, options.maxDepth ? Number.parseInt(options.maxDepth, 10) : undefined);
7864
8206
  handleErrors(result.errors, options.json ?? false);
7865
- if (!result.data.packageDependencies) {
8207
+ if (!result.data?.packageDependencies) {
7866
8208
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
7867
8209
  return;
7868
8210
  }
7869
8211
  const format = options.json ? "json" : options.format ?? "human";
7870
8212
  if (format === "json") {
7871
- const data = result.data.packageDependencies;
8213
+ const data = result.data?.packageDependencies;
7872
8214
  output({
7873
8215
  package: `${data.package?.name}@${data.package?.version}`,
7874
8216
  directCount: data.dependencies?.summary?.directCount ?? 0,
@@ -7881,7 +8223,7 @@ async function pkgDepsAction(packageArg, options, deps) {
7881
8223
  }))
7882
8224
  }, true);
7883
8225
  } else if (format === "summary") {
7884
- const data = result.data.packageDependencies;
8226
+ const data = result.data?.packageDependencies;
7885
8227
  const deps2 = data.dependencies;
7886
8228
  const directCount = deps2?.summary?.directCount ?? 0;
7887
8229
  const uniquePackagesCount = deps2?.summary?.uniquePackagesCount ?? 0;
@@ -7894,7 +8236,7 @@ async function pkgDepsAction(packageArg, options, deps) {
7894
8236
  console.log(lines.join(`
7895
8237
  `));
7896
8238
  } else {
7897
- console.log(formatPackageDependencies(result.data.packageDependencies, transitiveRequested));
8239
+ console.log(formatPackageDependencies(result.data?.packageDependencies, transitiveRequested));
7898
8240
  }
7899
8241
  }
7900
8242
  var DEPS_DESCRIPTION = `Get package dependencies.
@@ -7978,12 +8320,12 @@ async function pkgInfoAction(packageArg, options, deps) {
7978
8320
  const registry = toGraphQLRegistry(parsed.registry !== "npm" ? parsed.registry : options.registry);
7979
8321
  const result = await pkgseerService.cliPackageInfo(registry, parsed.name);
7980
8322
  handleErrors(result.errors, options.json ?? false);
7981
- if (!result.data.packageSummary) {
8323
+ if (!result.data?.packageSummary) {
7982
8324
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
7983
8325
  return;
7984
8326
  }
7985
8327
  if (options.json) {
7986
- const data = result.data.packageSummary;
8328
+ const data = result.data?.packageSummary;
7987
8329
  const pkg = data.package;
7988
8330
  const slim = {
7989
8331
  name: pkg?.name,
@@ -7995,7 +8337,7 @@ async function pkgInfoAction(packageArg, options, deps) {
7995
8337
  };
7996
8338
  output(slim, true);
7997
8339
  } else {
7998
- console.log(formatPackageSummary(result.data.packageSummary));
8340
+ console.log(formatPackageSummary(result.data?.packageSummary));
7999
8341
  }
8000
8342
  }
8001
8343
  var INFO_DESCRIPTION = `Get package summary and metadata.
@@ -8046,15 +8388,15 @@ async function pkgQualityAction(packageArg, options, deps) {
8046
8388
  const version2 = parsed.version ?? options.pkgVersion;
8047
8389
  const result = await pkgseerService.cliPackageQuality(registry, parsed.name, version2);
8048
8390
  handleErrors(result.errors, options.json ?? false);
8049
- if (!result.data.packageQuality) {
8391
+ if (!result.data?.packageQuality) {
8050
8392
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
8051
8393
  return;
8052
8394
  }
8053
8395
  const format = options.json ? "json" : options.format ?? "human";
8054
8396
  if (format === "json") {
8055
- const quality = result.data.packageQuality.quality;
8397
+ const quality = result.data?.packageQuality.quality;
8056
8398
  const slim = {
8057
- package: `${result.data.packageQuality.package?.name}@${result.data.packageQuality.package?.version}`,
8399
+ package: `${result.data?.packageQuality.package?.name}@${result.data?.packageQuality.package?.version}`,
8058
8400
  score: quality?.overallScore,
8059
8401
  grade: quality?.grade,
8060
8402
  categories: quality?.categories?.filter((c) => c).map((c) => ({
@@ -8064,7 +8406,7 @@ async function pkgQualityAction(packageArg, options, deps) {
8064
8406
  };
8065
8407
  output(slim, true);
8066
8408
  } else {
8067
- console.log(formatPackageQuality(result.data.packageQuality));
8409
+ console.log(formatPackageQuality(result.data?.packageQuality));
8068
8410
  }
8069
8411
  }
8070
8412
  var QUALITY_DESCRIPTION = `Get package quality score and breakdown.
@@ -8149,12 +8491,12 @@ async function pkgVulnsAction(packageArg, options, deps) {
8149
8491
  const version2 = parsed.version ?? options.pkgVersion;
8150
8492
  const result = await pkgseerService.cliPackageVulns(registry, parsed.name, version2);
8151
8493
  handleErrors(result.errors, options.json ?? false);
8152
- if (!result.data.packageVulnerabilities) {
8494
+ if (!result.data?.packageVulnerabilities) {
8153
8495
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
8154
8496
  return;
8155
8497
  }
8156
8498
  if (options.json) {
8157
- const data = result.data.packageVulnerabilities;
8499
+ const data = result.data?.packageVulnerabilities;
8158
8500
  const vulns = data.security?.vulnerabilities ?? [];
8159
8501
  const slim = {
8160
8502
  package: `${data.package?.name}@${data.package?.version}`,
@@ -8168,7 +8510,7 @@ async function pkgVulnsAction(packageArg, options, deps) {
8168
8510
  };
8169
8511
  output(slim, true);
8170
8512
  } else {
8171
- console.log(formatPackageVulnerabilities(result.data.packageVulnerabilities));
8513
+ console.log(formatPackageVulnerabilities(result.data?.packageVulnerabilities));
8172
8514
  }
8173
8515
  }
8174
8516
  var VULNS_DESCRIPTION = `Check package for security vulnerabilities.