@pkgseer/cli 0.2.2 → 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-jyykfhaq.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);
@@ -3858,13 +3901,7 @@ function generateSkillContent(invocation = "pkgseer") {
3858
3901
  return `---
3859
3902
  name: pkgseer
3860
3903
  version: ${version}
3861
- description: >-
3862
- Search code and documentation across npm, PyPI, and Hex packages.
3863
- Find functions, classes, APIs, and usage examples. Also provides
3864
- quality scores, security vulnerabilities, dependencies, and comparisons.
3865
- Triggers on: "how does X work", "find examples of", "search for",
3866
- "is X secure", "compare X vs Y", package evaluation, dependency decisions,
3867
- "what package should I use for", API lookup, security audits.
3904
+ description: Search code/docs across npm, PyPI, Hex packages. Provides quality scores, security vulnerabilities, dependencies, comparisons. Use when user asks about package security, CVEs, vulnerabilities, code examples, API usage, package comparison, dependency analysis, or which package to use.
3868
3905
  ---
3869
3906
 
3870
3907
  # PkgSeer - Package Intelligence
@@ -3875,50 +3912,55 @@ Search and analyze packages across npm, PyPI, and Hex. All commands support \`--
3875
3912
 
3876
3913
  \`\`\`bash
3877
3914
  # Search code and docs across packages
3878
- ${invocation} search "<query>" -P lodash,express # npm packages
3879
- ${invocation} search "<query>" -P requests -r pypi # PyPI packages
3880
- ${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
3881
3918
 
3882
3919
  # Search modes
3883
- ${invocation} search "<query>" -P <packages> --code # Code only
3884
- ${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
3885
3922
 
3886
- # Search project dependencies (requires pkgseer.yml)
3887
- ${invocation} docs search "<query>"
3923
+ # Docs-only search (shorthand)
3924
+ ${invocation} docs search "<query>" -P <packages>
3888
3925
  \`\`\`
3889
3926
 
3890
3927
  ## Package Analysis
3891
3928
 
3929
+ Package format: \`[registry:]name[@version]\`
3930
+
3892
3931
  \`\`\`bash
3893
3932
  # Overview: metadata, versions, quickstart
3894
- ${invocation} pkg info <package> [-r npm|pypi|hex]
3933
+ ${invocation} pkg info lodash
3934
+ ${invocation} pkg info pypi:requests
3895
3935
 
3896
3936
  # Quality score (0-100) with category breakdown
3897
- ${invocation} pkg quality <package> [-r registry] [-v version]
3937
+ ${invocation} pkg quality express@4.18.0
3938
+ ${invocation} pkg quality pypi:django@4.2
3898
3939
 
3899
3940
  # Security: CVEs, severity, upgrade paths
3900
- ${invocation} pkg vulns <package> [-r registry] [-v version]
3941
+ ${invocation} pkg vulns lodash@4.17.21
3901
3942
 
3902
3943
  # Dependencies: direct, transitive, tree view
3903
- ${invocation} pkg deps <package> [-r registry] [-t] [-d depth]
3944
+ ${invocation} pkg deps express --transitive
3904
3945
 
3905
3946
  # Compare up to 10 packages
3906
3947
  ${invocation} pkg compare lodash underscore ramda
3907
- ${invocation} pkg compare npm:axios pypi:httpx # cross-registry
3948
+ ${invocation} pkg compare axios pypi:httpx # cross-registry
3908
3949
  \`\`\`
3909
3950
 
3910
3951
  ## Documentation
3911
3952
 
3912
3953
  \`\`\`bash
3913
- ${invocation} docs list <package> [-r registry] # List pages
3914
- ${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
3915
3957
  \`\`\`
3916
3958
 
3917
3959
  ## Tips
3918
3960
 
3919
- - 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
3920
3963
  - Use \`--json\` for structured output when parsing
3921
- - Version defaults to latest; use \`-v\` for specific
3922
3964
  `;
3923
3965
  }
