github-search-cli 9.7.0 → 9.7.1
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 +29 -0
- package/README.md +7 -5
- package/bin/ghfind.js +5 -5
- package/dist/cli.js +255 -110
- package/package.json +3 -2
- package/release-notes/9.7.1.md +62 -0
- package/scripts/build.ts +1 -1
- package/scripts/postinstall.js +8 -8
- package/src/cli.ts +21 -13
- package/src/compare.ts +5 -5
- package/src/config.ts +3 -3
- package/src/deepdive.ts +0 -1
- package/src/doctor.ts +144 -8
- package/src/error-report.ts +14 -8
- package/src/export.ts +1 -1
- package/src/history.ts +5 -5
- package/src/init.ts +2 -2
- package/src/login.ts +4 -4
- package/src/markdown-render.ts +13 -13
- package/src/mcp-server.ts +4 -3
- package/src/open-url.ts +16 -5
- package/src/output.ts +1 -1
- package/src/package-browser.ts +1 -1
- package/src/releases.ts +1 -14
- package/src/search-state.ts +1 -4
- package/src/search.ts +3 -4
- package/src/share.ts +2 -2
- package/src/storage.ts +4 -4
- package/src/themes.ts +1 -0
- package/src/trending-parser.ts +3 -4
- package/src/trending.ts +7 -7
- package/src/tui.ts +38 -42
- package/src/update-check.ts +96 -34
- package/src/version.ts +3 -3
- package/src/watch.ts +1 -1
package/src/update-check.ts
CHANGED
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
* Persists check state in the XDG state dir via storage.ts.
|
|
6
6
|
*/
|
|
7
7
|
import { readJSON, writeJSON, debugLog } from "./storage";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { fileURLToPath } from "url";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { join, dirname, posix } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
12
11
|
|
|
13
12
|
const REGISTRY_URL = "https://registry.npmjs.org/github-search-cli/latest";
|
|
13
|
+
// The npm package name is `github-search-cli`; `ghfind` is the bin name.
|
|
14
|
+
const PKG_NAME = "github-search-cli";
|
|
14
15
|
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
15
16
|
const REQUEST_TIMEOUT_MS = 5000;
|
|
16
17
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -147,39 +148,100 @@ export function suppressUpdateNotices(): void {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
151
|
+
* Where the running install lives and which package manager owns it.
|
|
152
|
+
*
|
|
153
|
+
* Detection walks up from the package dir to its `node_modules` ancestor:
|
|
154
|
+
* - npm global install: `<prefix>/node_modules/<pkg>` (Windows) or
|
|
155
|
+
* `<prefix>/lib/node_modules/<pkg>` (Unix)
|
|
156
|
+
* - Bun global install: `<bun-home>/install/global/node_modules/<pkg>`
|
|
157
|
+
* - Anything else (compiled binary, `bunx`/`npx` cache edges, dev checkout
|
|
158
|
+
* without a `node_modules` ancestor): `null` — self-update is refused
|
|
159
|
+
* rather than npm-installing a copy the user isn't running.
|
|
160
|
+
*/
|
|
161
|
+
export interface InstallLocation {
|
|
162
|
+
channel: "npm" | "bun";
|
|
163
|
+
/** npm prefix that owns the running install (npm channel only). */
|
|
164
|
+
prefix?: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Detect the install channel and owning npm prefix for a package directory.
|
|
169
|
+
* Separators are normalized so layouts match regardless of the host OS.
|
|
152
170
|
*/
|
|
153
|
-
export
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
171
|
+
export function detectInstallLocation(pkgDir: string): InstallLocation | null {
|
|
172
|
+
const norm = pkgDir.split("\\").join("/");
|
|
173
|
+
const nodeModules = posix.dirname(norm);
|
|
174
|
+
if (posix.basename(nodeModules) !== "node_modules") return null;
|
|
175
|
+
const owner = posix.dirname(nodeModules);
|
|
176
|
+
// Bun's global root is ~/.bun by default (BUN_INSTALL override may rename
|
|
177
|
+
// it, but the default layout is what `bun install -g` produces for the
|
|
178
|
+
// overwhelming majority of installs).
|
|
179
|
+
if (/(^|\/)\.bun(\/|$)/.test(owner)) return { channel: "bun" };
|
|
180
|
+
// Unix npm global layout nests one level deeper: <prefix>/lib/node_modules
|
|
181
|
+
const prefix = posix.basename(owner) === "lib" ? posix.dirname(owner) : owner;
|
|
182
|
+
return { channel: "npm", prefix };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build the install command for an update, targeting the npm prefix that
|
|
187
|
+
* owns the running install. Under Bun this must NOT be `bun install -g`:
|
|
188
|
+
* that writes to Bun's own global dir, leaving the running npm copy stale
|
|
189
|
+
* while still exiting 0.
|
|
190
|
+
*/
|
|
191
|
+
export function buildUpdateCommand(loc: InstallLocation): string[] {
|
|
192
|
+
if (loc.channel === "bun") return ["bun", "install", "-g", PKG_NAME];
|
|
193
|
+
const args = ["npm", "install", "-g"];
|
|
194
|
+
if (loc.prefix) args.push("--prefix", loc.prefix);
|
|
195
|
+
args.push(PKG_NAME);
|
|
196
|
+
return args;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Minimal quoting for win32 spawns, which go through a shell. */
|
|
200
|
+
function quoteWin32(arg: string): string {
|
|
201
|
+
return /\s/.test(arg) ? `"${arg}"` : arg;
|
|
202
|
+
}
|
|
161
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Attempt to update ghfind in-place.
|
|
206
|
+
*
|
|
207
|
+
* Runs the install command through `node:child_process` on every runtime
|
|
208
|
+
* (works under Bun and Node alike; on Windows npm is `npm.cmd`, which needs
|
|
209
|
+
* `shell: true` to resolve). Always returns a definitive boolean — the TUI
|
|
210
|
+
* surfaces false as "Update failed".
|
|
211
|
+
*/
|
|
212
|
+
export async function performUpdate(
|
|
213
|
+
location: InstallLocation | null = detectInstallLocation(PKG_DIR),
|
|
214
|
+
): Promise<boolean> {
|
|
215
|
+
if (!location) {
|
|
216
|
+
debugLog(
|
|
217
|
+
"Update skipped: the running install is not under a node_modules tree " +
|
|
218
|
+
"(compiled binary or dev checkout). Update it manually.",
|
|
219
|
+
);
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
const argv = buildUpdateCommand(location);
|
|
162
223
|
try {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return proc.exitCode === 0;
|
|
174
|
-
}
|
|
175
|
-
// Node fallback
|
|
176
|
-
const { spawn } = await import("child_process");
|
|
177
|
-
return await new Promise<boolean>((resolve) => {
|
|
178
|
-
const proc = spawn(cmd[0], cmd.slice(1), { stdio: "ignore" });
|
|
179
|
-
proc.on("close", (code) => resolve(code === 0));
|
|
180
|
-
proc.on("error", () => resolve(false));
|
|
224
|
+
const { spawn } = await import("node:child_process");
|
|
225
|
+
const useShell = process.platform === "win32";
|
|
226
|
+
const proc = spawn(
|
|
227
|
+
argv[0],
|
|
228
|
+
useShell ? argv.slice(1).map(quoteWin32) : argv.slice(1),
|
|
229
|
+
{ stdio: ["ignore", "ignore", "pipe"], shell: useShell },
|
|
230
|
+
);
|
|
231
|
+
let stderr = "";
|
|
232
|
+
proc.stderr?.on("data", (chunk: Buffer) => {
|
|
233
|
+
stderr += String(chunk);
|
|
181
234
|
});
|
|
182
|
-
|
|
235
|
+
const code = await new Promise<number | null>((resolve) => {
|
|
236
|
+
proc.on("close", (c) => resolve(c));
|
|
237
|
+
proc.on("error", () => resolve(-1));
|
|
238
|
+
});
|
|
239
|
+
if (code !== 0) {
|
|
240
|
+
debugLog(`Install failed (code ${code}): ${stderr.trim()}`);
|
|
241
|
+
}
|
|
242
|
+
return code === 0;
|
|
243
|
+
} catch (e) {
|
|
244
|
+
debugLog(`Update failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
183
245
|
return false;
|
|
184
246
|
}
|
|
185
247
|
}
|
|
@@ -215,7 +277,7 @@ export function fetchReleaseNotes(version: string): string | null {
|
|
|
215
277
|
const changelogPath = join(PKG_DIR, "CHANGELOG.md");
|
|
216
278
|
const changelog = readFileSync(changelogPath, "utf-8");
|
|
217
279
|
// ponytail: find "## [version]" header, slice until next "## "
|
|
218
|
-
const header =
|
|
280
|
+
const header = `## [${version}]`;
|
|
219
281
|
const start = changelog.indexOf(header);
|
|
220
282
|
if (start === -1) return null;
|
|
221
283
|
const bodyStart = start + header.length;
|
package/src/version.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* standalone single-file binaries where package.json is not present.
|
|
7
7
|
* Fallback: read package.json relative to this module (source / dist trees).
|
|
8
8
|
*/
|
|
9
|
-
import { readFileSync } from "fs";
|
|
10
|
-
import { join, dirname } from "path";
|
|
11
|
-
import { fileURLToPath } from "url";
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { join, dirname } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
12
|
|
|
13
13
|
declare const __GHFIND_VERSION__: string | undefined;
|
|
14
14
|
|