feat(tools): improve coding workflow recovery

This commit is contained in:
Xubin Ren
2026-05-21 00:58:05 +08:00
parent 3e154bb5cf
commit 480ca28a2d
9 changed files with 571 additions and 32 deletions
+65 -1
View File
@@ -12,7 +12,7 @@ import pytest
from nanobot.agent.loop import AgentLoop
from nanobot.agent.subagent import SubagentManager, SubagentStatus
from nanobot.agent.tools.search import GrepTool
from nanobot.agent.tools.search import FindFilesTool, GrepTool
from nanobot.agent.tools.web import WebSearchTool
from nanobot.bus.queue import MessageBus
from nanobot.config.schema import WebSearchConfig
@@ -33,6 +33,68 @@ async def test_web_search_tool_refreshes_dynamic_config_loader(monkeypatch) -> N
assert await tool.execute("nanobot") == "duckduckgo:nanobot:3"
@pytest.mark.asyncio
async def test_find_files_filters_by_query_glob_and_type(tmp_path: Path) -> None:
(tmp_path / "src").mkdir()
(tmp_path / "src" / "settings_view.tsx").write_text("export {}\n", encoding="utf-8")
(tmp_path / "src" / "settings_api.py").write_text("pass\n", encoding="utf-8")
(tmp_path / "README.md").write_text("settings\n", encoding="utf-8")
tool = FindFilesTool(workspace=tmp_path, allowed_dir=tmp_path)
result = await tool.execute(
path=".",
query="settings",
glob="src/**",
type="ts",
)
assert result.splitlines() == ["src/settings_view.tsx"]
@pytest.mark.asyncio
async def test_find_files_can_include_directories(tmp_path: Path) -> None:
(tmp_path / "src" / "settings").mkdir(parents=True)
(tmp_path / "src" / "settings" / "index.ts").write_text("export {}\n", encoding="utf-8")
tool = FindFilesTool(workspace=tmp_path, allowed_dir=tmp_path)
result = await tool.execute(path="src", query="settings", include_dirs=True)
assert "src/settings/" in result.splitlines()
assert "src/settings/index.ts" in result.splitlines()
@pytest.mark.asyncio
async def test_find_files_supports_modified_sort_and_pagination(tmp_path: Path) -> None:
(tmp_path / "src").mkdir()
for idx, name in enumerate(("a.py", "b.py", "c.py"), start=1):
file_path = tmp_path / "src" / name
file_path.write_text("pass\n", encoding="utf-8")
os.utime(file_path, (idx, idx))
tool = FindFilesTool(workspace=tmp_path, allowed_dir=tmp_path)
result = await tool.execute(
path="src",
type="py",
sort="modified",
head_limit=1,
offset=1,
)
assert result.splitlines()[0] == "src/b.py"
assert "pagination: limit=1, offset=1" in result
@pytest.mark.asyncio
async def test_find_files_rejects_paths_outside_workspace(tmp_path: Path) -> None:
outside = tmp_path.parent / "outside-find-files.txt"
outside.write_text("secret\n", encoding="utf-8")
tool = FindFilesTool(workspace=tmp_path, allowed_dir=tmp_path)
result = await tool.execute(path=str(outside))
assert result.startswith("Error:")
@pytest.mark.asyncio
async def test_grep_respects_glob_filter_and_context(tmp_path: Path) -> None:
(tmp_path / "src").mkdir()
@@ -249,6 +311,7 @@ def test_agent_loop_registers_grep(tmp_path: Path) -> None:
loop = AgentLoop(bus=bus, provider=provider, workspace=tmp_path, model="test-model")
assert "find_files" in loop.tools.tool_names
assert "grep" in loop.tools.tool_names
@@ -280,6 +343,7 @@ async def test_subagent_registers_grep(tmp_path: Path) -> None:
status = SubagentStatus(task_id="sub-1", label="label", task_description="search task", started_at=time.monotonic())
await mgr._run_subagent("sub-1", "search task", "label", {"channel": "cli", "chat_id": "direct"}, status)
assert "find_files" in captured["tool_names"]
assert "grep" in captured["tool_names"]