Skip to content

feat(cli): report what semantic_search actually searched - #14335

Merged
marius-kilocode merged 2 commits into
Kilo-Org:mainfrom
sylwester-liljegren:feat/semantic-search-scope-reporting
Sep 21, 2026
Merged

marius-kilocode merged 2 commits into
Kilo-Org:mainfrom
sylwester-liljegren:feat/semantic-search-scope-reporting

Conversation

@sylwester-liljegren

Copy link
Copy Markdown
Contributor

Issue

No existing issue. Split out of #13589 at review request — these changes affect semantic_search output for every user, not only multi-root workspaces, so they are easier to judge on their own.

Context

semantic_search covers a single indexed root, and two things about how it reports that were misleading:

Its description claimed it searched "the entire current workspace", and pointed at Grep and Glob for exploring outside that root. Both are bounded to the same root and fail with Path escapes the active Location (tool/glob.ts, tool/grep.ts), so Read is the only one that can actually reach outside. That advice was wrong before this PR.

More importantly, KiloIndexing.search returns [] when the index is disabled, still building, or broken (indexing.ts:579), which was indistinguishable from a genuine miss. The output said No relevant code found, so a model would take that as evidence the code does not exist and act on it.

Implementation

Output now names the scope actually searched, and an empty result explains itself in terms of index state — complete, still building with progress, disabled, or failed. Index status is only consulted on the empty path, and it rides on state search() has already resolved (both go through the same hit()), so no extra indexing work is triggered.

The wording lives in semantic-search-output.ts so it can be tested without booting the indexing worker, which is why the tests are fast and hermetic.

No change to the indexer, consent, storage, or embedding behaviour.

Screenshots / Video

N/A — CLI tool output, no visual surface.

How to Test

Manual/local verification

Executed by the agent:

  • bun run typecheck in packages/opencode — pass
  • bun run script/test-runner.ts kilocode/semantic-search.test.ts kilocode/tool/semantic-search-output.test.ts — pass

semantic-search-output.test.ts is new and covers each index state, the scope wording, Windows path separators, and that a disabled index never claims the code is absent. Three assertions in the existing semantic-search.test.ts were updated for the new output, and index status is stubbed there so the wording no longer depends on real indexing progress — it was picking up a genuine "still building (0%, 0/0 files)" state in CI.

Reviewer test steps

  1. In a project with codebase indexing enabled and fully built, run a semantic_search for something that does not exist — the output should say the index is up to date and therefore that no similar code exists
  2. Disable indexing in Kilo Settings and repeat — the output should say nothing was searched, and should not claim the code is absent
  3. Run a search that matches — the header should name the indexed root

Blocked checks and substitute verification

  • bun turbo typecheck at the repo root could not complete: @kilocode/kilo-jetbrains#typecheck fails with "Cannot find a Java installation matching languageVersion=21" on this machine. This PR touches no Kotlin. Substitute verification was running typecheck directly in packages/opencode.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A
  • Changeset considered for user-facing changes
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

Get in Touch

semantic_search covers one indexed root, but its description claimed it
searched the entire current workspace, and it pointed at Grep and Glob
for exploring outside that root even though both are bounded to the same
root and cannot reach outside it either. Read is the only one that can.

KiloIndexing.search returns an empty array when the index is disabled,
still building, or broken, which was indistinguishable from a genuine
miss. A model reading "No relevant code found" would take it as proof
the code does not exist and act on that.

Output now names the scope it searched, and an empty result explains
itself in terms of index state. The wording lives in its own module so
it can be exercised without booting the indexing worker.

Split out of the multi-root @ mentions work, since it changes tool
output for every user rather than only for multi-root workspaces.
- Find files by filename or extension — use `Glob`
- Read the contents of a known file — use `Read`
- Explore files outside the current workspace - use `Grep`, `Glob`, and `Read`
- Explore files outside the indexed root — use `Read` with an absolute path. `Grep` and `Glob` are bounded to the same root as this tool and cannot reach outside it either.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: This states that Grep and Glob "cannot reach outside" the indexed root, but that appears inaccurate. Both tools accept absolute paths and delegate to assertExternalDirectoryEffect (packages/opencode/src/tool/grep.ts:60, packages/opencode/src/tool/glob.ts:65), which allows paths outside the root after an external_directory prompt — and the default action when no rule matches is ask (packages/opencode/src/permission/index.ts:107). glob.ts even has explicit Kilo support for absolute patterns such as ~/.config/kilo/command/*.md. Bounded-by-default is the useful point; "cannot reach outside" is stronger than the implementation. Consider rewording to something like "they search the indexed root by default and need external_directory approval to go outside it" so the model does not avoid a working approach.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — you're right, and I've fixed it in 8b7b8a5.

I had verified this earlier against an older revision where both tools resolved a base and then hard-failed with Path escapes the active Location. That is no longer how they work: glob.ts:65 and grep.ts:60 now call assertExternalDirectoryEffect, which returns false when the target is inside the instance directory or worktree and otherwise raises an external_directory prompt via ctx.ask (external-directory.ts:36-54). It permits the path once approved rather than refusing it, and glob.ts carries an explicit Kilo change for absolute patterns.

So "cannot reach outside" was wrong, and wrong in the direction that matters — it would steer a model away from an approach that works.

Reworded to keep the useful distinction rather than drop it, since semantic_search genuinely cannot go outside its indexed root:

Explore files outside the indexed root — use Read, Grep or Glob with an absolute path. They search the indexed root by default and need external_directory approval to go beyond it, but they can get there; this tool cannot.

I applied the same correction to the constraint line and to the runtime empty-result message, which carried the same "use Read with an absolute path" advice. Tests updated for the new wording and passing.

@kilo-code-bot

kilo-code-bot Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/kilocode/tool/semantic-search.txt 14 New agent guidance says Grep and Glob "cannot reach outside" the indexed root, but both accept absolute paths and reach external directories after an external_directory ask (default action is ask; glob.ts has explicit absolute-pattern support). The claim overstates the restriction.
Files Reviewed (6 files)
  • .changeset/semantic-search-scope-reporting.md - 0 issues
  • packages/opencode/src/kilocode/tool/semantic-search-output.ts - 0 issues
  • packages/opencode/src/kilocode/tool/semantic-search.ts - 0 issues
  • packages/opencode/src/kilocode/tool/semantic-search.txt - 1 issue
  • packages/opencode/test/kilocode/semantic-search.test.ts - 0 issues
  • packages/opencode/test/kilocode/tool/semantic-search-output.test.ts - 0 issues

Notes: the output module split is clean and Kilo-owned, wording is covered by hermetic tests, no memory leaks, no let/else/empty-catch/any violations, and the changeset is present and user-facing. KiloIndexing.status is only consulted on the empty path, consistent with the stated intent.

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

Both delegate to assertExternalDirectoryEffect, which prompts for external_directory and allows the path once approved, rather than refusing it. Saying they cannot reach outside the root would steer a model away from an approach that works. semantic_search itself still cannot, which is the distinction worth drawing.

@marius-kilocode marius-kilocode left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clean split and the focused tests.

Reviewed and tested locally: the two test files pass (16 focused, 234 in the kilo tool suite), bun run typecheck is clean, and I verified the new output end to end in VS Code with indexing disabled. It now says nothing was searched instead of reporting a miss, and names the searched root.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants