@ff-labs/fff-bun 0.1.0-nightly.fcdf4a9 → 0.2.4-dev.8d9ef38

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/finder.ts CHANGED
@@ -40,33 +40,7 @@ import type {
40
40
  SearchResult,
41
41
  } from "./types";
42
42
 
43
- import {
44
- createGrepCursor,
45
- err,
46
- toInternalGrepOptions,
47
- toInternalInitOptions,
48
- toInternalMultiGrepOptions,
49
- toInternalSearchOptions,
50
- } from "./types";
51
-
52
- /** Transform raw FFI grep result into typed GrepResult with opaque cursor. */
53
- function transformGrepResult(result: Result<unknown>): Result<GrepResult> {
54
- if (!result.ok) {
55
- return result;
56
- }
57
- const raw = result.value as Record<string, unknown>;
58
- const nextFileOffset = raw.nextFileOffset as number;
59
- const grepResult: GrepResult = {
60
- items: raw.items as GrepResult["items"],
61
- totalMatched: raw.totalMatched as number,
62
- totalFilesSearched: raw.totalFilesSearched as number,
63
- totalFiles: raw.totalFiles as number,
64
- filteredFileCount: raw.filteredFileCount as number,
65
- nextCursor: nextFileOffset > 0 ? createGrepCursor(nextFileOffset) : null,
66
- regexFallbackError: raw.regexFallbackError as string | undefined,
67
- };
68
- return { ok: true, value: grepResult };
69
- }
43
+ import { err } from "./types";
70
44
 
71
45
  /**
72
46
  * FileFinder - Fast file finder with fuzzy search
@@ -127,8 +101,14 @@ export class FileFinder {
127
101
  * ```
128
102
  */
