@pkgseer/cli 0.4.1 → 0.4.2

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-d340pysc.js";
4
+ } from "./shared/chunk-w4ht5me5.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -689,12 +689,15 @@ var FindSymbolDocument = gql`
689
689
  }
690
690
  `;
691
691
  var SearchSymbolsDocument = gql`
692
- query SearchSymbols($registry: Registry!, $packageName: String!, $query: String!, $kind: SymbolKind, $version: String, $limit: Int, $mode: NavigationMode, $waitTimeoutMs: Int) {
692
+ query SearchSymbols($registry: Registry!, $packageName: String!, $query: String, $keywords: [String!], $matchMode: MatchMode, $kind: SymbolKind, $filePath: String, $version: String, $limit: Int, $mode: NavigationMode, $waitTimeoutMs: Int) {
693
693
  searchSymbols(
694
694
  registry: $registry
695
695
  packageName: $packageName
696
696
  query: $query
697
+ keywords: $keywords
698
+ matchMode: $matchMode
697
699
  kind: $kind
700
+ filePath: $filePath
698
701
  version: $version
699
702
  limit: $limit
700
703
  mode: $mode
@@ -708,11 +711,25 @@ var SearchSymbolsDocument = gql`
708
711
  endLine
709
712
  preview
710
713
  contentPreview
714
+ code
711
715
  language
716
+ symbolId
717
+ qualifiedPath
718
+ kind
719
+ arity
720
+ isPublic
721
+ containedSymbols
712
722
  }
713
723
  totalMatches
714
724
  hasMore
715
725
  indexedVersion
726
+ diagnostics {
727
+ indexed
728
+ chunkCount
729
+ fileCount
730
+ indexedAt
731
+ hint
732
+ }
716
733
  }
717
734
  }
718
735
  `;
@@ -913,6 +930,112 @@ var SearchProjectDocsDocument = gql`
913
930
  }
914
931
  }
915
932
  `;
933
+ var ListRepoFilesDocument = gql`
934
+ query ListRepoFiles($registry: Registry!, $packageName: String!, $version: String, $pathPrefix: String, $limit: Int, $waitTimeoutMs: Int) {
935
+ listRepoFiles(
936
+ registry: $registry
937
+ packageName: $packageName
938
+ version: $version
939
+ pathPrefix: $pathPrefix
940
+ limit: $limit
941
+ waitTimeoutMs: $waitTimeoutMs
942
+ ) {
943
+ files {
944
+ path
945
+ name
946
+ language
947
+ fileType
948
+ byteSize
949
+ }
950
+ total
951
+ hasMore
952
+ indexedVersion
953
+ }
954
+ }
955
+ `;
956
+ var GrepRepoFileDocument = gql`
957
+ query GrepRepoFile($registry: Registry!, $packageName: String!, $filePath: String!, $pattern: String!, $contextLines: Int, $maxMatches: Int, $version: String, $waitTimeoutMs: Int) {
958
+ grepRepoFile(
959
+ registry: $registry
960
+ packageName: $packageName
961
+ filePath: $filePath
962
+ pattern: $pattern
963
+ contextLines: $contextLines
964
+ maxMatches: $maxMatches
965
+ version: $version
966
+ waitTimeoutMs: $waitTimeoutMs
967
+ ) {
968
+ matches {
969
+ lineNumber
970
+ lineContent
971
+ contextBefore
972
+ contextAfter
973
+ }
974
+ totalMatches
975
+ hasMore
976
+ filePath
977
+ language
978
+ totalLines
979
+ indexedVersion
980
+ }
981
+ }
982
+ `;
983
+ var VersionDiffDocument = gql`
984
+ query VersionDiff($registry: Registry!, $packageName: String!, $fromVersion: String!, $toVersion: String!, $includePrivate: Boolean, $kind: SymbolKind, $limit: Int, $waitTimeoutMs: Int) {
985
+ versionDiff(
986
+ registry: $registry
987
+ packageName: $packageName
988
+ fromVersion: $fromVersion
989
+ toVersion: $toVersion
990
+ includePrivate: $includePrivate
991
+ kind: $kind
992
+ limit: $limit
993
+ waitTimeoutMs: $waitTimeoutMs
994
+ ) {
995
+ fromVersion
996
+ toVersion
997
+ summary {
998
+ added
999
+ removed
1000
+ moved
1001
+ modified
1002
+ unchanged
1003
+ breaking
1004
+ behaviorChange
1005
+ }
1006
+ changes {
1007
+ qualifiedPath
1008
+ kind
1009
+ arity
1010
+ name
1011
+ changeType
1012
+ severity
1013
+ changedFields
1014
+ from {
1015
+ contentHash
1016
+ filePath
1017
+ arity
1018
+ startLine
1019
+ endLine
1020
+ isPublic
1021
+ minArity
1022
+ symbolId
1023
+ }
1024
+ to {
1025
+ contentHash
1026
+ filePath
1027
+ arity
1028
+ startLine
1029
+ endLine
1030
+ isPublic
1031
+ minArity
1032
+ symbolId
1033
+ }
1034
+ }
1035
+ hasMore
1036
+ }
1037
+ }
1038
+ `;
916
1039
  var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
917
1040
  var CliPackageInfoDocumentString = print(CliPackageInfoDocument);
918
1041
  var CliPackageVulnsDocumentString = print(CliPackageVulnsDocument);
@@ -946,6 +1069,9 @@ var SymbolDependentsDocumentString = print(SymbolDependentsDocument);
946
1069
  var PackageImportsDocumentString = print(PackageImportsDocument);
947
1070
  var CallPathDocumentString = print(CallPathDocument);
948
1071
  var SearchProjectDocsDocumentString = print(SearchProjectDocsDocument);
1072
+ var ListRepoFilesDocumentString = print(ListRepoFilesDocument);
1073
+ var GrepRepoFileDocumentString = print(GrepRepoFileDocument);
1074
+ var VersionDiffDocumentString = print(VersionDiffDocument);
949
1075
  function getSdk(client, withWrapper = defaultWrapper) {
950
1076
  return {
951
1077
  CliPackageInfo(variables, requestHeaders) {
@@ -1043,6 +1169,15 @@ function getSdk(client, withWrapper = defaultWrapper) {
1043
1169
  },
1044
1170
  SearchProjectDocs(variables, requestHeaders) {
1045
1171
  return withWrapper((wrappedRequestHeaders) => client.rawRequest(SearchProjectDocsDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "SearchProjectDocs", "query", variables);
1172
+ },
1173
+ ListRepoFiles(variables, requestHeaders) {
1174
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(ListRepoFilesDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "ListRepoFiles", "query", variables);
1175
+ },
1176
+ GrepRepoFile(variables, requestHeaders) {
1177
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(GrepRepoFileDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "GrepRepoFile", "query", variables);
1178
+ },
1179
+ VersionDiff(variables, requestHeaders) {
1180
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(VersionDiffDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "VersionDiff", "query", variables);
1046
1181
  }
1047
1182
  };
1048
1183
  }
@@ -1381,7 +1516,10 @@ var TOOL_NAMES = [
1381
1516
  "symbol_callees",
1382
1517
  "package_imports",
1383
1518
  "call_path",
1384
- "trigger_indexing"
1519
+ "trigger_indexing",
1520
+ "list_repo_files",
1521
+ "grep_repo_file",
1522
+ "version_diff"
1385
1523
  ];
1386
1524
  var ToolNameSchema = z.enum(TOOL_NAMES);
1387
1525
  var SharedConfigFields = {
@@ -1820,12 +1958,15 @@ class PkgseerServiceImpl {
1820
1958
  });
1821
1959
  return { data: result.data, errors: result.errors };
1822
1960
  }
1823
- async searchSymbols(registry, packageName, query, options) {
1961
+ async searchSymbols(registry, packageName, options) {
1824
1962
  const result = await this.client.SearchSymbols({
1825
1963
  registry,
1826
1964
  packageName,
1827
- query,
1965
+ query: options?.query,
1966
+ keywords: options?.keywords,
1967
+ matchMode: options?.matchMode,
1828
1968
  kind: options?.kind,
1969
+ filePath: options?.filePath,
1829
1970
  version: options?.version,
1830
1971
  limit: options?.limit,
1831
1972
  mode: options?.mode,
@@ -1894,6 +2035,43 @@ class PkgseerServiceImpl {
1894
2035
  });
1895
2036
  return { data: result.data, errors: result.errors };
1896
2037
  }
2038
+ async listRepoFiles(registry, packageName, options) {
2039
+ const result = await this.client.ListRepoFiles({
2040
+ registry,
2041
+ packageName,
2042
+ version: options?.version,
2043
+ pathPrefix: options?.pathPrefix,
2044
+ limit: options?.limit,
2045
+ waitTimeoutMs: options?.waitTimeoutMs
2046
+ });
2047
+ return { data: result.data, errors: result.errors };
2048
+ }
2049
+ async grepRepoFile(registry, packageName, filePath, pattern, options) {
2050
+ const result = await this.client.GrepRepoFile({
2051
+ registry,
2052
+ packageName,
2053
+ filePath,
2054
+ pattern,
2055
+ contextLines: options?.contextLines,
2056
+ maxMatches: options?.maxMatches,
2057
+ version: options?.version,
2058
+ waitTimeoutMs: options?.waitTimeoutMs
2059
+ });
2060
+ return { data: result.data, errors: result.errors };
2061
+ }
2062
+ async versionDiff(registry, packageName, fromVersion, toVersion, options) {
2063
+ const result = await this.client.VersionDiff({
2064
+ registry,
2065
+ packageName,
2066
+ fromVersion,
2067
+ toVersion,
2068
+ includePrivate: options?.includePrivate,
2069
+ kind: options?.kind,
2070
+ limit: options?.limit,
2071
+ waitTimeoutMs: options?.waitTimeoutMs
2072
+ });
2073
+ return { data: result.data, errors: result.errors };
2074
+ }
1897
2075
  async triggerIndexing(input) {
1898
2076
  const result = await this.client.TriggerIndexing({ input });
1899
2077
  return { data: result.data, errors: result.errors };
@@ -2599,6 +2777,138 @@ function registerCodeCallersCommand(program) {
2599
2777
  });
2600
2778
  });
2601
2779
  }
2780
+ // src/commands/code/diff.ts
2781
+ function formatDiffResult(data) {
2782
+ const lines = [];
2783
+ lines.push(`Version diff: ${data.fromVersion} → ${data.toVersion}`);
2784
+ lines.push("");
2785
+ const s = data.summary;
2786
+ lines.push("Summary:");
2787
+ lines.push(` Added: ${s.added} Removed: ${s.removed} Modified: ${s.modified} Moved: ${s.moved}`);
2788
+ lines.push(` Unchanged: ${s.unchanged} Breaking: ${s.breaking} Behavior changes: ${s.behaviorChange}`);
2789
+ if (data.hasMore) {
2790
+ lines.push(" (more changes available, increase --limit)");
2791
+ }
2792
+ lines.push("");
2793
+ for (const change of data.changes) {
2794
+ const severity = change.severity ? ` [${change.severity}]` : "";
2795
+ const kindLabel = change.kind ? `[${change.kind}]` : "";
2796
+ const name = change.qualifiedPath ?? change.name ?? "unknown";
2797
+ lines.push(`${change.changeType}${severity} ${kindLabel} ${name}`.trim());
2798
+ if (change.from?.filePath) {
2799
+ const loc = change.from.startLine ? `${change.from.filePath}:${change.from.startLine}` : change.from.filePath;
2800
+ lines.push(` From: ${loc}`);
2801
+ }
2802
+ if (change.to?.filePath) {
2803
+ const loc = change.to.startLine ? `${change.to.filePath}:${change.to.startLine}` : change.to.filePath;
2804
+ lines.push(` To: ${loc}`);
2805
+ }
2806
+ if (change.changedFields?.length) {
2807
+ lines.push(` Changed: ${change.changedFields.join(", ")}`);
2808
+ }
2809
+ lines.push("");
2810
+ }
2811
+ return lines.join(`
2812
+ `).trimEnd();
2813
+ }
2814
+ async function codeDiffAction(packageArg, fromVersion, toVersion, options, deps) {
2815
+ const { pkgseerService } = deps;
2816
+ const parsed = parsePackageSpec(packageArg);
2817
+ const registry = toGraphQLRegistry(parsed.registry);
2818
+ const result = await pkgseerService.versionDiff(registry, parsed.name, fromVersion, toVersion, {
2819
+ includePrivate: options.includePrivate,
2820
+ kind: options.kind?.toUpperCase(),
2821
+ limit: parseIntOption(options.limit, "--limit"),
2822
+ waitTimeoutMs: parseWaitTimeout(options.wait)
2823
+ });
2824
+ handleErrors(result.errors, options.json ?? false);
2825
+ if (!result.data.versionDiff) {
2826
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2827
+ return;
2828
+ }
2829
+ if (options.json) {
2830
+ output(result.data.versionDiff, true);
2831
+ } else {
2832
+ console.log(formatDiffResult(result.data.versionDiff));
2833
+ }
2834
+ }
2835
+ var DIFF_DESCRIPTION = `Compare symbols between two package versions.
2836
+
2837
+ Detects added, removed, moved, and modified symbols with severity classification.
2838
+ Returns summary counts and per-symbol changes with file locations.
2839
+
2840
+ Examples:
2841
+ pkgseer code diff npm:express v4.18.2 v5.0.0
2842
+ pkgseer code diff npm:lodash 4.17.20 4.17.21 --include-private
2843
+ pkgseer code diff pypi:requests 2.30.0 2.31.0 --kind function`;
2844
+ function registerCodeDiffCommand(program) {
2845
+ program.command("diff <package> <from> <to>").summary("Compare symbols between versions").description(DIFF_DESCRIPTION).option("--include-private", "Include private/non-exported symbols").option("--kind <kind>", "Filter by symbol kind").option("--limit <n>", "Max changes to return (max 500)").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (packageArg, fromVersion, toVersion, options) => {
2846
+ await withCliErrorHandling(options.json ?? false, async () => {
2847
+ const deps = await createContainer();
2848
+ await codeDiffAction(packageArg, fromVersion, toVersion, options, deps);
2849
+ });
2850
+ });
2851
+ }
2852
+ // src/commands/code/files.ts
2853
+ function formatFilesResult(data) {
2854
+ const lines = [];
2855
+ lines.push(`${data.total} file(s)${data.hasMore ? " (more available)" : ""}`);
2856
+ if (data.indexedVersion) {
2857
+ lines.push(`Indexed version: ${data.indexedVersion}`);
2858
+ }
2859
+ lines.push("");
2860
+ for (const file of data.files) {
2861
+ const meta = [];
2862
+ if (file.language)
2863
+ meta.push(file.language);
2864
+ if (file.fileType)
2865
+ meta.push(file.fileType);
2866
+ if (file.byteSize != null)
2867
+ meta.push(`${file.byteSize}B`);
2868
+ const suffix = meta.length > 0 ? ` (${meta.join(", ")})` : "";
2869
+ lines.push(`${file.path}${suffix}`);
2870
+ }
2871
+ return lines.join(`
2872
+ `).trimEnd();
2873
+ }
2874
+ async function codeFilesAction(packageArg, options, deps) {
2875
+ const { pkgseerService } = deps;
2876
+ const parsed = parsePackageSpec(packageArg);
2877
+ const registry = toGraphQLRegistry(parsed.registry);
2878
+ const result = await pkgseerService.listRepoFiles(registry, parsed.name, {
2879
+ version: parsed.version,
2880
+ pathPrefix: options.pathPrefix,
2881
+ limit: parseIntOption(options.limit, "--limit"),
2882
+ waitTimeoutMs: parseWaitTimeout(options.wait)
2883
+ });
2884
+ handleErrors(result.errors, options.json ?? false);
2885
+ if (!result.data.listRepoFiles) {
2886
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2887
+ return;
2888
+ }
2889
+ if (options.json) {
2890
+ output(result.data.listRepoFiles, true);
2891
+ } else {
2892
+ console.log(formatFilesResult(result.data.listRepoFiles));
2893
+ }
2894
+ }
2895
+ var FILES_DESCRIPTION = `List files in a package's indexed repository.
2896
+
2897
+ Returns file paths, names, languages, types, and sizes.
2898
+ Use --path-prefix to filter by directory.
2899
+
2900
+ Examples:
2901
+ pkgseer code files npm:express
2902
+ pkgseer code files npm:express --path-prefix src/
2903
+ pkgseer code files pypi:requests --limit 50`;
2904
+ function registerCodeFilesCommand(program) {
2905
+ program.command("files <package>").summary("List files in package repository").description(FILES_DESCRIPTION).option("--path-prefix <prefix>", "Filter to files under this path").option("--limit <n>", "Max files to return (max 1000)").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (packageArg, options) => {
2906
+ await withCliErrorHandling(options.json ?? false, async () => {
2907
+ const deps = await createContainer();
2908
+ await codeFilesAction(packageArg, options, deps);
2909
+ });
2910
+ });
2911
+ }
2602
2912
  // src/commands/code/find.ts
2603
2913
  function formatFindResult(data) {
2604
2914
  const lines = [];
@@ -2658,6 +2968,72 @@ function registerCodeFindCommand(program) {
2658
2968
  });
2659
2969
  });
