chore: lint

This commit is contained in:
Richard Tang
2026-05-01 17:53:44 -07:00
parent 3a94f52009
commit 9a75d45351
16 changed files with 47 additions and 110 deletions
@@ -129,10 +129,7 @@ _TOOL_CATEGORIES: dict[str, list[str]] = {
# Research — paper search, Wikipedia, ad-hoc web scrape. Pair with
# browser_basic for richer site-by-site research; this category is the
# lightweight always-available fallback.
"research": [
"web_scrape",
"pdf_read"
],
"research": ["web_scrape", "pdf_read"],
# Security — defensive scanning and reconnaissance. Engineering-only
# surface; the rest of the queens shouldn't see port scanners.
"security": [
+2 -2
View File
@@ -52,11 +52,11 @@ _DEFAULT_LOCAL_SERVERS: dict[str, dict[str, Any]] = {
"args": ["run", "python", "files_server.py", "--stdio"],
},
"terminal-tools": {
"description": "Terminal capabilities: process exec, background jobs, PTY sessions, fs search. Bash-only on POSIX.",
"description": "Terminal capabilities",
"args": ["run", "python", "terminal_tools_server.py", "--stdio"],
},
"chart-tools": {
"description": "BI/financial chart + diagram rendering: ECharts, Mermaid. Returns spec + downloadable PNG; chat embeds live.",
"description": "BI/financial chart + diagram rendering: ECharts, Mermaid",
"args": ["run", "python", "chart_tools_server.py", "--stdio"],
},
}
+12 -42
View File
@@ -22,13 +22,11 @@ Usage:
from __future__ import annotations
import contextlib
import difflib
import fnmatch
import os
import re
import subprocess
import sys
import threading as _threading
from collections.abc import Callable
from dataclasses import dataclass, field
@@ -924,8 +922,7 @@ def _apply_hunk(content: str, hunk: _Hunk) -> tuple[str, str | None]:
count = content.count(hunk.context_hint)
if count > 1:
return content, (
f"addition-only hunk: context hint "
f"'{hunk.context_hint}' is ambiguous ({count} occurrences)"
f"addition-only hunk: context hint '{hunk.context_hint}' is ambiguous ({count} occurrences)"
)
if count == 1:
idx = content.find(hunk.context_hint)
@@ -1045,9 +1042,7 @@ def _apply_v4a(
for hunk_idx, hunk in enumerate(op.hunks):
new_content, herr = _apply_hunk(content, hunk)
if herr:
errors.append(
f"Op #{op_idx + 1} update {op.path} hunk #{hunk_idx + 1}: {herr}"
)
errors.append(f"Op #{op_idx + 1} update {op.path} hunk #{hunk_idx + 1}: {herr}")
break
content = new_content
fs_state[resolved] = content
@@ -1063,9 +1058,7 @@ def _apply_v4a(
errors.append(f"Op #{op_idx + 1} move {op.path}: {err}")
continue
if os.path.exists(dst_resolved) and fs_exists.get(dst_resolved, True):
errors.append(
f"Op #{op_idx + 1} move {op.path}: destination already exists"
)
errors.append(f"Op #{op_idx + 1} move {op.path}: destination already exists")
continue
fs_state[dst_resolved] = fs_state[resolved]
fs_exists[dst_resolved] = True
@@ -1121,8 +1114,7 @@ def _apply_v4a(
if apply_errors:
return None, (
"Apply phase failed (state may be inconsistent — run `git diff` to assess):\n "
+ "\n ".join(apply_errors)
"Apply phase failed (state may be inconsistent — run `git diff` to assess):\n " + "\n ".join(apply_errors)
)
summary_parts: list[str] = []
@@ -1177,10 +1169,7 @@ def _patch_replace(
f"harness can track its state before you edit it."
)
if _fresh.status is Freshness.STALE:
return (
f"Refusing to edit '{path}': {_fresh.detail}. Re-read the file with "
f"read_file before editing."
)
return f"Refusing to edit '{path}': {_fresh.detail}. Re-read the file with read_file before editing."
try:
with open(resolved, encoding="utf-8") as f:
@@ -1217,9 +1206,7 @@ def _patch_replace(
break
if matched is None:
close = difflib.get_close_matches(
old_string[:200], content.split("\n"), n=3, cutoff=0.4
)
close = difflib.get_close_matches(old_string[:200], content.split("\n"), n=3, cutoff=0.4)
msg = (
f"Error: Could not find a unique match for old_string in {path}. "
f"Use read_file to verify the current content, or search_files "
@@ -1352,14 +1339,8 @@ EDIT_FILE_PARAMS = {
"tabs vs spaces, smart quotes vs ASCII, and literal \\n/\\t/\\r "
"vs real control chars."
),
"new_string": (
"Replace mode only. Replacement text. Pass an empty string to "
"delete the matched text."
),
"replace_all": (
"Replace mode only. Replace every occurrence instead of requiring "
"a unique match. Default False."
),
"new_string": ("Replace mode only. Replacement text. Pass an empty string to delete the matched text."),
"replace_all": ("Replace mode only. Replace every occurrence instead of requiring a unique match. Default False."),
"patch_text": (
"Patch mode only. Structured patch body. File paths are embedded "
"inside the body via '*** Update File: <path>' / "
@@ -1396,18 +1377,14 @@ SEARCH_FILES_DOC = (
)
SEARCH_FILES_PARAMS = {
"pattern": (
"Regex (content mode) or glob (files mode, e.g. '*.py'). For an "
"'ls'-style listing pass '*' or '*.<ext>'."
"Regex (content mode) or glob (files mode, e.g. '*.py'). For an 'ls'-style listing pass '*' or '*.<ext>'."
),
"target": (
"'content' to grep inside files, 'files' to list/find files. "
"Legacy aliases: 'grep' -> 'content', 'find'/'ls' -> 'files'. "
"Default 'content'."
),
"path": (
"Directory (or, in content mode, a single file) to search. "
"Default '.'."
),
"path": ("Directory (or, in content mode, a single file) to search. Default '.'."),
"file_glob": (
"Restrict content search to filenames matching this glob. "
"Ignored in files mode (use the 'pattern' argument instead)."
@@ -1419,14 +1396,8 @@ SEARCH_FILES_PARAMS = {
"default), 'files_only' (paths only), 'count' (per-file match "
"counts)."
),
"context": (
"Lines of context before and after each match (content mode "
"only). Default 0."
),
"hashline": (
"Content mode: include N:hhhh hash anchors in matched lines. "
"Default False."
),
"context": ("Lines of context before and after each match (content mode only). Default 0."),
"hashline": ("Content mode: include N:hhhh hash anchors in matched lines. Default False."),
"task_id": (
"Optional anti-loop scope key. Defaults to a shared bucket; pass "
"a per-task id when multiple agents share a process."
@@ -1719,4 +1690,3 @@ def register_file_tools(
"Results have not changed — use what you have instead of re-searching.]"
)
return result
@@ -137,10 +137,7 @@ def register_tools(mcp: FastMCP) -> None:
"error": f"Blocked by robots.txt: {url}",
"url": url,
"skipped": True,
"hint": (
"Pass respect_robots_txt=False if you have "
"authorization to scrape this site."
),
"hint": ("Pass respect_robots_txt=False if you have authorization to scrape this site."),
}
except Exception:
pass # If robots.txt can't be fetched, proceed anyway
@@ -343,8 +340,19 @@ def register_tools(mcp: FastMCP) -> None:
for br in content_elem.find_all("br"):
br.replace_with(NavigableString("\n"))
block_tags = (
"p", "h1", "h2", "h3", "h4", "h5", "h6",
"li", "tr", "div", "section", "article", "blockquote",
"p",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"li",
"tr",
"div",
"section",
"article",
"blockquote",
)
for block in content_elem.find_all(block_tags):
block.insert_before(NavigableString("\n"))
+1 -5
View File
@@ -29,11 +29,7 @@ def register_chart_tools(mcp: FastMCP) -> list[str]:
register_tools(mcp)
return [
name
for name in mcp._tool_manager._tools.keys()
if name.startswith("chart_")
]
return [name for name in mcp._tool_manager._tools.keys() if name.startswith("chart_")]
__all__ = ["register_chart_tools"]
+1 -3
View File
@@ -247,9 +247,7 @@ async def _render_in_page(
# expected to have already coerced JSON-string specs into dicts
# in chart_tools/tools.py — this is a defense-in-depth check.
if isinstance(spec, str):
raise RendererError(
"spec arrived as a string; it should have been parsed to a dict in chart_render"
)
raise RendererError("spec arrived as a string; it should have been parsed to a dict in chart_render")
try:
json.dumps(spec)
except (TypeError, ValueError) as exc:
+1 -3
View File
@@ -174,9 +174,7 @@ def register_tools(mcp: FastMCP) -> None:
# browser-side flakes. We retry once for the latter; if
# the second attempt fails too, surface the error so the
# agent can fix it.
logger.warning(
"chart_render attempt %d/%d failed: %s", attempt + 1, 2, exc
)
logger.warning("chart_render attempt %d/%d failed: %s", attempt + 1, 2, exc)
if attempt == 0:
await asyncio.sleep(0.15)
continue
@@ -71,17 +71,10 @@ def build_exec_envelope(
# the foundational skill documents). For simplicity we always
# store both when either overflows so the agent can fetch the
# other stream in full too if it wants.
combined = (
b"--- stdout ---\n"
+ stdout_bytes
+ b"\n--- stderr ---\n"
+ stderr_bytes
)
combined = b"--- stdout ---\n" + stdout_bytes + b"\n--- stderr ---\n" + stderr_bytes
output_handle = store.put(combined)
semantic_status, semantic_message = classify(
command, exit_code, timed_out=timed_out, signaled=signaled
)
semantic_status, semantic_message = classify(command, exit_code, timed_out=timed_out, signaled=signaled)
warning = get_warning(command)
+3 -4
View File
@@ -53,9 +53,7 @@ if TYPE_CHECKING:
# directly — the alternative is spawning the first program with the rest
# of the line as junk argv, which either errors or returns fake success
# (e.g. `echo "..." && ps ...` → echo prints the literal command).
_SHELL_METACHARS: frozenset[str] = frozenset(
{"|", "&&", "||", ";", ">", "<", ">>", "<<", "&", "2>", "2>&1", "|&"}
)
_SHELL_METACHARS: frozenset[str] = frozenset({"|", "&&", "||", ";", ">", "<", ">>", "<<", "&", "2>", "2>&1", "|&"})
def register_exec_tools(mcp: FastMCP) -> None:
@@ -126,7 +124,8 @@ def register_exec_tools(mcp: FastMCP) -> None:
return _err_envelope(command, "command was empty")
if any(t in _SHELL_METACHARS for t in tokens) or any(
# globs that shlex left unexpanded (`*`, `?`, `[`)
any(c in t for c in "*?[") and t != "[" for t in tokens
any(c in t for c in "*?[") and t != "["
for t in tokens
):
auto_shell = True
@@ -20,7 +20,6 @@ from gcu.browser.bridge import BeelineBridge
from gcu.browser.tools.advanced import register_advanced_tools
from gcu.browser.tools.inspection import register_inspection_tools
from gcu.browser.tools.interactions import register_interaction_tools
from gcu.browser.tools.lifecycle import register_lifecycle_tools
from gcu.browser.tools.navigation import register_navigation_tools
from gcu.browser.tools.tabs import register_tab_tools
+1 -3
View File
@@ -20,9 +20,7 @@ def test_register_chart_tools_lands_all(mcp):
from chart_tools import register_chart_tools
names = register_chart_tools(mcp)
assert set(names) == EXPECTED_TOOLS, (
f"missing: {EXPECTED_TOOLS - set(names)}, extra: {set(names) - EXPECTED_TOOLS}"
)
assert set(names) == EXPECTED_TOOLS, f"missing: {EXPECTED_TOOLS - set(names)}, extra: {set(names) - EXPECTED_TOOLS}"
def test_all_tools_have_chart_prefix(mcp):
+1 -3
View File
@@ -63,9 +63,7 @@ def test_merge_stderr(job_tools):
merge_stderr=True,
)
job_id = started["job_id"]
result = job_tools["logs"](
job_id=job_id, stream="merged", wait_until_exit=True, wait_timeout_sec=5
)
result = job_tools["logs"](job_id=job_id, stream="merged", wait_until_exit=True, wait_timeout_sec=5)
assert "stdout1" in result["data"]
assert "stderr1" in result["data"]
+1 -3
View File
@@ -20,9 +20,7 @@ def test_register_terminal_tools_lands_all_ten(mcp):
from terminal_tools import register_terminal_tools
names = register_terminal_tools(mcp)
assert set(names) == EXPECTED_TOOLS, (
f"missing: {EXPECTED_TOOLS - set(names)}, extra: {set(names) - EXPECTED_TOOLS}"
)
assert set(names) == EXPECTED_TOOLS, f"missing: {EXPECTED_TOOLS - set(names)}, extra: {set(names) - EXPECTED_TOOLS}"
def test_all_tools_have_terminal_prefix(mcp):
+3 -16
View File
@@ -280,7 +280,7 @@ class TestPatchToolReplaceMode:
result = edit_fn(
mode="replace",
path="b.py",
old_string='print(“hi”)',
old_string="print(“hi”)",
new_string='print("HELLO")',
)
assert "Error" not in result
@@ -331,14 +331,7 @@ class TestPatchToolPatchMode:
"""A V4A Update hunk replaces matched lines and writes."""
target = tmp_path / "u.py"
target.write_text("def f():\n return 1\n", encoding="utf-8")
body = (
"*** Begin Patch\n"
"*** Update File: u.py\n"
" def f():\n"
"- return 1\n"
"+ return 42\n"
"*** End Patch\n"
)
body = "*** Begin Patch\n*** Update File: u.py\n def f():\n- return 1\n+ return 42\n*** End Patch\n"
edit_fn = _get_tool_fn(file_ops_mcp, "edit_file")
result = edit_fn(mode="patch", patch_text=body)
assert "Error" not in result
@@ -347,13 +340,7 @@ class TestPatchToolPatchMode:
def test_patch_add_file(self, file_ops_mcp, tmp_path):
"""Add File: creates a new file from + lines."""
body = (
"*** Begin Patch\n"
"*** Add File: new.py\n"
"+# new\n"
"+x = 1\n"
"*** End Patch\n"
)
body = "*** Begin Patch\n*** Add File: new.py\n+# new\n+x = 1\n*** End Patch\n"
edit_fn = _get_tool_fn(file_ops_mcp, "edit_file")
result = edit_fn(mode="patch", patch_text=body)
assert "Error" not in result
+1 -3
View File
@@ -466,9 +466,7 @@ class TestWebScrapeToolAIFriendlyOutput:
result = await web_scrape_fn(url="https://example.com")
assert "structured_data" in result
assert result["structured_data"]["json_ld"] == [
{"@type": "Article", "headline": "Hello"}
]
assert result["structured_data"]["json_ld"] == [{"@type": "Article", "headline": "Hello"}]
@pytest.mark.asyncio
@patch(_STEALTH_PATH)