deepagents-cli 0.0.1__py3-none-any.whl → 0.0.3__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/__init__.py +1 -12
- deepagents/cli.py +257 -272
- deepagents/default_agent_prompt.md +0 -27
- deepagents/graph.py +16 -40
- deepagents/memory/__init__.py +17 -0
- deepagents/memory/backends/__init__.py +15 -0
- deepagents/memory/backends/composite.py +250 -0
- deepagents/memory/backends/filesystem.py +330 -0
- deepagents/memory/backends/state.py +206 -0
- deepagents/memory/backends/store.py +351 -0
- deepagents/memory/backends/utils.py +319 -0
- deepagents/memory/protocol.py +164 -0
- deepagents/middleware/__init__.py +3 -3
- deepagents/middleware/agent_memory.py +207 -0
- deepagents/middleware/filesystem.py +229 -773
- deepagents/middleware/patch_tool_calls.py +44 -0
- deepagents/middleware/subagents.py +7 -6
- deepagents/pretty_cli.py +289 -0
- {deepagents_cli-0.0.1.dist-info → deepagents_cli-0.0.3.dist-info}/METADATA +26 -30
- deepagents_cli-0.0.3.dist-info/RECORD +24 -0
- deepagents/middleware/common.py +0 -16
- deepagents/middleware/local_filesystem.py +0 -741
- deepagents/prompts.py +0 -327
- deepagents/skills.py +0 -85
- deepagents_cli-0.0.1.dist-info/RECORD +0 -17
- {deepagents_cli-0.0.1.dist-info → deepagents_cli-0.0.3.dist-info}/WHEEL +0 -0
- {deepagents_cli-0.0.1.dist-info → deepagents_cli-0.0.3.dist-info}/entry_points.txt +0 -0
- {deepagents_cli-0.0.1.dist-info → deepagents_cli-0.0.3.dist-info}/licenses/LICENSE +0 -0
- {deepagents_cli-0.0.1.dist-info → deepagents_cli-0.0.3.dist-info}/top_level.txt +0 -0
deepagents/cli.py
CHANGED
|
@@ -11,16 +11,16 @@ from pathlib import Path
|
|
|
11
11
|
from tavily import TavilyClient
|
|
12
12
|
from deepagents import create_deep_agent
|
|
13
13
|
from langgraph.checkpoint.memory import InMemorySaver
|
|
14
|
-
from
|
|
14
|
+
from langchain.agents.middleware import ShellToolMiddleware, HostExecutionPolicy
|
|
15
15
|
|
|
16
16
|
from rich.console import Console
|
|
17
17
|
from rich.markdown import Markdown
|
|
18
18
|
from rich.panel import Panel
|
|
19
|
-
from rich.syntax import Syntax
|
|
20
|
-
from rich.text import Text
|
|
21
|
-
from rich.live import Live
|
|
22
19
|
from rich.spinner import Spinner
|
|
23
|
-
from
|
|
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
24
|
import shutil
|
|
25
25
|
from rich import box
|
|
26
26
|
|
|
@@ -28,6 +28,31 @@ import dotenv
|
|
|
28
28
|
|
|
29
29
|
dotenv.load_dotenv()
|
|
30
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
|
+
|
|
31
56
|
console = Console()
|
|
32
57
|
|
|
33
58
|
tavily_client = TavilyClient(api_key=os.environ.get("TAVILY_API_KEY")) if os.environ.get("TAVILY_API_KEY") else None
|
|
@@ -137,62 +162,19 @@ def web_search(
|
|
|
137
162
|
}
|
|
138
163
|
|
|
139
164
|
|
|
140
|
-
def get_default_coding_instructions(
|
|
165
|
+
def get_default_coding_instructions() -> str:
|
|
141
166
|
"""Get the default coding agent instructions.
|
|
142
167
|
|
|
143
|
-
|
|
144
|
-
|
|
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.
|
|
145
170
|
"""
|
|
146
171
|
default_prompt_path = Path(__file__).parent / "default_agent_prompt.md"
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if not include_memory:
|
|
150
|
-
# Remove the Long-term Memory section
|
|
151
|
-
lines = prompt.split('\n')
|
|
152
|
-
result_lines = []
|
|
153
|
-
skip = False
|
|
154
|
-
|
|
155
|
-
for line in lines:
|
|
156
|
-
if line.strip() == "## Long-term Memory":
|
|
157
|
-
skip = True
|
|
158
|
-
continue
|
|
159
|
-
if skip and line.startswith('##'):
|
|
160
|
-
skip = False
|
|
161
|
-
if not skip:
|
|
162
|
-
result_lines.append(line)
|
|
163
|
-
|
|
164
|
-
prompt = '\n'.join(result_lines).rstrip() + '\n'
|
|
165
|
-
|
|
166
|
-
return prompt
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
def get_coding_instructions(agent_name: str | None, long_term_memory: bool = False) -> str:
|
|
170
|
-
"""Get the coding agent instructions from file or create default.
|
|
171
|
-
|
|
172
|
-
If agent_name is None or long_term_memory is False, returns default instructions without memory features.
|
|
173
|
-
"""
|
|
174
|
-
if agent_name is None or not long_term_memory:
|
|
175
|
-
return get_default_coding_instructions(include_memory=False)
|
|
176
|
-
|
|
177
|
-
agent_dir = Path.home() / ".deepagents" / agent_name
|
|
178
|
-
agent_prompt_file = agent_dir / "agent.md"
|
|
179
|
-
|
|
180
|
-
if agent_prompt_file.exists():
|
|
181
|
-
agent_memory_content = agent_prompt_file.read_text()
|
|
182
|
-
else:
|
|
183
|
-
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
184
|
-
default_prompt = get_default_coding_instructions(include_memory=True)
|
|
185
|
-
agent_prompt_file.write_text(default_prompt)
|
|
186
|
-
agent_memory_content = default_prompt
|
|
187
|
-
|
|
188
|
-
return f"<agent_memory>\n{agent_memory_content}\n</agent_memory>"
|
|
172
|
+
return default_prompt_path.read_text()
|
|
189
173
|
|
|
190
174
|
|
|
191
175
|
config = {"recursion_limit": 1000}
|
|
192
176
|
|
|
193
|
-
|
|
194
|
-
MAX_ARG_LENGTH = 200
|
|
195
|
-
MAX_RESULT_LENGTH = 200
|
|
177
|
+
MAX_ARG_LENGTH = 150
|
|
196
178
|
|
|
197
179
|
|
|
198
180
|
def truncate_value(value: str, max_length: int = MAX_ARG_LENGTH) -> str:
|
|
@@ -202,22 +184,22 @@ def truncate_value(value: str, max_length: int = MAX_ARG_LENGTH) -> str:
|
|
|
202
184
|
return value
|
|
203
185
|
|
|
204
186
|
|
|
205
|
-
def
|
|
206
|
-
"""
|
|
207
|
-
|
|
208
|
-
return ""
|
|
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()
|
|
209
190
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
args_parts.append(f"{key}={value_str}")
|
|
191
|
+
config = {
|
|
192
|
+
"configurable": {"thread_id": "main"},
|
|
193
|
+
"metadata": {"assistant_id": assistant_id} if assistant_id else {}
|
|
194
|
+
}
|
|
215
195
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
|
221
203
|
|
|
222
204
|
tool_icons = {
|
|
223
205
|
"read_file": "📖",
|
|
@@ -233,52 +215,6 @@ def display_tool_call(tool_name: str, tool_input: dict):
|
|
|
233
215
|
"write_todos": "📋",
|
|
234
216
|
}
|
|
235
217
|
|
|
236
|
-
icon = tool_icons.get(tool_name, "🔧")
|
|
237
|
-
args_str = format_tool_args(tool_input)
|
|
238
|
-
|
|
239
|
-
# Display: icon tool_name(args)
|
|
240
|
-
if args_str:
|
|
241
|
-
console.print(f"[dim]{icon} {tool_name}({args_str})[/dim]")
|
|
242
|
-
else:
|
|
243
|
-
console.print(f"[dim]{icon} {tool_name}()[/dim]")
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def display_text_content(text: str):
|
|
247
|
-
"""Display text content with markdown rendering."""
|
|
248
|
-
if text.strip():
|
|
249
|
-
if "```" in text or "#" in text or "**" in text:
|
|
250
|
-
md = Markdown(text)
|
|
251
|
-
console.print(md)
|
|
252
|
-
else:
|
|
253
|
-
console.print(text, style="white")
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def extract_and_display_content(message_content):
|
|
257
|
-
"""Extract content from agent messages and display with rich formatting."""
|
|
258
|
-
if isinstance(message_content, str):
|
|
259
|
-
display_text_content(message_content)
|
|
260
|
-
return
|
|
261
|
-
|
|
262
|
-
if isinstance(message_content, list):
|
|
263
|
-
for block in message_content:
|
|
264
|
-
if isinstance(block, dict):
|
|
265
|
-
if block.get("type") == "text" and "text" in block:
|
|
266
|
-
display_text_content(block["text"])
|
|
267
|
-
elif block.get("type") == "tool_use":
|
|
268
|
-
tool_name = block.get("name", "unknown_tool")
|
|
269
|
-
tool_input = block.get("input", {})
|
|
270
|
-
display_tool_call(tool_name, tool_input)
|
|
271
|
-
# Skip tool_result blocks - they're just noise
|
|
272
|
-
elif block.get("type") == "tool_result":
|
|
273
|
-
pass # Don't display tool results
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
def execute_task(user_input: str, agent, agent_name: str | None):
|
|
277
|
-
"""Execute any task by passing it directly to the AI agent."""
|
|
278
|
-
console.print()
|
|
279
|
-
|
|
280
|
-
config = {"configurable": {"thread_id": "main", "agent_name": agent_name}} if agent_name else {"configurable": {"thread_id": "main"}}
|
|
281
|
-
|
|
282
218
|
for _, chunk in agent.stream(
|
|
283
219
|
{"messages": [{"role": "user", "content": user_input}]},
|
|
284
220
|
stream_mode="updates",
|
|
@@ -286,112 +222,144 @@ def execute_task(user_input: str, agent, agent_name: str | None):
|
|
|
286
222
|
config=config,
|
|
287
223
|
durability="exit",
|
|
288
224
|
):
|
|
289
|
-
|
|
290
|
-
if
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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 = ""
|
|
295
241
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
icon = tool_icons.get(tool_name, "🔧")
|
|
334
|
-
args_str = format_tool_args(tool_input)
|
|
335
|
-
|
|
336
|
-
if args_str:
|
|
337
|
-
console.print(f"[dim]{icon} {tool_name}({args_str})[/dim]")
|
|
338
|
-
else:
|
|
339
|
-
console.print(f"[dim]{icon} {tool_name}()[/dim]")
|
|
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", {})
|
|
340
277
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
console.print(f"[dim] → {result_str}[/dim]")
|
|
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
|
+
)
|
|
346
282
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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):
|
|
367
328
|
"""Main CLI loop."""
|
|
329
|
+
console.clear()
|
|
330
|
+
console.print(DEEP_AGENTS_ASCII, style=f"bold {COLORS['primary']}")
|
|
368
331
|
console.print()
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
)
|
|
374
|
-
|
|
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']}")
|
|
375
343
|
console.print()
|
|
376
344
|
|
|
377
345
|
while True:
|
|
378
346
|
try:
|
|
379
|
-
console.print("
|
|
347
|
+
console.print(f"> ", style=COLORS["user"], end="")
|
|
380
348
|
user_input = input().strip()
|
|
381
349
|
except EOFError:
|
|
382
350
|
break
|
|
351
|
+
except KeyboardInterrupt:
|
|
352
|
+
console.print()
|
|
353
|
+
break
|
|
383
354
|
|
|
384
355
|
if not user_input:
|
|
385
356
|
continue
|
|
386
357
|
|
|
387
358
|
if user_input.lower() in ["quit", "exit", "q"]:
|
|
388
|
-
console.print("\
|
|
359
|
+
console.print(f"\nGoodbye!", style=COLORS["primary"])
|
|
389
360
|
break
|
|
390
361
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
else:
|
|
394
|
-
execute_task(user_input, agent, agent_name)
|
|
362
|
+
execute_task(user_input, agent, assistant_id)
|
|
395
363
|
|
|
396
364
|
|
|
397
365
|
def list_agents():
|
|
@@ -400,10 +368,10 @@ def list_agents():
|
|
|
400
368
|
|
|
401
369
|
if not agents_dir.exists() or not any(agents_dir.iterdir()):
|
|
402
370
|
console.print("[yellow]No agents found.[/yellow]")
|
|
403
|
-
console.print("[dim]Agents will be created in ~/.deepagents/ when you first use them.[/dim]")
|
|
371
|
+
console.print(f"[dim]Agents will be created in ~/.deepagents/ when you first use them.[/dim]", style=COLORS["dim"])
|
|
404
372
|
return
|
|
405
373
|
|
|
406
|
-
console.print("\n[bold
|
|
374
|
+
console.print(f"\n[bold]Available Agents:[/bold]\n", style=COLORS["primary"])
|
|
407
375
|
|
|
408
376
|
for agent_path in sorted(agents_dir.iterdir()):
|
|
409
377
|
if agent_path.is_dir():
|
|
@@ -411,11 +379,11 @@ def list_agents():
|
|
|
411
379
|
agent_md = agent_path / "agent.md"
|
|
412
380
|
|
|
413
381
|
if agent_md.exists():
|
|
414
|
-
console.print(f"
|
|
415
|
-
console.print(f"
|
|
382
|
+
console.print(f" • [bold]{agent_name}[/bold]", style=COLORS["primary"])
|
|
383
|
+
console.print(f" {agent_path}", style=COLORS["dim"])
|
|
416
384
|
else:
|
|
417
|
-
console.print(f"
|
|
418
|
-
console.print(f"
|
|
385
|
+
console.print(f" • [bold]{agent_name}[/bold] [dim](incomplete)[/dim]", style=COLORS["tool"])
|
|
386
|
+
console.print(f" {agent_path}", style=COLORS["dim"])
|
|
419
387
|
|
|
420
388
|
console.print()
|
|
421
389
|
|
|
@@ -437,58 +405,57 @@ def reset_agent(agent_name: str, source_agent: str = None):
|
|
|
437
405
|
action_desc = f"contents of agent '{source_agent}'"
|
|
438
406
|
else:
|
|
439
407
|
source_content = get_default_coding_instructions()
|
|
440
|
-
action_desc = "default
|
|
408
|
+
action_desc = "default"
|
|
441
409
|
|
|
442
410
|
if agent_dir.exists():
|
|
443
411
|
shutil.rmtree(agent_dir)
|
|
444
|
-
console.print(f"
|
|
412
|
+
console.print(f"Removed existing agent directory: {agent_dir}", style=COLORS["tool"])
|
|
445
413
|
|
|
446
414
|
agent_dir.mkdir(parents=True, exist_ok=True)
|
|
447
415
|
agent_md = agent_dir / "agent.md"
|
|
448
416
|
agent_md.write_text(source_content)
|
|
449
417
|
|
|
450
|
-
console.print(f"
|
|
451
|
-
console.print(f"
|
|
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"])
|
|
452
420
|
|
|
453
421
|
|
|
454
422
|
def show_help():
|
|
455
423
|
"""Show help information."""
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
deepagents
|
|
462
|
-
deepagents
|
|
463
|
-
deepagents reset --agent AGENT
|
|
464
|
-
deepagents
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
deepagents
|
|
470
|
-
deepagents
|
|
471
|
-
deepagents
|
|
472
|
-
deepagents reset --agent mybot
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
[bold]Agent Storage:[/bold]
|
|
484
|
-
Agents are stored in: ~/.deepagents/AGENT_NAME/
|
|
485
|
-
Each agent has an agent.md file containing its prompt
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
console.print(help_text)
|
|
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()
|
|
492
459
|
|
|
493
460
|
|
|
494
461
|
def parse_args():
|
|
@@ -519,47 +486,59 @@ def parse_args():
|
|
|
519
486
|
help="Agent identifier for separate memory stores (default: agent).",
|
|
520
487
|
)
|
|
521
488
|
|
|
522
|
-
parser.add_argument(
|
|
523
|
-
"--no-memory",
|
|
524
|
-
action="store_true",
|
|
525
|
-
help="Disable long-term memory features (no agent.md or /memories/ access).",
|
|
526
|
-
)
|
|
527
|
-
|
|
528
489
|
return parser.parse_args()
|
|
529
490
|
|
|
530
491
|
|
|
531
|
-
async def main(
|
|
492
|
+
async def main(assistant_id: str):
|
|
532
493
|
"""Main entry point."""
|
|
533
|
-
# Error if both --agent and --no-memory are specified
|
|
534
|
-
if agent_name != "agent" and no_memory:
|
|
535
|
-
console.print("[bold red]Error:[/bold red] Cannot use --agent with --no-memory flag.")
|
|
536
|
-
console.print("Either specify --agent for memory-enabled mode, or use --no-memory for memory-disabled mode.")
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
# Disable long-term memory if --no-memory flag is set
|
|
540
|
-
long_term_memory = not no_memory
|
|
541
|
-
|
|
542
|
-
# If memory is disabled, set agent_name to None
|
|
543
|
-
effective_agent_name = None if no_memory else agent_name
|
|
544
494
|
|
|
545
495
|
# Create agent with conditional tools
|
|
546
496
|
tools = [http_request]
|
|
547
497
|
if tavily_client is not None:
|
|
548
498
|
tools.append(web_search)
|
|
549
|
-
|
|
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
|
+
|
|
550
529
|
agent = create_deep_agent(
|
|
530
|
+
system_prompt=system_prompt,
|
|
551
531
|
tools=tools,
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
long_term_memory=long_term_memory,
|
|
532
|
+
memory_backend=backend,
|
|
533
|
+
middleware=agent_middleware,
|
|
555
534
|
).with_config(config)
|
|
556
535
|
|
|
557
536
|
agent.checkpointer = InMemorySaver()
|
|
558
537
|
|
|
559
538
|
try:
|
|
560
|
-
await simple_cli(agent,
|
|
539
|
+
await simple_cli(agent, assistant_id)
|
|
561
540
|
except KeyboardInterrupt:
|
|
562
|
-
console.print("\n\
|
|
541
|
+
console.print(f"\n\nGoodbye!", style=COLORS["primary"])
|
|
563
542
|
except Exception as e:
|
|
564
543
|
console.print(f"\n[bold red]❌ Error:[/bold red] {e}\n")
|
|
565
544
|
|
|
@@ -575,8 +554,14 @@ def cli_main():
|
|
|
575
554
|
elif args.command == "reset":
|
|
576
555
|
reset_agent(args.agent, args.source_agent)
|
|
577
556
|
else:
|
|
578
|
-
|
|
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))
|
|
579
564
|
|
|
580
565
|
|
|
581
566
|
if __name__ == "__main__":
|
|
582
|
-
cli_main()
|
|
567
|
+
cli_main()
|