@pkgseer/cli 0.1.2 → 0.1.4
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 +465 -122
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-2wbvwsc9.js → chunk-z505vakm.js} +1 -1
- package/package.json +1 -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-z505vakm.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -71,12 +71,13 @@ var CliPackageQualityDocument = gql`
|
|
|
71
71
|
}
|
|
72
72
|
`;
|
|
73
73
|
var CliPackageDepsDocument = gql`
|
|
74
|
-
query CliPackageDeps($registry: Registry!, $name: String!, $version: String, $includeTransitive: Boolean) {
|
|
74
|
+
query CliPackageDeps($registry: Registry!, $name: String!, $version: String, $includeTransitive: Boolean, $maxDepth: Int) {
|
|
75
75
|
packageDependencies(
|
|
76
76
|
registry: $registry
|
|
77
77
|
name: $name
|
|
78
78
|
version: $version
|
|
79
79
|
includeTransitive: $includeTransitive
|
|
80
|
+
maxDepth: $maxDepth
|
|
80
81
|
) {
|
|
81
82
|
package {
|
|
82
83
|
name
|
|
@@ -202,7 +203,7 @@ var CliDocsListDocument = gql`
|
|
|
202
203
|
packageName
|
|
203
204
|
version
|
|
204
205
|
pages {
|
|
205
|
-
|
|
206
|
+
id
|
|
206
207
|
title
|
|
207
208
|
words
|
|
208
209
|
}
|
|
@@ -465,13 +466,35 @@ var FetchPackageDocDocument = gql`
|
|
|
465
466
|
}
|
|
466
467
|
}
|
|
467
468
|
`;
|
|
469
|
+
var GetDocPageDocument = gql`
|
|
470
|
+
query GetDocPage($pageId: String!) {
|
|
471
|
+
getDocPage(pageId: $pageId) {
|
|
472
|
+
schemaVersion
|
|
473
|
+
page {
|
|
474
|
+
id
|
|
475
|
+
title
|
|
476
|
+
content
|
|
477
|
+
contentFormat
|
|
478
|
+
breadcrumbs
|
|
479
|
+
linkName
|
|
480
|
+
linkTargets
|
|
481
|
+
lastUpdatedAt
|
|
482
|
+
source {
|
|
483
|
+
url
|
|
484
|
+
label
|
|
485
|
+
}
|
|
486
|
+
baseUrl
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
`;
|
|
468
491
|
var SearchPackageDocsDocument = gql`
|
|
469
|
-
query SearchPackageDocs($registry: Registry!, $packageName: String!, $keywords: [String!], $
|
|
492
|
+
query SearchPackageDocs($registry: Registry!, $packageName: String!, $keywords: [String!], $matchMode: MatchMode, $includeSnippets: Boolean, $limit: Int, $version: String) {
|
|
470
493
|
searchPackageDocs(
|
|
471
494
|
registry: $registry
|
|
472
495
|
packageName: $packageName
|
|
473
496
|
keywords: $keywords
|
|
474
|
-
|
|
497
|
+
matchMode: $matchMode
|
|
475
498
|
includeSnippets: $includeSnippets
|
|
476
499
|
limit: $limit
|
|
477
500
|
version: $version
|
|
@@ -480,7 +503,6 @@ var SearchPackageDocsDocument = gql`
|
|
|
480
503
|
registry
|
|
481
504
|
packageName
|
|
482
505
|
version
|
|
483
|
-
query
|
|
484
506
|
entries {
|
|
485
507
|
id
|
|
486
508
|
title
|
|
@@ -501,17 +523,16 @@ var SearchPackageDocsDocument = gql`
|
|
|
501
523
|
}
|
|
502
524
|
`;
|
|
503
525
|
var SearchProjectDocsDocument = gql`
|
|
504
|
-
query SearchProjectDocs($project: String!, $keywords: [String!], $
|
|
526
|
+
query SearchProjectDocs($project: String!, $keywords: [String!], $matchMode: MatchMode, $includeSnippets: Boolean, $limit: Int) {
|
|
505
527
|
searchProjectDocs(
|
|
506
528
|
project: $project
|
|
507
529
|
keywords: $keywords
|
|
508
|
-
|
|
530
|
+
matchMode: $matchMode
|
|
509
531
|
includeSnippets: $includeSnippets
|
|
510
532
|
limit: $limit
|
|
511
533
|
) {
|
|
512
534
|
schemaVersion
|
|
513
535
|
project
|
|
514
|
-
query
|
|
515
536
|
entries {
|
|
516
537
|
id
|
|
517
538
|
title
|
|
@@ -548,6 +569,7 @@ var PackageQualityDocumentString = print(PackageQualityDocument);
|
|
|
548
569
|
var ComparePackagesDocumentString = print(ComparePackagesDocument);
|
|
549
570
|
var ListPackageDocsDocumentString = print(ListPackageDocsDocument);
|
|
550
571
|
var FetchPackageDocDocumentString = print(FetchPackageDocDocument);
|
|
572
|
+
var GetDocPageDocumentString = print(GetDocPageDocument);
|
|
551
573
|
var SearchPackageDocsDocumentString = print(SearchPackageDocsDocument);
|
|
552
574
|
var SearchProjectDocsDocumentString = print(SearchProjectDocsDocument);
|
|
553
575
|
function getSdk(client, withWrapper = defaultWrapper) {
|
|
@@ -603,6 +625,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
603
625
|
FetchPackageDoc(variables, requestHeaders) {
|
|
604
626
|
return withWrapper((wrappedRequestHeaders) => client.rawRequest(FetchPackageDocDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "FetchPackageDoc", "query", variables);
|
|
605
627
|
},
|
|
628
|
+
GetDocPage(variables, requestHeaders) {
|
|
629
|
+
return withWrapper((wrappedRequestHeaders) => client.rawRequest(GetDocPageDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "GetDocPage", "query", variables);
|
|
630
|
+
},
|
|
606
631
|
SearchPackageDocs(variables, requestHeaders) {
|
|
607
632
|
return withWrapper((wrappedRequestHeaders) => client.rawRequest(SearchPackageDocsDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "SearchPackageDocs", "query", variables);
|
|
608
633
|
},
|
|
@@ -1220,12 +1245,16 @@ class PkgseerServiceImpl {
|
|
|
1220
1245
|
});
|
|
1221
1246
|
return { data: result.data, errors: result.errors };
|
|
1222
1247
|
}
|
|
1248
|
+
async getDocPage(pageId) {
|
|
1249
|
+
const result = await this.client.GetDocPage({ pageId });
|
|
1250
|
+
return { data: result.data, errors: result.errors };
|
|
1251
|
+
}
|
|
1223
1252
|
async searchPackageDocs(registry, packageName, options) {
|
|
1224
1253
|
const result = await this.client.SearchPackageDocs({
|
|
1225
1254
|
registry,
|
|
1226
1255
|
packageName,
|
|
1227
1256
|
keywords: options?.keywords,
|
|
1228
|
-
|
|
1257
|
+
matchMode: options?.matchMode,
|
|
1229
1258
|
includeSnippets: options?.includeSnippets,
|
|
1230
1259
|
limit: options?.limit,
|
|
1231
1260
|
version: options?.version
|
|
@@ -1236,7 +1265,7 @@ class PkgseerServiceImpl {
|
|
|
1236
1265
|
const result = await this.client.SearchProjectDocs({
|
|
1237
1266
|
project,
|
|
1238
1267
|
keywords: options?.keywords,
|
|
1239
|
-
|
|
1268
|
+
matchMode: options?.matchMode,
|
|
1240
1269
|
includeSnippets: options?.includeSnippets,
|
|
1241
1270
|
limit: options?.limit
|
|
1242
1271
|
});
|
|
@@ -1262,12 +1291,13 @@ class PkgseerServiceImpl {
|
|
|
1262
1291
|
});
|
|
1263
1292
|
return { data: result.data, errors: result.errors };
|
|
1264
1293
|
}
|
|
1265
|
-
async cliPackageDeps(registry, name, version2, includeTransitive) {
|
|
1294
|
+
async cliPackageDeps(registry, name, version2, includeTransitive, maxDepth) {
|
|
1266
1295
|
const result = await this.client.CliPackageDeps({
|
|
1267
1296
|
registry,
|
|
1268
1297
|
name,
|
|
1269
1298
|
version: version2,
|
|
1270
|
-
includeTransitive
|
|
1299
|
+
includeTransitive,
|
|
1300
|
+
maxDepth
|
|
1271
1301
|
});
|
|
1272
1302
|
return { data: result.data, errors: result.errors };
|
|
1273
1303
|
}
|
|
@@ -1694,10 +1724,26 @@ function outputError(message, json) {
|
|
|
1694
1724
|
process.exit(1);
|
|
1695
1725
|
}
|
|
1696
1726
|
function handleErrors(errors, json) {
|
|
1697
|
-
if (errors
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1727
|
+
if (!errors || errors.length === 0)
|
|
1728
|
+
return;
|
|
1729
|
+
const combined = errors.map((e) => e.message).filter(Boolean).join(", ");
|
|
1730
|
+
const lower = combined.toLowerCase();
|
|
1731
|
+
const isTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
|
|
1732
|
+
const isNotFound = lower.includes("not found") || lower.includes("does not exist") || lower.includes("unknown");
|
|
1733
|
+
const isAuth = lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("token") || lower.includes("authentication") || lower.includes("permission");
|
|
1734
|
+
const isRateLimit = lower.includes("rate limit") || lower.includes("too many requests");
|
|
1735
|
+
let hint = "";
|
|
1736
|
+
if (isTimeout) {
|
|
1737
|
+
hint = " Hint: lower the limit or narrow the scope, then retry.";
|
|
1738
|
+
} else if (isNotFound) {
|
|
1739
|
+
hint = " Hint: verify the name/registry and that the resource exists.";
|
|
1740
|
+
} else if (isAuth) {
|
|
1741
|
+
hint = " Hint: check login or token validity (pkgseer auth-status).";
|
|
1742
|
+
} else if (isRateLimit) {
|
|
1743
|
+
hint = " Hint: wait and retry, or reduce request frequency.";
|
|
1744
|
+
}
|
|
1745
|
+
const message = `${combined}${hint}`;
|
|
1746
|
+
outputError(message, json);
|
|
1701
1747
|
}
|
|
1702
1748
|
function formatNumber(num) {
|
|
1703
1749
|
if (num == null)
|
|
@@ -1775,6 +1821,19 @@ async function withCliErrorHandling(json, fn) {
|
|
|
1775
1821
|
return;
|
|
1776
1822
|
}
|
|
1777
1823
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1824
|
+
const lower = message.toLowerCase();
|
|
1825
|
+
if (lower.includes("-32001") || lower.includes("timeout")) {
|
|
1826
|
+
outputError(`${message}. Hint: lower the limit or narrow the scope, then retry.`, json);
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
if (lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("token") || lower.includes("authentication") || lower.includes("permission")) {
|
|
1830
|
+
outputError(`${message}. Hint: check login or token validity.`, json);
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
if (lower.includes("rate limit") || lower.includes("too many requests")) {
|
|
1834
|
+
outputError(`${message}. Hint: wait and retry.`, json);
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1778
1837
|
const colonMatch = message.match(/^([^:]+):/);
|
|
1779
1838
|
const shortMessage = colonMatch?.[1] ?? message;
|
|
1780
1839
|
outputError(shortMessage, json);
|
|
@@ -1792,10 +1851,11 @@ function formatScore(score) {
|
|
|
1792
1851
|
// src/commands/docs/get.ts
|
|
1793
1852
|
function parsePackageRef(ref) {
|
|
1794
1853
|
if (!ref.includes("/")) {
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1854
|
+
return {
|
|
1855
|
+
pageId: ref,
|
|
1856
|
+
originalRef: ref,
|
|
1857
|
+
isGlobalId: true
|
|
1858
|
+
};
|
|
1799
1859
|
}
|
|
1800
1860
|
const parts = ref.split("/");
|
|
1801
1861
|
if (parts.length < 2) {
|
|
@@ -1824,7 +1884,7 @@ function parsePackageRef(ref) {
|
|
|
1824
1884
|
}
|
|
1825
1885
|
return { packageName, pageId, originalRef: ref };
|
|
1826
1886
|
}
|
|
1827
|
-
function formatDocPage(data, ref) {
|
|
1887
|
+
function formatDocPage(data, ref, verbose = false, previewLines) {
|
|
1828
1888
|
const lines = [];
|
|
1829
1889
|
const page = data.page;
|
|
1830
1890
|
if (!page) {
|
|
@@ -1832,6 +1892,49 @@ function formatDocPage(data, ref) {
|
|
|
1832
1892
|
}
|
|
1833
1893
|
lines.push(`[${ref}]`);
|
|
1834
1894
|
lines.push("");
|
|
1895
|
+
if (verbose) {
|
|
1896
|
+
const metadata = [];
|
|
1897
|
+
if (data.schemaVersion) {
|
|
1898
|
+
metadata.push(`Schema Version: ${data.schemaVersion}`);
|
|
1899
|
+
}
|
|
1900
|
+
if (page.id) {
|
|
1901
|
+
metadata.push(`Page ID: ${page.id}`);
|
|
1902
|
+
}
|
|
1903
|
+
if (page.contentFormat) {
|
|
1904
|
+
metadata.push(`Format: ${page.contentFormat}`);
|
|
1905
|
+
}
|
|
1906
|
+
if (page.lastUpdatedAt) {
|
|
1907
|
+
metadata.push(`Last Updated: ${page.lastUpdatedAt}`);
|
|
1908
|
+
}
|
|
1909
|
+
if (page.source) {
|
|
1910
|
+
const sourceParts = [];
|
|
1911
|
+
if (page.source.url) {
|
|
1912
|
+
sourceParts.push(page.source.url);
|
|
1913
|
+
}
|
|
1914
|
+
if (page.source.label) {
|
|
1915
|
+
sourceParts.push(`(${page.source.label})`);
|
|
1916
|
+
}
|
|
1917
|
+
if (sourceParts.length > 0) {
|
|
1918
|
+
metadata.push(`Source: ${sourceParts.join(" ")}`);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
if (page.baseUrl) {
|
|
1922
|
+
metadata.push(`Base URL: ${page.baseUrl}`);
|
|
1923
|
+
}
|
|
1924
|
+
if (page.linkName) {
|
|
1925
|
+
metadata.push(`Link Name: ${page.linkName}`);
|
|
1926
|
+
}
|
|
1927
|
+
if (page.linkTargets && page.linkTargets.length > 0) {
|
|
1928
|
+
metadata.push(`Link Targets: ${page.linkTargets.join(", ")}`);
|
|
1929
|
+
}
|
|
1930
|
+
if (metadata.length > 0) {
|
|
1931
|
+
lines.push("Metadata:");
|
|
1932
|
+
for (const meta of metadata) {
|
|
1933
|
+
lines.push(` ${meta}`);
|
|
1934
|
+
}
|
|
1935
|
+
lines.push("");
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1835
1938
|
if (page.breadcrumbs && page.breadcrumbs.length > 0) {
|
|
1836
1939
|
lines.push(page.breadcrumbs.join(" > "));
|
|
1837
1940
|
lines.push("");
|
|
@@ -1839,9 +1942,22 @@ function formatDocPage(data, ref) {
|
|
|
1839
1942
|
lines.push(`# ${page.title}`);
|
|
1840
1943
|
lines.push("");
|
|
1841
1944
|
if (page.content) {
|
|
1842
|
-
|
|
1945
|
+
const previewLimit = previewLines ?? 20;
|
|
1946
|
+
if (previewLimit === 0) {
|
|
1947
|
+
lines.push(page.content);
|
|
1948
|
+
} else {
|
|
1949
|
+
const contentLines = page.content.split(`
|
|
1950
|
+
`);
|
|
1951
|
+
const preview = contentLines.slice(0, previewLimit).join(`
|
|
1952
|
+
`);
|
|
1953
|
+
lines.push(preview);
|
|
1954
|
+
if (contentLines.length > previewLimit) {
|
|
1955
|
+
lines.push("");
|
|
1956
|
+
lines.push("(Preview truncated; use --preview-lines 0 for full content.)");
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1843
1959
|
} else {
|
|
1844
|
-
lines.push("(No content available)");
|
|
1960
|
+
lines.push("(No content available. Verify the page ID or try another version.)");
|
|
1845
1961
|
}
|
|
1846
1962
|
return lines.join(`
|
|
1847
1963
|
`);
|
|
@@ -1861,13 +1977,66 @@ function extractErrorMessage(error) {
|
|
|
1861
1977
|
}
|
|
1862
1978
|
async function fetchPage(ref, defaultRegistry, defaultVersion, pkgseerService) {
|
|
1863
1979
|
try {
|
|
1980
|
+
if (ref.isGlobalId) {
|
|
1981
|
+
const result2 = await pkgseerService.getDocPage(ref.pageId);
|
|
1982
|
+
if (result2.errors && result2.errors.length > 0) {
|
|
1983
|
+
const errorMsg = result2.errors.map((e) => {
|
|
1984
|
+
if (typeof e === "object" && e !== null) {
|
|
1985
|
+
const parts = [];
|
|
1986
|
+
if ("message" in e) {
|
|
1987
|
+
parts.push(String(e.message));
|
|
1988
|
+
}
|
|
1989
|
+
if ("extensions" in e && e.extensions) {
|
|
1990
|
+
const ext = e.extensions;
|
|
1991
|
+
if (typeof ext === "object" && "code" in ext) {
|
|
1992
|
+
parts.push(`Code: ${ext.code}`);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
return parts.length > 0 ? parts.join(" ") : JSON.stringify(e);
|
|
1996
|
+
}
|
|
1997
|
+
return String(e);
|
|
1998
|
+
}).join("; ");
|
|
1999
|
+
return { ref: ref.originalRef, result: null, error: errorMsg };
|
|
2000
|
+
}
|
|
2001
|
+
if (!result2.data.getDocPage) {
|
|
2002
|
+
return {
|
|
2003
|
+
ref: ref.originalRef,
|
|
2004
|
+
result: null,
|
|
2005
|
+
error: `Page not found: ${ref.pageId}`
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
return {
|
|
2009
|
+
ref: ref.originalRef,
|
|
2010
|
+
result: {
|
|
2011
|
+
schemaVersion: result2.data.getDocPage?.schemaVersion ?? null,
|
|
2012
|
+
page: result2.data.getDocPage?.page ?? null
|
|
2013
|
+
}
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
1864
2016
|
const registry = ref.registry ? toGraphQLRegistry(ref.registry) : defaultRegistry;
|
|
1865
2017
|
const version2 = ref.version ?? defaultVersion;
|
|
2018
|
+
if (!ref.packageName) {
|
|
2019
|
+
return {
|
|
2020
|
+
ref: ref.originalRef,
|
|
2021
|
+
result: null,
|
|
2022
|
+
error: `Package name required for reference: ${ref.originalRef}`
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
1866
2025
|
const result = await pkgseerService.cliDocsGet(registry, ref.packageName, ref.pageId, version2);
|
|
1867
2026
|
if (result.errors && result.errors.length > 0) {
|
|
1868
2027
|
const errorMsg = result.errors.map((e) => {
|
|
1869
|
-
if (typeof e === "object" && e !== null
|
|
1870
|
-
|
|
2028
|
+
if (typeof e === "object" && e !== null) {
|
|
2029
|
+
const parts = [];
|
|
2030
|
+
if ("message" in e) {
|
|
2031
|
+
parts.push(String(e.message));
|
|
2032
|
+
}
|
|
2033
|
+
if ("extensions" in e && e.extensions) {
|
|
2034
|
+
const ext = e.extensions;
|
|
2035
|
+
if (typeof ext === "object" && "code" in ext) {
|
|
2036
|
+
parts.push(`Code: ${ext.code}`);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return parts.length > 0 ? parts.join(" ") : JSON.stringify(e);
|
|
1871
2040
|
}
|
|
1872
2041
|
return String(e);
|
|
1873
2042
|
}).join("; ");
|
|
@@ -1880,7 +2049,12 @@ async function fetchPage(ref, defaultRegistry, defaultVersion, pkgseerService) {
|
|
|
1880
2049
|
error: `Page not found: ${ref.pageId} for ${ref.packageName}`
|
|
1881
2050
|
};
|
|
1882
2051
|
}
|
|
1883
|
-
return {
|
|
2052
|
+
return {
|
|
2053
|
+
ref: ref.originalRef,
|
|
2054
|
+
result: result.data.fetchPackageDoc ? {
|
|
2055
|
+
page: result.data.fetchPackageDoc.page ?? null
|
|
2056
|
+
} : null
|
|
2057
|
+
};
|
|
1884
2058
|
} catch (error) {
|
|
1885
2059
|
return {
|
|
1886
2060
|
ref: ref.originalRef,
|
|
@@ -1891,11 +2065,13 @@ async function fetchPage(ref, defaultRegistry, defaultVersion, pkgseerService) {
|
|
|
1891
2065
|
}
|
|
1892
2066
|
async function docsGetAction(refs, options, deps) {
|
|
1893
2067
|
if (refs.length === 0) {
|
|
1894
|
-
outputError("At least one page reference required. Format: <package>/<
|
|
2068
|
+
outputError("At least one page reference required. Format: <globally-unique-id> or <package>/<document>", options.json ?? false);
|
|
1895
2069
|
return;
|
|
1896
2070
|
}
|
|
1897
2071
|
const { pkgseerService } = deps;
|
|
1898
2072
|
const defaultRegistry = toGraphQLRegistry(options.registry);
|
|
2073
|
+
const previewLines = options.previewLines != null ? Number.parseInt(options.previewLines, 10) : 20;
|
|
2074
|
+
const resolvedPreview = Number.isNaN(previewLines) ? 20 : previewLines;
|
|
1899
2075
|
const parsedRefs = [];
|
|
1900
2076
|
for (const ref of refs) {
|
|
1901
2077
|
try {
|
|
@@ -1929,17 +2105,24 @@ ${errorMessages}`, options.json ?? false);
|
|
|
1929
2105
|
if (r.error) {
|
|
1930
2106
|
return { ref: r.ref, error: r.error };
|
|
1931
2107
|
}
|
|
2108
|
+
if (options.verbose) {
|
|
2109
|
+
return {
|
|
2110
|
+
ref: r.ref,
|
|
2111
|
+
...r.result
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
1932
2114
|
const page = r.result?.page;
|
|
1933
2115
|
return {
|
|
1934
2116
|
ref: r.ref,
|
|
1935
2117
|
title: page?.title,
|
|
1936
|
-
content: page?.content
|
|
2118
|
+
content: page?.content,
|
|
2119
|
+
breadcrumbs: page?.breadcrumbs
|
|
1937
2120
|
};
|
|
1938
2121
|
});
|
|
1939
2122
|
output(jsonResults, true);
|
|
1940
2123
|
} else {
|
|
1941
2124
|
if (successes.length > 0) {
|
|
1942
|
-
const pages = successes.filter((r) => r.result !== null).map((r) => formatDocPage(r.result, r.ref));
|
|
2125
|
+
const pages = successes.filter((r) => r.result !== null).map((r) => formatDocPage(r.result, r.ref, options.verbose, resolvedPreview));
|
|
1943
2126
|
console.log(pages.join(`
|
|
1944
2127
|
|
|
1945
2128
|
---
|
|
@@ -1956,29 +2139,28 @@ var GET_DESCRIPTION = `Fetch one or more documentation pages.
|
|
|
1956
2139
|
Retrieves the full content of documentation pages including
|
|
1957
2140
|
title, breadcrumbs, content (markdown), and source URL.
|
|
1958
2141
|
|
|
1959
|
-
Use 'pkgseer docs list' first to discover available page IDs
|
|
1960
|
-
use the output format from 'pkgseer docs search --refs-only'.
|
|
2142
|
+
Use 'pkgseer docs list' first to discover available page IDs.
|
|
1961
2143
|
|
|
1962
|
-
Format: <
|
|
2144
|
+
Format: <globally-unique-id> (preferred)
|
|
2145
|
+
or <registry>/<package>/<version>/<document> (full form)
|
|
1963
2146
|
or <package>/<document> (short form, requires --registry/--pkg-version)
|
|
1964
2147
|
|
|
1965
2148
|
Examples:
|
|
1966
|
-
#
|
|
2149
|
+
# Globally unique ID (from list output)
|
|
2150
|
+
pkgseer docs get 24293-shared-plugins-during-build-2
|
|
2151
|
+
|
|
2152
|
+
# Full form
|
|
1967
2153
|
pkgseer docs get npm/express/4.18.2/readme
|
|
1968
2154
|
pkgseer docs get hex/postgrex/1.15.0/readme
|
|
1969
2155
|
|
|
1970
|
-
# Short form
|
|
2156
|
+
# Short form
|
|
1971
2157
|
pkgseer docs get express/readme --registry npm --pkg-version 4.18.2
|
|
1972
2158
|
pkgseer docs get postgrex/readme --registry hex
|
|
1973
2159
|
|
|
1974
2160
|
# Multiple pages
|
|
1975
|
-
pkgseer docs get
|
|
1976
|
-
|
|
1977
|
-
# Pipe from search (full form works seamlessly)
|
|
1978
|
-
pkgseer docs search log --package express --refs-only | \\
|
|
1979
|
-
xargs pkgseer docs get`;
|
|
2161
|
+
pkgseer docs get 24293-shared-plugins-during-build-2 npm/express/4.18.2/readme`;
|
|
1980
2162
|
function registerDocsGetCommand(program) {
|
|
1981
|
-
program.command("get <refs...>").summary("Fetch documentation page(s)").description(GET_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("--json", "Output as JSON").action(async (refs, options) => {
|
|
2163
|
+
program.command("get <refs...>").summary("Fetch documentation page(s)").description(GET_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("--json", "Output as JSON").option("--verbose", "Include metadata (ID, format, source, links, etc.)").option("--preview-lines <n>", "Lines of content to show (0 for full content; default 20)").action(async (refs, options) => {
|
|
1982
2164
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
1983
2165
|
const deps = await createContainer();
|
|
1984
2166
|
await docsGetAction(refs, options, deps);
|
|
@@ -1998,11 +2180,11 @@ function formatDocsList(docs) {
|
|
|
1998
2180
|
lines.push(`Found ${docs.pages.length} pages:`);
|
|
1999
2181
|
lines.push("");
|
|
2000
2182
|
for (const page of docs.pages) {
|
|
2001
|
-
if (!page)
|
|
2183
|
+
if (!page || !page.id)
|
|
2002
2184
|
continue;
|
|
2003
2185
|
const words = page.words ? ` (${formatNumber(page.words)} words)` : "";
|
|
2004
2186
|
lines.push(` ${page.title}${words}`);
|
|
2005
|
-
lines.push(` ID: ${page.
|
|
2187
|
+
lines.push(` ID: ${page.id}`);
|
|
2006
2188
|
lines.push("");
|
|
2007
2189
|
}
|
|
2008
2190
|
lines.push("Use 'pkgseer docs get <package>/<page-id>' to fetch a page.");
|
|
@@ -2020,10 +2202,14 @@ async function docsListAction(packageName, options, deps) {
|
|
|
2020
2202
|
}
|
|
2021
2203
|
if (options.json) {
|
|
2022
2204
|
const pages = result.data.listPackageDocs.pages?.filter((p) => p) ?? [];
|
|
2023
|
-
const slim = pages.map((p) =>
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2205
|
+
const slim = pages.map((p) => {
|
|
2206
|
+
if (!p || !p.id)
|
|
2207
|
+
return null;
|
|
2208
|
+
return {
|
|
2209
|
+
id: p.id,
|
|
2210
|
+
title: p.title
|
|
2211
|
+
};
|
|
2212
|
+
}).filter((p) => p !== null);
|
|
2027
2213
|
output(slim, true);
|
|
2028
2214
|
} else {
|
|
2029
2215
|
console.log(formatDocsList(result.data.listPackageDocs));
|
|
@@ -2031,7 +2217,7 @@ async function docsListAction(packageName, options, deps) {
|
|
|
2031
2217
|
}
|
|
2032
2218
|
var LIST_DESCRIPTION = `List available documentation pages for a package.
|
|
2033
2219
|
|
|
2034
|
-
Shows all documentation pages with titles, page IDs
|
|
2220
|
+
Shows all documentation pages with titles, globally unique page IDs,
|
|
2035
2221
|
word counts, and descriptions. Use the page ID with 'docs get'
|
|
2036
2222
|
to fetch the full content of a specific page.
|
|
2037
2223
|
|
|
@@ -2048,6 +2234,22 @@ function registerDocsListCommand(program) {
|
|
|
2048
2234
|
});
|
|
2049
2235
|
}
|
|
2050
2236
|
// src/commands/docs/search.ts
|
|
2237
|
+
function summarizeSearchResults(results) {
|
|
2238
|
+
const entries = results.entries ?? [];
|
|
2239
|
+
const count = entries.length;
|
|
2240
|
+
const totalMatches = entries.reduce((acc, e) => acc + (e?.matchCount ?? 0), 0);
|
|
2241
|
+
const lines = [];
|
|
2242
|
+
lines.push(`Results: ${count} page${count === 1 ? "" : "s"} | matches: ${totalMatches}`);
|
|
2243
|
+
lines.push("");
|
|
2244
|
+
for (const entry of entries.filter((e) => !!e)) {
|
|
2245
|
+
lines.push(`- ${entry.title ?? entry.slug ?? "untitled"} (${entry.matchCount ?? 0} matches)`);
|
|
2246
|
+
}
|
|
2247
|
+
if (count === 0) {
|
|
2248
|
+
lines.push("No matches. Try broader terms or fewer constraints.");
|
|
2249
|
+
}
|
|
2250
|
+
return lines.join(`
|
|
2251
|
+
`);
|
|
2252
|
+
}
|
|
2051
2253
|
function buildDocRef(entry, searchResult) {
|
|
2052
2254
|
if (!entry?.slug)
|
|
2053
2255
|
return "";
|
|
@@ -2157,7 +2359,7 @@ function formatEntry(entry, searchResult, useColors) {
|
|
|
2157
2359
|
function formatSearchResults(results, useColors) {
|
|
2158
2360
|
const entries = results.entries ?? [];
|
|
2159
2361
|
if (entries.length === 0) {
|
|
2160
|
-
return "No matching documentation found.";
|
|
2362
|
+
return "No matching documentation found. Try broader terms or fewer constraints.";
|
|
2161
2363
|
}
|
|
2162
2364
|
const formatted = entries.filter((e) => e != null).map((entry) => formatEntry(entry, results, useColors));
|
|
2163
2365
|
return formatted.join(`
|
|
@@ -2201,25 +2403,24 @@ function slimSearchResults(results) {
|
|
|
2201
2403
|
}
|
|
2202
2404
|
async function docsSearchAction(queryArg, options, deps) {
|
|
2203
2405
|
const { pkgseerService, config } = deps;
|
|
2204
|
-
const
|
|
2205
|
-
const
|
|
2206
|
-
if (!
|
|
2207
|
-
outputError("Search
|
|
2406
|
+
const providedTerms = Array.isArray(queryArg) ? queryArg : queryArg ? [queryArg] : [];
|
|
2407
|
+
const terms = providedTerms.concat(options.terms ?? []);
|
|
2408
|
+
if (!terms.length) {
|
|
2409
|
+
outputError("Search terms required. Provide terms as argument or with --terms", options.json ?? false);
|
|
2208
2410
|
return;
|
|
2209
2411
|
}
|
|
2210
2412
|
const contextBefore = options.context ? Number.parseInt(options.context, 10) : options.before ? Number.parseInt(options.before, 10) : 2;
|
|
2211
2413
|
const contextAfter = options.context ? Number.parseInt(options.context, 10) : options.after ? Number.parseInt(options.after, 10) : 2;
|
|
2212
2414
|
const maxMatches = options.maxMatches ? Number.parseInt(options.maxMatches, 10) : 5;
|
|
2213
2415
|
const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
|
|
2214
|
-
const matchMode = options.mode?.toUpperCase() || undefined;
|
|
2416
|
+
const matchMode = options.match_mode?.toUpperCase() || options.mode?.toUpperCase() || undefined;
|
|
2215
2417
|
const useColors = shouldUseColors(options.noColor);
|
|
2216
2418
|
const project = options.project ?? config.project;
|
|
2217
2419
|
const packageName = options.package;
|
|
2218
2420
|
if (packageName) {
|
|
2219
2421
|
const registry = toGraphQLRegistry(options.registry ?? "npm");
|
|
2220
2422
|
const result = await pkgseerService.cliDocsSearch(registry, packageName, {
|
|
2221
|
-
|
|
2222
|
-
keywords,
|
|
2423
|
+
keywords: terms,
|
|
2223
2424
|
matchMode,
|
|
2224
2425
|
limit,
|
|
2225
2426
|
version: options.pkgVersion,
|
|
@@ -2237,8 +2438,7 @@ async function docsSearchAction(queryArg, options, deps) {
|
|
|
2237
2438
|
}
|
|
2238
2439
|
if (project) {
|
|
2239
2440
|
const result = await pkgseerService.cliProjectDocsSearch(project, {
|
|
2240
|
-
|
|
2241
|
-
keywords,
|
|
2441
|
+
keywords: terms,
|
|
2242
2442
|
matchMode,
|
|
2243
2443
|
limit,
|
|
2244
2444
|
contextLinesBefore: contextBefore,
|
|
@@ -2259,12 +2459,15 @@ async function docsSearchAction(queryArg, options, deps) {
|
|
|
2259
2459
|
` + " - Use --package <name> to search a specific package", options.json ?? false);
|
|
2260
2460
|
}
|
|
2261
2461
|
function outputResults(results, options, useColors) {
|
|
2262
|
-
|
|
2462
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
2463
|
+
if (format === "json") {
|
|
2263
2464
|
output(slimSearchResults(results), true);
|
|
2264
2465
|
} else if (options.refsOnly) {
|
|
2265
2466
|
console.log(formatRefsOnly(results));
|
|
2266
2467
|
} else if (options.count) {
|
|
2267
2468
|
console.log(formatCount(results));
|
|
2469
|
+
} else if (format === "summary") {
|
|
2470
|
+
console.log(summarizeSearchResults(results));
|
|
2268
2471
|
} else {
|
|
2269
2472
|
console.log(formatSearchResults(results, useColors));
|
|
2270
2473
|
}
|
|
@@ -2284,11 +2487,11 @@ Examples:
|
|
|
2284
2487
|
pkgseer docs search "error handling" --package express
|
|
2285
2488
|
pkgseer docs search log --package express -C 3
|
|
2286
2489
|
|
|
2287
|
-
# Multiple
|
|
2288
|
-
pkgseer docs search -
|
|
2490
|
+
# Multiple terms (OR by default)
|
|
2491
|
+
pkgseer docs search -t "middleware,routing" --package express
|
|
2289
2492
|
|
|
2290
2493
|
# Strict matching (AND mode)
|
|
2291
|
-
pkgseer docs search -
|
|
2494
|
+
pkgseer docs search -t "error,middleware" --match-mode all --package express
|
|
2292
2495
|
|
|
2293
2496
|
# Output for piping
|
|
2294
2497
|
pkgseer docs search log --package express --refs-only | \\
|
|
@@ -2297,14 +2500,14 @@ Examples:
|
|
|
2297
2500
|
# Count matches
|
|
2298
2501
|
pkgseer docs search error --package express --count`;
|
|
2299
2502
|
function addSearchOptions(cmd) {
|
|
2300
|
-
return cmd.option("-p, --package <name>", "Search specific package (overrides project)").option("-r, --registry <registry>", "Package registry (with --package)", "npm").option("-v, --pkg-version <version>", "Package version (with --package)").option("--project <name>", "Project name (overrides config)").option("-
|
|
2503
|
+
return cmd.option("-p, --package <name>", "Search specific package (overrides project)").option("-r, --registry <registry>", "Package registry (with --package)", "npm").option("-v, --pkg-version <version>", "Package version (with --package)").option("--project <name>", "Project name (overrides config)").option("-t, --terms <words>", "Comma-separated search terms", (val) => val.split(",").map((s) => s.trim())).option("-l, --limit <n>", "Max results (default: 25)").option("-A, --after <n>", "Lines of context after match (default: 2)").option("-B, --before <n>", "Lines of context before match (default: 2)").option("-C, --context <n>", "Lines of context before and after match").option("--max-matches <n>", "Max matches per page (default: 5)").option("--match-mode <mode>", "How to combine terms: any/or (default) or all/and").option("--refs-only", "Output only page references (for piping)").option("--count", "Output only match counts per page").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
|
|
2301
2504
|
}
|
|
2302
2505
|
function registerDocsSearchCommand(program) {
|
|
2303
|
-
const cmd = program.command("search [
|
|
2304
|
-
addSearchOptions(cmd).action(async (
|
|
2506
|
+
const cmd = program.command("search [terms...]").summary("Search documentation").description(SEARCH_DESCRIPTION);
|
|
2507
|
+
addSearchOptions(cmd).action(async (terms, options) => {
|
|
2305
2508
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
2306
2509
|
const deps = await createContainer();
|
|
2307
|
-
await docsSearchAction(
|
|
2510
|
+
await docsSearchAction(terms, options, deps);
|
|
2308
2511
|
});
|
|
2309
2512
|
});
|
|
2310
2513
|
}
|
|
@@ -3669,9 +3872,28 @@ var schemas = {
|
|
|
3669
3872
|
packageName: z2.string().max(255).describe("Name of the package"),
|
|
3670
3873
|
version: z2.string().max(100).optional().describe("Specific version (defaults to latest)")
|
|
3671
3874
|
};
|
|
3875
|
+
function buildHintedMessage(operation, message) {
|
|
3876
|
+
const lower = message.toLowerCase();
|
|
3877
|
+
const isTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
|
|
3878
|
+
if (isTimeout) {
|
|
3879
|
+
return `Failed to ${operation}: ${message}. ` + "Hint: try lowering the limit or narrowing the scope, then retry.";
|
|
3880
|
+
}
|
|
3881
|
+
return `Failed to ${operation}: ${message}`;
|
|
3882
|
+
}
|
|
3672
3883
|
function handleGraphQLErrors(errors) {
|
|
3673
3884
|
if (errors && errors.length > 0) {
|
|
3674
|
-
|
|
3885
|
+
const messages = errors.map((e) => e.message).filter(Boolean);
|
|
3886
|
+
const combined = messages.join(", ");
|
|
3887
|
+
const lower = combined.toLowerCase();
|
|
3888
|
+
const hasNotFound = lower.includes("not found") || lower.includes("does not exist");
|
|
3889
|
+
const hasTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
|
|
3890
|
+
if (hasNotFound) {
|
|
3891
|
+
return errorResult(`Error: ${combined}. Hint: verify the name/registry and that the resource exists.`);
|
|
3892
|
+
}
|
|
3893
|
+
if (hasTimeout) {
|
|
3894
|
+
return errorResult(`Error: ${combined}. Hint: try lowering the limit or narrowing the scope, then retry.`);
|
|
3895
|
+
}
|
|
3896
|
+
return errorResult(`Error: ${combined}`);
|
|
3675
3897
|
}
|
|
3676
3898
|
return null;
|
|
3677
3899
|
}
|
|
@@ -3680,7 +3902,7 @@ async function withErrorHandling(operation, fn) {
|
|
|
3680
3902
|
return await fn();
|
|
3681
3903
|
} catch (error2) {
|
|
3682
3904
|
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
3683
|
-
return errorResult(
|
|
3905
|
+
return errorResult(buildHintedMessage(operation, message));
|
|
3684
3906
|
}
|
|
3685
3907
|
}
|
|
3686
3908
|
function notFoundError(packageName, registry) {
|
|
@@ -3699,7 +3921,7 @@ var argsSchema = {
|
|
|
3699
3921
|
function createComparePackagesTool(pkgseerService) {
|
|
3700
3922
|
return {
|
|
3701
3923
|
name: "compare_packages",
|
|
3702
|
-
description:
|
|
3924
|
+
description: 'Compares 2-10 packages across metadata, quality, and security. Example: [{"registry":"npm","name":"react","version":"18.2.0"},{"registry":"pypi","name":"requests"}].',
|
|
3703
3925
|
schema: argsSchema,
|
|
3704
3926
|
handler: async ({ packages }, _extra) => {
|
|
3705
3927
|
return withErrorHandling("compare packages", async () => {
|
|
@@ -3723,26 +3945,23 @@ function createComparePackagesTool(pkgseerService) {
|
|
|
3723
3945
|
// src/tools/fetch-package-doc.ts
|
|
3724
3946
|
import { z as z4 } from "zod";
|
|
3725
3947
|
var argsSchema2 = {
|
|
3726
|
-
|
|
3727
|
-
package_name: schemas.packageName.describe("Name of the package to fetch documentation for"),
|
|
3728
|
-
page_id: z4.string().max(500).describe("Documentation page identifier (from list_package_docs)"),
|
|
3729
|
-
version: schemas.version
|
|
3948
|
+
page_id: z4.string().max(500).describe("Globally unique documentation page identifier (from list_package_docs)")
|
|
3730
3949
|
};
|
|
3731
3950
|
function createFetchPackageDocTool(pkgseerService) {
|
|
3732
3951
|
return {
|
|
3733
3952
|
name: "fetch_package_doc",
|
|
3734
|
-
description: "Fetches the full content of a
|
|
3953
|
+
description: "Fetches the full content of a documentation page by globally unique page ID (from list_package_docs). Returns full metadata (title, content, format, breadcrumbs, source, base URL, last updated).",
|
|
3735
3954
|
schema: argsSchema2,
|
|
3736
|
-
handler: async ({
|
|
3955
|
+
handler: async ({ page_id }, _extra) => {
|
|
3737
3956
|
return withErrorHandling("fetch documentation page", async () => {
|
|
3738
|
-
const result = await pkgseerService.
|
|
3957
|
+
const result = await pkgseerService.getDocPage(page_id);
|
|
3739
3958
|
const graphqlError = handleGraphQLErrors(result.errors);
|
|
3740
3959
|
if (graphqlError)
|
|
3741
3960
|
return graphqlError;
|
|
3742
|
-
if (!result.data.
|
|
3743
|
-
return errorResult(`Documentation page not found: ${page_id}
|
|
3961
|
+
if (!result.data.getDocPage) {
|
|
3962
|
+
return errorResult(`Documentation page not found: ${page_id}`);
|
|
3744
3963
|
}
|
|
3745
|
-
return textResult(JSON.stringify(result.data.
|
|
3964
|
+
return textResult(JSON.stringify(result.data.getDocPage, null, 2));
|
|
3746
3965
|
});
|
|
3747
3966
|
}
|
|
3748
3967
|
};
|
|
@@ -3756,7 +3975,7 @@ var argsSchema3 = {
|
|
|
3756
3975
|
function createListPackageDocsTool(pkgseerService) {
|
|
3757
3976
|
return {
|
|
3758
3977
|
name: "list_package_docs",
|
|
3759
|
-
description: "Lists documentation pages for a package version. Returns page titles,
|
|
3978
|
+
description: "Lists documentation pages for a package version. Returns page titles, globally unique IDs, and metadata. Use this to discover available documentation before fetching specific pages.",
|
|
3760
3979
|
schema: argsSchema3,
|
|
3761
3980
|
handler: async ({ registry, package_name, version: version2 }, _extra) => {
|
|
3762
3981
|
return withErrorHandling("list package documentation", async () => {
|
|
@@ -3781,10 +4000,82 @@ var argsSchema4 = {
|
|
|
3781
4000
|
include_transitive: z5.boolean().optional().describe("Whether to include transitive dependency DAG"),
|
|
3782
4001
|
max_depth: z5.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
|
|
3783
4002
|
};
|
|
4003
|
+
function decodeDag(rawDag) {
|
|
4004
|
+
if (!rawDag || typeof rawDag !== "object")
|
|
4005
|
+
return;
|
|
4006
|
+
const dag = rawDag;
|
|
4007
|
+
const nodesSource = dag.n ?? dag.nodes;
|
|
4008
|
+
const nodesRaw = nodesSource && typeof nodesSource === "object" && !Array.isArray(nodesSource) ? nodesSource : undefined;
|
|
4009
|
+
const edgesSource = dag.e ?? dag.edges;
|
|
4010
|
+
const edgesRaw = Array.isArray(edgesSource) ? edgesSource : undefined;
|
|
4011
|
+
const nodes = nodesRaw ? Object.entries(nodesRaw).map(([id, value]) => {
|
|
4012
|
+
const {
|
|
4013
|
+
n: nameField,
|
|
4014
|
+
v: versionField,
|
|
4015
|
+
l: labelField,
|
|
4016
|
+
...rest
|
|
4017
|
+
} = value ?? {};
|
|
4018
|
+
return {
|
|
4019
|
+
id,
|
|
4020
|
+
name: nameField ?? rest.name,
|
|
4021
|
+
version: versionField ?? rest.version,
|
|
4022
|
+
label: labelField ?? rest.label,
|
|
4023
|
+
...rest
|
|
4024
|
+
};
|
|
4025
|
+
}) : [];
|
|
4026
|
+
const edges = edgesRaw?.map((edge) => {
|
|
4027
|
+
if (Array.isArray(edge) && edge.length >= 2) {
|
|
4028
|
+
return { from: String(edge[0]), to: String(edge[1]) };
|
|
4029
|
+
}
|
|
4030
|
+
const edgeObj = edge ?? {};
|
|
4031
|
+
return {
|
|
4032
|
+
from: String(edgeObj.f ?? edgeObj.from ?? edgeObj.source ?? edgeObj.s ?? ""),
|
|
4033
|
+
to: String(edgeObj.t ?? edgeObj.to ?? edgeObj.target ?? edgeObj.d ?? "")
|
|
4034
|
+
};
|
|
4035
|
+
}) ?? [];
|
|
4036
|
+
return {
|
|
4037
|
+
version: dag.v ?? dag.version,
|
|
4038
|
+
nodes,
|
|
4039
|
+
edges
|
|
4040
|
+
};
|
|
4041
|
+
}
|
|
4042
|
+
function buildEdgeDepths(decodedDag, rootId) {
|
|
4043
|
+
if (!decodedDag)
|
|
4044
|
+
return;
|
|
4045
|
+
const root = rootId ?? decodedDag.nodes[0]?.id;
|
|
4046
|
+
if (!root)
|
|
4047
|
+
return;
|
|
4048
|
+
const depthMap = new Map([[root, 0]]);
|
|
4049
|
+
const adjacency = new Map;
|
|
4050
|
+
for (const edge of decodedDag.edges) {
|
|
4051
|
+
if (!edge.from || !edge.to)
|
|
4052
|
+
continue;
|
|
4053
|
+
const list = adjacency.get(edge.from) ?? [];
|
|
4054
|
+
list.push(edge.to);
|
|
4055
|
+
adjacency.set(edge.from, list);
|
|
4056
|
+
}
|
|
4057
|
+
const queue = [root];
|
|
4058
|
+
while (queue.length > 0) {
|
|
4059
|
+
const current = queue.shift();
|
|
4060
|
+
const currentDepth = depthMap.get(current) ?? 0;
|
|
4061
|
+
for (const next of adjacency.get(current) ?? []) {
|
|
4062
|
+
if (!depthMap.has(next)) {
|
|
4063
|
+
depthMap.set(next, currentDepth + 1);
|
|
4064
|
+
queue.push(next);
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return decodedDag.edges.map((edge) => ({
|
|
4069
|
+
from: edge.from,
|
|
4070
|
+
to: edge.to,
|
|
4071
|
+
depthFrom: depthMap.get(edge.from),
|
|
4072
|
+
depthTo: depthMap.get(edge.to)
|
|
4073
|
+
}));
|
|
4074
|
+
}
|
|
3784
4075
|
function createPackageDependenciesTool(pkgseerService) {
|
|
3785
4076
|
return {
|
|
3786
4077
|
name: "package_dependencies",
|
|
3787
|
-
description: "Retrieves direct and transitive dependencies for a package version",
|
|
4078
|
+
description: "Retrieves direct and transitive dependencies for a package version. Options: include_transitive (bool) and max_depth (1-10). Transitive output includes a DAG (`n/e/v` keys) decoded into readable nodes/edges with depth and counts; raw DAG is preserved alongside decoded form.",
|
|
3788
4079
|
schema: argsSchema4,
|
|
3789
4080
|
handler: async ({ registry, package_name, version: version2, include_transitive, max_depth }, _extra) => {
|
|
3790
4081
|
return withErrorHandling("fetch package dependencies", async () => {
|
|
@@ -3795,7 +4086,29 @@ function createPackageDependenciesTool(pkgseerService) {
|
|
|
3795
4086
|
if (!result.data.packageDependencies) {
|
|
3796
4087
|
return notFoundError(package_name, registry);
|
|
3797
4088
|
}
|
|
3798
|
-
|
|
4089
|
+
const deps = result.data.packageDependencies.dependencies;
|
|
4090
|
+
const decodedDag = decodeDag(deps?.transitive?.dag);
|
|
4091
|
+
const transitiveDag = deps?.transitive?.dag;
|
|
4092
|
+
const edgesWithDepth = buildEdgeDepths(decodedDag, typeof transitiveDag?.root === "string" ? transitiveDag.root : undefined);
|
|
4093
|
+
const output2 = {
|
|
4094
|
+
summary: deps?.summary,
|
|
4095
|
+
package: result.data.packageDependencies.package,
|
|
4096
|
+
direct: deps?.direct,
|
|
4097
|
+
transitive: deps?.transitive ? {
|
|
4098
|
+
totalEdges: deps.transitive.totalEdges,
|
|
4099
|
+
uniquePackagesCount: deps.transitive.uniquePackagesCount,
|
|
4100
|
+
uniqueDependencies: deps.transitive.uniqueDependencies,
|
|
4101
|
+
conflicts: deps.transitive.conflicts,
|
|
4102
|
+
circularDependencies: deps.transitive.circularDependencies,
|
|
4103
|
+
dag: {
|
|
4104
|
+
decoded: decodedDag,
|
|
4105
|
+
edgesWithDepth,
|
|
4106
|
+
raw: deps.transitive.dag
|
|
4107
|
+
}
|
|
4108
|
+
} : undefined,
|
|
4109
|
+
raw: result.data.packageDependencies
|
|
4110
|
+
};
|
|
4111
|
+
return textResult(JSON.stringify(output2, null, 2));
|
|
3799
4112
|
});
|
|
3800
4113
|
}
|
|
3801
4114
|
};
|
|
@@ -3879,8 +4192,8 @@ import { z as z6 } from "zod";
|
|
|
3879
4192
|
var argsSchema8 = {
|
|
3880
4193
|
registry: schemas.registry,
|
|
3881
4194
|
package_name: schemas.packageName.describe("Name of the package to search documentation for"),
|
|
3882
|
-
|
|
3883
|
-
|
|
4195
|
+
terms: z6.array(z6.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
|
|
4196
|
+
match_mode: z6.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
|
|
3884
4197
|
include_snippets: z6.boolean().optional().describe("Include content excerpts around matches"),
|
|
3885
4198
|
limit: z6.number().int().min(1).max(100).optional().describe("Maximum number of results to return"),
|
|
3886
4199
|
version: schemas.version
|
|
@@ -3888,25 +4201,28 @@ var argsSchema8 = {
|
|
|
3888
4201
|
function createSearchPackageDocsTool(pkgseerService) {
|
|
3889
4202
|
return {
|
|
3890
4203
|
name: "search_package_docs",
|
|
3891
|
-
description: "Searches package documentation
|
|
4204
|
+
description: "Searches package documentation using search terms and match modes (any/all). Returns ranked results with match context. Defaults to include_snippets=true.",
|
|
3892
4205
|
schema: argsSchema8,
|
|
3893
4206
|
handler: async ({
|
|
3894
4207
|
registry,
|
|
3895
4208
|
package_name,
|
|
3896
|
-
|
|
3897
|
-
|
|
4209
|
+
terms,
|
|
4210
|
+
match_mode,
|
|
3898
4211
|
include_snippets,
|
|
3899
4212
|
limit,
|
|
3900
4213
|
version: version2
|
|
3901
4214
|
}, _extra) => {
|
|
3902
4215
|
return withErrorHandling("search package documentation", async () => {
|
|
3903
|
-
|
|
3904
|
-
|
|
4216
|
+
const normalizedTerms = terms?.map((term) => term.trim()).filter((term) => term.length > 0) ?? [];
|
|
4217
|
+
if (normalizedTerms.length === 0) {
|
|
4218
|
+
return errorResult("Search terms are required to run documentation search.");
|
|
3905
4219
|
}
|
|
4220
|
+
const matchMode = match_mode === "all" || match_mode === "and" ? "AND" : match_mode === "any" || match_mode === "or" ? "OR" : undefined;
|
|
4221
|
+
const includeSnippets = include_snippets ?? true;
|
|
3906
4222
|
const result = await pkgseerService.searchPackageDocs(toGraphQLRegistry2(registry), package_name, {
|
|
3907
|
-
keywords,
|
|
3908
|
-
|
|
3909
|
-
includeSnippets
|
|
4223
|
+
keywords: normalizedTerms,
|
|
4224
|
+
matchMode,
|
|
4225
|
+
includeSnippets,
|
|
3910
4226
|
limit,
|
|
3911
4227
|
version: version2
|
|
3912
4228
|
});
|
|
@@ -3916,6 +4232,9 @@ function createSearchPackageDocsTool(pkgseerService) {
|
|
|
3916
4232
|
if (!result.data.searchPackageDocs) {
|
|
3917
4233
|
return errorResult(`No documentation found for ${package_name} in ${registry}`);
|
|
3918
4234
|
}
|
|
4235
|
+
if ((result.data.searchPackageDocs.entries ?? []).length === 0 && normalizedTerms.length > 0) {
|
|
4236
|
+
return errorResult(`No documentation matched: ${normalizedTerms.join(", ")}. ` + "Try fewer or broader terms, or reduce match constraints.");
|
|
4237
|
+
}
|
|
3919
4238
|
return textResult(JSON.stringify(result.data.searchPackageDocs, null, 2));
|
|
3920
4239
|
});
|
|
3921
4240
|
}
|
|
@@ -3925,8 +4244,8 @@ function createSearchPackageDocsTool(pkgseerService) {
|
|
|
3925
4244
|
import { z as z7 } from "zod";
|
|
3926
4245
|
var argsSchema9 = {
|
|
3927
4246
|
project: z7.string().optional().describe("Project name to search. Optional if configured in pkgseer.yml; only needed to search a different project."),
|
|
3928
|
-
|
|
3929
|
-
|
|
4247
|
+
terms: z7.array(z7.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
|
|
4248
|
+
match_mode: z7.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
|
|
3930
4249
|
include_snippets: z7.boolean().optional().describe("Include content excerpts around matches"),
|
|
3931
4250
|
limit: z7.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
|
|
3932
4251
|
};
|
|
@@ -3934,21 +4253,24 @@ function createSearchProjectDocsTool(deps) {
|
|
|
3934
4253
|
const { pkgseerService, config } = deps;
|
|
3935
4254
|
return {
|
|
3936
4255
|
name: "search_project_docs",
|
|
3937
|
-
description: "Searches documentation across all dependencies in a PkgSeer project. Returns ranked results from multiple packages. Uses project from pkgseer.yml config by default.",
|
|
4256
|
+
description: "Searches documentation across all dependencies in a PkgSeer project using search terms and match modes (any/all). Returns ranked results from multiple packages. Uses project from pkgseer.yml config by default.",
|
|
3938
4257
|
schema: argsSchema9,
|
|
3939
|
-
handler: async ({ project,
|
|
4258
|
+
handler: async ({ project, terms, match_mode, include_snippets, limit }, _extra) => {
|
|
3940
4259
|
return withErrorHandling("search project documentation", async () => {
|
|
3941
4260
|
const resolvedProject = project ?? config.project;
|
|
3942
4261
|
if (!resolvedProject) {
|
|
3943
4262
|
return errorResult("No project provided and none configured in pkgseer.yml. " + "Either pass project parameter or add project to your config.");
|
|
3944
4263
|
}
|
|
3945
|
-
|
|
3946
|
-
|
|
4264
|
+
const normalizedTerms = terms?.map((term) => term.trim()).filter((term) => term.length > 0) ?? [];
|
|
4265
|
+
if (normalizedTerms.length === 0) {
|
|
4266
|
+
return errorResult("Search terms are required to run documentation search.");
|
|
3947
4267
|
}
|
|
4268
|
+
const matchMode = match_mode === "all" || match_mode === "and" ? "AND" : match_mode === "any" || match_mode === "or" ? "OR" : undefined;
|
|
4269
|
+
const includeSnippets = include_snippets ?? true;
|
|
3948
4270
|
const result = await pkgseerService.searchProjectDocs(resolvedProject, {
|
|
3949
|
-
keywords,
|
|
3950
|
-
|
|
3951
|
-
includeSnippets
|
|
4271
|
+
keywords: normalizedTerms,
|
|
4272
|
+
matchMode,
|
|
4273
|
+
includeSnippets,
|
|
3952
4274
|
limit
|
|
3953
4275
|
});
|
|
3954
4276
|
const graphqlError = handleGraphQLErrors(result.errors);
|
|
@@ -3957,6 +4279,9 @@ function createSearchProjectDocsTool(deps) {
|
|
|
3957
4279
|
if (!result.data.searchProjectDocs) {
|
|
3958
4280
|
return errorResult(`Project not found: ${resolvedProject}`);
|
|
3959
4281
|
}
|
|
4282
|
+
if ((result.data.searchProjectDocs.entries ?? []).length === 0 && normalizedTerms.length > 0) {
|
|
4283
|
+
return errorResult(`No documentation matched: ${normalizedTerms.join(", ")}. ` + "Try fewer or broader terms, or reduce match constraints.");
|
|
4284
|
+
}
|
|
3960
4285
|
return textResult(JSON.stringify(result.data.searchProjectDocs, null, 2));
|
|
3961
4286
|
});
|
|
3962
4287
|
}
|
|
@@ -4174,7 +4499,7 @@ function registerPkgCompareCommand(program) {
|
|
|
4174
4499
|
});
|
|
4175
4500
|
}
|
|
4176
4501
|
// src/commands/pkg/deps.ts
|
|
4177
|
-
function formatPackageDependencies(data) {
|
|
4502
|
+
function formatPackageDependencies(data, transitiveRequested) {
|
|
4178
4503
|
const lines = [];
|
|
4179
4504
|
const pkg = data.package;
|
|
4180
4505
|
const deps = data.dependencies;
|
|
@@ -4202,32 +4527,52 @@ function formatPackageDependencies(data) {
|
|
|
4202
4527
|
} else {
|
|
4203
4528
|
lines.push("No direct dependencies.");
|
|
4204
4529
|
}
|
|
4530
|
+
if (!transitiveRequested) {
|
|
4531
|
+
lines.push("");
|
|
4532
|
+
lines.push("Tip: use --transitive for full dependency graph traversal.");
|
|
4533
|
+
}
|
|
4205
4534
|
return lines.join(`
|
|
4206
4535
|
`);
|
|
4207
4536
|
}
|
|
4208
4537
|
async function pkgDepsAction(packageName, options, deps) {
|
|
4209
4538
|
const { pkgseerService } = deps;
|
|
4210
4539
|
const registry = toGraphQLRegistry(options.registry);
|
|
4211
|
-
const
|
|
4540
|
+
const transitiveRequested = options.transitive ?? false;
|
|
4541
|
+
const result = await pkgseerService.cliPackageDeps(registry, packageName, options.pkgVersion, options.transitive, options.maxDepth ? Number.parseInt(options.maxDepth, 10) : undefined);
|
|
4212
4542
|
handleErrors(result.errors, options.json ?? false);
|
|
4213
4543
|
if (!result.data.packageDependencies) {
|
|
4214
4544
|
outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
|
|
4215
4545
|
return;
|
|
4216
4546
|
}
|
|
4217
|
-
|
|
4547
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
4548
|
+
if (format === "json") {
|
|
4218
4549
|
const data = result.data.packageDependencies;
|
|
4219
|
-
|
|
4550
|
+
output({
|
|
4220
4551
|
package: `${data.package?.name}@${data.package?.version}`,
|
|
4221
4552
|
directCount: data.dependencies?.summary?.directCount ?? 0,
|
|
4222
|
-
|
|
4553
|
+
uniquePackagesCount: data.dependencies?.summary?.uniquePackagesCount ?? 0,
|
|
4554
|
+
transitiveIncluded: options.transitive ?? false,
|
|
4555
|
+
dependencies: data.dependencies?.direct?.filter((d) => Boolean(d)).map((d) => ({
|
|
4223
4556
|
name: d.name,
|
|
4224
4557
|
version: d.versionConstraint,
|
|
4225
4558
|
type: d.type
|
|
4226
4559
|
}))
|
|
4227
|
-
};
|
|
4228
|
-
|
|
4560
|
+
}, true);
|
|
4561
|
+
} else if (format === "summary") {
|
|
4562
|
+
const data = result.data.packageDependencies;
|
|
4563
|
+
const deps2 = data.dependencies;
|
|
4564
|
+
const directCount = deps2?.summary?.directCount ?? 0;
|
|
4565
|
+
const uniquePackagesCount = deps2?.summary?.uniquePackagesCount ?? 0;
|
|
4566
|
+
const lines = [
|
|
4567
|
+
`Package: ${data.package?.name ?? ""}@${data.package?.version ?? ""}`,
|
|
4568
|
+
`Direct deps: ${directCount}`,
|
|
4569
|
+
`Unique packages: ${uniquePackagesCount || "N/A"}`,
|
|
4570
|
+
transitiveRequested ? "Transitive: included" : "Transitive: not included (use --transitive)"
|
|
4571
|
+
];
|
|
4572
|
+
console.log(lines.join(`
|
|
4573
|
+
`));
|
|
4229
4574
|
} else {
|
|
4230
|
-
console.log(formatPackageDependencies(result.data.packageDependencies));
|
|
4575
|
+
console.log(formatPackageDependencies(result.data.packageDependencies, transitiveRequested));
|
|
4231
4576
|
}
|
|
4232
4577
|
}
|
|
4233
4578
|
var DEPS_DESCRIPTION = `Get package dependencies.
|
|
@@ -4240,7 +4585,7 @@ Examples:
|
|
|
4240
4585
|
pkgseer pkg deps lodash --transitive
|
|
4241
4586
|
pkgseer pkg deps requests --registry pypi --json`;
|
|
4242
4587
|
function registerPkgDepsCommand(program) {
|
|
4243
|
-
program.command("deps <package>").summary("Get package dependencies").description(DEPS_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("-t, --transitive", "Include transitive dependencies").option("--json", "Output as JSON").action(async (packageName, options) => {
|
|
4588
|
+
program.command("deps <package>").summary("Get package dependencies").description(DEPS_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("-t, --transitive", "Include transitive dependencies").option("--max-depth <n>", "Maximum transitive depth (1-10, defaults to server)").option("--format <format>", "Output format: human|summary|json", "human").option("--json", "Output as JSON").action(async (packageName, options) => {
|
|
4244
4589
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
4245
4590
|
const deps = await createContainer();
|
|
4246
4591
|
await pkgDepsAction(packageName, options, deps);
|
|
@@ -4341,18 +4686,15 @@ function formatPackageQuality(data) {
|
|
|
4341
4686
|
if (!quality) {
|
|
4342
4687
|
return "No quality data available.";
|
|
4343
4688
|
}
|
|
4344
|
-
|
|
4345
|
-
lines.push("");
|
|
4346
|
-
if (
|
|
4347
|
-
lines.push("
|
|
4348
|
-
for (const category of
|
|
4349
|
-
|
|
4350
|
-
const name = (category.category || "Unknown").padEnd(20);
|
|
4351
|
-
lines.push(` ${name} ${formatScore(category.score)}`);
|
|
4352
|
-
}
|
|
4689
|
+
const topCategories = (quality.categories ?? []).filter((c) => Boolean(c)).sort((a, b) => (b?.score ?? 0) - (a?.score ?? 0)).slice(0, 3);
|
|
4690
|
+
lines.push(`\uD83D\uDCCA Quality: Grade ${quality.grade ?? "N/A"} (${formatScore(quality.overallScore)})`);
|
|
4691
|
+
if (topCategories.length > 0) {
|
|
4692
|
+
lines.push("Top drivers:");
|
|
4693
|
+
for (const category of topCategories) {
|
|
4694
|
+
lines.push(` - ${(category.category ?? "Unknown").toLowerCase()}: ${formatScore(category.score)}`);
|
|
4353
4695
|
}
|
|
4354
|
-
lines.push("");
|
|
4355
4696
|
}
|
|
4697
|
+
lines.push("Tip: use --json for full category breakdown.");
|
|
4356
4698
|
return lines.join(`
|
|
4357
4699
|
`);
|
|
4358
4700
|
}
|
|
@@ -4365,7 +4707,8 @@ async function pkgQualityAction(packageName, options, deps) {
|
|
|
4365
4707
|
outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
|
|
4366
4708
|
return;
|
|
4367
4709
|
}
|
|
4368
|
-
|
|
4710
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
4711
|
+
if (format === "json") {
|
|
4369
4712
|
const quality = result.data.packageQuality.quality;
|
|
4370
4713
|
const slim = {
|
|
4371
4714
|
package: `${result.data.packageQuality.package?.name}@${result.data.packageQuality.package?.version}`,
|
|
@@ -4394,7 +4737,7 @@ Examples:
|
|
|
4394
4737
|
pkgseer pkg quality express -v 4.18.0
|
|
4395
4738
|
pkgseer pkg quality requests --registry pypi --json`;
|
|
4396
4739
|
function registerPkgQualityCommand(program) {
|
|
4397
|
-
program.command("quality <package>").summary("Get package quality score").description(QUALITY_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("--json", "Output as JSON").action(async (packageName, options) => {
|
|
4740
|
+
program.command("quality <package>").summary("Get package quality score").description(QUALITY_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("--format <format>", "Output format: human|summary|json", "human").option("--json", "Output as JSON").action(async (packageName, options) => {
|
|
4398
4741
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
4399
4742
|
const deps = await createContainer();
|
|
4400
4743
|
await pkgQualityAction(packageName, options, deps);
|
package/dist/index.js
CHANGED