deepagents 0.2.3__py3-none-any.whl → 0.2.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.
Files changed (37) hide show
  1. deepagents/backends/__init__.py +1 -1
  2. deepagents/backends/composite.py +32 -42
  3. deepagents/backends/filesystem.py +92 -86
  4. deepagents/backends/protocol.py +39 -13
  5. deepagents/backends/state.py +59 -58
  6. deepagents/backends/store.py +74 -67
  7. deepagents/backends/utils.py +7 -21
  8. deepagents/graph.py +1 -1
  9. deepagents/middleware/filesystem.py +49 -47
  10. deepagents/middleware/resumable_shell.py +5 -4
  11. deepagents/middleware/subagents.py +1 -2
  12. {deepagents-0.2.3.dist-info → deepagents-0.2.5.dist-info}/METADATA +7 -7
  13. deepagents-0.2.5.dist-info/RECORD +38 -0
  14. deepagents-0.2.5.dist-info/top_level.txt +2 -0
  15. deepagents-cli/README.md +3 -0
  16. deepagents-cli/deepagents_cli/README.md +196 -0
  17. deepagents-cli/deepagents_cli/__init__.py +5 -0
  18. deepagents-cli/deepagents_cli/__main__.py +6 -0
  19. deepagents-cli/deepagents_cli/agent.py +278 -0
  20. {deepagents/middleware → deepagents-cli/deepagents_cli}/agent_memory.py +16 -12
  21. deepagents-cli/deepagents_cli/commands.py +89 -0
  22. deepagents-cli/deepagents_cli/config.py +118 -0
  23. deepagents-cli/deepagents_cli/execution.py +636 -0
  24. deepagents-cli/deepagents_cli/file_ops.py +347 -0
  25. deepagents-cli/deepagents_cli/input.py +270 -0
  26. deepagents-cli/deepagents_cli/main.py +226 -0
  27. deepagents-cli/deepagents_cli/py.typed +0 -0
  28. deepagents-cli/deepagents_cli/token_utils.py +63 -0
  29. deepagents-cli/deepagents_cli/tools.py +140 -0
  30. deepagents-cli/deepagents_cli/ui.py +489 -0
  31. deepagents-cli/tests/test_file_ops.py +119 -0
  32. deepagents-cli/tests/test_placeholder.py +5 -0
  33. deepagents-0.2.3.dist-info/RECORD +0 -21
  34. deepagents-0.2.3.dist-info/top_level.txt +0 -1
  35. {deepagents-0.2.3.dist-info → deepagents-0.2.5.dist-info}/WHEEL +0 -0
  36. {deepagents-0.2.3.dist-info → deepagents-0.2.5.dist-info}/licenses/LICENSE +0 -0
  37. {deepagents → deepagents-cli/deepagents_cli}/default_agent_prompt.md +0 -0
@@ -1,12 +1,9 @@
1
1
  """Middleware for providing filesystem tools to an agent."""
2
2
  # ruff: noqa: E501
3
3
 
4
- from collections.abc import Awaitable, Callable, Sequence
5
- from typing import Annotated
6
- from typing_extensions import NotRequired
7
-
8
4
  import os
9
- from typing import Literal, Optional
5
+ from collections.abc import Awaitable, Callable, Sequence
6
+ from typing import Annotated, Literal, NotRequired
10
7
 
