@pkgseer/cli 0.2.5 → 0.3.0

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/README.md CHANGED
@@ -96,8 +96,14 @@ pkgseer search "json parsing" -P hex:jason,hex:poison
96
96
  # Search modes
97
97
  pkgseer search "auth" -P lodash --mode code # Code only
98
98
  pkgseer search "auth" -P lodash --mode docs # Docs only
99
+
100
+ # Wait options (for packages that need indexing)
101
+ pkgseer search "api" -P new-package --wait 60000 # Wait up to 60s
102
+ pkgseer search "api" -P new-package --no-wait # Return immediately
99
103
  ```
100
104
 
105
+ If packages haven't been indexed yet, the search will wait up to 30 seconds by default. Use `--wait <ms>` to customize or `--no-wait` to return immediately with progress info.
106
+
101
107
  ### Package Commands
102
108
 
103
109
  ```bash
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-drz16bhv.js";
4
+ } from "./shared/chunk-9yar14cw.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -211,36 +211,52 @@ var CliDocsListDocument = gql`
211
211
  }
212
212
  `;
213
213
  var CombinedSearchDocument = gql`
214
- query CombinedSearch($packages: [SearchPackageInput!]!, $query: String!, $mode: SearchMode, $limit: Int) {
215
- combinedSearch(packages: $packages, query: $query, mode: $mode, limit: $limit) {
216
- query
217
- mode
218
- totalResults
219
- entries {
220
- id
221
- type
222
- title
223
- subtitle
224
- packageName
225
- registry
226
- score
227
- snippet
228
- filePath
229
- startLine
230
- endLine
231
- language
232
- chunkType
233
- repoUrl
234
- gitRef
235
- pageId
236
- sourceUrl
214
+ query CombinedSearch($packages: [SearchPackageInput!]!, $query: String!, $mode: SearchMode, $limit: Int, $waitTimeoutMs: Int) {
215
+ combinedSearch(
216
+ packages: $packages
217
+ query: $query
218
+ mode: $mode
219
+ limit: $limit
220
+ waitTimeoutMs: $waitTimeoutMs
221
+ ) {
222
+ completed
223
+ searchRef
224
+ result {
225
+ query
226
+ mode
227
+ totalResults
228
+ entries {
229
+ id
230
+ type
231
+ title
232
+ subtitle
233
+ packageName
234
+ registry
235
+ score
236
+ snippet
237
+ filePath
238
+ startLine
239
+ endLine
240
+ language
241
+ chunkType
242
+ repoUrl
243
+ gitRef
244
+ pageId
245
+ sourceUrl
246
+ }
247
+ indexingStatus {
248
+ registry
249
+ packageName
250
+ version
251
+ codeStatus
252
+ docsStatus
253
+ }
237
254
  }
238
- indexingStatus {
239
- registry
240
- packageName
241
- version
242
- codeStatus
243
- docsStatus
255
+ progress {
256
+ status
257
+ packagesTotal
258
+ packagesReady
259
+ elapsedMs
244
260
  }
245
261
  }
246
262
  }
@@ -1419,7 +1435,8 @@ class PkgseerServiceImpl {
1419
1435
  packages,
1420
1436
  query,
1421
1437
  mode: options?.mode,
1422
- limit: options?.limit
1438
+ limit: options?.limit,
1439
+ waitTimeoutMs: options?.waitTimeoutMs
1423
1440
  });
1424
1441
  return { data: result.data, errors: result.errors };
1425
1442
  }
@@ -2342,6 +2359,7 @@ function registerDocsListCommand(program) {
2342
2359
  });
2343
2360
  }
2344
2361
  // src/commands/search.ts