129
103
  static create(options: InitOptions): Result<FileFinder> {
130
- const internalOpts = toInternalInitOptions(options);
131
- const result = ffiCreate(JSON.stringify(internalOpts));
104
+ const result = ffiCreate(
105
+ options.basePath,
106
+ options.frecencyDbPath ?? "",
107
+ options.historyDbPath ?? "",
108
+ options.useUnsafeNoLock ?? false,
109
+ options.warmupMmapCache ?? false,
110
+ options.aiMode ?? false,
111
+ );
132
112
 
133
113
  if (!result.ok) {
134
114
  return result;
@@ -192,18 +172,20 @@ export class FileFinder {
192
172
  * }
193
173
  * ```
194
174
  */
195
- search(query: string, options?: SearchOptions): Result<SearchResult> {
175
+ fileSearch(query: string, options?: SearchOptions): Result<SearchResult> {
196
176
  const guard = this.ensureAlive();
197
177
  if (!guard.ok) return guard;
198
178
 
199
- const internalOpts = toInternalSearchOptions(options);
200
- const result = ffiSearch(guard.value, query, JSON.stringify(internalOpts));
201
-
202
- if (!result.ok) {
203
- return result;
204
- }
205
-
206
- return result as Result<SearchResult>;
179
+ return ffiSearch(
180
+ guard.value,
181
+ query,
182
+ options?.currentFile ?? "",
183
+ options?.maxThreads ?? 0,
184
+ options?.pageIndex ?? 0,
185
+ options?.pageSize ?? 0,
186
+ options?.comboBoostMultiplier ?? 0,
187
+ options?.minComboCount ?? 0,
188
+ );
207
189
  }
208
190
 
209
191
  /**
@@ -228,28 +210,38 @@ export class FileFinder {
228
210
  * @example
229
211
  * ```typescript
230
212
  * // First page
231
- * const result = finder.liveGrep("TODO", { mode: "plain" });
213
+ * const result = finder.grep("TODO", { mode: "plain" });
232
214
  * if (result.ok) {
233
215
  * for (const match of result.value.items) {
234
216
  * console.log(`${match.relativePath}:${match.lineNumber}: ${match.lineContent}`);
235
217
  * }
236
218
  * // Fetch next page
237
219
  * if (result.value.nextCursor) {
238
- * const page2 = finder.liveGrep("TODO", {
220
+ * const page2 = finder.grep("TODO", {
239
221
  * cursor: result.value.nextCursor,
240
222
  * });
241
223
  * }
242
224
  * }
243
225
  * ```
244
226
  */
245
- liveGrep(query: string, options?: GrepOptions): Result<GrepResult> {
227
+ grep(query: string, options?: GrepOptions): Result<GrepResult> {
246
228
  const guard = this.ensureAlive();
247
229
  if (!guard.ok) return guard;
248
230
 
249
- const internalOpts = toInternalGrepOptions(options);
250
- const result = ffiLiveGrep(guard.value, query, JSON.stringify(internalOpts));
251
-
252
- return transformGrepResult(result);
231
+ return ffiLiveGrep(
232
+ guard.value,
233
+ query,
234
+ options?.mode ?? "plain",
235
+ options?.maxFileSize ?? 0,
236
+ options?.maxMatchesPerFile ?? 0,
237
+ options?.smartCase ?? true,
238
+ options?.cursor?._offset ?? 0,
239
+ 0, // page_limit (0 = default 50)
240
+ options?.timeBudgetMs ?? 0,
241
+ options?.beforeContext ?? 0,
242
+ options?.afterContext ?? 0,
243
+ false,
244
+ );
253
245
  }
254
246
 
255
247
  /**
@@ -285,10 +277,20 @@ export class FileFinder {
285
277
  return err("patterns array must have at least 1 element");
286
278
  }
287
279
 
288
- const internalOpts = toInternalMultiGrepOptions(options);
289
- const result = ffiMultiGrep(guard.value, JSON.stringify(internalOpts));
290
-
291
- return transformGrepResult(result);
280
+ return ffiMultiGrep(
281
+ guard.value,
282
+ options.patterns.join("\n"),
283
+ options.constraints ?? "",
284
+ options.maxFileSize ?? 0,
285
+ options.maxMatchesPerFile ?? 0,
286
+ options.smartCase ?? true,
287
+ options.cursor?._offset ?? 0,
288
+ 0, // page_limit (0 = default 50)
289
+ options.timeBudgetMs ?? 0,
290
+ options.beforeContext ?? 0,
291
+ options.afterContext ?? 0,
292
+ false,
293
+ );
292
294
  }
293
295
 
294
296
  /**
@@ -412,14 +414,9 @@ export class FileFinder {
412
414
  return isAvailable();
413
415
  }
414
416
 
415
- /**
416
- * Ensure the native library is loaded.
417
- *
418
- * This will download the binary if needed and load it.
419
- * Useful for preloading before first use.
420
- */
421
- static async ensureLoaded(): Promise<void> {
422
- return ensureLoaded();
417
+ /** Ensure the native library is loaded. */
418
+ static ensureLoaded(): void {
419
+ ensureLoaded();
423
420
  }
424
421
 
425
422
  /**
@@ -433,3 +430,4 @@ export class FileFinder {
433
430
  return ffiHealthCheck(null, testPath || "") as Result<HealthCheck>;
434
431
  }
435
432
  }
433
+
@@ -51,7 +51,7 @@ function sleep(ms: number) {
51
51
  }
52
52
 
53
53
  function findFile(finder: FileFinder, name: string): FileItem | undefined {
54
- const result = finder.search(name, { pageSize: 200 });
54
+ const result = finder.fileSearch(name, { pageSize: 200 });
55
55
  if (!result.ok) throw new Error(`search failed: ${result.error}`);
56
56
  return result.value.items.find((item) => item.fileName === name);
57
57
  }
@@ -99,15 +99,15 @@ async function waitForFileGone(finder: FileFinder, name: string): Promise<boolea
99
99
  async function waitForFileCount(finder: FileFinder, count: number): Promise<number> {
100
100
  const start = Date.now();
101
101
  while (Date.now() - start < WATCHER_TIMEOUT_MS) {
102
- const result = finder.search("", { pageSize: 200 });
102
+ const result = finder.fileSearch("", { pageSize: 200 });
103
103
  if (result.ok && result.value.totalFiles === count) return count;
104
104
  await sleep(POLL_INTERVAL_MS);
105
105
  }
106
- const result = finder.search("", { pageSize: 200 });
106
+ const result = finder.fileSearch("", { pageSize: 200 });
107
107
  return result.ok ? result.value.totalFiles : -1;
108
108
  }
109
109
 
110
- /** Poll liveGrep until predicate on totalMatched is satisfied, or the timeout is exceeded. */
110
+ /** Poll grep until predicate on totalMatched is satisfied, or the timeout is exceeded. */
111
111
  async function waitForGrep(
112
112
  finder: FileFinder,
113
113
  pattern: string,
@@ -116,11 +116,11 @@ async function waitForGrep(
116
116
  ) {
117
117
  const start = Date.now();
118
118
  while (Date.now() - start < WATCHER_TIMEOUT_MS) {
119
- const result = finder.liveGrep(pattern, options);
119
+ const result = finder.grep(pattern, options);
120
120
  if (result.ok && predicate(result.value.totalMatched)) return result;
121
121
  await sleep(POLL_INTERVAL_MS);
122
122
  }
123
- return finder.liveGrep(pattern, options);
123
+ return finder.grep(pattern, options);
124
124
  }
125
125
 
126
126
  describe.skipIf(process.platform === "win32")("Git lifecycle integration", () => {
@@ -161,7 +161,7 @@ describe.skipIf(process.platform === "win32")("Git lifecycle integration", () =>
161
161
  });
162
162
 
163
163
  test("initial scan indexes all committed files", () => {
164
- const result = finder.search("", { pageSize: 200 });
164
+ const result = finder.fileSearch("", { pageSize: 200 });
165
165
  expect(result.ok).toBe(true);
166
166
  if (!result.ok) return;
167
167
 
package/src/index.test.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
- import { findBinary, getDevBinaryPath } from "./download";
2
+ import { findBinary } from "./download";
3
3
  import { FileFinder } from "./index";
4
4
  import { getLibExtension, getLibFilename, getTriple } from "./platform";
5
5
 
@@ -46,14 +46,6 @@ describe("Platform Detection", () => {
46
46
  });
47
47
 
48
48
  describe("Binary Detection", () => {
49
- test("getDevBinaryPath finds local build", () => {
50
- const devPath = getDevBinaryPath();
51
- expect(devPath).not.toBeNull();
52
- // Normalize path for cross-platform comparison (Windows uses backslashes)
53
- const normalizedPath = normalizePath(devPath);
54
- expect(normalizedPath).toContain("target/release");
55
- });
56
-
57
49
  test("findBinary returns a path", () => {
58
50
  const path = findBinary();
59
51
  expect(path).not.toBeNull();
@@ -120,7 +112,7 @@ describe("FileFinder - Full Lifecycle", () => {
120
112
  if (progress.ok) {
121
113
  }
122
114
 
123
- const result = finder.search("");
115
+ const result = finder.fileSearch("");
124
116
  expect(result.ok).toBe(true);
125
117
 
126
118
  if (result.ok) {
@@ -140,7 +132,7 @@ describe("FileFinder - Full Lifecycle", () => {
140
132
  });
141
133
 
142
134
  test("search returns a valid result structure", () => {
143
- const result = finder.search("Cargo.toml");
135
+ const result = finder.fileSearch("Cargo.toml");
144
136
  expect(result.ok).toBe(true);
145
137
 
146
138
  if (result.ok) {
@@ -152,7 +144,7 @@ describe("FileFinder - Full Lifecycle", () => {
152
144
  });
153
145
 
154
146
  test("search returns empty for non-matching query", () => {
155
- const result = finder.search("xyznonexistentfilenamexyz123456");
147
+ const result = finder.fileSearch("xyznonexistentfilenamexyz123456");
156
148
  expect(result.ok).toBe(true);
157
149
 
158
150
  if (result.ok) {
@@ -162,7 +154,7 @@ describe("FileFinder - Full Lifecycle", () => {
162
154
  });
163
155
 
164
156
  test("search respects pageSize option", () => {
165
- const result = finder.search("ts", { pageSize: 3 });
157
+ const result = finder.fileSearch("ts", { pageSize: 3 });
166
158
  expect(result.ok).toBe(true);
167
159
 
168
160
  if (result.ok) {
@@ -170,8 +162,8 @@ describe("FileFinder - Full Lifecycle", () => {
170
162
  }
171
163
  });
172
164
 
173
- test("liveGrep plain text returns matching lines", () => {
174
- const result = finder.liveGrep("fff-core", {
165
+ test("grep plain text returns matching lines", () => {
166
+ const result = finder.grep("fff-core", {
175
167
  mode: "plain",
176
168
  });
177
169
  expect(result.ok).toBe(true);
@@ -205,9 +197,9 @@ describe("FileFinder - Full Lifecycle", () => {
205
197
  }
206
198
  });
207
199
 
208
- test("liveGrep fuzzy mode returns results with scores", () => {
200
+ test("grep fuzzy mode returns results with scores", () => {
209
201
  // Intentional typo: "depdnency" instead of "dependency" to exercise fuzzy matching
210
- const result = finder.liveGrep("depdnency", {
202
+ const result = finder.grep("depdnency", {
211
203
  mode: "fuzzy",
212
204
  });
213
205
  expect(result.ok).toBe(true);
@@ -273,8 +265,8 @@ describe("FileFinder - Full Lifecycle", () => {
273
265
  const finder2 = result2.value;
274
266
 
275
267
  // Both should work independently
276
- const search1 = finder.search("Cargo");
277
- const search2 = finder2.search("Cargo");
268
+ const search1 = finder.fileSearch("Cargo");
269
+ const search2 = finder2.fileSearch("Cargo");
278
270
 
279
271
  expect(search1.ok).toBe(true);
280
272
  expect(search2.ok).toBe(true);
@@ -282,7 +274,7 @@ describe("FileFinder - Full Lifecycle", () => {
282
274
  // Destroying one should not affect the other
283
275
  finder2.destroy();
284
276
 
285
- const search3 = finder.search("Cargo");
277
+ const search3 = finder.fileSearch("Cargo");
286
278
  expect(search3.ok).toBe(true);
287
279
  }
288
280
  });
@@ -297,7 +289,7 @@ describe("FileFinder - Error Handling", () => {
297
289
  const f = createResult.value;
298
290
  f.destroy();
299
291
 
300
- const result = f.search("test");
292
+ const result = f.fileSearch("test");
301
293
  expect(result.ok).toBe(false);
302
294
  if (!result.ok) {
303
295
  expect(result.error).toContain("destroyed");
package/src/index.ts CHANGED
@@ -23,7 +23,7 @@
23
23
  * finder.waitForScan(5000);
24
24
  *
25
25
  * // Search for files
26
- * const search = finder.search("main.ts");
26
+ * const search = finder.fileSearch("main.ts");
27
27
  * if (search.ok) {
28
28
  * for (const item of search.value.items) {
29
29
  * console.log(item.relativePath);
@@ -37,15 +37,11 @@
37
37
  * @packageDocumentation
38
38
  */
39
39
 
40
- export { FileFinder } from "./finder";
41
-
42
40
  export {
43
41
  binaryExists,
44
- downloadBinary,
45
- ensureBinary,
46
42
  findBinary,
47
- getBinaryPath,
48
43
  } from "./download";
44
+ export { FileFinder } from "./finder";
49
45
 
50
46
  export {
51
47
  getLibExtension,
package/src/platform.ts CHANGED
@@ -95,20 +95,20 @@ export function getLibFilename(): string {
95
95
  * Map from Rust target triple to npm platform package name
96
96
  */
97
97
  const TRIPLE_TO_NPM_PACKAGE: Record<string, string> = {
98
- "aarch64-apple-darwin": "@ff-labs/fff-bun-darwin-arm64",
99
- "x86_64-apple-darwin": "@ff-labs/fff-bun-darwin-x64",
100
- "x86_64-unknown-linux-gnu": "@ff-labs/fff-bun-linux-x64-gnu",
101
- "aarch64-unknown-linux-gnu": "@ff-labs/fff-bun-linux-arm64-gnu",
102
- "x86_64-unknown-linux-musl": "@ff-labs/fff-bun-linux-x64-musl",
103
- "aarch64-unknown-linux-musl": "@ff-labs/fff-bun-linux-arm64-musl",
104
- "x86_64-pc-windows-msvc": "@ff-labs/fff-bun-win32-x64",
105
- "aarch64-pc-windows-msvc": "@ff-labs/fff-bun-win32-arm64",
98
+ "aarch64-apple-darwin": "@ff-labs/fff-bin-darwin-arm64",
99
+ "x86_64-apple-darwin": "@ff-labs/fff-bin-darwin-x64",
100
+ "x86_64-unknown-linux-gnu": "@ff-labs/fff-bin-linux-x64-gnu",
101
+ "aarch64-unknown-linux-gnu": "@ff-labs/fff-bin-linux-arm64-gnu",
102
+ "x86_64-unknown-linux-musl": "@ff-labs/fff-bin-linux-x64-musl",
103
+ "aarch64-unknown-linux-musl": "@ff-labs/fff-bin-linux-arm64-musl",
104
+ "x86_64-pc-windows-msvc": "@ff-labs/fff-bin-win32-x64",
105
+ "aarch64-pc-windows-msvc": "@ff-labs/fff-bin-win32-arm64",
106
106
  };
107
107
 
108
108
  /**
109
109
  * Get the npm package name for the current platform's native binary.
110
110
  *
111
- * @returns Package name like "@ff-labs/fff-bun-darwin-arm64"
111
+ * @returns Package name like "@ff-labs/fff-bin-darwin-arm64"
112
112
  * @throws If the current platform is not supported
113
113
  */
114
114
  export function getNpmPackageName(): string {
package/src/types.ts CHANGED
@@ -208,62 +208,6 @@ export interface HealthCheck {
208
208
  };
209
209
  }
210
210
 
211
- /**
212
- * Internal: Options format sent to Rust FFI
213
- * @internal
214
- */
215
- export interface InitOptionsInternal {
216
- base_path: string;
217
- frecency_db_path?: string;
218
- history_db_path?: string;
219
- use_unsafe_no_lock: boolean;
220
- warmup_mmap_cache: boolean;
221
- ai_mode: boolean;
222
- }
223
-
224
- /**
225
- * Internal: Search options format sent to Rust FFI
226
- * @internal
227
- */
228
- export interface SearchOptionsInternal {
229
- max_threads?: number;
230
- current_file?: string;
231
- combo_boost_multiplier?: number;
232
- min_combo_count?: number;
233
- page_index?: number;
234
- page_size?: number;
235
- }
236
-
237
- /**
238
- * Convert public InitOptions to internal format
239
- * @internal
240
- */
241
- export function toInternalInitOptions(opts: InitOptions): InitOptionsInternal {
242
- return {
243
- base_path: opts.basePath,
244
- frecency_db_path: opts.frecencyDbPath,
245
- history_db_path: opts.historyDbPath,
246
- use_unsafe_no_lock: opts.useUnsafeNoLock ?? false,
247
- warmup_mmap_cache: opts.warmupMmapCache ?? false,
248
- ai_mode: opts.aiMode ?? false,
249
- };
250
- }
251
-
252
- /**
253
- * Convert public SearchOptions to internal format
254
- * @internal
255
- */
256
- export function toInternalSearchOptions(opts?: SearchOptions): SearchOptionsInternal {
257
- return {
258
- max_threads: opts?.maxThreads,
259
- current_file: opts?.currentFile,
260
- combo_boost_multiplier: opts?.comboBoostMultiplier,
261
- min_combo_count: opts?.minComboCount,
262
- page_index: opts?.pageIndex,
263
- page_size: opts?.pageSize,
264
- };
265
- }
266
-
267
211
  /**
268
212
  * Grep search mode
269
213
  */
@@ -417,78 +361,3 @@ export interface MultiGrepOptions {
417
361
  afterContext?: number;
418
362
  }
419
363
 
420
- /**
421
- * Internal: Multi-grep options format sent to Rust FFI
422
- * @internal
423
- */
424
- export interface MultiGrepOptionsInternal {
425
- patterns: string[];
426
- constraints?: string;
427
- max_file_size?: number;
428
- max_matches_per_file?: number;
429
- smart_case?: boolean;
430
- file_offset?: number;
431
- page_limit?: number;
432
- time_budget_ms?: number;
433
- before_context?: number;
434
- after_context?: number;
435
- }
436
-
437
- /**
438
- * Convert public MultiGrepOptions to internal format
439
- * @internal
440
- */
441
- export function toInternalMultiGrepOptions(
442
- opts: MultiGrepOptions,
443
- pageLimit?: number,
444
- ): MultiGrepOptionsInternal {
445
- return {
446
- patterns: opts.patterns,
447
- constraints: opts.constraints,
448
- max_file_size: opts.maxFileSize,
449
- max_matches_per_file: opts.maxMatchesPerFile,
450
- smart_case: opts.smartCase,
451
- file_offset: opts.cursor?._offset ?? 0,
452
- page_limit: pageLimit,
453
- time_budget_ms: opts.timeBudgetMs,
454
- before_context: opts.beforeContext,
455
- after_context: opts.afterContext,
456
- };
457
- }
458
-
459
- /**
460
- * Internal: Grep options format sent to Rust FFI
461
- * @internal
462
- */
463
- export interface GrepOptionsInternal {
464
- max_file_size?: number;
465
- max_matches_per_file?: number;
466
- smart_case?: boolean;
467
- file_offset?: number;
468
- page_limit?: number;
469
- mode?: string;
470
- time_budget_ms?: number;
471
- before_context?: number;
472
- after_context?: number;
473
- }
474
-
475
- /**
476
- * Convert public GrepOptions to internal format
477
- * @internal
478
- */
479
- export function toInternalGrepOptions(
480
- opts?: GrepOptions,
481
- pageLimit?: number,
482
- ): GrepOptionsInternal {
483
- return {
484
- max_file_size: opts?.maxFileSize,
485
- max_matches_per_file: opts?.maxMatchesPerFile,
486
- smart_case: opts?.smartCase,
487
- file_offset: opts?.cursor?._offset ?? 0,
488
- page_limit: pageLimit,
489
- mode: opts?.mode,
490
- time_budget_ms: opts?.timeBudgetMs,
491
- before_context: opts?.beforeContext,
492
- after_context: opts?.afterContext,
493
- };
494
- }
Binary file
package/scripts/cli.ts DELETED
@@ -1,116 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * CLI tool for fff package management
4
- *
5
- * Usage:
6
- * bunx fff download [tag] - Download native binary from GitHub
7
- * bunx fff info - Show platform and binary info
8
- */
9
-
10
- import { downloadBinary, getBinaryPath, findBinary } from "../src/download";
11
- import {
12
- getTriple,
13
- getLibExtension,
14
- getLibFilename,
15
- getNpmPackageName,
16
- } from "../src/platform";
17
- import { dirname, join } from "node:path";
18
- import { fileURLToPath } from "node:url";
19
-
20
- const args = process.argv.slice(2);
21
- const command = args[0];
22
-
23
- interface PackageJson {
24
- version: string;
25
- }
26
-
27
- async function getPackageInfo(): Promise<PackageJson> {
28
- const currentDir = dirname(fileURLToPath(import.meta.url));
29
- const packageJsonPath = join(currentDir, "..", "package.json");
30
-
31
- try {
32
- return await Bun.file(packageJsonPath).json();
33
- } catch {
34
- return { version: "unknown" };
35
- }
36
- }
37
-
38
- async function main() {
39
- switch (command) {
40
- case "download": {
41
- const tag = args[1];
42
- console.log("fff: Downloading native library from GitHub...");
43
- try {
44
- const resolvedTag = await downloadBinary(tag);
45
- console.log(`fff: Download complete! (${resolvedTag})`);
46
- } catch (error) {
47
- console.error("fff: Download failed:", error);
48
- process.exit(1);
49
- }
50
- break;
51
- }
52
-
53
- case "info": {
54
- const pkg = await getPackageInfo();
55
- let npmPackage: string;
56
- try {
57
- npmPackage = getNpmPackageName();
58
- } catch {
59
- npmPackage = "unsupported";
60
- }
61
-
62
- console.log("fff - Fast File Finder");
63
- console.log(`Package version: ${pkg.version}`);
64
- console.log("");
65
- console.log("Platform Information:");
66
- console.log(` Triple: ${getTriple()}`);
67
- console.log(` Extension: ${getLibExtension()}`);
68
- console.log(` Library name: ${getLibFilename()}`);
69
- console.log(` npm package: ${npmPackage}`);
70
- console.log("");
71
- console.log("Binary Status:");
72
- const existing = findBinary();
73
- if (existing) {
74
- console.log(` Found: ${existing}`);
75
- } else {
76
- console.log(` Not found`);
77
- console.log(` Expected path: ${getBinaryPath()}`);
78
- console.log(` Try: bun add ${npmPackage}`);
79
- }
80
- break;
81
- }
82
-
83
- case "version":
84
- case "--version":
85
- case "-v": {
86
- const pkg = await getPackageInfo();
87
- console.log(pkg.version);
88
- break;
89
- }
90
-
91
- default: {
92
- const pkg = await getPackageInfo();
93
- console.log(`fff - Fast File Finder CLI v${pkg.version}`);
94
- console.log("");
95
- console.log("Usage:");
96
- console.log(
97
- " bunx fff download [tag] Download native binary from GitHub (fallback)",
98
- );
99
- console.log(" bunx fff info Show platform and binary info");
100
- console.log(" bunx fff version Show version");
101
- console.log(" bunx fff help Show this help message");
102
- console.log("");
103
- console.log("Examples:");
104
- console.log(" bunx fff download Download latest binary from GitHub");
105
- console.log(" bunx fff download abc1234 Download specific release tag");
106
- console.log("");
107
- console.log(
108
- "Note: Binaries are normally provided via platform-specific npm packages.",
109
- );
110
- console.log("The download command is a fallback for when those aren't available.");
111
- break;
112
- }
113
- }
114
- }
115
-
116
- main();