github-search-cli 9.5.0 → 9.6.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/src/search.ts CHANGED
@@ -519,6 +519,72 @@ function appendPage(url: string, page: number, perPage: number): string {
519
519
 
520
520
  const TRENDING_URL = "https://github.com/trending";
521
521
 
522
+ /**
523
+ * Languages github.com/trending accepts as a `?language=` filter (path-slug
524
+ * form, lowercase). The MCP `ghfind_trending` tool validates against this
525
+ * list instead of round-tripping an invalid language through a scrape.
526
+ */
527
+ export const TRENDING_LANGUAGES: ReadonlySet<string> = new Set([
528
+ "c",
529
+ "c#",
530
+ "c++",
531
+ "clojure",
532
+ "crystal",
533
+ "css",
534
+ "dart",
535
+ "dockerfile",
536
+ "elixir",
537
+ "go",
538
+ "haskell",
539
+ "html",
540
+ "java",
541
+ "javascript",
542
+ "jupyter-notebook",
543
+ "kotlin",
544
+ "lua",
545
+ "objective-c",
546
+ "ocaml",
547
+ "perl",
548
+ "php",
549
+ "powershell",
550
+ "python",
551
+ "r",
552
+ "ruby",
553
+ "rust",
554
+ "scala",
555
+ "shell",
556
+ "solidity",
557
+ "svelte",
558
+ "swift",
559
+ "typescript",
560
+ "vue",
561
+ "zig",
562
+ ]);
563
+
564
+ /**
565
+ * Normalize a user-supplied language to the slug form github.com/trending
566
+ * uses: lowercase, whitespace runs collapsed to dashes ("Jupyter Notebook"
567
+ * → "jupyter-notebook").
568
+ */
569
+ export function trendingLanguageSlug(language: string): string {
570
+ return language.trim().toLowerCase().replace(/\s+/g, "-");
571
+ }
572
+
573
+ /** Build the github.com/trending URL for a period + optional language filter. */
574
+ export function buildTrendingUrl(
575
+ since: "daily" | "weekly" | "monthly",
576
+ language?: string,
577
+ ): string {
578
+ const params = new URLSearchParams();
579
+ if (since !== "daily") params.set("since", since);
580
+ if (language) {
581
+ const slug = trendingLanguageSlug(language);
582
+ if (TRENDING_LANGUAGES.has(slug)) params.set("language", slug);
583
+ }
584
+ const qs = params.toString();
585
+ return qs ? `${TRENDING_URL}?${qs}` : TRENDING_URL;
586
+ }
587
+
522
588
  function trendingRepoToRepo(r: RawTrendingRepo): Repo {
523
589
  return {
524
590
  id: 0,
@@ -540,9 +606,7 @@ function trendingRepoToRepo(r: RawTrendingRepo): Repo {
540
606
  pushedAt: "",
541
607
  score: r.starsToday,
542
608
  };
543
- }
544
-
545
- /**
609
+ } /**
546
610
  * Trending adapter — scrapes github.com/trending HTML and returns SearchResponse.
547
611
  */
548
612
  export class TrendingAdapter implements SearchAdapter {
@@ -558,8 +622,7 @@ export class TrendingAdapter implements SearchAdapter {
558
622
  options: SearchOptions,
559
623
  ): Promise<SearchResponse> {
560
624
  const since = options.trendingSince ?? "daily";
561
- const url =
562
- since === "daily" ? TRENDING_URL : `${TRENDING_URL}?since=${since}`;
625
+ const url = buildTrendingUrl(since, options.trendingLanguage);
563
626
  let res: Response;
564
627
  try {
565
628
  res = await fetch(url, { headers: { "User-Agent": "ghfind" } });
@@ -639,6 +702,7 @@ export class SearchModule implements SearchProvider {
639
702
  String(options.limit),
640
703
  String(options.page ?? 1),
641
704
  options.trendingSince ?? "none",
705
+ options.trendingLanguage ?? "none",
642
706
  );
643
707
  const cached = SearchModule.cache.get(cacheKey);
644
708
  if (cached) {
package/src/types.ts CHANGED
@@ -73,6 +73,8 @@ export interface SearchOptions {
73
73
  page?: number;
74
74
  /** Trending period: daily|weekly|monthly. Used by TrendingAdapter. */
75
75
  trendingSince?: "daily" | "weekly" | "monthly";
76
+ /** Trending language filter (e.g. "rust", "python"). Used by TrendingAdapter. */
77
+ trendingLanguage?: string;
76
78
  /** Verbose logging (outgoing query, errors, rate limits). */
77
79
  verbose: boolean;
78
80
  }