@pkgseer/cli 0.4.1 → 0.4.3

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-d3mfgdr9.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -685,16 +685,26 @@ var FindSymbolDocument = gql`
685
685
  totalMatches
686
686
  hasMore
687
687
  indexedVersion
688
+ diagnostics {
689
+ indexed
690
+ chunkCount
691
+ fileCount
692
+ indexedAt
693
+ hint
694
+ }
688
695
  }
689
696
  }
690
697
  `;
691
698
  var SearchSymbolsDocument = gql`
692
- query SearchSymbols($registry: Registry!, $packageName: String!, $query: String!, $kind: SymbolKind, $version: String, $limit: Int, $mode: NavigationMode, $waitTimeoutMs: Int) {
699
+ 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
700
  searchSymbols(
694
701
  registry: $registry
695
702
  packageName: $packageName
696
703
  query: $query
704
+ keywords: $keywords
705
+ matchMode: $matchMode
697
706
  kind: $kind
707
+ filePath: $filePath
698
708
  version: $version
699
709
  limit: $limit
700
710
  mode: $mode
@@ -708,11 +718,26 @@ var SearchSymbolsDocument = gql`
708
718
  endLine
709
719
  preview
710
720
  contentPreview
721
+ code
711
722
  language
723
+ symbolId
724
+ qualifiedPath
725
+ kind
726
+ arity
727
+ isPublic
728
+ containedSymbols
712
729
  }
713
730
  totalMatches
714
731
  hasMore
715
732
  indexedVersion
733
+ diagnostics {
734
+ indexed
735
+ chunkCount
736
+ fileCount
737
+ indexedAt
738
+ hint
739
+ }
740
+ warning
716
741
  }
717
742
  }
718
743
  `;
@@ -750,7 +775,6 @@ var ListSymbolsDocument = gql`
750
775
  path
751
776
  language
752
777
  }
753
- error
754
778
  }
755
779
  }
756
780
  `;
@@ -913,6 +937,126 @@ var SearchProjectDocsDocument = gql`
913
937
  }
914
938
  }
