code-puppy 0.0.128__py3-none-any.whl → 0.0.130__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/runtime_manager.py +23 -4
- code_puppy/command_line/model_picker_completion.py +1 -12
- code_puppy/command_line/prompt_toolkit_completion.py +2 -2
- code_puppy/http_utils.py +1 -1
- code_puppy/main.py +57 -11
- {code_puppy-0.0.128.dist-info → code_puppy-0.0.130.dist-info}/METADATA +3 -1
- {code_puppy-0.0.128.dist-info → code_puppy-0.0.130.dist-info}/RECORD +11 -11
- {code_puppy-0.0.128.data → code_puppy-0.0.130.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.128.dist-info → code_puppy-0.0.130.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.128.dist-info → code_puppy-0.0.130.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.128.dist-info → code_puppy-0.0.130.dist-info}/licenses/LICENSE +0 -0
|
@@ -97,14 +97,33 @@ class RuntimeAgentManager:
|
|
|
97
97
|
except* InterruptedError as ie:
|
|
98
98
|
emit_warning(f"Interrupted: {str(ie)}")
|
|
99
99
|
except* Exception as other_error:
|
|
100
|
-
|
|
100
|
+
# Filter out CancelledError from the exception group - let it propagate
|
|
101
|
+
remaining_exceptions = []
|
|
102
|
+
def collect_non_cancelled_exceptions(exc):
|
|
101
103
|
if isinstance(exc, ExceptionGroup):
|
|
102
104
|
for sub_exc in exc.exceptions:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
collect_non_cancelled_exceptions(sub_exc)
|
|
106
|
+
elif not isinstance(exc, asyncio.CancelledError):
|
|
107
|
+
remaining_exceptions.append(exc)
|
|
105
108
|
emit_warning(f"Unexpected error: {str(exc)}", group_id=group_id)
|
|
106
109
|
emit_warning(f"{str(exc.args)}", group_id=group_id)
|
|
107
|
-
|
|
110
|
+
|
|
111
|
+
collect_non_cancelled_exceptions(other_error)
|
|
112
|
+
|
|
113
|
+
# If there are CancelledError exceptions in the group, re-raise them
|
|
114
|
+
cancelled_exceptions = []
|
|
115
|
+
def collect_cancelled_exceptions(exc):
|
|
116
|
+
if isinstance(exc, ExceptionGroup):
|
|
117
|
+
for sub_exc in exc.exceptions:
|
|
118
|
+
collect_cancelled_exceptions(sub_exc)
|
|
119
|
+
elif isinstance(exc, asyncio.CancelledError):
|
|
120
|
+
cancelled_exceptions.append(exc)
|
|
121
|
+
|
|
122
|
+
collect_cancelled_exceptions(other_error)
|
|
123
|
+
|
|
124
|
+
if cancelled_exceptions:
|
|
125
|
+
# Re-raise the first CancelledError to propagate cancellation
|
|
126
|
+
raise cancelled_exceptions[0]
|
|
108
127
|
|
|
109
128
|
# Create the task FIRST
|
|
110
129
|
agent_task = asyncio.create_task(run_agent_task())
|
|
@@ -70,7 +70,7 @@ class ModelNameCompleter(Completer):
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
def update_model_in_input(text: str) -> Optional[str]:
|
|
73
|
-
# If input starts with /model
|
|
73
|
+
# If input starts with /model and a model name, set model and strip it out
|
|
74
74
|
content = text.strip()
|
|
75
75
|
|
|
76
76
|
# Check for /model command
|
|
@@ -84,17 +84,6 @@ def update_model_in_input(text: str) -> Optional[str]:
|
|
|
84
84
|
if idx != -1:
|
|
85
85
|
new_text = (text[:idx] + text[idx + len("/model" + model) :]).strip()
|
|
86
86
|
return new_text
|
|
87
|
-
# Also check for legacy /m command for backward compatibility
|
|
88
|
-
elif content.startswith("/m"):
|
|
89
|
-
rest = content[2:].strip() # Remove '/m'
|
|
90
|
-
for model in load_model_names():
|
|
91
|
-
if rest == model:
|
|
92
|
-
set_active_model(model)
|
|
93
|
-
# Remove /m from the input
|
|
94
|
-
idx = text.find("/m" + model)
|
|
95
|
-
if idx != -1:
|
|
96
|
-
new_text = (text[:idx] + text[idx + len("/m" + model) :]).strip()
|
|
97
|
-
return new_text
|
|
98
87
|
return None
|
|
99
88
|
|
|
100
89
|
|
|
@@ -163,7 +163,7 @@ async def get_input_with_combined_completion(
|
|
|
163
163
|
completer = merge_completers(
|
|
164
164
|
[
|
|
165
165
|
FilePathCompleter(symbol="@"),
|
|
166
|
-
ModelNameCompleter(trigger="/
|
|
166
|
+
ModelNameCompleter(trigger="/model"),
|
|
167
167
|
CDCompleter(trigger="/cd"),
|
|
168
168
|
SetCompleter(trigger="/set"),
|
|
169
169
|
LoadContextCompleter(trigger="/load_context"),
|
|
@@ -226,7 +226,7 @@ async def get_input_with_combined_completion(
|
|
|
226
226
|
|
|
227
227
|
|
|
228
228
|
if __name__ == "__main__":
|
|
229
|
-
print("Type '@' for path-completion or '/
|
|
229
|
+
print("Type '@' for path-completion or '/model' to pick a model. Ctrl+D to exit.")
|
|
230
230
|
|
|
231
231
|
async def main():
|
|
232
232
|
while True:
|
code_puppy/http_utils.py
CHANGED
|
@@ -90,7 +90,7 @@ def create_reopenable_async_client(
|
|
|
90
90
|
timeout: int = 180,
|
|
91
91
|
verify: Union[bool, str] = None,
|
|
92
92
|
headers: Optional[Dict[str, str]] = None,
|
|
93
|
-
) -> Union[
|
|
93
|
+
) -> Union[ReopenableAsyncClient, httpx.AsyncClient]:
|
|
94
94
|
if verify is None:
|
|
95
95
|
verify = get_cert_bundle_path()
|
|
96
96
|
|
code_puppy/main.py
CHANGED
|
@@ -424,30 +424,76 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
|
424
424
|
from code_puppy.messaging import emit_warning
|
|
425
425
|
from code_puppy.messaging.spinner import ConsoleSpinner
|
|
426
426
|
|
|
427
|
-
#
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
427
|
+
# Create a task that mimics TUI behavior - avoid signal handler conflicts
|
|
428
|
+
current_task = None
|
|
429
|
+
signal_handled = False # Prevent multiple signal handler calls (reset per task)
|
|
430
|
+
|
|
431
|
+
async def run_task():
|
|
432
|
+
# Use the simpler run() method instead of run_with_mcp() to avoid signal handler
|
|
433
|
+
agent = agent_manager.get_agent()
|
|
434
|
+
async with agent:
|
|
435
|
+
return await agent.run(
|
|
432
436
|
task,
|
|
433
|
-
message_history=get_message_history(),
|
|
437
|
+
message_history=get_message_history(),
|
|
434
438
|
usage_limits=get_custom_usage_limits(),
|
|
435
439
|
)
|
|
440
|
+
|
|
441
|
+
def handle_keyboard_interrupt():
|
|
442
|
+
"""Handle Ctrl+C like TUI does - kill processes but only cancel task if no processes killed"""
|
|
443
|
+
nonlocal signal_handled
|
|
444
|
+
if signal_handled:
|
|
445
|
+
return
|
|
446
|
+
signal_handled = True
|
|
447
|
+
|
|
448
|
+
from code_puppy.tools.command_runner import kill_all_running_shell_processes
|
|
449
|
+
|
|
450
|
+
killed = kill_all_running_shell_processes()
|
|
451
|
+
if killed:
|
|
452
|
+
emit_warning(f"🔥 Cancelled {killed} running shell process(es)")
|
|
453
|
+
# Don't cancel the agent task - let it continue processing
|
|
454
|
+
# Shell processes killed, but agent continues running
|
|
455
|
+
else:
|
|
456
|
+
# Only cancel the agent task if NO processes were killed
|
|
457
|
+
if current_task and not current_task.done():
|
|
458
|
+
current_task.cancel()
|
|
459
|
+
emit_warning("⚠️ Processing cancelled by user")
|
|
460
|
+
|
|
461
|
+
# Set up proper signal handling to override asyncio's default behavior
|
|
462
|
+
import signal
|
|
463
|
+
|
|
464
|
+
def signal_handler(sig, frame):
|
|
465
|
+
"""Handle Ctrl+C by killing processes and cancelling the current task"""
|
|
466
|
+
handle_keyboard_interrupt()
|
|
467
|
+
|
|
468
|
+
# Replace asyncio's SIGINT handler with our own
|
|
469
|
+
original_handler = signal.signal(signal.SIGINT, signal_handler)
|
|
470
|
+
|
|
471
|
+
# Use ConsoleSpinner for better user experience
|
|
472
|
+
try:
|
|
473
|
+
with ConsoleSpinner(console=display_console):
|
|
474
|
+
current_task = asyncio.create_task(run_task())
|
|
475
|
+
result = await current_task
|
|
436
476
|
except asyncio.CancelledError:
|
|
437
|
-
# Agent was cancelled by
|
|
477
|
+
# Agent was cancelled by our signal handler
|
|
438
478
|
result = None
|
|
439
479
|
except KeyboardInterrupt:
|
|
440
|
-
#
|
|
441
|
-
emit_warning("\n⚠️ Caught KeyboardInterrupt
|
|
480
|
+
# Fallback - handle Ctrl+C if it gets through as KeyboardInterrupt
|
|
481
|
+
emit_warning("\n⚠️ Caught KeyboardInterrupt")
|
|
482
|
+
handle_keyboard_interrupt()
|
|
442
483
|
result = None
|
|
443
484
|
finally:
|
|
485
|
+
# Restore original signal handler
|
|
486
|
+
if 'original_handler' in locals():
|
|
487
|
+
signal.signal(signal.SIGINT, original_handler)
|
|
444
488
|
set_message_history(
|
|
445
489
|
prune_interrupted_tool_calls(get_message_history())
|
|
446
490
|
)
|
|
447
491
|
|
|
448
|
-
# Check if the task was cancelled
|
|
492
|
+
# Check if the task was cancelled (but don't show message if we just killed processes)
|
|
449
493
|
if result is None:
|
|
450
|
-
|
|
494
|
+
# Only show cancellation message if we actually cancelled the agent task
|
|
495
|
+
# If we just killed shell processes, the agent should continue normally
|
|
496
|
+
pass # Don't always show this message
|
|
451
497
|
# Skip the rest of this loop iteration
|
|
452
498
|
continue
|
|
453
499
|
# Get the structured response
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-puppy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.130
|
|
4
4
|
Summary: Code generation agent
|
|
5
|
+
Project-URL: repository, https://github.com/mpfaffenberger/code_puppy
|
|
6
|
+
Project-URL: HomePage, https://github.com/mpfaffenberger/code_puppy
|
|
5
7
|
Author: Michael Pfaffenberger
|
|
6
8
|
License: MIT
|
|
7
9
|
License-File: LICENSE
|
|
@@ -3,8 +3,8 @@ code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
|
|
|
3
3
|
code_puppy/agent.py,sha256=s6jQH4apmJAQOYsI_52vgQf8CsrjB6aIgXXWgpJ_8Iw,7136
|
|
4
4
|
code_puppy/callbacks.py,sha256=6wYB6K_fGSCkKKEFaYOYkJT45WaV5W_NhUIzcvVH_nU,5060
|
|
5
5
|
code_puppy/config.py,sha256=n0aV-kfIzFkbEbPmwi6dbRGqpfB-pahnpyVyQPIN3-E,15436
|
|
6
|
-
code_puppy/http_utils.py,sha256=
|
|
7
|
-
code_puppy/main.py,sha256=
|
|
6
|
+
code_puppy/http_utils.py,sha256=BAvt4hed7fVMXglA7eS9gOb08h2YTuOyai6VmQq09fg,3432
|
|
7
|
+
code_puppy/main.py,sha256=7Sfsy6WA1bvp3UoACOAwxXlNtpoRe7fiQ0_Qtl0EgbM,25314
|
|
8
8
|
code_puppy/message_history_processor.py,sha256=O2rKp7W6YeIg93W8b0XySTUEQgIZm0f_06--_kzHugM,16145
|
|
9
9
|
code_puppy/model_factory.py,sha256=NoG9wDTosaaDrFIGtq3oq8gDe0J_7N6CUKuesXz87qM,10878
|
|
10
10
|
code_puppy/models.json,sha256=GpvtWnBKERm6T7HCZJQUIVAS5256-tZ_bFuRtnKXEsY,3128
|
|
@@ -20,16 +20,16 @@ code_puppy/agents/agent_creator_agent.py,sha256=hpmr3UmTaoDfcJ0tbMdxyUyxQ941N9XX
|
|
|
20
20
|
code_puppy/agents/agent_manager.py,sha256=2ZHIR6gqnkAffHccFYf93iBF2qwlWKm7vCGk3pnwMNE,6733
|
|
21
21
|
code_puppy/agents/base_agent.py,sha256=qqwQhyCNDQSD-jf6B55Ipim5I2svB4t2v3A3E9kU7Rk,1501
|
|
22
22
|
code_puppy/agents/json_agent.py,sha256=0j6_P1ppje7TsjaZIbxKn8meiuvoBngvjVLNdtCkGwc,4272
|
|
23
|
-
code_puppy/agents/runtime_manager.py,sha256=
|
|
23
|
+
code_puppy/agents/runtime_manager.py,sha256=YCex9YrxE2l16_MaaeyCQEg0hzte4dxYogRB8Lb6_SQ,8982
|
|
24
24
|
code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
|
|
25
25
|
code_puppy/command_line/command_handler.py,sha256=IhDaDa0GmpW0BCFfmkgpL_6iQBJlXEKQC9SDmpj_XeI,20730
|
|
26
26
|
code_puppy/command_line/file_path_completion.py,sha256=gw8NpIxa6GOpczUJRyh7VNZwoXKKn-yvCqit7h2y6Gg,2931
|
|
27
27
|
code_puppy/command_line/load_context_completion.py,sha256=6eZxV6Bs-EFwZjN93V8ZDZUC-6RaWxvtZk-04Wtikyw,2240
|
|
28
28
|
code_puppy/command_line/mcp_commands.py,sha256=qqEdac7zR4Wydn0JE3lTVnLi3dh_Lc8DJmjOq8dQeJM,55379
|
|
29
29
|
code_puppy/command_line/meta_command_handler.py,sha256=02NU4Lspf5qRMPTsrGiMRLSUshZhdmS0XQA26k8vUjw,5665
|
|
30
|
-
code_puppy/command_line/model_picker_completion.py,sha256=
|
|
30
|
+
code_puppy/command_line/model_picker_completion.py,sha256=xvwgthVmLRA9a8RJG6iFImxR2yD6rJYPJJav0YJoVCc,3599
|
|
31
31
|
code_puppy/command_line/motd.py,sha256=PEdkp3ZnydVfvd7mNJylm8YyFNUKg9jmY6uwkA1em8c,2152
|
|
32
|
-
code_puppy/command_line/prompt_toolkit_completion.py,sha256=
|
|
32
|
+
code_puppy/command_line/prompt_toolkit_completion.py,sha256=BKNw-DwacZPNTKjjXlxnjrd4q7UfOVynReUFQrVes_g,9052
|
|
33
33
|
code_puppy/command_line/utils.py,sha256=7eyxDHjPjPB9wGDJQQcXV_zOsGdYsFgI0SGCetVmTqE,1251
|
|
34
34
|
code_puppy/mcp/__init__.py,sha256=LJd9mGStskhXYBEp1UhtHlrAQ3rCHnfTa7KSmqtZe34,1143
|
|
35
35
|
code_puppy/mcp/async_lifecycle.py,sha256=45tw7ZcDV6LVBrTvvNkMCDhnTapgQCYcc01W8Gp9c5A,8064
|
|
@@ -100,9 +100,9 @@ code_puppy/tui/tests/test_sidebar_history_navigation.py,sha256=JGiyua8A2B8dLfwiE
|
|
|
100
100
|
code_puppy/tui/tests/test_status_bar.py,sha256=nYT_FZGdmqnnbn6o0ZuOkLtNUtJzLSmtX8P72liQ5Vo,1797
|
|
101
101
|
code_puppy/tui/tests/test_timestamped_history.py,sha256=nVXt9hExZZ_8MFP-AZj4L4bB_1Eo_mc-ZhVICzTuw3I,1799
|
|
102
102
|
code_puppy/tui/tests/test_tools.py,sha256=kgzzAkK4r0DPzQwHHD4cePpVNgrHor6cFr05Pg6DBWg,2687
|
|
103
|
-
code_puppy-0.0.
|
|
104
|
-
code_puppy-0.0.
|
|
105
|
-
code_puppy-0.0.
|
|
106
|
-
code_puppy-0.0.
|
|
107
|
-
code_puppy-0.0.
|
|
108
|
-
code_puppy-0.0.
|
|
103
|
+
code_puppy-0.0.130.data/data/code_puppy/models.json,sha256=GpvtWnBKERm6T7HCZJQUIVAS5256-tZ_bFuRtnKXEsY,3128
|
|
104
|
+
code_puppy-0.0.130.dist-info/METADATA,sha256=4ZUO2tCKE7Iztu63OFd-FDXLU5_hTXyjyfbddKu8hbI,19873
|
|
105
|
+
code_puppy-0.0.130.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
106
|
+
code_puppy-0.0.130.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
|
|
107
|
+
code_puppy-0.0.130.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
|
108
|
+
code_puppy-0.0.130.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|