super-code-assistant 3.3.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- commands/__init__.py +859 -0
- core/__init__.py +0 -0
- core/config.py +263 -0
- core/config_template.json +7 -0
- core/context.py +271 -0
- core/engine.py +635 -0
- core/file_state.py +279 -0
- core/llm.py +309 -0
- core/model_capabilities.py +45 -0
- core/permissions.py +204 -0
- core/sandbox/__init__.py +15 -0
- core/sandbox/blacklist.py +176 -0
- core/sandbox/config.py +38 -0
- core/sandbox/network.py +136 -0
- core/sandbox/path_protection.py +126 -0
- core/session.py +295 -0
- core/tool.py +45 -0
- features/__init__.py +0 -0
- features/compact.py +945 -0
- features/coordinator.py +105 -0
- features/cost_tracker.py +184 -0
- features/extract_memories.py +326 -0
- features/find_relevant_memories.py +376 -0
- features/git_ai.py +256 -0
- features/memory.py +531 -0
- features/memory_age.py +66 -0
- features/memory_scan.py +153 -0
- features/memory_types.py +34 -0
- features/plan.py +327 -0
- features/skills.py +300 -0
- features/worker_manager.py +232 -0
- mcp/__init__.py +0 -0
- mcp/client.py +112 -0
- mcp/loader.py +80 -0
- mcp/tool_proxy.py +59 -0
- super_code_assistant-3.3.6.dist-info/METADATA +45 -0
- super_code_assistant-3.3.6.dist-info/RECORD +61 -0
- super_code_assistant-3.3.6.dist-info/WHEEL +5 -0
- super_code_assistant-3.3.6.dist-info/entry_points.txt +2 -0
- super_code_assistant-3.3.6.dist-info/top_level.txt +7 -0
- tools/__init__.py +21 -0
- tools/agent.py +132 -0
- tools/ask_user.py +111 -0
- tools/bash.py +77 -0
- tools/file_edit.py +269 -0
- tools/file_read.py +206 -0
- tools/file_write.py +78 -0
- tools/glob_tool.py +81 -0
- tools/grep_tool.py +134 -0
- tools/plan_tools.py +75 -0
- tools/skill.py +108 -0
- tools/tool.py +44 -0
- tools/web_fetch.py +129 -0
- tools/web_search.py +220 -0
- tui/__init__.py +0 -0
- tui/app.py +726 -0
- tui/clipboard_image.py +42 -0
- tui/keylistener.py +140 -0
- tui/prompt.py +752 -0
- tui/query.py +200 -0
- tui/rendering.py +135 -0
tools/web_search.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""WebSearch tool: search the web using Bing and return structured results.
|
|
2
|
+
|
|
3
|
+
Uses Bing.com search — zero dependencies, pure stdlib. Works in China.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import html
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import urllib.request
|
|
11
|
+
import urllib.error
|
|
12
|
+
import urllib.parse
|
|
13
|
+
from html.parser import HTMLParser
|
|
14
|
+
|
|
15
|
+
from core.tool import Tool, ToolResult
|
|
16
|
+
|
|
17
|
+
_MAX_RESULTS = 10
|
|
18
|
+
_DEFAULT_RESULTS = 5
|
|
19
|
+
_MAX_CHARS = 3_000
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _BingParser(HTMLParser):
|
|
23
|
+
"""Parse Bing search result page HTML.
|
|
24
|
+
|
|
25
|
+
Bing results are in <li class="b_algo"> blocks:
|
|
26
|
+
<h2><a href="URL">title</a></h2>
|
|
27
|
+
<div class="b_caption"><p>snippet</p></div>
|
|
28
|
+
|
|
29
|
+
Uses depth counters to handle nested elements inside b_caption
|
|
30
|
+
(e.g. <div class="b_caption"><div class="f">...</div></div>).
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
super().__init__()
|
|
35
|
+
self.results: list[dict[str, str]] = []
|
|
36
|
+
self._in_algo = False
|
|
37
|
+
self._in_title = False # inside <h2><a>
|
|
38
|
+
self._in_caption = False # inside <div class="b_caption">
|
|
39
|
+
self._caption_depth = 0 # 嵌套 div 深度计数
|
|
40
|
+
self._text: list[str] = []
|
|
41
|
+
self._current_href = ""
|
|
42
|
+
self._current_title = ""
|
|
43
|
+
self._current_snippet = ""
|
|
44
|
+
|
|
45
|
+
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
|
46
|
+
d = dict(attrs)
|
|
47
|
+
cls = d.get("class", "")
|
|
48
|
+
|
|
49
|
+
if tag == "li" and "b_algo" in cls.split():
|
|
50
|
+
self._in_algo = True
|
|
51
|
+
self._in_title = False
|
|
52
|
+
self._in_caption = False
|
|
53
|
+
self._caption_depth = 0
|
|
54
|
+
self._current_href = ""
|
|
55
|
+
self._current_title = ""
|
|
56
|
+
self._current_snippet = ""
|
|
57
|
+
|
|
58
|
+
elif self._in_algo and not self._current_href:
|
|
59
|
+
# 第一个 <h2><a href="..."> 就是结果标题链接
|
|
60
|
+
if tag == "h2":
|
|
61
|
+
self._in_title = True
|
|
62
|
+
elif self._in_title and tag == "a":
|
|
63
|
+
self._current_href = d.get("href", "")
|
|
64
|
+
self._text = []
|
|
65
|
+
|
|
66
|
+
elif self._in_algo:
|
|
67
|
+
if tag == "div" and "b_caption" in cls.split():
|
|
68
|
+
self._in_caption = True
|
|
69
|
+
self._caption_depth = 1
|
|
70
|
+
elif self._in_caption and tag == "div":
|
|
71
|
+
self._caption_depth += 1
|
|
72
|
+
|
|
73
|
+
def handle_endtag(self, tag: str) -> None:
|
|
74
|
+
if self._in_title and tag == "a":
|
|
75
|
+
self._current_title = "".join(self._text).strip()
|
|
76
|
+
elif self._in_title and tag == "h2":
|
|
77
|
+
self._in_title = False
|
|
78
|
+
elif self._in_caption and tag == "div":
|
|
79
|
+
self._caption_depth -= 1
|
|
80
|
+
if self._caption_depth > 0:
|
|
81
|
+
return # 内层 </div>,跳过
|
|
82
|
+
self._in_caption = False
|
|
83
|
+
# 外层 b_caption 的 </div>,收尾
|
|
84
|
+
if self._current_href:
|
|
85
|
+
self.results.append({
|
|
86
|
+
"title": self._current_title,
|
|
87
|
+
"href": self._current_href,
|
|
88
|
+
"snippet": self._current_snippet,
|
|
89
|
+
})
|
|
90
|
+
self._in_algo = False
|
|
91
|
+
elif self._in_algo and tag == "li":
|
|
92
|
+
# 有些结果可能没有 b_caption(如视频/图片结果),在 </li> 时兜底
|
|
93
|
+
if self._current_href and not self._current_snippet:
|
|
94
|
+
self.results.append({
|
|
95
|
+
"title": self._current_title,
|
|
96
|
+
"href": self._current_href,
|
|
97
|
+
"snippet": self._current_snippet,
|
|
98
|
+
})
|
|
99
|
+
self._in_algo = False
|
|
100
|
+
|
|
101
|
+
def handle_data(self, data: str) -> None:
|
|
102
|
+
if self._in_title and self._current_href:
|
|
103
|
+
self._text.append(data)
|
|
104
|
+
elif self._in_caption:
|
|
105
|
+
self._current_snippet += data
|
|
106
|
+
|
|
107
|
+
def finalize(self) -> None:
|
|
108
|
+
if self._in_algo and self._current_href:
|
|
109
|
+
self.results.append({
|
|
110
|
+
"title": self._current_title,
|
|
111
|
+
"href": self._current_href,
|
|
112
|
+
"snippet": self._current_snippet.strip(),
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class WebSearchTool(Tool):
|
|
117
|
+
name = "WebSearch"
|
|
118
|
+
description = (
|
|
119
|
+
"Searches the web using Bing and returns results as structured text. "
|
|
120
|
+
"Use this to find documentation, code examples, or any information on the web. "
|
|
121
|
+
"Each result includes a title, URL, and text snippet."
|
|
122
|
+
)
|
|
123
|
+
input_schema = {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"properties": {
|
|
126
|
+
"query": {
|
|
127
|
+
"type": "string",
|
|
128
|
+
"description": "Search query string",
|
|
129
|
+
},
|
|
130
|
+
"max_results": {
|
|
131
|
+
"type": "integer",
|
|
132
|
+
"description": f"Maximum number of results (1-{_MAX_RESULTS}, default {_DEFAULT_RESULTS})",
|
|
133
|
+
"default": _DEFAULT_RESULTS,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
"required": ["query"],
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
def is_read_only(self) -> bool:
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
def get_activity_description(self, **kwargs) -> str | None:
|
|
143
|
+
query = kwargs.get("query", "")
|
|
144
|
+
return f"Searching: {query}" if query else None
|
|
145
|
+
|
|
146
|
+
def execute(self, query: str, max_results: int = _DEFAULT_RESULTS, **kwargs) -> ToolResult:
|
|
147
|
+
if not query.strip():
|
|
148
|
+
return ToolResult(content="Error: query must not be empty.", is_error=True)
|
|
149
|
+
|
|
150
|
+
max_results = min(max(max_results, 1), _MAX_RESULTS)
|
|
151
|
+
|
|
152
|
+
encoded_query = urllib.parse.quote_plus(query)
|
|
153
|
+
url = f"https://www.bing.com/search?q={encoded_query}"
|
|
154
|
+
|
|
155
|
+
# 代理支持:读取环境变量 HTTPS_PROXY / HTTP_PROXY
|
|
156
|
+
proxy_url = os.environ.get("HTTPS_PROXY") or os.environ.get("HTTP_PROXY")
|
|
157
|
+
opener = (
|
|
158
|
+
urllib.request.build_opener(
|
|
159
|
+
urllib.request.ProxyHandler({"https": proxy_url, "http": proxy_url})
|
|
160
|
+
)
|
|
161
|
+
if proxy_url
|
|
162
|
+
else urllib.request.build_opener()
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
req = urllib.request.Request(
|
|
167
|
+
url,
|
|
168
|
+
headers={
|
|
169
|
+
"User-Agent": (
|
|
170
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
171
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
172
|
+
"Chrome/120.0.0.0 Safari/537.36"
|
|
173
|
+
),
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
with opener.open(req, timeout=15) as resp:
|
|
177
|
+
raw_bytes = resp.read(200_000)
|
|
178
|
+
except urllib.error.HTTPError as e:
|
|
179
|
+
return ToolResult(content=f"Search error: HTTP {e.code} {e.reason}", is_error=True)
|
|
180
|
+
except urllib.error.URLError as e:
|
|
181
|
+
return ToolResult(content=f"Search error: {e.reason}", is_error=True)
|
|
182
|
+
except TimeoutError:
|
|
183
|
+
return ToolResult(content="Search error: request timed out after 15s", is_error=True)
|
|
184
|
+
except Exception as e:
|
|
185
|
+
return ToolResult(content=f"Search error: {e}", is_error=True)
|
|
186
|
+
|
|
187
|
+
charset = resp.headers.get_content_charset() or "utf-8"
|
|
188
|
+
try:
|
|
189
|
+
raw_html = raw_bytes.decode(charset, errors="replace")
|
|
190
|
+
except LookupError:
|
|
191
|
+
raw_html = raw_bytes.decode("utf-8", errors="replace")
|
|
192
|
+
|
|
193
|
+
parser = _BingParser()
|
|
194
|
+
try:
|
|
195
|
+
parser.feed(raw_html)
|
|
196
|
+
parser.finalize()
|
|
197
|
+
except Exception:
|
|
198
|
+
return ToolResult(content="Search error: failed to parse results.", is_error=True)
|
|
199
|
+
|
|
200
|
+
results = parser.results[:max_results]
|
|
201
|
+
|
|
202
|
+
if not results:
|
|
203
|
+
return ToolResult(content="No results found.")
|
|
204
|
+
|
|
205
|
+
lines = []
|
|
206
|
+
total = 0
|
|
207
|
+
for i, r in enumerate(results, 1):
|
|
208
|
+
title = r["title"] or "(no title)"
|
|
209
|
+
href = r["href"]
|
|
210
|
+
# 清理 HTML 标签和实体
|
|
211
|
+
snippet = html.unescape(re.sub(r"<[^>]+>", "", r["snippet"].strip()))
|
|
212
|
+
line = f"{i}. **{title}**\n {href}\n {snippet}"
|
|
213
|
+
total += len(line)
|
|
214
|
+
if total > _MAX_CHARS:
|
|
215
|
+
if i == 1:
|
|
216
|
+
lines.append(line[:_MAX_CHARS] + "...")
|
|
217
|
+
break
|
|
218
|
+
lines.append(line)
|
|
219
|
+
|
|
220
|
+
return ToolResult(content="\n\n".join(lines))
|
tui/__init__.py
ADDED
|
File without changes
|