915
939
  `;
940
+ var ListRepoFilesDocument = gql`
941
+ query ListRepoFiles($registry: Registry!, $packageName: String!, $version: String, $pathPrefix: String, $limit: Int, $waitTimeoutMs: Int) {
942
+ listRepoFiles(
943
+ registry: $registry
944
+ packageName: $packageName
945
+ version: $version
946
+ pathPrefix: $pathPrefix
947
+ limit: $limit
948
+ waitTimeoutMs: $waitTimeoutMs
949
+ ) {
950
+ files {
951
+ path
952
+ name
953
+ language
954
+ fileType
955
+ byteSize
956
+ }
957
+ total
958
+ hasMore
959
+ indexedVersion
960
+ diagnostics {
961
+ indexed
962
+ chunkCount
963
+ fileCount
964
+ indexedAt
965
+ hint
966
+ }
967
+ }
968
+ }
969
+ `;
970
+ var GrepRepoFileDocument = gql`
971
+ query GrepRepoFile($registry: Registry!, $packageName: String!, $filePath: String!, $pattern: String!, $contextLines: Int, $maxMatches: Int, $version: String, $waitTimeoutMs: Int) {
972
+ grepRepoFile(
973
+ registry: $registry
974
+ packageName: $packageName
975
+ filePath: $filePath
976
+ pattern: $pattern
977
+ contextLines: $contextLines
978
+ maxMatches: $maxMatches
979
+ version: $version
980
+ waitTimeoutMs: $waitTimeoutMs
981
+ ) {
982
+ matches {
983
+ lineNumber
984
+ lineContent
985
+ contextBefore
986
+ contextAfter
987
+ }
988
+ totalMatches
989
+ hasMore
990
+ filePath
991
+ language
992
+ totalLines
993
+ indexedVersion
994
+ diagnostics {
995
+ indexed
996
+ chunkCount
997
+ fileCount
998
+ indexedAt
999
+ hint
1000
+ }
1001
+ }
1002
+ }
1003
+ `;
1004
+ var VersionDiffDocument = gql`
1005
+ query VersionDiff($registry: Registry!, $packageName: String!, $fromVersion: String!, $toVersion: String!, $includePrivate: Boolean, $kind: SymbolKind, $limit: Int, $waitTimeoutMs: Int) {
1006
+ versionDiff(
1007
+ registry: $registry
1008
+ packageName: $packageName
1009
+ fromVersion: $fromVersion
1010
+ toVersion: $toVersion
1011
+ includePrivate: $includePrivate
1012
+ kind: $kind
1013
+ limit: $limit
1014
+ waitTimeoutMs: $waitTimeoutMs
1015
+ ) {
1016
+ fromVersion
1017
+ toVersion
1018
+ summary {
1019
+ added
1020
+ removed
1021
+ moved
1022
+ modified
1023
+ unchanged
1024
+ breaking
1025
+ behaviorChange
1026
+ }
1027
+ changes {
1028
+ qualifiedPath
1029
+ kind
1030
+ arity
1031
+ name
1032
+ changeType
1033
+ severity
1034
+ changedFields
1035
+ from {
1036
+ contentHash
1037
+ filePath
1038
+ arity
1039
+ startLine
1040
+ endLine
1041
+ isPublic
1042
+ minArity
1043
+ symbolId
1044
+ }
1045
+ to {
1046
+ contentHash
1047
+ filePath
1048
+ arity
1049
+ startLine
1050
+ endLine
1051
+ isPublic
1052
+ minArity
1053
+ symbolId
1054
+ }
1055
+ }
1056
+ hasMore
1057
+ }
1058
+ }
1059
+ `;
916
1060
  var defaultWrapper = (action, _operationName, _operationType, _variables) => action();
917
1061
  var CliPackageInfoDocumentString = print(CliPackageInfoDocument);
918
1062
  var CliPackageVulnsDocumentString = print(CliPackageVulnsDocument);
@@ -946,6 +1090,9 @@ var SymbolDependentsDocumentString = print(SymbolDependentsDocument);
946
1090
  var PackageImportsDocumentString = print(PackageImportsDocument);
947
1091
  var CallPathDocumentString = print(CallPathDocument);
948
1092
  var SearchProjectDocsDocumentString = print(SearchProjectDocsDocument);
1093
+ var ListRepoFilesDocumentString = print(ListRepoFilesDocument);
1094
+ var GrepRepoFileDocumentString = print(GrepRepoFileDocument);
1095
+ var VersionDiffDocumentString = print(VersionDiffDocument);
949
1096
  function getSdk(client, withWrapper = defaultWrapper) {
950
1097
  return {
951
1098
  CliPackageInfo(variables, requestHeaders) {
@@ -1043,6 +1190,15 @@ function getSdk(client, withWrapper = defaultWrapper) {
1043
1190
  },
1044
1191
  SearchProjectDocs(variables, requestHeaders) {
1045
1192
  return withWrapper((wrappedRequestHeaders) => client.rawRequest(SearchProjectDocsDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "SearchProjectDocs", "query", variables);
1193
+ },
1194
+ ListRepoFiles(variables, requestHeaders) {
1195
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(ListRepoFilesDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "ListRepoFiles", "query", variables);
1196
+ },
1197
+ GrepRepoFile(variables, requestHeaders) {
1198
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(GrepRepoFileDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "GrepRepoFile", "query", variables);
1199
+ },
1200
+ VersionDiff(variables, requestHeaders) {
1201
+ return withWrapper((wrappedRequestHeaders) => client.rawRequest(VersionDiffDocumentString, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "VersionDiff", "query", variables);
1046
1202
  }
1047
1203
  };
1048
1204
  }
@@ -1381,7 +1537,10 @@ var TOOL_NAMES = [
1381
1537
  "symbol_callees",
1382
1538
  "package_imports",
1383
1539
  "call_path",
1384
- "trigger_indexing"
1540
+ "trigger_indexing",
1541
+ "list_repo_files",
1542
+ "grep_repo_file",
1543
+ "version_diff"
1385
1544
  ];
1386
1545
  var ToolNameSchema = z.enum(TOOL_NAMES);
1387
1546
  var SharedConfigFields = {
@@ -1820,12 +1979,15 @@ class PkgseerServiceImpl {
1820
1979
  });
1821
1980
  return { data: result.data, errors: result.errors };
1822
1981
  }
1823
- async searchSymbols(registry, packageName, query, options) {
1982
+ async searchSymbols(registry, packageName, options) {
1824
1983
  const result = await this.client.SearchSymbols({
1825
1984
  registry,
1826
1985
  packageName,
1827
- query,
1986
+ query: options?.query,
1987
+ keywords: options?.keywords,
1988
+ matchMode: options?.matchMode,
1828
1989
  kind: options?.kind,
1990
+ filePath: options?.filePath,
1829
1991
  version: options?.version,
1830
1992
  limit: options?.limit,
1831
1993
  mode: options?.mode,
@@ -1894,6 +2056,43 @@ class PkgseerServiceImpl {
1894
2056
  });
1895
2057
  return { data: result.data, errors: result.errors };
1896
2058
  }
2059
+ async listRepoFiles(registry, packageName, options) {
2060
+ const result = await this.client.ListRepoFiles({
2061
+ registry,
2062
+ packageName,
2063
+ version: options?.version,
2064
+ pathPrefix: options?.pathPrefix,
2065
+ limit: options?.limit,
2066
+ waitTimeoutMs: options?.waitTimeoutMs
2067
+ });
2068
+ return { data: result.data, errors: result.errors };
2069
+ }
2070
+ async grepRepoFile(registry, packageName, filePath, pattern, options) {
2071
+ const result = await this.client.GrepRepoFile({
2072
+ registry,
2073
+ packageName,
2074
+ filePath,
2075
+ pattern,
2076
+ contextLines: options?.contextLines,
2077
+ maxMatches: options?.maxMatches,
2078
+ version: options?.version,
2079
+ waitTimeoutMs: options?.waitTimeoutMs
2080
+ });
2081
+ return { data: result.data, errors: result.errors };
2082
+ }
2083
+ async versionDiff(registry, packageName, fromVersion, toVersion, options) {
2084
+ const result = await this.client.VersionDiff({
2085
+ registry,
2086
+ packageName,
2087
+ fromVersion,
2088
+ toVersion,
2089
+ includePrivate: options?.includePrivate,
2090
+ kind: options?.kind,
2091
+ limit: options?.limit,
2092
+ waitTimeoutMs: options?.waitTimeoutMs
2093
+ });
2094
+ return { data: result.data, errors: result.errors };
2095
+ }
1897
2096
  async triggerIndexing(input) {
1898
2097
  const result = await this.client.TriggerIndexing({ input });
1899
2098
  return { data: result.data, errors: result.errors };
@@ -2263,8 +2462,11 @@ function handleErrors(errors, json) {
2263
2462
  const isNotFound = lower.includes("not found") || lower.includes("does not exist") || lower.includes("unknown");
2264
2463
  const isAuth = lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("token") || lower.includes("authentication") || lower.includes("permission");
2265
2464
  const isRateLimit = lower.includes("rate limit") || lower.includes("too many requests");
2465
+ const isBeingIndexed = lower.includes("being indexed") || lower.includes("retry with waittimeoutms");
2266
2466
  let hint = "";
2267
- if (isTimeout) {
2467
+ if (isBeingIndexed) {
2468
+ hint = " Hint: package is being indexed. Try again with a longer --wait value (e.g., --wait 30000).";
2469
+ } else if (isTimeout) {
2268
2470
  hint = " Hint: lower the limit or narrow the scope, then retry.";
2269
2471
  } else if (isNotFound) {
2270
2472
  hint = " Hint: verify the name/registry and that the resource exists.";
@@ -2302,14 +2504,20 @@ function formatSeverity(severity) {
2302
2504
  return indicators[severity] || severity;
2303
2505
  }
2304
2506
  function extractGraphQLError(error) {
2305
- if (error && typeof error === "object" && "response" in error && error.response && typeof error.response === "object" && "errors" in error.response && Array.isArray(error.response.errors)) {
2306
- const errors = error.response.errors;
2307
- if (errors.length > 0) {
2308
- const messages = errors.map((e) => e.message).filter((m) => typeof m === "string");
2507
+ if (error && typeof error === "object" && "response" in error && error.response && typeof error.response === "object" && "errors" in error.response) {
2508
+ const respErrors = error.response.errors;
2509
+ if (Array.isArray(respErrors)) {
2510
+ const messages = respErrors.map((e) => e.message).filter((m) => typeof m === "string");
2309
2511
  if (messages.length > 0) {
2310
2512
  return messages.join("; ");
2311
2513
  }
2312
2514
  }
2515
+ if (respErrors && typeof respErrors === "object" && !Array.isArray(respErrors) && "detail" in respErrors) {
2516
+ const detail = respErrors.detail;
2517
+ if (typeof detail === "string") {
2518
+ return detail;
2519
+ }
2520
+ }
2313
2521
  }
2314
2522
  if (error && typeof error === "object" && "errors" in error && Array.isArray(error.errors)) {
2315
2523
  const errors = error.errors;
@@ -2358,6 +2566,10 @@ async function withCliErrorHandling(json, fn) {
2358
2566
  }
2359
2567
  const message = error instanceof Error ? error.message : "Unknown error";
2360
2568
  const lower = message.toLowerCase();
2569
+ if (lower.includes("being indexed") || lower.includes("retry with waittimeoutms")) {
2570
+ outputError(`${message}. Hint: package is being indexed. Try again with a longer --wait value (e.g., --wait 30000).`, json);
2571
+ return;
2572
+ }
2361
2573
  if (lower.includes("-32001") || lower.includes("timeout")) {
2362
2574
  outputError(`${message}. Hint: lower the limit or narrow the scope, then retry.`, json);
2363
2575
  return;
@@ -2599,6 +2811,145 @@ function registerCodeCallersCommand(program) {
2599
2811
  });
2600
2812
  });
2601
2813
  }
2814
+ // src/commands/code/diff.ts
2815
+ function formatDiffResult(data) {
2816
+ const lines = [];
2817
+ lines.push(`Version diff: ${data.fromVersion} → ${data.toVersion}`);
2818
+ lines.push("");
2819
+ const s = data.summary;
2820
+ lines.push("Summary:");
2821
+ lines.push(` Added: ${s.added} Removed: ${s.removed} Modified: ${s.modified} Moved: ${s.moved}`);
2822
+ lines.push(` Unchanged: ${s.unchanged} Breaking: ${s.breaking} Behavior changes: ${s.behaviorChange}`);
2823
+ if (data.hasMore) {
2824
+ lines.push(" (more changes available, increase --limit)");
2825
+ }
2826
+ lines.push("");
2827
+ for (const change of data.changes) {
2828
+ const severity = change.severity ? ` [${change.severity}]` : "";
2829
+ const kindLabel = change.kind ? `[${change.kind}]` : "";
2830
+ const name = change.qualifiedPath ?? change.name ?? "unknown";
2831
+ lines.push(`${change.changeType}${severity} ${kindLabel} ${name}`.trim());
2832
+ if (change.from?.filePath) {
2833
+ const loc = change.from.startLine ? `${change.from.filePath}:${change.from.startLine}` : change.from.filePath;
2834
+ lines.push(` From: ${loc}`);
2835
+ }
2836
+ if (change.to?.filePath) {
2837
+ const loc = change.to.startLine ? `${change.to.filePath}:${change.to.startLine}` : change.to.filePath;
2838
+ lines.push(` To: ${loc}`);
2839
+ }
2840
+ if (change.changedFields?.length) {
2841
+ lines.push(` Changed: ${change.changedFields.join(", ")}`);
2842
+ }
2843
+ lines.push("");
2844
+ }
2845
+ return lines.join(`
2846
+ `).trimEnd();
2847
+ }
2848
+ async function codeDiffAction(packageArg, fromVersion, toVersion, options, deps) {
2849
+ const { pkgseerService } = deps;
2850
+ const parsed = parsePackageSpec(packageArg);
2851
+ const registry = toGraphQLRegistry(parsed.registry);
2852
+ const result = await pkgseerService.versionDiff(registry, parsed.name, fromVersion, toVersion, {
2853
+ includePrivate: options.includePrivate,
2854
+ kind: options.kind?.toUpperCase(),
2855
+ limit: parseIntOption(options.limit, "--limit"),
2856
+ waitTimeoutMs: parseWaitTimeout(options.wait)
2857
+ });
2858
+ handleErrors(result.errors, options.json ?? false);
2859
+ if (!result.data.versionDiff) {
2860
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2861
+ return;
2862
+ }
2863
+ if (options.json) {
2864
+ output(result.data.versionDiff, true);
2865
+ } else {
2866
+ console.log(formatDiffResult(result.data.versionDiff));
2867
+ }
2868
+ }
2869
+ var DIFF_DESCRIPTION = `Compare symbols between two package versions.
2870
+
2871
+ Detects added, removed, moved, and modified symbols with severity classification.
2872
+ Returns summary counts and per-symbol changes with file locations.
2873
+
2874
+ Examples:
2875
+ pkgseer code diff npm:express v4.18.2 v5.0.0
2876
+ pkgseer code diff npm:lodash 4.17.20 4.17.21 --include-private
2877
+ pkgseer code diff pypi:requests 2.30.0 2.31.0 --kind function`;
2878
+ function registerCodeDiffCommand(program) {
2879
+ 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) => {
2880
+ await withCliErrorHandling(options.json ?? false, async () => {
2881
+ const deps = await createContainer();
2882
+ await codeDiffAction(packageArg, fromVersion, toVersion, options, deps);
2883
+ });
2884
+ });
2885
+ }
2886
+ // src/commands/code/files.ts
2887
+ function formatFilesResult(data) {
2888
+ const lines = [];
2889
+ lines.push(`${data.total} file(s)${data.hasMore ? " (more available)" : ""}`);
2890
+ if (data.indexedVersion) {
2891
+ lines.push(`Indexed version: ${data.indexedVersion}`);
2892
+ }
2893
+ lines.push("");
2894
+ for (const file of data.files) {
2895
+ const meta = [];
2896
+ if (file.language)
2897
+ meta.push(file.language);
2898
+ if (file.fileType)
2899
+ meta.push(file.fileType);
2900
+ if (file.byteSize != null)
2901
+ meta.push(`${file.byteSize}B`);
2902
+ const suffix = meta.length > 0 ? ` (${meta.join(", ")})` : "";
2903
+ lines.push(`${file.path}${suffix}`);
2904
+ }
2905
+ return lines.join(`
2906
+ `).trimEnd();
2907
+ }
2908
+ async function codeFilesAction(packageArg, options, deps) {
2909
+ const { pkgseerService } = deps;
2910
+ const parsed = parsePackageSpec(packageArg);
2911
+ const registry = toGraphQLRegistry(parsed.registry);
2912
+ const result = await pkgseerService.listRepoFiles(registry, parsed.name, {
2913
+ version: parsed.version,
2914
+ pathPrefix: options.pathPrefix,
2915
+ limit: parseIntOption(options.limit, "--limit"),
2916
+ waitTimeoutMs: parseWaitTimeout(options.wait)
2917
+ });
2918
+ handleErrors(result.errors, options.json ?? false);
2919
+ if (!result.data.listRepoFiles) {
2920
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2921
+ return;
2922
+ }
2923
+ const data = result.data.listRepoFiles;
2924
+ if (options.json) {
2925
+ output(data, true);
2926
+ } else {
2927
+ const formatted = formatFilesResult(data);
2928
+ if (data.files.length === 0 && data.diagnostics?.hint) {
2929
+ console.log(`${formatted}
2930
+ Note: ${data.diagnostics.hint}`);
2931
+ } else {
2932
+ console.log(formatted);
2933
+ }
2934
+ }
2935
+ }
2936
+ var FILES_DESCRIPTION = `List files in a package's indexed repository.
2937
+
2938
+ Returns file paths, names, languages, types, and sizes.
2939
+ Use --path-prefix to filter by directory.
2940
+
2941
+ Examples:
2942
+ pkgseer code files npm:express
2943
+ pkgseer code files npm:express --path-prefix src/
2944
+ pkgseer code files pypi:requests --limit 50`;
2945
+ function registerCodeFilesCommand(program) {
2946
+ 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) => {
2947
+ await withCliErrorHandling(options.json ?? false, async () => {
2948
+ const deps = await createContainer();
2949
+ await codeFilesAction(packageArg, options, deps);
2950
+ });
2951
+ });
2952
+ }
2602
2953
  // src/commands/code/find.ts
2603
2954
  function formatFindResult(data) {
2604
2955
  const lines = [];
@@ -2634,10 +2985,17 @@ async function codeFindAction(packageArg, nameArg, options, deps) {
2634
2985
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2635
2986
  return;
2636
2987
  }
2988
+ const data = result.data.findSymbol;
2637
2989
  if (options.json) {
2638
- output(result.data.findSymbol, true);
2990
+ output(data, true);
2639
2991
  } else {
2640
- console.log(formatFindResult(result.data.findSymbol));
2992
+ const formatted = formatFindResult(data);
2993
+ if (data.symbols.length === 0 && data.diagnostics?.hint) {
2994
+ console.log(`${formatted}
2995
+ Note: ${data.diagnostics.hint}`);
2996
+ } else {
2997
+ console.log(formatted);
2998
+ }
2641
2999
  }
2642
3000
  }
2643
3001
  var FIND_DESCRIPTION = `Find symbols (functions, classes, modules) by name.
