github-search-cli 9.4.0 → 9.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
@@ -495,10 +495,10 @@ var SearchModule = class _SearchModule {
495
495
  String(options.page ?? 1),
496
496
  options.trendingSince ?? "none"
497
497
  );
498
- const cached = _SearchModule.cache.get(cacheKey);
499
- if (cached) {
498
+ const cached2 = _SearchModule.cache.get(cacheKey);
499
+ if (cached2) {
500
500
  this.logger.debug("[search] cache hit");
501
- return cached;
501
+ return cached2;
502
502
  }
503
503
  this.logger.debug("[search] cache miss");
504
504
  const response = await this.adapter.search(query, options);
@@ -1131,22 +1131,42 @@ async function runDoctor() {
1131
1131
  }
1132
1132
 
1133
1133
  // src/error-report.ts
1134
- import { platform as osPlatform, arch as osArch, version as nodeVersion, versions as nodeVersions } from "process";
1134
+ import {
1135
+ platform as osPlatform,
1136
+ arch as osArch,
1137
+ version as nodeVersion,
1138
+ versions as nodeVersions
1139
+ } from "process";
1135
1140
  import { spawn } from "child_process";
1141
+
1142
+ // src/version.ts
1136
1143
  import { readFileSync as readFileSync2 } from "fs";
1137
1144
  import { join as join2, dirname as dirname2 } from "path";
1138
1145
  import { fileURLToPath as fileURLToPath2 } from "url";
1139
- var REPO = "codersdfs/search-cli";
1140
- var NEW_ISSUE_URL = `https://github.com/${REPO}/issues/new`;
1141
- function getPackageVersion() {
1146
+ var cached = null;
1147
+ function getVersion() {
1148
+ if (cached) return cached;
1149
+ if (true) {
1150
+ cached = "9.4.2";
1151
+ return cached;
1152
+ }
1142
1153
  try {
1143
- const __dirname5 = dirname2(fileURLToPath2(import.meta.url));
1144
- const pkgPath = join2(__dirname5, "..", "package.json");
1145
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
1146
- return pkg.version ?? "unknown";
1154
+ const here = dirname2(fileURLToPath2(import.meta.url));
1155
+ const pkg = JSON.parse(
1156
+ readFileSync2(join2(here, "..", "package.json"), "utf-8")
1157
+ );
1158
+ cached = pkg.version ?? "unknown";
1147
1159
  } catch {
1148
- return "unknown";
1160
+ cached = "unknown";
1149
1161
  }
1162
+ return cached;
1163
+ }
1164
+
1165
+ // src/error-report.ts
1166
+ var REPO = "codersdfs/search-cli";
1167
+ var NEW_ISSUE_URL = `https://github.com/${REPO}/issues/new`;
1168
+ function getPackageVersion() {
1169
+ return getVersion();
1150
1170
  }
1151
1171
  function runtimeName() {
1152
1172
  const bunVer = nodeVersions.bun;
@@ -1374,6 +1394,170 @@ async function runWatch(opts, onChange, onError) {
1374
1394
  }
1375
1395
  }
1376
1396
 
1397
+ // src/org.ts
1398
+ function apiHeaders(token) {
1399
+ return {
1400
+ Accept: "application/vnd.github+json",
1401
+ "X-GitHub-Api-Version": "2022-11-28",
1402
+ "User-Agent": "ghfind",
1403
+ ...token ? { Authorization: `Bearer ${token}` } : {}
1404
+ };
1405
+ }
1406
+ function normalizeOrgRepo(item) {
1407
+ return {
1408
+ fullName: item.full_name ?? "",
1409
+ url: item.html_url ?? "",
1410
+ description: item.description ?? null,
1411
+ language: item.language ?? null,
1412
+ stars: item.stargazers_count ?? 0,
1413
+ forks: item.forks_count ?? 0,
1414
+ pushedAt: item.pushed_at ?? ""
1415
+ };
1416
+ }
1417
+ function normalizeOrg(payload, repos) {
1418
+ const orgRepos = (repos ?? []).map(normalizeOrgRepo);
1419
+ const langCounts = /* @__PURE__ */ new Map();
1420
+ for (const r of orgRepos) {
1421
+ if (!r.language) continue;
1422
+ langCounts.set(r.language, (langCounts.get(r.language) ?? 0) + 1);
1423
+ }
1424
+ const topLanguages = [...langCounts.entries()].map(([language, count]) => ({ language, repos: count })).sort((a, b) => b.repos - a.repos || a.language.localeCompare(b.language)).slice(0, 5);
1425
+ const byStars = [...orgRepos].sort(
1426
+ (a, b) => b.stars - a.stars || a.fullName.localeCompare(b.fullName)
1427
+ );
1428
+ const byPushed = [...orgRepos].filter((r) => r.pushedAt).sort((a, b) => Date.parse(b.pushedAt) - Date.parse(a.pushedAt));
1429
+ return {
1430
+ login: payload.login ?? "",
1431
+ name: payload.name ?? null,
1432
+ description: payload.description ?? null,
1433
+ url: payload.html_url ?? "",
1434
+ blog: payload.blog ?? null,
1435
+ location: payload.location ?? null,
1436
+ createdAt: payload.created_at ?? "",
1437
+ publicRepoCount: payload.public_repos ?? 0,
1438
+ fetchedRepoCount: orgRepos.length,
1439
+ totalStars: orgRepos.reduce((sum, r) => sum + r.stars, 0),
1440
+ totalForks: orgRepos.reduce((sum, r) => sum + r.forks, 0),
1441
+ topLanguages,
1442
+ topRepos: byStars.slice(0, 5),
1443
+ activeRepos: byPushed.slice(0, 5)
1444
+ };
1445
+ }
1446
+ async function fetchOrgProfile(org, options = {}) {
1447
+ const name = org.trim().replace(/^@/, "");
1448
+ if (!name) throw new Error("Usage: ghfind org <org-name>");
1449
+ const token = options.token;
1450
+ const limit = Math.min(Math.max(options.limit ?? 100, 1), 500);
1451
+ let orgPayload;
1452
+ try {
1453
+ const res = await fetch(`https://api.github.com/orgs/${encodeURIComponent(name)}`, {
1454
+ headers: apiHeaders(token)
1455
+ });
1456
+ if (res.status === 404) {
1457
+ throw new Error(`Organization "${name}" not found.`);
1458
+ }
1459
+ if (!res.ok) {
1460
+ const body = await res.text().catch(() => "");
1461
+ throw new Error(`GitHub API error ${res.status}: ${body.slice(0, 200)}`);
1462
+ }
1463
+ orgPayload = await res.json();
1464
+ } catch (err) {
1465
+ if (err instanceof Error && err.message.includes(`Organization "${name}"`)) throw err;
1466
+ throw new Error(`Failed to fetch org "${name}": ${err instanceof Error ? err.message : String(err)}`);
1467
+ }
1468
+ const repos = [];
1469
+ if (orgPayload.public_repos > 0) {
1470
+ const perPage = Math.min(limit, 100);
1471
+ const maxPages = Math.ceil(limit / perPage);
1472
+ for (let page = 1; page <= maxPages; page++) {
1473
+ const url = `https://api.github.com/orgs/${encodeURIComponent(name)}/repos?per_page=${perPage}&sort=pushed&page=${page}`;
1474
+ const res = await fetch(url, { headers: apiHeaders(token) });
1475
+ if (!res.ok) {
1476
+ break;
1477
+ }
1478
+ const items = await res.json();
1479
+ if (!Array.isArray(items)) break;
1480
+ repos.push(...items);
1481
+ if (repos.length >= limit || items.length < perPage) break;
1482
+ }
1483
+ }
1484
+ return normalizeOrg(orgPayload, repos.slice(0, limit));
1485
+ }
1486
+ function formatOrgText(p) {
1487
+ const lines = [];
1488
+ const title = p.name ? `${p.login} \u2014 ${p.name}` : p.login;
1489
+ lines.push(title);
1490
+ if (p.description) lines.push(p.description);
1491
+ const meta = [];
1492
+ if (p.location) meta.push(`Location ${p.location}`);
1493
+ if (p.blog) meta.push(`Web ${p.blog}`);
1494
+ if (p.createdAt) meta.push(`Created ${p.createdAt.slice(0, 10)}`);
1495
+ if (meta.length > 0) lines.push(meta.join("\n"));
1496
+ lines.push(`Repos ${p.publicRepoCount.toLocaleString()} public${p.fetchedRepoCount < p.publicRepoCount ? ` (aggregated ${p.fetchedRepoCount})` : ""}`);
1497
+ lines.push(`Stars ${p.totalStars.toLocaleString()} (sum of aggregated repos)`);
1498
+ lines.push(`Forks ${p.totalForks.toLocaleString()} (sum of aggregated repos)`);
1499
+ lines.push(`URL ${p.url}`);
1500
+ if (p.topLanguages.length > 0) {
1501
+ const langs = p.topLanguages.map((l) => `${l.language} (${l.repos})`).join(", ");
1502
+ lines.push(`Languages ${langs}`);
1503
+ }
1504
+ if (p.topRepos.length > 0) {
1505
+ lines.push("");
1506
+ lines.push("Top repos by stars:");
1507
+ for (const r of p.topRepos) {
1508
+ lines.push(` ${r.fullName} \u2605 ${r.stars.toLocaleString()} ${r.language ?? ""}`);
1509
+ }
1510
+ }
1511
+ if (p.activeRepos.length > 0) {
1512
+ lines.push("");
1513
+ lines.push("Recently active:");
1514
+ for (const r of p.activeRepos) {
1515
+ lines.push(` ${r.fullName} pushed ${r.pushedAt.slice(0, 10)}`);
1516
+ }
1517
+ }
1518
+ return lines.join("\n");
1519
+ }
1520
+ function formatOrgJson(p) {
1521
+ return JSON.stringify(p, null, 2);
1522
+ }
1523
+ function formatOrgCsv(p) {
1524
+ const esc = (v) => /[",\n]/.test(v) ? `"${v.replace(/"/g, '""')}"` : v;
1525
+ const row = [
1526
+ p.login,
1527
+ p.name ?? "",
1528
+ p.url,
1529
+ String(p.publicRepoCount),
1530
+ String(p.totalStars),
1531
+ String(p.totalForks),
1532
+ p.topLanguages.map((l) => l.language).join(";"),
1533
+ p.createdAt
1534
+ ].map(esc);
1535
+ return ["login,name,url,public_repos,total_stars,total_forks,top_languages,created_at", row.join(",")].join("\n");
1536
+ }
1537
+ function formatOrgMarkdown(p) {
1538
+ const lines = [];
1539
+ lines.push(`# ${p.login}${p.name ? ` \u2014 ${p.name}` : ""}`);
1540
+ if (p.description) lines.push("", p.description);
1541
+ lines.push(
1542
+ "",
1543
+ "| Metric | Value |",
1544
+ "| --- | --- |",
1545
+ `| Public repos | ${p.publicRepoCount} |`,
1546
+ `| Total stars (aggregated) | ${p.totalStars.toLocaleString()} |`,
1547
+ `| Total forks (aggregated) | ${p.totalForks.toLocaleString()} |`
1548
+ );
1549
+ if (p.topLanguages.length > 0) {
1550
+ lines.push(`| Top languages | ${p.topLanguages.map((l) => `${l.language} (${l.repos})`).join(", ")} |`);
1551
+ }
1552
+ if (p.topRepos.length > 0) {
1553
+ lines.push("", "## Top repos by stars", "", "| Repo | Stars | Language |", "| --- | --- | --- |");
1554
+ for (const r of p.topRepos) {
1555
+ lines.push(`| [${r.fullName}](${r.url}) | ${r.stars.toLocaleString()} | ${r.language ?? "\u2014"} |`);
1556
+ }
1557
+ }
1558
+ return lines.join("\n");
1559
+ }
1560
+
1377
1561
  // src/storage.ts
1378
1562
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, appendFileSync, unlinkSync } from "fs";
1379
1563
  import { join as join3 } from "path";
@@ -1726,9 +1910,9 @@ function buildComparisonTable(repos) {
1726
1910
  }
1727
1911
 
1728
1912
  // src/cli.ts
1729
- import { readFileSync as readFileSync6 } from "fs";
1730
- import { join as join6, dirname as dirname5 } from "path";
1731
- import { fileURLToPath as fileURLToPath5 } from "url";
1913
+ import { readFileSync as readFileSync5 } from "fs";
1914
+ import { join as join5, dirname as dirname4 } from "path";
1915
+ import { fileURLToPath as fileURLToPath4 } from "url";
1732
1916
 
1733
1917
  // src/tui.ts
1734
1918
  import {
@@ -2375,11 +2559,6 @@ function nextTip() {
2375
2559
  return defaultRotator.next();
2376
2560
  }
2377
2561
 
2378
- // src/tui.ts
2379
- import { readFileSync as readFileSync5 } from "fs";
2380
- import { join as join5, dirname as dirname4 } from "path";
2381
- import { fileURLToPath as fileURLToPath4 } from "url";
2382
-
2383
2562
  // src/update-check.ts
2384
2563
  import { readFileSync as readFileSync4 } from "fs";
2385
2564
  import { join as join4, dirname as dirname3 } from "path";
@@ -2423,7 +2602,8 @@ function isNewerVersion(current, latest) {
2423
2602
  return false;
2424
2603
  }
2425
2604
  async function checkForUpdate(currentVersion) {
2426
- if (process.env.DEBUG) debugLog(`Checking for update (current: ${currentVersion})`);
2605
+ if (process.env.DEBUG)
2606
+ debugLog(`Checking for update (current: ${currentVersion})`);
2427
2607
  try {
2428
2608
  const res = await fetch(REGISTRY_URL, {
2429
2609
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
@@ -2442,7 +2622,10 @@ async function checkForUpdate(currentVersion) {
2442
2622
  if (!isNewerVersion(currentVersion, latest)) return null;
2443
2623
  return latest;
2444
2624
  } catch (e) {
2445
- if (process.env.DEBUG) debugLog(`Update check failed: ${e instanceof Error ? e.message : String(e)}`);
2625
+ if (process.env.DEBUG)
2626
+ debugLog(
2627
+ `Update check failed: ${e instanceof Error ? e.message : String(e)}`
2628
+ );
2446
2629
  return null;
2447
2630
  }
2448
2631
  }
@@ -2466,19 +2649,28 @@ function suppressUpdateNotices() {
2466
2649
  });
2467
2650
  }
2468
2651
  async function performUpdate() {
2652
+ const pkg = "github-search-cli";
2469
2653
  const isBun = typeof process.versions !== "undefined" && "bun" in process.versions;
2470
- const cmd = isBun ? ["bun", "install", "-g", "ghfind"] : ["npm", "install", "-g", "ghfind"];
2654
+ const cmd = isBun ? ["bun", "install", "-g", pkg] : ["npm", "install", "-g", pkg];
2471
2655
  try {
2472
- const proc = Bun.spawn(cmd, {
2473
- stdout: "pipe",
2474
- stderr: "pipe"
2475
- });
2476
- await proc.exited;
2477
- if (proc.exitCode !== 0) {
2478
- const err = await proc.stderr.text();
2479
- debugLog(`Install failed (code ${proc.exitCode}): ${err}`);
2656
+ if (isBun) {
2657
+ const proc = Bun.spawn(cmd, {
2658
+ stdout: "pipe",
2659
+ stderr: "pipe"
2660
+ });
2661
+ await proc.exited;
2662
+ if (proc.exitCode !== 0) {
2663
+ const err = await proc.stderr.text();
2664
+ debugLog(`Install failed (code ${proc.exitCode}): ${err}`);
2665
+ }
2666
+ return proc.exitCode === 0;
2480
2667
  }
2481
- return proc.exitCode === 0;
2668
+ const { spawn: spawn3 } = await import("child_process");
2669
+ return await new Promise((resolve) => {
2670
+ const proc = spawn3(cmd[0], cmd.slice(1), { stdio: "ignore" });
2671
+ proc.on("close", (code) => resolve(code === 0));
2672
+ proc.on("error", () => resolve(false));
2673
+ });
2482
2674
  } catch {
2483
2675
  return false;
2484
2676
  }
@@ -2513,8 +2705,6 @@ function fetchReleaseNotes(version) {
2513
2705
  }
2514
2706
 
2515
2707
  // src/tui.ts
2516
- var __dirname3 = dirname4(fileURLToPath4(import.meta.url));
2517
- var PKG_DIR3 = join5(__dirname3, "..");
2518
2708
  var cachedVersion = null;
2519
2709
  var colors = {
2520
2710
  bg: "#3D3B3B",
@@ -2885,7 +3075,10 @@ async function launchBrowser(theme_override) {
2885
3075
  updateLandingCards();
2886
3076
  renderer.requestRender();
2887
3077
  } else if (key.name === "down" || key.name === "j") {
2888
- landingSelected = Math.min(landingOptions.length - 1, landingSelected + 1);
3078
+ landingSelected = Math.min(
3079
+ landingOptions.length - 1,
3080
+ landingSelected + 1
3081
+ );
2889
3082
  updateLandingCards();
2890
3083
  renderer.requestRender();
2891
3084
  } else if (key.name === "enter" || key.name === "return") {
@@ -2972,6 +3165,8 @@ async function launchBrowser(theme_override) {
2972
3165
  leaderBox.visible = true;
2973
3166
  } else if (type === "readme") {
2974
3167
  readmeBox.visible = true;
3168
+ } else if (type === "org") {
3169
+ orgBox.visible = true;
2975
3170
  } else if (type === "update") {
2976
3171
  updateDim.visible = true;
2977
3172
  updateBox.visible = true;
@@ -3623,6 +3818,19 @@ async function launchBrowser(theme_override) {
3623
3818
  });
3624
3819
  }
3625
3820
  repoItems.push(
3821
+ {
3822
+ type: "action",
3823
+ name: "Org profile",
3824
+ description: "Org summary for the selected repo's owner",
3825
+ action: () => {
3826
+ const opt = resultsSelect.getSelectedOption();
3827
+ const repo = opt?.value;
3828
+ if (repo?.owner) {
3829
+ showOverlay("none");
3830
+ showOrgProfile(repo.owner);
3831
+ }
3832
+ }
3833
+ },
3626
3834
  {
3627
3835
  type: "action",
3628
3836
  name: "Bookmark",
@@ -3653,15 +3861,28 @@ async function launchBrowser(theme_override) {
3653
3861
  });
3654
3862
  }
3655
3863
  const navItems = [];
3656
- navItems.push({
3657
- type: "action",
3658
- name: "Packages",
3659
- description: "Search npm packages",
3660
- action: () => {
3661
- showOverlay("none");
3662
- showPackagesMode();
3864
+ navItems.push(
3865
+ {
3866
+ type: "action",
3867
+ name: "Packages",
3868
+ description: "Search npm packages",
3869
+ action: () => {
3870
+ showOverlay("none");
3871
+ showPackagesMode();
3872
+ }
3873
+ },
3874
+ {
3875
+ type: "action",
3876
+ name: "Org profile",
3877
+ description: "Look up any GitHub organization",
3878
+ action: () => {
3879
+ showOverlay("none");
3880
+ const opt = resultsSelect.getSelectedOption();
3881
+ const repo = opt?.value;
3882
+ showOrgProfile(repo?.owner ?? currentQueryInput.trim());
3883
+ }
3663
3884
  }
3664
- });
3885
+ );
3665
3886
  if (currentMode === "search") {
3666
3887
  navItems.push({
3667
3888
  type: "action",
@@ -3789,12 +4010,7 @@ async function launchBrowser(theme_override) {
3789
4010
  const state = readUpdateState();
3790
4011
  if (state.suppressed) return;
3791
4012
  if (!cachedVersion) {
3792
- try {
3793
- const pkg = JSON.parse(readFileSync5(join5(PKG_DIR3, "package.json"), "utf-8"));
3794
- cachedVersion = pkg.version;
3795
- } catch {
3796
- return;
3797
- }
4013
+ cachedVersion = getVersion();
3798
4014
  }
3799
4015
  const currentVersion2 = cachedVersion;
3800
4016
  if (!state.lastInstalledVersion) return;
@@ -3811,13 +4027,7 @@ ${notesBlock}`;
3811
4027
  async function checkForUpdateAndShow(force = false) {
3812
4028
  if (!force && !shouldCheckUpdate()) return;
3813
4029
  if (cachedVersion === null) {
3814
- try {
3815
- const pkg = JSON.parse(readFileSync5(join5(PKG_DIR3, "package.json"), "utf-8"));
3816
- cachedVersion = pkg.version;
3817
- } catch {
3818
- debugLog("Failed to read package.json");
3819
- return;
3820
- }
4030
+ cachedVersion = getVersion();
3821
4031
  }
3822
4032
  const currentVersion2 = cachedVersion;
3823
4033
  const latest = process.env.DEBUG_FORCE_UPDATE || await checkForUpdate(currentVersion2);
@@ -3919,6 +4129,102 @@ ${notesBlock}`;
3919
4129
  renderer.requestRender();
3920
4130
  }
3921
4131
  }
4132
+ const orgBox = new BoxRenderable3(renderer, {
4133
+ visible: false,
4134
+ flexGrow: 1,
4135
+ backgroundColor: colors.surface,
4136
+ borderBottom: true,
4137
+ borderBottomColor: colors.border,
4138
+ title: " Org profile ",
4139
+ titleColor: colors.green
4140
+ });
4141
+ const orgScroll = new ScrollBoxRenderable3(renderer, {
4142
+ flexGrow: 1,
4143
+ backgroundColor: colors.bg,
4144
+ scrollY: true,
4145
+ scrollX: false,
4146
+ paddingX: 1,
4147
+ viewportOptions: { backgroundColor: colors.bg },
4148
+ contentOptions: { backgroundColor: colors.bg, flexDirection: "column" },
4149
+ scrollbarOptions: {
4150
+ backgroundColor: colors.bg,
4151
+ foregroundColor: colors.muted,
4152
+ width: 1
4153
+ }
4154
+ });
4155
+ const orgText = new TextRenderable3(renderer, {
4156
+ content: "",
4157
+ color: colors.text,
4158
+ backgroundColor: colors.bg
4159
+ });
4160
+ orgScroll.add(orgText);
4161
+ orgBox.add(orgScroll);
4162
+ const orgFooter = new TextRenderable3(renderer, {
4163
+ content: " \u2191\u2193/jk scroll Esc/q close",
4164
+ color: colors.muted,
4165
+ backgroundColor: colors.bg,
4166
+ height: 1,
4167
+ paddingX: 1
4168
+ });
4169
+ orgBox.add(orgFooter);
4170
+ root.add(orgBox);
4171
+ function formatOrgSummary(p) {
4172
+ const lines = [];
4173
+ const title = p.name ? `${p.login} \u2014 ${p.name}` : p.login;
4174
+ lines.push(title);
4175
+ if (p.description) lines.push(p.description);
4176
+ if (p.location) lines.push(`Location ${p.location}`);
4177
+ if (p.blog) lines.push(`Web ${p.blog}`);
4178
+ if (p.createdAt) lines.push(`Created ${p.createdAt.slice(0, 10)}`);
4179
+ lines.push(
4180
+ `Repos ${p.publicRepoCount.toLocaleString()} public${p.fetchedRepoCount < p.publicRepoCount ? ` (aggregated ${p.fetchedRepoCount})` : ""}`
4181
+ );
4182
+ lines.push(
4183
+ `Stars ${p.totalStars.toLocaleString()} (sum of aggregated repos)`
4184
+ );
4185
+ lines.push(
4186
+ `Forks ${p.totalForks.toLocaleString()} (sum of aggregated repos)`
4187
+ );
4188
+ if (p.topLanguages.length > 0) {
4189
+ lines.push(
4190
+ `Languages ${p.topLanguages.map((l) => `${l.language} (${l.repos})`).join(", ")}`
4191
+ );
4192
+ }
4193
+ if (p.topRepos.length > 0) {
4194
+ lines.push("");
4195
+ lines.push("Top repos by stars:");
4196
+ for (const r of p.topRepos) {
4197
+ lines.push(
4198
+ ` ${r.fullName} \u2605 ${r.stars.toLocaleString()} ${r.language ?? ""}`
4199
+ );
4200
+ }
4201
+ }
4202
+ if (p.activeRepos.length > 0) {
4203
+ lines.push("");
4204
+ lines.push("Recently active:");
4205
+ for (const r of p.activeRepos) {
4206
+ lines.push(` ${r.fullName} pushed ${r.pushedAt.slice(0, 10)}`);
4207
+ }
4208
+ }
4209
+ return lines.join("\n");
4210
+ }
4211
+ async function showOrgProfile(owner) {
4212
+ const name = owner.trim().replace(/^@/, "");
4213
+ if (!name) {
4214
+ setStatus("No org selected");
4215
+ return;
4216
+ }
4217
+ orgBox.title = ` Org profile \u2014 ${name} `;
4218
+ orgText.content = ` Loading org profile for ${name}...`;
4219
+ showOverlay("org");
4220
+ try {
4221
+ const profile = await fetchOrgProfile(name, { token: githubToken });
4222
+ orgText.content = formatOrgSummary(profile);
4223
+ } catch (err) {
4224
+ orgText.content = ` Failed to load org profile: ${err instanceof Error ? err.message : String(err)}`;
4225
+ }
4226
+ renderer.requestRender();
4227
+ }
3922
4228
  const readmeBox = new BoxRenderable3(renderer, {
3923
4229
  visible: false,
3924
4230
  flexGrow: 1,
@@ -4157,12 +4463,16 @@ ${notesBlock}`;
4157
4463
  const chartW = Math.max(20, Math.min(60, termW));
4158
4464
  const chartH = 10;
4159
4465
  const chartLines = buildChartString(chartCommitData, chartW, chartH);
4160
- chartSection = ["", "Weekly commits (52 weeks)", ...chartLines].join("\n");
4466
+ chartSection = ["", "Weekly commits (52 weeks)", ...chartLines].join(
4467
+ "\n"
4468
+ );
4161
4469
  } else if (data?.message) {
4162
4470
  chartSection = ["", `Chart unavailable: ${data.message}`].join("\n");
4163
4471
  }
4164
4472
  } else {
4165
- chartSection = ["", `Chart unavailable (API ${chartRes.status})`].join("\n");
4473
+ chartSection = ["", `Chart unavailable (API ${chartRes.status})`].join(
4474
+ "\n"
4475
+ );
4166
4476
  }
4167
4477
  const desc = repo.description ?? "";
4168
4478
  const lang = repo.language ?? "\u2014";
@@ -4234,7 +4544,13 @@ ${notesBlock}`;
4234
4544
  const searchModule = new SearchModule(new TrendingAdapter());
4235
4545
  const response = await searchModule.search(
4236
4546
  { keywords: [], qualifiers: [], raw: "trending" },
4237
- { limit: 25, sort: "stars", json: false, verbose: false, trendingSince: tabSince(trendingTab) }
4547
+ {
4548
+ limit: 25,
4549
+ sort: "stars",
4550
+ json: false,
4551
+ verbose: false,
4552
+ trendingSince: tabSince(trendingTab)
4553
+ }
4238
4554
  );
4239
4555
  const fetched = response.repos;
4240
4556
  const since = tabSince(trendingTab);
@@ -4293,7 +4609,11 @@ ${notesBlock}`;
4293
4609
  searchInput.focus();
4294
4610
  packages = [];
4295
4611
  resultsSelect.options = [
4296
- { name: "", description: "Type a query and press Enter to search", value: null }
4612
+ {
4613
+ name: "",
4614
+ description: "Type a query and press Enter to search",
4615
+ value: null
4616
+ }
4297
4617
  ];
4298
4618
  detailText.content = "";
4299
4619
  renderer.requestRender();
@@ -4424,7 +4744,9 @@ ${notesBlock}`;
4424
4744
  }
4425
4745
  }
4426
4746
  function changePackageSort() {
4427
- const idx = PACKAGE_SORT_MODES.findIndex((m) => m.key === currentPackageSort);
4747
+ const idx = PACKAGE_SORT_MODES.findIndex(
4748
+ (m) => m.key === currentPackageSort
4749
+ );
4428
4750
  const next = PACKAGE_SORT_MODES[(idx + 1) % PACKAGE_SORT_MODES.length];
4429
4751
  currentPackageSort = next.key;
4430
4752
  setToolbar();
@@ -4533,6 +4855,9 @@ ${pack.description ?? ""}`;
4533
4855
  return;
4534
4856
  }
4535
4857
  }
4858
+ if (currentOverlay === "org") {
4859
+ return;
4860
+ }
4536
4861
  if (currentOverlay === "readme") {
4537
4862
  return;
4538
4863
  }
@@ -4866,17 +5191,7 @@ ${pack.description ?? ""}`;
4866
5191
  return;
4867
5192
  }
4868
5193
  });
4869
- const pkgPath = join5(
4870
- dirname4(fileURLToPath4(import.meta.url)),
4871
- "..",
4872
- "package.json"
4873
- );
4874
- let currentVersion = "0.0.0";
4875
- try {
4876
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
4877
- currentVersion = pkg.version;
4878
- } catch {
4879
- }
5194
+ const currentVersion = getVersion();
4880
5195
  if (!session || !session.mode) {
4881
5196
  showLanding(true);
4882
5197
  }
@@ -4926,8 +5241,8 @@ if (import.meta.main && false) {
4926
5241
 
4927
5242
  // src/cli.ts
4928
5243
  process.env.GHFIND_CLI_RUN = "1";
4929
- var __dirname4 = dirname5(fileURLToPath5(import.meta.url));
4930
- var PKG_DIR4 = join6(__dirname4, "..");
5244
+ var __dirname3 = dirname4(fileURLToPath4(import.meta.url));
5245
+ var PKG_DIR3 = join5(__dirname3, "..");
4931
5246
  function parseArgs(args) {
4932
5247
  const flags = {
4933
5248
  query: "",
@@ -5025,6 +5340,14 @@ function parseArgs(args) {
5025
5340
  case "--completion":
5026
5341
  flags.completion = args[++i];
5027
5342
  break;
5343
+ case "org": {
5344
+ const parts = [];
5345
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
5346
+ parts.push(args[++i]);
5347
+ }
5348
+ flags.org = parts.join("-");
5349
+ break;
5350
+ }
5028
5351
  case "--compare": {
5029
5352
  const repos = [];
5030
5353
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
@@ -5047,10 +5370,7 @@ function parseArgs(args) {
5047
5370
  async function main() {
5048
5371
  const flags = parseArgs(process.argv.slice(2));
5049
5372
  if (flags.version) {
5050
- const pkg = JSON.parse(
5051
- readFileSync6(join6(PKG_DIR4, "package.json"), "utf-8")
5052
- );
5053
- console.log(`ghfind v${pkg.version}`);
5373
+ console.log(`ghfind v${getVersion()}`);
5054
5374
  return;
5055
5375
  }
5056
5376
  if (flags.doctor) {
@@ -5072,6 +5392,7 @@ Usage:
5072
5392
  ghfind --trending --json Trending repos as JSON
5073
5393
  ghfind --releases Check bookmarks for new releases
5074
5394
  ghfind --compare <repo1> <repo2> Compare two+ repos side-by-side
5395
+ ghfind org <name> --json Org profile: repos, stars, top languages
5075
5396
  ghfind pkg <query> --json Search npm packages, output JSON
5076
5397
  ghfind pkg <query> Search npm packages, text list
5077
5398
  ghfind --watch <query> Watch mode (poll every Ns)
@@ -5102,8 +5423,8 @@ Options:
5102
5423
  if (flags.completion) {
5103
5424
  const shell = flags.completion;
5104
5425
  try {
5105
- const content = readFileSync6(
5106
- join6(PKG_DIR4, "completions", `ghfind.${shell}`),
5426
+ const content = readFileSync5(
5427
+ join5(PKG_DIR3, "completions", `ghfind.${shell}`),
5107
5428
  "utf-8"
5108
5429
  );
5109
5430
  console.log(content);
@@ -5127,6 +5448,10 @@ Options:
5127
5448
  return;
5128
5449
  }
5129
5450
  const outputFormat = flags.json ? "json" : flags.csv ? "csv" : flags.markdown ? "markdown" : void 0;
5451
+ if (flags.org !== void 0) {
5452
+ await runOrgProfile(flags, outputFormat);
5453
+ return;
5454
+ }
5130
5455
  const isNonInteractive = outputFormat || flags.count || flags.format || flags.pipe || flags.watch || flags.releases;
5131
5456
  if (isNonInteractive) {
5132
5457
  if (flags.releases) {
@@ -5146,6 +5471,28 @@ function buildSearchContext(flags) {
5146
5471
  provider: createGitHubSearch(void 0, flags.token ? [flags.token] : [])
5147
5472
  };
5148
5473
  }
5474
+ async function runOrgProfile(flags, outputFormat) {
5475
+ try {
5476
+ const profile = await fetchOrgProfile(flags.org, {
5477
+ token: flags.token,
5478
+ limit: flags.limit
5479
+ });
5480
+ if (outputFormat === "json") {
5481
+ console.log(formatOrgJson(profile));
5482
+ } else if (outputFormat === "csv") {
5483
+ console.log(formatOrgCsv(profile));
5484
+ } else if (outputFormat === "markdown") {
5485
+ console.log(formatOrgMarkdown(profile));
5486
+ } else if (flags.count) {
5487
+ console.log(profile.publicRepoCount);
5488
+ } else {
5489
+ console.log(formatOrgText(profile));
5490
+ }
5491
+ } catch (err) {
5492
+ console.error(err instanceof Error ? err.message : String(err));
5493
+ process.exit(1);
5494
+ }
5495
+ }
5149
5496
  async function runPackageSearch(flags) {
5150
5497
  if (flags.registry !== "npm") {
5151
5498
  console.error(`Registry "${flags.registry}" not yet supported (only npm).`);
@@ -5206,22 +5553,32 @@ async function runReleases(flags, outputFormat) {
5206
5553
  const results2 = await checkAndNotify({ token: flags.token });
5207
5554
  const releases = allCachedReleases();
5208
5555
  if (outputFormat === "json") {
5209
- console.log(JSON.stringify(releases.map((r) => ({
5210
- fullName: r.fullName,
5211
- tag: r.tagName,
5212
- name: r.name,
5213
- url: r.url,
5214
- publishedAt: r.publishedAt,
5215
- prerelease: r.prerelease
5216
- })), null, 2));
5556
+ console.log(
5557
+ JSON.stringify(
5558
+ releases.map((r) => ({
5559
+ fullName: r.fullName,
5560
+ tag: r.tagName,
5561
+ name: r.name,
5562
+ url: r.url,
5563
+ publishedAt: r.publishedAt,
5564
+ prerelease: r.prerelease
5565
+ })),
5566
+ null,
5567
+ 2
5568
+ )
5569
+ );
5217
5570
  return;
5218
5571
  }
5219
5572
  const unseen = results2.reduce((n, r) => n + r.newReleases.length, 0);
5220
5573
  const errors = results2.filter((r) => r.error).length;
5221
- console.log(`Checked ${results2.length} bookmarked repos \u2014 ${unseen} new release${unseen === 1 ? "" : "s"}${errors ? `, ${errors} error${errors === 1 ? "" : "s"}` : ""}.`);
5574
+ console.log(
5575
+ `Checked ${results2.length} bookmarked repos \u2014 ${unseen} new release${unseen === 1 ? "" : "s"}${errors ? `, ${errors} error${errors === 1 ? "" : "s"}` : ""}.`
5576
+ );
5222
5577
  for (const r of releases) {
5223
- console.log(`${r.fullName} ${r.prerelease ? "[pre] " : ""}${r.tagName} ${r.publishedAt.slice(0, 10)}
5224
- ${r.url}`);
5578
+ console.log(
5579
+ `${r.fullName} ${r.prerelease ? "[pre] " : ""}${r.tagName} ${r.publishedAt.slice(0, 10)}
5580
+ ${r.url}`
5581
+ );
5225
5582
  }
5226
5583
  }
5227
5584
  async function runNonInteractive(flags, outputFormat) {