The default line prefix was the padded ' 42| ' form; the compact '42|' form
existed only as opt-in (compact_line_numbers). The padding and trailing space
carry no information an LLM needs, costing ~3 chars/line — ~300 tokens on a
400-line read, the highest-volume path in the server. Flip the default to
compact; compact_line_numbers=false, mode/line_numbers=full, and the session
'full' pref all still restore the padded alignment (explicit override wins).
Updates the two tests that pinned the old default and refreshes stale comments.
On the first page (offset==0) ripgrep capped results at exactly max_results
and set Truncated=true, but the cursor-footer guard (len>max_results) was
false, so the response said "(truncated...)" with no cursor — the caller
was told more matches exist but given no way to fetch them. Pagination only
worked from page 2. Emit a cursor on the first page using rg's total match
count for the remaining estimate.
Adds a strict regression test (asserts, not skips); the existing pagination
test was silently skipping its round-trip because page 1 never had a cursor.
Two file_search gaps:
- max_results defaulted to 0 (unbounded), so a broad pattern returned every match — unbounded memory + token blowup. The configured MaxSearchResults (default 1000) existed but was dead; wire it as the builtIn default. Explicit max_results still wins, and the cursor footer pages the rest.
- search.Request.IncludeHidden was a dead field with no tool param. Add an include_hidden bool (default false) so dotfiles (.github/, etc.) are searchable.
A misspelled or stale symbol_name returned a bare 'not found' error, forcing blind retries. nearestSymbolSuggestion fuzzy-matches the requested name against the file's actual symbols (via existing pkg/fuzzy) and appends 'did you mean: a, b, c?'. The re-parse is served from the parser cache. Adds TestReadSymbolNotFoundSuggestsNearest.
FindSymbolRange returned the first AST node matching a name, so file_read with symbol_name silently read the wrong symbol when a name collided (e.g. two String() methods on different receivers, or a func and type sharing a name). New FindSymbolCandidates returns all matches; the file_read handler now errors on ambiguity, listing each candidate's node type and line range so the caller can disambiguate via symbol_kind or line_start/line_end. FindSymbolRange is kept (first match) for callers that don't need ambiguity detection. Adds TestFindSymbolCandidates.
Two AST-summary/query path fixes plus colocated QF1012 style fixups:
- runASTQueryWalk now checks info.Size() > MaxFileSize before os.ReadFile, avoiding a full read of files it will discard.
- generateASTSummary used len(splitLines(...)) for the '(N lines)' header, over-counting newline-terminated files by one (phantom trailing element). New countLines() helper counts via bytes.Count. Adds TestCountLines.
* v2.0: token-optimization overhaul
Additive (backward-compatible flags):
- file_read: skeleton mode, strip (imports/license/block_comments),
compact_line_numbers, 8-char etag with prefix-match compat
- ast_query: format=verbose|compact|location, pagination cursor
- file_search: cluster mode, pagination cursor
- lsp_query (references): compact output
Breaking (v2):
- Preambles removed; opt-in verbose=true restores
- edit_apply: response=count|diff|none, default count
- ping tool removed
- symbol_at/find_definition/find_references merged into lsp_query
- Tool descriptions trimmed -83%, help moved to filepuff://help/<tool>
- Batch file_read dedups by etag
Protocol:
- ResourceLink returned for file_read >64 KiB (force_inline override)
- OnAfterInitialize hook reads capabilities.experimental.filepuff
for session defaults (default_format, default_max_results,
default_cluster, compact_refs, line_numbers,
resource_link_threshold)
* fix: drop --max-total-count from ripgrep args
The flag does not exist in stable ripgrep (confirmed up to 15.1.0 --
"unrecognized flag --max-total-count, similar flags that are
available: --max-count"). Every file_search call failed on hosts with
stock rg. --max-count is per-file, not a drop-in replacement, so rely
on the in-process truncation in parseOutput that was already the
documented safety net.