@@ -2658,6 +3016,79 @@ function registerCodeFindCommand(program) {
2658
3016
  });
2659
3017
  });
2660
3018
  }
3019
+ // src/commands/code/grep.ts
3020
+ function formatGrepResult(data) {
3021
+ const lines = [];
3022
+ lines.push(`${data.totalMatches} match(es) in ${data.filePath ?? "unknown"}${data.hasMore ? " (more available)" : ""}`);
3023
+ if (data.language)
3024
+ lines.push(`Language: ${data.language}`);
3025
+ if (data.totalLines != null)
3026
+ lines.push(`Total lines: ${data.totalLines}`);
3027
+ if (data.indexedVersion)
3028
+ lines.push(`Indexed version: ${data.indexedVersion}`);
3029
+ lines.push("");
3030
+ for (const match of data.matches) {
3031
+ if (match.contextBefore) {
3032
+ for (const line of match.contextBefore) {
3033
+ lines.push(` ${line}`);
3034
+ }
3035
+ }
3036
+ lines.push(`${match.lineNumber}: ${match.lineContent}`);
3037
+ if (match.contextAfter) {
3038
+ for (const line of match.contextAfter) {
3039
+ lines.push(` ${line}`);
3040
+ }
3041
+ }
3042
+ lines.push("");
3043
+ }
3044
+ return lines.join(`
3045
+ `).trimEnd();
3046
+ }
3047
+ async function codeGrepAction(packageArg, filePath, pattern, options, deps) {
3048
+ const { pkgseerService } = deps;
3049
+ const parsed = parsePackageSpec(packageArg);
3050
+ const registry = toGraphQLRegistry(parsed.registry);
3051
+ const result = await pkgseerService.grepRepoFile(registry, parsed.name, filePath, pattern, {
3052
+ contextLines: parseIntOption(options.contextLines, "--context-lines"),
3053
+ maxMatches: parseIntOption(options.maxMatches, "--max-matches"),
3054
+ version: parsed.version,
3055
+ waitTimeoutMs: parseWaitTimeout(options.wait)
3056
+ });
3057
+ handleErrors(result.errors, options.json ?? false);
3058
+ if (!result.data.grepRepoFile) {
3059
+ outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
3060
+ return;
3061
+ }
3062
+ const data = result.data.grepRepoFile;
3063
+ if (options.json) {
3064
+ output(data, true);
3065
+ } else {
3066
+ const formatted = formatGrepResult(data);
3067
+ if (data.matches.length === 0 && data.diagnostics?.hint) {
3068
+ console.log(`${formatted}
3069
+ Note: ${data.diagnostics.hint}`);
3070
+ } else {
3071
+ console.log(formatted);
3072
+ }
3073
+ }
3074
+ }
3075
+ var GREP_DESCRIPTION = `Search for a pattern within a file in a package's repository.
3076
+
3077
+ Case-insensitive substring matching. Returns matching lines with context.
3078
+ Use 'code files' to discover available file paths.
3079
+
3080
+ Examples:
3081
+ pkgseer code grep npm:express "lib/router/index.js" "middleware"
3082
+ pkgseer code grep pypi:requests "requests/api.py" "timeout" --context-lines 3
3083
+ pkgseer code grep npm:lodash "lodash.js" "cloneDeep" --max-matches 5`;
3084
+ function registerCodeGrepCommand(program) {
3085
+ 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) => {
3086
+ await withCliErrorHandling(options.json ?? false, async () => {
3087
+ const deps = await createContainer();
3088
+ await codeGrepAction(packageArg, filePath, pattern, options, deps);
3089
+ });
3090
+ });
3091
+ }
2661
3092
  // src/commands/code/imports.ts
