@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 +671 -329
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-d3mfgdr9.js → chunk-edcjw6wf.js} +1 -1
- package/package.json +2 -1
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
794
|
+
symbolRef
|
|
794
795
|
name
|
|
795
796
|
qualifiedPath
|
|
796
797
|
}
|
|
797
798
|
dependencies {
|
|
798
|
-
|
|
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
|
-
|
|
831
|
+
symbolRef
|
|
830
832
|
name
|
|
831
833
|
qualifiedPath
|
|
832
834
|
}
|
|
833
835
|
dependents {
|
|
834
|
-
|
|
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
|
-
|
|
892
|
+
symbolRef
|
|
891
893
|
name
|
|
892
894
|
qualifiedPath
|
|
893
895
|
}
|
|
894
896
|
toSymbol {
|
|
895
|
-
|
|
897
|
+
symbolRef
|
|
896
898
|
name
|
|
897
899
|
qualifiedPath
|
|
898
900
|
}
|
|
899
901
|
paths {
|
|
900
902
|
length
|
|
901
903
|
hops {
|
|
902
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
2461
|
-
|
|
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
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
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,
|
|
2619
|
-
if (
|
|
2620
|
-
return {
|
|
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 --
|
|
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 = [`
|
|
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.
|
|
2697
|
-
parts.push(`
|
|
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} (
|
|
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.
|
|
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
|
|
2738
|
-
outputError("Symbol not found. Verify the symbol reference or use --
|
|
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
|
|
3015
|
+
output(result.data?.symbolDependencies, true);
|
|
2743
3016
|
} else {
|
|
2744
|
-
console.log(formatCalleesResult(result.data
|
|
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 --
|
|
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 --
|
|
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("--
|
|
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} (
|
|
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.
|
|
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
|
|
2788
|
-
outputError("Symbol not found. Verify the symbol reference or use --
|
|
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
|
|
3066
|
+
output(result.data?.symbolDependents, true);
|
|
2793
3067
|
} else {
|
|
2794
|
-
console.log(formatCallersResult(result.data
|
|
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 --
|
|
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 --
|
|
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("--
|
|
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
|
|
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
|
|
3138
|
+
output(result.data?.versionDiff, true);
|
|
2865
3139
|
} else {
|
|
2866
|
-
console.log(formatDiffResult(result.data
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3424
|
+
output(result.data?.packageImports, true);
|
|
3151
3425
|
} else {
|
|
3152
|
-
console.log(formatImportsResult(result.data
|
|
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
|
|
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
|
|
3493
|
+
output(result.data?.listSymbols, true);
|
|
3220
3494
|
} else {
|
|
3221
|
-
console.log(formatListResult(result.data
|
|
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
|
|
3534
|
+
const refPart = hop.symbolRef ? ` (ref:${hop.symbolRef})` : "";
|
|
3261
3535
|
const arrow = j < callPath.hops.length - 1 ? " ->" : "";
|
|
3262
|
-
lines.push(` ${j + 1}. ${name}${
|
|
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.
|
|
3272
|
-
const toRef = buildSymbolReference(toArg, options.
|
|
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
|
|
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
|
|
3558
|
+
output(result.data?.callPath, true);
|
|
3285
3559
|
} else {
|
|
3286
|
-
console.log(formatPathResult(result.data
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3612
|
-
page: result2.data
|
|
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
|
|
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
|
|
3655
|
-
page: result.data
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4383
|
-
outputResults(resultsResponse.data
|
|
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
|
|
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
|
|
4719
|
+
if (resultsResponse.data?.searchResults) {
|
|
4446
4720
|
console.log("");
|
|
4447
|
-
outputResults(resultsResponse.data
|
|
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
|
|
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("
|
|
4848
|
-
console.log("
|
|
4849
|
-
console.log("
|
|
4850
|
-
console.log("
|
|
4851
|
-
console.log("
|
|
4852
|
-
console.log("
|
|
4853
|
-
console.log("
|
|
4854
|
-
console.log("
|
|
4855
|
-
console.log("
|
|
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:
|
|
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
|
-
|
|
6055
|
+
Use PkgSeer instead of guessing library behavior, cloning repos, or browsing GitHub.
|
|
5757
6056
|
|
|
5758
|
-
##
|
|
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
|
|
5780
|
-
${invocation} pkg
|
|
5781
|
-
${invocation} pkg
|
|
5782
|
-
|
|
5783
|
-
|
|
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
|
-
` + ` "
|
|
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
|
-
|
|
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
|
-
|
|
6499
|
-
registry: ref.registry ?
|
|
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
|
|
6506
|
-
const
|
|
6507
|
-
if (
|
|
6508
|
-
return `Failed to ${operation}: ${message}.
|
|
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
|
|
6535
|
-
const
|
|
6536
|
-
const
|
|
6537
|
-
|
|
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
|
|
6568
|
-
to: schemas.symbolReference.describe("Destination symbol. Provide either
|
|
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: "
|
|
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.
|
|
6583
|
-
return errorResult("Invalid 'from' symbol: provide either
|
|
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.
|
|
6586
|
-
return errorResult("Invalid 'to' symbol: provide either
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
6924
|
+
if (!result.data?.comparePackages) {
|
|
6631
6925
|
return errorResult("Comparison failed: no results returned");
|
|
6632
6926
|
}
|
|
6633
|
-
return textResult(JSON.stringify(result.data
|
|
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: "
|
|
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
|
|
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
|
|
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
|
|
6981
|
+
if (!result.data?.getDocPage) {
|
|
6686
6982
|
return errorResult(`Documentation page not found: ${page_id}`);
|
|
6687
6983
|
}
|
|
6688
|
-
return textResult(JSON.stringify(result.data
|
|
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: "
|
|
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(
|
|
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
|
|
7034
|
+
if (!result.data?.findSymbol) {
|
|
6738
7035
|
return notFoundError(args.package_name, args.registry);
|
|
6739
7036
|
}
|
|
6740
|
-
const data = result.data
|
|
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
|
|
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(
|
|
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
|
|
7075
|
+
if (!result.data?.grepRepoFile) {
|
|
6778
7076
|
return notFoundError(args.package_name, args.registry);
|
|
6779
7077
|
}
|
|
6780
|
-
const data = result.data
|
|
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
|
|
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(
|
|
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
|
|
7105
|
+
if (!result.data?.listPackageDocs) {
|
|
6807
7106
|
return notFoundError(package_name, registry);
|
|
6808
7107
|
}
|
|
6809
|
-
return textResult(JSON.stringify(result.data
|
|
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: "
|
|
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(
|
|
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
|
|
7140
|
+
if (!result.data?.listRepoFiles) {
|
|
6841
7141
|
return notFoundError(args.package_name, args.registry);
|
|
6842
7142
|
}
|
|
6843
|
-
const data = result.data
|
|
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
|
|
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(
|
|
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
|
|
7200
|
+
if (!result.data?.listSymbols) {
|
|
6900
7201
|
return notFoundError(args.package_name, args.registry);
|
|
6901
7202
|
}
|
|
6902
|
-
return textResult(JSON.stringify(result.data
|
|
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(
|
|
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
|
|
7301
|
+
if (!result.data?.packageDependencies) {
|
|
7000
7302
|
return notFoundError(package_name, registry);
|
|
7001
7303
|
}
|
|
7002
|
-
const deps = result.data
|
|
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
|
|
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
|
|
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: "
|
|
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(
|
|
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
|
|
7362
|
+
if (!result.data?.packageImports) {
|
|
7060
7363
|
return notFoundError(args.package_name, args.registry);
|
|
7061
7364
|
}
|
|
7062
|
-
return textResult(JSON.stringify(result.data
|
|
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(
|
|
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
|
|
7388
|
+
if (!result.data?.packageQuality) {
|
|
7085
7389
|
return notFoundError(package_name, registry);
|
|
7086
7390
|
}
|
|
7087
|
-
return textResult(JSON.stringify(result.data
|
|
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(
|
|
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
|
|
7413
|
+
if (!result.data?.packageSummary) {
|
|
7109
7414
|
return notFoundError(package_name, registry);
|
|
7110
7415
|
}
|
|
7111
|
-
return textResult(JSON.stringify(result.data
|
|
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(
|
|
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
|
|
7439
|
+
if (!result.data?.packageVulnerabilities) {
|
|
7134
7440
|
return notFoundError(package_name, registry);
|
|
7135
7441
|
}
|
|
7136
|
-
return textResult(JSON.stringify(result.data
|
|
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:
|
|
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
|
|
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: "
|
|
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
|
|
7601
|
+
if (!result.data?.searchProjectDocs) {
|
|
7294
7602
|
return errorResult(`Project not found: ${resolvedProject}`);
|
|
7295
7603
|
}
|
|
7296
|
-
if ((result.data
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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(
|
|
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
|
|
7707
|
+
if (!result.data?.searchSymbols) {
|
|
7398
7708
|
return notFoundError(args.package_name, args.registry);
|
|
7399
7709
|
}
|
|
7400
|
-
const data = result.data
|
|
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
|
|
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: "
|
|
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.
|
|
7436
|
-
return errorResult("Provide either
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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.
|
|
7475
|
-
return errorResult("Provide either
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
7902
|
+
if (!result.data?.versionDiff) {
|
|
7587
7903
|
return notFoundError(args.package_name, args.registry);
|
|
7588
7904
|
}
|
|
7589
|
-
return textResult(JSON.stringify(result.data
|
|
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, {
|
|
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:
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8397
|
+
const quality = result.data?.packageQuality.quality;
|
|
8056
8398
|
const slim = {
|
|
8057
|
-
package: `${result.data
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8513
|
+
console.log(formatPackageVulnerabilities(result.data?.packageVulnerabilities));
|
|
8172
8514
|
}
|
|
8173
8515
|
}
|
|
8174
8516
|
var VULNS_DESCRIPTION = `Check package for security vulnerabilities.
|