deepagents-cli 0.0.3__py3-none-any.whl → 0.0.4__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.
Potentially problematic release.
This version of deepagents-cli might be problematic. Click here for more details.
- deepagents_cli/__init__.py +5 -0
- deepagents_cli/__main__.py +6 -0
- deepagents_cli/agent.py +267 -0
- deepagents_cli/cli.py +13 -0
- deepagents_cli/commands.py +86 -0
- deepagents_cli/config.py +138 -0
- deepagents_cli/execution.py +644 -0
- deepagents_cli/file_ops.py +347 -0
- deepagents_cli/input.py +249 -0
- deepagents_cli/main.py +217 -0
- deepagents_cli/py.typed +0 -0
- deepagents_cli/tools.py +140 -0
- deepagents_cli/ui.py +455 -0
- deepagents_cli-0.0.4.dist-info/METADATA +18 -0
- deepagents_cli-0.0.4.dist-info/RECORD +18 -0
- deepagents_cli-0.0.4.dist-info/entry_points.txt +3 -0
- deepagents_cli-0.0.4.dist-info/top_level.txt +1 -0
- deepagents/__init__.py +0 -7
- deepagents/cli.py +0 -567
- deepagents/default_agent_prompt.md +0 -64
- deepagents/graph.py +0 -144
- deepagents/memory/__init__.py +0 -17
- deepagents/memory/backends/__init__.py +0 -15
- deepagents/memory/backends/composite.py +0 -250
- deepagents/memory/backends/filesystem.py +0 -330
- deepagents/memory/backends/state.py +0 -206
- deepagents/memory/backends/store.py +0 -351
- deepagents/memory/backends/utils.py +0 -319
- deepagents/memory/protocol.py +0 -164
- deepagents/middleware/__init__.py +0 -13
- deepagents/middleware/agent_memory.py +0 -207
- deepagents/middleware/filesystem.py +0 -615
- deepagents/middleware/patch_tool_calls.py +0 -44
- deepagents/middleware/subagents.py +0 -481
- deepagents/pretty_cli.py +0 -289
- deepagents_cli-0.0.3.dist-info/METADATA +0 -551
- deepagents_cli-0.0.3.dist-info/RECORD +0 -24
- deepagents_cli-0.0.3.dist-info/entry_points.txt +0 -2
- deepagents_cli-0.0.3.dist-info/licenses/LICENSE +0 -21
- deepagents_cli-0.0.3.dist-info/top_level.txt +0 -1
- {deepagents_cli-0.0.3.dist-info → deepagents_cli-0.0.4.dist-info}/WHEEL +0 -0
deepagents/cli.py
DELETED
|
@@ -1,567 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
import argparse
|
|
3
|
-
import asyncio
|
|
4
|
-
import os
|
|
5
|
-
import subprocess
|
|
6
|
-
import platform
|
|
7
|
-
import requests
|
|
8
|
-
from typing import Dict, Any, Union, Literal
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
|
-
from tavily import TavilyClient
|
|
12
|
-
from deepagents import create_deep_agent
|
|
13
|
-
from langgraph.checkpoint.memory import InMemorySaver
|
|
14
|
-
from langchain.agents.middleware import ShellToolMiddleware, HostExecutionPolicy
|
|
15
|
-
|
|
16
|
-
from rich.console import Console
|
|
17
|
-
from rich.markdown import Markdown
|
|
18
|
-
from rich.panel import Panel
|
|
19
|
-
from rich.spinner import Spinner
|
|
20
|
-
from deepagents.memory.backends.filesystem import FilesystemBackend
|
|
21
|
-
from deepagents.memory.backends import CompositeBackend
|
|
22
|
-
from deepagents.middleware.agent_memory import AgentMemoryMiddleware
|
|
23
|
-
from pathlib import Path
|
|
24
|
-
import shutil
|
|
25
|
-
from rich import box
|
|
26
|
-
|
|
27
|
-
import dotenv
|
|
28
|
-
|
|
29
|
-
dotenv.load_dotenv()
|
|
30
|
-
|
|
31
|
-
COLORS = {
|
|
32
|
-
"primary": "#10b981",
|
|
33
|
-
"dim": "#6b7280",
|
|
34
|
-
"user": "#ffffff",
|
|
35
|
-
"agent": "#10b981",
|
|
36
|
-
"thinking": "#34d399",
|
|
37
|
-
"tool": "#fbbf24",
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
DEEP_AGENTS_ASCII = """
|
|
41
|
-
██████╗ ███████╗ ███████╗ ██████╗
|
|
42
|
-
██╔══██╗ ██╔════╝ ██╔════╝ ██╔══██╗
|
|
43
|
-
██║ ██║ █████╗ █████╗ ██████╔╝
|
|
44
|
-
██║ ██║ ██╔══╝ ██╔══╝ ██╔═══╝
|
|
45
|
-
██████╔╝ ███████╗ ███████╗ ██║
|
|
46
|
-
╚═════╝ ╚══════╝ ╚══════╝ ╚═╝
|
|
47
|
-
|
|
48
|
-
█████╗ ██████╗ ███████╗ ███╗ ██╗ ████████╗ ███████╗
|
|
49
|
-
██╔══██╗ ██╔════╝ ██╔════╝ ████╗ ██║ ╚══██╔══╝ ██╔════╝
|
|
50
|
-
███████║ ██║ ███╗ █████╗ ██╔██╗ ██║ ██║ ███████╗
|
|
51
|
-
██╔══██║ ██║ ██║ ██╔══╝ ██║╚██╗██║ ██║ ╚════██║
|
|
52
|
-
██║ ██║ ╚██████╔╝ ███████╗ ██║ ╚████║ ██║ ███████║
|
|
53
|
-
╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═══╝ ╚═╝ ╚══════╝
|
|
54
|
-
"""
|
|
55
|
-
|
|
56
|
-
console = Console()
|
|
57
|
-
|
|
58
|
-
tavily_client = TavilyClient(api_key=os.environ.get("TAVILY_API_KEY")) if os.environ.get("TAVILY_API_KEY") else None
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def http_request(
|
|
62
|
-
url: str,
|
|
63
|
-
method: str = "GET",
|
|
64
|
-
headers: Dict[str, str] = None,
|
|
65
|
-
data: Union[str, Dict] = None,
|
|
66
|
-
params: Dict[str, str] = None,
|
|
67
|
-
timeout: int = 30,
|
|
68
|
-
) -> Dict[str, Any]:
|
|
69
|
-
"""
|
|
70
|
-
Make HTTP requests to APIs and web services.
|
|
71
|
-
|
|
72
|
-
Args:
|
|
73
|
-
url: Target URL
|
|
74
|
-
method: HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
75
|
-
headers: HTTP headers to include
|
|
76
|
-
data: Request body data (string or dict)
|
|
77
|
-
params: URL query parameters
|
|
78
|
-
timeout: Request timeout in seconds
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
Dictionary with response data including status, headers, and content
|
|
82
|
-
"""
|
|
83
|
-
try:
|
|
84
|
-
kwargs = {"url": url, "method": method.upper(), "timeout": timeout}
|
|
85
|
-
|
|
86
|
-
if headers:
|
|
87
|
-
kwargs["headers"] = headers
|
|
88
|
-
if params:
|
|
89
|
-
kwargs["params"] = params
|
|
90
|
-
if data:
|
|
91
|
-
if isinstance(data, dict):
|
|
92
|
-
kwargs["json"] = data
|
|
93
|
-
else:
|
|
94
|
-
kwargs["data"] = data
|
|
95
|
-
|
|
96
|
-
response = requests.request(**kwargs)
|
|
97
|
-
|
|
98
|
-
try:
|
|
99
|
-
content = response.json()
|
|
100
|
-
except:
|
|
101
|
-
content = response.text
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
"success": response.status_code < 400,
|
|
105
|
-
"status_code": response.status_code,
|
|
106
|
-
"headers": dict(response.headers),
|
|
107
|
-
"content": content,
|
|
108
|
-
"url": response.url,
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
except requests.exceptions.Timeout:
|
|
112
|
-
return {
|
|
113
|
-
"success": False,
|
|
114
|
-
"status_code": 0,
|
|
115
|
-
"headers": {},
|
|
116
|
-
"content": f"Request timed out after {timeout} seconds",
|
|
117
|
-
"url": url,
|
|
118
|
-
}
|
|
119
|
-
except requests.exceptions.RequestException as e:
|
|
120
|
-
return {
|
|
121
|
-
"success": False,
|
|
122
|
-
"status_code": 0,
|
|
123
|
-
"headers": {},
|
|
124
|
-
"content": f"Request error: {str(e)}",
|
|
125
|
-
"url": url,
|
|
126
|
-
}
|
|
127
|
-
except Exception as e:
|
|
128
|
-
return {
|
|
129
|
-
"success": False,
|
|
130
|
-
"status_code": 0,
|
|
131
|
-
"headers": {},
|
|
132
|
-
"content": f"Error making request: {str(e)}",
|
|
133
|
-
"url": url,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def web_search(
|
|
138
|
-
query: str,
|
|
139
|
-
max_results: int = 5,
|
|
140
|
-
topic: Literal["general", "news", "finance"] = "general",
|
|
141
|
-
include_raw_content: bool = False,
|
|
142
|
-
):
|
|
143
|
-
"""Search the web using Tavily for programming-related information."""
|
|
144
|
-
if tavily_client is None:
|
|
145
|
-
return {
|
|
146
|
-
"error": "Tavily API key not configured. Please set TAVILY_API_KEY environment variable.",
|
|
147
|
-
"query": query
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
try:
|
|
151
|
-
search_docs = tavily_client.search(
|
|
152
|
-
query,
|
|
153
|
-
max_results=max_results,
|
|
154
|
-
include_raw_content=include_raw_content,
|
|
155
|
-
topic=topic,
|
|
156
|
-
)
|
|
157
|
-
return search_docs
|
|
158
|
-
except Exception as e:
|
|
159
|
-
return {
|
|
160
|
-
"error": f"Web search error: {str(e)}",
|
|
161
|
-
"query": query
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def get_default_coding_instructions() -> str:
|
|
166
|
-
"""Get the default coding agent instructions.
|
|
167
|
-
|
|
168
|
-
These are the immutable base instructions that cannot be modified by the agent.
|
|
169
|
-
Long-term memory (agent.md) is handled separately by the middleware.
|
|
170
|
-
"""
|
|
171
|
-
default_prompt_path = Path(__file__).parent / "default_agent_prompt.md"
|
|
172
|
-
return default_prompt_path.read_text()
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
config = {"recursion_limit": 1000}
|
|
176
|
-
|
|
177
|
-
MAX_ARG_LENGTH = 150
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def truncate_value(value: str, max_length: int = MAX_ARG_LENGTH) -> str:
|
|
181
|
-
"""Truncate a string value if it exceeds max_length."""
|
|
182
|
-
if len(value) > max_length:
|
|
183
|
-
return value[:max_length] + "..."
|
|
184
|
-
return value
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def execute_task(user_input: str, agent, assistant_id: str | None):
|
|
188
|
-
"""Execute any task by passing it directly to the AI agent."""
|
|
189
|
-
console.print()
|
|
190
|
-
|
|
191
|
-
config = {
|
|
192
|
-
"configurable": {"thread_id": "main"},
|
|
193
|
-
"metadata": {"assistant_id": assistant_id} if assistant_id else {}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
has_responded = False
|
|
197
|
-
current_text = ""
|
|
198
|
-
printed_tool_calls_after_text = False
|
|
199
|
-
|
|
200
|
-
status = console.status(f"[bold {COLORS['thinking']}]Agent is thinking...", spinner="dots")
|
|
201
|
-
status.start()
|
|
202
|
-
spinner_active = True
|
|
203
|
-
|
|
204
|
-
tool_icons = {
|
|
205
|
-
"read_file": "📖",
|
|
206
|
-
"write_file": "✏️",
|
|
207
|
-
"edit_file": "✂️",
|
|
208
|
-
"ls": "📁",
|
|
209
|
-
"glob": "🔍",
|
|
210
|
-
"grep": "🔎",
|
|
211
|
-
"shell": "⚡",
|
|
212
|
-
"web_search": "🌐",
|
|
213
|
-
"http_request": "🌍",
|
|
214
|
-
"task": "🤖",
|
|
215
|
-
"write_todos": "📋",
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
for _, chunk in agent.stream(
|
|
219
|
-
{"messages": [{"role": "user", "content": user_input}]},
|
|
220
|
-
stream_mode="updates",
|
|
221
|
-
subgraphs=True,
|
|
222
|
-
config=config,
|
|
223
|
-
durability="exit",
|
|
224
|
-
):
|
|
225
|
-
chunk_data = list(chunk.values())[0]
|
|
226
|
-
if not chunk_data or "messages" not in chunk_data:
|
|
227
|
-
continue
|
|
228
|
-
|
|
229
|
-
last_message = chunk_data["messages"][-1]
|
|
230
|
-
message_role = getattr(last_message, "type", None)
|
|
231
|
-
message_content = getattr(last_message, "content", None)
|
|
232
|
-
|
|
233
|
-
# Skip tool results
|
|
234
|
-
if message_role == "tool":
|
|
235
|
-
continue
|
|
236
|
-
|
|
237
|
-
# Handle AI messages
|
|
238
|
-
if message_role == "ai":
|
|
239
|
-
# First, extract and display text content
|
|
240
|
-
text_content = ""
|
|
241
|
-
|
|
242
|
-
if isinstance(message_content, str):
|
|
243
|
-
text_content = message_content
|
|
244
|
-
elif isinstance(message_content, list):
|
|
245
|
-
for block in message_content:
|
|
246
|
-
if isinstance(block, dict) and block.get("type") == "text":
|
|
247
|
-
text_content = block.get("text", "")
|
|
248
|
-
break
|
|
249
|
-
|
|
250
|
-
if text_content.strip():
|
|
251
|
-
if spinner_active:
|
|
252
|
-
status.stop()
|
|
253
|
-
spinner_active = False
|
|
254
|
-
|
|
255
|
-
if not has_responded:
|
|
256
|
-
console.print("... ", style=COLORS["agent"], end="", markup=False)
|
|
257
|
-
has_responded = True
|
|
258
|
-
printed_tool_calls_after_text = False
|
|
259
|
-
|
|
260
|
-
if text_content != current_text:
|
|
261
|
-
new_text = text_content[len(current_text):]
|
|
262
|
-
console.print(new_text, style=COLORS["agent"], end="", markup=False)
|
|
263
|
-
current_text = text_content
|
|
264
|
-
printed_tool_calls_after_text = False
|
|
265
|
-
|
|
266
|
-
# Then, handle tool calls from tool_calls attribute
|
|
267
|
-
tool_calls = getattr(last_message, "tool_calls", None)
|
|
268
|
-
if tool_calls:
|
|
269
|
-
# If we've printed text, ensure tool calls go on new line
|
|
270
|
-
if has_responded and current_text and not printed_tool_calls_after_text:
|
|
271
|
-
console.print()
|
|
272
|
-
printed_tool_calls_after_text = True
|
|
273
|
-
|
|
274
|
-
for tool_call in tool_calls:
|
|
275
|
-
tool_name = tool_call.get("name", "unknown")
|
|
276
|
-
tool_args = tool_call.get("args", {})
|
|
277
|
-
|
|
278
|
-
icon = tool_icons.get(tool_name, "🔧")
|
|
279
|
-
args_str = ", ".join(
|
|
280
|
-
f"{k}={truncate_value(str(v), 50)}" for k, v in tool_args.items()
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
if spinner_active:
|
|
284
|
-
status.stop()
|
|
285
|
-
console.print(f" {icon} {tool_name}({args_str})", style=f"dim {COLORS['tool']}")
|
|
286
|
-
if spinner_active:
|
|
287
|
-
status.start()
|
|
288
|
-
|
|
289
|
-
# Handle tool calls from content blocks (alternative format) - only if not already handled
|
|
290
|
-
elif isinstance(message_content, list):
|
|
291
|
-
has_tool_use = False
|
|
292
|
-
for block in message_content:
|
|
293
|
-
if isinstance(block, dict) and block.get("type") == "tool_use":
|
|
294
|
-
has_tool_use = True
|
|
295
|
-
break
|
|
296
|
-
|
|
297
|
-
if has_tool_use:
|
|
298
|
-
# If we've printed text, ensure tool calls go on new line
|
|
299
|
-
if has_responded and current_text and not printed_tool_calls_after_text:
|
|
300
|
-
console.print()
|
|
301
|
-
printed_tool_calls_after_text = True
|
|
302
|
-
|
|
303
|
-
for block in message_content:
|
|
304
|
-
if isinstance(block, dict) and block.get("type") == "tool_use":
|
|
305
|
-
tool_name = block.get("name", "unknown")
|
|
306
|
-
tool_input = block.get("input", {})
|
|
307
|
-
|
|
308
|
-
icon = tool_icons.get(tool_name, "🔧")
|
|
309
|
-
args = ", ".join(
|
|
310
|
-
f"{k}={truncate_value(str(v), 50)}" for k, v in tool_input.items()
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
if spinner_active:
|
|
314
|
-
status.stop()
|
|
315
|
-
console.print(f" {icon} {tool_name}({args})", style=f"dim {COLORS['tool']}")
|
|
316
|
-
if spinner_active:
|
|
317
|
-
status.start()
|
|
318
|
-
|
|
319
|
-
if spinner_active:
|
|
320
|
-
status.stop()
|
|
321
|
-
|
|
322
|
-
if has_responded:
|
|
323
|
-
console.print()
|
|
324
|
-
console.print()
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
async def simple_cli(agent, assistant_id: str | None):
|
|
328
|
-
"""Main CLI loop."""
|
|
329
|
-
console.clear()
|
|
330
|
-
console.print(DEEP_AGENTS_ASCII, style=f"bold {COLORS['primary']}")
|
|
331
|
-
console.print()
|
|
332
|
-
|
|
333
|
-
if tavily_client is None:
|
|
334
|
-
console.print(f"[yellow]⚠ Web search disabled:[/yellow] TAVILY_API_KEY not found.", style=COLORS["dim"])
|
|
335
|
-
console.print(f" To enable web search, set your Tavily API key:", style=COLORS["dim"])
|
|
336
|
-
console.print(f" export TAVILY_API_KEY=your_api_key_here", style=COLORS["dim"])
|
|
337
|
-
console.print(f" Or add it to your .env file. Get your key at: https://tavily.com", style=COLORS["dim"])
|
|
338
|
-
console.print()
|
|
339
|
-
|
|
340
|
-
console.print("... Ready to code! What would you like to build?", style=COLORS["agent"])
|
|
341
|
-
console.print()
|
|
342
|
-
console.print(f" Tip: Type 'quit' to exit", style=f"dim {COLORS['dim']}")
|
|
343
|
-
console.print()
|
|
344
|
-
|
|
345
|
-
while True:
|
|
346
|
-
try:
|
|
347
|
-
console.print(f"> ", style=COLORS["user"], end="")
|
|
348
|
-
user_input = input().strip()
|
|
349
|
-
except EOFError:
|
|
350
|
-
break
|
|
351
|
-
except KeyboardInterrupt:
|
|
352
|
-
console.print()
|
|
353
|
-
break
|
|
354
|
-
|
|
355
|
-
if not user_input:
|
|
356
|
-
continue
|
|
357
|
-
|
|
358
|
-
if user_input.lower() in ["quit", "exit", "q"]:
|
|
359
|
-
console.print(f"\nGoodbye!", style=COLORS["primary"])
|
|
360
|
-
break
|
|
361
|
-
|
|
362
|
-
execute_task(user_input, agent, assistant_id)
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
def list_agents():
|
|
366
|
-
"""List all available agents."""
|
|
367
|
-
agents_dir = Path.home() / ".deepagents"
|
|
368
|
-
|
|
369
|
-
if not agents_dir.exists() or not any(agents_dir.iterdir()):
|
|
370
|
-
console.print("[yellow]No agents found.[/yellow]")
|
|
371
|
-
console.print(f"[dim]Agents will be created in ~/.deepagents/ when you first use them.[/dim]", style=COLORS["dim"])
|
|
372
|
-
return
|
|
373
|
-
|
|
374
|
-
console.print(f"\n[bold]Available Agents:[/bold]\n", style=COLORS["primary"])
|
|
375
|
-
|
|
376
|
-
for agent_path in sorted(agents_dir.iterdir()):
|
|
377
|
-
if agent_path.is_dir():
|
|
378
|
-
agent_name = agent_path.name
|
|
379
|
-
agent_md = agent_path / "agent.md"
|
|
380
|
-
|
|
381
|
-
if agent_md.exists():
|
|
382
|
-
console.print(f" • [bold]{agent_name}[/bold]", style=COLORS["primary"])
|
|
383
|
-
console.print(f" {agent_path}", style=COLORS["dim"])
|
|
384
|
-
else:
|
|
385
|
-
console.print(f" • [bold]{agent_name}[/bold] [dim](incomplete)[/dim]", style=COLORS["tool"])
|
|
386
|
-
console.print(f" {agent_path}", style=COLORS["dim"])
|
|
387
|
-
|
|
388
|
-
console.print()
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
def reset_agent(agent_name: str, source_agent: str = None):
|
|
392
|
-
"""Reset an agent to default or copy from another agent."""
|
|
393
|
-
agents_dir = Path.home() / ".deepagents"
|
|
394
|
-
agent_dir = agents_dir / agent_name
|
|
395
|
-
|
|
396
|
-
if source_agent:
|
|
397
|
-
source_dir = agents_dir / source_agent
|
|
398
|
-
source_md = source_dir / "agent.md"
|
|
399
|
-
|
|
400
|
-
if not source_md.exists():
|
|
401
|
-
console.print(f"[bold red]Error:[/bold red] Source agent '{source_agent}' not found or has no agent.md")
|
|
402
|
-
return
|
|
403
|
-
|
|
404
|
-
source_content = source_md.read_text()
|
|
405
|
-
action_desc = f"contents of agent '{source_agent}'"
|
|
406
|
-
else:
|
|
407
|
-
source_content = get_default_coding_instructions()
|
|
408
|
-
action_desc = "default"
|
|
409
|
-
|
|
410
|
-
if agent_dir.exists():
|
|
411
|
-
shutil.rmtree(agent_dir)
|
|
412
|
-
console.print(f"Removed existing agent directory: {agent_dir}", style=COLORS["tool"])
|
|
413
|
-
|
|
414
|
-
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
415
|
-
agent_md = agent_dir / "agent.md"
|
|
416
|
-
agent_md.write_text(source_content)
|
|
417
|
-
|
|
418
|
-
console.print(f"✓ Agent '{agent_name}' reset to {action_desc}", style=COLORS["primary"])
|
|
419
|
-
console.print(f"Location: {agent_dir}\n", style=COLORS["dim"])
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
def show_help():
|
|
423
|
-
"""Show help information."""
|
|
424
|
-
console.print()
|
|
425
|
-
console.print(DEEP_AGENTS_ASCII, style=f"bold {COLORS['primary']}")
|
|
426
|
-
console.print()
|
|
427
|
-
|
|
428
|
-
console.print("[bold]Usage:[/bold]", style=COLORS["primary"])
|
|
429
|
-
console.print(" deepagents [--agent NAME] Start interactive session")
|
|
430
|
-
console.print(" deepagents list List all available agents")
|
|
431
|
-
console.print(" deepagents reset --agent AGENT Reset agent to default prompt")
|
|
432
|
-
console.print(" deepagents reset --agent AGENT --target SOURCE Reset agent to copy of another agent")
|
|
433
|
-
console.print(" deepagents help Show this help message")
|
|
434
|
-
console.print()
|
|
435
|
-
|
|
436
|
-
console.print("[bold]Examples:[/bold]", style=COLORS["primary"])
|
|
437
|
-
console.print(" deepagents # Start with default agent", style=COLORS["dim"])
|
|
438
|
-
console.print(" deepagents --agent mybot # Start with agent named 'mybot'", style=COLORS["dim"])
|
|
439
|
-
console.print(" deepagents list # List all agents", style=COLORS["dim"])
|
|
440
|
-
console.print(" deepagents reset --agent mybot # Reset mybot to default", style=COLORS["dim"])
|
|
441
|
-
console.print(" deepagents reset --agent mybot --target other # Reset mybot to copy of 'other' agent", style=COLORS["dim"])
|
|
442
|
-
console.print()
|
|
443
|
-
|
|
444
|
-
console.print("[bold]Long-term Memory:[/bold]", style=COLORS["primary"])
|
|
445
|
-
console.print(" By default, long-term memory is ENABLED using agent name 'agent'.", style=COLORS["dim"])
|
|
446
|
-
console.print(" Memory includes:", style=COLORS["dim"])
|
|
447
|
-
console.print(" - Persistent agent.md file with your instructions", style=COLORS["dim"])
|
|
448
|
-
console.print(" - /memories/ folder for storing context across sessions", style=COLORS["dim"])
|
|
449
|
-
console.print()
|
|
450
|
-
|
|
451
|
-
console.print("[bold]Agent Storage:[/bold]", style=COLORS["primary"])
|
|
452
|
-
console.print(" Agents are stored in: ~/.deepagents/AGENT_NAME/", style=COLORS["dim"])
|
|
453
|
-
console.print(" Each agent has an agent.md file containing its prompt", style=COLORS["dim"])
|
|
454
|
-
console.print()
|
|
455
|
-
|
|
456
|
-
console.print("[bold]Interactive Commands:[/bold]", style=COLORS["primary"])
|
|
457
|
-
console.print(" quit, exit, q Exit the session", style=COLORS["dim"])
|
|
458
|
-
console.print()
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
def parse_args():
|
|
462
|
-
"""Parse command line arguments."""
|
|
463
|
-
parser = argparse.ArgumentParser(
|
|
464
|
-
description="DeepAgents - AI Coding Assistant",
|
|
465
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
466
|
-
add_help=False,
|
|
467
|
-
)
|
|
468
|
-
|
|
469
|
-
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
470
|
-
|
|
471
|
-
# List command
|
|
472
|
-
subparsers.add_parser("list", help="List all available agents")
|
|
473
|
-
|
|
474
|
-
# Help command
|
|
475
|
-
subparsers.add_parser("help", help="Show help information")
|
|
476
|
-
|
|
477
|
-
# Reset command
|
|
478
|
-
reset_parser = subparsers.add_parser("reset", help="Reset an agent")
|
|
479
|
-
reset_parser.add_argument("--agent", required=True, help="Name of agent to reset")
|
|
480
|
-
reset_parser.add_argument("--target", dest="source_agent", help="Copy prompt from another agent")
|
|
481
|
-
|
|
482
|
-
# Default interactive mode
|
|
483
|
-
parser.add_argument(
|
|
484
|
-
"--agent",
|
|
485
|
-
default="agent",
|
|
486
|
-
help="Agent identifier for separate memory stores (default: agent).",
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
return parser.parse_args()
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
async def main(assistant_id: str):
|
|
493
|
-
"""Main entry point."""
|
|
494
|
-
|
|
495
|
-
# Create agent with conditional tools
|
|
496
|
-
tools = [http_request]
|
|
497
|
-
if tavily_client is not None:
|
|
498
|
-
tools.append(web_search)
|
|
499
|
-
|
|
500
|
-
shell_middleware = ShellToolMiddleware(
|
|
501
|
-
workspace_root=os.getcwd(),
|
|
502
|
-
execution_policy=HostExecutionPolicy()
|
|
503
|
-
)
|
|
504
|
-
|
|
505
|
-
# For long-term memory, point to ~/.deepagents/AGENT_NAME/ with /memories/ prefix
|
|
506
|
-
agent_dir = Path.home() / ".deepagents" / assistant_id
|
|
507
|
-
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
508
|
-
agent_md = agent_dir / "agent.md"
|
|
509
|
-
if not agent_md.exists():
|
|
510
|
-
source_content = get_default_coding_instructions()
|
|
511
|
-
agent_md.write_text(source_content)
|
|
512
|
-
|
|
513
|
-
# Long-term backend - rooted at agent directory
|
|
514
|
-
# This handles both /memories/ files and /agent.md
|
|
515
|
-
long_term_backend = FilesystemBackend(root_dir=agent_dir, virtual_mode=True)
|
|
516
|
-
|
|
517
|
-
# Composite backend: current working directory for default, agent directory for /memories/
|
|
518
|
-
backend = CompositeBackend(
|
|
519
|
-
default=FilesystemBackend(),
|
|
520
|
-
routes={"/memories/": long_term_backend}
|
|
521
|
-
)
|
|
522
|
-
|
|
523
|
-
# Use the same backend for agent memory middleware
|
|
524
|
-
agent_middleware = [AgentMemoryMiddleware(backend=long_term_backend, memory_path="/memories/"), shell_middleware]
|
|
525
|
-
system_prompt = f"""### Current Working Directory
|
|
526
|
-
|
|
527
|
-
The filesystem backend is currently operating in: `{Path.cwd()}`"""
|
|
528
|
-
|
|
529
|
-
agent = create_deep_agent(
|
|
530
|
-
system_prompt=system_prompt,
|
|
531
|
-
tools=tools,
|
|
532
|
-
memory_backend=backend,
|
|
533
|
-
middleware=agent_middleware,
|
|
534
|
-
).with_config(config)
|
|
535
|
-
|
|
536
|
-
agent.checkpointer = InMemorySaver()
|
|
537
|
-
|
|
538
|
-
try:
|
|
539
|
-
await simple_cli(agent, assistant_id)
|
|
540
|
-
except KeyboardInterrupt:
|
|
541
|
-
console.print(f"\n\nGoodbye!", style=COLORS["primary"])
|
|
542
|
-
except Exception as e:
|
|
543
|
-
console.print(f"\n[bold red]❌ Error:[/bold red] {e}\n")
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
def cli_main():
|
|
547
|
-
"""Entry point for console script."""
|
|
548
|
-
args = parse_args()
|
|
549
|
-
|
|
550
|
-
if args.command == "help":
|
|
551
|
-
show_help()
|
|
552
|
-
elif args.command == "list":
|
|
553
|
-
list_agents()
|
|
554
|
-
elif args.command == "reset":
|
|
555
|
-
reset_agent(args.agent, args.source_agent)
|
|
556
|
-
else:
|
|
557
|
-
if not os.environ.get("ANTHROPIC_API_KEY"):
|
|
558
|
-
console.print("[bold red]Error:[/bold red] ANTHROPIC_API_KEY environment variable is not set.")
|
|
559
|
-
console.print("Please set your Anthropic API key:")
|
|
560
|
-
console.print(" export ANTHROPIC_API_KEY=your_api_key_here")
|
|
561
|
-
console.print("\nOr add it to your .env file.")
|
|
562
|
-
return
|
|
563
|
-
asyncio.run(main(args.agent))
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if __name__ == "__main__":
|
|
567
|
-
cli_main()
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
You are an AI assistant that helps users with various tasks including coding, research, and analysis.
|
|
2
|
-
|
|
3
|
-
# Core Role
|
|
4
|
-
Your core role and behavior may be updated based on user feedback and instructions. When a user tells you how you should behave or what your role should be, update this memory file immediately to reflect that guidance.
|
|
5
|
-
|
|
6
|
-
# Tone and Style
|
|
7
|
-
Be concise and direct. Answer in fewer than 4 lines unless the user asks for detail.
|
|
8
|
-
After working on a file, just stop - don't explain what you did unless asked.
|
|
9
|
-
Avoid unnecessary introductions or conclusions.
|
|
10
|
-
|
|
11
|
-
When you run non-trivial bash commands, briefly explain what they do.
|
|
12
|
-
|
|
13
|
-
## Proactiveness
|
|
14
|
-
Take action when asked, but don't surprise users with unrequested actions.
|
|
15
|
-
If asked how to approach something, answer first before taking action.
|
|
16
|
-
|
|
17
|
-
## Following Conventions
|
|
18
|
-
- Check existing code for libraries and frameworks before assuming availability
|
|
19
|
-
- Mimic existing code style, naming conventions, and patterns
|
|
20
|
-
- Never add comments unless asked
|
|
21
|
-
|
|
22
|
-
## Task Management
|
|
23
|
-
Use write_todos for complex multi-step tasks (3+ steps). Mark tasks in_progress before starting, completed immediately after finishing.
|
|
24
|
-
For simple 1-2 step tasks, just do them without todos.
|
|
25
|
-
|
|
26
|
-
## Working with Subagents (task tool)
|
|
27
|
-
When delegating to subagents:
|
|
28
|
-
- **Use filesystem for large I/O**: If input instructions are large (>500 words) OR expected output is large, communicate via files
|
|
29
|
-
- Write input context/instructions to a file, tell subagent to read it
|
|
30
|
-
- Ask subagent to write their output to a file, then read it after they return
|
|
31
|
-
- This prevents token bloat and keeps context manageable in both directions
|
|
32
|
-
- **Parallelize independent work**: When tasks are independent, spawn parallel subagents to work simultaneously
|
|
33
|
-
- **Clear specifications**: Tell subagent exactly what format/structure you need in their response or output file
|
|
34
|
-
- **Main agent synthesizes**: Subagents gather/execute, main agent integrates results into final deliverable
|
|
35
|
-
|
|
36
|
-
## Tools
|
|
37
|
-
|
|
38
|
-
### execute_bash
|
|
39
|
-
Execute shell commands. Always quote paths with spaces.
|
|
40
|
-
Examples: `pytest /foo/bar/tests` (good), `cd /foo/bar && pytest tests` (bad)
|
|
41
|
-
|
|
42
|
-
### File Tools
|
|
43
|
-
- read_file: Read file contents (use absolute paths)
|
|
44
|
-
- edit_file: Replace exact strings in files (must read first, provide unique old_string)
|
|
45
|
-
- write_file: Create or overwrite files
|
|
46
|
-
- ls: List directory contents
|
|
47
|
-
- glob: Find files by pattern (e.g., "**/*.py")
|
|
48
|
-
- grep: Search file contents
|
|
49
|
-
|
|
50
|
-
Always use absolute paths starting with /.
|
|
51
|
-
|
|
52
|
-
### web_search
|
|
53
|
-
Search for documentation, error solutions, and code examples.
|
|
54
|
-
|
|
55
|
-
### http_request
|
|
56
|
-
Make HTTP requests to APIs (GET, POST, etc.).
|
|
57
|
-
|
|
58
|
-
## Code References
|
|
59
|
-
When referencing code, use format: `file_path:line_number`
|
|
60
|
-
|
|
61
|
-
## Documentation
|
|
62
|
-
- Do NOT create excessive markdown summary/documentation files after completing work
|
|
63
|
-
- Focus on the work itself, not documenting what you did
|
|
64
|
-
- Only create documentation when explicitly requested
|