feat(tools): glob + grep read-only workspace search tools

Weak local models reached for shell find/grep -r (unjailed, dumps
.gitignore'd trees, floods context) to answer 'does frontend/ exist?' /
'where is symbol X?'. Add two jailed, gitignore-aware read-only tools:

- GlobTool (name 'glob', T1, FILE_READ+DIRECTORY_LIST): find files by
  glob pattern; survey-exempt from anti-hallucination read gates like
  list_dir.
- GrepTool (name 'grep', T1, FILE_READ): regex content search, returns
  path:line: text; skips gitignored, binary (NUL), and >2MB files.

Both reuse GitignoreMatcher + PathJail from the filesystem package,
share FileReadTool's jail/anchor/toggle, cap at 200 results. Registered
in ToolConfig.buildTools (fileRead block) and added to
StageConfig.ALWAYS_AVAILABLE_READ_TOOLS so every tool-granting stage can
call them. 6 tests green.

Vikunja #37.
This commit is contained in:
2026-07-12 18:33:36 +04:00
parent d89d4e32a9
commit bfd925b518
4 changed files with 347 additions and 1 deletions
@@ -5,6 +5,8 @@ import com.correx.infrastructure.tools.filesystem.FileDeleteTool
import com.correx.infrastructure.tools.filesystem.FileEditTool
import com.correx.infrastructure.tools.filesystem.FileReadTool
import com.correx.infrastructure.tools.filesystem.FileWriteTool
import com.correx.infrastructure.tools.filesystem.GlobTool
import com.correx.infrastructure.tools.filesystem.GrepTool
import com.correx.infrastructure.tools.filesystem.ListDirTool
import com.correx.infrastructure.tools.shell.ShellTool
import com.correx.infrastructure.tools.web.WebFetchTool
@@ -82,6 +84,10 @@ fun ToolConfig.buildTools(): List<Tool> = buildList {
workingDir = fileRead.workingDir,
),
)
// glob (find by pattern) + grep (search contents) — read-only search, same jail/anchor/toggle;
// the affordance weak models need instead of shell find/grep -r.
add(GlobTool(allowedPaths = fileRead.allowedPaths, workingDir = fileRead.workingDir))
add(GrepTool(allowedPaths = fileRead.allowedPaths, workingDir = fileRead.workingDir))
}
if (fileWrite.enabled) {
add(