2662
3093
  function formatImport(entry) {
2663
3094
  const parts = [];
@@ -2748,9 +3179,6 @@ function formatListResult(data) {
2748
3179
  if (data.file) {
2749
3180
  lines.push(`File: ${data.file.path}${data.file.language ? ` (${data.file.language})` : ""}`);
2750
3181
  }
2751
- if (data.error) {
2752
- lines.push(`Warning: ${data.error}`);
2753
- }
2754
3182
  lines.push("");
2755
3183
  for (const symbol of data.symbols) {
2756
3184
  lines.push(formatSymbol(symbol));
@@ -2899,12 +3327,29 @@ function formatSearchResult(data) {
2899
3327
  return lines.join(`
2900
3328
  `).trimEnd();
2901
3329
  }
3330
+ function parseMatchMode(mode) {
3331
+ if (!mode)
3332
+ return;
3333
+ const upper = mode.toUpperCase();
3334
+ if (upper === "OR" || upper === "AND")
3335
+ return upper;
3336
+ throw new Error("--match-mode must be 'or' or 'and'");
3337
+ }
2902
3338
  async function codeSearchAction(packageArg, query, options, deps) {
2903
3339
  const { pkgseerService } = deps;
2904
3340
  const parsed = parsePackageSpec(packageArg);
2905
3341
  const registry = toGraphQLRegistry(parsed.registry);
2906
- const result = await pkgseerService.searchSymbols(registry, parsed.name, query, {
3342
+ const keywords = options.keywords ? options.keywords.split(",").map((k) => k.trim()).filter(Boolean) : undefined;
3343
+ if (!query && !keywords?.length) {
3344
+ outputError("Either a query argument or --keywords must be provided", options.json ?? false);
3345
+ return;
3346
+ }
3347
+ const result = await pkgseerService.searchSymbols(registry, parsed.name, {
3348
+ query: query || undefined,
3349
+ keywords,
3350
+ matchMode: parseMatchMode(options.matchMode),
2907
3351
  kind: options.kind?.toUpperCase(),
3352
+ filePath: options.filePath,
2908
3353
  version: parsed.version,
2909
3354
  limit: parseIntOption(options.limit, "--limit"),
2910
3355
  mode: parseNavigationMode(options.mode),
@@ -2915,23 +3360,36 @@ async function codeSearchAction(packageArg, query, options, deps) {
2915
3360
  outputError(`Package not found: ${parsed.name} in ${parsed.registry}`, options.json ?? false);
2916
3361
  return;
2917
3362
  }
3363
+ const data = result.data.searchSymbols;
2918
3364
  if (options.json) {
2919
- output(result.data.searchSymbols, true);
3365
+ output(data, true);
2920
3366
  } else {
2921
- console.log(formatSearchResult(result.data.searchSymbols));
3367
+ let formatted = formatSearchResult(data);
3368
+ if (data.warning) {
3369
+ formatted = `Warning: ${data.warning}
3370
+
3371
+ ${formatted}`;
3372
+ }
3373
+ if (data.results.length === 0 && data.diagnostics?.hint) {
3374
+ formatted = `${formatted}
3375
+ Note: ${data.diagnostics.hint}`;
3376
+ }
3377
+ console.log(formatted);
2922
3378
  }
2923
3379
  }
2924
3380
  var SEARCH_DESCRIPTION = `Full-text search within package code.
2925
3381
 
2926
3382
  Searches across functions, classes, modules, and doc sections.
3383
+ Supports query string or keyword list with match mode.
2927
3384
  Returns matching code chunks with names, types, and previews.
2928
3385
 
2929
3386
  Examples:
2930
3387
  pkgseer code search npm:express "middleware"
2931
3388
  pkgseer code search pypi:requests "timeout" --kind function
2932
- pkgseer code search npm:lodash "deep clone" --limit 10`;
3389
+ pkgseer code search npm:lodash --keywords "deep,clone" --match-mode and
3390
+ pkgseer code search npm:express "router" --file-path src/`;
2933
3391
  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) => {
3392
+ 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
3393
  await withCliErrorHandling(options.json ?? false, async () => {
2936
3394
  const deps = await createContainer();
2937
3395
  await codeSearchAction(packageArg, query, options, deps);
@@ -6006,6 +6464,14 @@ function toSymbolKind(kind) {
6006
6464
  return upper;
6007
6465
  return;
6008
6466
  }
6467
+ function toMatchMode(mode) {
6468
+ if (!mode)
6469
+ return;
6470
+ const upper = mode.toUpperCase();
6471
+ if (upper === "OR" || upper === "AND")
6472
+ return upper;
6473
+ return;
6474
+ }
6009
6475
  function toListScope(scope) {
6010
6476
  if (!scope)
6011
6477
  return;
@@ -6037,10 +6503,30 @@ function toSymbolReferenceInput(ref) {
6037
6503
  }
6038
6504
  function buildHintedMessage(operation, message) {
6039
6505
  const lower = message.toLowerCase();
6506
+ const isBeingIndexed = lower.includes("being indexed") || lower.includes("retry with waittimeoutms");
6507
+ if (isBeingIndexed) {
6508
+ return `Failed to ${operation}: ${message}. ` + "Hint: package is being indexed. Try again with a longer wait_timeout_ms (e.g., 30000).";
6509
+ }
6040
6510
  const isTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
6041
6511
  if (isTimeout) {
6042
6512
  return `Failed to ${operation}: ${message}. ` + "Hint: try lowering the limit or narrowing the scope, then retry.";
6043
6513
  }
6514
+ if (lower.includes("graphql error (code: 500)")) {
6515
+ try {
6516
+ const jsonMatch = message.match(/\{.*\}/s);
6517
+ if (jsonMatch) {
6518
+ const parsed = JSON.parse(jsonMatch[0]);
6519
+ const detail = parsed.errors?.detail ?? parsed.detail;
6520
+ if (typeof detail === "string") {
6521
+ return `Failed to ${operation}: ${detail}`;
6522
+ }
6523
+ }
6524
+ } catch {}
6525
+ const detailMatch = message.match(/"detail"\s*:\s*"([^"]+)"/);
6526
+ if (detailMatch?.[1]) {
6527
+ return `Failed to ${operation}: ${detailMatch[1]}`;
6528
+ }
6529
+ }
6044
6530
  return `Failed to ${operation}: ${message}`;
6045
6531
  }
6046
6532
  function handleGraphQLErrors(errors) {
@@ -6049,10 +6535,14 @@ function handleGraphQLErrors(errors) {
6049
6535
  const combined = messages.join(", ");
6050
6536
  const lower = combined.toLowerCase();
6051
6537
  const hasNotFound = lower.includes("not found") || lower.includes("does not exist");
6538
+ const hasBeingIndexed = lower.includes("being indexed") || lower.includes("retry with waittimeoutms");
6052
6539
  const hasTimeout = lower.includes("-32001") || lower.includes("timeout") || lower.includes("timed out");
6053
6540
  if (hasNotFound) {
6054
6541
  return errorResult(`Error: ${combined}. Hint: verify the name/registry and that the resource exists.`);
6055
6542
  }
6543
+ if (hasBeingIndexed) {
6544
+ return errorResult(`Error: ${combined}. Hint: package is being indexed. Try again with a longer wait_timeout_ms (e.g., 30000).`);
6545
+ }
6056
6546
  if (hasTimeout) {
6057
6547
  return errorResult(`Error: ${combined}. Hint: try lowering the limit or narrowing the scope, then retry.`);
6058
6548
  }
@@ -6247,13 +6737,57 @@ function createFindSymbolTool(pkgseerService) {
6247
6737
  if (!result.data.findSymbol) {
6248
6738
  return notFoundError(args.package_name, args.registry);
6249
6739
  }
6250
- return textResult(JSON.stringify(result.data.findSymbol, null, 2));
6740
+ const data = result.data.findSymbol;
6741
+ if (data.symbols.length === 0 && data.diagnostics?.hint) {
6742
+ return textResult(JSON.stringify({ ...data, _hint: data.diagnostics.hint }, null, 2));
6743
+ }
6744
+ return textResult(JSON.stringify(data, null, 2));
6251
6745
  });
6252
6746
  }
6253
6747
  };
6254
6748
  }
6255
- // src/tools/list-package-docs.ts
6749
+ // src/tools/grep-repo-file.ts
6750
+ import { z as z8 } from "zod";
6256
6751
  var argsSchema6 = {
6752
+ registry: schemas.registry,
6753
+ package_name: schemas.packageName.describe("Name of the package to search within"),
6754
+ file_path: z8.string().describe("Path to the file (relative to package root)"),
6755
+ pattern: z8.string().max(200).describe("Case-insensitive substring to search for (max 200 chars)"),
6756
+ context_lines: z8.number().int().min(0).max(10).optional().describe("Lines of context around each match (max 10)"),
6757
+ max_matches: z8.number().int().min(1).max(200).optional().describe("Maximum matches to return (max 200)"),
6758
+ version: schemas.version,
6759
+ wait_timeout_ms: schemas.waitTimeoutMs
6760
+ };
6761
+ function createGrepRepoFileTool(pkgseerService) {
6762
+ return {
6763
+ name: "grep_repo_file",
6764
+ 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.",
6765
+ schema: argsSchema6,
6766
+ handler: async (args, _extra) => {
6767
+ return withErrorHandling("grep repo file", async () => {
6768
+ const result = await pkgseerService.grepRepoFile(toGraphQLRegistry2(args.registry), args.package_name, args.file_path, args.pattern, {
6769
+ contextLines: args.context_lines,
6770
+ maxMatches: args.max_matches,
6771
+ version: args.version,
6772
+ waitTimeoutMs: args.wait_timeout_ms
6773
+ });
6774
+ const graphqlError = handleGraphQLErrors(result.errors);
6775
+ if (graphqlError)
6776
+ return graphqlError;
6777
+ if (!result.data.grepRepoFile) {
6778
+ return notFoundError(args.package_name, args.registry);
6779
+ }
6780
+ const data = result.data.grepRepoFile;
6781
+ if (data.matches.length === 0 && data.diagnostics?.hint) {
6782
+ return textResult(JSON.stringify({ ...data, _hint: data.diagnostics.hint }, null, 2));
6783
+ }
6784
+ return textResult(JSON.stringify(data, null, 2));
6785
+ });
6786
+ }
6787
+ };
6788
+ }
6789
+ // src/tools/list-package-docs.ts
6790
+ var argsSchema7 = {
6257
6791
  registry: schemas.registry,
6258
6792
  package_name: schemas.packageName.describe("Name of the package to list documentation for"),
6259
6793
  version: schemas.version
@@ -6262,7 +6796,7 @@ function createListPackageDocsTool(pkgseerService) {
6262
6796
  return {
6263
6797
  name: "list_package_docs",
6264
6798
  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,
6799
+ schema: argsSchema7,
6266
6800
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
6267
6801
  return withErrorHandling("list package documentation", async () => {
6268
6802
  const result = await pkgseerService.listPackageDocs(toGraphQLRegistry2(registry), package_name, version2);
@@ -6277,17 +6811,55 @@ function createListPackageDocsTool(pkgseerService) {
6277
6811
  }
6278
6812
  };
6279
6813
  }
6814
+ // src/tools/list-repo-files.ts
6815
+ import { z as z9 } from "zod";
6816
+ var argsSchema8 = {
6817
+ registry: schemas.registry,
6818
+ package_name: schemas.packageName.describe("Name of the package to list files for"),
6819
+ version: schemas.version,
6820
+ path_prefix: z9.string().optional().describe("Filter to files under this path (e.g., 'src/')"),
6821
+ limit: z9.number().int().min(1).max(1000).optional().describe("Max files to return (max 1000)"),
6822
+ wait_timeout_ms: schemas.waitTimeoutMs
6823
+ };
6824
+ function createListRepoFilesTool(pkgseerService) {
6825
+ return {
6826
+ name: "list_repo_files",
6827
+ 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.",
6828
+ schema: argsSchema8,
6829
+ handler: async (args, _extra) => {
6830
+ return withErrorHandling("list repo files", async () => {
6831
+ const result = await pkgseerService.listRepoFiles(toGraphQLRegistry2(args.registry), args.package_name, {
6832
+ version: args.version,
6833
+ pathPrefix: args.path_prefix,
6834
+ limit: args.limit,
6835
+ waitTimeoutMs: args.wait_timeout_ms
6836
+ });
6837
+ const graphqlError = handleGraphQLErrors(result.errors);
6838
+ if (graphqlError)
6839
+ return graphqlError;
6840
+ if (!result.data.listRepoFiles) {
6841
+ return notFoundError(args.package_name, args.registry);
6842
+ }
6843
+ const data = result.data.listRepoFiles;
6844
+ if (data.files.length === 0 && data.diagnostics?.hint) {
6845
+ return textResult(JSON.stringify({ ...data, _hint: data.diagnostics.hint }, null, 2));
6846
+ }
6847
+ return textResult(JSON.stringify(data, null, 2));
6848
+ });
6849
+ }
6850
+ };
6851
+ }
6280
6852
  // src/tools/list-symbols.ts
6281
- import { z as z8 } from "zod";
6282
- var argsSchema7 = {
6853
+ import { z as z10 } from "zod";
6854
+ var argsSchema9 = {
6283
6855
  registry: schemas.registry,
6284
6856
  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)"),
6857
+ scope: z10.enum(["exports", "file"]).describe("Browsing scope: 'exports' for public API, 'file' for all symbols in a specific file"),
6858
+ file_path: z10.string().max(1000).optional().describe("File path within the repo (required when scope=file)"),
6859
+ namespace: z10.string().max(500).optional().describe("Filter by namespace prefix (e.g., 'React' to see React.*)"),
6860
+ public_only: z10.boolean().optional().describe("Only return public symbols"),
6861
+ include_popularity: z10.boolean().optional().describe("Include callerCount for each symbol (slightly slower)"),
6862
+ limit: z10.number().int().min(1).max(100).optional().describe("Max symbols to return (max 100)"),
6291
6863
  version: schemas.version,
6292
6864
  mode: schemas.navigationMode,
6293
6865
  wait_timeout_ms: schemas.waitTimeoutMs
@@ -6296,7 +6868,7 @@ function createListSymbolsTool(pkgseerService) {
6296
6868
  return {
6297
6869
  name: "list_symbols",
6298
6870
  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,
6871
+ schema: argsSchema9,
6300
6872
  handler: async (args, _extra) => {
6301
6873
  return withErrorHandling("list symbols", async () => {
6302
6874
  const scope = toListScope(args.scope);
@@ -6333,13 +6905,13 @@ function createListSymbolsTool(pkgseerService) {
6333
6905
  };
6334
6906
  }
6335
6907
  // src/tools/package-dependencies.ts
6336
- import { z as z9 } from "zod";
6337
- var argsSchema8 = {
6908
+ import { z as z11 } from "zod";
6909
+ var argsSchema10 = {
6338
6910
  registry: schemas.registry,
6339
6911
  package_name: schemas.packageName.describe("Name of the package to retrieve dependencies for"),
6340
6912
  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)")
6913
+ include_transitive: z11.boolean().optional().describe("Whether to include transitive dependency DAG"),
6914
+ max_depth: z11.number().int().min(1).max(10).optional().describe("Maximum depth for transitive traversal (1-10)")
6343
6915
  };
6344
6916
  function decodeDag(rawDag) {
6345
6917
  if (!rawDag || typeof rawDag !== "object")
@@ -6417,7 +6989,7 @@ function createPackageDependenciesTool(pkgseerService) {
6417
6989
  return {
6418
6990
  name: "package_dependencies",
6419
6991
  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,
6992
+ schema: argsSchema10,
6421
6993
  handler: async ({ registry, package_name, version: version2, include_transitive, max_depth }, _extra) => {
6422
6994
  return withErrorHandling("fetch package dependencies", async () => {
6423
6995
  const result = await pkgseerService.getPackageDependencies(toGraphQLRegistry2(registry), package_name, version2, include_transitive, max_depth);
@@ -6455,13 +7027,13 @@ function createPackageDependenciesTool(pkgseerService) {
6455
7027
  };
6456
7028
  }
6457
7029
  // src/tools/package-imports.ts
6458
- import { z as z10 } from "zod";
6459
- var argsSchema9 = {
7030
+ import { z as z12 } from "zod";
7031
+ var argsSchema11 = {
6460
7032
  registry: schemas.registry,
6461
7033
  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"),
7034
+ file_path: z12.string().max(1000).optional().describe("Filter to a specific file path"),
7035
+ resolved_only: z12.boolean().optional().describe("Only return imports resolved to a known package"),
7036
+ limit: z12.number().int().min(1).max(500).optional().describe("Max imports to return"),
6465
7037
  version: schemas.version,
6466
7038
  mode: schemas.navigationMode,
6467
7039
  wait_timeout_ms: schemas.waitTimeoutMs
@@ -6470,7 +7042,7 @@ function createPackageImportsTool(pkgseerService) {
6470
7042
  return {
6471
7043
  name: "package_imports",
6472
7044
  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,
7045
+ schema: argsSchema11,
6474
7046
  handler: async (args, _extra) => {
6475
7047
  return withErrorHandling("fetch package imports", async () => {
6476
7048
  const result = await pkgseerService.packageImports(toGraphQLRegistry2(args.registry), args.package_name, {
@@ -6493,7 +7065,7 @@ function createPackageImportsTool(pkgseerService) {
6493
7065
  };
6494
7066
  }
6495
7067
  // src/tools/package-quality.ts
6496
- var argsSchema10 = {
7068
+ var argsSchema12 = {
6497
7069
  registry: schemas.registry,
6498
7070
  package_name: schemas.packageName.describe("Name of the package to analyze"),
6499
7071
  version: schemas.version
@@ -6502,7 +7074,7 @@ function createPackageQualityTool(pkgseerService) {
6502
7074
  return {
6503
7075
  name: "package_quality",
6504
7076
  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,
7077
+ schema: argsSchema12,
6506
7078
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
6507
7079
  return withErrorHandling("fetch package quality", async () => {
6508
7080
  const result = await pkgseerService.getPackageQuality(toGraphQLRegistry2(registry), package_name, version2);
@@ -6518,7 +7090,7 @@ function createPackageQualityTool(pkgseerService) {
6518
7090
  };
6519
7091
  }
6520
7092
  // src/tools/package-summary.ts
6521
- var argsSchema11 = {
7093
+ var argsSchema13 = {
6522
7094
  registry: schemas.registry,
6523
7095
  package_name: schemas.packageName.describe("Name of the package to retrieve summary for")
6524
7096
  };
@@ -6526,7 +7098,7 @@ function createPackageSummaryTool(pkgseerService) {
6526
7098
  return {
6527
7099
  name: "package_summary",
6528
7100
  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,
7101
+ schema: argsSchema13,
6530
7102
  handler: async ({ registry, package_name }, _extra) => {
6531
7103
  return withErrorHandling("fetch package summary", async () => {
6532
7104
  const result = await pkgseerService.getPackageSummary(toGraphQLRegistry2(registry), package_name);
@@ -6542,7 +7114,7 @@ function createPackageSummaryTool(pkgseerService) {
6542
7114
  };
6543
7115
  }
6544
7116
  // src/tools/package-vulnerabilities.ts
6545
- var argsSchema12 = {
7117
+ var argsSchema14 = {
6546
7118
  registry: schemas.registry,
6547
7119
  package_name: schemas.packageName.describe("Name of the package to inspect for vulnerabilities"),
6548
7120
  version: schemas.version
@@ -6551,7 +7123,7 @@ function createPackageVulnerabilitiesTool(pkgseerService) {
6551
7123
  return {
6552
7124
  name: "package_vulnerabilities",
6553
7125
  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,
7126
+ schema: argsSchema14,
6555
7127
  handler: async ({ registry, package_name, version: version2 }, _extra) => {
6556
7128
  return withErrorHandling("fetch package vulnerabilities", async () => {
6557
7129
  const result = await pkgseerService.getPackageVulnerabilities(toGraphQLRegistry2(registry), package_name, version2);
@@ -6567,19 +7139,19 @@ function createPackageVulnerabilitiesTool(pkgseerService) {
6567
7139
  };
6568
7140
  }
6569
7141
  // src/tools/search.ts
6570
- import { z as z11 } from "zod";
6571
- var packageInputSchema2 = z11.object({
7142
+ import { z as z13 } from "zod";
7143
+ var packageInputSchema2 = z13.object({
6572
7144
  registry: schemas.registry,
6573
- name: z11.string().min(1).describe("Package name"),
6574
- version: z11.string().optional().describe("Specific version (defaults to latest)")
7145
+ name: z13.string().min(1).describe("Package name"),
7146
+ version: z13.string().optional().describe("Specific version (defaults to latest)")
6575
7147
  });
6576
7148
  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)")
7149
+ var argsSchema15 = {
7150
+ packages: z13.array(packageInputSchema2).min(1).max(20).describe("Packages to search (1-20). Each package needs registry and name."),
7151
+ query: z13.string().min(1).describe("Search query - natural language or keywords"),
7152
+ mode: z13.enum(["all", "code", "docs"]).optional().describe('Search mode: "all" (default), "code" only, or "docs" only'),
7153
+ limit: z13.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)"),
7154
+ waitTimeoutMs: z13.number().int().min(0).max(60000).optional().describe("Max milliseconds to wait for indexing (default: 10000, 0=immediate)")
6583
7155
  };
6584
7156
  function toSearchMode2(mode) {
6585
7157
  if (!mode)
@@ -6618,7 +7190,7 @@ function createSearchTool(pkgseerService) {
6618
7190
  return {
6619
7191
  name: "search",
6620
7192
  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,
7193
+ schema: argsSchema15,
6622
7194
  handler: async ({ packages, query, mode, limit, waitTimeoutMs }, _extra) => {
6623
7195
  return withErrorHandling("search packages", async () => {
6624
7196
  const normalizedQuery = query.trim();
@@ -6670,21 +7242,21 @@ function createSearchTool(pkgseerService) {
6670
7242
  };
6671
7243
  }
6672
7244
  // 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")
7245
+ import { z as z14 } from "zod";
7246
+ var argsSchema16 = {
7247
+ 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."),
7248
+ project: z14.string().optional().describe("Project name to search. Overrides value from pkgseer.yml if provided."),
7249
+ terms: z14.array(z14.string()).optional().describe("Search terms to match. Provide a few key words or phrases that should appear in results."),
7250
+ match_mode: z14.enum(["any", "or", "all", "and"]).optional().describe('How to combine terms: "any" (OR) or "all" (AND).'),
7251
+ include_snippets: z14.boolean().optional().describe("Include content excerpts around matches"),
7252
+ limit: z14.number().int().min(1).max(100).optional().describe("Maximum number of results to return")
6681
7253
  };
6682
7254
  function createSearchProjectDocsTool(deps) {
6683
7255
  const { pkgseerService, configService, defaultProjectDir } = deps;
6684
7256
  return {
6685
7257
  name: "search_project_docs",
6686
7258
  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,
7259
+ schema: argsSchema16,
6688
7260
  handler: async ({
6689
7261
  project_directory,
6690
7262
  project,
@@ -6730,15 +7302,15 @@ function createSearchProjectDocsTool(deps) {
6730
7302
  };
6731
7303
  }
6732
7304
  // 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")
7305
+ import { z as z15 } from "zod";
7306
+ var argsSchema17 = {
7307
+ searchRef: z15.string().min(1).describe("Search reference from a previous incomplete search")
6736
7308
  };
6737
7309
  function createSearchStatusTool(pkgseerService) {
6738
7310
  return {
6739
7311
  name: "search_status",
6740
7312
  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,
7313
+ schema: argsSchema17,
6742
7314
  handler: async ({ searchRef }, _extra) => {
6743
7315
  return withErrorHandling("check search status", async () => {
6744
7316
  const progressResult = await pkgseerService.getSearchProgress(searchRef);
@@ -6779,12 +7351,14 @@ function createSearchStatusTool(pkgseerService) {
6779
7351
  };
6780
7352
  }
6781
7353
  // src/tools/search-symbols.ts
6782
- import { z as z14 } from "zod";
6783
- var argsSchema16 = {
7354
+ import { z as z16 } from "zod";
7355
+ var argsSchema18 = {
6784
7356
  registry: schemas.registry,
6785
7357
  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([
7358
+ query: z16.string().max(500).optional().describe("Search query for full-text code search (max 500 chars). Required if keywords not provided."),
7359
+ keywords: z16.array(z16.string()).max(20).optional().describe("Search keywords (max 20). Combined using match_mode. Alternative to query."),
7360
+ match_mode: z16.enum(["or", "and"]).optional().describe("How to combine keywords: or (any match) or and (all must match)"),
7361
+ kind: z16.enum([
6788
7362
  "function",
6789
7363
  "method",
6790
7364
  "class",
@@ -6793,20 +7367,25 @@ var argsSchema16 = {
6793
7367
  "type",
6794
7368
  "doc_section"
6795
7369
  ]).optional().describe("Filter results by code chunk type"),
7370
+ file_path: z16.string().optional().describe("Filter results to files whose path starts with this value (e.g., 'src/' for a directory)"),
6796
7371
  version: schemas.version,
6797
- limit: z14.number().int().min(1).max(50).optional().describe("Max results to return (max 50)"),
7372
+ limit: z16.number().int().min(1).max(50).optional().describe("Max results to return (max 50)"),
6798
7373
  mode: schemas.navigationMode,
6799
7374
  wait_timeout_ms: schemas.waitTimeoutMs
6800
7375
  };
6801
7376
  function createSearchSymbolsTool(pkgseerService) {
6802
7377
  return {
6803
7378
  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,
7379
+ 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.",
7380
+ schema: argsSchema18,
6806
7381
  handler: async (args, _extra) => {
6807
7382
  return withErrorHandling("search symbols", async () => {
6808
- const result = await pkgseerService.searchSymbols(toGraphQLRegistry2(args.registry), args.package_name, args.query, {
7383
+ const result = await pkgseerService.searchSymbols(toGraphQLRegistry2(args.registry), args.package_name, {
7384
+ query: args.query,
7385
+ keywords: args.keywords,
7386
+ matchMode: toMatchMode(args.match_mode),
6809
7387
  kind: toSymbolKind(args.kind),
7388
+ filePath: args.file_path,
6810
7389
  version: args.version,
6811
7390
  limit: args.limit,
6812
7391
  mode: toNavigationMode(args.mode),
@@ -6818,19 +7397,30 @@ function createSearchSymbolsTool(pkgseerService) {
6818
7397
  if (!result.data.searchSymbols) {
6819
7398
  return notFoundError(args.package_name, args.registry);
6820
7399
  }
6821
- return textResult(JSON.stringify(result.data.searchSymbols, null, 2));
7400
+ const data = result.data.searchSymbols;
7401
+ const extra = {};
7402
+ if (data.results.length === 0 && data.diagnostics?.hint) {
7403
+ extra._hint = data.diagnostics.hint;
7404
+ }
7405
+ if (data.warning) {
7406
+ extra._warning = data.warning;
7407
+ }
7408
+ if (Object.keys(extra).length > 0) {
7409
+ return textResult(JSON.stringify({ ...data, ...extra }, null, 2));
7410
+ }
7411
+ return textResult(JSON.stringify(data, null, 2));
6822
7412
  });
6823
7413
  }
6824
7414
  };
6825
7415
  }
6826
7416
  // src/tools/symbol-callees.ts
6827
- import { z as z15 } from "zod";
6828
- var argsSchema17 = {
7417
+ import { z as z17 } from "zod";
7418
+ var argsSchema19 = {
6829
7419
  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.)"),
7420
+ max_depth: z17.number().int().min(1).max(5).optional().describe("BFS traversal depth (1 = direct callees only, 2+ = transitive, max 5)"),
7421
+ limit: z17.number().int().min(1).max(100).optional().describe("Maximum dependency entries to return"),
7422
+ include_external: z17.boolean().optional().describe("Include calls to symbols in other packages"),
7423
+ include_builtins: z17.boolean().optional().describe("Include calls to built-in/standard library functions (console, Math, etc.)"),
6834
7424
  mode: schemas.navigationMode,
6835
7425
  wait_timeout_ms: schemas.waitTimeoutMs
6836
7426
  };
@@ -6838,7 +7428,7 @@ function createSymbolCalleesTool(pkgseerService) {
6838
7428
  return {
6839
7429
  name: "symbol_callees",
6840
7430
  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,
7431
+ schema: argsSchema19,
6842
7432
  handler: async (args, _extra) => {
6843
7433
  return withErrorHandling("find symbol callees", async () => {
6844
7434
  const symbolInput = toSymbolReferenceInput(args.symbol);
@@ -6865,11 +7455,11 @@ function createSymbolCalleesTool(pkgseerService) {
6865
7455
  };
6866
7456
  }
6867
7457
  // src/tools/symbol-callers.ts
6868
- import { z as z16 } from "zod";
6869
- var argsSchema18 = {
7458
+ import { z as z18 } from "zod";
7459
+ var argsSchema20 = {
6870
7460
  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"),
7461
+ same_package_only: z18.boolean().optional().describe("Only return callers from the same package"),
7462
+ limit: z18.number().int().min(1).max(100).optional().describe("Maximum entries to return"),
6873
7463
  mode: schemas.navigationMode,
6874
7464
  wait_timeout_ms: schemas.waitTimeoutMs
6875
7465
  };
@@ -6877,7 +7467,7 @@ function createSymbolCallersTool(pkgseerService) {
6877
7467
  return {
6878
7468
  name: "symbol_callers",
6879
7469
  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,
7470
+ schema: argsSchema20,
6881
7471
  handler: async (args, _extra) => {
6882
7472
  return withErrorHandling("find symbol callers", async () => {
6883
7473
  const symbolInput = toSymbolReferenceInput(args.symbol);
@@ -6902,26 +7492,26 @@ function createSymbolCallersTool(pkgseerService) {
6902
7492
  };
6903
7493
  }
6904
7494
  // src/tools/trigger-indexing.ts
6905
- import { z as z17 } from "zod";
6906
- var packageInputSchema3 = z17.object({
7495
+ import { z as z19 } from "zod";
7496
+ var packageInputSchema3 = z19.object({
6907
7497
  registry: schemas.registry,
6908
- name: z17.string().max(255).describe("Package name"),
6909
- version: z17.string().max(100).optional().describe("Specific version to index")
7498
+ name: z19.string().max(255).describe("Package name"),
7499
+ version: z19.string().max(100).optional().describe("Specific version to index")
6910
7500
  });
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")
7501
+ var repositoryInputSchema = z19.object({
7502
+ url: z19.string().max(2000).describe("GitHub repository URL"),
7503
+ ref: z19.string().max(200).optional().describe("Git ref to index (tag, branch, commit). Defaults to HEAD"),
7504
+ discover_packages: z19.boolean().optional().describe("If true, discover packages published from this repo and trigger their metadata fetch")
6915
7505
  });
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)")
7506
+ var argsSchema21 = {
7507
+ packages: z19.array(packageInputSchema3).max(100).optional().describe("Package versions to index (max 100)"),
7508
+ repositories: z19.array(repositoryInputSchema).max(50).optional().describe("Repository URLs to index (max 50)")
6919
7509
  };
6920
7510
  function createTriggerIndexingTool(pkgseerService) {
6921
7511
  return {
6922
7512
  name: "trigger_indexing",
6923
7513
  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,
7514
+ schema: argsSchema21,
6925
7515
  handler: async (args, _extra) => {
6926
7516
  return withErrorHandling("trigger indexing", async () => {
6927
7517
  const packages = args.packages?.map((p) => ({
@@ -6957,6 +7547,50 @@ function createTriggerIndexingTool(pkgseerService) {
6957
7547
  }
6958
7548
  };
6959
7549
  }
7550
+ // src/tools/version-diff.ts
7551
+ import { z as z20 } from "zod";
7552
+ var argsSchema22 = {
7553
+ registry: schemas.registry,
7554
+ package_name: schemas.packageName.describe("Name of the package to compare versions for"),
7555
+ from_version: z20.string().max(100).describe("Source version (git ref, e.g., 'v4.18.2')"),
7556
+ to_version: z20.string().max(100).describe("Target version (git ref, e.g., 'v5.0.0')"),
7557
+ include_private: z20.boolean().optional().describe("Include private/non-exported symbols in the diff"),
7558
+ kind: z20.enum([
7559
+ "function",
7560
+ "method",
7561
+ "class",
7562
+ "module",
7563
+ "interface",
7564
+ "type",
7565
+ "doc_section"
7566
+ ]).optional().describe("Filter by symbol kind"),
7567
+ limit: z20.number().int().min(1).max(500).optional().describe("Max changes to return (max 500). Summary always reflects full counts."),
7568
+ wait_timeout_ms: schemas.waitTimeoutMs
7569
+ };
7570
+ function createVersionDiffTool(pkgseerService) {
7571
+ return {
7572
+ name: "version_diff",
7573
+ 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.",
7574
+ schema: argsSchema22,
7575
+ handler: async (args, _extra) => {
7576
+ return withErrorHandling("version diff", async () => {
7577
+ const result = await pkgseerService.versionDiff(toGraphQLRegistry2(args.registry), args.package_name, args.from_version, args.to_version, {
7578
+ includePrivate: args.include_private,
7579
+ kind: toSymbolKind(args.kind),
7580
+ limit: args.limit,
7581
+ waitTimeoutMs: args.wait_timeout_ms
7582
+ });
7583
+ const graphqlError = handleGraphQLErrors(result.errors);
7584
+ if (graphqlError)
7585
+ return graphqlError;
7586
+ if (!result.data.versionDiff) {
7587
+ return notFoundError(args.package_name, args.registry);
7588
+ }
7589
+ return textResult(JSON.stringify(result.data.versionDiff, null, 2));
7590
+ });
7591
+ }
7592
+ };
7593
+ }
6960
7594
  // src/commands/mcp.ts
6961
7595
  var TOOL_FACTORIES = {
6962
7596
  package_summary: ({ pkgseerService }) => createPackageSummaryTool(pkgseerService),
@@ -6981,7 +7615,10 @@ var TOOL_FACTORIES = {
6981
7615
  symbol_callees: ({ pkgseerService }) => createSymbolCalleesTool(pkgseerService),
6982
7616
  package_imports: ({ pkgseerService }) => createPackageImportsTool(pkgseerService),
6983
7617
  call_path: ({ pkgseerService }) => createCallPathTool(pkgseerService),
6984
- trigger_indexing: ({ pkgseerService }) => createTriggerIndexingTool(pkgseerService)
7618
+ trigger_indexing: ({ pkgseerService }) => createTriggerIndexingTool(pkgseerService),
7619
+ list_repo_files: ({ pkgseerService }) => createListRepoFilesTool(pkgseerService),
7620
+ grep_repo_file: ({ pkgseerService }) => createGrepRepoFileTool(pkgseerService),
7621
+ version_diff: ({ pkgseerService }) => createVersionDiffTool(pkgseerService)
6985
7622
  };
6986
7623
  var PUBLIC_READ_TOOLS = [
6987
7624
  "package_summary",
@@ -7001,7 +7638,10 @@ var PUBLIC_READ_TOOLS = [
7001
7638
  "symbol_callees",
7002
7639
  "package_imports",
7003
7640
  "call_path",
7004
- "trigger_indexing"
7641
+ "trigger_indexing",
7642
+ "list_repo_files",
7643
+ "grep_repo_file",
7644
+ "version_diff"
7005
7645
  ];
7006
7646
  var PROJECT_READ_TOOLS = ["search_project_docs"];
7007
7647
  var ALL_TOOLS = [...PUBLIC_READ_TOOLS, ...PROJECT_READ_TOOLS];
@@ -8003,8 +8643,11 @@ Documentation commands:
8003
8643
 
8004
8644
  Code navigation commands:
8005
8645
  pkgseer code find <pkg> [name] Find symbols by name
8006
- pkgseer code search <pkg> <q> Search in package code
8646
+ pkgseer code search <pkg> [q] Search in package code
8007
8647
  pkgseer code list <package> Browse package symbols
8648
+ pkgseer code files <package> List repository files
8649
+ pkgseer code grep <pkg> <f> <p> Search pattern in a file
8650
+ pkgseer code diff <pkg> <v> <v> Compare versions
8008
8651
  pkgseer code callers <symbol> Find what calls a symbol
8009
8652
  pkgseer code callees <symbol> Find what a symbol calls
8010
8653
  pkgseer code imports <package> List package imports
@@ -8038,6 +8681,9 @@ var code = program.command("code").description("Code navigation commands");
8038
8681
  registerCodeFindCommand(code);
8039
8682
  registerCodeSearchCommand(code);
8040
8683
  registerCodeListCommand(code);
8684
+ registerCodeFilesCommand(code);
8685
+ registerCodeGrepCommand(code);
8686
+ registerCodeDiffCommand(code);
8041
8687
  registerCodeCallersCommand(code);
8042
8688
  registerCodeCalleesCommand(code);
8043
8689
  registerCodeImportsCommand(code);