From 2eef88978b222ac4fb672c5df302cc4058f48e7c Mon Sep 17 00:00:00 2001 From: can1357 Date: Sun, 21 Jun 2026 07:02:32 +0200 Subject: [PATCH] feat(coding-agent): made write and find tools essential - Promoted `write` and `find` tools to `essential` status to ensure they are always available regardless of discovery mode. - Updated `DEFAULT_ESSENTIAL_TOOL_NAMES` to include these tools by default. - Updated documentation and tests to reflect the change in default essential tool availability. Fixes #3165 --- docs/tools/search_tool_bm25.md | 2 +- packages/coding-agent/CHANGELOG.md | 4 ++++ packages/coding-agent/src/tools/find.ts | 3 +-- packages/coding-agent/src/tools/index.ts | 2 +- packages/coding-agent/src/tools/write.ts | 3 +-- .../test/tool-discovery/initial-tools.test.ts | 16 ++++++++-------- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/tools/search_tool_bm25.md b/docs/tools/search_tool_bm25.md index 942280a33..b7c741425 100644 --- a/docs/tools/search_tool_bm25.md +++ b/docs/tools/search_tool_bm25.md @@ -113,6 +113,6 @@ - Built-in entries appear only in `"all"` mode and only for registry tools whose `loadMode === "discoverable"` and are not currently active. - Hidden/internal built-ins are intentionally excluded from the built-in corpus: `resolve`, `yield`, `report_finding`, `report_tool_issue` are called out in the `#collectDiscoverableBuiltinTools()` comment. - `DiscoverableToolSource` includes `"extension"` and `"custom"`, but `AgentSession.getDiscoverableTools()` currently assembles only built-in and MCP sources. -- On startup, `packages/coding-agent/src/sdk.ts` resolves `"auto"` after the full registry exists and injects `search_tool_bm25` when the count exceeds 40. It hides non-essential discoverable built-ins only in `tools.discoveryMode = "all"`; defaults are `read`, `bash`, and `edit` unless `tools.essentialOverride` changes them. +- On startup, `packages/coding-agent/src/sdk.ts` resolves `"auto"` after the full registry exists and injects `search_tool_bm25` when the count exceeds 40. It hides non-essential discoverable built-ins only in `tools.discoveryMode = "all"`. Tools whose class is marked as `loadMode === "essential"` (defaults are `read`, `bash`, `edit`, `write`, and `find`) are always active; they survive hiding regardless of configuration. `tools.essentialOverride` can be used to treat additional discoverable tools as essential (active on startup) or to explicitly specify the active essential list. - Query tokenization is simple and deterministic: Unicode is NFKD-normalized, combining marks are dropped, acronym/camelCase and digit-to-capital boundaries are split, non-letter/non-number characters become spaces, tokens are lowercased, and only non-empty tokens survive. - Scores are rounded differently by surface: `details.tools[].score` keeps 6 decimals; the TUI line renders 3. diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index a8d38bd0d..e1dcec526 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -6,6 +6,10 @@ - Added `tab.ariaSnapshot(selector?)` to the browser tool for Playwright-format ARIA-tree YAML - Added `tab.ref("e5")` and support for `aria-ref=e5` selectors in all `tab` action methods +### Changed + +- Made `write` and `find` tools essential so they are always available initially (surviving `tools.discoveryMode === "all"` hiding) to ensure instructions to write/find files are immediately executable ([#3165](https://github.com/can1357/oh-my-pi/issues/3165)) + ### Fixed - Fixed streamed tool-call previews freezing on their placeholder body (`$ …`, `Write: …`, an empty args tree) even after the tool finished: the pending card was created while arguments streamed, but when the closing full-arguments `message_update` never arrived (smooth-streaming disabled leaving the throttled arguments stale, an owned-dialect projector, or a superseded/aborted turn that still ran the call) nothing re-applied the final args. `tool_execution_start` — the one event every execution path emits with validated full arguments right before the result — now reconciles them onto the existing pending card and cancels any in-flight reveal so a late tick can't re-truncate the body. diff --git a/packages/coding-agent/src/tools/find.ts b/packages/coding-agent/src/tools/find.ts index 1bb9b7767..0e83dbcf7 100644 --- a/packages/coding-agent/src/tools/find.ts +++ b/packages/coding-agent/src/tools/find.ts @@ -101,8 +101,7 @@ interface FindTarget { export class FindTool implements AgentTool { readonly name = "find"; readonly approval = "read" as const; - readonly summary = "Find files and directories matching a glob pattern"; - readonly loadMode = "discoverable"; + readonly loadMode = "essential"; readonly label = "Find"; readonly description: string; readonly parameters = findSchema; diff --git a/packages/coding-agent/src/tools/index.ts b/packages/coding-agent/src/tools/index.ts index 1ba174606..35c7f83c0 100644 --- a/packages/coding-agent/src/tools/index.ts +++ b/packages/coding-agent/src/tools/index.ts @@ -375,7 +375,7 @@ export type ToolFactory = (session: ToolSession) => Tool | null | Promise { read: "essential", edit: "essential", todo: "discoverable", - find: "discoverable", + search: "discoverable", }; const base = { loadModeOf: (name: string): BuiltinToolLoadMode | undefined => loadModes[name], - essentialNames: new Set(["read", "bash", "edit"]), + essentialNames: new Set(["read", "bash", "edit", "write", "find"]), explicitlyRequested: new Set(), restored: new Set(), forceActive: new Set(), }; it("hides non-essential discoverable built-ins", () => { - expect(filterInitialToolsForDiscoveryAll(["read", "edit", "todo", "find"], base)).toEqual(["read", "edit"]); + expect(filterInitialToolsForDiscoveryAll(["read", "edit", "todo", "search"], base)).toEqual(["read", "edit"]); }); it("keeps discoverable tools required by a forced tool_choice (eager todo)", () => { - const result = filterInitialToolsForDiscoveryAll(["read", "todo", "find"], { + const result = filterInitialToolsForDiscoveryAll(["read", "todo", "search"], { ...base, forceActive: new Set(["todo"]), }); @@ -151,15 +151,15 @@ describe("filterInitialToolsForDiscoveryAll", () => { }); it("keeps explicitly requested and restored discoverable tools", () => { - const result = filterInitialToolsForDiscoveryAll(["todo", "find"], { + const result = filterInitialToolsForDiscoveryAll(["todo", "search"], { ...base, - explicitlyRequested: new Set(["find"]), + explicitlyRequested: new Set(["search"]), restored: new Set(["todo"]), }); - expect([...result].sort()).toEqual(["find", "todo"]); + expect([...result].sort()).toEqual(["search", "todo"]); }); it("never hides tools without a built-in loadMode (MCP/custom/extension)", () => { - expect(filterInitialToolsForDiscoveryAll(["mcp__server__tool", "find"], base)).toEqual(["mcp__server__tool"]); + expect(filterInitialToolsForDiscoveryAll(["mcp__server__tool", "search"], base)).toEqual(["mcp__server__tool"]); }); });