3924
3966
  function extractSkillVersion(content) {
@@ -5161,27 +5203,6 @@ in MCP configuration files. Use 'pkgseer mcp' for interactive setup.`).action(as
5161
5203
  }
5162
5204
 
5163
5205
  // src/commands/pkg/compare.ts
5164
- function parsePackageSpec(spec) {
5165
- let registry = "npm";
5166
- let rest = spec;
5167
- if (spec.includes(":")) {
5168
- const colonIndex = spec.indexOf(":");
5169
- const potentialRegistry = spec.slice(0, colonIndex).toLowerCase();
5170
- if (["npm", "pypi", "hex"].includes(potentialRegistry)) {
5171
- registry = potentialRegistry;
5172
- rest = spec.slice(colonIndex + 1);
5173
- }
5174
- }
5175
- const atIndex = rest.lastIndexOf("@");
5176
- if (atIndex > 0) {
5177
- return {
5178
- registry,
5179
- name: rest.slice(0, atIndex),
5180
- version: rest.slice(atIndex + 1)
5181
- };
5182
- }
5183
- return { registry, name: rest };
5184
- }
5185
5206
  function formatPackageComparison(comparison) {
5186
5207
  const lines = [];
5187
5208
  lines.push("\uD83D\uDCCA Package Comparison");
@@ -5302,14 +5323,16 @@ function formatPackageDependencies(data, transitiveRequested) {
5302
5323
  return lines.join(`
5303
5324
  `);
5304
5325
  }
5305
- async function pkgDepsAction(packageName, options, deps) {
5326
+ async function pkgDepsAction(packageArg, options, deps) {
5306
5327
  const { pkgseerService } = deps;
5307
- 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;
5308
5331
  const transitiveRequested = options.transitive ?? false;
5309
- 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);
5310
5333
  handleErrors(result.errors, options.json ?? false);
5311
5334
  if (!result.data.packageDependencies) {
5312
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5335
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5313
5336
  return;
5314
5337
  }
5315
5338
  const format = options.json ? "json" : options.format ?? "human";
@@ -5348,12 +5371,14 @@ var DEPS_DESCRIPTION = `Get package dependencies.
5348
5371
  Lists direct dependencies and shows version constraints and
5349
5372
  dependency types (runtime, dev, optional).
5350
5373
 
5374
+ Package format: [registry:]name[@version]
5375
+
5351
5376
  Examples:
5352
5377
  pkgseer pkg deps express
5353
- pkgseer pkg deps lodash --transitive
5354
- 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`;
5355
5380
  function registerPkgDepsCommand(program) {
5356
- 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) => {
5357
5382
  await withCliErrorHandling(options.json ?? false, async () => {
5358
5383
  const deps = await createContainer();
5359
5384
  await pkgDepsAction(packageName, options, deps);
@@ -5361,6 +5386,15 @@ function registerPkgDepsCommand(program) {
5361
5386
  });
5362
5387
  }
5363
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
+ }
5364
5398
  function formatPackageSummary(data) {
5365
5399
  const lines = [];
5366
5400
  const pkg = data.package;
@@ -5380,6 +5414,11 @@ function formatPackageSummary(data) {
5380
5414
  ["License", pkg.license]
5381
5415
  ]));
5382
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
+ }
5383
5422
  if (pkg.homepage || pkg.repositoryUrl) {
5384
5423
  lines.push("Links:");
5385
5424
  const links = [];
@@ -5402,13 +5441,14 @@ function formatPackageSummary(data) {
5402
5441
  return lines.join(`
5403
5442
  `);
5404
5443
  }
5405
- async function pkgInfoAction(packageName, options, deps) {
5444
+ async function pkgInfoAction(packageArg, options, deps) {
5406
5445
  const { pkgseerService } = deps;
5407
- const registry = toGraphQLRegistry(options.registry);
5408
- 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);
5409
5449
  handleErrors(result.errors, options.json ?? false);
5410
5450
  if (!result.data.packageSummary) {
5411
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5451
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5412
5452
  return;
5413
5453
  }
5414
5454
  if (options.json) {
@@ -5429,16 +5469,18 @@ async function pkgInfoAction(packageName, options, deps) {
5429
5469
  }
5430
5470
  var INFO_DESCRIPTION = `Get package summary and metadata.
5431
5471
 
5432
- Displays comprehensive information about a package including:
5472
+ Displays information about a package's latest version including:
5433
5473
  - Basic metadata (version, license, description)
5434
5474
  - Download statistics
5435
- - Security advisories
5436
5475
  - Quick start instructions
5437
5476
 
5477
+ Note: This command shows the latest version only.
5478
+ For version-specific info, use 'pkg vulns', 'pkg quality', or 'pkg deps'.
5479
+
5438
5480
  Examples:
5439
5481
  pkgseer pkg info lodash
5440
- pkgseer pkg info requests --registry pypi
5441
- pkgseer pkg info phoenix --registry hex --json`;
5482
+ pkgseer pkg info pypi:requests
5483
+ pkgseer pkg info hex:phoenix --json`;
5442
5484
  function registerPkgInfoCommand(program) {
5443
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) => {
5444
5486
  await withCliErrorHandling(options.json ?? false, async () => {
@@ -5466,13 +5508,15 @@ function formatPackageQuality(data) {
5466
5508
  return lines.join(`
5467
5509
  `);
5468
5510
  }
5469
- async function pkgQualityAction(packageName, options, deps) {
5511
+ async function pkgQualityAction(packageArg, options, deps) {
5470
5512
  const { pkgseerService } = deps;
5471
- const registry = toGraphQLRegistry(options.registry);
5472
- 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);
5473
5517
  handleErrors(result.errors, options.json ?? false);
5474
5518
  if (!result.data.packageQuality) {
5475
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5519
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5476
5520
  return;
5477
5521
  }
5478
5522
  const format = options.json ? "json" : options.format ?? "human";
@@ -5500,12 +5544,14 @@ Analyzes package quality across multiple dimensions:
5500
5544
  - Security practices
5501
5545
  - Community engagement
5502
5546
 
5547
+ Package format: [registry:]name[@version]
5548
+
5503
5549
  Examples:
5504
5550
  pkgseer pkg quality lodash
5505
- pkgseer pkg quality express -v 4.18.0
5506
- 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`;
5507
5553
  function registerPkgQualityCommand(program) {
5508
- 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) => {
5509
5555
  await withCliErrorHandling(options.json ?? false, async () => {
5510
5556
  const deps = await createContainer();
5511
5557
  await pkgQualityAction(packageName, options, deps);
@@ -5565,13 +5611,15 @@ function formatPackageVulnerabilities(data) {
5565
5611
  return lines.join(`
5566
5612
  `);
5567
5613
  }
5568
- async function pkgVulnsAction(packageName, options, deps) {
5614
+ async function pkgVulnsAction(packageArg, options, deps) {
5569
5615
  const { pkgseerService } = deps;
5570
- const registry = toGraphQLRegistry(options.registry);
5571
- 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);
5572
5620
  handleErrors(result.errors, options.json ?? false);
5573
5621
  if (!result.data.packageVulnerabilities) {
5574
- outputError(`Package not found: ${packageName} in ${options.registry}`, options.json ?? false);
5622
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
5575
5623
  return;
5576
5624
  }
5577
5625
  if (options.json) {
@@ -5600,12 +5648,14 @@ Scans for known security vulnerabilities and provides:
5600
5648
  - Affected version ranges
5601
5649
  - Upgrade recommendations
5602
5650
 
5651
+ Package format: [registry:]name[@version]
5652
+
5603
5653
  Examples:
5604
5654
  pkgseer pkg vulns lodash
5605
- pkgseer pkg vulns express -v 4.17.0
5606
- 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`;
5607
5657
  function registerPkgVulnsCommand(program) {
5608
- 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) => {
5609
5659
  await withCliErrorHandling(options.json ?? false, async () => {
5610
5660
  const deps = await createContainer();
5611
5661
  await pkgVulnsAction(packageName, options, deps);
@@ -6044,6 +6094,9 @@ Getting started:
6044
6094
  pkgseer login Authenticate with your account
6045
6095
  pkgseer skill init Install AI agent skill
6046
6096
 
6097
+ Search:
6098
+ pkgseer search <query> -P <packages> Search code and docs
6099
+
6047
6100
  Package commands:
6048
6101
  pkgseer pkg info <package> Get package summary
6049
6102
  pkgseer pkg vulns <package> Check vulnerabilities
@@ -6054,7 +6107,7 @@ Package commands:
6054
6107
  Documentation commands:
6055
6108
  pkgseer docs list <package> List doc pages
6056
6109
  pkgseer docs get <pkg> <page> Fetch a doc page
6057
- pkgseer docs search <query> Search documentation
6110
+ pkgseer docs search <query> Search docs only
6058
6111
 
6059
6112
  Learn more at https://pkgseer.dev`);
6060
6113
  registerInitCommand(program);
@@ -6062,6 +6115,7 @@ registerMcpCommand(program);
6062
6115
  registerSkillCommand(program);
6063
6116
  registerLoginCommand(program);
6064
6117
  registerLogoutCommand(program);
6118
+ registerSearchCommand(program);
6065
6119
  var auth = program.command("auth").description("View and manage authentication");
6066
6120
  registerAuthStatusCommand(auth);
6067
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-jyykfhaq.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.2";
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.2",
4
+ "version": "0.2.4",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",