2660
2970
  }
2971
+ // src/commands/code/grep.ts
2972
+ function formatGrepResult(data) {
2973
+ const lines = [];
2974
+ lines.push(`${data.totalMatches} match(es) in ${data.filePath ?? "unknown"}${data.hasMore ? " (more available)" : ""}`);
2975
+ if (data.language)
2976
+ lines.push(`Language: ${data.language}`);
2977
+ if (data.totalLines != null)
2978
+ lines.push(`Total lines: ${data.totalLines}`);
2979
+ if (data.indexedVersion)
2980
+ lines.push(`Indexed version: ${data.indexedVersion}`);
2981
+ lines.push("");
2982
+ for (const match of data.matches) {
2983
+ if (match.contextBefore) {
2984
+ for (const line of match.contextBefore) {
2985
+ lines.push(` ${line}`);
2986
+ }
2987
+ }
2988
+ lines.push(`${match.lineNumber}: ${match.lineContent}`);
2989
+ if (match.contextAfter) {
2990
+ for (const line of match.contextAfter) {
2991
+ lines.push(` ${line}`);
2992
+ }
2993
+ }
2994
+ lines.push("");
2995
+ }
2996
+ return lines.join(`
2997
+ `).trimEnd();
2998
+ }
2999
+ async function codeGrepAction(packageArg, filePath, pattern, options, deps) {
3000
+ const { pkgseerService } = deps;
3001
+ const parsed = parsePackageSpec(packageArg);
3002
+ const registry = toGraphQLRegistry(parsed.registry);
3003
+ const result = await pkgseerService.grepRepoFile(registry, parsed.name, filePath, pattern, {
3004
+ contextLines: parseIntOption(options.contextLines, "--context-lines"),
3005
+ maxMatches: parseIntOption(options.maxMatches, "--max-matches"),
3006
+ version: parsed.version,
3007
+ waitTimeoutMs: parseWaitTimeout(options.wait)
3008
+ });
3009
+ handleErrors(result.errors, options.json ?? false);
3010
+ if (!result.data.grepRepoFile) {
3011
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
3012
+ return;
3013
+ }
3014
+ if (options.json) {
3015
+ output(result.data.grepRepoFile, true);
3016
+ } else {
3017
+ console.log(formatGrepResult(result.data.grepRepoFile));
3018
+ }
3019
+ }
3020
+ var GREP_DESCRIPTION = `Search for a pattern within a file in a package's repository.
3021
+
3022
+ Case-insensitive substring matching. Returns matching lines with context.
3023
+ Use 'code files' to discover available file paths.
3024
+
3025
+ Examples:
3026
+ pkgseer code grep npm:express "lib/router/index.js" "middleware"
3027
+ pkgseer code grep pypi:requests "requests/api.py" "timeout" --context-lines 3
3028
+ pkgseer code grep npm:lodash "lodash.js" "cloneDeep" --max-matches 5`;
3029
+ function registerCodeGrepCommand(program) {
3030
+ program.command("grep <package> <file> <pattern>").summary("Search for pattern in a package file").description(GREP_DESCRIPTION).option("--context-lines <n>", "Lines of context around each match (max 10)").option("--max-matches <n>", "Maximum matches to return (max 200)").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (packageArg, filePath, pattern, options) => {
3031
+ await withCliErrorHandling(options.json ?? false, async () => {
3032
+ const deps = await createContainer();
3033
+ await codeGrepAction(packageArg, filePath, pattern, options, deps);
3034
+ });
3035
+ });
3036
+ }
2661
3037
  // src/commands/code/imports.ts
