code-puppy 0.0.348__py3-none-any.whl → 0.0.361__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.
- code_puppy/agents/__init__.py +2 -0
- code_puppy/agents/agent_manager.py +49 -0
- code_puppy/agents/agent_pack_leader.py +383 -0
- code_puppy/agents/agent_qa_kitten.py +12 -7
- code_puppy/agents/agent_terminal_qa.py +323 -0
- code_puppy/agents/base_agent.py +17 -4
- code_puppy/agents/event_stream_handler.py +101 -8
- code_puppy/agents/pack/__init__.py +34 -0
- code_puppy/agents/pack/bloodhound.py +304 -0
- code_puppy/agents/pack/husky.py +321 -0
- code_puppy/agents/pack/retriever.py +393 -0
- code_puppy/agents/pack/shepherd.py +348 -0
- code_puppy/agents/pack/terrier.py +287 -0
- code_puppy/agents/pack/watchdog.py +367 -0
- code_puppy/agents/subagent_stream_handler.py +276 -0
- code_puppy/api/__init__.py +13 -0
- code_puppy/api/app.py +169 -0
- code_puppy/api/main.py +21 -0
- code_puppy/api/pty_manager.py +446 -0
- code_puppy/api/routers/__init__.py +12 -0
- code_puppy/api/routers/agents.py +36 -0
- code_puppy/api/routers/commands.py +217 -0
- code_puppy/api/routers/config.py +74 -0
- code_puppy/api/routers/sessions.py +232 -0
- code_puppy/api/templates/terminal.html +361 -0
- code_puppy/api/websocket.py +154 -0
- code_puppy/callbacks.py +73 -0
- code_puppy/claude_cache_client.py +249 -34
- code_puppy/command_line/core_commands.py +85 -0
- code_puppy/config.py +66 -62
- code_puppy/messaging/__init__.py +15 -0
- code_puppy/messaging/messages.py +27 -0
- code_puppy/messaging/queue_console.py +1 -1
- code_puppy/messaging/rich_renderer.py +36 -1
- code_puppy/messaging/spinner/__init__.py +20 -2
- code_puppy/messaging/subagent_console.py +461 -0
- code_puppy/model_utils.py +54 -0
- code_puppy/plugins/antigravity_oauth/antigravity_model.py +90 -19
- code_puppy/plugins/antigravity_oauth/transport.py +1 -0
- code_puppy/plugins/frontend_emitter/__init__.py +25 -0
- code_puppy/plugins/frontend_emitter/emitter.py +121 -0
- code_puppy/plugins/frontend_emitter/register_callbacks.py +261 -0
- code_puppy/prompts/antigravity_system_prompt.md +1 -0
- code_puppy/status_display.py +6 -2
- code_puppy/tools/__init__.py +37 -1
- code_puppy/tools/agent_tools.py +83 -33
- code_puppy/tools/browser/__init__.py +37 -0
- code_puppy/tools/browser/browser_control.py +6 -6
- code_puppy/tools/browser/browser_interactions.py +21 -20
- code_puppy/tools/browser/browser_locators.py +9 -9
- code_puppy/tools/browser/browser_navigation.py +7 -7
- code_puppy/tools/browser/browser_screenshot.py +78 -140
- code_puppy/tools/browser/browser_scripts.py +15 -13
- code_puppy/tools/browser/camoufox_manager.py +226 -64
- code_puppy/tools/browser/chromium_terminal_manager.py +259 -0
- code_puppy/tools/browser/terminal_command_tools.py +521 -0
- code_puppy/tools/browser/terminal_screenshot_tools.py +556 -0
- code_puppy/tools/browser/terminal_tools.py +525 -0
- code_puppy/tools/command_runner.py +292 -101
- code_puppy/tools/common.py +176 -1
- code_puppy/tools/display.py +84 -0
- code_puppy/tools/subagent_context.py +158 -0
- {code_puppy-0.0.348.dist-info → code_puppy-0.0.361.dist-info}/METADATA +13 -11
- {code_puppy-0.0.348.dist-info → code_puppy-0.0.361.dist-info}/RECORD +69 -38
- code_puppy/tools/browser/vqa_agent.py +0 -90
- {code_puppy-0.0.348.data → code_puppy-0.0.361.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.348.data → code_puppy-0.0.361.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.348.dist-info → code_puppy-0.0.361.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.348.dist-info → code_puppy-0.0.361.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.348.dist-info → code_puppy-0.0.361.dist-info}/licenses/LICENSE +0 -0
code_puppy/tools/common.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import fnmatch
|
|
3
|
+
import functools
|
|
2
4
|
import hashlib
|
|
5
|
+
import logging
|
|
3
6
|
import os
|
|
4
7
|
import sys
|
|
5
8
|
import time
|
|
6
9
|
from pathlib import Path
|
|
7
|
-
from typing import Callable, Optional, Tuple
|
|
10
|
+
from typing import Any, Callable, Optional, Tuple
|
|
8
11
|
|
|
9
12
|
from prompt_toolkit import Application
|
|
10
13
|
from prompt_toolkit.formatted_text import HTML
|
|
@@ -1406,3 +1409,175 @@ def generate_group_id(tool_name: str, extra_context: str = "") -> str:
|
|
|
1406
1409
|
short_hash = hash_obj.hexdigest()[:8]
|
|
1407
1410
|
|
|
1408
1411
|
return f"{tool_name}_{short_hash}"
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
# =============================================================================
|
|
1415
|
+
# TOOL CALLBACK WRAPPER
|
|
1416
|
+
# =============================================================================
|
|
1417
|
+
|
|
1418
|
+
logger = logging.getLogger(__name__)
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
def with_tool_callbacks(tool_name: str) -> Callable:
|
|
1422
|
+
"""Decorator that wraps tool functions with pre/post callback hooks.
|
|
1423
|
+
|
|
1424
|
+
This decorator enables plugins to hook into tool execution for:
|
|
1425
|
+
- Logging and analytics
|
|
1426
|
+
- Pre-execution validation or modification
|
|
1427
|
+
- Post-execution result processing
|
|
1428
|
+
- Performance monitoring
|
|
1429
|
+
|
|
1430
|
+
Args:
|
|
1431
|
+
tool_name: The name of the tool being wrapped (e.g., 'edit_file', 'list_files')
|
|
1432
|
+
|
|
1433
|
+
Returns:
|
|
1434
|
+
A decorator function that wraps the tool with callbacks.
|
|
1435
|
+
|
|
1436
|
+
Example:
|
|
1437
|
+
@with_tool_callbacks('my_tool')
|
|
1438
|
+
async def my_tool_impl(ctx, **kwargs):
|
|
1439
|
+
return result
|
|
1440
|
+
"""
|
|
1441
|
+
|
|
1442
|
+
def decorator(func: Callable) -> Callable:
|
|
1443
|
+
@functools.wraps(func)
|
|
1444
|
+
async def async_wrapper(*args, **kwargs) -> Any:
|
|
1445
|
+
# Extract context from args if available (usually first arg is RunContext)
|
|
1446
|
+
context = None
|
|
1447
|
+
tool_args = kwargs.copy()
|
|
1448
|
+
|
|
1449
|
+
# Try to get session context
|
|
1450
|
+
try:
|
|
1451
|
+
from code_puppy.messaging import get_session_context
|
|
1452
|
+
|
|
1453
|
+
context = get_session_context()
|
|
1454
|
+
except ImportError:
|
|
1455
|
+
pass
|
|
1456
|
+
|
|
1457
|
+
# Fire pre-tool callback (non-blocking)
|
|
1458
|
+
try:
|
|
1459
|
+
from code_puppy import callbacks
|
|
1460
|
+
|
|
1461
|
+
asyncio.create_task(
|
|
1462
|
+
callbacks.on_pre_tool_call(tool_name, tool_args, context)
|
|
1463
|
+
)
|
|
1464
|
+
except ImportError:
|
|
1465
|
+
logger.debug("callbacks module not available for pre_tool_call")
|
|
1466
|
+
except Exception as e:
|
|
1467
|
+
logger.debug(f"Error in pre_tool_call callback: {e}")
|
|
1468
|
+
|
|
1469
|
+
# Execute the tool and measure duration
|
|
1470
|
+
start_time = time.perf_counter()
|
|
1471
|
+
result = None
|
|
1472
|
+
error = None
|
|
1473
|
+
|
|
1474
|
+
try:
|
|
1475
|
+
result = await func(*args, **kwargs)
|
|
1476
|
+
return result
|
|
1477
|
+
except Exception as e:
|
|
1478
|
+
error = e
|
|
1479
|
+
raise
|
|
1480
|
+
finally:
|
|
1481
|
+
end_time = time.perf_counter()
|
|
1482
|
+
duration_ms = (end_time - start_time) * 1000
|
|
1483
|
+
|
|
1484
|
+
# Fire post-tool callback (non-blocking)
|
|
1485
|
+
final_result = result if error is None else {"error": str(error)}
|
|
1486
|
+
try:
|
|
1487
|
+
from code_puppy import callbacks
|
|
1488
|
+
|
|
1489
|
+
asyncio.create_task(
|
|
1490
|
+
callbacks.on_post_tool_call(
|
|
1491
|
+
tool_name, tool_args, final_result, duration_ms, context
|
|
1492
|
+
)
|
|
1493
|
+
)
|
|
1494
|
+
except ImportError:
|
|
1495
|
+
logger.debug("callbacks module not available for post_tool_call")
|
|
1496
|
+
except Exception as e:
|
|
1497
|
+
logger.debug(f"Error in post_tool_call callback: {e}")
|
|
1498
|
+
|
|
1499
|
+
@functools.wraps(func)
|
|
1500
|
+
def sync_wrapper(*args, **kwargs) -> Any:
|
|
1501
|
+
"""Sync wrapper for non-async tool functions."""
|
|
1502
|
+
# Extract context
|
|
1503
|
+
context = None
|
|
1504
|
+
tool_args = kwargs.copy()
|
|
1505
|
+
|
|
1506
|
+
try:
|
|
1507
|
+
from code_puppy.messaging import get_session_context
|
|
1508
|
+
|
|
1509
|
+
context = get_session_context()
|
|
1510
|
+
except ImportError:
|
|
1511
|
+
pass
|
|
1512
|
+
|
|
1513
|
+
# For sync functions, we can't use asyncio.create_task directly
|
|
1514
|
+
# Instead, we'll try to schedule it if there's a running loop
|
|
1515
|
+
def fire_pre_callback():
|
|
1516
|
+
try:
|
|
1517
|
+
from code_puppy import callbacks
|
|
1518
|
+
|
|
1519
|
+
loop = asyncio.get_running_loop()
|
|
1520
|
+
asyncio.run_coroutine_threadsafe(
|
|
1521
|
+
callbacks.on_pre_tool_call(tool_name, tool_args, context),
|
|
1522
|
+
loop,
|
|
1523
|
+
)
|
|
1524
|
+
except RuntimeError:
|
|
1525
|
+
# No running loop - skip async callback
|
|
1526
|
+
pass
|
|
1527
|
+
except ImportError:
|
|
1528
|
+
pass
|
|
1529
|
+
except Exception as e:
|
|
1530
|
+
logger.debug(f"Error in sync pre_tool_call: {e}")
|
|
1531
|
+
|
|
1532
|
+
fire_pre_callback()
|
|
1533
|
+
|
|
1534
|
+
# Execute the tool
|
|
1535
|
+
start_time = time.perf_counter()
|
|
1536
|
+
result = None
|
|
1537
|
+
error = None
|
|
1538
|
+
|
|
1539
|
+
try:
|
|
1540
|
+
result = func(*args, **kwargs)
|
|
1541
|
+
return result
|
|
1542
|
+
except Exception as e:
|
|
1543
|
+
error = e
|
|
1544
|
+
raise
|
|
1545
|
+
finally:
|
|
1546
|
+
end_time = time.perf_counter()
|
|
1547
|
+
duration_ms = (end_time - start_time) * 1000
|
|
1548
|
+
|
|
1549
|
+
# Fire post-tool callback
|
|
1550
|
+
final_result = result if error is None else {"error": str(error)}
|
|
1551
|
+
|
|
1552
|
+
def fire_post_callback():
|
|
1553
|
+
try:
|
|
1554
|
+
from code_puppy import callbacks
|
|
1555
|
+
|
|
1556
|
+
loop = asyncio.get_running_loop()
|
|
1557
|
+
asyncio.run_coroutine_threadsafe(
|
|
1558
|
+
callbacks.on_post_tool_call(
|
|
1559
|
+
tool_name,
|
|
1560
|
+
tool_args,
|
|
1561
|
+
final_result,
|
|
1562
|
+
duration_ms,
|
|
1563
|
+
context,
|
|
1564
|
+
),
|
|
1565
|
+
loop,
|
|
1566
|
+
)
|
|
1567
|
+
except RuntimeError:
|
|
1568
|
+
# No running loop - skip async callback
|
|
1569
|
+
pass
|
|
1570
|
+
except ImportError:
|
|
1571
|
+
pass
|
|
1572
|
+
except Exception as e:
|
|
1573
|
+
logger.debug(f"Error in sync post_tool_call: {e}")
|
|
1574
|
+
|
|
1575
|
+
fire_post_callback()
|
|
1576
|
+
|
|
1577
|
+
# Return appropriate wrapper based on function type
|
|
1578
|
+
if asyncio.iscoroutinefunction(func):
|
|
1579
|
+
return async_wrapper
|
|
1580
|
+
else:
|
|
1581
|
+
return sync_wrapper
|
|
1582
|
+
|
|
1583
|
+
return decorator
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Common display utilities for rendering agent outputs.
|
|
2
|
+
|
|
3
|
+
This module provides non-streaming display functions for rendering
|
|
4
|
+
agent results and other structured content using termflow for markdown.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from code_puppy.config import get_banner_color, get_subagent_verbose
|
|
12
|
+
from code_puppy.tools.subagent_context import is_subagent
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def display_non_streamed_result(
|
|
16
|
+
content: str,
|
|
17
|
+
console: Optional[Console] = None,
|
|
18
|
+
banner_text: str = "AGENT RESPONSE",
|
|
19
|
+
banner_name: str = "agent_response",
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Display a non-streamed result with markdown rendering via termflow.
|
|
22
|
+
|
|
23
|
+
This function renders markdown content using termflow for beautiful
|
|
24
|
+
terminal output. Use this instead of streaming for sub-agent responses
|
|
25
|
+
or any other content that arrives all at once.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
content: The content to display (can include markdown).
|
|
29
|
+
console: Rich Console to use for output. If None, creates a new one.
|
|
30
|
+
banner_text: Text to display in the banner (default: "AGENT RESPONSE").
|
|
31
|
+
banner_name: Banner config key for color lookup (default: "agent_response").
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
>>> display_non_streamed_result("# Hello\n\nThis is **bold** text.")
|
|
35
|
+
# Renders with AGENT RESPONSE banner and formatted markdown
|
|
36
|
+
"""
|
|
37
|
+
# Skip display for sub-agents unless verbose mode
|
|
38
|
+
if is_subagent() and not get_subagent_verbose():
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
import time
|
|
42
|
+
|
|
43
|
+
from rich.text import Text
|
|
44
|
+
from termflow import Parser as TermflowParser
|
|
45
|
+
from termflow import Renderer as TermflowRenderer
|
|
46
|
+
|
|
47
|
+
from code_puppy.messaging.spinner import pause_all_spinners, resume_all_spinners
|
|
48
|
+
|
|
49
|
+
if console is None:
|
|
50
|
+
console = Console()
|
|
51
|
+
|
|
52
|
+
# Pause spinners and give time to clear
|
|
53
|
+
pause_all_spinners()
|
|
54
|
+
time.sleep(0.1)
|
|
55
|
+
|
|
56
|
+
# Clear line and print banner
|
|
57
|
+
console.print(" " * 50, end="\r")
|
|
58
|
+
console.print() # Newline before banner
|
|
59
|
+
|
|
60
|
+
banner_color = get_banner_color(banner_name)
|
|
61
|
+
console.print(
|
|
62
|
+
Text.from_markup(
|
|
63
|
+
f"[bold white on {banner_color}] {banner_text} [/bold white on {banner_color}]"
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Use termflow for markdown rendering
|
|
68
|
+
parser = TermflowParser()
|
|
69
|
+
renderer = TermflowRenderer(output=console.file, width=console.width)
|
|
70
|
+
|
|
71
|
+
# Process content line by line
|
|
72
|
+
for line in content.split("\n"):
|
|
73
|
+
events = parser.parse_line(line)
|
|
74
|
+
renderer.render_all(events)
|
|
75
|
+
|
|
76
|
+
# Finalize to close any open markdown blocks
|
|
77
|
+
final_events = parser.finalize()
|
|
78
|
+
renderer.render_all(final_events)
|
|
79
|
+
|
|
80
|
+
# Resume spinners
|
|
81
|
+
resume_all_spinners()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
__all__ = ["display_non_streamed_result"]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Sub-agent context management with async-safe state tracking.
|
|
2
|
+
|
|
3
|
+
This module provides context-aware tracking of sub-agent execution state using
|
|
4
|
+
Python's contextvars for async-safe isolation. This ensures that sub-agent state
|
|
5
|
+
is properly isolated across different async tasks and execution contexts.
|
|
6
|
+
|
|
7
|
+
## Why ContextVars?
|
|
8
|
+
|
|
9
|
+
ContextVars provide automatic context isolation in async environments:
|
|
10
|
+
- Each async task gets its own copy of the context
|
|
11
|
+
- State changes in one task don't affect others
|
|
12
|
+
- Perfect for tracking execution depth in nested agent calls
|
|
13
|
+
- Token-based reset ensures proper cleanup even with exceptions
|
|
14
|
+
|
|
15
|
+
## Usage Example:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from code_puppy.tools.subagent_context import subagent_context, is_subagent
|
|
19
|
+
|
|
20
|
+
# Main agent
|
|
21
|
+
print(is_subagent()) # False
|
|
22
|
+
|
|
23
|
+
async def run_subagent():
|
|
24
|
+
with subagent_context("retriever"):
|
|
25
|
+
print(is_subagent()) # True
|
|
26
|
+
print(get_subagent_name()) # "retriever"
|
|
27
|
+
print(get_subagent_depth()) # 1
|
|
28
|
+
|
|
29
|
+
# Nested sub-agent
|
|
30
|
+
with subagent_context("terrier"):
|
|
31
|
+
print(get_subagent_depth()) # 2
|
|
32
|
+
print(get_subagent_name()) # "terrier"
|
|
33
|
+
|
|
34
|
+
# Back to parent sub-agent
|
|
35
|
+
print(get_subagent_name()) # "retriever"
|
|
36
|
+
print(get_subagent_depth()) # 1
|
|
37
|
+
|
|
38
|
+
# After context exits
|
|
39
|
+
print(is_subagent()) # False
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Benefits:
|
|
43
|
+
|
|
44
|
+
1. **Async Safety**: Multiple sub-agents can run concurrently without interference
|
|
45
|
+
2. **Nested Support**: Properly handles sub-agents calling other sub-agents
|
|
46
|
+
3. **Clean Restoration**: Token-based reset ensures state is restored even on errors
|
|
47
|
+
4. **Zero Overhead**: When not in a sub-agent context, minimal performance impact
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from contextlib import contextmanager
|
|
51
|
+
from contextvars import ContextVar
|
|
52
|
+
from typing import Generator
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
"subagent_context",
|
|
56
|
+
"is_subagent",
|
|
57
|
+
"get_subagent_name",
|
|
58
|
+
"get_subagent_depth",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Track sub-agent depth (0 = main agent, 1+ = sub-agent)
|
|
62
|
+
_subagent_depth: ContextVar[int] = ContextVar("subagent_depth", default=0)
|
|
63
|
+
|
|
64
|
+
# Track current sub-agent name (None = main agent)
|
|
65
|
+
_subagent_name: ContextVar[str | None] = ContextVar("subagent_name", default=None)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@contextmanager
|
|
69
|
+
def subagent_context(agent_name: str) -> Generator[None, None, None]:
|
|
70
|
+
"""Context manager for tracking sub-agent execution.
|
|
71
|
+
|
|
72
|
+
Increments the sub-agent depth and sets the current agent name on entry,
|
|
73
|
+
then restores the previous state on exit. Uses token-based reset for
|
|
74
|
+
proper async isolation and exception safety.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
agent_name: Name of the sub-agent being executed (e.g., "retriever", "husky")
|
|
78
|
+
|
|
79
|
+
Yields:
|
|
80
|
+
None
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
>>> with subagent_context("retriever"):
|
|
84
|
+
... assert is_subagent() is True
|
|
85
|
+
... assert get_subagent_name() == "retriever"
|
|
86
|
+
>>> assert is_subagent() is False
|
|
87
|
+
|
|
88
|
+
Note:
|
|
89
|
+
Token-based reset ensures that even if an exception occurs, the context
|
|
90
|
+
is properly restored. This is especially important in async environments
|
|
91
|
+
where multiple tasks may be running concurrently.
|
|
92
|
+
"""
|
|
93
|
+
# Get current depth for incrementing
|
|
94
|
+
current_depth = _subagent_depth.get()
|
|
95
|
+
|
|
96
|
+
# Set new values and save tokens for restoration
|
|
97
|
+
depth_token = _subagent_depth.set(current_depth + 1)
|
|
98
|
+
name_token = _subagent_name.set(agent_name)
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
yield
|
|
102
|
+
finally:
|
|
103
|
+
# Use token-based reset for proper async isolation
|
|
104
|
+
# This ensures the context is restored even if an exception occurs
|
|
105
|
+
_subagent_depth.reset(depth_token)
|
|
106
|
+
_subagent_name.reset(name_token)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def is_subagent() -> bool:
|
|
110
|
+
"""Check if currently executing within a sub-agent context.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
True if depth > 0 (inside a sub-agent), False otherwise (main agent)
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
>>> is_subagent()
|
|
117
|
+
False
|
|
118
|
+
>>> with subagent_context("retriever"):
|
|
119
|
+
... is_subagent()
|
|
120
|
+
True
|
|
121
|
+
"""
|
|
122
|
+
return _subagent_depth.get() > 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_subagent_name() -> str | None:
|
|
126
|
+
"""Get the name of the current sub-agent.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Current sub-agent name, or None if in main agent context
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
>>> get_subagent_name()
|
|
133
|
+
None
|
|
134
|
+
>>> with subagent_context("husky"):
|
|
135
|
+
... get_subagent_name()
|
|
136
|
+
'husky'
|
|
137
|
+
"""
|
|
138
|
+
return _subagent_name.get()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_subagent_depth() -> int:
|
|
142
|
+
"""Get the current sub-agent nesting depth.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Current depth level (0 = main agent, 1 = first-level sub-agent,
|
|
146
|
+
2 = nested sub-agent, etc.)
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
>>> get_subagent_depth()
|
|
150
|
+
0
|
|
151
|
+
>>> with subagent_context("retriever"):
|
|
152
|
+
... get_subagent_depth()
|
|
153
|
+
1
|
|
154
|
+
... with subagent_context("terrier"):
|
|
155
|
+
... get_subagent_depth()
|
|
156
|
+
2
|
|
157
|
+
"""
|
|
158
|
+
return _subagent_depth.get()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-puppy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.361
|
|
4
4
|
Summary: Code generation agent
|
|
5
5
|
Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
|
|
6
6
|
Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
|
|
@@ -17,7 +17,7 @@ Classifier: Topic :: Software Development :: Code Generators
|
|
|
17
17
|
Requires-Python: <3.14,>=3.11
|
|
18
18
|
Requires-Dist: camoufox>=0.4.11
|
|
19
19
|
Requires-Dist: dbos>=2.5.0
|
|
20
|
-
Requires-Dist: fastapi>=0.
|
|
20
|
+
Requires-Dist: fastapi>=0.109.0
|
|
21
21
|
Requires-Dist: httpx[http2]>=0.24.1
|
|
22
22
|
Requires-Dist: json-repair>=0.46.2
|
|
23
23
|
Requires-Dist: logfire>=0.7.1
|
|
@@ -35,8 +35,9 @@ Requires-Dist: rich>=13.4.2
|
|
|
35
35
|
Requires-Dist: ripgrep==14.1.0
|
|
36
36
|
Requires-Dist: ruff>=0.11.11
|
|
37
37
|
Requires-Dist: tenacity>=8.2.0
|
|
38
|
-
Requires-Dist: termflow-md>=0.1.
|
|
39
|
-
Requires-Dist: uvicorn>=0.
|
|
38
|
+
Requires-Dist: termflow-md>=0.1.8
|
|
39
|
+
Requires-Dist: uvicorn[standard]>=0.27.0
|
|
40
|
+
Requires-Dist: websockets>=12.0
|
|
40
41
|
Description-Content-Type: text/markdown
|
|
41
42
|
|
|
42
43
|
<div align="center">
|
|
@@ -46,20 +47,18 @@ Description-Content-Type: text/markdown
|
|
|
46
47
|
**🐶✨The sassy AI code agent that makes IDEs look outdated** ✨🐶
|
|
47
48
|
|
|
48
49
|
[](https://pypi.org/project/code-puppy/)
|
|
49
|
-
[](https://pypi.org/project/code-puppy/)
|
|
50
51
|
[](https://python.org)
|
|
51
52
|
[](LICENSE)
|
|
52
53
|
[](https://github.com/mpfaffenberger/code_puppy/actions)
|
|
53
|
-
[](https://github.com/mpfaffenberger/code_puppy)
|
|
54
|
-
[](https://github.com/psf/black)
|
|
55
54
|
[](https://github.com/mpfaffenberger/code_puppy/tests)
|
|
56
55
|
|
|
57
|
-
[](https://openai.com)
|
|
56
|
+
[](https://openai.com)
|
|
58
57
|
[](https://ai.google.dev/)
|
|
59
58
|
[](https://anthropic.com)
|
|
60
|
-
[](https://synthetic.new)
|
|
59
|
+
[](https://cerebras.ai)
|
|
60
|
+
[](https://z.ai/)
|
|
61
|
+
[](https://synthetic.new)
|
|
63
62
|
|
|
64
63
|
[](https://github.com/mpfaffenberger/code_puppy)
|
|
65
64
|
[](https://github.com/pydantic/pydantic-ai)
|
|
@@ -69,6 +68,9 @@ Description-Content-Type: text/markdown
|
|
|
69
68
|
[](https://github.com/mpfaffenberger/code_puppy/stargazers)
|
|
70
69
|
[](https://github.com/mpfaffenberger/code_puppy/network)
|
|
71
70
|
|
|
71
|
+
[](https://discord.gg/SqYAaXVy)
|
|
72
|
+
[](https://code-puppy.dev)
|
|
73
|
+
|
|
72
74
|
**[⭐ Star this repo if you hate expensive IDEs! ⭐](#quick-start)**
|
|
73
75
|
|
|
74
76
|
*"Who needs an IDE when you have 1024 angry puppies?"* - Someone, probably.
|