code-puppy 0.0.128__py3-none-any.whl → 0.0.129__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.
@@ -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
- def log_exceptions(exc):
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
- log_exceptions(sub_exc)
104
- else:
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
- log_exceptions(other_error)
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())
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["ReopenableAsyncClient", httpx.AsyncClient]:
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
- # Use ConsoleSpinner for better user experience
428
- try:
429
- with ConsoleSpinner(console=display_console):
430
- # The manager handles all cancellation logic internally
431
- result = await agent_manager.run_with_mcp(
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 user
477
+ # Agent was cancelled by our signal handler
438
478
  result = None
439
479
  except KeyboardInterrupt:
440
- # Keyboard interrupt
441
- emit_warning("\n⚠️ Caught KeyboardInterrupt in main")
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
- emit_warning("\n⚠️ Processing cancelled by user (Ctrl+C)")
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.128
3
+ Version: 0.0.129
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=zN0_F8cq4agamL3GRRLLhgEjBgjKtWrgGe8thORC7YE,3434
7
- code_puppy/main.py,sha256=s0OShY_oVPsTIZd6Us90ju1DZhmjfqkUbNniD8cvjq0,22722
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,7 +20,7 @@ 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=M42a2kxNQlItIPtC0--VTrtm7czlXsTGVTOkbySJ6a0,7916
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
@@ -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.128.data/data/code_puppy/models.json,sha256=GpvtWnBKERm6T7HCZJQUIVAS5256-tZ_bFuRtnKXEsY,3128
104
- code_puppy-0.0.128.dist-info/METADATA,sha256=BVbsZj83x6XOW2FdMdzNEAWskZAUBkMNYbYs7RmFTwM,19735
105
- code_puppy-0.0.128.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
106
- code_puppy-0.0.128.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
107
- code_puppy-0.0.128.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
108
- code_puppy-0.0.128.dist-info/RECORD,,
103
+ code_puppy-0.0.129.data/data/code_puppy/models.json,sha256=GpvtWnBKERm6T7HCZJQUIVAS5256-tZ_bFuRtnKXEsY,3128
104
+ code_puppy-0.0.129.dist-info/METADATA,sha256=I9nCCixmGJa-fFUGfQXI7QGPgouFNu59OtbF-6niPWg,19873
105
+ code_puppy-0.0.129.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
106
+ code_puppy-0.0.129.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
107
+ code_puppy-0.0.129.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
108
+ code_puppy-0.0.129.dist-info/RECORD,,