2362
+ var DEFAULT_WAIT_TIMEOUT_MS = 30000;
2345
2363
  var colors = {
2346
2364
  reset: "\x1B[0m",
2347
2365
  bold: "\x1B[1m",
@@ -2562,6 +2580,36 @@ function truncate(text, maxLen) {
2562
2580
  return text;
2563
2581
  return `${text.slice(0, maxLen - 3)}...`;
2564
2582
  }
2583
+ function isSubstantiveLine(line) {
2584
+ const trimmed = line.trim();
2585
+ if (trimmed.length === 0)
2586
+ return false;
2587
+ if (/^[(){}[\];,]+$/.test(trimmed))
2588
+ return false;
2589
+ if (trimmed.length <= 3 && /^[^a-zA-Z0-9]*$/.test(trimmed))
2590
+ return false;
2591
+ return true;
2592
+ }
2593
+ function findBestSnippetLine(snippet) {
2594
+ const lines = snippet.split(`
2595
+ `).map((l) => l.trim());
2596
+ if (lines.length === 0)
2597
+ return null;
2598
+ const middleIndex = Math.floor((lines.length - 1) / 2);
2599
+ for (let offset = 0;offset <= lines.length; offset++) {
2600
+ const beforeIdx = middleIndex - offset;
2601
+ if (beforeIdx >= 0 && isSubstantiveLine(lines[beforeIdx])) {
2602
+ return lines[beforeIdx];
2603
+ }
2604
+ if (offset > 0) {
2605
+ const afterIdx = middleIndex + offset;
2606
+ if (afterIdx < lines.length && isSubstantiveLine(lines[afterIdx])) {
2607
+ return lines[afterIdx];
2608
+ }
2609
+ }
2610
+ }
2611
+ return null;
2612
+ }
2565
2613
  function formatEntryCompact(entry, useColors) {
2566
2614
  if (!entry)
2567
2615
  return "";
@@ -2579,12 +2627,11 @@ function formatEntryCompact(entry, useColors) {
2579
2627
  }
2580
2628
  let snippet = "";
2581
2629
  if (entry.snippet) {
2582
- const firstLine = entry.snippet.split(`
2583
- `).map((l) => l.trim()).find((l) => l.length > 0);
2584
- if (firstLine) {
2585
- snippet = ` ${truncate(firstLine, 80)}`;
2630
+ const bestLine = findBestSnippetLine(entry.snippet);
2631
+ if (bestLine) {
2632
+ snippet = ` ${truncate(bestLine, 80)}`;
2586
2633
  if (useColors) {
2587
- snippet = ` ${colors.dim}${truncate(firstLine, 80)}${colors.reset}`;
2634
+ snippet = ` ${colors.dim}${truncate(bestLine, 80)}${colors.reset}`;
2588
2635
  }
2589
2636
  }
2590
2637
  }
@@ -2662,6 +2709,50 @@ function summarizeSearchResults(results) {
2662
2709
  return lines.join(`
2663
2710
  `);
2664
2711
  }
2712
+ function formatSearchProgress(progress, searchRef, useColors) {
2713
+ const lines = [];
2714
+ if (useColors) {
2715
+ lines.push(`${colors.yellow}Search in progress...${colors.reset}`);
2716
+ } else {
2717
+ lines.push("Search in progress...");
2718
+ }
2719
+ if (progress) {
2720
+ const statusText = progress.status?.toLowerCase() ?? "unknown";
2721
+ lines.push(`Status: ${statusText}`);
2722
+ if (progress.packagesTotal != null && progress.packagesReady != null) {
2723
+ lines.push(`Packages: ${progress.packagesReady}/${progress.packagesTotal} ready`);
2724
+ }
2725
+ if (progress.elapsedMs != null) {
2726
+ lines.push(`Elapsed: ${(progress.elapsedMs / 1000).toFixed(1)}s`);
2727
+ }
2728
+ }
2729
+ lines.push("");
2730
+ lines.push("Some packages may still be indexing.");
2731
+ lines.push("Run again in a moment for complete results.");
2732
+ if (searchRef) {
2733
+ lines.push("");
2734
+ if (useColors) {
2735
+ lines.push(`${colors.dim}Search ref: ${searchRef}${colors.reset}`);
2736
+ } else {
2737
+ lines.push(`Search ref: ${searchRef}`);
2738
+ }
2739
+ }
2740
+ return lines.join(`
2741
+ `);
2742
+ }
2743
+ function getWaitTimeoutMs(options) {
2744
+ if (options.noWait) {
2745
+ return 0;
2746
+ }
2747
+ if (options.wait) {
2748
+ const parsed = Number.parseInt(options.wait, 10);
2749
+ if (!Number.isNaN(parsed) && parsed >= 0) {
2750
+ return parsed;
2751
+ }
2752
+ console.warn(`Warning: Invalid --wait value "${options.wait}". Using default (${DEFAULT_WAIT_TIMEOUT_MS}ms).`);
2753
+ }
2754
+ return DEFAULT_WAIT_TIMEOUT_MS;
2755
+ }
2665
2756
  async function searchAction(queryArg, options, deps, defaultMode = "ALL") {
2666
2757
  const { pkgseerService } = deps;
2667
2758
  const query = Array.isArray(queryArg) ? queryArg.join(" ") : queryArg ?? "";
@@ -2681,16 +2772,37 @@ async function searchAction(queryArg, options, deps, defaultMode = "ALL") {
2681
2772
  const limit = options.limit ? Number.parseInt(options.limit, 10) : 25;
2682
2773
  const mode = options.mode ? toSearchMode(options.mode) : defaultMode;
2683
2774
  const useColors = shouldUseColors(options.noColor);
2775
+ const waitTimeoutMs = getWaitTimeoutMs(options);
2684
2776
  const result = await pkgseerService.combinedSearch(packages, query, {
2685
2777
  mode,
2686
- limit
2778
+ limit,
2779
+ waitTimeoutMs
2687
2780
  });
2688
2781
  handleErrors(result.errors, options.json ?? false);
2689
- if (!result.data.combinedSearch) {
2782
+ const asyncResult = result.data.combinedSearch;
2783
+ if (!asyncResult) {
2690
2784
  outputError("No results returned. Check package names and registries.", options.json ?? false);
2691
2785
  return;
2692
2786
  }
2693
- outputResults(result.data.combinedSearch, options, useColors);
2787
+ if (!asyncResult.completed) {
2788
+ if (options.json) {
2789
+ output({
2790
+ completed: false,
2791
+ searchRef: asyncResult.searchRef,
2792
+ progress: asyncResult.progress,
2793
+ message: "Search is still indexing. Retry for complete results."
2794
+ }, true);
2795
+ } else {
2796
+ console.log(formatSearchProgress(asyncResult.progress, asyncResult.searchRef, useColors));
2797
+ }
2798
+ return;
2799
+ }
2800
+ const searchResults = asyncResult.result;
2801
+ if (!searchResults) {
2802
+ outputError("Search completed but no results returned.", options.json ?? false);
2803
+ return;
2804
+ }
2805
+ outputResults(searchResults, options, useColors);
2694
2806
  }
2695
2807
  function outputResults(results, options, useColors) {
2696
2808
  const format = options.json ? "json" : options.format ?? "human";
@@ -2741,7 +2853,7 @@ Examples:
2741
2853
  # JSON output
2742
2854
  pkgseer search "error" -P express --json`;
2743
2855
  function addSearchOptions(cmd) {
2744
- return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: name or registry/name[@version]. Examples: express | express,lodash | pypi/django@4.2").option("-m, --mode <mode>", "Search mode: all (default), code, docs").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
2856
+ return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: name or registry/name[@version]. Examples: express | express,lodash | pypi/django@4.2").option("-m, --mode <mode>", "Search mode: all (default), code, docs").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON").option("--wait <ms>", "Max milliseconds to wait for indexing (default: 30000)").option("--no-wait", "Return immediately without waiting for indexing");
2745
2857
  }
2746
2858
  function registerSearchCommand(program) {
2747
2859
  const cmd = program.command("search [query...]").summary("Search code and documentation across packages").description(SEARCH_DESCRIPTION);
@@ -2782,7 +2894,7 @@ Examples:
2782
2894
 
2783
2895
  Note: For code search, use 'pkgseer search --mode code' instead.`;
2784
2896
  function addDocsSearchOptions(cmd) {
2785
- return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: [registry:]name[@version]. Examples: express | express,lodash | pypi:django@4.2").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON");
2897
+ return cmd.option("-P, --packages <packages>", "Packages to search (comma-separated). Format: [registry:]name[@version]. Examples: express | express,lodash | pypi:django@4.2").option("-l, --limit <n>", "Max results (default: 25)").option("-v, --verbose", "Expanded output with more details").option("--refs-only", "Output only references (for piping)").option("--count", "Output result counts by package").option("--format <format>", "Output format: human|summary|json", "human").option("--no-color", "Disable colored output").option("--json", "Output as JSON").option("--wait <ms>", "Max milliseconds to wait for indexing (default: 30000)").option("--no-wait", "Return immediately without waiting for indexing");
2786
2898
  }
2787
2899
  function registerDocsSearchCommand(program) {
2788
2900
  const cmd = program.command("search [query...]").summary("Search documentation").description(DOCS_SEARCH_DESCRIPTION);
@@ -4951,11 +5063,13 @@ var packageInputSchema2 = z7.object({
4951
5063
  name: z7.string().min(1).describe("Package name"),
4952
5064
  version: z7.string().optional().describe("Specific version (defaults to latest)")
4953
5065
  });
5066
+ var DEFAULT_AGENT_WAIT_TIMEOUT_MS = 1e4;
4954
5067
  var argsSchema9 = {
4955
5068
  packages: z7.array(packageInputSchema2).min(1).max(20).describe("Packages to search (1-20). Each package needs registry and name."),
4956
5069
  query: z7.string().min(1).describe("Search query - natural language or keywords"),
4957
5070
  mode: z7.enum(["all", "code", "docs"]).optional().describe('Search mode: "all" (default), "code" only, or "docs" only'),
4958
- limit: z7.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)")
5071
+ limit: z7.number().int().min(1).max(100).optional().describe("Maximum results (default: 20)"),
5072
+ waitTimeoutMs: z7.number().int().min(0).max(60000).optional().describe("Max milliseconds to wait for indexing (default: 10000, 0=immediate)")
4959
5073
  };
4960
5074
  function toSearchMode2(mode) {
4961
5075
  if (!mode)
@@ -4993,9 +5107,9 @@ Results may be incomplete. Retry later for more complete results.`;
4993
5107
  function createSearchTool(pkgseerService) {
4994
5108
  return {
4995
5109
  name: "search",
4996
- description: "Search code and documentation across packages. Returns functions, classes, and documentation pages " + "matching your query. Use mode='code' for code only, mode='docs' for documentation only, or " + "mode='all' (default) for both. Provide 1-20 packages to search. Results include relevance scores " + "and snippets showing matches.",
5110
+ description: "Search code and documentation across packages. Returns functions, classes, and documentation pages " + "matching your query. Use mode='code' for code only, mode='docs' for documentation only, or " + "mode='all' (default) for both. Provide 1-20 packages to search. Results include relevance scores " + "and snippets showing matches. If packages need indexing, the search will wait up to waitTimeoutMs " + "(default 10s). If not complete, returns progress info with searchRef for follow-up.",
4997
5111
  schema: argsSchema9,
4998
- handler: async ({ packages, query, mode, limit }, _extra) => {
5112
+ handler: async ({ packages, query, mode, limit, waitTimeoutMs }, _extra) => {
4999
5113
  return withErrorHandling("search packages", async () => {
5000
5114
  const normalizedQuery = query.trim();
5001
5115
  if (normalizedQuery.length === 0) {
@@ -5008,15 +5122,29 @@ function createSearchTool(pkgseerService) {
5008
5122
  }));
5009
5123
  const result = await pkgseerService.combinedSearch(graphqlPackages, normalizedQuery, {
5010
5124
  mode: toSearchMode2(mode),
5011
- limit
5125
+ limit,
5126
+ waitTimeoutMs: waitTimeoutMs ?? DEFAULT_AGENT_WAIT_TIMEOUT_MS
5012
5127
  });
5013
5128
  const graphqlError = handleGraphQLErrors(result.errors);
5014
5129
  if (graphqlError)
5015
5130
  return graphqlError;
5016
- if (!result.data.combinedSearch) {
5131
+ const asyncResult = result.data.combinedSearch;
5132
+ if (!asyncResult) {
5017
5133
  return errorResult("No search results returned. Check package names and registries.");
5018
5134
  }
5019
- const searchResult = result.data.combinedSearch;
5135
+ if (!asyncResult.completed) {
5136
+ const progressInfo = {
5137
+ completed: false,
5138
+ searchRef: asyncResult.searchRef,
5139
+ progress: asyncResult.progress,
5140
+ message: "Search is still indexing. Retry with same query for results, " + "or increase waitTimeoutMs to wait longer."
5141
+ };
5142
+ return textResult(JSON.stringify(progressInfo, null, 2));
5143
+ }
5144
+ const searchResult = asyncResult.result;
5145
+ if (!searchResult) {
5146
+ return errorResult("Search completed but no results returned.");
5147
+ }
5020
5148
  const entries = searchResult.entries ?? [];
5021
5149
  if (entries.length === 0) {
5022
5150
  return errorResult(`No results found for "${normalizedQuery}". ` + "Try broader terms or different packages.");
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  version
3
- } from "./shared/chunk-drz16bhv.js";
3
+ } from "./shared/chunk-9yar14cw.js";
4
4
  export {
5
5
  version
6
6
  };
@@ -1,4 +1,4 @@
1
1
  // package.json
2
- var version = "0.2.5";
2
+ var version = "0.3.0";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pkgseer/cli",
3
3
  "description": "CLI companion for PkgSeer - package intelligence for developers and AI assistants",
4
- "version": "0.2.5",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",