deepagents-cli 0.0.3__py3-none-any.whl → 0.0.5__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.

Files changed (42) hide show
  1. deepagents_cli/__init__.py +5 -0
  2. deepagents_cli/__main__.py +6 -0
  3. deepagents_cli/agent.py +278 -0
  4. deepagents_cli/cli.py +13 -0
  5. deepagents_cli/commands.py +89 -0
  6. deepagents_cli/config.py +138 -0
  7. deepagents_cli/execution.py +644 -0
  8. deepagents_cli/file_ops.py +347 -0
  9. deepagents_cli/input.py +249 -0
  10. deepagents_cli/main.py +226 -0
  11. deepagents_cli/py.typed +0 -0
  12. deepagents_cli/token_utils.py +63 -0
  13. deepagents_cli/tools.py +140 -0
  14. deepagents_cli/ui.py +489 -0
  15. deepagents_cli-0.0.5.dist-info/METADATA +18 -0
  16. deepagents_cli-0.0.5.dist-info/RECORD +19 -0
  17. deepagents_cli-0.0.5.dist-info/entry_points.txt +3 -0
  18. deepagents_cli-0.0.5.dist-info/top_level.txt +1 -0
  19. deepagents/__init__.py +0 -7
  20. deepagents/cli.py +0 -567
  21. deepagents/default_agent_prompt.md +0 -64
  22. deepagents/graph.py +0 -144
  23. deepagents/memory/__init__.py +0 -17
  24. deepagents/memory/backends/__init__.py +0 -15
  25. deepagents/memory/backends/composite.py +0 -250
  26. deepagents/memory/backends/filesystem.py +0 -330
  27. deepagents/memory/backends/state.py +0 -206
  28. deepagents/memory/backends/store.py +0 -351
  29. deepagents/memory/backends/utils.py +0 -319
  30. deepagents/memory/protocol.py +0 -164
  31. deepagents/middleware/__init__.py +0 -13
  32. deepagents/middleware/agent_memory.py +0 -207
  33. deepagents/middleware/filesystem.py +0 -615
  34. deepagents/middleware/patch_tool_calls.py +0 -44
  35. deepagents/middleware/subagents.py +0 -481
  36. deepagents/pretty_cli.py +0 -289
  37. deepagents_cli-0.0.3.dist-info/METADATA +0 -551
  38. deepagents_cli-0.0.3.dist-info/RECORD +0 -24
  39. deepagents_cli-0.0.3.dist-info/entry_points.txt +0 -2
  40. deepagents_cli-0.0.3.dist-info/licenses/LICENSE +0 -21
  41. deepagents_cli-0.0.3.dist-info/top_level.txt +0 -1
  42. {deepagents_cli-0.0.3.dist-info → deepagents_cli-0.0.5.dist-info}/WHEEL +0 -0
