@pkgseer/cli 0.1.3 → 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 +289 -85
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-8dn0z2ja.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
|
|
@@ -488,12 +489,12 @@ var GetDocPageDocument = gql`
|
|
|
488
489
|
}
|
|
489
490
|
`;
|
|
490
491
|
var SearchPackageDocsDocument = gql`
|
|
491
|
-
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) {
|
|
492
493
|
searchPackageDocs(
|
|
493
494
|
registry: $registry
|
|
494
495
|
packageName: $packageName
|
|
495
496
|
keywords: $keywords
|
|
496
|
-
|
|
497
|
+
matchMode: $matchMode
|
|
497
498
|
includeSnippets: $includeSnippets
|
|
498
499
|
limit: $limit
|
|
499
500
|
version: $version
|
|
@@ -502,7 +503,6 @@ var SearchPackageDocsDocument = gql`
|
|
|
502
503
|
registry
|
|
503
504
|
packageName
|
|
504
505
|
version
|
|
505
|
-
query
|
|
506
506
|
entries {
|
|
507
507
|
id
|
|
508
508
|
title
|
|
@@ -523,17 +523,16 @@ var SearchPackageDocsDocument = gql`
|
|
|
523
523
|
}
|
|
524
524
|
`;
|
|
525
525
|
var SearchProjectDocsDocument = gql`
|
|
526
|
-
query SearchProjectDocs($project: String!, $keywords: [String!], $
|
|
526
|
+
query SearchProjectDocs($project: String!, $keywords: [String!], $matchMode: MatchMode, $includeSnippets: Boolean, $limit: Int) {
|
|
527
527
|
searchProjectDocs(
|
|
528
528
|
project: $project
|
|
529
529
|
keywords: $keywords
|
|
530
|
-
|
|
530
|
+
matchMode: $matchMode
|
|
531
531
|
includeSnippets: $includeSnippets
|
|
532
532
|
limit: $limit
|
|
533
533
|
) {
|
|
534
534
|
schemaVersion
|
|
535
535
|
project
|
|
536
|
-
query
|
|
537
536
|
entries {
|
|
538
537
|
id
|
|
539
538
|
title
|
|
@@ -1255,7 +1254,7 @@ class PkgseerServiceImpl {
|
|
|
1255
1254
|
registry,
|
|
1256
1255
|
packageName,
|
|
1257
1256
|
keywords: options?.keywords,
|
|
1258
|
-
|
|
1257
|
+
matchMode: options?.matchMode,
|
|
1259
1258
|
includeSnippets: options?.includeSnippets,
|
|
1260
1259
|
limit: options?.limit,
|
|
1261
1260
|
version: options?.version
|
|
@@ -1266,7 +1265,7 @@ class PkgseerServiceImpl {
|
|
|
1266
1265
|
const result = await this.client.SearchProjectDocs({
|
|
1267
1266
|
project,
|
|
1268
1267
|
keywords: options?.keywords,
|
|
1269
|
-
|
|
1268
|
+
matchMode: options?.matchMode,
|
|
1270
1269
|
includeSnippets: options?.includeSnippets,
|
|
1271
1270
|
limit: options?.limit
|
|
1272
1271
|
});
|
|
@@ -1292,12 +1291,13 @@ class PkgseerServiceImpl {
|
|
|
1292
1291
|
});
|
|
1293
1292
|
return { data: result.data, errors: result.errors };
|
|
1294
1293
|
}
|
|
1295
|
-
async cliPackageDeps(registry, name, version2, includeTransitive) {
|
|
1294
|
+
async cliPackageDeps(registry, name, version2, includeTransitive, maxDepth) {
|
|
1296
1295
|
const result = await this.client.CliPackageDeps({
|
|
1297
1296
|
registry,
|
|
1298
1297
|
name,
|
|
1299
1298
|
version: version2,
|
|
1300
|
-
includeTransitive
|
|
1299
|
+
includeTransitive,
|
|
1300
|
+
maxDepth
|
|
1301
1301
|
});
|
|
1302
1302
|
return { data: result.data, errors: result.errors };
|
|
1303
1303
|
}
|
|
@@ -1724,10 +1724,26 @@ function outputError(message, json) {
|
|
|
1724
1724
|
process.exit(1);
|
|
1725
1725
|
}
|
|
1726
1726
|
function handleErrors(errors, json) {
|
|
1727
|
-
if (errors
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
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);
|
|
1731
1747
|
}
|
|
1732
1748
|
function formatNumber(num) {
|
|
1733
1749
|
if (num == null)
|
|
@@ -1805,6 +1821,19 @@ async function withCliErrorHandling(json, fn) {
|
|
|
1805
1821
|
return;
|
|
1806
1822
|
}
|
|
1807
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
|
+
}
|
|
1808
1837
|
const colonMatch = message.match(/^([^:]+):/);
|
|
1809
1838
|
const shortMessage = colonMatch?.[1] ?? message;
|
|
1810
1839
|
outputError(shortMessage, json);
|
|
@@ -1855,7 +1884,7 @@ function parsePackageRef(ref) {
|
|
|
1855
1884
|
}
|
|
1856
1885
|
return { packageName, pageId, originalRef: ref };
|
|
1857
1886
|
}
|
|
1858
|
-
function formatDocPage(data, ref, verbose = false) {
|
|
1887
|
+
function formatDocPage(data, ref, verbose = false, previewLines) {
|
|
1859
1888
|
const lines = [];
|
|
1860
1889
|
const page = data.page;
|
|
1861
1890
|
if (!page) {
|
|
@@ -1913,9 +1942,22 @@ function formatDocPage(data, ref, verbose = false) {
|
|
|
1913
1942
|
lines.push(`# ${page.title}`);
|
|
1914
1943
|
lines.push("");
|
|
1915
1944
|
if (page.content) {
|
|
1916
|
-
|
|
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
|
+
}
|
|
1917
1959
|
} else {
|
|
1918
|
-
lines.push("(No content available)");
|
|
1960
|
+
lines.push("(No content available. Verify the page ID or try another version.)");
|
|
1919
1961
|
}
|
|
1920
1962
|
return lines.join(`
|
|
1921
1963
|
`);
|
|
@@ -2028,6 +2070,8 @@ async function docsGetAction(refs, options, deps) {
|
|
|
2028
2070
|
}
|
|
2029
2071
|
const { pkgseerService } = deps;
|
|
2030
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;
|
|
2031
2075
|
const parsedRefs = [];
|
|
2032
2076
|
for (const ref of refs) {
|
|
2033
2077
|
try {
|
|
@@ -2078,7 +2122,7 @@ ${errorMessages}`, options.json ?? false);
|
|
|
2078
2122
|
output(jsonResults, true);
|
|
2079
2123
|
} else {
|
|
2080
2124
|
if (successes.length > 0) {
|
|
2081
|
-
const pages = successes.filter((r) => r.result !== null).map((r) => formatDocPage(r.result, r.ref, options.verbose));
|
|
2125
|
+
const pages = successes.filter((r) => r.result !== null).map((r) => formatDocPage(r.result, r.ref, options.verbose, resolvedPreview));
|
|
2082
2126
|
console.log(pages.join(`
|
|
2083
2127
|
|
|
2084
2128
|
---
|
|
@@ -2116,7 +2160,7 @@ Examples:
|
|
|
2116
2160
|
# Multiple pages
|
|
2117
2161
|
pkgseer docs get 24293-shared-plugins-during-build-2 npm/express/4.18.2/readme`;
|
|
2118
2162
|
function registerDocsGetCommand(program) {
|
|
2119
|
-
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.)").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) => {
|
|
2120
2164
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
2121
2165
|
const deps = await createContainer();
|
|
2122
2166
|
await docsGetAction(refs, options, deps);
|
|
@@ -2190,6 +2234,22 @@ function registerDocsListCommand(program) {
|
|
|
2190
2234
|
});
|
|
2191
2235
|
}
|
|
2192
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
|
+
}
|
|
2193
2253
|
function buildDocRef(entry, searchResult) {
|
|
2194
2254
|
if (!entry?.slug)
|
|
2195
2255
|
return "";
|
|
@@ -2299,7 +2359,7 @@ function formatEntry(entry, searchResult, useColors) {
|
|
|
2299
2359
|
function formatSearchResults(results, useColors) {
|
|
2300
2360
|
const entries = results.entries ?? [];
|
|
2301
2361
|
if (entries.length === 0) {
|
|
2302
|
-
return "No matching documentation found.";
|
|
2362
|
+
return "No matching documentation found. Try broader terms or fewer constraints.";
|
|
2303
2363
|
}
|
|
2304
2364
|
const formatted = entries.filter((e) => e != null).map((entry) => formatEntry(entry, results, useColors));
|
|
2305
2365
|
return formatted.join(`
|
|
@@ -2343,25 +2403,24 @@ function slimSearchResults(results) {
|
|
|
2343
2403
|
}
|
|
2344
2404
|
async function docsSearchAction(queryArg, options, deps) {
|
|
2345
2405
|
const { pkgseerService, config } = deps;
|
|
2346
|
-
const
|
|
2347
|
-
const
|
|
2348
|
-
if (!
|
|
2349
|
-
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);
|
|
2350
2410
|
return;
|
|
2351
2411
|
}
|
|
2352
2412
|
const contextBefore = options.context ? Number.parseInt(options.context, 10) : options.before ? Number.parseInt(options.before, 10) : 2;
|
|
2353
2413
|
const contextAfter = options.context ? Number.parseInt(options.context, 10) : options.after ? Number.parseInt(options.after, 10) : 2;
|
|
2354
2414
|
const maxMatches = options.maxMatches ? Number.parseInt(options.maxMatches, 10) : 5;
|
|
2355
2415
|
const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
|
|
2356
|
-
const matchMode = options.mode?.toUpperCase() || undefined;
|
|
2416
|
+
const matchMode = options.match_mode?.toUpperCase() || options.mode?.toUpperCase() || undefined;
|
|
2357
2417
|
const useColors = shouldUseColors(options.noColor);
|
|
2358
2418
|
const project = options.project ?? config.project;
|
|
2359
2419
|
const packageName = options.package;
|
|
2360
2420
|
if (packageName) {
|
|
2361
2421
|
const registry = toGraphQLRegistry(options.registry ?? "npm");
|
|
2362
2422
|
const result = await pkgseerService.cliDocsSearch(registry, packageName, {
|
|
2363
|
-
|
|
2364
|
-
keywords,
|
|
2423
|
+
keywords: terms,
|
|
2365
2424
|
matchMode,
|
|
2366
2425
|
limit,
|
|
2367
2426
|
version: options.pkgVersion,
|
|
@@ -2379,8 +2438,7 @@ async function docsSearchAction(queryArg, options, deps) {
|
|
|
2379
2438
|
}
|
|
2380
2439
|
if (project) {
|
|
2381
2440
|
const result = await pkgseerService.cliProjectDocsSearch(project, {
|
|
2382
|
-
|
|
2383
|
-
keywords,
|
|
2441
|
+
keywords: terms,
|
|
2384
2442
|
matchMode,
|
|
2385
2443
|
limit,
|
|
2386
2444
|
contextLinesBefore: contextBefore,
|
|
@@ -2401,12 +2459,15 @@ async function docsSearchAction(queryArg, options, deps) {
|
|
|
2401
2459
|
` + " - Use --package <name> to search a specific package", options.json ?? false);
|
|
2402
2460
|
}
|
|
2403
2461
|
function outputResults(results, options, useColors) {
|
|
2404
|
-
|
|
2462
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
2463
|
+
if (format === "json") {
|
|
2405
2464
|
output(slimSearchResults(results), true);
|
|
2406
2465
|
} else if (options.refsOnly) {
|
|
2407
2466
|
console.log(formatRefsOnly(results));
|
|
2408
2467
|
} else if (options.count) {
|
|
2409
2468
|
console.log(formatCount(results));
|
|
2469
|
+
} else if (format === "summary") {
|
|
2470
|
+
console.log(summarizeSearchResults(results));
|
|
2410
2471
|
} else {
|
|
2411
2472
|
console.log(formatSearchResults(results, useColors));
|
|
2412
2473
|
}
|
|
@@ -2426,11 +2487,11 @@ Examples:
|
|
|
2426
2487
|
pkgseer docs search "error handling" --package express
|
|
2427
2488
|
pkgseer docs search log --package express -C 3
|
|
2428
2489
|
|
|
2429
|
-
# Multiple
|
|
2430
|
-
pkgseer docs search -
|
|
2490
|
+
# Multiple terms (OR by default)
|
|
2491
|
+
pkgseer docs search -t "middleware,routing" --package express
|
|
2431
2492
|
|
|
2432
2493
|
# Strict matching (AND mode)
|
|
2433
|
-
pkgseer docs search -
|
|
2494
|
+
pkgseer docs search -t "error,middleware" --match-mode all --package express
|
|
2434
2495
|
|
|
2435
2496
|
# Output for piping
|
|
2436
2497
|
pkgseer docs search log --package express --refs-only | \\
|
|
@@ -2439,14 +2500,14 @@ Examples:
|
|
|
2439
2500
|
# Count matches
|
|
2440
2501
|
pkgseer docs search error --package express --count`;
|
|
2441
2502
|
function addSearchOptions(cmd) {
|
|
2442
|
-
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");
|
|
2443
2504
|
}
|
|
2444
2505
|
function registerDocsSearchCommand(program) {
|
|
2445
|
-
const cmd = program.command("search [
|
|
2446
|
-
addSearchOptions(cmd).action(async (
|
|
2506
|
+
const cmd = program.command("search [terms...]").summary("Search documentation").description(SEARCH_DESCRIPTION);
|
|
2507
|
+
addSearchOptions(cmd).action(async (terms, options) => {
|
|
2447
2508
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
2448
2509
|
const deps = await createContainer();
|
|
2449
|
-
await docsSearchAction(
|
|
2510
|
+
await docsSearchAction(terms, options, deps);
|
|
2450
2511
|
});
|
|
2451
2512
|
});
|
|
2452
2513
|
}
|
|
@@ -3811,9 +3872,28 @@ var schemas = {
|
|
|
3811
3872
|
packageName: z2.string().max(255).describe("Name of the package"),
|
|
3812
3873
|
version: z2.string().max(100).optional().describe("Specific version (defaults to latest)")
|
|
3813
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
|
+
}
|
|
3814
3883
|
function handleGraphQLErrors(errors) {
|
|
3815
3884
|
if (errors && errors.length > 0) {
|
|
3816
|
-
|
|
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}`);
|
|
3817
3897
|
}
|
|
3818
3898
|
return null;
|
|
3819
3899
|
}
|
|
@@ -3822,7 +3902,7 @@ async function withErrorHandling(operation, fn) {
|
|
|
3822
3902
|
return await fn();
|
|
3823
3903
|
} catch (error2) {
|
|
3824
3904
|
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
3825
|
-
return errorResult(
|
|
3905
|
+
return errorResult(buildHintedMessage(operation, message));
|
|
3826
3906
|
}
|
|
3827
3907
|
}
|
|
3828
3908
|
function notFoundError(packageName, registry) {
|
|
@@ -3841,7 +3921,7 @@ var argsSchema = {
|
|
|
3841
3921
|
function createComparePackagesTool(pkgseerService) {
|
|
3842
3922
|
return {
|
|
3843
3923
|
name: "compare_packages",
|
|
3844
|
-
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"}].',
|
|
3845
3925
|
schema: argsSchema,
|
|
3846
3926
|
handler: async ({ packages }, _extra) => {
|
|
3847
3927
|
return withErrorHandling("compare packages", async () => {
|
|
@@ -3870,7 +3950,7 @@ var argsSchema2 = {
|
|
|
3870
3950
|
function createFetchPackageDocTool(pkgseerService) {
|
|
3871
3951
|
return {
|
|
3872
3952
|
name: "fetch_package_doc",
|
|
3873
|
-
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).",
|
|
3874
3954
|
schema: argsSchema2,
|
|
3875
3955
|
handler: async ({ page_id }, _extra) => {
|
|
3876
3956
|
return withErrorHandling("fetch documentation page", async () => {
|
|
@@ -3920,10 +4000,82 @@ var argsSchema4 = {
|
|
|
3920
4000
|
include_transitive: z5.boolean().optional().describe("Whether to include transitive dependency DAG"),
|
|
3921
4001
|
max_depth: z5.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
|
|
3922
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
|
+
}
|
|
3923
4075
|
function createPackageDependenciesTool(pkgseerService) {
|
|
3924
4076
|
return {
|
|
3925
4077
|
name: "package_dependencies",
|
|
3926
|
-
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.",
|
|
3927
4079
|
schema: argsSchema4,
|
|
3928
4080
|
handler: async ({ registry, package_name, version: version2, include_transitive, max_depth }, _extra) => {
|
|
3929
4081
|
return withErrorHandling("fetch package dependencies", async () => {
|
|
@@ -3934,7 +4086,29 @@ function createPackageDependenciesTool(pkgseerService) {
|
|
|
3934
4086
|
if (!result.data.packageDependencies) {
|
|
3935
4087
|
return notFoundError(package_name, registry);
|
|
3936
4088
|
}
|
|
3937
|
-
|
|
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));
|
|
3938
4112
|
});
|
|
3939
4113
|
}
|
|
3940
4114
|
};
|
|
@@ -4018,8 +4192,8 @@ import { z as z6 } from "zod";
|
|
|
4018
4192
|
var argsSchema8 = {
|
|
4019
4193
|
registry: schemas.registry,
|
|
4020
4194
|
package_name: schemas.packageName.describe("Name of the package to search documentation for"),
|
|
4021
|
-
|
|
4022
|
-
|
|
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).'),
|
|
4023
4197
|
include_snippets: z6.boolean().optional().describe("Include content excerpts around matches"),
|
|
4024
4198
|
limit: z6.number().int().min(1).max(100).optional().describe("Maximum number of results to return"),
|
|
4025
4199
|
version: schemas.version
|
|
@@ -4027,25 +4201,28 @@ var argsSchema8 = {
|
|
|
4027
4201
|
function createSearchPackageDocsTool(pkgseerService) {
|
|
4028
4202
|
return {
|
|
4029
4203
|
name: "search_package_docs",
|
|
4030
|
-
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.",
|
|
4031
4205
|
schema: argsSchema8,
|
|
4032
4206
|
handler: async ({
|
|
4033
4207
|
registry,
|
|
4034
4208
|
package_name,
|
|
4035
|
-
|
|
4036
|
-
|
|
4209
|
+
terms,
|
|
4210
|
+
match_mode,
|
|
4037
4211
|
include_snippets,
|
|
4038
4212
|
limit,
|
|
4039
4213
|
version: version2
|
|
4040
4214
|
}, _extra) => {
|
|
4041
4215
|
return withErrorHandling("search package documentation", async () => {
|
|
4042
|
-
|
|
4043
|
-
|
|
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.");
|
|
4044
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;
|
|
4045
4222
|
const result = await pkgseerService.searchPackageDocs(toGraphQLRegistry2(registry), package_name, {
|
|
4046
|
-
keywords,
|
|
4047
|
-
|
|
4048
|
-
includeSnippets
|
|
4223
|
+
keywords: normalizedTerms,
|
|
4224
|
+
matchMode,
|
|
4225
|
+
includeSnippets,
|
|
4049
4226
|
limit,
|
|
4050
4227
|
version: version2
|
|
4051
4228
|
});
|
|
@@ -4055,6 +4232,9 @@ function createSearchPackageDocsTool(pkgseerService) {
|
|
|
4055
4232
|
if (!result.data.searchPackageDocs) {
|
|
4056
4233
|
return errorResult(`No documentation found for ${package_name} in ${registry}`);
|
|
4057
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
|
+
}
|
|
4058
4238
|
return textResult(JSON.stringify(result.data.searchPackageDocs, null, 2));
|
|
4059
4239
|
});
|
|
4060
4240
|
}
|
|
@@ -4064,8 +4244,8 @@ function createSearchPackageDocsTool(pkgseerService) {
|
|
|
4064
4244
|
import { z as z7 } from "zod";
|
|
4065
4245
|
var argsSchema9 = {
|
|
4066
4246
|
project: z7.string().optional().describe("Project name to search. Optional if configured in pkgseer.yml; only needed to search a different project."),
|
|
4067
|
-
|
|
4068
|
-
|
|
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).'),
|
|
4069
4249
|
include_snippets: z7.boolean().optional().describe("Include content excerpts around matches"),
|
|
4070
4250
|
limit: z7.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
|
|
4071
4251
|
};
|
|
@@ -4073,21 +4253,24 @@ function createSearchProjectDocsTool(deps) {
|
|
|
4073
4253
|
const { pkgseerService, config } = deps;
|
|
4074
4254
|
return {
|
|
4075
4255
|
name: "search_project_docs",
|
|
4076
|
-
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.",
|
|
4077
4257
|
schema: argsSchema9,
|
|
4078
|
-
handler: async ({ project,
|
|
4258
|
+
handler: async ({ project, terms, match_mode, include_snippets, limit }, _extra) => {
|
|
4079
4259
|
return withErrorHandling("search project documentation", async () => {
|
|
4080
4260
|
const resolvedProject = project ?? config.project;
|
|
4081
4261
|
if (!resolvedProject) {
|
|
4082
4262
|
return errorResult("No project provided and none configured in pkgseer.yml. " + "Either pass project parameter or add project to your config.");
|
|
4083
4263
|
}
|
|
4084
|
-
|
|
4085
|
-
|
|
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.");
|
|
4086
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;
|
|
4087
4270
|
const result = await pkgseerService.searchProjectDocs(resolvedProject, {
|
|
4088
|
-
keywords,
|
|
4089
|
-
|
|
4090
|
-
includeSnippets
|
|
4271
|
+
keywords: normalizedTerms,
|
|
4272
|
+
matchMode,
|
|
4273
|
+
includeSnippets,
|
|
4091
4274
|
limit
|
|
4092
4275
|
});
|
|
4093
4276
|
const graphqlError = handleGraphQLErrors(result.errors);
|
|
@@ -4096,6 +4279,9 @@ function createSearchProjectDocsTool(deps) {
|
|
|
4096
4279
|
if (!result.data.searchProjectDocs) {
|
|
4097
4280
|
return errorResult(`Project not found: ${resolvedProject}`);
|
|
4098
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
|
+
}
|
|
4099
4285
|
return textResult(JSON.stringify(result.data.searchProjectDocs, null, 2));
|
|
4100
4286
|
});
|
|
4101
4287
|
}
|
|
@@ -4313,7 +4499,7 @@ function registerPkgCompareCommand(program) {
|
|
|
4313
4499
|
});
|
|
4314
4500
|
}
|
|
4315
4501
|
// src/commands/pkg/deps.ts
|
|
4316
|
-
function formatPackageDependencies(data) {
|
|
4502
|
+
function formatPackageDependencies(data, transitiveRequested) {
|
|
4317
4503
|
const lines = [];
|
|
4318
4504
|
const pkg = data.package;
|
|
4319
4505
|
const deps = data.dependencies;
|
|
@@ -4341,32 +4527,52 @@ function formatPackageDependencies(data) {
|
|
|
4341
4527
|
} else {
|
|
4342
4528
|
lines.push("No direct dependencies.");
|
|
4343
4529
|
}
|
|
4530
|
+
if (!transitiveRequested) {
|
|
4531
|
+
lines.push("");
|
|
4532
|
+
lines.push("Tip: use --transitive for full dependency graph traversal.");
|
|
4533
|
+
}
|
|
4344
4534
|
return lines.join(`
|
|
4345
4535
|
`);
|
|
4346
4536
|
}
|
|
4347
4537
|
async function pkgDepsAction(packageName, options, deps) {
|
|
4348
4538
|
const { pkgseerService } = deps;
|
|
4349
4539
|
const registry = toGraphQLRegistry(options.registry);
|
|
4350
|
-
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);
|
|
4351
4542
|
handleErrors(result.errors, options.json ?? false);
|
|
4352
4543
|
if (!result.data.packageDependencies) {
|
|
4353
4544
|
outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
|
|
4354
4545
|
return;
|
|
4355
4546
|
}
|
|
4356
|
-
|
|
4547
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
4548
|
+
if (format === "json") {
|
|
4357
4549
|
const data = result.data.packageDependencies;
|
|
4358
|
-
|
|
4550
|
+
output({
|
|
4359
4551
|
package: `${data.package?.name}@${data.package?.version}`,
|
|
4360
4552
|
directCount: data.dependencies?.summary?.directCount ?? 0,
|
|
4361
|
-
|
|
4553
|
+
uniquePackagesCount: data.dependencies?.summary?.uniquePackagesCount ?? 0,
|
|
4554
|
+
transitiveIncluded: options.transitive ?? false,
|
|
4555
|
+
dependencies: data.dependencies?.direct?.filter((d) => Boolean(d)).map((d) => ({
|
|
4362
4556
|
name: d.name,
|
|
4363
4557
|
version: d.versionConstraint,
|
|
4364
4558
|
type: d.type
|
|
4365
4559
|
}))
|
|
4366
|
-
};
|
|
4367
|
-
|
|
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
|
+
`));
|
|
4368
4574
|
} else {
|
|
4369
|
-
console.log(formatPackageDependencies(result.data.packageDependencies));
|
|
4575
|
+
console.log(formatPackageDependencies(result.data.packageDependencies, transitiveRequested));
|
|
4370
4576
|
}
|
|
4371
4577
|
}
|
|
4372
4578
|
var DEPS_DESCRIPTION = `Get package dependencies.
|
|
@@ -4379,7 +4585,7 @@ Examples:
|
|
|
4379
4585
|
pkgseer pkg deps lodash --transitive
|
|
4380
4586
|
pkgseer pkg deps requests --registry pypi --json`;
|
|
4381
4587
|
function registerPkgDepsCommand(program) {
|
|
4382
|
-
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) => {
|
|
4383
4589
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
4384
4590
|
const deps = await createContainer();
|
|
4385
4591
|
await pkgDepsAction(packageName, options, deps);
|
|
@@ -4480,18 +4686,15 @@ function formatPackageQuality(data) {
|
|
|
4480
4686
|
if (!quality) {
|
|
4481
4687
|
return "No quality data available.";
|
|
4482
4688
|
}
|
|
4483
|
-
|
|
4484
|
-
lines.push("");
|
|
4485
|
-
if (
|
|
4486
|
-
lines.push("
|
|
4487
|
-
for (const category of
|
|
4488
|
-
|
|
4489
|
-
const name = (category.category || "Unknown").padEnd(20);
|
|
4490
|
-
lines.push(` ${name} ${formatScore(category.score)}`);
|
|
4491
|
-
}
|
|
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)}`);
|
|
4492
4695
|
}
|
|
4493
|
-
lines.push("");
|
|
4494
4696
|
}
|
|
4697
|
+
lines.push("Tip: use --json for full category breakdown.");
|
|
4495
4698
|
return lines.join(`
|
|
4496
4699
|
`);
|
|
4497
4700
|
}
|
|
@@ -4504,7 +4707,8 @@ async function pkgQualityAction(packageName, options, deps) {
|
|
|
4504
4707
|
outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
|
|
4505
4708
|
return;
|
|
4506
4709
|
}
|
|
4507
|
-
|
|
4710
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
4711
|
+
if (format === "json") {
|
|
4508
4712
|
const quality = result.data.packageQuality.quality;
|
|
4509
4713
|
const slim = {
|
|
4510
4714
|
package: `${result.data.packageQuality.package?.name}@${result.data.packageQuality.package?.version}`,
|
|
@@ -4533,7 +4737,7 @@ Examples:
|
|
|
4533
4737
|
pkgseer pkg quality express -v 4.18.0
|
|
4534
4738
|
pkgseer pkg quality requests --registry pypi --json`;
|
|
4535
4739
|
function registerPkgQualityCommand(program) {
|
|
4536
|
-
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) => {
|
|
4537
4741
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
4538
4742
|
const deps = await createContainer();
|
|
4539
4743
|
await pkgQualityAction(packageName, options, deps);
|
package/dist/index.js
CHANGED