@ff-labs/bun 0.1.0 → 0.1.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/examples/search.ts +226 -0
- package/package.json +5 -3
- package/scripts/cli.ts +0 -0
- package/scripts/cli.cjs +0 -16
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Interactive file finder demo
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bunx fff-demo [directory]
|
|
7
|
+
* bun examples/search.ts [directory]
|
|
8
|
+
*
|
|
9
|
+
* Indexes the specified directory (or cwd) and provides an interactive
|
|
10
|
+
* search prompt with detailed metadata about results.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { FileFinder } from "../src/index";
|
|
14
|
+
import * as readline from "readline";
|
|
15
|
+
|
|
16
|
+
const RESET = "\x1b[0m";
|
|
17
|
+
const BOLD = "\x1b[1m";
|
|
18
|
+
const DIM = "\x1b[2m";
|
|
19
|
+
const GREEN = "\x1b[32m";
|
|
20
|
+
const YELLOW = "\x1b[33m";
|
|
21
|
+
const BLUE = "\x1b[34m";
|
|
22
|
+
const MAGENTA = "\x1b[35m";
|
|
23
|
+
const CYAN = "\x1b[36m";
|
|
24
|
+
const RED = "\x1b[31m";
|
|
25
|
+
|
|
26
|
+
function formatGitStatus(status: string): string {
|
|
27
|
+
switch (status) {
|
|
28
|
+
case "modified":
|
|
29
|
+
return `${YELLOW}M${RESET}`;
|
|
30
|
+
case "untracked":
|
|
31
|
+
return `${GREEN}?${RESET}`;
|
|
32
|
+
case "added":
|
|
33
|
+
return `${GREEN}A${RESET}`;
|
|
34
|
+
case "deleted":
|
|
35
|
+
return `${RED}D${RESET}`;
|
|
36
|
+
case "renamed":
|
|
37
|
+
return `${BLUE}R${RESET}`;
|
|
38
|
+
case "clear":
|
|
39
|
+
case "current":
|
|
40
|
+
return `${DIM} ${RESET}`;
|
|
41
|
+
default:
|
|
42
|
+
return `${DIM}${status.charAt(0)}${RESET}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function formatScore(score: number): string {
|
|
47
|
+
if (score >= 100) return `${GREEN}${score}${RESET}`;
|
|
48
|
+
if (score >= 50) return `${YELLOW}${score}${RESET}`;
|
|
49
|
+
if (score > 0) return `${DIM}${score}${RESET}`;
|
|
50
|
+
return `${DIM}0${RESET}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function formatSize(bytes: number): string {
|
|
54
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
55
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}K`;
|
|
56
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}M`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function formatTime(unixSeconds: number): string {
|
|
60
|
+
if (unixSeconds === 0) return "unknown";
|
|
61
|
+
const date = new Date(unixSeconds * 1000);
|
|
62
|
+
const now = new Date();
|
|
63
|
+
const diffMs = now.getTime() - date.getTime();
|
|
64
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
65
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
66
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
67
|
+
|
|
68
|
+
if (diffMins < 1) return "just now";
|
|
69
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
70
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
71
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
72
|
+
return date.toLocaleDateString();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function main() {
|
|
76
|
+
const targetDir = process.argv[2] || process.cwd();
|
|
77
|
+
|
|
78
|
+
console.log(`${BOLD}${CYAN}fff - Fast File Finder Demo${RESET}\n`);
|
|
79
|
+
|
|
80
|
+
// Check library availability
|
|
81
|
+
if (!FileFinder.isAvailable()) {
|
|
82
|
+
console.error(`${RED}Error: Native library not found.${RESET}`);
|
|
83
|
+
console.error("Build with: cargo build --release -p fff-c");
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Initialize
|
|
88
|
+
console.log(`${DIM}Initializing index for: ${targetDir}${RESET}`);
|
|
89
|
+
const initResult = FileFinder.init({
|
|
90
|
+
basePath: targetDir,
|
|
91
|
+
skipDatabases: true, // Skip frecency DB for demo simplicity
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (!initResult.ok) {
|
|
95
|
+
console.error(`${RED}Init failed: ${initResult.error}${RESET}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Wait for scan with progress
|
|
100
|
+
process.stdout.write(`${DIM}Scanning files...${RESET}`);
|
|
101
|
+
const startTime = Date.now();
|
|
102
|
+
let lastCount = 0;
|
|
103
|
+
|
|
104
|
+
while (FileFinder.isScanning()) {
|
|
105
|
+
const progress = FileFinder.getScanProgress();
|
|
106
|
+
if (progress.ok && progress.value.scannedFilesCount !== lastCount) {
|
|
107
|
+
lastCount = progress.value.scannedFilesCount;
|
|
108
|
+
process.stdout.write(`\r${DIM}Scanning files... ${lastCount}${RESET} `);
|
|
109
|
+
}
|
|
110
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const scanTime = Date.now() - startTime;
|
|
114
|
+
const finalProgress = FileFinder.getScanProgress();
|
|
115
|
+
const totalFiles = finalProgress.ok ? finalProgress.value.scannedFilesCount : 0;
|
|
116
|
+
|
|
117
|
+
console.log(`\r${GREEN}✓${RESET} Indexed ${BOLD}${totalFiles}${RESET} files in ${scanTime}ms\n`);
|
|
118
|
+
|
|
119
|
+
// Show index info
|
|
120
|
+
const health = FileFinder.healthCheck();
|
|
121
|
+
if (health.ok) {
|
|
122
|
+
console.log(`${DIM}─────────────────────────────────────────${RESET}`);
|
|
123
|
+
console.log(`${DIM}Version:${RESET} ${health.value.version}`);
|
|
124
|
+
console.log(`${DIM}Base path:${RESET} ${health.value.filePicker.basePath}`);
|
|
125
|
+
if (health.value.git.repositoryFound) {
|
|
126
|
+
console.log(`${DIM}Git root:${RESET} ${health.value.git.workdir}`);
|
|
127
|
+
}
|
|
128
|
+
console.log(`${DIM}─────────────────────────────────────────${RESET}\n`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Interactive search loop
|
|
132
|
+
const rl = readline.createInterface({
|
|
133
|
+
input: process.stdin,
|
|
134
|
+
output: process.stdout,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
console.log(`${BOLD}Enter a search query${RESET} (or 'q' to quit, empty for all files):\n`);
|
|
138
|
+
|
|
139
|
+
const prompt = () => {
|
|
140
|
+
rl.question(`${CYAN}search>${RESET} `, (query) => {
|
|
141
|
+
if (query.toLowerCase() === "q" || query.toLowerCase() === "quit") {
|
|
142
|
+
console.log(`\n${DIM}Goodbye!${RESET}`);
|
|
143
|
+
FileFinder.destroy();
|
|
144
|
+
rl.close();
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const searchStart = Date.now();
|
|
149
|
+
const result = FileFinder.search(query, { pageSize: 15 });
|
|
150
|
+
const searchTime = Date.now() - searchStart;
|
|
151
|
+
|
|
152
|
+
if (!result.ok) {
|
|
153
|
+
console.log(`${RED}Search error: ${result.error}${RESET}\n`);
|
|
154
|
+
prompt();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const { items, scores, totalMatched, totalFiles } = result.value;
|
|
159
|
+
|
|
160
|
+
console.log();
|
|
161
|
+
console.log(
|
|
162
|
+
`${DIM}Found ${BOLD}${totalMatched}${RESET}${DIM} matches in ${totalFiles} files (${searchTime}ms)${RESET}`
|
|
163
|
+
);
|
|
164
|
+
console.log();
|
|
165
|
+
|
|
166
|
+
if (items.length === 0) {
|
|
167
|
+
console.log(`${DIM}No matches found.${RESET}\n`);
|
|
168
|
+
prompt();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Header
|
|
173
|
+
console.log(
|
|
174
|
+
`${DIM} Git │ Score │ Size │ Modified │ Path${RESET}`
|
|
175
|
+
);
|
|
176
|
+
console.log(`${DIM}──────┼───────┼────────┼────────────┼${"─".repeat(40)}${RESET}`);
|
|
177
|
+
|
|
178
|
+
// Results
|
|
179
|
+
for (let i = 0; i < items.length; i++) {
|
|
180
|
+
const item = items[i];
|
|
181
|
+
const score = scores[i];
|
|
182
|
+
|
|
183
|
+
const gitStatus = formatGitStatus(item.gitStatus);
|
|
184
|
+
const totalScore = formatScore(score.total);
|
|
185
|
+
const size = formatSize(item.size).padStart(6);
|
|
186
|
+
const modified = formatTime(item.modified).padEnd(10);
|
|
187
|
+
const path = item.relativePath;
|
|
188
|
+
|
|
189
|
+
console.log(
|
|
190
|
+
` ${gitStatus} │ ${totalScore.padStart(5)} │ ${size} │ ${modified} │ ${path}`
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Show score breakdown for top results
|
|
194
|
+
if (i < 3 && score.total > 0) {
|
|
195
|
+
const breakdown: string[] = [];
|
|
196
|
+
if (score.baseScore > 0) breakdown.push(`base:${score.baseScore}`);
|
|
197
|
+
if (score.filenameBonus > 0) breakdown.push(`filename:+${score.filenameBonus}`);
|
|
198
|
+
if (score.frecencyBoost > 0) breakdown.push(`frecency:+${score.frecencyBoost}`);
|
|
199
|
+
if (score.comboMatchBoost > 0) breakdown.push(`combo:+${score.comboMatchBoost}`);
|
|
200
|
+
if (score.distancePenalty < 0) breakdown.push(`distance:${score.distancePenalty}`);
|
|
201
|
+
if (score.exactMatch) breakdown.push(`${GREEN}exact${RESET}`);
|
|
202
|
+
|
|
203
|
+
if (breakdown.length > 0) {
|
|
204
|
+
console.log(`${DIM} │ │ │ │ └─ ${breakdown.join(", ")}${RESET}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (totalMatched > items.length) {
|
|
210
|
+
console.log(
|
|
211
|
+
`${DIM} │ │ │ │ ... and ${totalMatched - items.length} more${RESET}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log();
|
|
216
|
+
prompt();
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
prompt();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
main().catch((err) => {
|
|
224
|
+
console.error(`${RED}Fatal error: ${err.message}${RESET}`);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ff-labs/bun",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"nativeBinaryHash": "6c641aa",
|
|
6
6
|
"description": "High-performance fuzzy file finder for Bun - perfect for LLM agent tools",
|
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"bin": {
|
|
17
|
-
"fff": "./scripts/cli.
|
|
17
|
+
"fff": "./scripts/cli.ts",
|
|
18
|
+
"fff-demo": "./examples/search.ts"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
21
|
"src",
|
|
21
22
|
"bin",
|
|
22
|
-
"scripts"
|
|
23
|
+
"scripts",
|
|
24
|
+
"examples"
|
|
23
25
|
],
|
|
24
26
|
"scripts": {
|
|
25
27
|
"postinstall": "bun ./scripts/postinstall.ts",
|
package/scripts/cli.ts
CHANGED
|
File without changes
|
package/scripts/cli.cjs
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
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
|
-
}
|