deepagents_cli/main.py ADDED
@@ -0,0 +1,226 @@
1
+ """Main entry point and CLI loop for deepagents."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from .agent import create_agent_with_config, list_agents, reset_agent
9
+ from .commands import execute_bash_command, handle_command
10
+ from .config import COLORS, DEEP_AGENTS_ASCII, SessionState, console, create_model
11
+ from .execution import execute_task
12
+ from .input import create_prompt_session
13
+ from .tools import http_request, tavily_client, web_search
14
+ from .ui import TokenTracker, show_help
15
+
16
+
17
+ def check_cli_dependencies():
18
+ """Check if CLI optional dependencies are installed."""
19
+ missing = []
20
+
21
+ try:
22
+ import rich
23
+ except ImportError:
24
+ missing.append("rich")
25
+
26
+ try:
27
+ import requests
28
+ except ImportError:
29
+ missing.append("requests")
30
+
31
+ try:
32
+ import dotenv
33
+ except ImportError:
34
+ missing.append("python-dotenv")
35
+
36
+ try:
37
+ import tavily
38
+ except ImportError:
39
+ missing.append("tavily-python")
40
+
41
+ try:
42
+ import prompt_toolkit
43
+ except ImportError:
44
+ missing.append("prompt-toolkit")
45
+
46
+ if missing:
47
+ print("\n❌ Missing required CLI dependencies!")
48
+ print("\nThe following packages are required to use the deepagents CLI:")
49
+ for pkg in missing:
50
+ print(f" - {pkg}")
51
+ print("\nPlease install them with:")
52
+ print(" pip install deepagents[cli]")
53
+ print("\nOr install all dependencies:")
54
+ print(" pip install 'deepagents[cli]'")
55
+ sys.exit(1)
56
+
57
+
58
+ def parse_args():
59
+ """Parse command line arguments."""
60
+ parser = argparse.ArgumentParser(
61
+ description="DeepAgents - AI Coding Assistant",
62
+ formatter_class=argparse.RawDescriptionHelpFormatter,
63
+ add_help=False,
64
+ )
65
+
66
+ subparsers = parser.add_subparsers(dest="command", help="Command to run")
67
+
68
+ # List command
69
+ subparsers.add_parser("list", help="List all available agents")
70
+
71
+ # Help command
72
+ subparsers.add_parser("help", help="Show help information")
73
+
74
+ # Reset command
75
+ reset_parser = subparsers.add_parser("reset", help="Reset an agent")
76
+ reset_parser.add_argument("--agent", required=True, help="Name of agent to reset")
77
+ reset_parser.add_argument(
78
+ "--target", dest="source_agent", help="Copy prompt from another agent"
79
+ )
80
+
81
+ # Default interactive mode
82
+ parser.add_argument(
83
+ "--agent",
84
+ default="agent",
85
+ help="Agent identifier for separate memory stores (default: agent).",
86
+ )
87
+ parser.add_argument(
88
+ "--auto-approve",
89
+ action="store_true",
90
+ help="Auto-approve tool usage without prompting (disables human-in-the-loop)",
91
+ )
92
+
93
+ return parser.parse_args()
94
+
95
+
96
+ async def simple_cli(agent, assistant_id: str | None, session_state, baseline_tokens: int = 0):
97
+ """Main CLI loop."""
98
+ console.clear()
99
+ console.print(DEEP_AGENTS_ASCII, style=f"bold {COLORS['primary']}")
100
+ console.print()
101
+
102
+ if tavily_client is None:
103
+ console.print(
104
+ "[yellow]⚠ Web search disabled:[/yellow] TAVILY_API_KEY not found.",
105
+ style=COLORS["dim"],
106
+ )
107
+ console.print(" To enable web search, set your Tavily API key:", style=COLORS["dim"])
108
+ console.print(" export TAVILY_API_KEY=your_api_key_here", style=COLORS["dim"])
109
+ console.print(
110
+ " Or add it to your .env file. Get your key at: https://tavily.com",
111
+ style=COLORS["dim"],
112
+ )
113
+ console.print()
114
+
115
+ console.print("... Ready to code! What would you like to build?", style=COLORS["agent"])
116
+ console.print(f" [dim]Working directory: {Path.cwd()}[/dim]")
117
+ console.print()
118
+
119
+ if session_state.auto_approve:
120
+ console.print(
121
+ " [yellow]⚡ Auto-approve: ON[/yellow] [dim](tools run without confirmation)[/dim]"
122
+ )
123
+ console.print()
124
+
125
+ console.print(
126
+ " Tips: Enter to submit, Alt+Enter for newline, Ctrl+E for editor, Ctrl+T to toggle auto-approve, Ctrl+C to interrupt",
127
+ style=f"dim {COLORS['dim']}",
128
+ )
129
+ console.print()
130
+
131
+ # Create prompt session and token tracker
132
+ session = create_prompt_session(assistant_id, session_state)
133
+ token_tracker = TokenTracker()
134
+ token_tracker.set_baseline(baseline_tokens)
135
+
136
+ while True:
137
+ try:
138
+ user_input = await session.prompt_async()
139
+ user_input = user_input.strip()
140
+ except EOFError:
141
+ break
142
+ except KeyboardInterrupt:
143
+ # Ctrl+C at prompt - exit the program
144
+ console.print("\n\nGoodbye!", style=COLORS["primary"])
145
+ break
146
+
147
+ if not user_input:
148
+ continue
149
+
150
+ # Check for slash commands first
151
+ if user_input.startswith("/"):
152
+ result = handle_command(user_input, agent, token_tracker)
153
+ if result == "exit":
154
+ console.print("\nGoodbye!", style=COLORS["primary"])
155
+ break
156
+ if result:
157
+ # Command was handled, continue to next input
158
+ continue
159
+
160
+ # Check for bash commands (!)
161
+ if user_input.startswith("!"):
162
+ execute_bash_command(user_input)
163
+ continue
164
+
165
+ # Handle regular quit keywords
166
+ if user_input.lower() in ["quit", "exit", "q"]:
167
+ console.print("\nGoodbye!", style=COLORS["primary"])
168
+ break
169
+
170
+ execute_task(user_input, agent, assistant_id, session_state, token_tracker)
171
+
172
+
173
+ async def main(assistant_id: str, session_state):
174
+ """Main entry point."""
175
+ # Create the model (checks API keys)
176
+ model = create_model()
177
+
178
+ # Create agent with conditional tools
179
+ tools = [http_request]
180
+ if tavily_client is not None:
181
+ tools.append(web_search)
182
+
183
+ agent = create_agent_with_config(model, assistant_id, tools)
184
+
185
+ # Calculate baseline token count for accurate token tracking
186
+ from .agent import get_system_prompt
187
+ from .token_utils import calculate_baseline_tokens
188
+
189
+ agent_dir = Path.home() / ".deepagents" / assistant_id
190
+ system_prompt = get_system_prompt()
191
+ baseline_tokens = calculate_baseline_tokens(model, agent_dir, system_prompt)
192
+
193
+ try:
194
+ await simple_cli(agent, assistant_id, session_state, baseline_tokens)
195
+ except Exception as e:
196
+ console.print(f"\n[bold red]❌ Error:[/bold red] {e}\n")
197
+
198
+
199
+ def cli_main():
200
+ """Entry point for console script."""
201
+ # Check dependencies first
202
+ check_cli_dependencies()
203
+
204
+ try:
205
+ args = parse_args()
206
+
207
+ if args.command == "help":
208
+ show_help()
209
+ elif args.command == "list":
210
+ list_agents()
211
+ elif args.command == "reset":
212
+ reset_agent(args.agent, args.source_agent)
213
+ else:
214
+ # Create session state from args
215
+ session_state = SessionState(auto_approve=args.auto_approve)
216
+
217
+ # API key validation happens in create_model()
218
+ asyncio.run(main(args.agent, session_state))
219
+ except KeyboardInterrupt:
220
+ # Clean exit on Ctrl+C - suppress ugly traceback
221
+ console.print("\n\n[yellow]Interrupted[/yellow]")
222
+ sys.exit(0)
223
+
224
+
225
+ if __name__ == "__main__":
226
+ cli_main()
File without changes
@@ -0,0 +1,63 @@
1
+ """Utilities for accurate token counting using LangChain models."""
2
+
3
+ from pathlib import Path
4
+
5
+ from langchain_core.messages import SystemMessage
6
+
7
+ from .config import console
8
+
9
+
10
+ def calculate_baseline_tokens(model, agent_dir: Path, system_prompt: str) -> int:
11
+ """Calculate baseline context tokens using the model's official tokenizer.
12
+
13
+ This uses the model's get_num_tokens_from_messages() method to get
14
+ accurate token counts for the initial context (system prompt + agent.md).
15
+
16
+ Note: Tool definitions cannot be accurately counted before the first API call
17
+ due to LangChain limitations. They will be included in the total after the
18
+ first message is sent (~5,000 tokens).
19
+
20
+ Args:
21
+ model: LangChain model instance (ChatAnthropic or ChatOpenAI)
22
+ agent_dir: Path to agent directory containing agent.md
23
+ system_prompt: The base system prompt string
24
+
25
+ Returns:
26
+ Token count for system prompt + agent.md (tools not included)
27
+ """
28
+ # Load agent.md content
29
+ agent_md_path = agent_dir / "agent.md"
30
+ agent_memory = ""
31
+ if agent_md_path.exists():
32
+ agent_memory = agent_md_path.read_text()
33
+
34
+ # Build the complete system prompt as it will be sent
35
+ # This mimics what AgentMemoryMiddleware.wrap_model_call() does
36
+ memory_section = f"<agent_memory>\n{agent_memory}\n</agent_memory>"
37
+
38
+ # Get the long-term memory system prompt
39
+ memory_system_prompt = get_memory_system_prompt()
40
+
41
+ # Combine all parts in the same order as the middleware
42
+ full_system_prompt = memory_section + "\n\n" + system_prompt + "\n\n" + memory_system_prompt
43
+
44
+ # Count tokens using the model's official method
45
+ messages = [SystemMessage(content=full_system_prompt)]
46
+
47
+ try:
48
+ # Note: tools parameter is not supported by LangChain's token counting
49
+ # Tool tokens will be included in the API response after first message
50
+ token_count = model.get_num_tokens_from_messages(messages)
51
+ return token_count
52
+ except Exception as e:
53
+ # Fallback if token counting fails
54
+ console.print(f"[yellow]Warning: Could not calculate baseline tokens: {e}[/yellow]")
55
+ return 0
56
+
57
+
58
+ def get_memory_system_prompt() -> str:
59
+ """Get the long-term memory system prompt text."""
60
+ # Import from agent_memory middleware
61
+ from deepagents.middleware.agent_memory import LONGTERM_MEMORY_SYSTEM_PROMPT
62
+
63
+ return LONGTERM_MEMORY_SYSTEM_PROMPT.format(memory_path="/memories/")
@@ -0,0 +1,140 @@
1
+ """Custom tools for the CLI agent."""
2
+
3
+ import os
4
+ from typing import Any, Literal
5
+
6
+ import requests
7
+ from tavily import TavilyClient
8
+
9
+ # Initialize Tavily client if API key is available
10
+ tavily_client = (
11
+ TavilyClient(api_key=os.environ.get("TAVILY_API_KEY"))
12
+ if os.environ.get("TAVILY_API_KEY")
13
+ else None
14
+ )
15
+
16
+
17
+ def http_request(
18
+ url: str,
19
+ method: str = "GET",
20
+ headers: dict[str, str] = None,
21
+ data: str | dict = None,
22
+ params: dict[str, str] = None,
23
+ timeout: int = 30,
24
+ ) -> dict[str, Any]:
25
+ """Make HTTP requests to APIs and web services.
26
+
27
+ Args:
28
+ url: Target URL
29
+ method: HTTP method (GET, POST, PUT, DELETE, etc.)
30
+ headers: HTTP headers to include
31
+ data: Request body data (string or dict)
32
+ params: URL query parameters
33
+ timeout: Request timeout in seconds
34
+
35
+ Returns:
36
+ Dictionary with response data including status, headers, and content
37
+ """
38
+ try:
39
+ kwargs = {"url": url, "method": method.upper(), "timeout": timeout}
40
+
41
+ if headers:
42
+ kwargs["headers"] = headers
43
+ if params:
44
+ kwargs["params"] = params
45
+ if data:
46
+ if isinstance(data, dict):
47
+ kwargs["json"] = data
48
+ else:
49
+ kwargs["data"] = data
50
+
51
+ response = requests.request(**kwargs)
52
+
53
+ try:
54
+ content = response.json()
55
+ except:
56
+ content = response.text
57
+
58
+ return {
59
+ "success": response.status_code < 400,
60
+ "status_code": response.status_code,
61
+ "headers": dict(response.headers),
62
+ "content": content,
63
+ "url": response.url,
64
+ }
65
+
66
+ except requests.exceptions.Timeout:
67
+ return {
68
+ "success": False,
69
+ "status_code": 0,
70
+ "headers": {},
71
+ "content": f"Request timed out after {timeout} seconds",
72
+ "url": url,
73
+ }
74
+ except requests.exceptions.RequestException as e:
75
+ return {
76
+ "success": False,
77
+ "status_code": 0,
78
+ "headers": {},
79
+ "content": f"Request error: {e!s}",
80
+ "url": url,
81
+ }
82
+ except Exception as e:
83
+ return {
84
+ "success": False,
85
+ "status_code": 0,
86
+ "headers": {},
87
+ "content": f"Error making request: {e!s}",
88
+ "url": url,
89
+ }
90
+
91
+
92
+ def web_search(
93
+ query: str,
94
+ max_results: int = 5,
95
+ topic: Literal["general", "news", "finance"] = "general",
96
+ include_raw_content: bool = False,
97
+ ):
98
+ """Search the web using Tavily for current information and documentation.
99
+
100
+ This tool searches the web and returns relevant results. After receiving results,
101
+ you MUST synthesize the information into a natural, helpful response for the user.
102
+
103
+ Args:
104
+ query: The search query (be specific and detailed)
105
+ max_results: Number of results to return (default: 5)
106
+ topic: Search topic type - "general" for most queries, "news" for current events
107
+ include_raw_content: Include full page content (warning: uses more tokens)
108
+
109
+ Returns:
110
+ Dictionary containing:
111
+ - results: List of search results, each with:
112
+ - title: Page title
113
+ - url: Page URL
114
+ - content: Relevant excerpt from the page
115
+ - score: Relevance score (0-1)
116
+ - query: The original search query
117
+
118
+ IMPORTANT: After using this tool:
119
+ 1. Read through the 'content' field of each result
120
+ 2. Extract relevant information that answers the user's question
121
+ 3. Synthesize this into a clear, natural language response
122
+ 4. Cite sources by mentioning the page titles or URLs
123
+ 5. NEVER show the raw JSON to the user - always provide a formatted response
124
+ """
125
+ if tavily_client is None:
126
+ return {
127
+ "error": "Tavily API key not configured. Please set TAVILY_API_KEY environment variable.",
128
+ "query": query,
129
+ }
130
+
131
+ try:
132
+ search_docs = tavily_client.search(
133
+ query,
134
+ max_results=max_results,
135
+ include_raw_content=include_raw_content,
136
+ topic=topic,
137
+ )
138
+ return search_docs
139
+ except Exception as e:
140
+ return {"error": f"Web search error: {e!s}", "query": query}