2662
3038
  function formatImport(entry) {
2663
3039
  const parts = [];
@@ -2899,12 +3275,29 @@ function formatSearchResult(data) {
2899
3275
  return lines.join(`
2900
3276
  `).trimEnd();
2901
3277
  }
3278
+ function parseMatchMode(mode) {
3279
+ if (!mode)
3280
+ return;
3281
+ const upper = mode.toUpperCase();
3282
+ if (upper === "OR" || upper === "AND")
3283
+ return upper;
3284
+ throw new Error("--match-mode must be 'or' or 'and'");
3285
+ }
2902
3286
  async function codeSearchAction(packageArg, query, options, deps) {
2903
3287
  const { pkgseerService } = deps;
2904
3288
  const parsed = parsePackageSpec(packageArg);
2905
3289
  const registry = toGraphQLRegistry(parsed.registry);
2906
- const result = await pkgseerService.searchSymbols(registry, parsed.name, query, {
3290
+ const keywords = options.keywords ? options.keywords.split(",").map((k) => k.trim()).filter(Boolean) : undefined;
3291
+ if (!query && !keywords?.length) {
3292
+ outputError("Either a query argument or --keywords must be provided", options.json ?? false);
3293
+ return;
3294
+ }
3295
+ const result = await pkgseerService.searchSymbols(registry, parsed.name, {
3296
+ query: query || undefined,
3297
+ keywords,
3298
+ matchMode: parseMatchMode(options.matchMode),
2907
3299
  kind: options.kind?.toUpperCase(),
3300
+ filePath: options.filePath,
2908
3301
  version: parsed.version,
2909
3302
  limit: parseIntOption(options.limit, "--limit"),
2910
3303
  mode: parseNavigationMode(options.mode),
@@ -2924,14 +3317,16 @@ async function codeSearchAction(packageArg, query, options, deps) {
2924
3317
  var SEARCH_DESCRIPTION = `Full-text search within package code.
2925
3318
 
2926
3319
  Searches across functions, classes, modules, and doc sections.
3320
+ Supports query string or keyword list with match mode.
2927
3321
  Returns matching code chunks with names, types, and previews.
2928
3322
 
2929
3323
  Examples:
2930
3324
  pkgseer code search npm:express "middleware"
2931
3325
  pkgseer code search pypi:requests "timeout" --kind function
2932
- pkgseer code search npm:lodash "deep clone" --limit 10`;
3326
+ pkgseer code search npm:lodash --keywords "deep,clone" --match-mode and
3327
+ pkgseer code search npm:express "router" --file-path src/`;
2933
3328
  function registerCodeSearchCommand(program) {
2934
- program.command("search <package> <query>").summary("Full-text search in package code").description(SEARCH_DESCRIPTION).option("--kind <kind>", "Filter by chunk type (function, class, module, etc.)").option("--limit <n>", "Max results (default: 25, max: 50)").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (packageArg, query, options) => {
3329
+ program.command("search <package> [query]").summary("Full-text search in package code").description(SEARCH_DESCRIPTION).option("--kind <kind>", "Filter by chunk type (function, class, module, etc.)").option("--limit <n>", "Max results (default: 25, max: 50)").option("--keywords <words>", "Comma-separated keywords (alternative to query)").option("--match-mode <mode>", "How to combine keywords: or (any) or and (all)").option("--file-path <prefix>", "Filter to files matching path prefix").option("--mode <mode>", "Response detail: summary or detailed").option("--wait <ms>", "Max ms to wait for indexing (0-30000)").option("--json", "Output as JSON").action(async (packageArg, query, options) => {
2935
3330
  await withCliErrorHandling(options.json ?? false, async () => {
2936
3331
  const deps = await createContainer();
2937
3332
  await codeSearchAction(packageArg, query, options, deps);
@@ -6006,6 +6401,14 @@ function toSymbolKind(kind) {
6006
6401
  return upper;
6007
6402
  return;
6008
6403
  }
6404
+ function toMatchMode(mode) {
6405
+ if (!mode)
6406
+ return;
6407
+ const upper = mode.toUpperCase();
6408
+ if (upper === "OR" || upper === "AND")
6409
+ return upper;
6410
+ return;
6411
+ }
6009
6412
  function toListScope(scope) {
6010
6413
  if (!scope)
6011
6414
  return;
@@ -6252,8 +6655,44 @@ function createFindSymbolTool(pkgseerService) {
6252
6655
  }
6253
6656
  };
6254
6657
  }
6255
- // src/tools/list-package-docs.ts
6658
+ // src/tools/grep-repo-file.ts
6659
+ import { z as z8 } from "zod";
6256
6660
  var argsSchema6 = {
6661
+ registry: schemas.registry,
6662
+ package_name: schemas.packageName.describe("Name of the package to search within"),
6663
+ file_path: z8.string().describe("Path to the file (relative to package root)"),
6664
+ pattern: z8.string().max(200).describe("Case-insensitive substring to search for (max 200 chars)"),
6665
+ context_lines: z8.number().int().min(0).max(10).optional().describe("Lines of context around each match (max 10)"),
6666
+ max_matches: z8.number().int().min(1).max(200).optional().describe("Maximum matches to return (max 200)"),
6667
+ version: schemas.version,
6668
+ wait_timeout_ms: schemas.waitTimeoutMs
6669
+ };
6670
+ function createGrepRepoFileTool(pkgseerService) {
6671
+ return {
6672
+ name: "grep_repo_file",
6673
+ description: "Search for a pattern within a file in a package's repository. " + "Case-insensitive substring matching. Returns matching lines with context. " + "Faster than fetch_code_context for large files. " + "Use list_repo_files to discover available file paths.",
6674
+ schema: argsSchema6,
6675
+ handler: async (args, _extra) => {
6676
+ return withErrorHandling("grep repo file", async () => {
6677
+ const result = await pkgseerService.grepRepoFile(toGraphQLRegistry2(args.registry), args.package_name, args.file_path, args.pattern, {
6678
+ contextLines: args.context_lines,
6679
+ maxMatches: args.max_matches,
6680
+ version: args.version,
6681
+ waitTimeoutMs: args.wait_timeout_ms
6682
+ });
6683
+ const graphqlError = handleGraphQLErrors(result.errors);
6684
+ if (graphqlError)
6685
+ return graphqlError;
6686
+ if (!result.data.grepRepoFile) {
6687
+ return notFoundError(args.package_name, args.registry);
6688
+ }
6689
+ return textResult(JSON.stringify(result.data.grepRepoFile, null, 2));
6690
+ });
6691
+ }
6692
+ };
6693
+ }
6694
+ // src/tools/list-package-docs.ts
6695
+ var argsSchema7 = {
6257
6696
  registry: schemas.registry,
6258
6697
  package_name: schemas.packageName.describe("Name of the package to list documentation for"),
6259
6698
  version: schemas.version
@@ -6262,7 +6701,7 @@ function createListPackageDocsTool(pkgseerService) {
6262
6701
  return {
6263
6702
  name: "list_package_docs",
6264
6703
  description: "Discover available documentation pages for a package. Start here before fetching or searching docs. " + "Returns: page titles, unique IDs (needed for fetch_package_doc), word counts, and update timestamps. " + "Workflow: list_package_docs → fetch_package_doc for full content, or search_package_docs to find specific topics.",
6265
- schema: argsSchema6,
6704
+ schema: argsSchema7,
6266
6705
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
6267
6706
  return withErrorHandling("list package documentation", async () => {
6268
6707
  const result = await pkgseerService.listPackageDocs(toGraphQLRegistry2(registry), package_name, version2);
@@ -6277,17 +6716,51 @@ function createListPackageDocsTool(pkgseerService) {
6277
6716
  }
6278
6717
  };
6279
6718
  }
6719
+ // src/tools/list-repo-files.ts
6720
+ import { z as z9 } from "zod";
6721
+ var argsSchema8 = {
6722
+ registry: schemas.registry,
6723
+ package_name: schemas.packageName.describe("Name of the package to list files for"),
6724
+ version: schemas.version,
6725
+ path_prefix: z9.string().optional().describe("Filter to files under this path (e.g., 'src/')"),
6726
+ limit: z9.number().int().min(1).max(1000).optional().describe("Max files to return (max 1000)"),
6727
+ wait_timeout_ms: schemas.waitTimeoutMs
6728
+ };
6729
+ function createListRepoFilesTool(pkgseerService) {
6730
+ return {
6731
+ name: "list_repo_files",
6732
+ description: "List files in a package's indexed repository. " + "Returns file paths, names, languages, types (source/doc/config), and sizes. " + "Use path_prefix to filter by directory (e.g., 'src/'). " + "Useful for exploring package structure before searching or reading code.",
6733
+ schema: argsSchema8,
6734
+ handler: async (args, _extra) => {
6735
+ return withErrorHandling("list repo files", async () => {
6736
+ const result = await pkgseerService.listRepoFiles(toGraphQLRegistry2(args.registry), args.package_name, {
6737
+ version: args.version,
6738
+ pathPrefix: args.path_prefix,
6739
+ limit: args.limit,
6740
+ waitTimeoutMs: args.wait_timeout_ms
6741
+ });
6742
+ const graphqlError = handleGraphQLErrors(result.errors);
6743
+ if (graphqlError)
6744
+ return graphqlError;
6745
+ if (!result.data.listRepoFiles) {
6746
+ return notFoundError(args.package_name, args.registry);
6747
+ }
6748
+ return textResult(JSON.stringify(result.data.listRepoFiles, null, 2));
6749
+ });
6750
+ }
6751
+ };
6752
+ }
6280
6753
  // src/tools/list-symbols.ts
6281
- import { z as z8 } from "zod";
6282
- var argsSchema7 = {
6754
+ import { z as z10 } from "zod";
6755
+ var argsSchema9 = {
6283
6756
  registry: schemas.registry,
6284
6757
  package_name: schemas.packageName.describe("Name of the package to browse symbols for"),
6285
- scope: z8.enum(["exports", "file"]).describe("Browsing scope: 'exports' for public API, 'file' for all symbols in a specific file"),
6286
- file_path: z8.string().max(1000).optional().describe("File path within the repo (required when scope=file)"),
6287
- namespace: z8.string().max(500).optional().describe("Filter by namespace prefix (e.g., 'React' to see React.*)"),
6288
- public_only: z8.boolean().optional().describe("Only return public symbols"),
6289
- include_popularity: z8.boolean().optional().describe("Include callerCount for each symbol (slightly slower)"),
6290
- limit: z8.number().int().min(1).max(100).optional().describe("Max symbols to return (max 100)"),
6758
+ scope: z10.enum(["exports", "file"]).describe("Browsing scope: 'exports' for public API, 'file' for all symbols in a specific file"),
6759
+ file_path: z10.string().max(1000).optional().describe("File path within the repo (required when scope=file)"),
6760
+ namespace: z10.string().max(500).optional().describe("Filter by namespace prefix (e.g., 'React' to see React.*)"),
6761
+ public_only: z10.boolean().optional().describe("Only return public symbols"),
6762
+ include_popularity: z10.boolean().optional().describe("Include callerCount for each symbol (slightly slower)"),
6763
+ limit: z10.number().int().min(1).max(100).optional().describe("Max symbols to return (max 100)"),
6291
6764
  version: schemas.version,
6292
6765
  mode: schemas.navigationMode,
6293
6766
  wait_timeout_ms: schemas.waitTimeoutMs
@@ -6296,7 +6769,7 @@ function createListSymbolsTool(pkgseerService) {
6296
6769
  return {
6297
6770
  name: "list_symbols",
6298
6771
  description: "Browse symbols in a package by scope. " + "Use scope='exports' for public API with namespace grouping and optional popularity. " + "Use scope='file' with file_path for all symbols in a specific file. " + "Returns symbols with IDs, names, qualified paths, kinds, and file locations. " + "Use find_symbol(name=<symbol>, include_code=true) to get source for any listed symbol.",
6299
- schema: argsSchema7,
6772
+ schema: argsSchema9,
6300
6773
  handler: async (args, _extra) => {
6301
6774
  return withErrorHandling("list symbols", async () => {
6302
6775
  const scope = toListScope(args.scope);
@@ -6333,13 +6806,13 @@ function createListSymbolsTool(pkgseerService) {
6333
6806
  };
6334
6807
  }
6335
6808
  // src/tools/package-dependencies.ts
6336
- import { z as z9 } from "zod";
6337
- var argsSchema8 = {
6809
+ import { z as z11 } from "zod";
6810
+ var argsSchema10 = {
6338
6811
  registry: schemas.registry,
6339
6812
  package_name: schemas.packageName.describe("Name of the package to retrieve dependencies for"),
6340
6813
  version: schemas.version,
6341
- include_transitive: z9.boolean().optional().describe("Whether to include transitive dependency DAG"),
6342
- max_depth: z9.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
6814
+ include_transitive: z11.boolean().optional().describe("Whether to include transitive dependency DAG"),
6815
+ max_depth: z11.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
6343
6816
  };
6344
6817
  function decodeDag(rawDag) {
6345
6818
  if (!rawDag || typeof rawDag !== "object")
@@ -6417,7 +6890,7 @@ function createPackageDependenciesTool(pkgseerService) {
6417
6890
  return {
6418
6891
  name: "package_dependencies",
6419
6892
  description: "Analyze package dependencies. By default returns direct dependencies only. " + "Set include_transitive=true to get the full dependency tree (use max_depth to limit large trees). " + "Returns: dependency names, version constraints, and for transitive deps, a graph with depth levels. " + "Use this to understand complexity before adding a package, or to find nested dependencies.",
6420
- schema: argsSchema8,
6893
+ schema: argsSchema10,
6421
6894
  handler: async ({ registry, package_name, version: version2, include_transitive, max_depth }, _extra) => {
6422
6895
  return withErrorHandling("fetch package dependencies", async () => {
6423
6896
  const result = await pkgseerService.getPackageDependencies(toGraphQLRegistry2(registry), package_name, version2, include_transitive, max_depth);
@@ -6455,13 +6928,13 @@ function createPackageDependenciesTool(pkgseerService) {
6455
6928
  };
6456
6929
  }
6457
6930
  // src/tools/package-imports.ts
6458
- import { z as z10 } from "zod";
6459
- var argsSchema9 = {
6931
+ import { z as z12 } from "zod";
6932
+ var argsSchema11 = {
6460
6933
  registry: schemas.registry,
6461
6934
  package_name: schemas.packageName.describe("Name of the package to get imports for"),
6462
- file_path: z10.string().max(1000).optional().describe("Filter to a specific file path"),
6463
- resolved_only: z10.boolean().optional().describe("Only return imports resolved to a known package"),
6464
- limit: z10.number().int().min(1).max(500).optional().describe("Max imports to return"),
6935
+ file_path: z12.string().max(1000).optional().describe("Filter to a specific file path"),
6936
+ resolved_only: z12.boolean().optional().describe("Only return imports resolved to a known package"),
6937
+ limit: z12.number().int().min(1).max(500).optional().describe("Max imports to return"),
6465
6938
  version: schemas.version,
6466
6939
  mode: schemas.navigationMode,
6467
6940
  wait_timeout_ms: schemas.waitTimeoutMs
@@ -6470,7 +6943,7 @@ function createPackageImportsTool(pkgseerService) {
6470
6943
  return {
6471
6944
  name: "package_imports",
6472
6945
  description: "Get import statements in a package. " + "Returns parsed import/require/use statements with optional resolution to known packages. " + "Use resolved_only=true to see only imports that map to packages in the PkgSeer registry. " + "Includes: source path, imported names, and de-duplicated dependency list.",
6473
- schema: argsSchema9,
6946
+ schema: argsSchema11,
6474
6947
  handler: async (args, _extra) => {
6475
6948
  return withErrorHandling("fetch package imports", async () => {
6476
6949
  const result = await pkgseerService.packageImports(toGraphQLRegistry2(args.registry), args.package_name, {
@@ -6493,7 +6966,7 @@ function createPackageImportsTool(pkgseerService) {
6493
6966
  };
6494
6967
  }
6495
6968
  // src/tools/package-quality.ts
6496
- var argsSchema10 = {
6969
+ var argsSchema12 = {
6497
6970
  registry: schemas.registry,
6498
6971
  package_name: schemas.packageName.describe("Name of the package to analyze"),
6499
6972
  version: schemas.version
@@ -6502,7 +6975,7 @@ function createPackageQualityTool(pkgseerService) {
6502
6975
  return {
6503
6976
  name: "package_quality",
6504
6977
  description: "Evaluate package maintenance health and code quality. Use this to assess if a package is well-maintained " + "before adding it as a dependency. Returns: overall score (0-100), category scores (documentation, " + "testing, community, maintenance), and individual rule results with pass/fail status. " + "Useful for comparing quality between alternative packages.",
6505
- schema: argsSchema10,
6978
+ schema: argsSchema12,
6506
6979
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
6507
6980
  return withErrorHandling("fetch package quality", async () => {
6508
6981
  const result = await pkgseerService.getPackageQuality(toGraphQLRegistry2(registry), package_name, version2);
@@ -6518,7 +6991,7 @@ function createPackageQualityTool(pkgseerService) {
6518
6991
  };
6519
6992
  }
6520
6993
  // src/tools/package-summary.ts
6521
- var argsSchema11 = {
6994
+ var argsSchema13 = {
6522
6995
  registry: schemas.registry,
6523
6996
  package_name: schemas.packageName.describe("Name of the package to retrieve summary for")
6524
6997
  };
@@ -6526,7 +6999,7 @@ function createPackageSummaryTool(pkgseerService) {
6526
6999
  return {
6527
7000
  name: "package_summary",
6528
7001
  description: "Get a comprehensive overview of a package. Use this as your first tool when researching a package. " + "Returns: description, latest version, license, repository URL, download stats, " + "active security advisories count, and quickstart/installation instructions. " + "For deeper analysis, follow up with package_quality (maintenance health) or " + "package_vulnerabilities (security details).",
6529
- schema: argsSchema11,
7002
+ schema: argsSchema13,
6530
7003
  handler: async ({ registry, package_name }, _extra) => {
6531
7004
  return withErrorHandling("fetch package summary", async () => {
6532
7005
  const result = await pkgseerService.getPackageSummary(toGraphQLRegistry2(registry), package_name);
@@ -6542,7 +7015,7 @@ function createPackageSummaryTool(pkgseerService) {
6542
7015
  };
6543
7016
  }
6544
7017
  // src/tools/package-vulnerabilities.ts
6545
- var argsSchema12 = {
7018
+ var argsSchema14 = {
6546
7019
  registry: schemas.registry,
6547
7020
  package_name: schemas.packageName.describe("Name of the package to inspect for vulnerabilities"),
6548
7021
  version: schemas.version
@@ -6551,7 +7024,7 @@ function createPackageVulnerabilitiesTool(pkgseerService) {
6551
7024
  return {
6552
7025
  name: "package_vulnerabilities",
6553
7026
  description: "Check security vulnerabilities for a package. Use this before adding dependencies or when auditing existing ones. " + "Returns: list of CVEs/advisories with severity levels, affected version ranges, fixed versions, " + "and upgrade recommendations. If version is specified, shows only vulnerabilities affecting that version.",
6554
- schema: argsSchema12,
7027
+ schema: argsSchema14,
6555
7028
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
6556
7029
  return withErrorHandling("fetch package vulnerabilities", async () => {
6557
7030
  const result = await pkgseerService.getPackageVulnerabilities(toGraphQLRegistry2(registry), package_name, version2);
@@ -6567,19 +7040,19 @@ function createPackageVulnerabilitiesTool(pkgseerService) {
6567
7040
  };
6568
7041
  }
6569
7042
  // src/tools/search.ts
6570
- import { z as z11 } from "zod";
6571
- var packageInputSchema2 = z11.object({
7043
+ import { z as z13 } from "zod";
7044
+ var packageInputSchema2 = z13.object({
6572
7045
  registry: schemas.registry,
6573
- name: z11.string().min(1).describe("Package name"),
6574
- version: z11.string().optional().describe("Specific version (defaults to latest)")
7046
+ name: z13.string().min(1).describe("Package name"),
7047
+ version: z13.string().optional().describe("Specific version (defaults to latest)")
6575
7048
  });
6576
7049
  var DEFAULT_AGENT_WAIT_TIMEOUT_MS = 1e4;
6577
- var argsSchema13 = {
6578
- packages: z11.array(packageInputSchema2).min(1).max(20).describe("Packages to search (1-20). Each package needs registry and name."),
6579
- query: z11.string().min(1).describe("Search query - natural language or keywords"),
6580
- mode: z11.enum(["all", "code", "docs"]).optional().describe('Search mode: "all" (default), "code" only, or "docs" only'),
6581
- limit: z11.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)"),
6582
- waitTimeoutMs: z11.number().int().min(0).max(60000).optional().describe("Max milliseconds to wait for indexing (default: 10000, 0=immediate)")
7050
+ var argsSchema15 = {
7051
+ packages: z13.array(packageInputSchema2).min(1).max(20).describe("Packages to search (1-20). Each package needs registry and name."),
7052
+ query: z13.string().min(1).describe("Search query - natural language or keywords"),
7053
+ mode: z13.enum(["all", "code", "docs"]).optional().describe('Search mode: "all" (default), "code" only, or "docs" only'),
7054
+ limit: z13.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)"),
7055
+ waitTimeoutMs: z13.number().int().min(0).max(60000).optional().describe("Max milliseconds to wait for indexing (default: 10000, 0=immediate)")
6583
7056
  };
6584
7057
  function toSearchMode2(mode) {
6585
7058
  if (!mode)
@@ -6618,7 +7091,7 @@ function createSearchTool(pkgseerService) {
6618
7091
  return {
6619
7092
  name: "search",
6620
7093
  description: "Search code and documentation across packages. Returns functions, classes, and doc pages " + "matching your query. Use mode='code' for code only, mode='docs' for docs only, or " + "mode='all' (default). Provide 1-20 packages. Results include relevance scores and snippets. " + "For full source of code results, use find_symbol(name=<title>, include_code=true). " + "If packages need indexing, waits up to waitTimeoutMs (default 10s) or returns searchRef — use search_status to poll.",
6621
- schema: argsSchema13,
7094
+ schema: argsSchema15,
6622
7095
  handler: async ({ packages, query, mode, limit, waitTimeoutMs }, _extra) => {
6623
7096
  return withErrorHandling("search packages", async () => {
6624
7097
  const normalizedQuery = query.trim();
@@ -6670,21 +7143,21 @@ function createSearchTool(pkgseerService) {
6670
7143
  };
6671
7144
  }
6672
7145
  // src/tools/search-project-docs.ts
6673
- import { z as z12 } from "zod";
6674
- var argsSchema14 = {
6675
- project_directory: z12.string().optional().describe("Directory to search for pkgseer.yml configuration. " + "Use this to specify which project's context to use when the MCP server " + "is installed at user level. Defaults to MCP server's working directory."),
6676
- project: z12.string().optional().describe("Project name to search. Overrides value from pkgseer.yml if provided."),
6677
- terms: z12.array(z12.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
6678
- match_mode: z12.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
6679
- include_snippets: z12.boolean().optional().describe("Include content excerpts around matches"),
6680
- limit: z12.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
7146
+ import { z as z14 } from "zod";
7147
+ var argsSchema16 = {
7148
+ project_directory: z14.string().optional().describe("Directory to search for pkgseer.yml configuration. " + "Use this to specify which project's context to use when the MCP server " + "is installed at user level. Defaults to MCP server's working directory."),
7149
+ project: z14.string().optional().describe("Project name to search. Overrides value from pkgseer.yml if provided."),
7150
+ terms: z14.array(z14.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
7151
+ match_mode: z14.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
7152
+ include_snippets: z14.boolean().optional().describe("Include content excerpts around matches"),
7153
+ limit: z14.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
6681
7154
  };
6682
7155
  function createSearchProjectDocsTool(deps) {
6683
7156
  const { pkgseerService, configService, defaultProjectDir } = deps;
6684
7157
  return {
6685
7158
  name: "search_project_docs",
6686
7159
  description: "Searches documentation across all dependencies in a PkgSeer project using search terms and match modes (any/all). Returns ranked results from multiple packages. Use project_directory to specify which project's context to use, or project to search a specific project directly.",
6687
- schema: argsSchema14,
7160
+ schema: argsSchema16,
6688
7161
  handler: async ({
6689
7162
  project_directory,
6690
7163
  project,
@@ -6730,15 +7203,15 @@ function createSearchProjectDocsTool(deps) {
6730
7203
  };
6731
7204
  }
6732
7205
  // src/tools/search-status.ts
6733
- import { z as z13 } from "zod";
6734
- var argsSchema15 = {
6735
- searchRef: z13.string().min(1).describe("Search reference from a previous incomplete search")
7206
+ import { z as z15 } from "zod";
7207
+ var argsSchema17 = {
7208
+ searchRef: z15.string().min(1).describe("Search reference from a previous incomplete search")
6736
7209
  };
6737
7210
  function createSearchStatusTool(pkgseerService) {
6738
7211
  return {
6739
7212
  name: "search_status",
6740
7213
  description: "Check the status of an async search and get results if complete. " + "Use after search returns completed=false with a searchRef. Sessions expire after 1 hour. " + "Returns status (PENDING, INDEXING, SEARCHING, COMPLETED, TIMEOUT, FAILED), " + "progress info (packagesReady/packagesTotal), and results when COMPLETED.",
6741
- schema: argsSchema15,
7214
+ schema: argsSchema17,
6742
7215
  handler: async ({ searchRef }, _extra) => {
6743
7216
  return withErrorHandling("check search status", async () => {
6744
7217
  const progressResult = await pkgseerService.getSearchProgress(searchRef);
@@ -6779,12 +7252,14 @@ function createSearchStatusTool(pkgseerService) {
6779
7252
  };
6780
7253
  }
6781
7254
  // src/tools/search-symbols.ts
6782
- import { z as z14 } from "zod";
6783
- var argsSchema16 = {
7255
+ import { z as z16 } from "zod";
7256
+ var argsSchema18 = {
6784
7257
  registry: schemas.registry,
6785
7258
  package_name: schemas.packageName.describe("Name of the package to search within"),
6786
- query: z14.string().max(500).describe("Search query for full-text code search (max 500 chars)"),
6787
- kind: z14.enum([
7259
+ query: z16.string().max(500).optional().describe("Search query for full-text code search (max 500 chars). Required if keywords not provided."),
7260
+ keywords: z16.array(z16.string()).max(20).optional().describe("Search keywords (max 20). Combined using match_mode. Alternative to query."),
7261
+ match_mode: z16.enum(["or", "and"]).optional().describe("How to combine keywords: or (any match) or and (all must match)"),
7262
+ kind: z16.enum([
6788
7263
  "function",
6789
7264
  "method",
6790
7265
  "class",
@@ -6793,20 +7268,25 @@ var argsSchema16 = {
6793
7268
  "type",
6794
7269
  "doc_section"
6795
7270
  ]).optional().describe("Filter results by code chunk type"),
7271
+ file_path: z16.string().optional().describe("Filter results to files whose path starts with this value (e.g., 'src/' for a directory)"),
6796
7272
  version: schemas.version,
6797
- limit: z14.number().int().min(1).max(50).optional().describe("Max results to return (max 50)"),
7273
+ limit: z16.number().int().min(1).max(50).optional().describe("Max results to return (max 50)"),
6798
7274
  mode: schemas.navigationMode,
6799
7275
  wait_timeout_ms: schemas.waitTimeoutMs
6800
7276
  };
6801
7277
  function createSearchSymbolsTool(pkgseerService) {
6802
7278
  return {
6803
7279
  name: "search_symbols",
6804
- description: "Full-text search within package code. " + "Searches across functions, classes, modules, and doc sections. " + "Returns matching code chunks with name, type, file path, line numbers, and content previews. " + "Follow-up: use find_symbol(name=<result.name>, include_code=true) to get full source.",
6805
- schema: argsSchema16,
7280
+ description: "Full-text search within package code. " + "Searches across functions, classes, modules, and doc sections. " + "Supports query string or keyword list with match_mode. " + "Use file_path to filter by directory (e.g., 'src/'). " + "Returns matching code chunks with name, type, file path, line numbers, and content previews. " + "Follow-up: use find_symbol(name=<result.name>, include_code=true) to get full source.",
7281
+ schema: argsSchema18,
6806
7282
  handler: async (args, _extra) => {
6807
7283
  return withErrorHandling("search symbols", async () => {
6808
- const result = await pkgseerService.searchSymbols(toGraphQLRegistry2(args.registry), args.package_name, args.query, {
7284
+ const result = await pkgseerService.searchSymbols(toGraphQLRegistry2(args.registry), args.package_name, {
7285
+ query: args.query,
7286
+ keywords: args.keywords,
7287
+ matchMode: toMatchMode(args.match_mode),
6809
7288
  kind: toSymbolKind(args.kind),
7289
+ filePath: args.file_path,
6810
7290
  version: args.version,
6811
7291
  limit: args.limit,
6812
7292
  mode: toNavigationMode(args.mode),
@@ -6824,13 +7304,13 @@ function createSearchSymbolsTool(pkgseerService) {
6824
7304
  };
6825
7305
  }
6826
7306
  // src/tools/symbol-callees.ts
6827
- import { z as z15 } from "zod";
6828
- var argsSchema17 = {
7307
+ import { z as z17 } from "zod";
7308
+ var argsSchema19 = {
6829
7309
  symbol: schemas.symbolReference.describe("Symbol to find callees for. Provide either symbol_id or registry + package_name + symbol_name."),
6830
- max_depth: z15.number().int().min(1).max(5).optional().describe("BFS traversal depth (1 = direct callees only, 2+ = transitive, max 5)"),
6831
- limit: z15.number().int().min(1).max(100).optional().describe("Maximum dependency entries to return"),
6832
- include_external: z15.boolean().optional().describe("Include calls to symbols in other packages"),
6833
- include_builtins: z15.boolean().optional().describe("Include calls to built-in/standard library functions (console, Math, etc.)"),
7310
+ max_depth: z17.number().int().min(1).max(5).optional().describe("BFS traversal depth (1 = direct callees only, 2+ = transitive, max 5)"),
7311
+ limit: z17.number().int().min(1).max(100).optional().describe("Maximum dependency entries to return"),
7312
+ include_external: z17.boolean().optional().describe("Include calls to symbols in other packages"),
7313
+ include_builtins: z17.boolean().optional().describe("Include calls to built-in/standard library functions (console, Math, etc.)"),
6834
7314
  mode: schemas.navigationMode,
6835
7315
  wait_timeout_ms: schemas.waitTimeoutMs
6836
7316
  };
@@ -6838,7 +7318,7 @@ function createSymbolCalleesTool(pkgseerService) {
6838
7318
  return {
6839
7319
  name: "symbol_callees",
6840
7320
  description: "Find what a symbol calls (its dependencies). " + "Returns direct callees at max_depth=1, or transitive call chains at higher depths. " + "Includes callee names, qualified paths, file locations, call lines, and external package references. " + "Get symbol_id from find_symbol or list_symbols results. See symbol_callers for the reverse.",
6841
- schema: argsSchema17,
7321
+ schema: argsSchema19,
6842
7322
  handler: async (args, _extra) => {
6843
7323
  return withErrorHandling("find symbol callees", async () => {
6844
7324
  const symbolInput = toSymbolReferenceInput(args.symbol);
@@ -6865,11 +7345,11 @@ function createSymbolCalleesTool(pkgseerService) {
6865
7345
  };
6866
7346
  }
6867
7347
  // src/tools/symbol-callers.ts
6868
- import { z as z16 } from "zod";
6869
- var argsSchema18 = {
7348
+ import { z as z18 } from "zod";
7349
+ var argsSchema20 = {
6870
7350
  symbol: schemas.symbolReference.describe("Symbol to find callers for. Provide either symbol_id or registry + package_name + symbol_name."),
6871
- same_package_only: z16.boolean().optional().describe("Only return callers from the same package"),
6872
- limit: z16.number().int().min(1).max(100).optional().describe("Maximum entries to return"),
7351
+ same_package_only: z18.boolean().optional().describe("Only return callers from the same package"),
7352
+ limit: z18.number().int().min(1).max(100).optional().describe("Maximum entries to return"),
6873
7353
  mode: schemas.navigationMode,
6874
7354
  wait_timeout_ms: schemas.waitTimeoutMs
6875
7355
  };
@@ -6877,7 +7357,7 @@ function createSymbolCallersTool(pkgseerService) {
6877
7357
  return {
6878
7358
  name: "symbol_callers",
6879
7359
  description: "Find what calls a symbol (its dependents/callers). " + "Returns the target symbol and a list of callers with file locations and call line numbers. " + "Use same_package_only to limit to callers within the same package. " + "Get symbol_id from find_symbol or list_symbols results. See symbol_callees for the reverse.",
6880
- schema: argsSchema18,
7360
+ schema: argsSchema20,
6881
7361
  handler: async (args, _extra) => {
6882
7362
  return withErrorHandling("find symbol callers", async () => {
6883
7363
  const symbolInput = toSymbolReferenceInput(args.symbol);
@@ -6902,26 +7382,26 @@ function createSymbolCallersTool(pkgseerService) {
6902
7382
  };
6903
7383
  }
6904
7384
  // src/tools/trigger-indexing.ts
6905
- import { z as z17 } from "zod";
6906
- var packageInputSchema3 = z17.object({
7385
+ import { z as z19 } from "zod";
7386
+ var packageInputSchema3 = z19.object({
6907
7387
  registry: schemas.registry,
6908
- name: z17.string().max(255).describe("Package name"),
6909
- version: z17.string().max(100).optional().describe("Specific version to index")
7388
+ name: z19.string().max(255).describe("Package name"),
7389
+ version: z19.string().max(100).optional().describe("Specific version to index")
6910
7390
  });
6911
- var repositoryInputSchema = z17.object({
6912
- url: z17.string().max(2000).describe("GitHub repository URL"),
6913
- ref: z17.string().max(200).optional().describe("Git ref to index (tag, branch, commit). Defaults to HEAD"),
6914
- discover_packages: z17.boolean().optional().describe("If true, discover packages published from this repo and trigger their metadata fetch")
7391
+ var repositoryInputSchema = z19.object({
7392
+ url: z19.string().max(2000).describe("GitHub repository URL"),
7393
+ ref: z19.string().max(200).optional().describe("Git ref to index (tag, branch, commit). Defaults to HEAD"),
7394
+ discover_packages: z19.boolean().optional().describe("If true, discover packages published from this repo and trigger their metadata fetch")
6915
7395
  });
6916
- var argsSchema19 = {
6917
- packages: z17.array(packageInputSchema3).max(100).optional().describe("Package versions to index (max 100)"),
6918
- repositories: z17.array(repositoryInputSchema).max(50).optional().describe("Repository URLs to index (max 50)")
7396
+ var argsSchema21 = {
7397
+ packages: z19.array(packageInputSchema3).max(100).optional().describe("Package versions to index (max 100)"),
7398
+ repositories: z19.array(repositoryInputSchema).max(50).optional().describe("Repository URLs to index (max 50)")
6919
7399
  };
6920
7400
  function createTriggerIndexingTool(pkgseerService) {
6921
7401
  return {
6922
7402
  name: "trigger_indexing",
6923
7403
  description: "Pre-warm PkgSeer indexes by triggering indexing for packages and/or repositories. " + "Fire-and-forget: triggers jobs and returns immediately with accepted/skipped counts. " + "Use before search requests to ensure packages are indexed. " + "Rate limited to 10 requests per minute.",
6924
- schema: argsSchema19,
7404
+ schema: argsSchema21,
6925
7405
  handler: async (args, _extra) => {
6926
7406
  return withErrorHandling("trigger indexing", async () => {
6927
7407
  const packages = args.packages?.map((p) => ({
@@ -6957,6 +7437,50 @@ function createTriggerIndexingTool(pkgseerService) {
6957
7437
  }
6958
7438
  };
6959
7439
  }
7440
+ // src/tools/version-diff.ts
7441
+ import { z as z20 } from "zod";
7442
+ var argsSchema22 = {
7443
+ registry: schemas.registry,
7444
+ package_name: schemas.packageName.describe("Name of the package to compare versions for"),
7445
+ from_version: z20.string().max(100).describe("Source version (git ref, e.g., 'v4.18.2')"),
7446
+ to_version: z20.string().max(100).describe("Target version (git ref, e.g., 'v5.0.0')"),
7447
+ include_private: z20.boolean().optional().describe("Include private/non-exported symbols in the diff"),
7448
+ kind: z20.enum([
7449
+ "function",
7450
+ "method",
7451
+ "class",
7452
+ "module",
7453
+ "interface",
7454
+ "type",
7455
+ "doc_section"
7456
+ ]).optional().describe("Filter by symbol kind"),
7457
+ limit: z20.number().int().min(1).max(500).optional().describe("Max changes to return (max 500). Summary always reflects full counts."),
7458
+ wait_timeout_ms: schemas.waitTimeoutMs
7459
+ };
7460
+ function createVersionDiffTool(pkgseerService) {
7461
+ return {
7462
+ name: "version_diff",
7463
+ description: "Compare symbols between two package versions. " + "Detects added, removed, moved, and modified symbols with severity " + "(breaking, behavior_change, non_breaking, internal). " + "Returns summary counts and per-symbol changes with file locations.",
7464
+ schema: argsSchema22,
7465
+ handler: async (args, _extra) => {
7466
+ return withErrorHandling("version diff", async () => {
7467
+ const result = await pkgseerService.versionDiff(toGraphQLRegistry2(args.registry), args.package_name, args.from_version, args.to_version, {
7468
+ includePrivate: args.include_private,
7469
+ kind: toSymbolKind(args.kind),
7470
+ limit: args.limit,
7471
+ waitTimeoutMs: args.wait_timeout_ms
7472
+ });
7473
+ const graphqlError = handleGraphQLErrors(result.errors);
7474
+ if (graphqlError)
7475
+ return graphqlError;
7476
+ if (!result.data.versionDiff) {
7477
+ return notFoundError(args.package_name, args.registry);
7478
+ }
7479
+ return textResult(JSON.stringify(result.data.versionDiff, null, 2));
7480
+ });
7481
+ }
7482
+ };
7483
+ }
6960
7484
  // src/commands/mcp.ts
6961
7485
  var TOOL_FACTORIES = {
6962
7486
  package_summary: ({ pkgseerService }) => createPackageSummaryTool(pkgseerService),
@@ -6981,7 +7505,10 @@ var TOOL_FACTORIES = {
6981
7505
  symbol_callees: ({ pkgseerService }) => createSymbolCalleesTool(pkgseerService),
6982
7506
  package_imports: ({ pkgseerService }) => createPackageImportsTool(pkgseerService),
6983
7507
  call_path: ({ pkgseerService }) => createCallPathTool(pkgseerService),
6984
- trigger_indexing: ({ pkgseerService }) => createTriggerIndexingTool(pkgseerService)
7508
+ trigger_indexing: ({ pkgseerService }) => createTriggerIndexingTool(pkgseerService),
7509
+ list_repo_files: ({ pkgseerService }) => createListRepoFilesTool(pkgseerService),
7510
+ grep_repo_file: ({ pkgseerService }) => createGrepRepoFileTool(pkgseerService),
7511
+ version_diff: ({ pkgseerService }) => createVersionDiffTool(pkgseerService)
6985
7512
  };
6986
7513
  var PUBLIC_READ_TOOLS = [
6987
7514
  "package_summary",
@@ -7001,7 +7528,10 @@ var PUBLIC_READ_TOOLS = [
7001
7528
  "symbol_callees",
7002
7529
  "package_imports",
7003
7530
  "call_path",
7004
- "trigger_indexing"
7531
+ "trigger_indexing",
7532
+ "list_repo_files",
7533
+ "grep_repo_file",
7534
+ "version_diff"
7005
7535
  ];
7006
7536
  var PROJECT_READ_TOOLS = ["search_project_docs"];
7007
7537
  var ALL_TOOLS = [...PUBLIC_READ_TOOLS, ...PROJECT_READ_TOOLS];
@@ -8003,8 +8533,11 @@ Documentation commands:
8003
8533
 
8004
8534
  Code navigation commands:
8005
8535
  pkgseer code find <pkg> [name] Find symbols by name
8006
- pkgseer code search <pkg> <q> Search in package code
8536
+ pkgseer code search <pkg> [q] Search in package code
8007
8537
  pkgseer code list <package> Browse package symbols
8538
+ pkgseer code files <package> List repository files
8539
+ pkgseer code grep <pkg> <f> <p> Search pattern in a file
8540
+ pkgseer code diff <pkg> <v> <v> Compare versions
8008
8541
  pkgseer code callers <symbol> Find what calls a symbol
8009
8542
  pkgseer code callees <symbol> Find what a symbol calls
8010
8543
  pkgseer code imports <package> List package imports
@@ -8038,6 +8571,9 @@ var code = program.command("code").description("Code navigation commands");
8038
8571
  registerCodeFindCommand(code);
8039
8572
  registerCodeSearchCommand(code);
8040
8573
  registerCodeListCommand(code);
8574
+ registerCodeFilesCommand(code);
8575
+ registerCodeGrepCommand(code);
8576
+ registerCodeDiffCommand(code);
8041
8577
  registerCodeCallersCommand(code);
8042
8578
  registerCodeCalleesCommand(code);
8043
8579
  registerCodeImportsCommand(code);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  version
3
- } from "./shared/chunk-d340pysc.js";
3
+ } from "./shared/chunk-w4ht5me5.js";
4
4
  export {
5
5
  version
6
6
  };
@@ -1,4 +1,4 @@
1
1
  // package.json
2
- var version = "0.4.1";
2
+ var version = "0.4.2";
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.4.1",
4
+ "version": "0.4.2",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",