diff --git a/src/core/prompts/sections/__tests__/objective.spec.ts b/src/core/prompts/sections/__tests__/objective.spec.ts index e129392925..f720032188 100644 --- a/src/core/prompts/sections/__tests__/objective.spec.ts +++ b/src/core/prompts/sections/__tests__/objective.spec.ts @@ -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) diff --git a/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts b/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts index 98e4da3a73..3dfe3beff5 100644 --- a/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts +++ b/src/core/prompts/sections/__tests__/tool-use-guidelines.spec.ts @@ -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 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) diff --git a/src/core/prompts/sections/capabilities.ts b/src/core/prompts/sections/capabilities.ts index e2d27db5bb..18a50abc16 100644 --- a/src/core/prompts/sections/capabilities.ts +++ b/src/core/prompts/sections/capabilities.ts @@ -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.` : "" diff --git a/src/core/prompts/sections/objective.ts b/src/core/prompts/sections/objective.ts index 3d18a7e209..76b76562ab 100644 --- a/src/core/prompts/sections/objective.ts +++ b/src/core/prompts/sections/objective.ts @@ -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, " diff --git a/src/core/prompts/sections/rules.ts b/src/core/prompts/sections/rules.ts index 5828568ac4..6a756469a9 100644 --- a/src/core/prompts/sections/rules.ts +++ b/src/core/prompts/sections/rules.ts @@ -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" diff --git a/src/core/prompts/sections/tool-use-guidelines.ts b/src/core/prompts/sections/tool-use-guidelines.ts index a526bbc711..36b49e9817 100644 --- a/src/core/prompts/sections/tool-use-guidelines.ts +++ b/src/core/prompts/sections/tool-use-guidelines.ts @@ -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 diff --git a/src/core/prompts/tools/__tests__/index.spec.ts b/src/core/prompts/tools/__tests__/index.spec.ts new file mode 100644 index 0000000000..d52ce683a6 --- /dev/null +++ b/src/core/prompts/tools/__tests__/index.spec.ts @@ -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") + } + }) + }) +}) diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 3fd5a636a4..d9e5ff61df 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -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") }