Skip to content

Commit f7d146d

Browse files
lzwjavaclaude
andcommitted
feat(commands): add /search command for direct web search
Searches the web and sends results as context to the model for a summarized answer. Falls back to raw output when not authenticated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 72325c4 commit f7d146d

3 files changed

Lines changed: 44 additions & 0 deletions

File tree

iclaw/completer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
COMMANDS = [
77
"/provider_model",
88
"/model",
9+
"/search",
910
"/provider_search",
1011
"/proxy",
1112
"/ca_bundle",

iclaw/main.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
COMMANDS_HELP = [
3030
("/provider_model", "Select and authenticate with the model provider"),
3131
("/model", "Select specific model from your provider"),
32+
("/search", "Web search (usage: /search <query>)"),
3233
("/provider_search", "Select the web search provider"),
3334
("/proxy", "Set HTTP/HTTPS proxy (usage: /proxy [url|off])"),
3435
("/ca_bundle", "Set CA bundle for HTTPS (usage: /ca_bundle [path|off])"),
@@ -116,6 +117,32 @@ def main():
116117
ca_bundle=ca_bundle,
117118
)
118119
continue
120+
if user_input.startswith("/search "):
121+
query = user_input.split(maxsplit=1)[1]
122+
search_context = web_search(query, num_results=5, provider=search_provider)
123+
if not copilot_token:
124+
print(f"\n{search_context}\n")
125+
continue
126+
search_msg = (
127+
f"Based on the following web search results for '{query}', "
128+
"provide a concise and helpful answer.\n\n"
129+
f"{search_context}"
130+
)
131+
messages.append({"role": "user", "content": search_msg})
132+
try:
133+
if time.monotonic() >= token_expiry and github_token:
134+
copilot_token = get_copilot_token(github_token)
135+
token_expiry = time.monotonic() + TOKEN_REFRESH_INTERVAL
136+
response_message = chat(
137+
messages, copilot_token, current_model, tools=TOOLS
138+
)
139+
reply = response_message.get("content", "")
140+
messages.append({"role": "assistant", "content": reply})
141+
last_reply = reply
142+
print(f"\n{reply}\n")
143+
except Exception as e:
144+
print(f"Error: {e}", file=sys.stderr)
145+
continue
119146
if user_input == "/provider_search":
120147
search_provider = handle_search_provider_command(search_provider)
121148
save_session_settings(

tests/test_main.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,22 @@ def test_main_search_provider(
137137
with patch("sys.stdout"), patch("iclaw.main.time.monotonic", return_value=0):
138138
main.main()
139139

140+
@patch("iclaw.main.http")
141+
@patch("iclaw.main.load_github_token", return_value="gt")
142+
@patch("iclaw.main.get_copilot_token", return_value="ct")
143+
@patch("iclaw.main.chat", return_value={"content": "Today is 2026-03-30."})
144+
@patch("iclaw.main.web_search", return_value="search results here")
145+
@patch("iclaw.main.PromptSession")
146+
def test_main_search_command(
147+
self, mock_ps, mock_ws, mock_chat, mock_cp, mock_load, mock_http
148+
):
149+
mock_ps.return_value = _mock_session("/search what is today", ".exit")
150+
with patch("sys.stdout"), patch("iclaw.main.time.monotonic", return_value=0):
151+
main.main()
152+
mock_ws.assert_called_once_with(
153+
"what is today", num_results=5, provider="duckduckgo"
154+
)
155+
140156
@patch("iclaw.main.http")
141157
@patch("iclaw.main.load_github_token", return_value="gt")
142158
@patch("iclaw.main.get_copilot_token", return_value="ct")

0 commit comments

Comments
 (0)