@ff-labs/pi-fff 0.10.6-nightly.e6df253 → 0.10.6
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 +7 -1
- package/package.json +3 -3
- package/pi-fff.schema.json +10 -0
- package/src/aux-finders.ts +2 -0
- package/src/config.ts +11 -1
- package/src/file-picker.ts +1 -0
- package/src/index.ts +45 -6
package/README.md
CHANGED
|
@@ -143,7 +143,9 @@ For persistent global configuration, create `pi-fff.json` in pi's agent director
|
|
|
143
143
|
"frecencyDbPath": "/path/to/frecency",
|
|
144
144
|
"historyDbPath": "/path/to/history",
|
|
145
145
|
"enableFsRootScanning": false,
|
|
146
|
-
"enableHomeDirScanning": true
|
|
146
|
+
"enableHomeDirScanning": true,
|
|
147
|
+
"warnOnHomeDirScan": true,
|
|
148
|
+
"followSymlinks": true
|
|
147
149
|
}
|
|
148
150
|
```
|
|
149
151
|
|
|
@@ -157,6 +159,8 @@ All fields are optional:
|
|
|
157
159
|
| `historyDbPath` | non-empty string | See [Data](#data) |
|
|
158
160
|
| `enableFsRootScanning` | boolean | `false` |
|
|
159
161
|
| `enableHomeDirScanning` | boolean | `true` |
|
|
162
|
+
| `warnOnHomeDirScan` | boolean | `true` |
|
|
163
|
+
| `followSymlinks` | boolean | `true` |
|
|
160
164
|
|
|
161
165
|
CLI flags take precedence over environment variables, which take precedence over this file. A missing file is ignored. Malformed JSON, unknown fields, and invalid values stop the extension from loading and report the file path and error. `/fff-mode` changes the current session; it does not edit this file.
|
|
162
166
|
|
|
@@ -169,6 +173,8 @@ The file is global only. Project-level config cannot safely control tool names b
|
|
|
169
173
|
- `--fff-history-db <path>` — path to query history database (also: `FFF_HISTORY_DB` env). Optional; see [Data](#data) for the default.
|
|
170
174
|
- `--fff-enable-root-scan` — allow indexing when launched from `/` (also: `FFF_ENABLE_ROOT_SCAN=1` env). FFF refuses to init at the filesystem root by default.
|
|
171
175
|
- `--fff-enable-home-scan` — index the home directory when launched from `$HOME` (also: `FFF_ENABLE_HOME_SCAN` env). Enabled by default. Disable with `--fff-enable-home-scan=false` or `FFF_ENABLE_HOME_SCAN=0` if your `$HOME` contains huge trees (toolchains, kernel sources, build outputs) that make the background index run for a long time. When launched from `$HOME` with this enabled, pi shows a warning that the whole home tree is being indexed.
|
|
176
|
+
- `--fff-warn-home-scan` — show the warning notification when `$HOME` is indexed (also: `FFF_WARN_HOME_SCAN` env). Enabled by default. Disable with `--fff-warn-home-scan=false`, `FFF_WARN_HOME_SCAN=0`, or `"warnOnHomeDirScan": false` in `pi-fff.json`. Indexing and the footer status are unaffected.
|
|
177
|
+
- `--fff-follow-symlinks` — index through directory symlinks (also: `FFF_FOLLOW_SYMLINKS` env, or `"followSymlinks"` in `pi-fff.json`). Enabled by default: trees that reach their real files through links — a git worktree whose `docs/` links back to the main checkout, or a stowed dotfiles layout — would otherwise be missing from `@`-mentions and from find/grep with no visible sign. Disable with `--fff-follow-symlinks=false` or `FFF_FOLLOW_SYMLINKS=0` to keep the walk inside the real tree, which is worth doing when a linked target pulls in a large tree outside the workspace. Symlink cycles are detected and broken by the walker.
|
|
172
178
|
|
|
173
179
|
## Data
|
|
174
180
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ff-labs/pi-fff",
|
|
3
3
|
"public": true,
|
|
4
|
-
"version": "0.10.6
|
|
4
|
+
"version": "0.10.6",
|
|
5
5
|
"description": "pi extension: FFF-powered fuzzy file and content search",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@ff-labs/fff-bun": "0.10.6
|
|
45
|
-
"@ff-labs/fff-node": "0.10.6
|
|
44
|
+
"@ff-labs/fff-bun": "0.10.6",
|
|
45
|
+
"@ff-labs/fff-node": "0.10.6"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@earendil-works/pi-coding-agent": "*",
|
package/pi-fff.schema.json
CHANGED
|
@@ -36,6 +36,16 @@
|
|
|
36
36
|
"type": "boolean",
|
|
37
37
|
"default": true,
|
|
38
38
|
"description": "Allows indexing when pi is launched from the home directory."
|
|
39
|
+
},
|
|
40
|
+
"warnOnHomeDirScan": {
|
|
41
|
+
"type": "boolean",
|
|
42
|
+
"default": true,
|
|
43
|
+
"description": "Shows a warning notification when the home directory is indexed."
|
|
44
|
+
},
|
|
45
|
+
"followSymlinks": {
|
|
46
|
+
"type": "boolean",
|
|
47
|
+
"default": true,
|
|
48
|
+
"description": "Indexes through directory symlinks, e.g. a git worktree or stow layout whose files live behind links. Set to false to keep the walk inside the real tree."
|
|
39
49
|
}
|
|
40
50
|
}
|
|
41
51
|
}
|
package/src/aux-finders.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface AuxPicker {
|
|
|
16
16
|
export interface AuxOpts {
|
|
17
17
|
enableFsRootScanning: boolean;
|
|
18
18
|
enableHomeDirScanning?: boolean;
|
|
19
|
+
followSymlinks?: boolean;
|
|
19
20
|
pickers: FilePickerFactory;
|
|
20
21
|
// Called before a newly spawned aux picker starts a scan that covers $HOME.
|
|
21
22
|
onHomeDirScan?: (root: string) => void;
|
|
@@ -104,6 +105,7 @@ export class AuxFinderPool {
|
|
|
104
105
|
basePath: root,
|
|
105
106
|
enableHomeDirScanning,
|
|
106
107
|
enableFsRootScanning: this.opts.enableFsRootScanning,
|
|
108
|
+
followSymlinks: this.opts.followSymlinks,
|
|
107
109
|
});
|
|
108
110
|
|
|
109
111
|
const entry: AuxPicker = { root, finder, lastUsed: Date.now() };
|
package/src/config.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface FffConfig {
|
|
|
14
14
|
historyDbPath?: string;
|
|
15
15
|
enableFsRootScanning?: boolean;
|
|
16
16
|
enableHomeDirScanning?: boolean;
|
|
17
|
+
warnOnHomeDirScan?: boolean;
|
|
18
|
+
followSymlinks?: boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const CONFIG_KEYS = new Set<keyof FffConfig>([
|
|
@@ -23,6 +25,8 @@ const CONFIG_KEYS = new Set<keyof FffConfig>([
|
|
|
23
25
|
"historyDbPath",
|
|
24
26
|
"enableFsRootScanning",
|
|
25
27
|
"enableHomeDirScanning",
|
|
28
|
+
"warnOnHomeDirScan",
|
|
29
|
+
"followSymlinks",
|
|
26
30
|
]);
|
|
27
31
|
|
|
28
32
|
export function loadConfig(agentDir = piDataDir()): FffConfig {
|
|
@@ -64,6 +68,8 @@ export function loadConfig(agentDir = piDataDir()): FffConfig {
|
|
|
64
68
|
validateString(configPath, parsed, "historyDbPath");
|
|
65
69
|
validateBoolean(configPath, parsed, "enableFsRootScanning");
|
|
66
70
|
validateBoolean(configPath, parsed, "enableHomeDirScanning");
|
|
71
|
+
validateBoolean(configPath, parsed, "warnOnHomeDirScan");
|
|
72
|
+
validateBoolean(configPath, parsed, "followSymlinks");
|
|
67
73
|
|
|
68
74
|
return parsed as FffConfig;
|
|
69
75
|
}
|
|
@@ -94,7 +100,11 @@ function validateString(
|
|
|
94
100
|
function validateBoolean(
|
|
95
101
|
configPath: string,
|
|
96
102
|
config: Record<string, unknown>,
|
|
97
|
-
key:
|
|
103
|
+
key:
|
|
104
|
+
| "enableFsRootScanning"
|
|
105
|
+
| "enableHomeDirScanning"
|
|
106
|
+
| "warnOnHomeDirScan"
|
|
107
|
+
| "followSymlinks",
|
|
98
108
|
): void {
|
|
99
109
|
const value = config[key];
|
|
100
110
|
if (value !== undefined && typeof value !== "boolean") {
|
package/src/file-picker.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -40,6 +40,10 @@ export { SCAN_TIMEOUT_MS } from "./sdk";
|
|
|
40
40
|
const DEFAULT_GREP_LIMIT = 20;
|
|
41
41
|
const DEFAULT_FIND_LIMIT = 30;
|
|
42
42
|
const GREP_PAGE_SIZE_MAX = 50;
|
|
43
|
+
// Per-file match cap. Must stay decoupled from pageSize: grep cursors advance by
|
|
44
|
+
// file offset, so clamping this to pageSize makes same-file overflow unreachable
|
|
45
|
+
// on later pages (#825). Matches the engine default.
|
|
46
|
+
const GREP_MAX_MATCHES_PER_FILE = 200;
|
|
43
47
|
const GREP_CONTEXT_MAX = 20;
|
|
44
48
|
const GREP_MAX_LINE_LENGTH = 500;
|
|
45
49
|
const MENTION_MAX_RESULTS = 20;
|
|
@@ -50,7 +54,8 @@ const GREP_TIME_BUDGET_MS = 10_000;
|
|
|
50
54
|
const HOME_SCAN_STATUS_KEY = "fff";
|
|
51
55
|
const HOME_SCAN_POLL_MS = 1_000;
|
|
52
56
|
const HOME_SCAN_DISABLE_HINT =
|
|
53
|
-
|
|
57
|
+
'You can prevent home dir indexing with --fff-enable-home-scan=false, FFF_ENABLE_HOME_SCAN=0, or "enableHomeDirScanning": false in pi-fff.json. ' +
|
|
58
|
+
'To keep indexing but silence this warning use --fff-warn-home-scan=false, FFF_WARN_HOME_SCAN=0, or "warnOnHomeDirScan": false in pi-fff.json.';
|
|
54
59
|
|
|
55
60
|
interface ToolNames {
|
|
56
61
|
grep: string;
|
|
@@ -343,6 +348,8 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
343
348
|
let resolvedDbPaths: ReturnType<typeof resolveDbPaths>;
|
|
344
349
|
let enableFsRootScanning = false;
|
|
345
350
|
let enableHomeDirScanning = true;
|
|
351
|
+
let warnOnHomeDirScan = true;
|
|
352
|
+
let followSymlinks = true;
|
|
346
353
|
|
|
347
354
|
function setMode(mode: FffMode): void {
|
|
348
355
|
currentMode = mode;
|
|
@@ -385,6 +392,22 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
385
392
|
true,
|
|
386
393
|
parseBoolean,
|
|
387
394
|
);
|
|
395
|
+
warnOnHomeDirScan = getConfigValue(
|
|
396
|
+
"fff-warn-home-scan",
|
|
397
|
+
"FFF_WARN_HOME_SCAN",
|
|
398
|
+
config.warnOnHomeDirScan,
|
|
399
|
+
true,
|
|
400
|
+
parseBoolean,
|
|
401
|
+
);
|
|
402
|
+
// On by default: worktree and stow layouts reach their files through links,
|
|
403
|
+
// and an agent silently missing them is worse than the extra walk.
|
|
404
|
+
followSymlinks = getConfigValue(
|
|
405
|
+
"fff-follow-symlinks",
|
|
406
|
+
"FFF_FOLLOW_SYMLINKS",
|
|
407
|
+
config.followSymlinks,
|
|
408
|
+
true,
|
|
409
|
+
parseBoolean,
|
|
410
|
+
);
|
|
388
411
|
}
|
|
389
412
|
|
|
390
413
|
function getMode(): FffMode {
|
|
@@ -406,6 +429,7 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
406
429
|
let homeScanTimer: ReturnType<typeof setInterval> | null = null;
|
|
407
430
|
|
|
408
431
|
function warnHomeDirScan(root: string): void {
|
|
432
|
+
if (!warnOnHomeDirScan) return;
|
|
409
433
|
uiCtx?.ui.notify(
|
|
410
434
|
`(fff): Your cwd (${root}) is too large. Indexing will take additional time and resources.\n${HOME_SCAN_DISABLE_HINT}`,
|
|
411
435
|
"warning",
|
|
@@ -430,6 +454,7 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
430
454
|
auxPool = new AuxFinderPool({
|
|
431
455
|
enableFsRootScanning,
|
|
432
456
|
enableHomeDirScanning,
|
|
457
|
+
followSymlinks,
|
|
433
458
|
onHomeDirScan: warnHomeDirScan,
|
|
434
459
|
pickers,
|
|
435
460
|
});
|
|
@@ -456,6 +481,7 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
456
481
|
basePath: cwd,
|
|
457
482
|
enableHomeDirScanning,
|
|
458
483
|
enableFsRootScanning,
|
|
484
|
+
followSymlinks,
|
|
459
485
|
});
|
|
460
486
|
finderCwd = cwd;
|
|
461
487
|
return mainFinder;
|
|
@@ -667,6 +693,18 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
667
693
|
type: "boolean",
|
|
668
694
|
});
|
|
669
695
|
|
|
696
|
+
pi.registerFlag("fff-follow-symlinks", {
|
|
697
|
+
description:
|
|
698
|
+
"Index through directory symlinks, e.g. a git worktree or stow layout (default true; disable with --fff-follow-symlinks=false or FFF_FOLLOW_SYMLINKS=0)",
|
|
699
|
+
type: "boolean",
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
pi.registerFlag("fff-warn-home-scan", {
|
|
703
|
+
description:
|
|
704
|
+
"Warn when indexing $HOME (default true; silence with --fff-warn-home-scan=false or FFF_WARN_HOME_SCAN=0)",
|
|
705
|
+
type: "boolean",
|
|
706
|
+
});
|
|
707
|
+
|
|
670
708
|
function reportInitFailure(ctx: ExtensionContext, error: unknown): void {
|
|
671
709
|
ctx.ui.notify(
|
|
672
710
|
`FFF init failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -833,8 +871,9 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
833
871
|
|
|
834
872
|
const picker = aux ? aux.finder : await ensureFinder(activeCwd);
|
|
835
873
|
const effectiveLimit = Math.max(1, params.limit ?? DEFAULT_GREP_LIMIT);
|
|
836
|
-
// pageSize caps TOTAL matches across all files
|
|
837
|
-
//
|
|
874
|
+
// pageSize caps TOTAL matches across all files (soft cap: the current file
|
|
875
|
+
// is always finished first). maxMatchesPerFile stays decoupled at the engine
|
|
876
|
+
// default so same-file overflow remains reachable via cursor (#825).
|
|
838
877
|
const pageSize = Math.min(effectiveLimit, GREP_PAGE_SIZE_MAX);
|
|
839
878
|
const context = clampContext(params.context);
|
|
840
879
|
const query = aux
|
|
@@ -884,7 +923,7 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
884
923
|
const grepResult = picker.grep(query, {
|
|
885
924
|
mode,
|
|
886
925
|
smartCase,
|
|
887
|
-
maxMatchesPerFile:
|
|
926
|
+
maxMatchesPerFile: GREP_MAX_MATCHES_PER_FILE,
|
|
888
927
|
pageSize,
|
|
889
928
|
cursor: (params.cursor ? getCursor(params.cursor) : null) ?? null,
|
|
890
929
|
beforeContext: context,
|
|
@@ -917,7 +956,7 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
917
956
|
const fuzzy = picker.grep(fuzzyQuery, {
|
|
918
957
|
mode: "fuzzy",
|
|
919
958
|
smartCase,
|
|
920
|
-
maxMatchesPerFile:
|
|
959
|
+
maxMatchesPerFile: GREP_MAX_MATCHES_PER_FILE,
|
|
921
960
|
pageSize,
|
|
922
961
|
cursor: null,
|
|
923
962
|
beforeContext: 0,
|
|
@@ -1167,7 +1206,7 @@ export default function fffExtension(pi: ExtensionAPI) {
|
|
|
1167
1206
|
const grepResult = f.multiGrep({
|
|
1168
1207
|
patterns: params.patterns,
|
|
1169
1208
|
constraints: params.constraints,
|
|
1170
|
-
maxMatchesPerFile:
|
|
1209
|
+
maxMatchesPerFile: GREP_MAX_MATCHES_PER_FILE,
|
|
1171
1210
|
pageSize,
|
|
1172
1211
|
smartCase: true,
|
|
1173
1212
|
cursor: (params.cursor ? getCursor(params.cursor) : null) ?? null,
|