Single-argument function macros were indexed under parentheses in C or the macro name in C++, breaking caller resolution.
Recover both parser shapes only when a preceding local macro definition establishes the function name, and mirror the recovery in the Rust kernel.
Preserve existing multi-argument recovery and ordinary declarations; add extraction, resolution, explore-flow, and parity regressions.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Path search marked nodes visited only on dequeue, allowing pending targets to be queued repeatedly with copied paths.
Track enqueued nodes from the starting node and exclude them from target lookups and subsequent pushes.
Add real-SQLite operation-count and path-correctness regression coverage.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Field-channel synthesis discarded receiver and binding identity when selecting handlers globally by name.
Reuse resolved function-reference edges at the registration site, retaining class, inheritance, and import resolution without global fallback.
Add regression coverage for target identity, unknown handlers, and explore registration metadata.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sync swallowed lock-acquisition failures and returned counters indistinguishable from a clean index.
Throw LockUnavailableError so the CLI reports failure and the watcher retains its retry behavior without inspecting counters.
Preserve quiet output and clean up resources when sync rejects.
Cover live locks, recovery, watcher retries, and CLI exit statuses.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(installer): resolve Antigravity codegraph command out of fnm's ephemeral per-shell dir
- resolveCodegraphCommand resolved `command -v codegraph`, which under fnm returns an ephemeral ~/.local/state/fnm_multishells/<pid>/bin path that fnm removes when the installing shell exits, leaving a dangling MCP command
- realpath the containing directory so the written command survives shell exit
- preserve the `codegraph` basename so agents that validate the command by basename still match
- fall back to the raw resolved path if realpath fails
* fix(installer): preserve Antigravity command after fnm shell exit (#1443)
Antigravity saved an ephemeral fnm shell path that disappeared after shell exit.
Canonicalize the containing directory while preserving the codegraph basename and existing fallbacks.
Add macOS regression coverage for symlink removal, stable paths, and lookup failures.
Co-authored-by: kevocodes <62626446+kevocodes@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: kevocodes <62626446+kevocodes@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Everyday uses of como and wie were treated as sufficient structural evidence for HIGH.
Remove these words from the unconditional keyword list so existing index verification determines HIGH, MEDIUM, or silence.
Add real-index CLI regressions while preserving existing English and multilingual structural tests.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(react-router): bound route scans to their declarations (#1348)
Fixed scan windows borrowed paths and components from neighboring or nested routes.
Scan JSX opening tags and data-router objects structurally, pairing only their own attributes and properties.
Add persisted-graph regressions across JavaScript and TypeScript route declarations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(react-router): bound route scans to their declarations (#1348)
Fixed scan windows borrowed paths and components from neighboring or nested routes.
Scan JSX opening tags and data-router objects structurally, pairing only their own attributes and properties.
Add persisted-graph regressions across JavaScript and TypeScript route declarations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
DFM/FMX component ranges stayed pinned to their declaration lines because closing blocks only popped IDs.
Store nodes in the component stack and update their ending positions at the matching end.
Cover persisted ranges and MCP source retrieval while preserving containment and event references.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(dead-code): treat an ancestor outside the index as opaque (#1973)
An override is excluded from the dead-code report when its ancestor is in
the graph, or is present but declares no members we can read. An ancestor
outside the project (React.Component, stream.Transform, a NestJS
interface) creates no edge at all, so a lifecycle method the framework
calls looked like it overrode nothing and was listed as unreferenced.
The resolver does record such an ancestor, as a failed extends /
implements row. A container with one, or a project ancestor of one that
has it, now counts as having an opaque ancestor, and its members are
excluded under "overriding".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(dead-code): propagate external ancestor uncertainty to descendants (#1973)
External ancestors leave unresolved inheritance references rather than graph edges.
Treat those ancestors as opaque and propagate uncertainty through every descendant root.
Track visited ancestor/root pairs while preserving cycle protection and the depth bound.
Cover direct and three-level inheritance, unused controls, and batched query behavior.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Workspace imports could follow excluded dependency archives and decode them without a size guard.
Reuse extraction's 1 MiB limit, reject non-files before reading, and cache null results.
Add sparse-HAR and real ohpm indexing coverage while preserving legitimate import edges.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(explore): find files by exact Chinese filenames
Co-Authored-By: GPT-5 <noreply@openai.com>
* fix(explore): retrieve exact Han filenames (#1372)
ASCII-only query preparation discarded Han filename queries before retrieval.
Add exact basename/stem file lookup and prioritize matches before the entry-point cap while respecting node-kind filters.
Preserve shared tokenization and retrieval budgets; cover Lua, Svelte, and TypeScript retrieval.
Co-authored-by: 小为 <1053122090@qq.com>
Co-authored-by: GPT-5 <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: 小为 <1053122090@qq.com>
Co-authored-by: GPT-5 <noreply@openai.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(extraction): index Python docstrings, not just preceding comments (#1905)
getPrecedingDocstring only walks preceding comment siblings, so a Python
docstring — a bare string literal first in the body — never reached the
docstring column, never entered nodes_fts, and was never shown by
`codegraph node`. The identical sentence written as a leading `#` comment was
both. For a Python codebase that is most of the prose there is. (#1905)
Adds an optional getBodyDocstring() to LanguageExtractor, in the same shape as
getSignature(), and routes every docstring call site through one docstringFor()
helper that consults both sources. A node carrying a comment AND a docstring
keeps both, joined: they are two things the author wrote about the same symbol
and the column is free text.
The Python implementation reads the grammar`s string_content rather than
slicing quotes off the raw text, so r/u/b prefixes and both triple-quote forms
work without a regex per case; f-strings are skipped because an interpolated
string is code, not prose. Dedent follows PEP 257 (first line exempt).
Scoped to Python deliberately. Julia (a string sibling before the def) and
Elixir (@doc) fit the same hook and are left as follow-ups.
* fix(extraction): index Python body docstrings across backends (#1905)
Python extraction only consulted preceding comments, leaving body docstrings absent from search and rendered prose.
Extend the contributor's hook to module and definition docstrings, and mirror it in the native kernel.
Handle comments, concatenated literals, and indentation consistently while rejecting bytes, f-strings, and tuples.
Verify persistence, exact-name ranking, MCP/CLI rendering, and native/WASM parity.
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The vendored grammar (master@0aca5d0a6f, in both the wasm and the kernel's C
sources) parsed `class A(x: X)(implicit y: Y) extends B(x)(y) with C with D`
with an ERROR and cut the extends clause after `B(x)(y)`, so `C` and `D` never
became inheritance edges. Both copies now come from the v0.26.2 release: the
wasm is the release asset, parser.c/scanner.c are the tag's generated sources.
The grammar change is additive over the old pin: no node type or field was
removed.
On two real repos the share of Scala files that fail to parse drops to zero
(lila 85/1565, playframework 83/846), with no file newly failing. Edges from
those files were built from error-recovered trees; playframework, for
example, nested top-level classes inside their neighbours and drew an
`extends` self-loop.
The kernel defer pins used inputs that the new grammar reads cleanly; they
now use inputs that still fail, and the old inputs pin that both paths
extract them in parity.
(cherry picked from commit 199bc8d8a0)
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): name a function bound through a curried wrapper (#1747)
reactHookBoundName already names an anonymous function after the
variable_declarator that binds it. The shape is general, but the method
is bounded to three React hooks, so `const run =
Effect.fn("Session.run")(function* () {…})` produced no function node at
all -- and its body's calls were attributed to the enclosing container,
so a file gained an outgoing edge belonging to a function and the
callee's caller list named the file instead.
Bound the new path by a different, equally decidable test: the callee is
itself a call, i.e. a factory returning the wrapper. That admits
Effect.fn(...)(fn), connect(mapState)(fn) and a project's own
wrap("n")(fn), and excludes the single-call forms whose argument is a
computation -- useMemo(() => …, []), arr.map(…) -- which stay anonymous
exactly as before. Widening to "any callee" would have given useMemo a
function node and reddened react-hook-handlers.test.ts.
Generators are admitted here and not in reactHookBoundName: a React
handler is never a generator, while function* is the common form in the
ecosystem this shape comes from, and it is what the report opens with --
broadening the callee test alone would not have fixed that repro.
Mirrored in the Rust kernel; kernel-parity.mjs reports byte-parity on
the new shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WgtWMywGTyu6twnk1Bu9nP
(cherry picked from commit 4f2b40080d)
* fix(extraction): name a curried-wrapper function that is an object member (#1747)
The curried-wrapper bound stopped at a variable_declarator parent, so
`return { getMode: Effect.fn("ACP.Session.getMode")(function* () {…}) }`
still produced no function node. An Effect service is usually an object a
factory returns, and on sst/opencode (df23b7f) these member positions were
the bulk of what the branch missed.
The member already has a name in the source: its property key. This repo
names `key: () => {}` pairs by that key (extractObjectLiteralFunctions /
objectKeyName), so a `pair` parent is now named the same way. The
callee-is-a-call rule is unchanged, so `key: useMemo(() => …, [])` and
`key: arr.map(…)` stay anonymous.
extractObjectLiteralFunctions also reaches such a member, because an
exported object with an inline function member is extracted member by member
and its initializer is never walked.
A wrapper passed as an argument to another call has no binding at all and is
left as it was.
Mirrored in the Rust kernel; kernel-parity.mjs reports byte-parity on the
new shapes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dfb3aazPY1ccmp5vam4k3L
(cherry picked from commit bbdef20542)
* chore: drop CHANGELOG entry (collected separately)
---------
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The trail store saves up to 64 hops, /api/flow read at most 24, the
in-memory trail had no limit, and /api/nodes answers 60 ids per request.
So "Read as flow" failed on any walk of 25 or more hops, including trails
the store had just saved, and a cold load of a 61-64 hop link never got its
names back.
/api/flow now reads up to the store's MAX_TRAIL_HOPS. The trail keeps the
same 64 hops, dropping the oldest on push and on hydrate. resolveTrailNames
asks /api/nodes in batches of 60 and merges the answers.
(cherry picked from commit 4da454022d)
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
buildRoutes looked up node ids only for the first 60 distinct handler
files, one getNodesInFile query each, so every later row went out with
handlerId null and the viewer called the handler "not in the index". Load
the nodes of every handler file in one chunked query instead
(getNodesInFiles), which also drops the per-file query the cap existed
to bound.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
getImpactRadius, getCallers and getCallees are depth-limited DFS walks
with one visited set. A node first reached through a longer path at the
depth limit was marked visited without being expanded, so when a shorter
path reached it later it was skipped and its own dependents within the
limit were lost. Record the shallowest depth each node was expanded at
and expand it again when a nearer path reaches it. Results and edges are
still reported once, and output order is unchanged where nothing was
missing.
(cherry picked from commit ec56377b37)
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`readJsonFile` copied an unparseable file to `<path>.backup` and warned
that it was about to be overwritten. That is the right thing to do just
before a write, but every target's `detect()` reads its agent's config
through the same helper — and `install --refresh` detects every target
in order to skip the ones codegraph was never installed into.
So an empty `~/.gemini/config/mcp_config.json`, sitting beside an
Antigravity install codegraph does not manage, got a 0-byte
`mcp_config.json.backup` and a warning about an overwrite that never
came. Detection is meant to be read-only.
`readJsonFile` is now a pure read. Preserving an unparseable config
moves to `writeJsonFile`, the one place a target replaces a JSON config
— so the backup still happens wherever it mattered, and only there.
Reads that decide not to write (the legacy-entry cleanups, the uninstall
sweeps) no longer leave one behind either.
Unchanged on purpose: an install that really does overwrite an empty
config still backs it up. Skipping the copy when there is nothing to
preserve is a separate call.
A Go method is only reachable through a value or a method expression, so a
bare call such as `relogin(ctx)` on a func parameter was being bound to a
same-named method in its own package, or (when capitalised) to an exported
method of a package the file does not import. Extend the #1714 receiver-less
check to Go, reading the call site back from source so the
`pkg.Factory().Method()` chain that reaches the resolver as a bare `Method`
ref keeps its edge.
Claude-Session: https://claude.ai/code/session_01MuSia5rNjiWCqzqW9iTJGD
(cherry picked from commit 812cf0d45f)
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
On Windows this file leaked 171 temp directories per run and failed 4 tests, both from the same cause: things that hold the SQLite database open outlive the code that removes their directory.
afterEach removed tempDir only in the else branch of \if (cg)\, and cg is describe-scoped and never reset, so from the first test that assigns it every later test kept its directory. destroy() is a deprecated alias for close(): it releases the database but does not remove the project, so removal cannot be its alternative -- uninitialize() is the method that removes. Removal is now unconditional.
That alone turns the silent leak into EPERM, because five DatabaseConnection.open calls were never closed and four nested tests removed their own tempProject while the outer cg still held it open. Each connection is now closed once its rows are materialised, and each nested finally closes the graph before removing its directory.
Measured on Windows 11 with TEMP redirected to a private directory: before, 171 leaked directories and 4 failed / 196 passed; after, 0 leaked directories and 200 passed. Fixes#1777.
`extension_type_declaration` appears in none of the three places Dart's other
type-like declarations do. `extension_declaration` — the older `extension` — is
in all three, and the two names are near neighbours.
That is why #1780's `isInsideClassLikeNode()` gate dropped these members and no
others: the gate asks whether a class-like node is on the stack, and an
`extension type` never put one there, while `extension`, `mixin` and `class`
all did. The gate surfaced the omission rather than causing it. Before it, the
members were still reached — but as top-level `function:km` rather than
`method:MetersT::km`, indexed and attributed to nothing.
The kernel omits the same node type, so it walks the members through its own
fallback and mints them as top-level functions with no `MetersT` to belong to.
`kernel-dart-parity.test.ts` fails four cases on `main` because of it; fixing
only the wasm side would leave those red.
Listing it alongside the others is the whole fix — `extraClassNodeTypes` and
`dartEnclosingTypeName` on the wasm side, the two `matches!` arms on the
kernel's. Both paths now give:
class:MetersT · method:MetersT::km · method:MetersT::report
with `report`'s span running to its closing brace rather than stopping at the
signature line, and an ordinary class untouched.
Claude Code's UserPromptSubmit hook also fires for the
`<task-notification>…</task-notification>` blocks the host injects as
`user` messages when a background agent finishes. Those blocks are long
machine text full of identifier-shaped tokens, so they pass the structural
gate and the whole blob is sent to codegraph_explore as the query — a
3.5s median (7.4s max) stall on the blocking path, on text the user never
typed.
Recognize a prompt that consists solely of one task-notification envelope
(leading/trailing whitespace allowed) and exit before any project lookup,
gate or explore work. A prompt that merely mentions the marker, or has
text around it, is still treated as a user prompt.
Fixes#1832
Co-authored-by: danusha2345 <danusha2345@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Two bugs in the MyBatis mapper extractor:
1. A close tag with trailing whitespace (`</select >`, legal XML) was not
matched by the statement regex, causing the non-greedy body to overshoot
to the next close tag — silently swallowing the statement in between.
Fix: add `\s*` before `>` in the close-tag pattern.
2. A fully-qualified `<include refid="com.example.M.base">` had every dot
replaced with `::`, producing `com::example::M::base` — which never
matched the fragment node's `qualifiedName` (`com.example.M::base`).
Fix: split on the last dot only, matching `qualifyStatement`'s logic.
Also fixes the include-offset calculation to derive from the opening tag
length rather than subtracting the close tag, so it stays correct when the
close tag carries trailing whitespace.
Closes#1209
A directory removal adds no pending file, only needsFullScan. The retry
after a failed sync (lock contention or a generic failure) was gated on
pending files alone, so the owed full reconcile was dropped and the
removed directory's files kept their nodes. Reschedule while needsFullScan
is set as well. A regression test also pins the mid-sync scope-change case,
which already follows a scoped pass with a full scan.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
In a polyglot repo the Spring resolver is detected globally (a sibling
Java module with spring-boot in its pom is enough), and its resolve()
then ran on references from every language. Pattern 4's bare-name
fallback ('any [A-Z][a-zA-Z]+ name, prefer /model/ dirs, else ANY
same-named class') could therefore hijack a Scala 'extends ExtCustomer'
to an unrelated same-named test class, corrupting the inheritance graph
that impact analysis walks.
Two gates in front of the DI/convention patterns (1-5):
- skip refs whose language is not java/kotlin - Spring DI conventions
say nothing about other languages
- skip extends/implements refs entirely - inheriting from a class is
never a DI injection point; those must resolve via imports/name
matching
The Spring config-key resolution above the gates is untouched (it
already gates itself). Regression tests cover the hijack case and the
still-working Java DI path.
Co-authored-by: Hubert Tarnacki <hubert.tarnacki@comarch.com>
* fix: index Java packages named build (#1642)
* fix(extraction): preserve Java packages named build (#1642)
The default build-directory exclusion hid legal Java package paths before extraction.
Retain the contributor's main/test Java source-root exceptions, restricted to directories so other default exclusions survive.
Cover filesystem and git enumeration, explicit ignores, indexed retrieval, and incremental sync.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(extraction): preserve Java packages named build (#1642)
The default build-directory exclusion hid legal Java package paths before extraction.
Retain the contributor's main/test Java source-root exceptions, restricted to directories so other default exclusions survive.
Cover filesystem and git enumeration, explicit ignores, indexed retrieval, and incremental sync.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(mcp): match pending paths as whole paths in the stale-file banner (#1968)
withStalenessNotice counted a pending file as referenced whenever its path
was a substring of the response, so a pending src/app.ts was named in the
banner for a response that only showed src/app.tsx, and a pending root
app.tsx for one that showed src/app.tsx. A match now has to stand as a
whole path.
* fix(mcp): match complete paths in stale banners (#1968)
Substring matching incorrectly classified pending files as referenced inside longer paths.
Build on PR #1971 with response delimiters that reject Unicode and punctuation continuations while preserving exact paths and line references.
Add real-index regression coverage for truthful warnings and the elsewhere footer.
Co-authored-by: Eric Minish <eric.minish@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Eric Minish <eric.minish@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(explore): flag dynamic imports built from a template or concatenation (#1967)
The JS dynamic-import form skipped any argument that started with a quote,
so import(`./locales/${lang}.js`) and import('./x/' + y) were treated as
literal imports and the boundary note never appeared. The call is now
dropped only when its argument is exactly one complete string literal.
* fix(explore): detect runtime import templates and concatenations (#1967)
The detector skipped every import argument beginning with a quote or backtick.
Build on PR #1970 to suppress only complete static literals and recognize interpolation with escape-aware scanning.
Add scanner regressions and real-file/SQLite explore coverage.
Co-authored-by: Eric Minish <eric.minish@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Eric Minish <eric.minish@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A query span like `scripts/deploy` was neither pinned nor reported.
`isClearlyPathShaped` demands a dot-extension on the last segment, so a
dotless slashed span that resolved to no indexed file fell through every
arm: it was not consumed, never entered `unresolvedPathSpans`, and its
fragments (`scripts`, `deploy`) went on to feed FTS. The agent got a pile
of unrelated source with no hint that the file it named was never read.
Both caveat sinks — the empty-subgraph note and the summary line — were
already in place; they were simply never handed the span.
Shape alone cannot decide this: `and/or`, `input/output` and
`client/server` have exactly the same shape, and loosening the shape test
to "has a slash" strips that prose out of every query and mints a false
"No indexed file uniquely matches `and/or`" caveat (the existing
`leaves slash-bearing non-paths alone` test pins this). The file's
existence on disk is the discriminator instead — and since query-paths.ts
is deliberately pure (no DB, no fs, safe in the query-pool workers), the
check is injected by the caller as an `existsOnDisk` predicate. The call
site in tools.ts owns the project root and runs it through
`validatePathWithinRoot`, so a `../` span cannot probe outside the
project, and only a regular file counts — a directory span keeps flowing
to the normal matching pipeline.
Retrieval effect (AGENTS.md "do not regress"), measured as a deterministic
A/B on the built dist over an indexed repro, baseline = upstream/main:
- query that is only the dotless path — before: 998 chars of unrelated
source, no caveat; after: `No relevant code found ... (no indexed file
uniquely matches `scripts/deploy`)`.
- same path inside a real symbol query — output byte-identical except the
added caveat sentence (+51 chars); the same 4 symbols across 2 files.
- query with no path span — byte-identical between arms.
Claude-Session: https://claude.ai/code/session_01XDR1wm73oH8J8cRWnKyv9m
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
`codegraph node "src/app.ts:42-80"` answered `No indexed file matches`
for a file that IS indexed, worded byte-for-byte like a genuine miss, so
there was no way to tell "wrong path" from "right path, wrong spelling".
The file-view normalizer treated the `:42-80` as part of the filename.
explore has stripped exactly these shapes all along
(src/search/query-paths.ts) — file-view now does too, and the range it
strips becomes the read window.
- The literal spelling is resolved FIRST; only when that finds nothing is
the suffix stripped, so a file genuinely named `foo:12` still wins.
- Supports `:<a>`, `:<a>-<b>`, `#L<n>`, `#L<a>-L<b>` (and `#L<a>-<b>`).
- A stripped range maps to `offset=a, limit=b-a+1`; a bare `:<a>` is a
start-here pointer (Read given only an offset). An explicit
offset/limit from the caller always wins over the suffix.
- `#L<n>` was not mentioned in the issue but was broken the same way.
Tests: 8 new cases across __tests__/node-file-view.test.ts and
__tests__/cli-node-command.test.ts, including a vacuity guard (the same
path without the suffix resolves) and a genuine-miss case. Red-armed:
with the fix reverted, 6 of them fail with the exact
`No indexed file matches` string.
Claude-Session: https://claude.ai/code/session_01XDR1wm73oH8J8cRWnKyv9m
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Preserve the local regression-audit commits alongside their upstream squash and subsequent fixes. Resolve the overlapping changelog, regression tests, audit report, and name matcher to retain the reviewed upstream follow-ups.
Fixes#1840. Reuses and extends #1855 with lexical/import ownership and cache invalidation controls.
Co-authored-by: Max Hsu <maxmilian@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes#1861. Reuses L4XB's receiver retention and owner resolution from #1866, with duplicate-owner and edit/sync guards.
Co-authored-by: L4XB <lukas.buck@e-mail.de>
Preserve the indexed-commit approach and regression cases from #1848, with dirty-path retention, commit-race guards, NUL Git paths, and failed-I/O recovery.
Fixes#1829.
Co-authored-by: Eric Minish <eric.minish@gmail.com>