11
8
  from langchain.agents.middleware.types import (
12
9
  AgentMiddleware,
@@ -21,14 +18,13 @@ from langchain_core.tools import BaseTool, tool
21
18
  from langgraph.types import Command
22
19
  from typing_extensions import TypedDict
23
20
 
24
- from deepagents.backends.protocol import BackendProtocol, BackendFactory, WriteResult, EditResult
25
21
  from deepagents.backends import StateBackend
22
+ from deepagents.backends.protocol import BackendFactory, BackendProtocol, EditResult, WriteResult
26
23
  from deepagents.backends.utils import (
27
- update_file_data,
28
24
  format_content_with_line_numbers,
29
25
  format_grep_matches,
30
- truncate_if_too_long,
31
26
  sanitize_tool_call_id,
27
+ truncate_if_too_long,
32
28
  )
33
29
 
34
30
  EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents"
@@ -36,10 +32,7 @@ MAX_LINE_LENGTH = 2000
36
32
  LINE_NUMBER_WIDTH = 6
37
33
  DEFAULT_READ_OFFSET = 0
38
34
  DEFAULT_READ_LIMIT = 500
39
- BACKEND_TYPES = (
40
- BackendProtocol
41
- | BackendFactory
42
- )
35
+ BACKEND_TYPES = BackendProtocol | BackendFactory
43
36
 
44
37
 
45
38
  class FileData(TypedDict):
@@ -135,6 +128,7 @@ def _validate_path(path: str, *, allowed_prefixes: Sequence[str] | None = None)
135
128
 
136
129
  return normalized
137
130
 
131
+
138
132
  class FilesystemState(AgentState):
139
133
  """State for the filesystem middleware."""
140
134
 
@@ -327,15 +321,17 @@ def _write_file_tool_generator(
327
321
  return res.error
328
322
  # If backend returns state update, wrap into Command with ToolMessage
329
323
  if res.files_update is not None:
330
- return Command(update={
331
- "files": res.files_update,
332
- "messages": [
333
- ToolMessage(
334
- content=f"Updated file {res.path}",
335
- tool_call_id=runtime.tool_call_id,
336
- )
337
- ],
338
- })
324
+ return Command(
325
+ update={
326
+ "files": res.files_update,
327
+ "messages": [
328
+ ToolMessage(
329
+ content=f"Updated file {res.path}",
330
+ tool_call_id=runtime.tool_call_id,
331
+ )
332
+ ],
333
+ }
334
+ )
339
335
  return f"Updated file {res.path}"
340
336
 
341
337
  return write_file
@@ -371,15 +367,17 @@ def _edit_file_tool_generator(
371
367
  if res.error:
372
368
  return res.error
373
369
  if res.files_update is not None:
374
- return Command(update={
375
- "files": res.files_update,
376
- "messages": [
377
- ToolMessage(
378
- content=f"Successfully replaced {res.occurrences} instance(s) of the string in '{res.path}'",
379
- tool_call_id=runtime.tool_call_id,
380
- )
381
- ],
382
- })
370
+ return Command(
371
+ update={
372
+ "files": res.files_update,
373
+ "messages": [
374
+ ToolMessage(
375
+ content=f"Successfully replaced {res.occurrences} instance(s) of the string in '{res.path}'",
376
+ tool_call_id=runtime.tool_call_id,
377
+ )
378
+ ],
379
+ }
380
+ )
383
381
  return f"Successfully replaced {res.occurrences} instance(s) of the string in '{res.path}'"
384
382
 
385
383
  return edit_file
@@ -428,7 +426,7 @@ def _grep_tool_generator(
428
426
  def grep(
429
427
  pattern: str,
430
428
  runtime: ToolRuntime[None, FilesystemState],
431
- path: Optional[str] = None,
429
+ path: str | None = None,
432
430
  glob: str | None = None,
433
431
  output_mode: Literal["files_with_matches", "content", "count"] = "files_with_matches",
434
432
  ) -> str:
@@ -509,10 +507,7 @@ class FilesystemMiddleware(AgentMiddleware):
509
507
  agent = create_agent(middleware=[FilesystemMiddleware()])
510
508
 
511
509
  # With hybrid storage (ephemeral + persistent /memories/)
512
- backend = CompositeBackend(
513
- default=StateBackend(),
514
- routes={"/memories/": StoreBackend()}
515
- )
510
+ backend = CompositeBackend(default=StateBackend(), routes={"/memories/": StoreBackend()})
516
511
  agent = create_agent(middleware=[FilesystemMiddleware(memory_backend=backend)])
517
512
  ```
518
513
  """
@@ -608,7 +603,7 @@ class FilesystemMiddleware(AgentMiddleware):
608
603
  result = resolved_backend.write(file_path, content)
609
604
  if result.error:
610
605
  return message, None
611
- content_sample = format_content_with_line_numbers(content.splitlines()[:10], start_line=1)
606
+ content_sample = format_content_with_line_numbers([line[:1000] for line in content.splitlines()[:10]], start_line=1)
612
607
  processed_message = ToolMessage(
613
608
  TOO_LARGE_TOOL_MSG.format(
614
609
  tool_call_id=message.tool_call_id,
@@ -621,20 +616,25 @@ class FilesystemMiddleware(AgentMiddleware):
621
616
 
622
617
  def _intercept_large_tool_result(self, tool_result: ToolMessage | Command, runtime: ToolRuntime) -> ToolMessage | Command:
623
618
  if isinstance(tool_result, ToolMessage) and isinstance(tool_result.content, str):
624
- if not (self.tool_token_limit_before_evict and
625
- len(tool_result.content) > 4 * self.tool_token_limit_before_evict):
619
+ if not (self.tool_token_limit_before_evict and len(tool_result.content) > 4 * self.tool_token_limit_before_evict):
626
620
  return tool_result
627
621
  resolved_backend = self._get_backend(runtime)
628
622
  processed_message, files_update = self._process_large_message(
629
623
  tool_result,
630
624
  resolved_backend,
631
625
  )
632
- return (Command(update={
633
- "files": files_update,
634
- "messages": [processed_message],
635
- }) if files_update is not None else processed_message)
626
+ return (
627
+ Command(
628
+ update={
629
+ "files": files_update,
630
+ "messages": [processed_message],
631
+ }
632
+ )
633
+ if files_update is not None
634
+ else processed_message
635
+ )
636
636
 
637
- elif isinstance(tool_result, Command):
637
+ if isinstance(tool_result, Command):
638
638
  update = tool_result.update
639
639
  if update is None:
640
640
  return tool_result
@@ -643,10 +643,12 @@ class FilesystemMiddleware(AgentMiddleware):
643
643
  resolved_backend = self._get_backend(runtime)
644
644
  processed_messages = []
645
645
  for message in command_messages:
646
- if not (self.tool_token_limit_before_evict and
647
- isinstance(message, ToolMessage) and
648
- isinstance(message.content, str) and
649
- len(message.content) > 4 * self.tool_token_limit_before_evict):
646
+ if not (
647
+ self.tool_token_limit_before_evict
648
+ and isinstance(message, ToolMessage)
649
+ and isinstance(message.content, str)
650
+ and len(message.content) > 4 * self.tool_token_limit_before_evict
651
+ ):
650
652
  processed_messages.append(message)
651
653
  continue
652
654
  processed_message, files_update = self._process_large_message(
@@ -2,17 +2,18 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Awaitable, Callable, cast
5
+ from collections.abc import Awaitable, Callable
6
+ from typing import Any, cast
6
7
 
7
8
  from langchain.agents.middleware.shell_tool import (
8
9
  ShellToolMiddleware,
10
+ ShellToolState,
9
11
  _PersistentShellTool,
10
12
  _SessionResources,
11
- ShellToolState,
12
13
  )
13
14
  from langchain.agents.middleware.types import AgentState
14
- from langchain_core.messages import ToolMessage
15
15
  from langchain.tools.tool_node import ToolCallRequest
16
+ from langchain_core.messages import ToolMessage
16
17
  from langgraph.types import Command
17
18
 
18
19
 
@@ -78,7 +79,7 @@ class ResumableShellToolMiddleware(ShellToolMiddleware):
78
79
  return resources
79
80
 
80
81
  new_resources = self._create_resources()
81
- cast(dict[str, Any], state)["shell_session_resources"] = new_resources
82
+ cast("dict[str, Any]", state)["shell_session_resources"] = new_resources
82
83
  return new_resources
83
84
 
84
85
 
@@ -1,8 +1,7 @@
1
1
  """Middleware for providing subagents to an agent via a `task` tool."""
2
2
 
3
3
  from collections.abc import Awaitable, Callable, Sequence
4
- from typing import Any, TypedDict, cast
5
- from typing_extensions import NotRequired
4
+ from typing import Any, NotRequired, TypedDict, cast
6
5
 
7
6
  from langchain.agents import create_agent
8
7
  from langchain.agents.middleware import HumanInTheLoopMiddleware, InterruptOnConfig
@@ -1,8 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepagents
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: General purpose 'deep agent' with sub-agent spawning, todo list capabilities, and mock file system. Built on LangGraph.
5
5
  License: MIT
6
+ Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
7
+ Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
8
+ Project-URL: Source, https://github.com/langchain-ai/deepagents
9
+ Project-URL: Twitter, https://x.com/LangChainAI
10
+ Project-URL: Slack, https://www.langchain.com/join-community
11
+ Project-URL: Reddit, https://www.reddit.com/r/LangChain/
6
12
  Requires-Python: <4.0,>=3.11
7
13
  Description-Content-Type: text/markdown
8
14
  License-File: LICENSE
@@ -10,12 +16,6 @@ Requires-Dist: langchain-anthropic<2.0.0,>=1.0.0
10
16
  Requires-Dist: langchain<2.0.0,>=1.0.2
11
17
  Requires-Dist: langchain-core<2.0.0,>=1.0.0
12
18
  Requires-Dist: wcmatch
13
- Provides-Extra: dev
14
- Requires-Dist: pytest; extra == "dev"
15
- Requires-Dist: pytest-cov; extra == "dev"
16
- Requires-Dist: build; extra == "dev"
17
- Requires-Dist: twine; extra == "dev"
18
- Requires-Dist: langchain-openai; extra == "dev"
19
19
  Dynamic: license-file
20
20
 
21
21
  # 🧠🤖Deep Agents
@@ -0,0 +1,38 @@
1
+ deepagents/__init__.py,sha256=9BVNn4lfF5N8l2KY8Ttxi82zO609I-fGqoSIF7DAxiU,342
2
+ deepagents/graph.py,sha256=pwRKF9EsmBtc2LabAcqted92kIhG5b4SaEfACT2G4Ps,6094
3
+ deepagents/backends/__init__.py,sha256=BOKu2cQ1OdMyO_l2rLqZQiXppYFmQbx7OIQb7WYwvZc,457
4
+ deepagents/backends/composite.py,sha256=0jvuo2YBlr3LT3N10uE0p1-vVSCdkzK0uLciPRcUg8o,8431
5
+ deepagents/backends/filesystem.py,sha256=SsVDx__j_AARIwRzaDuokbxbkquJ_Lw3Qi7dLWaqRUs,18674
6
+ deepagents/backends/protocol.py,sha256=wmXk24BBN1vo3ALYTIZmCi5fjxNZeH8y06v1OXc5ABU,4555
7
+ deepagents/backends/state.py,sha256=ST_tUExPxArJaA3U8vc1dyzxuYl2BQH-HU7P0eu_Ty8,6518
8
+ deepagents/backends/store.py,sha256=HWGZ0euXp9ww4HvWYg1S-tEH17cuFy3F1lg3DiGN6CY,13232
9
+ deepagents/backends/utils.py,sha256=Iyk2jW-gfoLvMnz-W_2FRCoJW_j3r1zoumU9iww-jd0,13973
10
+ deepagents/middleware/__init__.py,sha256=x7UHqGcrKlhzORNdChPvnUwa_PIJCbFUHY6zTKVfloI,418
11
+ deepagents/middleware/filesystem.py,sha256=7O2GUeGPQBslNBU1Gdser-tkL2DO_f8nZ9yZsGC3INs,28294
12
+ deepagents/middleware/patch_tool_calls.py,sha256=PdNhxPaQqwnFkhEAZEE2kEzadTNAOO3_iJRA30WqpGE,1981
13
+ deepagents/middleware/resumable_shell.py,sha256=KjhafjKu28Nf_8pDmSk_aWRK7pgkXZoubvWQljIEv3w,3382
14
+ deepagents/middleware/subagents.py,sha256=dI9w_TXKb4I6-InHua993sICTQ4fw5GapgNkHc-q50I,23474
15
+ deepagents-0.2.5.dist-info/licenses/LICENSE,sha256=c__BaxUCK69leo2yEKynf8lWndu8iwYwge1CbyqAe-E,1071
16
+ deepagents-cli/README.md,sha256=LQksI_Z2qG3IMbIZ7ZRBQlWTufPZmIevGt7uya_HObg,49
17
+ deepagents-cli/deepagents_cli/README.md,sha256=ajP946mBaKQRn_k3vViWfQn8v1BKiaw58gqb0xKmp4o,7413
18
+ deepagents-cli/deepagents_cli/__init__.py,sha256=2W9tHzQianR_Q0ku9hc_ZI3kUYsilXQ6I_kUnpg9bzg,108
19
+ deepagents-cli/deepagents_cli/__main__.py,sha256=J9-RNZv_zxw4SyjWRI_N7k7m8G4--vclD8vKxYIiXPQ,128
20
+ deepagents-cli/deepagents_cli/agent.py,sha256=8GFi9XtfedYTL0J-f72ARb_glklIIFCyzI_9buAg3h4,11066
21
+ deepagents-cli/deepagents_cli/agent_memory.py,sha256=Ma4P8sFpZ84ytb8GtqRcTv9Uy5E15oXqJzuBV9vuKmU,9147
22
+ deepagents-cli/deepagents_cli/commands.py,sha256=rHsHij1xGs13N_m46YcBP2M2_MRGqRbSlnWaZaMnoco,2606
23
+ deepagents-cli/deepagents_cli/config.py,sha256=gcYPDPltyhgMIiTNXQOaoKGqsAc-izNGPOlAqf_o7I0,4403
24
+ deepagents-cli/deepagents_cli/default_agent_prompt.md,sha256=Pi9SvgOAa74qhgDUMexm2-S_OfUO_CTic3zdbUnuB4s,4964
25
+ deepagents-cli/deepagents_cli/execution.py,sha256=2VqjnrT_LQn0b7eLqIIPW6oB4GeLFSR1w2X_3kjU4Lg,26498
26
+ deepagents-cli/deepagents_cli/file_ops.py,sha256=LQ7NTXPWLwePbiTBDA-22_VHxEGil7NpBltHZx1C7r4,12362
27
+ deepagents-cli/deepagents_cli/input.py,sha256=KXv9Kq13V1fK5bSRLfJxheYn8FH6_xc_l4fpcQlwqNc,10177
28
+ deepagents-cli/deepagents_cli/main.py,sha256=cIyLfAU-eJrAf2i7aCEElVTO7xXPXW8jsje4REACfcg,7142
29
+ deepagents-cli/deepagents_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ deepagents-cli/deepagents_cli/token_utils.py,sha256=tEeghLW2-vsitc-ba9cklKYnjLgRpvK7rK7PRiUu8jA,2439
31
+ deepagents-cli/deepagents_cli/tools.py,sha256=Av92Luq-vGgUr25DqErGi7aI6y6DdFSXLigffhNLxYk,4287
32
+ deepagents-cli/deepagents_cli/ui.py,sha256=d5hMHuTYerAINehGHohTI0ln4u9vojPvK1fN4OrqEEI,18667
33
+ deepagents-cli/tests/test_file_ops.py,sha256=xtu1UCmVf6gPadK0H33CtscCon8GAChi5oCboZfq-hg,3008
34
+ deepagents-cli/tests/test_placeholder.py,sha256=awIMcOR8rWGq0p_kljsIkZKinVdDlyx3MqDMQgPSi6U,101
35
+ deepagents-0.2.5.dist-info/METADATA,sha256=PiLwTOCG0BRgMteCvhAf4JhOOYxZW3EPq5Qb0_95fec,18828
36
+ deepagents-0.2.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ deepagents-0.2.5.dist-info/top_level.txt,sha256=7lDw_vmqmbQPyEVg2wcsIMEM9rjFC8i4pDXlksJ7vao,26
38
+ deepagents-0.2.5.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ deepagents
2
+ deepagents-cli
@@ -0,0 +1,3 @@
1
+ # deepagents cli
2
+
3
+ This is the CLI for deepagents
@@ -0,0 +1,196 @@
1
+ # DeepAgents CLI
2
+
3
+ Interactive command-line interface for DeepAgents - an AI coding assistant with file operations, web search, and shell command execution.
4
+
5
+ ## Architecture
6
+
7
+ The CLI is organized into focused modules:
8
+
9
+ ```
10
+ cli/
11
+ ├── __init__.py # Package exports
12
+ ├── __main__.py # Entry point for `python -m deepagents.cli`
13
+ ├── main.py # CLI loop, argument parsing, main orchestration
14
+ ├── config.py # Configuration, constants, colors, model creation
15
+ ├── tools.py # Custom tools (http_request, web_search)
16
+ ├── ui.py # Display logic, TokenTracker, help screens
17
+ ├── input.py # Input handling, completers, prompt session
18
+ ├── commands.py # Slash command and bash command handlers
19
+ ├── execution.py # Task execution, streaming, HITL approval
20
+ └── agent.py # Agent creation, management, listing, reset
21
+ ```
22
+
23
+ ## Module Responsibilities
24
+
25
+ ### `main.py` - Entry Point & Main Loop
26
+ - **Purpose**: CLI entry point, argument parsing, main interactive loop
27
+ - **Key Functions**:
28
+ - `cli_main()` - Console script entry point (called when you run `deepagents`)
29
+ - `main()` - Async main function that orchestrates agent creation and CLI
30
+ - `simple_cli()` - Main interactive loop handling user input
31
+ - `parse_args()` - Command-line argument parsing
32
+ - `check_cli_dependencies()` - Validates required packages are installed
33
+
34
+ ### `config.py` - Configuration & Constants
35
+ - **Purpose**: Centralized configuration, constants, and model creation
36
+ - **Key Exports**:
37
+ - `COLORS` - Color scheme for terminal output
38
+ - `DEEP_AGENTS_ASCII` - ASCII art banner
39
+ - `COMMANDS` - Available slash commands
40
+ - `console` - Rich Console instance
41
+ - `create_model()` - Creates OpenAI or Anthropic model based on API keys
42
+ - `get_default_coding_instructions()` - Loads default agent prompt
43
+
44
+ ### `tools.py` - Custom Agent Tools
45
+ - **Purpose**: Additional tools for the agent beyond built-in filesystem operations
46
+ - **Tools**:
47
+ - `http_request()` - Make HTTP requests to APIs
48
+ - `web_search()` - Search the web using Tavily API
49
+ - `tavily_client` - Initialized Tavily client (if API key available)
50
+
51
+ ### `ui.py` - Display & Rendering
52
+ - **Purpose**: All UI rendering and display logic
53
+ - **Key Components**:
54
+ - `TokenTracker` - Track and display token usage across the session
55
+ - `render_todo_list()` - Render todo list with checkboxes
56
+ - `show_interactive_help()` - Display available commands during session
57
+ - `show_help()` - Full help screen
58
+ - `format_tool_message_content()` - Format tool messages for display
59
+ - `truncate_value()` - Truncate long values for readable display
60
+
61
+ ### `input.py` - Input Handling
62
+ - **Purpose**: User input, completers, and prompt session configuration
63
+ - **Key Components**:
64
+ - `FilePathCompleter` - Autocomplete for `@file` mentions
65
+ - `CommandCompleter` - Autocomplete for `/commands`
66
+ - `BashCompleter` - Autocomplete for `!bash` commands
67
+ - `parse_file_mentions()` - Extract `@file` mentions and inject content
68
+ - `create_prompt_session()` - Configure prompt_toolkit session with:
69
+ - Multi-line input (Alt+Enter for newlines, Enter to submit)
70
+ - Command history
71
+ - File/command autocomplete
72
+ - External editor support (Ctrl+E)
73
+
74
+ ### `commands.py` - Command Handlers
75
+ - **Purpose**: Handle slash commands (`/help`, `/clear`, etc.) and bash execution
76
+ - **Key Functions**:
77
+ - `handle_command()` - Route and execute slash commands
78
+ - `execute_bash_command()` - Execute bash commands prefixed with `!`
79
+
80
+ ### `execution.py` - Task Execution & Streaming
81
+ - **Purpose**: Core execution logic, streaming responses, HITL (Human-in-the-Loop)
82
+ - **Key Functions**:
83
+ - `execute_task()` - Main execution function that:
84
+ - Parses file mentions
85
+ - Streams agent responses
86
+ - Displays tool calls with icons
87
+ - Renders todo list updates
88
+ - Tracks token usage
89
+ - Handles Ctrl+C interruptions
90
+ - `prompt_for_shell_approval()` - Interactive shell command approval with arrow keys
91
+ - **Features**:
92
+ - Dual-stream mode (messages + updates) for HITL support
93
+ - Real-time todo list rendering
94
+ - Spinner with status updates
95
+ - Token tracking integration
96
+
97
+ ### `agent.py` - Agent Management
98
+ - **Purpose**: Agent creation, configuration, and management commands
99
+ - **Key Functions**:
100
+ - `create_agent_with_config()` - Create agent with:
101
+ - Filesystem backends (working directory + agent directory)
102
+ - Long-term memory middleware
103
+ - Shell execution with HITL approval
104
+ - Custom system prompt
105
+ - `list_agents()` - List all agents in `~/.deepagents/`
106
+ - `reset_agent()` - Reset agent to default or copy from another agent
107
+
108
+ ## Data Flow
109
+
110
+ ```
111
+ User Input
112
+
113
+ main.py (simple_cli)
114
+
115
+ input.py (parse_file_mentions, completers)
116
+
117
+ execution.py (execute_task)
118
+
119
+ agent.py (agent created with tools from tools.py)
120
+
121
+ Stream responses → ui.py (render todos, display tool calls)
122
+ → execution.py (HITL approval if needed)
123
+
124
+ Display output via ui.py (TokenTracker, console)
125
+ ```
126
+
127
+ ## Key Features
128
+
129
+ ### File Context Injection
130
+ Type `@filename` and press Tab to autocomplete and inject file content into your prompt.
131
+
132
+ ### Interactive Commands
133
+ - `/help` - Show help
134
+ - `/clear` - Clear screen and reset conversation
135
+ - `/tokens` - Show token usage
136
+ - `/quit` or `/exit` - Exit the CLI
137
+
138
+ ### Bash Commands
139
+ Type `!command` to execute bash commands directly (e.g., `!ls`, `!git status`)
140
+
141
+ ### Todo List Tracking
142
+ The agent can create and update a visual todo list for multi-step tasks.
143
+
144
+ ### File Operation Summaries & Diff Viewer
145
+ - File reads now show a concise summary with the number of lines streamed (e.g., `⏺ Read(example.py)` followed by `⎿ Read 44 lines (lines 1-44)`).
146
+ - Writes and edits capture before/after snapshots, reporting lines added or removed plus bytes written.
147
+ - A Rich-powered unified diff renders in-line with syntax highlighting so you can review every proposed change before confirming.
148
+ - Diff output truncates gracefully for very large edits while still surfacing a summary.
149
+ - When Human-in-the-Loop approval is required, the proposed diff is shown *before* you choose Approve/Reject.
150
+
151
+ ### Human-in-the-Loop Shell Approval
152
+ Shell commands require user approval with an interactive arrow-key menu.
153
+
154
+ ### Multi-line Input
155
+ - **Enter** - Submit (or accept completion if menu is open)
156
+ - **Alt+Enter** - Insert newline (Option+Enter on Mac, or ESC then Enter)
157
+ - **Ctrl+E** - Open in external editor (nano by default)
158
+
159
+ ## Agent Storage
160
+
161
+ Each agent stores its state in `~/.deepagents/AGENT_NAME/`:
162
+ - `agent.md` - Agent's custom instructions (long-term memory)
163
+ - `memories/` - Additional context files
164
+ - `history` - Command history
165
+
166
+ ## Development
167
+
168
+ To modify the CLI:
169
+
170
+ 1. **UI changes** → Edit `ui.py` or `input.py`
171
+ 2. **Add new tools** → Edit `tools.py`
172
+ 3. **Change execution flow** → Edit `execution.py`
173
+ 4. **Add commands** → Edit `commands.py`
174
+ 5. **Agent configuration** → Edit `agent.py`
175
+ 6. **Constants/colors** → Edit `config.py`
176
+
177
+ ## Running During Development
178
+
179
+ ```bash
180
+ # From project root
181
+ uv run python -m deepagents.cli
182
+
183
+ # Or install in editable mode
184
+ uv pip install -e .
185
+ deepagents
186
+ ```
187
+
188
+ ## Entry Point
189
+
190
+ The CLI is registered in `pyproject.toml` as:
191
+ ```toml
192
+ [project.scripts]
193
+ deepagents = "deepagents.cli:cli_main"
194
+ ```
195
+
196
+ This means when users install the package, they can run `deepagents` directly.
@@ -0,0 +1,5 @@
1
+ """DeepAgents CLI - Interactive AI coding assistant."""
2
+
3
+ from .main import cli_main
4
+
5
+ __all__ = ["cli_main"]
@@ -0,0 +1,6 @@
1
+ """Allow running the CLI as: python -m deepagents.cli"""
2
+
3
+ from .main import cli_main
4
+
5
+ if __name__ == "__main__":
6
+ cli_main()