From 7f51fbd8787b192aa96fc7e522e5f4585a783aa5 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 20 Sep 2026 22:59:01 -0500 Subject: [PATCH] feat(codemode): rank whole-word path matches above substring matches in search (#50275) --- packages/codemode/src/tool-runtime.ts | 4 ++++ packages/codemode/test/codemode.test.ts | 26 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/packages/codemode/src/tool-runtime.ts b/packages/codemode/src/tool-runtime.ts index a2e27cb304b..e30cb1108e7 100644 --- a/packages/codemode/src/tool-runtime.ts +++ b/packages/codemode/src/tool-runtime.ts @@ -192,6 +192,8 @@ export type Prepared = { export type SearchEntry = { readonly description: ToolDescription + /** The path split into words, so `zones` matches `get_zones` as a word rather than as a substring of `timezones`. */ + readonly pathWords: ReadonlyArray readonly searchText: string } @@ -247,6 +249,7 @@ const makeSearchTool = (searchIndex: ReadonlyArray): Tool => ({ (total, forms) => total + (forms.some((form) => path === form || path.endsWith(`.${form}`)) ? 20 : 0) + + (forms.some((form) => entry.pathWords.includes(form)) ? 12 : 0) + (forms.some((form) => path.includes(form)) ? 8 : 0) + (forms.some((form) => description.includes(form)) ? 4 : 0) + (forms.some((form) => entry.searchText.includes(form)) ? 2 : 0), @@ -281,6 +284,7 @@ export const searchSignature = (() => { const toSearchEntry = (visible: VisibleTool): SearchEntry => ({ description: describeTool(visible), + pathWords: tokenize(visible.path), searchText: [ visible.path, visible.tool.description, diff --git a/packages/codemode/test/codemode.test.ts b/packages/codemode/test/codemode.test.ts index dfdb7ca6797..1336a775bd3 100644 --- a/packages/codemode/test/codemode.test.ts +++ b/packages/codemode/test/codemode.test.ts @@ -1039,6 +1039,32 @@ describe("CodeMode public contract", () => { } }) + test("a query term that is a whole word of the path outranks a substring of it", async () => { + const simple = (description: string) => + Tool.make({ + description, + input: Schema.Struct({}), + output: Schema.String, + execute: () => Effect.succeed("ok"), + }) + const runtime = CodeMode.make({ + tools: { + // Declared so that alphabetical order would put the substring match first. + cloudflare: { get_timezones: simple("List timezones"), get_zones: simple("List zones") }, + }, + }) + + const ranked = await Effect.runPromise(runtime.execute(`return search({ query: "zones" })`)) + expect(ranked.ok).toBe(true) + if (ranked.ok) { + const value = ranked.value as { items: Array<{ path: string }> } + expect(value.items.map((item) => item.path)).toStrictEqual([ + "tools.cloudflare.get_zones", + "tools.cloudflare.get_timezones", + ]) + } + }) + test("a plural query term matches singular-only tool text", async () => { const simple = (description: string) => Tool.make({