dmtrKovalenko / dmtrKovalenko/fff
pi-fff: emit hashline [path#TAG] headers so ffgrep output is directly editable in omp
- Dominant language
- Rust
- Stars
- 10.7k
- Forks
- 446
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 39
Description
### Summary
`ffgrep` / `fffind` output carries no hashline `[path#TAG]` header, so in oh-my-pi (omp) every fff hit needs a follow-up native `read` before `edit` can anchor. That follow-up round trip spends most of the token saving fff just earned.
The fix needs **no session access** — omp's hashline tag is derived from file bytes alone. `pi-fff` can mint it locally.
### Why this is cheap
Tag recipe (`@oh-my-pi/hashline/src/format.ts:108-121`):
```js
const norm = text.replace(/[ \t\r]+(?=\n|$)/g, "");
const tag = (Bun.hash.xxHash32(norm, 0) & 0xffff).toString(16).padStart(4, "0").toUpperCase();
```
Apply gate consults **no** snapshot store (`@oh-my-pi/hashline/src/patcher.ts:688,728`):
```js
const liveMatches = expected !== undefined && computeFileHash(normalized) === expected;
if (expected === undefined || liveMatches) { /* applyEdits */ }
```
Verified empirically 2026-08-17 on omp 0.10.5-era build: hand-computed a tag (`DD92`) in a REPL for a file the agent had **never `read`**, issued `[path#DD92] PUT 2.=2:` and the edit applied, returning a fresh tag. So emitting `[relpath#TAG]` plus `N: text` rows from the bytes fff already has in hand makes fff output directly editable.
### Proposed change
When the host is omp (or unconditionally, behind a flag), prefix each file's match group in `ffgrep` output with `[#]` and format rows as `N: text`, matching omp's native `grep`/`read` shape (`src/tools/grep.ts:1466` mints the same header).
### Caveat worth encoding
If `edit.enforceSeenLines` is `true` (default `false`, `settings-schema.ts:3239-3241`), the stricter guard additionally wants seen-line provenance via `recordSeenLinesFromBody` (`src/edit/file-snapshot-store.ts:141`). That lives on `AgentSession.fileSnapshotStore`, and `ExtensionContext` exposes `sessionManager`, `modelRegistry`, `cwd`, `ui`, `model`, `models` — never the session (`extensibility/extensions/runner.ts:1045-1081`). So an extension **cannot** register provenance.
Fallback for that configuration only: register under the builtin name (override mode) and delegate to the native implementation via `ctx.invokeTool`, which is wired only when the extension tool name shadows a builtin (`extensibility/extensions/types.ts:497-509`, `wrapper.ts:69-86`). Note override mode is currently affected by #792.
### Prior art
Searched both `dmtrKovalenko/fff` and `can1357/oh-my-pi` — 0 hits for the tag gap. Related but distinct: #737 / PR #741 (dynamic `${toolNames.grep}` in `promptGuidelines`), #792 / PR #793 (tools registered before startup mode known).
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by tracing the ffgrep and fffind output paths, then compare their format with src/tools/grep.ts and the tag recipe in @oh-my-pi/hashline/src/format.ts. Verify the generated [relpath#TAG] headers and N: text rows against omp editing, while preserving the documented enforceSeenLines limitation and override-mode caveat.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, rust
- Domain
- cli, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100