fix: prevent semantic search when indexing is not complete (#5662)

- Add indexing state check to codebase_search tool availability logic
- Update all prompt sections to verify CodeIndexManager.state === 'Indexed'
- Ensure semantic search is only available when indexing is fully complete
- Add comprehensive tests for indexing state scenarios
- Prevents attempts to use semantic search during indexing process

Fixes #5662
This commit is contained in:
Roo Code 2025-07-13 13:30:17 +00:00
parent e84dd0a2cf
commit 8baaf7d18a
8 changed files with 223 additions and 6 deletions

View file

@ -7,6 +7,7 @@ describe("getObjectiveSection", () => {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: true,
state: "Indexed",
} as CodeIndexManager
// Mock CodeIndexManager with codebase search unavailable
@ -14,6 +15,15 @@ describe("getObjectiveSection", () => {
isFeatureEnabled: false,
isFeatureConfigured: false,
isInitialized: false,
state: "Standby",
} as CodeIndexManager
// Mock CodeIndexManager with indexing in progress
const mockCodeIndexManagerIndexing = {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: true,
state: "Indexing",
} as CodeIndexManager
describe("when codebase_search is available", () => {
@ -39,6 +49,16 @@ describe("getObjectiveSection", () => {
})
})
describe("when codebase_search is configured but indexing is in progress", () => {
it("should not include codebase_search enforcement when indexing is not complete", () => {
const objective = getObjectiveSection(mockCodeIndexManagerIndexing)
// Check that the objective does not include the codebase_search enforcement
expect(objective).not.toContain("you MUST use the `codebase_search` tool")
expect(objective).not.toContain("BEFORE using any other search or file exploration tools")
})
})
it("should maintain proper structure regardless of codebase_search availability", () => {
const objectiveEnabled = getObjectiveSection(mockCodeIndexManagerEnabled)
const objectiveDisabled = getObjectiveSection(mockCodeIndexManagerDisabled)

View file

@ -7,6 +7,7 @@ describe("getToolUseGuidelinesSection", () => {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: true,
state: "Indexed",
} as CodeIndexManager
// Mock CodeIndexManager with codebase search unavailable
@ -14,6 +15,15 @@ describe("getToolUseGuidelinesSection", () => {
isFeatureEnabled: false,
isFeatureConfigured: false,
isInitialized: false,
state: "Standby",
} as CodeIndexManager
// Mock CodeIndexManager with indexing in progress
const mockCodeIndexManagerIndexing = {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: true,
state: "Indexing",
} as CodeIndexManager
describe("when codebase_search is available", () => {
@ -68,6 +78,30 @@ describe("getToolUseGuidelinesSection", () => {
})
})
describe("when codebase_search is configured but indexing is in progress", () => {
it("should not include codebase_search enforcement when indexing is not complete", () => {
const guidelines = getToolUseGuidelinesSection(mockCodeIndexManagerIndexing)
// Check that the guidelines do not include the codebase_search enforcement
expect(guidelines).not.toContain(
"CRITICAL: For ANY exploration of code you haven't examined yet in this conversation, you MUST use the `codebase_search` tool FIRST",
)
expect(guidelines).not.toContain("semantic search to find relevant code based on meaning")
})
it("should maintain proper numbering without codebase_search when indexing", () => {
const guidelines = getToolUseGuidelinesSection(mockCodeIndexManagerIndexing)
// Check that all numbered items are present with correct numbering (same as disabled case)
expect(guidelines).toContain("1. In <thinking> tags")
expect(guidelines).toContain("2. Choose the most appropriate tool")
expect(guidelines).toContain("3. If multiple actions are needed")
expect(guidelines).toContain("4. Formulate your tool use")
expect(guidelines).toContain("5. After each tool use")
expect(guidelines).toContain("6. ALWAYS wait for user confirmation")
})
})
it("should include iterative process guidelines regardless of codebase_search availability", () => {
const guidelinesEnabled = getToolUseGuidelinesSection(mockCodeIndexManagerEnabled)
const guidelinesDisabled = getToolUseGuidelinesSection(mockCodeIndexManagerDisabled)

View file

@ -20,7 +20,8 @@ CAPABILITIES
codeIndexManager &&
codeIndexManager.isFeatureEnabled &&
codeIndexManager.isFeatureConfigured &&
codeIndexManager.isInitialized
codeIndexManager.isInitialized &&
codeIndexManager.state === "Indexed"
? `
- You can use the \`codebase_search\` tool to perform semantic searches across your entire codebase. This tool is powerful for finding functionally relevant code, even if you don't know the exact keywords or file names. It's particularly useful for understanding how features are implemented across multiple files, discovering usages of a particular API, or finding code examples related to a concept. This capability relies on a pre-built index of your code.`
: ""

View file

@ -8,7 +8,8 @@ export function getObjectiveSection(
codeIndexManager &&
codeIndexManager.isFeatureEnabled &&
codeIndexManager.isFeatureConfigured &&
codeIndexManager.isInitialized
codeIndexManager.isInitialized &&
codeIndexManager.state === "Indexed"
const codebaseSearchInstruction = isCodebaseSearchAvailable
? "First, for ANY exploration of code you haven't examined yet in this conversation, you MUST use the `codebase_search` tool to search for relevant code based on the task's intent BEFORE using any other search or file exploration tools. This applies throughout the entire task, not just at the beginning - whenever you need to explore a new area of code, codebase_search must come first. Then, "

View file

@ -55,7 +55,8 @@ export function getRulesSection(
codeIndexManager &&
codeIndexManager.isFeatureEnabled &&
codeIndexManager.isFeatureConfigured &&
codeIndexManager.isInitialized
codeIndexManager.isInitialized &&
codeIndexManager.state === "Indexed"
const codebaseSearchRule = isCodebaseSearchAvailable
? "- **CRITICAL: For ANY exploration of code you haven't examined yet in this conversation, you MUST use the `codebase_search` tool FIRST before using search_files or other file exploration tools.** This requirement applies throughout the entire conversation, not just when starting a task. The codebase_search tool uses semantic search to find relevant code based on meaning, not just keywords, making it much more effective for understanding how features are implemented. Even if you've already explored some parts of the codebase, any new area or functionality you need to understand requires using codebase_search first.\n"

View file

@ -5,7 +5,8 @@ export function getToolUseGuidelinesSection(codeIndexManager?: CodeIndexManager)
codeIndexManager &&
codeIndexManager.isFeatureEnabled &&
codeIndexManager.isFeatureConfigured &&
codeIndexManager.isInitialized
codeIndexManager.isInitialized &&
codeIndexManager.state === "Indexed"
// Build guidelines array with automatic numbering
let itemNumber = 1

View file

@ -0,0 +1,154 @@
import { getToolDescriptionsForMode } from "../index"
import type { CodeIndexManager } from "../../../../services/code-index/manager"
describe("getToolDescriptionsForMode", () => {
const mockCwd = "/test/workspace"
const mockSupportsComputerUse = false
// Mock CodeIndexManager with codebase search available (indexed state)
const mockCodeIndexManagerIndexed = {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: true,
state: "Indexed",
} as CodeIndexManager
// Mock CodeIndexManager with codebase search unavailable (disabled)
const mockCodeIndexManagerDisabled = {
isFeatureEnabled: false,
isFeatureConfigured: false,
isInitialized: false,
state: "Standby",
} as CodeIndexManager
// Mock CodeIndexManager with indexing in progress
const mockCodeIndexManagerIndexing = {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: true,
state: "Indexing",
} as CodeIndexManager
// Mock CodeIndexManager in error state
const mockCodeIndexManagerError = {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: true,
state: "Error",
} as CodeIndexManager
describe("codebase_search tool availability", () => {
it("should include codebase_search when manager is indexed", () => {
const tools = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerIndexed,
)
expect(tools).toContain("## codebase_search")
expect(tools).toContain("Find files most relevant to the search query")
})
it("should exclude codebase_search when feature is disabled", () => {
const tools = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerDisabled,
)
expect(tools).not.toContain("## codebase_search")
})
it("should exclude codebase_search when indexing is in progress", () => {
const tools = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerIndexing,
)
expect(tools).not.toContain("## codebase_search")
})
it("should exclude codebase_search when manager is in error state", () => {
const tools = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerError,
)
expect(tools).not.toContain("## codebase_search")
})
it("should exclude codebase_search when manager is undefined", () => {
const tools = getToolDescriptionsForMode("code", mockCwd, mockSupportsComputerUse, undefined)
expect(tools).not.toContain("## codebase_search")
})
it("should exclude codebase_search when feature is enabled but not configured", () => {
const mockCodeIndexManagerNotConfigured = {
isFeatureEnabled: true,
isFeatureConfigured: false,
isInitialized: true,
state: "Standby",
} as CodeIndexManager
const tools = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerNotConfigured,
)
expect(tools).not.toContain("## codebase_search")
})
it("should exclude codebase_search when feature is configured but not initialized", () => {
const mockCodeIndexManagerNotInitialized = {
isFeatureEnabled: true,
isFeatureConfigured: true,
isInitialized: false,
state: "Standby",
} as CodeIndexManager
const tools = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerNotInitialized,
)
expect(tools).not.toContain("## codebase_search")
})
})
describe("other tools availability", () => {
it("should always include basic tools regardless of codebase_search availability", () => {
const toolsWithCodebaseSearch = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerIndexed,
)
const toolsWithoutCodebaseSearch = getToolDescriptionsForMode(
"code",
mockCwd,
mockSupportsComputerUse,
mockCodeIndexManagerDisabled,
)
// Check that basic tools are present in both cases
for (const tools of [toolsWithCodebaseSearch, toolsWithoutCodebaseSearch]) {
expect(tools).toContain("## read_file")
expect(tools).toContain("## write_to_file")
expect(tools).toContain("## search_files")
expect(tools).toContain("## list_files")
}
})
})
})

View file

@ -101,10 +101,15 @@ export function getToolDescriptionsForMode(
// Add always available tools
ALWAYS_AVAILABLE_TOOLS.forEach((tool) => tools.add(tool))
// Conditionally exclude codebase_search if feature is disabled or not configured
// Conditionally exclude codebase_search if feature is disabled, not configured, or indexing is not complete
if (
!codeIndexManager ||
!(codeIndexManager.isFeatureEnabled && codeIndexManager.isFeatureConfigured && codeIndexManager.isInitialized)
!(
codeIndexManager.isFeatureEnabled &&
codeIndexManager.isFeatureConfigured &&
codeIndexManager.isInitialized
) ||
codeIndexManager.state !== "Indexed"
) {
tools.delete("codebase_search")
}