deepagent-code 0.1.3__tar.gz → 0.1.4__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.4}/PKG-INFO +2 -2
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/README.md +1 -1
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code/cli.py +147 -16
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code.egg-info/PKG-INFO +2 -2
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/pyproject.toml +1 -1
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/LICENSE +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code/__init__.py +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code/utils.py +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code.egg-info/SOURCES.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code.egg-info/dependency_links.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code.egg-info/entry_points.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code.egg-info/requires.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/deepagent_code.egg-info/top_level.txt +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/setup.cfg +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/tests/test_cli.py +0 -0
- {deepagent_code-0.1.3 → deepagent_code-0.1.4}/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.4
|
|
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
|
|
@@ -95,7 +95,7 @@ In the interactive loop:
|
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
97
|
# Agent location (path/to/file.py:variable_name or module:variable)
|
|
98
|
-
export
|
|
98
|
+
export DEEPAGENT_SPEC="my_agent.py:graph"
|
|
99
99
|
deepagent-code
|
|
100
100
|
|
|
101
101
|
# Working directory
|
|
@@ -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.4"
|
|
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,6 +1196,7 @@ 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,
|
|
@@ -1103,8 +1210,8 @@ def run_conversation_loop(
|
|
|
1103
1210
|
# Set up tab completion for slash commands
|
|
1104
1211
|
setup_readline_completion()
|
|
1105
1212
|
|
|
1106
|
-
# Print box-drawn header with agent name
|
|
1107
|
-
print_header_box(agent_name, os.getcwd())
|
|
1213
|
+
# Print box-drawn header with agent name and description
|
|
1214
|
+
print_header_box(agent_name, os.getcwd(), agent_description)
|
|
1108
1215
|
|
|
1109
1216
|
# Print welcome message with tips
|
|
1110
1217
|
print_welcome()
|
|
@@ -1167,6 +1274,28 @@ def run_conversation_loop(
|
|
|
1167
1274
|
print(f"{DIM}Type /help to see available commands{RESET}")
|
|
1168
1275
|
continue
|
|
1169
1276
|
|
|
1277
|
+
# Handle bang commands (!) - execute bash directly
|
|
1278
|
+
if user_input.startswith("!"):
|
|
1279
|
+
bash_cmd = user_input[1:].strip()
|
|
1280
|
+
if bash_cmd:
|
|
1281
|
+
print()
|
|
1282
|
+
try:
|
|
1283
|
+
result = subprocess.run(
|
|
1284
|
+
bash_cmd,
|
|
1285
|
+
shell=True,
|
|
1286
|
+
capture_output=True,
|
|
1287
|
+
text=True,
|
|
1288
|
+
)
|
|
1289
|
+
if result.stdout:
|
|
1290
|
+
print(result.stdout, end="")
|
|
1291
|
+
if result.stderr:
|
|
1292
|
+
print(f"{RED}{result.stderr}{RESET}", end="")
|
|
1293
|
+
if result.returncode != 0:
|
|
1294
|
+
print(f"{DIM}Exit code: {result.returncode}{RESET}")
|
|
1295
|
+
except Exception as e:
|
|
1296
|
+
print(f"{RED}Error executing command: {e}{RESET}")
|
|
1297
|
+
continue
|
|
1298
|
+
|
|
1170
1299
|
# Handle "exit" as a special case (without slash)
|
|
1171
1300
|
if user_input.lower() == "exit":
|
|
1172
1301
|
break
|
|
@@ -1253,7 +1382,7 @@ def main(
|
|
|
1253
1382
|
Supports environment variables for configuration:
|
|
1254
1383
|
|
|
1255
1384
|
\b
|
|
1256
|
-
-
|
|
1385
|
+
- DEEPAGENT_SPEC: Agent location (same formats as above)
|
|
1257
1386
|
- DEEPAGENT_WORKSPACE_ROOT: Working directory for the agent
|
|
1258
1387
|
- DEEPAGENT_CONFIG: Configuration JSON string or path to JSON file
|
|
1259
1388
|
- DEEPAGENT_STREAM_MODE: Stream mode for LangGraph (updates or values)
|
|
@@ -1268,8 +1397,8 @@ def main(
|
|
|
1268
1397
|
deepagent-code -m "Hello, agent!"
|
|
1269
1398
|
"""
|
|
1270
1399
|
try:
|
|
1271
|
-
# Get environment variables
|
|
1272
|
-
env_agent_spec = os.getenv('DEEPAGENT_AGENT_SPEC')
|
|
1400
|
+
# Get environment variables (DEEPAGENT_SPEC preferred, DEEPAGENT_AGENT_SPEC for backwards compat)
|
|
1401
|
+
env_agent_spec = os.getenv('DEEPAGENT_SPEC') or os.getenv('DEEPAGENT_AGENT_SPEC')
|
|
1273
1402
|
env_workspace_root = os.getenv('DEEPAGENT_WORKSPACE_ROOT')
|
|
1274
1403
|
env_config = os.getenv('DEEPAGENT_CONFIG')
|
|
1275
1404
|
env_stream_mode = os.getenv('DEEPAGENT_STREAM_MODE', 'updates')
|
|
@@ -1288,7 +1417,7 @@ def main(
|
|
|
1288
1417
|
print(f"\n{DIM}Usage:{RESET}")
|
|
1289
1418
|
print(f" deepagent-code path/to/agent.py:graph")
|
|
1290
1419
|
print(f" deepagent-code mypackage.module:agent")
|
|
1291
|
-
print(f"\n{DIM}Or set
|
|
1420
|
+
print(f"\n{DIM}Or set DEEPAGENT_SPEC environment variable{RESET}")
|
|
1292
1421
|
sys.exit(1)
|
|
1293
1422
|
|
|
1294
1423
|
# Change to workspace root if specified
|
|
@@ -1331,14 +1460,16 @@ def main(
|
|
|
1331
1460
|
if "thread_id" not in config_dict["configurable"]:
|
|
1332
1461
|
config_dict["configurable"]["thread_id"] = str(uuid.uuid4())
|
|
1333
1462
|
|
|
1334
|
-
# Extract agent name from graph object
|
|
1463
|
+
# Extract agent name and description from graph object
|
|
1335
1464
|
agent_name = get_agent_name(graph)
|
|
1465
|
+
agent_description = get_agent_description(graph)
|
|
1336
1466
|
|
|
1337
1467
|
# Run the conversation loop
|
|
1338
1468
|
run_conversation_loop(
|
|
1339
1469
|
graph=graph,
|
|
1340
1470
|
config=config_dict,
|
|
1341
1471
|
agent_name=agent_name,
|
|
1472
|
+
agent_description=agent_description,
|
|
1342
1473
|
use_async=use_async,
|
|
1343
1474
|
interactive=interactive,
|
|
1344
1475
|
verbose=verbose,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: deepagent-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
|
@@ -95,7 +95,7 @@ In the interactive loop:
|
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
97
|
# Agent location (path/to/file.py:variable_name or module:variable)
|
|
98
|
-
export
|
|
98
|
+
export DEEPAGENT_SPEC="my_agent.py:graph"
|
|
99
99
|
deepagent-code
|
|
100
100
|
|
|
101
101
|
# Working directory
|
|
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
|