@ff-labs/bun 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -0
- package/bin/libfff_c.dylib +0 -0
- package/package.json +74 -0
- package/scripts/cli.cjs +16 -0
- package/scripts/cli.ts +131 -0
- package/scripts/postinstall.ts +37 -0
- package/src/download.ts +316 -0
- package/src/ffi.ts +377 -0
- package/src/finder.ts +328 -0
- package/src/index.test.ts +263 -0
- package/src/index.ts +69 -0
- package/src/platform.ts +92 -0
- package/src/types.ts +260 -0
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# fff - Fast File Finder
|
|
2
|
+
|
|
3
|
+
High-performance fuzzy file finder for Bun, powered by Rust. Perfect for LLM agent tools that need to search through codebases.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Blazing fast** - Rust-powered fuzzy search with parallel processing
|
|
8
|
+
- **Smart ranking** - Frecency-based scoring (frequency + recency)
|
|
9
|
+
- **Git-aware** - Shows file git status in results
|
|
10
|
+
- **Query history** - Learns from your search patterns
|
|
11
|
+
- **Type-safe** - Full TypeScript support with Result types
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bun add fff
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The native binary will be downloaded automatically during installation.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { FileFinder } from "fff";
|
|
25
|
+
|
|
26
|
+
// Initialize with a directory
|
|
27
|
+
const result = FileFinder.init({ basePath: "/path/to/project" });
|
|
28
|
+
if (!result.ok) {
|
|
29
|
+
console.error(result.error);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Wait for initial scan
|
|
34
|
+
FileFinder.waitForScan(5000);
|
|
35
|
+
|
|
36
|
+
// Search for files
|
|
37
|
+
const search = FileFinder.search("main.ts");
|
|
38
|
+
if (search.ok) {
|
|
39
|
+
for (const item of search.value.items) {
|
|
40
|
+
console.log(item.relativePath);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Cleanup when done
|
|
45
|
+
FileFinder.destroy();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API Reference
|
|
49
|
+
|
|
50
|
+
### `FileFinder.init(options)`
|
|
51
|
+
|
|
52
|
+
Initialize the file finder.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
interface InitOptions {
|
|
56
|
+
basePath: string; // Directory to index (required)
|
|
57
|
+
frecencyDbPath?: string; // Custom frecency DB path
|
|
58
|
+
historyDbPath?: string; // Custom history DB path
|
|
59
|
+
useUnsafeNoLock?: boolean; // Faster but less safe DB mode
|
|
60
|
+
skipDatabases?: boolean; // Skip frecency/history (simpler mode)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const result = FileFinder.init({ basePath: "/my/project" });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `FileFinder.search(query, options?)`
|
|
67
|
+
|
|
68
|
+
Search for files.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
interface SearchOptions {
|
|
72
|
+
maxThreads?: number; // Parallel threads (0 = auto)
|
|
73
|
+
currentFile?: string; // Deprioritize this file
|
|
74
|
+
comboBoostMultiplier?: number; // Query history boost
|
|
75
|
+
minComboCount?: number; // Min history matches
|
|
76
|
+
pageIndex?: number; // Pagination offset
|
|
77
|
+
pageSize?: number; // Results per page
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const result = FileFinder.search("main.ts", { pageSize: 10 });
|
|
81
|
+
if (result.ok) {
|
|
82
|
+
console.log(`Found ${result.value.totalMatched} files`);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Query Syntax
|
|
87
|
+
|
|
88
|
+
- `foo bar` - Match files containing "foo" and "bar"
|
|
89
|
+
- `src/` - Match files in src directory
|
|
90
|
+
- `file.ts:42` - Match file.ts with line 42
|
|
91
|
+
- `file.ts:42:10` - Match with line and column
|
|
92
|
+
|
|
93
|
+
### `FileFinder.trackAccess(filePath)`
|
|
94
|
+
|
|
95
|
+
Track file access for frecency scoring.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// Call when user opens a file
|
|
99
|
+
FileFinder.trackAccess("/path/to/file.ts");
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### `FileFinder.trackQuery(query, selectedFile)`
|
|
103
|
+
|
|
104
|
+
Track query completion for smart suggestions.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Call when user selects a file from search
|
|
108
|
+
FileFinder.trackQuery("main", "/path/to/main.ts");
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `FileFinder.healthCheck(testPath?)`
|
|
112
|
+
|
|
113
|
+
Get diagnostic information.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const health = FileFinder.healthCheck();
|
|
117
|
+
if (health.ok) {
|
|
118
|
+
console.log(`Version: ${health.value.version}`);
|
|
119
|
+
console.log(`Indexed: ${health.value.filePicker.indexedFiles} files`);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Other Methods
|
|
124
|
+
|
|
125
|
+
- `FileFinder.scanFiles()` - Trigger rescan
|
|
126
|
+
- `FileFinder.isScanning()` - Check scan status
|
|
127
|
+
- `FileFinder.getScanProgress()` - Get scan progress
|
|
128
|
+
- `FileFinder.waitForScan(timeoutMs)` - Wait for scan
|
|
129
|
+
- `FileFinder.restartIndex(newPath)` - Change indexed directory
|
|
130
|
+
- `FileFinder.refreshGitStatus()` - Refresh git cache
|
|
131
|
+
- `FileFinder.getHistoricalQuery(offset)` - Get past queries
|
|
132
|
+
- `FileFinder.shortenPath(path, maxSize, strategy)` - Shorten paths
|
|
133
|
+
- `FileFinder.destroy()` - Cleanup resources
|
|
134
|
+
|
|
135
|
+
## Result Types
|
|
136
|
+
|
|
137
|
+
All methods return a `Result<T>` type for explicit error handling:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
type Result<T> =
|
|
141
|
+
| { ok: true; value: T }
|
|
142
|
+
| { ok: false; error: string };
|
|
143
|
+
|
|
144
|
+
const result = FileFinder.search("foo");
|
|
145
|
+
if (result.ok) {
|
|
146
|
+
// result.value is SearchResult
|
|
147
|
+
} else {
|
|
148
|
+
// result.error is string
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Search Result Types
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
interface SearchResult {
|
|
156
|
+
items: FileItem[];
|
|
157
|
+
scores: Score[];
|
|
158
|
+
totalMatched: number;
|
|
159
|
+
totalFiles: number;
|
|
160
|
+
location?: Location;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface FileItem {
|
|
164
|
+
path: string;
|
|
165
|
+
relativePath: string;
|
|
166
|
+
fileName: string;
|
|
167
|
+
size: number;
|
|
168
|
+
modified: number;
|
|
169
|
+
gitStatus: string; // 'clean', 'modified', 'untracked', etc.
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Building from Source
|
|
174
|
+
|
|
175
|
+
If prebuilt binaries aren't available for your platform:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Clone the repository
|
|
179
|
+
git clone https://github.com/dmtrKovalenko/fff.nvim
|
|
180
|
+
cd fff.nvim
|
|
181
|
+
|
|
182
|
+
# Build the C library
|
|
183
|
+
cargo build --release -p fff-c
|
|
184
|
+
|
|
185
|
+
# The binary will be at target/release/libfff_c.{so,dylib,dll}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## CLI Tools
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Download binary manually
|
|
192
|
+
bunx fff download [version]
|
|
193
|
+
|
|
194
|
+
# Show platform info
|
|
195
|
+
bunx fff info
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ff-labs/bun",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"nativeBinaryHash": "6c641aa",
|
|
6
|
+
"description": "High-performance fuzzy file finder for Bun - perfect for LLM agent tools",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "src/index.ts",
|
|
9
|
+
"types": "src/index.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./src/index.ts",
|
|
13
|
+
"types": "./src/index.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"fff": "./scripts/cli.cjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"src",
|
|
21
|
+
"bin",
|
|
22
|
+
"scripts"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"postinstall": "bun ./scripts/postinstall.ts",
|
|
26
|
+
"download": "bun ./scripts/cli.ts download",
|
|
27
|
+
"test": "bun test src/",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"demo": "bun ./examples/search.ts"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"bun": ">=1.0.0"
|
|
33
|
+
},
|
|
34
|
+
"os": [
|
|
35
|
+
"darwin",
|
|
36
|
+
"linux",
|
|
37
|
+
"win32"
|
|
38
|
+
],
|
|
39
|
+
"cpu": [
|
|
40
|
+
"x64",
|
|
41
|
+
"arm64"
|
|
42
|
+
],
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/dmtrKovalenko/fff.nvim.git",
|
|
46
|
+
"directory": "packages/fff"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"file-finder",
|
|
50
|
+
"fuzzy-search",
|
|
51
|
+
"bun",
|
|
52
|
+
"ffi",
|
|
53
|
+
"llm-tools",
|
|
54
|
+
"agent-tools",
|
|
55
|
+
"fast",
|
|
56
|
+
"rust"
|
|
57
|
+
],
|
|
58
|
+
"author": "Dmitry Kovalenko",
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/dmtrKovalenko/fff.nvim/issues"
|
|
65
|
+
},
|
|
66
|
+
"homepage": "https://github.com/dmtrKovalenko/fff.nvim#readme",
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/bun": "^1.3.8",
|
|
69
|
+
"typescript": "^5.0.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"bun": ">=1.0.0"
|
|
73
|
+
}
|
|
74
|
+
}
|
package/scripts/cli.cjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Wrapper script for npm - delegates to Bun
|
|
3
|
+
const { execSync } = require("child_process");
|
|
4
|
+
const { join } = require("path");
|
|
5
|
+
|
|
6
|
+
const script = join(__dirname, "cli.ts");
|
|
7
|
+
const args = process.argv.slice(2).join(" ");
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
execSync(`bun ${script} ${args}`, {
|
|
11
|
+
stdio: "inherit",
|
|
12
|
+
cwd: process.cwd()
|
|
13
|
+
});
|
|
14
|
+
} catch (error) {
|
|
15
|
+
process.exit(error.status || 1);
|
|
16
|
+
}
|
package/scripts/cli.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI tool for fff package management
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bunx fff download [hash] - Download native binary
|
|
7
|
+
* bunx fff info - Show platform and binary info
|
|
8
|
+
* bunx fff check - Check for updates
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
downloadBinary,
|
|
13
|
+
getBinaryPath,
|
|
14
|
+
findBinary,
|
|
15
|
+
getInstalledHash,
|
|
16
|
+
checkForUpdate
|
|
17
|
+
} from "../src/download";
|
|
18
|
+
import { getTriple, getLibExtension, getLibFilename } from "../src/platform";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
const command = args[0];
|
|
24
|
+
|
|
25
|
+
interface PackageJson {
|
|
26
|
+
version: string;
|
|
27
|
+
nativeBinaryHash?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function getPackageInfo(): Promise<PackageJson> {
|
|
31
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const packageJsonPath = join(currentDir, "..", "package.json");
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
return await Bun.file(packageJsonPath).json();
|
|
36
|
+
} catch {
|
|
37
|
+
return { version: "unknown" };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function main() {
|
|
42
|
+
switch (command) {
|
|
43
|
+
case "download": {
|
|
44
|
+
const hash = args[1];
|
|
45
|
+
console.log("fff: Downloading native library...");
|
|
46
|
+
try {
|
|
47
|
+
const resolvedHash = await downloadBinary(hash);
|
|
48
|
+
console.log(`fff: Download complete! (${resolvedHash})`);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("fff: Download failed:", error);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case "check": {
|
|
57
|
+
console.log("fff: Checking for updates...");
|
|
58
|
+
try {
|
|
59
|
+
const { currentHash, latestHash, updateAvailable } = await checkForUpdate();
|
|
60
|
+
console.log(` Installed: ${currentHash || "not installed"}`);
|
|
61
|
+
console.log(` Latest: ${latestHash}`);
|
|
62
|
+
if (updateAvailable) {
|
|
63
|
+
console.log("");
|
|
64
|
+
console.log(" Update available! Run: bunx fff download");
|
|
65
|
+
} else {
|
|
66
|
+
console.log("");
|
|
67
|
+
console.log(" You're up to date!");
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error("fff: Failed to check for updates:", error);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
case "info": {
|
|
77
|
+
const pkg = await getPackageInfo();
|
|
78
|
+
const installedHash = await getInstalledHash();
|
|
79
|
+
|
|
80
|
+
console.log("fff - Fast File Finder");
|
|
81
|
+
console.log(`Package version: ${pkg.version}`);
|
|
82
|
+
console.log(`Binary hash: ${installedHash || "not installed"}`);
|
|
83
|
+
console.log("");
|
|
84
|
+
console.log("Platform Information:");
|
|
85
|
+
console.log(` Triple: ${getTriple()}`);
|
|
86
|
+
console.log(` Extension: ${getLibExtension()}`);
|
|
87
|
+
console.log(` Library name: ${getLibFilename()}`);
|
|
88
|
+
console.log("");
|
|
89
|
+
console.log("Binary Status:");
|
|
90
|
+
const existing = findBinary();
|
|
91
|
+
if (existing) {
|
|
92
|
+
console.log(` Found: ${existing}`);
|
|
93
|
+
} else {
|
|
94
|
+
console.log(` Not found`);
|
|
95
|
+
console.log(` Expected path: ${getBinaryPath()}`);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
case "version":
|
|
101
|
+
case "--version":
|
|
102
|
+
case "-v": {
|
|
103
|
+
const pkg = await getPackageInfo();
|
|
104
|
+
console.log(pkg.version);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
case "help":
|
|
109
|
+
case "--help":
|
|
110
|
+
case "-h":
|
|
111
|
+
default: {
|
|
112
|
+
const pkg = await getPackageInfo();
|
|
113
|
+
console.log(`fff - Fast File Finder CLI v${pkg.version}`);
|
|
114
|
+
console.log("");
|
|
115
|
+
console.log("Usage:");
|
|
116
|
+
console.log(" bunx fff download [hash] Download native binary");
|
|
117
|
+
console.log(" bunx fff check Check for updates");
|
|
118
|
+
console.log(" bunx fff info Show platform and binary info");
|
|
119
|
+
console.log(" bunx fff version Show version");
|
|
120
|
+
console.log(" bunx fff help Show this help message");
|
|
121
|
+
console.log("");
|
|
122
|
+
console.log("Examples:");
|
|
123
|
+
console.log(" bunx fff download Download binary for configured hash");
|
|
124
|
+
console.log(" bunx fff download latest Download latest release");
|
|
125
|
+
console.log(" bunx fff download abc1234 Download specific commit hash");
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
main();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Postinstall script - automatically downloads the native binary
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { downloadBinary, findBinary, getInstalledHash } from "../src/download";
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
// Check if binary already exists (dev build or previous download)
|
|
10
|
+
const existing = findBinary();
|
|
11
|
+
if (existing) {
|
|
12
|
+
const hash = await getInstalledHash();
|
|
13
|
+
console.log(`fff: Native library found at ${existing}`);
|
|
14
|
+
if (hash) {
|
|
15
|
+
console.log(`fff: Version: ${hash}`);
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log("fff: Native library not found, downloading...");
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const hash = await downloadBinary();
|
|
24
|
+
console.log(`fff: Native library installed successfully! (${hash})`);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("fff: Failed to download native library:", error);
|
|
27
|
+
console.error("");
|
|
28
|
+
console.error("fff: You can build from source instead:");
|
|
29
|
+
console.error(" cd node_modules/fff && cargo build --release -p fff-c");
|
|
30
|
+
console.error("");
|
|
31
|
+
console.error("fff: Or run `bunx fff download` after fixing network issues.");
|
|
32
|
+
// Don't exit with error - allow install to complete
|
|
33
|
+
// The error will surface when the user tries to use the library
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
main();
|