github-search-cli 9.4.1 → 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/CHANGELOG.md +42 -0
- package/README.md +263 -237
- package/dist/cli.js +448 -91
- package/package.json +11 -3
- package/src/cli.ts +634 -566
- package/src/error-report.ts +8 -12
- package/src/org.ts +271 -0
- package/src/tui.ts +3019 -2867
- package/src/update-check.ts +231 -215
- package/src/version.ts +33 -0
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
|
|
499
|
-
if (
|
|
498
|
+
const cached2 = _SearchModule.cache.get(cacheKey);
|
|
499
|
+
if (cached2) {
|
|
500
500
|
this.logger.debug("[search] cache hit");
|
|
501
|
-
return
|
|
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 {
|
|
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
|
|
1140
|
-
|
|
1141
|
-
|
|
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
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
-
|
|
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
|
|
1730
|
-
import { join as
|
|
1731
|
-
import { fileURLToPath as
|
|
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)
|
|
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)
|
|
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",
|
|
2654
|
+
const cmd = isBun ? ["bun", "install", "-g", pkg] : ["npm", "install", "-g", pkg];
|
|
2471
2655
|
try {
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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(
|
|
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
|
|
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
|
|
4930
|
-
var
|
|
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
|
-
|
|
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 =
|
|
5106
|
-
|
|
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(
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
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(
|
|
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(
|
|
5224
|
-
${r.
|
|
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) {
|