Skip to content

Zero-result hint when --exact filters out all matches #88

Description

@Widthdom

Context

After #81 and #83, `symbols`, `definition`, `references`, `callers`, `callees` all support `--exact` / `exact: true` for ASCII-case-insensitive name equality. A common failure mode surfaced during dogfooding:

```
$ cdidx references HandleRequet --exact
No references found.
```

The user fat-fingered the name. Without `--exact`, `HandleRequet` (note the missing `s`) would never match anything either, but with `--exact` they now fall into a second failure mode too: they pass a correctly-spelled name whose casing does not match a non-ASCII indexed symbol (e.g. café_init vs Café_Init — SQLite NOCASE does not fold é, see #86), or whose exact spelling is right but the symbol actually doesn't exist in the codebase.

In all three cases, the current zero-result message is identical, so AI clients and humans cannot tell whether:

  1. The name is genuinely absent from the index.
  2. The name exists but only under different casing (non-ASCII; blocked until symbols --exact: fold non-ASCII casing via a stored Unicode-normalized name key #86 lands).
  3. The name is close to something that exists and a relaxed (substring) query would have found it.

Proposal

When `--exact` produces zero results, automatically run the same query without `--exact` and print a structured hint only if the relaxed query would have returned results. No extra round-trip for the caller — it happens server-side.

Shape for CLI human output:

```
No references found.
Hint: --exact found 0 matches, but substring matching would return 12 (e.g. `HandleRequest`, `HandleRequestAsync`). Drop --exact or pass the exact indexed casing.
```

Shape for JSON / MCP (new additive fields, no shape change for non-empty results):

```json
{
"count": 0,
"exact_zero_hint": {
"relaxed_count": 12,
"sample_names": ["HandleRequest", "HandleRequestAsync"],
"suggestion": "drop --exact or use the exact indexed casing"
}
}
```

Applies to: `symbols`, `definition`, `references`, `callers`, `callees` — same contract on all five so AI clients can rely on it uniformly. MCP tool output gets the same field under `structuredContent`.

Why this matters

Scope

  • Additive JSON fields (non-breaking).
  • Extra relaxed query only fires when `exact=true` AND the primary query returned 0 results, so the baseline (non-exact) path pays zero cost.
  • Cap `sample_names` at ~5 distinct names to keep payload small.

Out of scope

Reference

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions