From e23d19917fdd6e98174f972dfdf06dcb638c30d7 Mon Sep 17 00:00:00 2001 From: PatrickSys Date: Fri, 16 Oct 2026 02:00:24 +0200 Subject: [PATCH 1/3] fix(search): clarify first-use indexing guidance --- src/tools/search-codebase.ts | 3 +- tests/search-codebase-auto-heal.test.ts | 62 ++++++++++++++----------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/tools/search-codebase.ts b/src/tools/search-codebase.ts index 7a4f822..1eb9f7a 100644 --- a/src/tools/search-codebase.ts +++ b/src/tools/search-codebase.ts @@ -233,7 +233,8 @@ export async function handle( text: JSON.stringify( { status: 'indexing', - message: 'Index was corrupt. Rebuild started — retry shortly.' + message: + 'Index is unavailable or not ready. Indexing has started. Check indexing status, then retry.' }, null, 2 diff --git a/tests/search-codebase-auto-heal.test.ts b/tests/search-codebase-auto-heal.test.ts index 13cd1a0..d00d09e 100644 --- a/tests/search-codebase-auto-heal.test.ts +++ b/tests/search-codebase-auto-heal.test.ts @@ -143,33 +143,43 @@ describe('search_codebase auto-heal', () => { } }); - it('fires background re-index and returns retry message when IndexCorruptedError is thrown', async () => { - const { IndexCorruptedError } = await import('../src/errors/index.js'); - - searchMocks.search.mockRejectedValueOnce( - new IndexCorruptedError('LanceDB index corrupted: missing vector column') - ); - - const { server } = await import('../src/index.js'); - const handler = (server as any)._requestHandlers.get('tools/call'); - - const response = await handler({ - jsonrpc: '2.0', - id: 1, - method: 'tools/call', - params: { - name: 'search_codebase', - arguments: { - query: 'test' + it.each([ + [ + 'missing initial index metadata', + 'Index meta missing or unreadable (rebuild required): ENOENT' + ], + ['genuine index corruption', 'LanceDB index corrupted: missing vector column'] + ])( + 'starts background re-index and returns retry guidance for %s', + async (_scenario, errorMessage) => { + const { IndexCorruptedError } = await import('../src/errors/index.js'); + + searchMocks.search.mockRejectedValueOnce(new IndexCorruptedError(errorMessage)); + + const { server } = await import('../src/index.js'); + const handler = (server as any)._requestHandlers.get('tools/call'); + + const response = await handler({ + jsonrpc: '2.0', + id: 1, + method: 'tools/call', + params: { + name: 'search_codebase', + arguments: { + query: 'test' + } } - } - }); - - const payload = JSON.parse(response.content[0].text); - expect(payload.status).toBe('indexing'); - expect(payload.message).toContain('retry'); - expect(searchMocks.search).toHaveBeenCalledTimes(1); - }, 15000); + }); + + const payload = JSON.parse(response.content[0].text); + expect(payload.status).toBe('indexing'); + expect(payload.message).toBe( + 'Index is unavailable or not ready. Indexing has started. Check indexing status, then retry.' + ); + expect(searchMocks.search).toHaveBeenCalledTimes(1); + }, + 15000 + ); it('returns invalid_params when search_codebase query is missing', async () => { const { server } = await import('../src/index.js'); From d93bb4e7fbb9411732b85732e6d4de70981d7bf6 Mon Sep 17 00:00:00 2001 From: PatrickSys Date: Fri, 16 Oct 2026 02:11:05 +0200 Subject: [PATCH 2/3] fix(search): clarify index retry guidance --- src/index.ts | 4 ++-- src/tools/search-codebase.ts | 2 +- tests/multi-project-routing.test.ts | 30 +++++++++++++++++++++++-- tests/search-codebase-auto-heal.test.ts | 28 +++++++++++++++++++++++ 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index ab539b8..e7c829e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1041,7 +1041,7 @@ export function registerHandlers(target: Server): void { type: 'text', text: JSON.stringify({ status: 'indexing', - message: 'Index build in progress - please retry shortly' + message: 'Index build in progress. Check indexing status, then retry.' }) } ] @@ -1068,7 +1068,7 @@ export function registerHandlers(target: Server): void { type: 'text', text: JSON.stringify({ status: 'indexing', - message: 'Index rebuild in progress - please retry shortly', + message: 'Index rebuild in progress. Check indexing status, then retry.', index: indexSignal }) } diff --git a/src/tools/search-codebase.ts b/src/tools/search-codebase.ts index 1eb9f7a..d68f888 100644 --- a/src/tools/search-codebase.ts +++ b/src/tools/search-codebase.ts @@ -183,7 +183,7 @@ export async function handle( text: JSON.stringify( { status: 'indexing', - message: 'Index is still being built. Retry in a moment.', + message: 'Index build in progress. Check indexing status, then retry.', progress: ctx.indexState.indexer?.getProgress() }, null, diff --git a/tests/multi-project-routing.test.ts b/tests/multi-project-routing.test.ts index fa8f181..41a81f1 100644 --- a/tests/multi-project-routing.test.ts +++ b/tests/multi-project-routing.test.ts @@ -367,6 +367,30 @@ describe('multi-project routing', () => { } }); + it('reports status-check guidance when an index build is already running', async () => { + const { server } = await import('../src/index.js'); + const { getOrCreateProject } = await import('../src/project-state.js'); + const handler = (server as unknown as TestServer)._requestHandlers.get('tools/call'); + if (!handler) throw new Error('tools/call handler not registered'); + + const project = getOrCreateProject(primaryRoot); + const originalStatus = project.indexState.status; + project.indexState.status = 'indexing'; + try { + const response = await callTool(handler, 20, 'search_codebase', { + query: 'feature', + project: primaryRoot + }); + const payload = parsePayload(response) as { status: string; message: string }; + + expect(payload.status).toBe('indexing'); + expect(payload.message).toBe('Index build in progress. Check indexing status, then retry.'); + expect(searchMocks.search).not.toHaveBeenCalled(); + } finally { + project.indexState.status = originalStatus; + } + }); + it('triggers a background rebuild for a corrupted explicit project without falling back to cwd', async () => { delete process.env.CODEBASE_ROOT; delete process.argv[2]; @@ -398,7 +422,7 @@ describe('multi-project routing', () => { }; expect(payload.status).toBe('indexing'); - expect(payload.message).toContain('retry shortly'); + expect(payload.message).toBe('Index rebuild in progress. Check indexing status, then retry.'); expect(payload.index?.action).toBe('rebuild-started'); expect(String(payload.index?.reason || '')).toContain('Index meta'); } finally { @@ -591,7 +615,9 @@ describe('multi-project routing', () => { it('lists bounded and full context resources for active and project-scoped flows', async () => { const { server } = await import('../src/index.js'); const toolHandler = (server as unknown as TestServer)._requestHandlers.get('tools/call'); - const resourcesHandler = (server as unknown as TestServer)._requestHandlers.get('resources/list'); + const resourcesHandler = (server as unknown as TestServer)._requestHandlers.get( + 'resources/list' + ); if (!toolHandler || !resourcesHandler) { throw new Error('required handlers not registered'); diff --git a/tests/search-codebase-auto-heal.test.ts b/tests/search-codebase-auto-heal.test.ts index d00d09e..2be3bb2 100644 --- a/tests/search-codebase-auto-heal.test.ts +++ b/tests/search-codebase-auto-heal.test.ts @@ -181,6 +181,34 @@ describe('search_codebase auto-heal', () => { 15000 ); + it('returns status-check guidance without searching while an index build is active', async () => { + if (!tempRoot) throw new Error('tempRoot not initialized'); + + const { handle } = await import('../src/tools/search-codebase.js'); + const baseDir = path.join(tempRoot, CODEBASE_CONTEXT_DIRNAME); + const response = await handle( + { query: 'search something' }, + { + indexState: { status: 'indexing' }, + paths: { + baseDir, + memory: path.join(baseDir, 'memory.json'), + intelligence: path.join(baseDir, 'intelligence.json'), + health: path.join(baseDir, 'health.json'), + keywordIndex: path.join(baseDir, KEYWORD_INDEX_FILENAME), + vectorDb: path.join(baseDir, VECTOR_DB_DIRNAME) + }, + rootPath: tempRoot, + performIndexing: vi.fn() + } + ); + + const payload = JSON.parse(response.content?.[0]?.text ?? '{}'); + expect(payload.status).toBe('indexing'); + expect(payload.message).toBe('Index build in progress. Check indexing status, then retry.'); + expect(searchMocks.search).not.toHaveBeenCalled(); + }); + it('returns invalid_params when search_codebase query is missing', async () => { const { server } = await import('../src/index.js'); const handler = (server as any)._requestHandlers.get('tools/call'); From 5d030b898dfb7f130f758b2cc71d402bb4f87cd6 Mon Sep 17 00:00:00 2001 From: PatrickSys Date: Fri, 16 Oct 2026 07:29:59 +0200 Subject: [PATCH 3/3] fix: require Node 22 and test supported LTS versions --- .github/workflows/test.yml | 7 +++++-- package.json | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8496e23..d518c79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: version: 10 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '24' cache: 'pnpm' - run: pnpm install --frozen-lockfile @@ -35,6 +35,9 @@ jobs: test: name: Functional Tests runs-on: ubuntu-latest + strategy: + matrix: + node: [22, 24] steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v2 @@ -42,7 +45,7 @@ jobs: version: 10 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: ${{ matrix.node }} cache: 'pnpm' - run: pnpm install --frozen-lockfile diff --git a/package.json b/package.json index 2b75047..f7ca7f5 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ ], "packageManager": "pnpm@10.27.0", "engines": { - "node": ">=18.0.0" + "node": ">=22.0.0" }, "keywords": [ "mcp",