@pkgseer/cli 0.2.3 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  version
4
- } from "./shared/chunk-zaq9c2d8.js";
4
+ } from "./shared/chunk-48mwa8wt.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -23,6 +23,7 @@ var CliPackageInfoDocument = gql`
23
23
  license
24
24
  homepage
25
25
  repositoryUrl
26
+ downloadsLastMonth
26
27
  }
27
28
  security {
28
29
  vulnerabilityCount
@@ -1782,6 +1783,27 @@ function registerConfigShowCommand(program) {
1782
1783
  }
1783
1784
 
1784
1785
  // src/commands/shared.ts
1786
+ function parsePackageSpec(spec) {
1787
+ let registry = "npm";
1788
+ let rest = spec;
1789
+ if (spec.includes(":")) {
1790
+ const colonIndex = spec.indexOf(":");
1791
+ const potentialRegistry = spec.slice(0, colonIndex).toLowerCase();
1792
+ if (["npm", "pypi", "hex"].includes(potentialRegistry)) {
1793
+ registry = potentialRegistry;
1794
+ rest = spec.slice(colonIndex + 1);
1795
+ }
1796
+ }
1797
+ const atIndex = rest.lastIndexOf("@");
1798
+ if (atIndex > 0) {
1799
+ return {
1800
+ registry,
1801
+ name: rest.slice(0, atIndex),
1802
+ version: rest.slice(atIndex + 1)
1803
+ };
1804
+ }
1805
+ return { registry, name: rest };
1806
+ }
1785
1807
  function toGraphQLRegistry(registry) {
1786
1808
  const map = {
1787
1809
  npm: "NPM",
@@ -2242,7 +2264,7 @@ Examples:
2242
2264
  # Multiple pages
2243
2265
  pkgseer docs get 24293-shared-plugins-during-build-2 npm/express/4.18.2/readme`;
2244
2266
  function registerDocsGetCommand(program) {
2245
- 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) => {
2267
+ 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) => {
2246
2268
  await withCliErrorHandling(options.json ?? false, async () => {
2247
2269
  const deps = await createContainer();
2248
2270
  await docsGetAction(refs, options, deps);
@@ -2272,13 +2294,15 @@ function formatDocsList(docs) {
2272
2294
  return lines.join(`
2273
2295
  `);
2274
2296
  }
2275
- async function docsListAction(packageName, options, deps) {
2297
+ async function docsListAction(packageArg, options, deps) {
2276
2298
  const { pkgseerService } = deps;
2277
- const registry = toGraphQLRegistry(options.registry);
2278
- const result = await pkgseerService.cliDocsList(registry, packageName, options.pkgVersion);
2299
+ const parsed = parsePackageSpec(packageArg);
2300
+ const registry = toGraphQLRegistry(parsed.registry !== "npm" ? parsed.registry : options.registry);
2301
+ const version2 = parsed.version ?? options.pkgVersion;
2302
+ const result = await pkgseerService.cliDocsList(registry, parsed.name, version2);
2279
2303
  handleErrors(result.errors, options.json ?? false);
2280
2304
  if (!result.data.listPackageDocs) {
2281
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
2305
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2282
2306
  return;
2283
2307
  }
2284
2308
  if (options.json) {
@@ -2302,19 +2326,21 @@ Shows all documentation pages with titles, globally unique page IDs,
2302
2326
  word counts, and descriptions. Use the page ID with 'docs get'
2303
2327
  to fetch the full content of a specific page.
2304
2328
 
2329
+ Package format: [registry:]name[@version]
2330
+
2305
2331
  Examples:
2306
2332
  pkgseer docs list lodash
2307
- pkgseer docs list requests --registry pypi
2308
- pkgseer docs list phoenix --registry hex --version 1.7.0 --json`;
2333
+ pkgseer docs list pypi:requests
2334
+ pkgseer docs list hex:phoenix@1.7.0 --json`;
2309
2335
  function registerDocsListCommand(program) {
2310
- program.command("list <package>").summary("List documentation pages").description(LIST_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("--json", "Output as JSON").action(async (packageName, options) => {
2336
+ program.command("list <package>").summary("List documentation pages").description(LIST_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-V, --pkg-version <version>", "Package version").option("--json", "Output as JSON").action(async (packageName, options) => {
2311
2337
  await withCliErrorHandling(options.json ?? false, async () => {
2312
2338
  const deps = await createContainer();
2313
2339
  await docsListAction(packageName, options, deps);
2314
2340
  });
2315
2341
  });
2316
2342
  }
2317
- // src/commands/docs/search.ts
2343
+ // src/commands/search.ts
2318
2344
  var colors = {
2319
2345
  reset: "\x1B[0m",
2320
2346
  bold: "\x1B[1m",
@@ -2344,37 +2370,11 @@ function toSearchMode(mode) {
2344
2370
  }
2345
2371
  function parsePackageList(input2) {
2346
2372
  return input2.split(",").map((spec) => {
2347
- const trimmed = spec.trim();
2348
- const atIndex = trimmed.lastIndexOf("@");
2349
- let regPkg;
2350
- let version2;
2351
- if (atIndex > 0) {
2352
- regPkg = trimmed.slice(0, atIndex);
2353
- version2 = trimmed.slice(atIndex + 1);
2354
- } else {
2355
- regPkg = trimmed;
2356
- }
2357
- const slashIndex = regPkg.indexOf("/");
2358
- if (slashIndex === -1) {
2359
- return {
2360
- registry: "NPM",
2361
- name: regPkg,
2362
- version: version2
2363
- };
2364
- }
2365
- const beforeSlash = regPkg.slice(0, slashIndex);
2366
- const afterSlash = regPkg.slice(slashIndex + 1);
2367
- if (beforeSlash.startsWith("@")) {
2368
- return {
2369
- registry: "NPM",
2370
- name: regPkg,
2371
- version: version2
2372
- };
2373
- }
2373
+ const parsed = parsePackageSpec(spec.trim());
2374
2374
  return {
2375
- registry: toGraphQLRegistry(beforeSlash),
2376
- name: afterSlash,
2377
- version: version2
2375
+ registry: toGraphQLRegistry(parsed.registry),
2376
+ name: parsed.name,
2377
+ version: parsed.version
2378
2378
  };
2379
2379
  });
2380
2380
  }
@@ -2394,7 +2394,7 @@ function formatEntry(entry, useColors) {
2394
2394
  if (entry.type === "CODE") {
2395
2395
  const title = entry.title ?? "Unknown";
2396
2396
  const location = entry.filePath ? `${entry.filePath}:${entry.startLine ?? "?"}-${entry.endLine ?? "?"}` : "";
2397
- const lang = entry.language ? ` ${entry.language}` : "";
2397
+ const lang = entry.language ? ` * ${entry.language}` : "";
2398
2398
  if (useColors) {
2399
2399
  lines.push(`${badge} ${colors.bold}${title}${colors.reset}`);
2400
2400
  if (location) {
@@ -2446,7 +2446,7 @@ function formatPackageHeader(entry, prevEntry, useColors) {
2446
2446
  return "";
2447
2447
  }
2448
2448
  const header = `${pkg} (${registry})`;
2449
- const separator = "".repeat(Math.min(50, header.length + 10));
2449
+ const separator = "=".repeat(Math.min(50, header.length + 10));
2450
2450
  if (useColors) {
2451
2451
  return `
2452
2452
  ${colors.yellow}${header}${colors.reset}
@@ -2478,10 +2478,10 @@ function formatSearchResults(results, useColors) {
2478
2478
  const codeCount = entries.filter((e) => e?.type === "CODE").length;
2479
2479
  const docCount = entries.filter((e) => e?.type === "DOC" || e?.type === "REPO_DOC").length;
2480
2480
  if (useColors) {
2481
- lines.push(`${colors.dim}───────────────────────────────────────────────────${colors.reset}`);
2481
+ lines.push(`${colors.dim}---------------------------------------------------${colors.reset}`);
2482
2482
  lines.push(`${colors.dim}Results: ${entries.length} total (${codeCount} code, ${docCount} docs)${colors.reset}`);
2483
2483
  } else {
2484
- lines.push("───────────────────────────────────────────────────");
2484
+ lines.push("---------------------------------------------------");
2485
2485
  lines.push(`Results: ${entries.length} total (${codeCount} code, ${docCount} docs)`);
2486
2486
  }
2487
2487
  const indexingWarnings = formatIndexingWarnings(results.indexingStatus);
@@ -2661,7 +2661,7 @@ function summarizeSearchResults(results) {
2661
2661
  return lines.join(`
2662
2662
  `);
2663
2663
  }
2664
- async function docsSearchAction(queryArg, options, deps) {
2664
+ async function searchAction(queryArg, options, deps, defaultMode = "ALL") {
2665
2665
  const { pkgseerService } = deps;
2666
2666
  const query = Array.isArray(queryArg) ? queryArg.join(" ") : queryArg ?? "";
2667
2667
  if (!query.trim()) {
@@ -2678,7 +2678,7 @@ async function docsSearchAction(queryArg, options, deps) {
2678
2678
  }
2679
2679
  const packages = parsePackageList(options.packages);
2680
2680
  const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
2681
- const mode = toSearchMode(options.mode);
2681
+ const mode = options.mode ? toSearchMode(options.mode) : defaultMode;
2682
2682
  const useColors = shouldUseColors(options.noColor);
2683
2683
  const result = await pkgseerService.combinedSearch(packages, query, {
2684
2684
  mode,
@@ -2713,36 +2713,79 @@ Searches both code (functions, classes) and documentation pages
2713
2713
  using the combined search endpoint. Returns results with snippets
2714
2714
  showing matches.
2715
2715
 
2716
+ Package format: [registry:]name[@version]
2717
+
2716
2718
  Examples:
2717
2719
  # Search a single package (code + docs)
2718
- pkgseer docs search "error handling" -P express
2720
+ pkgseer search "error handling" -P express
2719
2721
 
2720
2722
  # Search code only
2721
- pkgseer docs search "Router.use" -P express --mode code
2723
+ pkgseer search "Router.use" -P express --mode code
2722
2724
 
2723
2725
  # Search docs only
2724
- pkgseer docs search "middleware" -P express --mode docs
2726
+ pkgseer search "middleware" -P express --mode docs
2725
2727
 
2726
2728
  # Search multiple packages
2727
- pkgseer docs search "auth" -P express,passport
2729
+ pkgseer search "auth" -P express,passport
2728
2730
 
2729
2731
  # With registry prefix (for non-npm packages)
2730
- pkgseer docs search "orm" -P pypi/django,pypi/sqlalchemy
2732
+ pkgseer search "orm" -P pypi:django,pypi:sqlalchemy
2731
2733
 
2732
2734
  # With specific version
2733
- pkgseer docs search "routing" -P express@4.18.2
2735
+ pkgseer search "routing" -P express@4.18.2
2734
2736
 
2735
2737
  # Output for piping
2736
- pkgseer docs search "routing" -P express --refs-only
2738
+ pkgseer search "routing" -P express --refs-only
2737
2739
 
2738
2740
  # JSON output
2739
- pkgseer docs search "error" -P express --json`;
2741
+ pkgseer search "error" -P express --json`;
2740
2742
  function addSearchOptions(cmd) {
2741
2743
  return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: name or registry/name[@version]. Examples: express | express,lodash | pypi/django@4.2").option("-m, --mode <mode>", "Search mode: all (default), code, docs").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
2742
2744
  }
2743
- function registerDocsSearchCommand(program) {
2744
- const cmd = program.command("search [query...]").summary("Search code and documentation").description(SEARCH_DESCRIPTION);
2745
+ function registerSearchCommand(program) {
2746
+ const cmd = program.command("search [query...]").summary("Search code and documentation across packages").description(SEARCH_DESCRIPTION);
2745
2747
  addSearchOptions(cmd).action(async (query, options) => {
2748
+ await withCliErrorHandling(options.json ?? false, async () => {
2749
+ const deps = await createContainer();
2750
+ await searchAction(query, options, deps);
2751
+ });
2752
+ });
2753
+ }
2754
+
2755
+ // src/commands/docs/search.ts
2756
+ async function docsSearchAction(queryArg, options, deps) {
2757
+ await searchAction(queryArg, options, deps, "DOCS");
2758
+ }
2759
+ var DOCS_SEARCH_DESCRIPTION = `Search documentation across packages.
2760
+
2761
+ Searches documentation pages in package docs. For searching both
2762
+ code and docs, use the top-level 'pkgseer search' command instead.
2763
+
2764
+ Package format: [registry:]name[@version]
2765
+
2766
+ Examples:
2767
+ # Search documentation
2768
+ pkgseer docs search "middleware" -P express
2769
+
2770
+ # Search multiple packages
2771
+ pkgseer docs search "auth" -P express,passport
2772
+
2773
+ # With registry prefix (for non-npm packages)
2774
+ pkgseer docs search "orm" -P pypi:django,pypi:sqlalchemy
2775
+
2776
+ # With specific version
2777
+ pkgseer docs search "routing" -P express@4.18.2
2778
+
2779
+ # JSON output
2780
+ pkgseer docs search "error" -P express --json
2781
+
2782
+ Note: For code search, use 'pkgseer search --mode code' instead.`;
2783
+ function addDocsSearchOptions(cmd) {
2784
+ return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: [registry:]name[@version]. Examples: express | express,lodash | pypi:django@4.2").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
2785
+ }
2786
+ function registerDocsSearchCommand(program) {
2787
+ const cmd = program.command("search [query...]").summary("Search documentation").description(DOCS_SEARCH_DESCRIPTION);
2788
+ addDocsSearchOptions(cmd).action(async (query, options) => {
2746
2789
  await withCliErrorHandling(options.json ?? false, async () => {
2747
2790
  const deps = await createContainer();
2748
2791
  await docsSearchAction(query, options, deps);
@@ -3869,50 +3912,55 @@ Search and analyze packages across npm, PyPI, and Hex. All commands support \`--
3869
3912
 
3870
3913
  \`\`\`bash
3871
3914
  # Search code and docs across packages
3872
- ${invocation} search "<query>" -P lodash,express # npm packages
3873
- ${invocation} search "<query>" -P requests -r pypi # PyPI packages
3874
- ${invocation} search "authentication" -P phoenix,plug -r hex
3915
+ ${invocation} search "<query>" -P lodash,express # npm packages
3916
+ ${invocation} search "<query>" -P pypi:requests # PyPI packages
3917
+ ${invocation} search "authentication" -P hex:phoenix,hex:plug
3875
3918
 
3876
3919
  # Search modes
3877
- ${invocation} search "<query>" -P <packages> --code # Code only
3878
- ${invocation} search "<query>" -P <packages> --docs # Docs only
3920
+ ${invocation} search "<query>" -P <packages> --mode code # Code only
3921
+ ${invocation} search "<query>" -P <packages> --mode docs # Docs only
3879
3922
 
3880
- # Search project dependencies (requires pkgseer.yml)
3881
- ${invocation} docs search "<query>"
3923
+ # Docs-only search (shorthand)
3924
+ ${invocation} docs search "<query>" -P <packages>
3882
3925
  \`\`\`
3883
3926
 
3884
3927
  ## Package Analysis
3885
3928
 
3929
+ Package format: \`[registry:]name[@version]\`
3930
+
3886
3931
  \`\`\`bash
3887
3932
  # Overview: metadata, versions, quickstart
3888
- ${invocation} pkg info <package> [-r npm|pypi|hex]
3933
+ ${invocation} pkg info lodash
3934
+ ${invocation} pkg info pypi:requests
3889
3935
 
3890
3936
  # Quality score (0-100) with category breakdown
3891
- ${invocation} pkg quality <package> [-r registry] [-v version]
3937
+ ${invocation} pkg quality express@4.18.0
3938
+ ${invocation} pkg quality pypi:django@4.2
3892
3939
 
3893
3940
  # Security: CVEs, severity, upgrade paths
3894
- ${invocation} pkg vulns <package> [-r registry] [-v version]
3941
+ ${invocation} pkg vulns lodash@4.17.21
3895
3942
 
3896
3943
  # Dependencies: direct, transitive, tree view
3897
- ${invocation} pkg deps <package> [-r registry] [-t] [-d depth]
3944
+ ${invocation} pkg deps express --transitive
3898
3945
 
3899
3946
  # Compare up to 10 packages
3900
3947
  ${invocation} pkg compare lodash underscore ramda
3901
- ${invocation} pkg compare npm:axios pypi:httpx # cross-registry
3948
+ ${invocation} pkg compare axios pypi:httpx # cross-registry
3902
3949
  \`\`\`
3903
3950
 
3904
3951
  ## Documentation
3905
3952
 
3906
3953
  \`\`\`bash
3907
- ${invocation} docs list <package> [-r registry] # List pages
3908
- ${invocation} docs get <package>/<page-id> # Fetch content
3954
+ ${invocation} docs list pypi:requests # List pages
3955
+ ${invocation} docs get <package>/<page-id> # Fetch content
3956
+ ${invocation} docs search "<query>" -P <packages> # Search docs only
3909
3957
  \`\`\`
3910
3958
 
3911
3959
  ## Tips
3912
3960
 
3913
- - Default registry: npm. Use \`-r pypi\` or \`-r hex\` for others
3961
+ - Package format: \`[registry:]name[@version]\` (e.g., \`pypi:django@4.2\`)
3962
+ - Default registry: npm
3914
3963
  - Use \`--json\` for structured output when parsing
3915
- - Version defaults to latest; use \`-v\` for specific
3916
3964
  `;
3917
3965
  }
3918
3966
  function extractSkillVersion(content) {
@@ -5155,27 +5203,6 @@ in MCP configuration files. Use 'pkgseer mcp' for interactive setup.`).action(as
5155
5203
  }
5156
5204
 
5157
5205
  // src/commands/pkg/compare.ts
5158
- function parsePackageSpec(spec) {
5159
- let registry = "npm";
5160
- let rest = spec;
5161
- if (spec.includes(":")) {
5162
- const colonIndex = spec.indexOf(":");
5163
- const potentialRegistry = spec.slice(0, colonIndex).toLowerCase();
5164
- if (["npm", "pypi", "hex"].includes(potentialRegistry)) {
5165
- registry = potentialRegistry;
5166
- rest = spec.slice(colonIndex + 1);
5167
- }
5168
- }
5169
- const atIndex = rest.lastIndexOf("@");
5170
- if (atIndex > 0) {
5171
- return {
5172
- registry,
5173
- name: rest.slice(0, atIndex),
5174
- version: rest.slice(atIndex + 1)
5175
- };
5176
- }
5177
- return { registry, name: rest };
5178
- }
5179
5206
  function formatPackageComparison(comparison) {
5180
5207
  const lines = [];
5181
5208
  lines.push("\uD83D\uDCCA Package Comparison");
@@ -5296,14 +5323,16 @@ function formatPackageDependencies(data, transitiveRequested) {
5296
5323
  return lines.join(`
5297
5324
  `);
5298
5325
  }
5299
- async function pkgDepsAction(packageName, options, deps) {
5326
+ async function pkgDepsAction(packageArg, options, deps) {
5300
5327
  const { pkgseerService } = deps;
5301
- const registry = toGraphQLRegistry(options.registry);
5328
+ const parsed = parsePackageSpec(packageArg);
5329
+ const registry = toGraphQLRegistry(parsed.registry !== "npm" ? parsed.registry : options.registry);
5330
+ const version2 = parsed.version ?? options.pkgVersion;
5302
5331
  const transitiveRequested = options.transitive ?? false;
5303
- const result = await pkgseerService.cliPackageDeps(registry, packageName, options.pkgVersion, options.transitive, options.maxDepth ? Number.parseInt(options.maxDepth, 10) : undefined);
5332
+ const result = await pkgseerService.cliPackageDeps(registry, parsed.name, version2, options.transitive, options.maxDepth ? Number.parseInt(options.maxDepth, 10) : undefined);
5304
5333
  handleErrors(result.errors, options.json ?? false);
5305
5334
  if (!result.data.packageDependencies) {
5306
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5335
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5307
5336
  return;
5308
5337
  }
5309
5338
  const format = options.json ? "json" : options.format ?? "human";
@@ -5342,12 +5371,14 @@ var DEPS_DESCRIPTION = `Get package dependencies.
5342
5371
  Lists direct dependencies and shows version constraints and
5343
5372
  dependency types (runtime, dev, optional).
5344
5373
 
5374
+ Package format: [registry:]name[@version]
5375
+
5345
5376
  Examples:
5346
5377
  pkgseer pkg deps express
5347
- pkgseer pkg deps lodash --transitive
5348
- pkgseer pkg deps requests --registry pypi --json`;
5378
+ pkgseer pkg deps lodash@4.17.21 --transitive
5379
+ pkgseer pkg deps pypi:requests@2.28.0 --json`;
5349
5380
  function registerPkgDepsCommand(program) {
5350
- 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) => {
5381
+ 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) => {
5351
5382
  await withCliErrorHandling(options.json ?? false, async () => {
5352
5383
  const deps = await createContainer();
5353
5384
  await pkgDepsAction(packageName, options, deps);
@@ -5355,6 +5386,15 @@ function registerPkgDepsCommand(program) {
5355
5386
  });
5356
5387
  }
5357
5388
  // src/commands/pkg/info.ts
5389
+ function formatDownloads(count) {
5390
+ if (count >= 1e6) {
5391
+ return `${(count / 1e6).toFixed(1)}M`;
5392
+ }
5393
+ if (count >= 1000) {
5394
+ return `${(count / 1000).toFixed(1)}K`;
5395
+ }
5396
+ return count.toLocaleString();
5397
+ }
5358
5398
  function formatPackageSummary(data) {
5359
5399
  const lines = [];
5360
5400
  const pkg = data.package;
@@ -5374,6 +5414,11 @@ function formatPackageSummary(data) {
5374
5414
  ["License", pkg.license]
5375
5415
  ]));
5376
5416
  lines.push("");
5417
+ if (pkg.downloadsLastMonth != null && pkg.downloadsLastMonth > 0) {
5418
+ lines.push("Downloads:");
5419
+ lines.push(keyValueTable([["Last Month", formatDownloads(pkg.downloadsLastMonth)]]));
5420
+ lines.push("");
5421
+ }
5377
5422
  if (pkg.homepage || pkg.repositoryUrl) {
5378
5423
  lines.push("Links:");
5379
5424
  const links = [];
@@ -5396,13 +5441,14 @@ function formatPackageSummary(data) {
5396
5441
  return lines.join(`
5397
5442
  `);
5398
5443
  }
5399
- async function pkgInfoAction(packageName, options, deps) {
5444
+ async function pkgInfoAction(packageArg, options, deps) {
5400
5445
  const { pkgseerService } = deps;
5401
- const registry = toGraphQLRegistry(options.registry);
5402
- const result = await pkgseerService.cliPackageInfo(registry, packageName);
5446
+ const parsed = parsePackageSpec(packageArg);
5447
+ const registry = toGraphQLRegistry(parsed.registry !== "npm" ? parsed.registry : options.registry);
5448
+ const result = await pkgseerService.cliPackageInfo(registry, parsed.name);
5403
5449
  handleErrors(result.errors, options.json ?? false);
5404
5450
  if (!result.data.packageSummary) {
5405
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5451
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5406
5452
  return;
5407
5453
  }
5408
5454
  if (options.json) {
@@ -5423,16 +5469,18 @@ async function pkgInfoAction(packageName, options, deps) {
5423
5469
  }
5424
5470
  var INFO_DESCRIPTION = `Get package summary and metadata.
5425
5471
 
5426
- Displays comprehensive information about a package including:
5472
+ Displays information about a package's latest version including:
5427
5473
  - Basic metadata (version, license, description)
5428
5474
  - Download statistics
5429
- - Security advisories
5430
5475
  - Quick start instructions
5431
5476
 
5477
+ Note: This command shows the latest version only.
5478
+ For version-specific info, use 'pkg vulns', 'pkg quality', or 'pkg deps'.
5479
+
5432
5480
  Examples:
5433
5481
  pkgseer pkg info lodash
5434
- pkgseer pkg info requests --registry pypi
5435
- pkgseer pkg info phoenix --registry hex --json`;
5482
+ pkgseer pkg info pypi:requests
5483
+ pkgseer pkg info hex:phoenix --json`;
5436
5484
  function registerPkgInfoCommand(program) {
5437
5485
  program.command("info <package>").summary("Get package summary and metadata").description(INFO_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("--json", "Output as JSON").action(async (packageName, options) => {
5438
5486
  await withCliErrorHandling(options.json ?? false, async () => {
@@ -5460,13 +5508,15 @@ function formatPackageQuality(data) {
5460
5508
  return lines.join(`
5461
5509
  `);
5462
5510
  }
5463
- async function pkgQualityAction(packageName, options, deps) {
5511
+ async function pkgQualityAction(packageArg, options, deps) {
5464
5512
  const { pkgseerService } = deps;
5465
- const registry = toGraphQLRegistry(options.registry);
5466
- const result = await pkgseerService.cliPackageQuality(registry, packageName, options.pkgVersion);
5513
+ const parsed = parsePackageSpec(packageArg);
5514
+ const registry = toGraphQLRegistry(parsed.registry !== "npm" ? parsed.registry : options.registry);
5515
+ const version2 = parsed.version ?? options.pkgVersion;
5516
+ const result = await pkgseerService.cliPackageQuality(registry, parsed.name, version2);
5467
5517
  handleErrors(result.errors, options.json ?? false);
5468
5518
  if (!result.data.packageQuality) {
5469
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5519
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5470
5520
  return;
5471
5521
  }
5472
5522
  const format = options.json ? "json" : options.format ?? "human";
@@ -5494,12 +5544,14 @@ Analyzes package quality across multiple dimensions:
5494
5544
  - Security practices
5495
5545
  - Community engagement
5496
5546
 
5547
+ Package format: [registry:]name[@version]
5548
+
5497
5549
  Examples:
5498
5550
  pkgseer pkg quality lodash
5499
- pkgseer pkg quality express -v 4.18.0
5500
- pkgseer pkg quality requests --registry pypi --json`;
5551
+ pkgseer pkg quality express@4.18.0
5552
+ pkgseer pkg quality pypi:requests@2.28.0 --json`;
5501
5553
  function registerPkgQualityCommand(program) {
5502
- 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) => {
5554
+ 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) => {
5503
5555
  await withCliErrorHandling(options.json ?? false, async () => {
5504
5556
  const deps = await createContainer();
5505
5557
  await pkgQualityAction(packageName, options, deps);
@@ -5559,13 +5611,15 @@ function formatPackageVulnerabilities(data) {
5559
5611
  return lines.join(`
5560
5612
  `);
5561
5613
  }
5562
- async function pkgVulnsAction(packageName, options, deps) {
5614
+ async function pkgVulnsAction(packageArg, options, deps) {
5563
5615
  const { pkgseerService } = deps;
5564
- const registry = toGraphQLRegistry(options.registry);
5565
- const result = await pkgseerService.cliPackageVulns(registry, packageName, options.pkgVersion);
5616
+ const parsed = parsePackageSpec(packageArg);
5617
+ const registry = toGraphQLRegistry(parsed.registry !== "npm" ? parsed.registry : options.registry);
5618
+ const version2 = parsed.version ?? options.pkgVersion;
5619
+ const result = await pkgseerService.cliPackageVulns(registry, parsed.name, version2);
5566
5620
  handleErrors(result.errors, options.json ?? false);
5567
5621
  if (!result.data.packageVulnerabilities) {
5568
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5622
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5569
5623
  return;
5570
5624
  }
5571
5625
  if (options.json) {
@@ -5594,12 +5648,14 @@ Scans for known security vulnerabilities and provides:
5594
5648
  - Affected version ranges
5595
5649
  - Upgrade recommendations
5596
5650
 
5651
+ Package format: [registry:]name[@version]
5652
+
5597
5653
  Examples:
5598
5654
  pkgseer pkg vulns lodash
5599
- pkgseer pkg vulns express -v 4.17.0
5600
- pkgseer pkg vulns requests --registry pypi --json`;
5655
+ pkgseer pkg vulns express@4.17.0
5656
+ pkgseer pkg vulns pypi:requests@2.28.0 --json`;
5601
5657
  function registerPkgVulnsCommand(program) {
5602
- program.command("vulns <package>").summary("Check for security vulnerabilities").description(VULNS_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-v, --pkg-version <version>", "Package version").option("--json", "Output as JSON").action(async (packageName, options) => {
5658
+ program.command("vulns <package>").summary("Check for security vulnerabilities").description(VULNS_DESCRIPTION).option("-r, --registry <registry>", "Package registry", "npm").option("-V, --pkg-version <version>", "Package version").option("--json", "Output as JSON").action(async (packageName, options) => {
5603
5659
  await withCliErrorHandling(options.json ?? false, async () => {
5604
5660
  const deps = await createContainer();
5605
5661
  await pkgVulnsAction(packageName, options, deps);
@@ -6038,6 +6094,9 @@ Getting started:
6038
6094
  pkgseer login Authenticate with your account
6039
6095
  pkgseer skill init Install AI agent skill
6040
6096
 
6097
+ Search:
6098
+ pkgseer search <query> -P <packages> Search code and docs
6099
+
6041
6100
  Package commands:
6042
6101
  pkgseer pkg info <package> Get package summary
6043
6102
  pkgseer pkg vulns <package> Check vulnerabilities
@@ -6048,7 +6107,7 @@ Package commands:
6048
6107
  Documentation commands:
6049
6108
  pkgseer docs list <package> List doc pages
6050
6109
  pkgseer docs get <pkg> <page> Fetch a doc page
6051
- pkgseer docs search <query> Search documentation
6110
+ pkgseer docs search <query> Search docs only
6052
6111
 
6053
6112
  Learn more at https://pkgseer.dev`);
6054
6113
  registerInitCommand(program);
@@ -6056,6 +6115,7 @@ registerMcpCommand(program);
6056
6115
  registerSkillCommand(program);
6057
6116
  registerLoginCommand(program);
6058
6117
  registerLogoutCommand(program);
6118
+ registerSearchCommand(program);
6059
6119
  var auth = program.command("auth").description("View and manage authentication");
6060
6120
  registerAuthStatusCommand(auth);
6061
6121
  var config = program.command("config").description("View and manage configuration");
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  version
3
- } from "./shared/chunk-zaq9c2d8.js";
3
+ } from "./shared/chunk-48mwa8wt.js";
4
4
  export {
5
5
  version
6
6
  };
@@ -1,4 +1,4 @@
1
1
  // package.json
2
- var version = "0.2.3";
2
+ var version = "0.2.4";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pkgseer/cli",
3
3
  "description": "CLI companion for PkgSeer - package intelligence for developers and AI assistants",
4
- "version": "0.2.3",
4
+ "version": "0.2.4",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",