@pkgseer/cli 0.1.3 → 0.1.5
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 +298 -103
- package/dist/index.js +1 -1
- package/dist/shared/{chunk-8dn0z2ja.js → chunk-0fnprry7.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-0fnprry7.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
|
}
|
|
@@ -2525,22 +2586,13 @@ function getCodexConfigPaths(fs, scope) {
|
|
|
2525
2586
|
function getClaudeCodeConfigPaths(fs, scope) {
|
|
2526
2587
|
if (scope === "project") {
|
|
2527
2588
|
const cwd = fs.getCwd();
|
|
2528
|
-
const configPath2 = fs.joinPath(cwd, ".
|
|
2529
|
-
const backupPath2 = fs.joinPath(cwd, ".
|
|
2589
|
+
const configPath2 = fs.joinPath(cwd, ".mcp.json");
|
|
2590
|
+
const backupPath2 = fs.joinPath(cwd, ".mcp.json.bak");
|
|
2530
2591
|
return { configPath: configPath2, backupPath: backupPath2 };
|
|
2531
2592
|
}
|
|
2532
|
-
const
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
if (platform === "win32") {
|
|
2536
|
-
const appData = process.env.APPDATA || fs.joinPath(fs.getHomeDir(), "AppData", "Roaming");
|
|
2537
|
-
configPath = fs.joinPath(appData, "Claude Code", "mcp.json");
|
|
2538
|
-
backupPath = fs.joinPath(appData, "Claude Code", "mcp.json.bak");
|
|
2539
|
-
} else {
|
|
2540
|
-
const home = fs.getHomeDir();
|
|
2541
|
-
configPath = fs.joinPath(home, ".claude-code", "mcp.json");
|
|
2542
|
-
backupPath = fs.joinPath(home, ".claude-code", "mcp.json.bak");
|
|
2543
|
-
}
|
|
2593
|
+
const home = fs.getHomeDir();
|
|
2594
|
+
const configPath = fs.joinPath(home, ".claude.json");
|
|
2595
|
+
const backupPath = fs.joinPath(home, ".claude.json.bak");
|
|
2544
2596
|
return { configPath, backupPath };
|
|
2545
2597
|
}
|
|
2546
2598
|
async function parseConfigFile(fs, path) {
|
|
@@ -2624,8 +2676,8 @@ args = ["-y", "@pkgseer/cli", "mcp", "start"]`;
|
|
|
2624
2676
|
};
|
|
2625
2677
|
console.log(JSON.stringify(configExample, null, 2));
|
|
2626
2678
|
}
|
|
2627
|
-
if ((tool === "cursor" || tool === "codex"
|
|
2628
|
-
const dirName = tool === "cursor" ? ".cursor" :
|
|
2679
|
+
if ((tool === "cursor" || tool === "codex") && scope === "project") {
|
|
2680
|
+
const dirName = tool === "cursor" ? ".cursor" : ".codex";
|
|
2629
2681
|
console.log(dim(`
|
|
2630
2682
|
Note: Create the ${dirName} directory if it doesn't exist.`, useColors));
|
|
2631
2683
|
}
|
|
@@ -2682,8 +2734,8 @@ Run ${highlight("pkgseer project init", useColors)} first, then ${highlight("pkg
|
|
|
2682
2734
|
}
|
|
2683
2735
|
let scope;
|
|
2684
2736
|
if (tool === "cursor" || tool === "codex" || tool === "claude-code") {
|
|
2685
|
-
const projectPath = tool === "cursor" ? ".cursor/mcp.json" : tool === "codex" ? ".codex/config.toml" : ".
|
|
2686
|
-
const globalPath = tool === "cursor" ? "~/.cursor/mcp.json" : tool === "codex" ? "~/.codex/config.toml" : "~/.claude
|
|
2737
|
+
const projectPath = tool === "cursor" ? ".cursor/mcp.json" : tool === "codex" ? ".codex/config.toml" : ".mcp.json";
|
|
2738
|
+
const globalPath = tool === "cursor" ? "~/.cursor/mcp.json" : tool === "codex" ? "~/.codex/config.toml" : "~/.claude.json";
|
|
2687
2739
|
if (hasProject) {
|
|
2688
2740
|
scope = await promptService.select("Where should the MCP config be created?", [
|
|
2689
2741
|
{
|
|
@@ -3811,9 +3863,28 @@ var schemas = {
|
|
|
3811
3863
|
packageName: z2.string().max(255).describe("Name of the package"),
|
|
3812
3864
|
version: z2.string().max(100).optional().describe("Specific version (defaults to latest)")
|
|
3813
3865
|
};
|
|
3866
|
+
function buildHintedMessage(operation, message) {
|
|
3867
|
+
const lower = message.toLowerCase();
|
|
3868
|
+
const isTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
|
|
3869
|
+
if (isTimeout) {
|
|
3870
|
+
return `Failed to ${operation}: ${message}. ` + "Hint: try lowering the limit or narrowing the scope, then retry.";
|
|
3871
|
+
}
|
|
3872
|
+
return `Failed to ${operation}: ${message}`;
|
|
3873
|
+
}
|
|
3814
3874
|
function handleGraphQLErrors(errors) {
|
|
3815
3875
|
if (errors && errors.length > 0) {
|
|
3816
|
-
|
|
3876
|
+
const messages = errors.map((e) => e.message).filter(Boolean);
|
|
3877
|
+
const combined = messages.join(", ");
|
|
3878
|
+
const lower = combined.toLowerCase();
|
|
3879
|
+
const hasNotFound = lower.includes("not found") || lower.includes("does not exist");
|
|
3880
|
+
const hasTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
|
|
3881
|
+
if (hasNotFound) {
|
|
3882
|
+
return errorResult(`Error: ${combined}. Hint: verify the name/registry and that the resource exists.`);
|
|
3883
|
+
}
|
|
3884
|
+
if (hasTimeout) {
|
|
3885
|
+
return errorResult(`Error: ${combined}. Hint: try lowering the limit or narrowing the scope, then retry.`);
|
|
3886
|
+
}
|
|
3887
|
+
return errorResult(`Error: ${combined}`);
|
|
3817
3888
|
}
|
|
3818
3889
|
return null;
|
|
3819
3890
|
}
|
|
@@ -3822,7 +3893,7 @@ async function withErrorHandling(operation, fn) {
|
|
|
3822
3893
|
return await fn();
|
|
3823
3894
|
} catch (error2) {
|
|
3824
3895
|
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
3825
|
-
return errorResult(
|
|
3896
|
+
return errorResult(buildHintedMessage(operation, message));
|
|
3826
3897
|
}
|
|
3827
3898
|
}
|
|
3828
3899
|
function notFoundError(packageName, registry) {
|
|
@@ -3841,7 +3912,7 @@ var argsSchema = {
|
|
|
3841
3912
|
function createComparePackagesTool(pkgseerService) {
|
|
3842
3913
|
return {
|
|
3843
3914
|
name: "compare_packages",
|
|
3844
|
-
description:
|
|
3915
|
+
description: 'Compares 2-10 packages across metadata, quality, and security. Example: [{"registry":"npm","name":"react","version":"18.2.0"},{"registry":"pypi","name":"requests"}].',
|
|
3845
3916
|
schema: argsSchema,
|
|
3846
3917
|
handler: async ({ packages }, _extra) => {
|
|
3847
3918
|
return withErrorHandling("compare packages", async () => {
|
|
@@ -3870,7 +3941,7 @@ var argsSchema2 = {
|
|
|
3870
3941
|
function createFetchPackageDocTool(pkgseerService) {
|
|
3871
3942
|
return {
|
|
3872
3943
|
name: "fetch_package_doc",
|
|
3873
|
-
description: "Fetches the full content of a
|
|
3944
|
+
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
3945
|
schema: argsSchema2,
|
|
3875
3946
|
handler: async ({ page_id }, _extra) => {
|
|
3876
3947
|
return withErrorHandling("fetch documentation page", async () => {
|
|
@@ -3920,10 +3991,82 @@ var argsSchema4 = {
|
|
|
3920
3991
|
include_transitive: z5.boolean().optional().describe("Whether to include transitive dependency DAG"),
|
|
3921
3992
|
max_depth: z5.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
|
|
3922
3993
|
};
|
|
3994
|
+
function decodeDag(rawDag) {
|
|
3995
|
+
if (!rawDag || typeof rawDag !== "object")
|
|
3996
|
+
return;
|
|
3997
|
+
const dag = rawDag;
|
|
3998
|
+
const nodesSource = dag.n ?? dag.nodes;
|
|
3999
|
+
const nodesRaw = nodesSource && typeof nodesSource === "object" && !Array.isArray(nodesSource) ? nodesSource : undefined;
|
|
4000
|
+
const edgesSource = dag.e ?? dag.edges;
|
|
4001
|
+
const edgesRaw = Array.isArray(edgesSource) ? edgesSource : undefined;
|
|
4002
|
+
const nodes = nodesRaw ? Object.entries(nodesRaw).map(([id, value]) => {
|
|
4003
|
+
const {
|
|
4004
|
+
n: nameField,
|
|
4005
|
+
v: versionField,
|
|
4006
|
+
l: labelField,
|
|
4007
|
+
...rest
|
|
4008
|
+
} = value ?? {};
|
|
4009
|
+
return {
|
|
4010
|
+
id,
|
|
4011
|
+
name: nameField ?? rest.name,
|
|
4012
|
+
version: versionField ?? rest.version,
|
|
4013
|
+
label: labelField ?? rest.label,
|
|
4014
|
+
...rest
|
|
4015
|
+
};
|
|
4016
|
+
}) : [];
|
|
4017
|
+
const edges = edgesRaw?.map((edge) => {
|
|
4018
|
+
if (Array.isArray(edge) && edge.length >= 2) {
|
|
4019
|
+
return { from: String(edge[0]), to: String(edge[1]) };
|
|
4020
|
+
}
|
|
4021
|
+
const edgeObj = edge ?? {};
|
|
4022
|
+
return {
|
|
4023
|
+
from: String(edgeObj.f ?? edgeObj.from ?? edgeObj.source ?? edgeObj.s ?? ""),
|
|
4024
|
+
to: String(edgeObj.t ?? edgeObj.to ?? edgeObj.target ?? edgeObj.d ?? "")
|
|
4025
|
+
};
|
|
4026
|
+
}) ?? [];
|
|
4027
|
+
return {
|
|
4028
|
+
version: dag.v ?? dag.version,
|
|
4029
|
+
nodes,
|
|
4030
|
+
edges
|
|
4031
|
+
};
|
|
4032
|
+
}
|
|
4033
|
+
function buildEdgeDepths(decodedDag, rootId) {
|
|
4034
|
+
if (!decodedDag)
|
|
4035
|
+
return;
|
|
4036
|
+
const root = rootId ?? decodedDag.nodes[0]?.id;
|
|
4037
|
+
if (!root)
|
|
4038
|
+
return;
|
|
4039
|
+
const depthMap = new Map([[root, 0]]);
|
|
4040
|
+
const adjacency = new Map;
|
|
4041
|
+
for (const edge of decodedDag.edges) {
|
|
4042
|
+
if (!edge.from || !edge.to)
|
|
4043
|
+
continue;
|
|
4044
|
+
const list = adjacency.get(edge.from) ?? [];
|
|
4045
|
+
list.push(edge.to);
|
|
4046
|
+
adjacency.set(edge.from, list);
|
|
4047
|
+
}
|
|
4048
|
+
const queue = [root];
|
|
4049
|
+
while (queue.length > 0) {
|
|
4050
|
+
const current = queue.shift();
|
|
4051
|
+
const currentDepth = depthMap.get(current) ?? 0;
|
|
4052
|
+
for (const next of adjacency.get(current) ?? []) {
|
|
4053
|
+
if (!depthMap.has(next)) {
|
|
4054
|
+
depthMap.set(next, currentDepth + 1);
|
|
4055
|
+
queue.push(next);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
return decodedDag.edges.map((edge) => ({
|
|
4060
|
+
from: edge.from,
|
|
4061
|
+
to: edge.to,
|
|
4062
|
+
depthFrom: depthMap.get(edge.from),
|
|
4063
|
+
depthTo: depthMap.get(edge.to)
|
|
4064
|
+
}));
|
|
4065
|
+
}
|
|
3923
4066
|
function createPackageDependenciesTool(pkgseerService) {
|
|
3924
4067
|
return {
|
|
3925
4068
|
name: "package_dependencies",
|
|
3926
|
-
description: "Retrieves direct and transitive dependencies for a package version",
|
|
4069
|
+
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
4070
|
schema: argsSchema4,
|
|
3928
4071
|
handler: async ({ registry, package_name, version: version2, include_transitive, max_depth }, _extra) => {
|
|
3929
4072
|
return withErrorHandling("fetch package dependencies", async () => {
|
|
@@ -3934,7 +4077,29 @@ function createPackageDependenciesTool(pkgseerService) {
|
|
|
3934
4077
|
if (!result.data.packageDependencies) {
|
|
3935
4078
|
return notFoundError(package_name, registry);
|
|
3936
4079
|
}
|
|
3937
|
-
|
|
4080
|
+
const deps = result.data.packageDependencies.dependencies;
|
|
4081
|
+
const decodedDag = decodeDag(deps?.transitive?.dag);
|
|
4082
|
+
const transitiveDag = deps?.transitive?.dag;
|
|
4083
|
+
const edgesWithDepth = buildEdgeDepths(decodedDag, typeof transitiveDag?.root === "string" ? transitiveDag.root : undefined);
|
|
4084
|
+
const output2 = {
|
|
4085
|
+
summary: deps?.summary,
|
|
4086
|
+
package: result.data.packageDependencies.package,
|
|
4087
|
+
direct: deps?.direct,
|
|
4088
|
+
transitive: deps?.transitive ? {
|
|
4089
|
+
totalEdges: deps.transitive.totalEdges,
|
|
4090
|
+
uniquePackagesCount: deps.transitive.uniquePackagesCount,
|
|
4091
|
+
uniqueDependencies: deps.transitive.uniqueDependencies,
|
|
4092
|
+
conflicts: deps.transitive.conflicts,
|
|
4093
|
+
circularDependencies: deps.transitive.circularDependencies,
|
|
4094
|
+
dag: {
|
|
4095
|
+
decoded: decodedDag,
|
|
4096
|
+
edgesWithDepth,
|
|
4097
|
+
raw: deps.transitive.dag
|
|
4098
|
+
}
|
|
4099
|
+
} : undefined,
|
|
4100
|
+
raw: result.data.packageDependencies
|
|
4101
|
+
};
|
|
4102
|
+
return textResult(JSON.stringify(output2, null, 2));
|
|
3938
4103
|
});
|
|
3939
4104
|
}
|
|
3940
4105
|
};
|
|
@@ -4018,8 +4183,8 @@ import { z as z6 } from "zod";
|
|
|
4018
4183
|
var argsSchema8 = {
|
|
4019
4184
|
registry: schemas.registry,
|
|
4020
4185
|
package_name: schemas.packageName.describe("Name of the package to search documentation for"),
|
|
4021
|
-
|
|
4022
|
-
|
|
4186
|
+
terms: z6.array(z6.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
|
|
4187
|
+
match_mode: z6.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
|
|
4023
4188
|
include_snippets: z6.boolean().optional().describe("Include content excerpts around matches"),
|
|
4024
4189
|
limit: z6.number().int().min(1).max(100).optional().describe("Maximum number of results to return"),
|
|
4025
4190
|
version: schemas.version
|
|
@@ -4027,25 +4192,28 @@ var argsSchema8 = {
|
|
|
4027
4192
|
function createSearchPackageDocsTool(pkgseerService) {
|
|
4028
4193
|
return {
|
|
4029
4194
|
name: "search_package_docs",
|
|
4030
|
-
description: "Searches package documentation
|
|
4195
|
+
description: "Searches package documentation using search terms and match modes (any/all). Returns ranked results with match context. Defaults to include_snippets=true.",
|
|
4031
4196
|
schema: argsSchema8,
|
|
4032
4197
|
handler: async ({
|
|
4033
4198
|
registry,
|
|
4034
4199
|
package_name,
|
|
4035
|
-
|
|
4036
|
-
|
|
4200
|
+
terms,
|
|
4201
|
+
match_mode,
|
|
4037
4202
|
include_snippets,
|
|
4038
4203
|
limit,
|
|
4039
4204
|
version: version2
|
|
4040
4205
|
}, _extra) => {
|
|
4041
4206
|
return withErrorHandling("search package documentation", async () => {
|
|
4042
|
-
|
|
4043
|
-
|
|
4207
|
+
const normalizedTerms = terms?.map((term) => term.trim()).filter((term) => term.length > 0) ?? [];
|
|
4208
|
+
if (normalizedTerms.length === 0) {
|
|
4209
|
+
return errorResult("Search terms are required to run documentation search.");
|
|
4044
4210
|
}
|
|
4211
|
+
const matchMode = match_mode === "all" || match_mode === "and" ? "AND" : match_mode === "any" || match_mode === "or" ? "OR" : undefined;
|
|
4212
|
+
const includeSnippets = include_snippets ?? true;
|
|
4045
4213
|
const result = await pkgseerService.searchPackageDocs(toGraphQLRegistry2(registry), package_name, {
|
|
4046
|
-
keywords,
|
|
4047
|
-
|
|
4048
|
-
includeSnippets
|
|
4214
|
+
keywords: normalizedTerms,
|
|
4215
|
+
matchMode,
|
|
4216
|
+
includeSnippets,
|
|
4049
4217
|
limit,
|
|
4050
4218
|
version: version2
|
|
4051
4219
|
});
|
|
@@ -4055,6 +4223,9 @@ function createSearchPackageDocsTool(pkgseerService) {
|
|
|
4055
4223
|
if (!result.data.searchPackageDocs) {
|
|
4056
4224
|
return errorResult(`No documentation found for ${package_name} in ${registry}`);
|
|
4057
4225
|
}
|
|
4226
|
+
if ((result.data.searchPackageDocs.entries ?? []).length === 0 && normalizedTerms.length > 0) {
|
|
4227
|
+
return errorResult(`No documentation matched: ${normalizedTerms.join(", ")}. ` + "Try fewer or broader terms, or reduce match constraints.");
|
|
4228
|
+
}
|
|
4058
4229
|
return textResult(JSON.stringify(result.data.searchPackageDocs, null, 2));
|
|
4059
4230
|
});
|
|
4060
4231
|
}
|
|
@@ -4064,8 +4235,8 @@ function createSearchPackageDocsTool(pkgseerService) {
|
|
|
4064
4235
|
import { z as z7 } from "zod";
|
|
4065
4236
|
var argsSchema9 = {
|
|
4066
4237
|
project: z7.string().optional().describe("Project name to search. Optional if configured in pkgseer.yml; only needed to search a different project."),
|
|
4067
|
-
|
|
4068
|
-
|
|
4238
|
+
terms: z7.array(z7.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
|
|
4239
|
+
match_mode: z7.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
|
|
4069
4240
|
include_snippets: z7.boolean().optional().describe("Include content excerpts around matches"),
|
|
4070
4241
|
limit: z7.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
|
|
4071
4242
|
};
|
|
@@ -4073,21 +4244,24 @@ function createSearchProjectDocsTool(deps) {
|
|
|
4073
4244
|
const { pkgseerService, config } = deps;
|
|
4074
4245
|
return {
|
|
4075
4246
|
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.",
|
|
4247
|
+
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
4248
|
schema: argsSchema9,
|
|
4078
|
-
handler: async ({ project,
|
|
4249
|
+
handler: async ({ project, terms, match_mode, include_snippets, limit }, _extra) => {
|
|
4079
4250
|
return withErrorHandling("search project documentation", async () => {
|
|
4080
4251
|
const resolvedProject = project ?? config.project;
|
|
4081
4252
|
if (!resolvedProject) {
|
|
4082
4253
|
return errorResult("No project provided and none configured in pkgseer.yml. " + "Either pass project parameter or add project to your config.");
|
|
4083
4254
|
}
|
|
4084
|
-
|
|
4085
|
-
|
|
4255
|
+
const normalizedTerms = terms?.map((term) => term.trim()).filter((term) => term.length > 0) ?? [];
|
|
4256
|
+
if (normalizedTerms.length === 0) {
|
|
4257
|
+
return errorResult("Search terms are required to run documentation search.");
|
|
4086
4258
|
}
|
|
4259
|
+
const matchMode = match_mode === "all" || match_mode === "and" ? "AND" : match_mode === "any" || match_mode === "or" ? "OR" : undefined;
|
|
4260
|
+
const includeSnippets = include_snippets ?? true;
|
|
4087
4261
|
const result = await pkgseerService.searchProjectDocs(resolvedProject, {
|
|
4088
|
-
keywords,
|
|
4089
|
-
|
|
4090
|
-
includeSnippets
|
|
4262
|
+
keywords: normalizedTerms,
|
|
4263
|
+
matchMode,
|
|
4264
|
+
includeSnippets,
|
|
4091
4265
|
limit
|
|
4092
4266
|
});
|
|
4093
4267
|
const graphqlError = handleGraphQLErrors(result.errors);
|
|
@@ -4096,6 +4270,9 @@ function createSearchProjectDocsTool(deps) {
|
|
|
4096
4270
|
if (!result.data.searchProjectDocs) {
|
|
4097
4271
|
return errorResult(`Project not found: ${resolvedProject}`);
|
|
4098
4272
|
}
|
|
4273
|
+
if ((result.data.searchProjectDocs.entries ?? []).length === 0 && normalizedTerms.length > 0) {
|
|
4274
|
+
return errorResult(`No documentation matched: ${normalizedTerms.join(", ")}. ` + "Try fewer or broader terms, or reduce match constraints.");
|
|
4275
|
+
}
|
|
4099
4276
|
return textResult(JSON.stringify(result.data.searchProjectDocs, null, 2));
|
|
4100
4277
|
});
|
|
4101
4278
|
}
|
|
@@ -4313,7 +4490,7 @@ function registerPkgCompareCommand(program) {
|
|
|
4313
4490
|
});
|
|
4314
4491
|
}
|
|
4315
4492
|
// src/commands/pkg/deps.ts
|
|
4316
|
-
function formatPackageDependencies(data) {
|
|
4493
|
+
function formatPackageDependencies(data, transitiveRequested) {
|
|
4317
4494
|
const lines = [];
|
|
4318
4495
|
const pkg = data.package;
|
|
4319
4496
|
const deps = data.dependencies;
|
|
@@ -4341,32 +4518,52 @@ function formatPackageDependencies(data) {
|
|
|
4341
4518
|
} else {
|
|
4342
4519
|
lines.push("No direct dependencies.");
|
|
4343
4520
|
}
|
|
4521
|
+
if (!transitiveRequested) {
|
|
4522
|
+
lines.push("");
|
|
4523
|
+
lines.push("Tip: use --transitive for full dependency graph traversal.");
|
|
4524
|
+
}
|
|
4344
4525
|
return lines.join(`
|
|
4345
4526
|
`);
|
|
4346
4527
|
}
|
|
4347
4528
|
async function pkgDepsAction(packageName, options, deps) {
|
|
4348
4529
|
const { pkgseerService } = deps;
|
|
4349
4530
|
const registry = toGraphQLRegistry(options.registry);
|
|
4350
|
-
const
|
|
4531
|
+
const transitiveRequested = options.transitive ?? false;
|
|
4532
|
+
const result = await pkgseerService.cliPackageDeps(registry, packageName, options.pkgVersion, options.transitive, options.maxDepth ? Number.parseInt(options.maxDepth, 10) : undefined);
|
|
4351
4533
|
handleErrors(result.errors, options.json ?? false);
|
|
4352
4534
|
if (!result.data.packageDependencies) {
|
|
4353
4535
|
outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
|
|
4354
4536
|
return;
|
|
4355
4537
|
}
|
|
4356
|
-
|
|
4538
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
4539
|
+
if (format === "json") {
|
|
4357
4540
|
const data = result.data.packageDependencies;
|
|
4358
|
-
|
|
4541
|
+
output({
|
|
4359
4542
|
package: `${data.package?.name}@${data.package?.version}`,
|
|
4360
4543
|
directCount: data.dependencies?.summary?.directCount ?? 0,
|
|
4361
|
-
|
|
4544
|
+
uniquePackagesCount: data.dependencies?.summary?.uniquePackagesCount ?? 0,
|
|
4545
|
+
transitiveIncluded: options.transitive ?? false,
|
|
4546
|
+
dependencies: data.dependencies?.direct?.filter((d) => Boolean(d)).map((d) => ({
|
|
4362
4547
|
name: d.name,
|
|
4363
4548
|
version: d.versionConstraint,
|
|
4364
4549
|
type: d.type
|
|
4365
4550
|
}))
|
|
4366
|
-
};
|
|
4367
|
-
|
|
4551
|
+
}, true);
|
|
4552
|
+
} else if (format === "summary") {
|
|
4553
|
+
const data = result.data.packageDependencies;
|
|
4554
|
+
const deps2 = data.dependencies;
|
|
4555
|
+
const directCount = deps2?.summary?.directCount ?? 0;
|
|
4556
|
+
const uniquePackagesCount = deps2?.summary?.uniquePackagesCount ?? 0;
|
|
4557
|
+
const lines = [
|
|
4558
|
+
`Package: ${data.package?.name ?? ""}@${data.package?.version ?? ""}`,
|
|
4559
|
+
`Direct deps: ${directCount}`,
|
|
4560
|
+
`Unique packages: ${uniquePackagesCount || "N/A"}`,
|
|
4561
|
+
transitiveRequested ? "Transitive: included" : "Transitive: not included (use --transitive)"
|
|
4562
|
+
];
|
|
4563
|
+
console.log(lines.join(`
|
|
4564
|
+
`));
|
|
4368
4565
|
} else {
|
|
4369
|
-
console.log(formatPackageDependencies(result.data.packageDependencies));
|
|
4566
|
+
console.log(formatPackageDependencies(result.data.packageDependencies, transitiveRequested));
|
|
4370
4567
|
}
|
|
4371
4568
|
}
|
|
4372
4569
|
var DEPS_DESCRIPTION = `Get package dependencies.
|
|
@@ -4379,7 +4576,7 @@ Examples:
|
|
|
4379
4576
|
pkgseer pkg deps lodash --transitive
|
|
4380
4577
|
pkgseer pkg deps requests --registry pypi --json`;
|
|
4381
4578
|
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) => {
|
|
4579
|
+
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
4580
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
4384
4581
|
const deps = await createContainer();
|
|
4385
4582
|
await pkgDepsAction(packageName, options, deps);
|
|
@@ -4480,18 +4677,15 @@ function formatPackageQuality(data) {
|
|
|
4480
4677
|
if (!quality) {
|
|
4481
4678
|
return "No quality data available.";
|
|
4482
4679
|
}
|
|
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
|
-
}
|
|
4680
|
+
const topCategories = (quality.categories ?? []).filter((c) => Boolean(c)).sort((a, b) => (b?.score ?? 0) - (a?.score ?? 0)).slice(0, 3);
|
|
4681
|
+
lines.push(`\uD83D\uDCCA Quality: Grade ${quality.grade ?? "N/A"} (${formatScore(quality.overallScore)})`);
|
|
4682
|
+
if (topCategories.length > 0) {
|
|
4683
|
+
lines.push("Top drivers:");
|
|
4684
|
+
for (const category of topCategories) {
|
|
4685
|
+
lines.push(` - ${(category.category ?? "Unknown").toLowerCase()}: ${formatScore(category.score)}`);
|
|
4492
4686
|
}
|
|
4493
|
-
lines.push("");
|
|
4494
4687
|
}
|
|
4688
|
+
lines.push("Tip: use --json for full category breakdown.");
|
|
4495
4689
|
return lines.join(`
|
|
4496
4690
|
`);
|
|
4497
4691
|
}
|
|
@@ -4504,7 +4698,8 @@ async function pkgQualityAction(packageName, options, deps) {
|
|
|
4504
4698
|
outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
|
|
4505
4699
|
return;
|
|
4506
4700
|
}
|
|
4507
|
-
|
|
4701
|
+
const format = options.json ? "json" : options.format ?? "human";
|
|
4702
|
+
if (format === "json") {
|
|
4508
4703
|
const quality = result.data.packageQuality.quality;
|
|
4509
4704
|
const slim = {
|
|
4510
4705
|
package: `${result.data.packageQuality.package?.name}@${result.data.packageQuality.package?.version}`,
|
|
@@ -4533,7 +4728,7 @@ Examples:
|
|
|
4533
4728
|
pkgseer pkg quality express -v 4.18.0
|
|
4534
4729
|
pkgseer pkg quality requests --registry pypi --json`;
|
|
4535
4730
|
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) => {
|
|
4731
|
+
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
4732
|
await withCliErrorHandling(options.json ?? false, async () => {
|
|
4538
4733
|
const deps = await createContainer();
|
|
4539
4734
|
await pkgQualityAction(packageName, options, deps);
|
package/dist/index.js
CHANGED