refactor(explore): make ast_grep_search discipline the core of Tool Strategy

The previous Tool Strategy was a neutral 5-bullet list that treated
ast_grep_search and grep as equals. LLMs read 'structural patterns
(function shapes, class structures)' and reach for ast_grep_search
first, then call it with regex ('foo|bar', '.*', '\\w') and silently
get zero results.

Rewrite so the default is clear - grep first, ast_grep_search only for
true AST shape matching - and enumerate the regex anti-patterns with
their corrective switches. Add an explicit rule: if ast_grep_search
returns zero matches and the printed hint says the pattern is regex-
shaped, switch to grep instead of retrying with another regex variant.

Preserves the existing absolute-path requirement, <results> block
format, and read-only / no-emoji constraints.
This commit is contained in:
YeonGyu-Kim
2026-04-22 12:27:41 +09:00
parent 2f4b1c3158
commit 5ef2a3fa7c
2 changed files with 114 additions and 6 deletions
+92
View File
@@ -0,0 +1,92 @@
/// <reference types="bun-types" />
import { describe, expect, it } from "bun:test"
import { createExploreAgent } from "./explore"
describe("explore agent tool strategy", () => {
const model = "openai/gpt-5.4-mini-fast"
it("#given the prompt #when inspecting #then defaults to grep for most searches", () => {
// given
const agent = createExploreAgent(model)
// when
const prompt = agent.prompt ?? ""
// then
expect(prompt.toLowerCase()).toContain("default to `grep`")
})
it("#given the prompt #when inspecting #then warns against regex in ast_grep_search", () => {
// given
const agent = createExploreAgent(model)
// when
const prompt = agent.prompt ?? ""
// then
expect(prompt).toContain("ast_grep_search")
expect(prompt.toLowerCase()).toContain("not use regex")
expect(prompt).toContain("|")
expect(prompt).toContain(".*")
expect(prompt).toContain("\\w")
})
it("#given the prompt #when inspecting #then mandates falling back to grep on regex-shaped patterns", () => {
// given
const agent = createExploreAgent(model)
// when
const prompt = agent.prompt ?? ""
// then
expect(prompt.toLowerCase()).toContain("switch to grep")
})
it("#given the prompt #when inspecting #then gives concrete AST pattern examples", () => {
// given
const agent = createExploreAgent(model)
// when
const prompt = agent.prompt ?? ""
// then
expect(prompt).toContain("$$$")
expect(prompt).toContain("function $NAME")
})
it("#given the prompt #when inspecting #then tells LLM to read the returned hint before retrying", () => {
// given
const agent = createExploreAgent(model)
// when
const prompt = agent.prompt ?? ""
// then
expect(prompt.toLowerCase()).toContain("read the hint")
})
it("#given the prompt #when inspecting #then preserves the absolute-path requirement", () => {
// given
const agent = createExploreAgent(model)
// when
const prompt = agent.prompt ?? ""
// then
expect(prompt).toContain("absolute")
expect(prompt).toContain("<results>")
})
it("#given the prompt #when inspecting #then keeps the read-only and no-emoji constraints", () => {
// given
const agent = createExploreAgent(model)
// when
const prompt = agent.prompt ?? ""
// then
expect(prompt).toContain("Read-only")
expect(prompt).toContain("No emojis")
})
})