deepagent-code 0.1.3__tar.gz → 0.1.5__tar.gz
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.
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/PKG-INFO +15 -11
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/README.md +14 -10
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code/cli.py +193 -26
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code.egg-info/PKG-INFO +15 -11
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/pyproject.toml +1 -1
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/LICENSE +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code/__init__.py +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code/utils.py +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code.egg-info/SOURCES.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code.egg-info/dependency_links.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code.egg-info/entry_points.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code.egg-info/requires.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/deepagent_code.egg-info/top_level.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/setup.cfg +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/tests/test_cli.py +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.5}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagent-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A Claude Code-style CLI for running LangGraph agents from the terminal
|
|
5
5
|
Author-email: Kedar Dabhadkar <kdabhadk@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -57,7 +57,7 @@ deepagent-code
|
|
|
57
57
|
|
|
58
58
|
Or specify your own agent:
|
|
59
59
|
```bash
|
|
60
|
-
deepagent-code path/to/your_agent.py:graph
|
|
60
|
+
deepagent-code -a path/to/your_agent.py:graph
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
This launches an interactive conversation loop with your agent.
|
|
@@ -68,14 +68,17 @@ This launches an interactive conversation loop with your agent.
|
|
|
68
68
|
# Use the default agent
|
|
69
69
|
deepagent-code
|
|
70
70
|
|
|
71
|
+
# Send a message directly
|
|
72
|
+
deepagent-code "Hello, agent!"
|
|
73
|
+
|
|
71
74
|
# Specify a custom agent file
|
|
72
|
-
deepagent-code my_agent.py:graph
|
|
75
|
+
deepagent-code -a my_agent.py:graph
|
|
73
76
|
|
|
74
77
|
# Use a module path
|
|
75
|
-
deepagent-code mypackage.agents:chatbot
|
|
78
|
+
deepagent-code -a mypackage.agents:chatbot
|
|
76
79
|
|
|
77
|
-
#
|
|
78
|
-
deepagent-code -
|
|
80
|
+
# Read message from a file
|
|
81
|
+
deepagent-code -f ./prompt.md
|
|
79
82
|
|
|
80
83
|
# Non-interactive mode (auto-approve tool calls)
|
|
81
84
|
deepagent-code --no-interactive
|
|
@@ -95,7 +98,7 @@ In the interactive loop:
|
|
|
95
98
|
|
|
96
99
|
```bash
|
|
97
100
|
# Agent location (path/to/file.py:variable_name or module:variable)
|
|
98
|
-
export
|
|
101
|
+
export DEEPAGENT_SPEC="my_agent.py:graph"
|
|
99
102
|
deepagent-code
|
|
100
103
|
|
|
101
104
|
# Working directory
|
|
@@ -108,14 +111,15 @@ export DEEPAGENT_CONFIG='{"configurable": {"thread_id": "1"}}'
|
|
|
108
111
|
## CLI Options
|
|
109
112
|
|
|
110
113
|
```
|
|
111
|
-
Usage: deepagent-code [OPTIONS] [
|
|
114
|
+
Usage: deepagent-code [OPTIONS] [MESSAGE]
|
|
112
115
|
|
|
113
116
|
Arguments:
|
|
114
|
-
|
|
117
|
+
MESSAGE Optional input to send to the agent immediately
|
|
115
118
|
|
|
116
119
|
Options:
|
|
120
|
+
-a, --agent TEXT Agent spec (path/to/file.py:graph or module:graph)
|
|
117
121
|
-g, --graph-name TEXT Graph variable name (default: "graph")
|
|
118
|
-
-
|
|
122
|
+
-f, --file PATH Read message from a file (any extension)
|
|
119
123
|
-c, --config TEXT Config JSON or file path
|
|
120
124
|
--interactive/--no-interactive Handle interrupts (default: interactive)
|
|
121
125
|
--async-mode/--sync-mode Async streaming (default: sync)
|
|
@@ -140,7 +144,7 @@ agent = create_deep_agent(
|
|
|
140
144
|
|
|
141
145
|
Then run it:
|
|
142
146
|
```bash
|
|
143
|
-
deepagent-code my_agent.py:agent
|
|
147
|
+
deepagent-code -a my_agent.py:agent
|
|
144
148
|
```
|
|
145
149
|
|
|
146
150
|
## Programmatic Use
|
|
@@ -25,7 +25,7 @@ deepagent-code
|
|
|
25
25
|
|
|
26
26
|
Or specify your own agent:
|
|
27
27
|
```bash
|
|
28
|
-
deepagent-code path/to/your_agent.py:graph
|
|
28
|
+
deepagent-code -a path/to/your_agent.py:graph
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
This launches an interactive conversation loop with your agent.
|
|
@@ -36,14 +36,17 @@ This launches an interactive conversation loop with your agent.
|
|
|
36
36
|
# Use the default agent
|
|
37
37
|
deepagent-code
|
|
38
38
|
|
|
39
|
+
# Send a message directly
|
|
40
|
+
deepagent-code "Hello, agent!"
|
|
41
|
+
|
|
39
42
|
# Specify a custom agent file
|
|
40
|
-
deepagent-code my_agent.py:graph
|
|
43
|
+
deepagent-code -a my_agent.py:graph
|
|
41
44
|
|
|
42
45
|
# Use a module path
|
|
43
|
-
deepagent-code mypackage.agents:chatbot
|
|
46
|
+
deepagent-code -a mypackage.agents:chatbot
|
|
44
47
|
|
|
45
|
-
#
|
|
46
|
-
deepagent-code -
|
|
48
|
+
# Read message from a file
|
|
49
|
+
deepagent-code -f ./prompt.md
|
|
47
50
|
|
|
48
51
|
# Non-interactive mode (auto-approve tool calls)
|
|
49
52
|
deepagent-code --no-interactive
|
|
@@ -63,7 +66,7 @@ In the interactive loop:
|
|
|
63
66
|
|
|
64
67
|
```bash
|
|
65
68
|
# Agent location (path/to/file.py:variable_name or module:variable)
|
|
66
|
-
export
|
|
69
|
+
export DEEPAGENT_SPEC="my_agent.py:graph"
|
|
67
70
|
deepagent-code
|
|
68
71
|
|
|
69
72
|
# Working directory
|
|
@@ -76,14 +79,15 @@ export DEEPAGENT_CONFIG='{"configurable": {"thread_id": "1"}}'
|
|
|
76
79
|
## CLI Options
|
|
77
80
|
|
|
78
81
|
```
|
|
79
|
-
Usage: deepagent-code [OPTIONS] [
|
|
82
|
+
Usage: deepagent-code [OPTIONS] [MESSAGE]
|
|
80
83
|
|
|
81
84
|
Arguments:
|
|
82
|
-
|
|
85
|
+
MESSAGE Optional input to send to the agent immediately
|
|
83
86
|
|
|
84
87
|
Options:
|
|
88
|
+
-a, --agent TEXT Agent spec (path/to/file.py:graph or module:graph)
|
|
85
89
|
-g, --graph-name TEXT Graph variable name (default: "graph")
|
|
86
|
-
-
|
|
90
|
+
-f, --file PATH Read message from a file (any extension)
|
|
87
91
|
-c, --config TEXT Config JSON or file path
|
|
88
92
|
--interactive/--no-interactive Handle interrupts (default: interactive)
|
|
89
93
|
--async-mode/--sync-mode Async streaming (default: sync)
|
|
@@ -108,7 +112,7 @@ agent = create_deep_agent(
|
|
|
108
112
|
|
|
109
113
|
Then run it:
|
|
110
114
|
```bash
|
|
111
|
-
deepagent-code my_agent.py:agent
|
|
115
|
+
deepagent-code -a my_agent.py:agent
|
|
112
116
|
```
|
|
113
117
|
|
|
114
118
|
## Programmatic Use
|
|
@@ -7,6 +7,7 @@ import importlib.util
|
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
9
|
import re
|
|
10
|
+
import subprocess
|
|
10
11
|
import sys
|
|
11
12
|
import threading
|
|
12
13
|
import time
|
|
@@ -53,7 +54,7 @@ SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇",
|
|
|
53
54
|
|
|
54
55
|
|
|
55
56
|
# Version info
|
|
56
|
-
__version__ = "0.1.
|
|
57
|
+
__version__ = "0.1.5"
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
# Slash command registry
|
|
@@ -256,8 +257,87 @@ def get_agent_name(graph) -> str:
|
|
|
256
257
|
return "AgentCode"
|
|
257
258
|
|
|
258
259
|
|
|
259
|
-
def
|
|
260
|
-
"""
|
|
260
|
+
def get_agent_description(graph) -> Optional[str]:
|
|
261
|
+
"""Extract agent description from graph object, if available."""
|
|
262
|
+
# Try common attribute names for agent description
|
|
263
|
+
for attr in ('description', 'agent_description', '_description', '__doc__'):
|
|
264
|
+
if hasattr(graph, attr):
|
|
265
|
+
desc = getattr(graph, attr)
|
|
266
|
+
if desc and isinstance(desc, str) and desc.strip():
|
|
267
|
+
return desc.strip()
|
|
268
|
+
# Check if it's a compiled graph with a description in builder
|
|
269
|
+
if hasattr(graph, 'builder') and hasattr(graph.builder, 'description'):
|
|
270
|
+
desc = graph.builder.description
|
|
271
|
+
if desc and isinstance(desc, str) and desc.strip():
|
|
272
|
+
return desc.strip()
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def text_to_ascii_art(text: str) -> List[str]:
|
|
277
|
+
"""Convert text to ASCII art using a clean block font.
|
|
278
|
+
|
|
279
|
+
Returns a list of strings, one per line of the ASCII art.
|
|
280
|
+
All characters are exactly 3 chars wide for consistent spacing.
|
|
281
|
+
"""
|
|
282
|
+
# Clean 3-line block font - each char is exactly 3 wide
|
|
283
|
+
FONT = {
|
|
284
|
+
'A': ['▄▀▄', '█▀█', '▀ ▀'],
|
|
285
|
+
'B': ['█▀▄', '█▀▄', '▀▀▀'],
|
|
286
|
+
'C': ['▄▀▀', '█ ', '▀▀▀'],
|
|
287
|
+
'D': ['█▀▄', '█ █', '▀▀▀'],
|
|
288
|
+
'E': ['█▀▀', '█▀▀', '▀▀▀'],
|
|
289
|
+
'F': ['█▀▀', '█▀▀', '▀ '],
|
|
290
|
+
'G': ['▄▀▀', '█▀█', '▀▀▀'],
|
|
291
|
+
'H': ['█ █', '█▀█', '▀ ▀'],
|
|
292
|
+
'I': ['▀█▀', ' █ ', '▀▀▀'],
|
|
293
|
+
'J': ['▀▀█', ' █', '▀▀▀'],
|
|
294
|
+
'K': ['█ █', '█▀▄', '▀ ▀'],
|
|
295
|
+
'L': ['█ ', '█ ', '▀▀▀'],
|
|
296
|
+
'M': ['█▄█', '█ █', '▀ ▀'],
|
|
297
|
+
'N': ['█▀█', '█ █', '▀ ▀'],
|
|
298
|
+
'O': ['▄▀▄', '█ █', '▀▀▀'],
|
|
299
|
+
'P': ['█▀▄', '█▀▀', '▀ '],
|
|
300
|
+
'Q': ['▄▀▄', '█ █', '▀▀█'],
|
|
301
|
+
'R': ['█▀▄', '█▀▄', '▀ ▀'],
|
|
302
|
+
'S': ['▄▀▀', '▀▀▄', '▀▀▀'],
|
|
303
|
+
'T': ['▀█▀', ' █ ', ' ▀ '],
|
|
304
|
+
'U': ['█ █', '█ █', '▀▀▀'],
|
|
305
|
+
'V': ['█ █', '█ █', ' ▀ '],
|
|
306
|
+
'W': ['█ █', '█▀█', '▀ ▀'],
|
|
307
|
+
'X': ['▀▄▀', ' █ ', '▀ ▀'],
|
|
308
|
+
'Y': ['█ █', ' █ ', ' ▀ '],
|
|
309
|
+
'Z': ['▀▀█', ' █ ', '█▀▀'],
|
|
310
|
+
'0': ['▄▀▄', '█ █', '▀▀▀'],
|
|
311
|
+
'1': ['▄█ ', ' █ ', '▀▀▀'],
|
|
312
|
+
'2': ['▀▀█', '▄▀▀', '▀▀▀'],
|
|
313
|
+
'3': ['▀▀█', ' ▀█', '▀▀▀'],
|
|
314
|
+
'4': ['█ █', '▀▀█', ' ▀'],
|
|
315
|
+
'5': ['█▀▀', '▀▀▄', '▀▀▀'],
|
|
316
|
+
'6': ['▄▀▀', '█▀█', '▀▀▀'],
|
|
317
|
+
'7': ['▀▀█', ' █', ' ▀'],
|
|
318
|
+
'8': ['▄▀▄', '█▀█', '▀▀▀'],
|
|
319
|
+
'9': ['▄▀█', '▀▀█', '▀▀▀'],
|
|
320
|
+
' ': [' ', ' ', ' '],
|
|
321
|
+
'-': [' ', '▀▀▀', ' '],
|
|
322
|
+
'_': [' ', ' ', '▀▀▀'],
|
|
323
|
+
'.': [' ', ' ', ' ▀ '],
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
# Default char for unknown characters
|
|
327
|
+
DEFAULT = [' ', ' █ ', ' ']
|
|
328
|
+
|
|
329
|
+
lines = ['', '', '']
|
|
330
|
+
for char in text.upper():
|
|
331
|
+
char_art = FONT.get(char, DEFAULT)
|
|
332
|
+
for i in range(3):
|
|
333
|
+
lines[i] += char_art[i] + ' '
|
|
334
|
+
|
|
335
|
+
# Remove only the final trailing space we added (not internal spaces from chars like T, P)
|
|
336
|
+
return [line[:-1] if line.endswith(' ') else line for line in lines]
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def print_header_box(agent_name: str, cwd: str, description: Optional[str] = None):
|
|
340
|
+
"""Print an elegant header with ASCII art agent name, optional description, and cwd."""
|
|
261
341
|
term_width = get_terminal_width()
|
|
262
342
|
|
|
263
343
|
# Box drawing characters
|
|
@@ -267,15 +347,41 @@ def print_header_box(agent_name: str, cwd: str):
|
|
|
267
347
|
# Calculate inner width (accounting for borders and padding)
|
|
268
348
|
inner_width = term_width - 4 # 2 for borders, 2 for padding
|
|
269
349
|
|
|
270
|
-
#
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
350
|
+
# Generate ASCII art for agent name
|
|
351
|
+
ascii_lines = text_to_ascii_art(agent_name)
|
|
352
|
+
ascii_width = max(len(line) for line in ascii_lines) if ascii_lines else 0
|
|
353
|
+
|
|
354
|
+
# Use ASCII art if it fits in terminal width
|
|
355
|
+
use_ascii = ascii_width <= inner_width
|
|
356
|
+
|
|
357
|
+
# Build cwd line with label
|
|
358
|
+
cwd_label = "cwd: "
|
|
359
|
+
max_cwd_len = inner_width - len(cwd_label)
|
|
360
|
+
cwd_display = cwd if len(cwd) <= max_cwd_len else "..." + cwd[-(max_cwd_len - 3):]
|
|
361
|
+
cwd_with_label = f"{cwd_label}{cwd_display}"
|
|
362
|
+
cwd_line = cwd_with_label.center(inner_width)
|
|
274
363
|
|
|
275
364
|
# Print the box with gradient-style coloring
|
|
276
365
|
print()
|
|
277
366
|
print(f"{BRIGHT_CYAN}{TL}{H * (term_width - 2)}{TR}{RESET}")
|
|
278
|
-
|
|
367
|
+
|
|
368
|
+
if use_ascii:
|
|
369
|
+
# Print ASCII art lines centered
|
|
370
|
+
for line in ascii_lines:
|
|
371
|
+
centered_line = line.center(inner_width)
|
|
372
|
+
print(f"{BRIGHT_CYAN}{V}{RESET} {BOLD}{BRIGHT_CYAN}{centered_line}{RESET} {BRIGHT_CYAN}{V}{RESET}")
|
|
373
|
+
else:
|
|
374
|
+
# Fall back to plain text if ASCII art doesn't fit
|
|
375
|
+
title_line = agent_name.center(inner_width)
|
|
376
|
+
print(f"{BRIGHT_CYAN}{V}{RESET} {BOLD}{BRIGHT_CYAN}{title_line}{RESET} {BRIGHT_CYAN}{V}{RESET}")
|
|
377
|
+
|
|
378
|
+
# Print description line if available
|
|
379
|
+
if description:
|
|
380
|
+
# Truncate description if too long
|
|
381
|
+
desc_display = description if len(description) <= inner_width else description[:inner_width - 3] + "..."
|
|
382
|
+
desc_line = desc_display.center(inner_width)
|
|
383
|
+
print(f"{CYAN}{V}{RESET} {DIM}{ITALIC}{desc_line}{RESET} {CYAN}{V}{RESET}")
|
|
384
|
+
|
|
279
385
|
print(f"{CYAN}{V}{RESET} {DIM}{cwd_line}{RESET} {CYAN}{V}{RESET}")
|
|
280
386
|
print(f"{CYAN}{BL}{H * (term_width - 2)}{BR}{RESET}")
|
|
281
387
|
|
|
@@ -298,7 +404,7 @@ def render_markdown(text: str) -> str:
|
|
|
298
404
|
|
|
299
405
|
def parse_agent_spec(agent_spec: str) -> Tuple[str, str]:
|
|
300
406
|
"""
|
|
301
|
-
Parse
|
|
407
|
+
Parse agent spec format: path/to/file.py:variable_name.
|
|
302
408
|
|
|
303
409
|
Args:
|
|
304
410
|
agent_spec: Agent specification string
|
|
@@ -1090,21 +1196,25 @@ def run_conversation_loop(
|
|
|
1090
1196
|
graph,
|
|
1091
1197
|
config: Dict[str, Any],
|
|
1092
1198
|
agent_name: str = "AgentCode",
|
|
1199
|
+
agent_description: Optional[str] = None,
|
|
1093
1200
|
use_async: bool = False,
|
|
1094
1201
|
interactive: bool = True,
|
|
1095
1202
|
verbose: bool = False,
|
|
1096
1203
|
stream_mode: str = "updates",
|
|
1097
1204
|
initial_message: Optional[str] = None,
|
|
1205
|
+
single_shot: bool = False,
|
|
1098
1206
|
):
|
|
1099
1207
|
"""
|
|
1100
1208
|
Run a continuous conversation loop with the LangGraph agent.
|
|
1101
1209
|
Styled after Claude Code / nanocode.
|
|
1210
|
+
|
|
1211
|
+
If single_shot is True and initial_message is provided, exit after processing.
|
|
1102
1212
|
"""
|
|
1103
1213
|
# Set up tab completion for slash commands
|
|
1104
1214
|
setup_readline_completion()
|
|
1105
1215
|
|
|
1106
|
-
# Print box-drawn header with agent name
|
|
1107
|
-
print_header_box(agent_name, os.getcwd())
|
|
1216
|
+
# Print box-drawn header with agent name and description
|
|
1217
|
+
print_header_box(agent_name, os.getcwd(), agent_description)
|
|
1108
1218
|
|
|
1109
1219
|
# Print welcome message with tips
|
|
1110
1220
|
print_welcome()
|
|
@@ -1135,6 +1245,10 @@ def run_conversation_loop(
|
|
|
1135
1245
|
print_timing(duration, verbose)
|
|
1136
1246
|
print()
|
|
1137
1247
|
|
|
1248
|
+
# Exit after single-shot execution
|
|
1249
|
+
if single_shot:
|
|
1250
|
+
return
|
|
1251
|
+
|
|
1138
1252
|
# Main conversation loop
|
|
1139
1253
|
while True:
|
|
1140
1254
|
try:
|
|
@@ -1167,6 +1281,28 @@ def run_conversation_loop(
|
|
|
1167
1281
|
print(f"{DIM}Type /help to see available commands{RESET}")
|
|
1168
1282
|
continue
|
|
1169
1283
|
|
|
1284
|
+
# Handle bang commands (!) - execute bash directly
|
|
1285
|
+
if user_input.startswith("!"):
|
|
1286
|
+
bash_cmd = user_input[1:].strip()
|
|
1287
|
+
if bash_cmd:
|
|
1288
|
+
print()
|
|
1289
|
+
try:
|
|
1290
|
+
result = subprocess.run(
|
|
1291
|
+
bash_cmd,
|
|
1292
|
+
shell=True,
|
|
1293
|
+
capture_output=True,
|
|
1294
|
+
text=True,
|
|
1295
|
+
)
|
|
1296
|
+
if result.stdout:
|
|
1297
|
+
print(result.stdout, end="")
|
|
1298
|
+
if result.stderr:
|
|
1299
|
+
print(f"{RED}{result.stderr}{RESET}", end="")
|
|
1300
|
+
if result.returncode != 0:
|
|
1301
|
+
print(f"{DIM}Exit code: {result.returncode}{RESET}")
|
|
1302
|
+
except Exception as e:
|
|
1303
|
+
print(f"{RED}Error executing command: {e}{RESET}")
|
|
1304
|
+
continue
|
|
1305
|
+
|
|
1170
1306
|
# Handle "exit" as a special case (without slash)
|
|
1171
1307
|
if user_input.lower() == "exit":
|
|
1172
1308
|
break
|
|
@@ -1193,16 +1329,24 @@ def run_conversation_loop(
|
|
|
1193
1329
|
|
|
1194
1330
|
|
|
1195
1331
|
@click.command()
|
|
1196
|
-
@click.argument("
|
|
1332
|
+
@click.argument("message", required=False)
|
|
1333
|
+
@click.option(
|
|
1334
|
+
"--agent",
|
|
1335
|
+
"-a",
|
|
1336
|
+
"agent_spec",
|
|
1337
|
+
help="Agent spec: path/to/file.py, path/to/file.py:graph, or module.path:graph",
|
|
1338
|
+
)
|
|
1197
1339
|
@click.option(
|
|
1198
1340
|
"--graph-name",
|
|
1199
1341
|
"-g",
|
|
1200
1342
|
help="Name of the graph variable (default: 'graph', overridden if spec includes :name)",
|
|
1201
1343
|
)
|
|
1202
1344
|
@click.option(
|
|
1203
|
-
"--
|
|
1204
|
-
"-
|
|
1205
|
-
|
|
1345
|
+
"--file",
|
|
1346
|
+
"-f",
|
|
1347
|
+
"prompt_file",
|
|
1348
|
+
type=click.Path(exists=True),
|
|
1349
|
+
help="Read input message from a file (any extension)",
|
|
1206
1350
|
)
|
|
1207
1351
|
@click.option(
|
|
1208
1352
|
"--config",
|
|
@@ -1231,9 +1375,10 @@ def run_conversation_loop(
|
|
|
1231
1375
|
help="Show verbose output including node names",
|
|
1232
1376
|
)
|
|
1233
1377
|
def main(
|
|
1378
|
+
message: Optional[str],
|
|
1234
1379
|
agent_spec: Optional[str],
|
|
1235
1380
|
graph_name: Optional[str],
|
|
1236
|
-
|
|
1381
|
+
prompt_file: Optional[str],
|
|
1237
1382
|
config: Optional[str],
|
|
1238
1383
|
interactive: bool,
|
|
1239
1384
|
use_async: bool,
|
|
@@ -1243,7 +1388,9 @@ def main(
|
|
|
1243
1388
|
"""
|
|
1244
1389
|
Run a LangGraph agent from the command line.
|
|
1245
1390
|
|
|
1246
|
-
|
|
1391
|
+
MESSAGE is an optional input to send to the agent immediately.
|
|
1392
|
+
|
|
1393
|
+
Agent spec (-a/--agent) can be:
|
|
1247
1394
|
\b
|
|
1248
1395
|
- path/to/file.py (uses default graph name 'graph')
|
|
1249
1396
|
- path/to/file.py:agent (specifies graph variable name)
|
|
@@ -1253,7 +1400,7 @@ def main(
|
|
|
1253
1400
|
Supports environment variables for configuration:
|
|
1254
1401
|
|
|
1255
1402
|
\b
|
|
1256
|
-
-
|
|
1403
|
+
- DEEPAGENT_SPEC: Agent location (same formats as above)
|
|
1257
1404
|
- DEEPAGENT_WORKSPACE_ROOT: Working directory for the agent
|
|
1258
1405
|
- DEEPAGENT_CONFIG: Configuration JSON string or path to JSON file
|
|
1259
1406
|
- DEEPAGENT_STREAM_MODE: Stream mode for LangGraph (updates or values)
|
|
@@ -1262,14 +1409,30 @@ def main(
|
|
|
1262
1409
|
|
|
1263
1410
|
\b
|
|
1264
1411
|
Examples:
|
|
1265
|
-
deepagent-code
|
|
1266
|
-
deepagent-code my_agent.py
|
|
1267
|
-
deepagent-code
|
|
1268
|
-
deepagent-code -
|
|
1412
|
+
deepagent-code "Hello, agent!"
|
|
1413
|
+
deepagent-code -a my_agent.py "What can you do?"
|
|
1414
|
+
deepagent-code -a my_agent.py:graph
|
|
1415
|
+
deepagent-code -f ./prompt.md
|
|
1269
1416
|
"""
|
|
1270
1417
|
try:
|
|
1271
|
-
#
|
|
1272
|
-
|
|
1418
|
+
# Handle -f/--file option: read message from file
|
|
1419
|
+
if prompt_file and message:
|
|
1420
|
+
print(f"{RED}⏺ Error: Cannot use both MESSAGE argument and -f/--file option{RESET}")
|
|
1421
|
+
sys.exit(1)
|
|
1422
|
+
|
|
1423
|
+
if prompt_file:
|
|
1424
|
+
try:
|
|
1425
|
+
with open(prompt_file, 'r', encoding='utf-8') as f:
|
|
1426
|
+
message = f.read().strip()
|
|
1427
|
+
if not message:
|
|
1428
|
+
print(f"{RED}⏺ Error: File '{prompt_file}' is empty{RESET}")
|
|
1429
|
+
sys.exit(1)
|
|
1430
|
+
except Exception as e:
|
|
1431
|
+
print(f"{RED}⏺ Error reading file '{prompt_file}': {e}{RESET}")
|
|
1432
|
+
sys.exit(1)
|
|
1433
|
+
|
|
1434
|
+
# Get environment variables (DEEPAGENT_SPEC preferred, DEEPAGENT_AGENT_SPEC for backwards compat)
|
|
1435
|
+
env_agent_spec = os.getenv('DEEPAGENT_SPEC') or os.getenv('DEEPAGENT_AGENT_SPEC')
|
|
1273
1436
|
env_workspace_root = os.getenv('DEEPAGENT_WORKSPACE_ROOT')
|
|
1274
1437
|
env_config = os.getenv('DEEPAGENT_CONFIG')
|
|
1275
1438
|
env_stream_mode = os.getenv('DEEPAGENT_STREAM_MODE', 'updates')
|
|
@@ -1288,7 +1451,7 @@ def main(
|
|
|
1288
1451
|
print(f"\n{DIM}Usage:{RESET}")
|
|
1289
1452
|
print(f" deepagent-code path/to/agent.py:graph")
|
|
1290
1453
|
print(f" deepagent-code mypackage.module:agent")
|
|
1291
|
-
print(f"\n{DIM}Or set
|
|
1454
|
+
print(f"\n{DIM}Or set DEEPAGENT_SPEC environment variable{RESET}")
|
|
1292
1455
|
sys.exit(1)
|
|
1293
1456
|
|
|
1294
1457
|
# Change to workspace root if specified
|
|
@@ -1331,19 +1494,23 @@ def main(
|
|
|
1331
1494
|
if "thread_id" not in config_dict["configurable"]:
|
|
1332
1495
|
config_dict["configurable"]["thread_id"] = str(uuid.uuid4())
|
|
1333
1496
|
|
|
1334
|
-
# Extract agent name from graph object
|
|
1497
|
+
# Extract agent name and description from graph object
|
|
1335
1498
|
agent_name = get_agent_name(graph)
|
|
1499
|
+
agent_description = get_agent_description(graph)
|
|
1336
1500
|
|
|
1337
1501
|
# Run the conversation loop
|
|
1502
|
+
# Single-shot mode: exit after processing if message was provided via CLI
|
|
1338
1503
|
run_conversation_loop(
|
|
1339
1504
|
graph=graph,
|
|
1340
1505
|
config=config_dict,
|
|
1341
1506
|
agent_name=agent_name,
|
|
1507
|
+
agent_description=agent_description,
|
|
1342
1508
|
use_async=use_async,
|
|
1343
1509
|
interactive=interactive,
|
|
1344
1510
|
verbose=verbose,
|
|
1345
1511
|
stream_mode=final_stream_mode,
|
|
1346
1512
|
initial_message=message,
|
|
1513
|
+
single_shot=bool(message),
|
|
1347
1514
|
)
|
|
1348
1515
|
|
|
1349
1516
|
except FileNotFoundError as e:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagent-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A Claude Code-style CLI for running LangGraph agents from the terminal
|
|
5
5
|
Author-email: Kedar Dabhadkar <kdabhadk@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -57,7 +57,7 @@ deepagent-code
|
|
|
57
57
|
|
|
58
58
|
Or specify your own agent:
|
|
59
59
|
```bash
|
|
60
|
-
deepagent-code path/to/your_agent.py:graph
|
|
60
|
+
deepagent-code -a path/to/your_agent.py:graph
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
This launches an interactive conversation loop with your agent.
|
|
@@ -68,14 +68,17 @@ This launches an interactive conversation loop with your agent.
|
|
|
68
68
|
# Use the default agent
|
|
69
69
|
deepagent-code
|
|
70
70
|
|
|
71
|
+
# Send a message directly
|
|
72
|
+
deepagent-code "Hello, agent!"
|
|
73
|
+
|
|
71
74
|
# Specify a custom agent file
|
|
72
|
-
deepagent-code my_agent.py:graph
|
|
75
|
+
deepagent-code -a my_agent.py:graph
|
|
73
76
|
|
|
74
77
|
# Use a module path
|
|
75
|
-
deepagent-code mypackage.agents:chatbot
|
|
78
|
+
deepagent-code -a mypackage.agents:chatbot
|
|
76
79
|
|
|
77
|
-
#
|
|
78
|
-
deepagent-code -
|
|
80
|
+
# Read message from a file
|
|
81
|
+
deepagent-code -f ./prompt.md
|
|
79
82
|
|
|
80
83
|
# Non-interactive mode (auto-approve tool calls)
|
|
81
84
|
deepagent-code --no-interactive
|
|
@@ -95,7 +98,7 @@ In the interactive loop:
|
|
|
95
98
|
|
|
96
99
|
```bash
|
|
97
100
|
# Agent location (path/to/file.py:variable_name or module:variable)
|
|
98
|
-
export
|
|
101
|
+
export DEEPAGENT_SPEC="my_agent.py:graph"
|
|
99
102
|
deepagent-code
|
|
100
103
|
|
|
101
104
|
# Working directory
|
|
@@ -108,14 +111,15 @@ export DEEPAGENT_CONFIG='{"configurable": {"thread_id": "1"}}'
|
|
|
108
111
|
## CLI Options
|
|
109
112
|
|
|
110
113
|
```
|
|
111
|
-
Usage: deepagent-code [OPTIONS] [
|
|
114
|
+
Usage: deepagent-code [OPTIONS] [MESSAGE]
|
|
112
115
|
|
|
113
116
|
Arguments:
|
|
114
|
-
|
|
117
|
+
MESSAGE Optional input to send to the agent immediately
|
|
115
118
|
|
|
116
119
|
Options:
|
|
120
|
+
-a, --agent TEXT Agent spec (path/to/file.py:graph or module:graph)
|
|
117
121
|
-g, --graph-name TEXT Graph variable name (default: "graph")
|
|
118
|
-
-
|
|
122
|
+
-f, --file PATH Read message from a file (any extension)
|
|
119
123
|
-c, --config TEXT Config JSON or file path
|
|
120
124
|
--interactive/--no-interactive Handle interrupts (default: interactive)
|
|
121
125
|
--async-mode/--sync-mode Async streaming (default: sync)
|
|
@@ -140,7 +144,7 @@ agent = create_deep_agent(
|
|
|
140
144
|
|
|
141
145
|
Then run it:
|
|
142
146
|
```bash
|
|
143
|
-
deepagent-code my_agent.py:agent
|
|
147
|
+
deepagent-code -a my_agent.py:agent
|
|
144
148
|
```
|
|
145
149
|
|
|
146
150
|
## Programmatic Use
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|