code-puppy 0.0.193__py3-none-any.whl → 0.0.194__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/command_line/command_handler.py +13 -1
- code_puppy/config.py +165 -0
- code_puppy/main.py +6 -2
- {code_puppy-0.0.193.dist-info → code_puppy-0.0.194.dist-info}/METADATA +1 -1
- {code_puppy-0.0.193.dist-info → code_puppy-0.0.194.dist-info}/RECORD +9 -9
- {code_puppy-0.0.193.data → code_puppy-0.0.194.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.193.dist-info → code_puppy-0.0.194.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.193.dist-info → code_puppy-0.0.194.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.193.dist-info → code_puppy-0.0.194.dist-info}/licenses/LICENSE +0 -0
@@ -76,10 +76,22 @@ def get_commands_help():
|
|
76
76
|
Text("/load_context", style="cyan")
|
77
77
|
+ Text(" <name> Load message history from file")
|
78
78
|
)
|
79
|
+
help_lines.append(
|
80
|
+
Text("", style="cyan")
|
81
|
+
+ Text("Session Management:", style="bold yellow")
|
82
|
+
)
|
83
|
+
help_lines.append(
|
84
|
+
Text("auto_save_session", style="cyan")
|
85
|
+
+ Text(" Auto-save session after each response (true/false)")
|
86
|
+
)
|
87
|
+
help_lines.append(
|
88
|
+
Text("max_saved_sessions", style="cyan")
|
89
|
+
+ Text(" Maximum number of sessions to keep (default: 20, 0 = unlimited)")
|
90
|
+
)
|
79
91
|
help_lines.append(
|
80
92
|
Text("/set", style="cyan")
|
81
93
|
+ Text(
|
82
|
-
" Set puppy config key-values (e.g., /set yolo_mode true, /set
|
94
|
+
" Set puppy config key-values (e.g., /set yolo_mode true, /set auto_save_session true, /set max_saved_sessions 20)"
|
83
95
|
)
|
84
96
|
)
|
85
97
|
help_lines.append(
|
code_puppy/config.py
CHANGED
@@ -121,6 +121,8 @@ def get_config_keys():
|
|
121
121
|
"message_limit",
|
122
122
|
"allow_recursion",
|
123
123
|
"openai_reasoning_effort",
|
124
|
+
"auto_save_session",
|
125
|
+
"max_saved_sessions",
|
124
126
|
]
|
125
127
|
config = configparser.ConfigParser()
|
126
128
|
config.read(CONFIG_FILE)
|
@@ -645,3 +647,166 @@ def clear_agent_pinned_model(agent_name: str):
|
|
645
647
|
# We can't easily delete keys from configparser, so set to empty string
|
646
648
|
# which will be treated as None by get_agent_pinned_model
|
647
649
|
set_config_value(f"agent_model_{agent_name}", "")
|
650
|
+
|
651
|
+
|
652
|
+
def get_auto_save_session() -> bool:
|
653
|
+
"""
|
654
|
+
Checks puppy.cfg for 'auto_save_session' (case-insensitive in value only).
|
655
|
+
Defaults to True if not set.
|
656
|
+
Allowed values for ON: 1, '1', 'true', 'yes', 'on' (all case-insensitive for value).
|
657
|
+
"""
|
658
|
+
true_vals = {"1", "true", "yes", "on"}
|
659
|
+
cfg_val = get_value("auto_save_session")
|
660
|
+
if cfg_val is not None:
|
661
|
+
if str(cfg_val).strip().lower() in true_vals:
|
662
|
+
return True
|
663
|
+
return False
|
664
|
+
return True
|
665
|
+
|
666
|
+
|
667
|
+
def set_auto_save_session(enabled: bool):
|
668
|
+
"""Sets the auto_save_session configuration value.
|
669
|
+
|
670
|
+
Args:
|
671
|
+
enabled: Whether to enable auto-saving of sessions
|
672
|
+
"""
|
673
|
+
set_config_value("auto_save_session", "true" if enabled else "false")
|
674
|
+
|
675
|
+
|
676
|
+
def get_max_saved_sessions() -> int:
|
677
|
+
"""
|
678
|
+
Gets the maximum number of sessions to keep.
|
679
|
+
Defaults to 20 if not set.
|
680
|
+
"""
|
681
|
+
cfg_val = get_value("max_saved_sessions")
|
682
|
+
if cfg_val is not None:
|
683
|
+
try:
|
684
|
+
val = int(cfg_val)
|
685
|
+
return max(0, val) # Ensure non-negative
|
686
|
+
except (ValueError, TypeError):
|
687
|
+
pass
|
688
|
+
return 20
|
689
|
+
|
690
|
+
|
691
|
+
def set_max_saved_sessions(max_sessions: int):
|
692
|
+
"""Sets the max_saved_sessions configuration value.
|
693
|
+
|
694
|
+
Args:
|
695
|
+
max_sessions: Maximum number of sessions to keep (0 for unlimited)
|
696
|
+
"""
|
697
|
+
set_config_value("max_saved_sessions", str(max_sessions))
|
698
|
+
|
699
|
+
|
700
|
+
def _cleanup_old_sessions():
|
701
|
+
"""Remove oldest sessions if we exceed the max_saved_sessions limit."""
|
702
|
+
max_sessions = get_max_saved_sessions()
|
703
|
+
if max_sessions <= 0: # 0 means unlimited
|
704
|
+
return
|
705
|
+
|
706
|
+
from pathlib import Path
|
707
|
+
|
708
|
+
contexts_dir = Path(CONFIG_DIR) / "contexts"
|
709
|
+
if not contexts_dir.exists():
|
710
|
+
return
|
711
|
+
|
712
|
+
# Get all .pkl files (session files) and sort by modification time
|
713
|
+
session_files = []
|
714
|
+
for pkl_file in contexts_dir.glob("*.pkl"):
|
715
|
+
try:
|
716
|
+
session_files.append((pkl_file.stat().st_mtime, pkl_file))
|
717
|
+
except OSError:
|
718
|
+
continue
|
719
|
+
|
720
|
+
# Sort by modification time (oldest first)
|
721
|
+
session_files.sort(key=lambda x: x[0])
|
722
|
+
|
723
|
+
# If we have more than max_sessions, remove the oldest ones
|
724
|
+
if len(session_files) > max_sessions:
|
725
|
+
files_to_remove = session_files[:-max_sessions] # All except the last max_sessions
|
726
|
+
|
727
|
+
from rich.console import Console
|
728
|
+
console = Console()
|
729
|
+
|
730
|
+
for _, old_file in files_to_remove:
|
731
|
+
try:
|
732
|
+
# Remove the .pkl file
|
733
|
+
old_file.unlink()
|
734
|
+
|
735
|
+
# Also remove the corresponding _meta.json file if it exists
|
736
|
+
meta_file = contexts_dir / f"{old_file.stem}_meta.json"
|
737
|
+
if meta_file.exists():
|
738
|
+
meta_file.unlink()
|
739
|
+
|
740
|
+
console.print(f"[dim]🗑️ Removed old session: {old_file.name}[/dim]")
|
741
|
+
|
742
|
+
except OSError as e:
|
743
|
+
console.print(f"[dim]❌ Failed to remove {old_file.name}: {e}[/dim]")
|
744
|
+
|
745
|
+
|
746
|
+
def auto_save_session_if_enabled() -> bool:
|
747
|
+
"""Automatically save the current session if auto_save_session is enabled.
|
748
|
+
|
749
|
+
Returns:
|
750
|
+
True if session was saved, False otherwise
|
751
|
+
"""
|
752
|
+
if not get_auto_save_session():
|
753
|
+
return False
|
754
|
+
|
755
|
+
try:
|
756
|
+
import datetime
|
757
|
+
import json
|
758
|
+
import pickle
|
759
|
+
from pathlib import Path
|
760
|
+
from code_puppy.agents.agent_manager import get_current_agent
|
761
|
+
|
762
|
+
# Get current agent and message history
|
763
|
+
current_agent = get_current_agent()
|
764
|
+
history = current_agent.get_message_history()
|
765
|
+
|
766
|
+
if not history:
|
767
|
+
return False # No history to save
|
768
|
+
|
769
|
+
# Create timestamp-based session name
|
770
|
+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
771
|
+
session_name = f"auto_session_{timestamp}"
|
772
|
+
|
773
|
+
# Create contexts directory if it doesn't exist
|
774
|
+
contexts_dir = Path(CONFIG_DIR) / "contexts"
|
775
|
+
contexts_dir.mkdir(parents=True, exist_ok=True)
|
776
|
+
|
777
|
+
# Save as pickle for exact preservation
|
778
|
+
pickle_file = contexts_dir / f"{session_name}.pkl"
|
779
|
+
with open(pickle_file, "wb") as f:
|
780
|
+
pickle.dump(history, f)
|
781
|
+
|
782
|
+
# Also save metadata as JSON for readability
|
783
|
+
meta_file = contexts_dir / f"{session_name}_meta.json"
|
784
|
+
metadata = {
|
785
|
+
"session_name": session_name,
|
786
|
+
"timestamp": datetime.datetime.now().isoformat(),
|
787
|
+
"message_count": len(history),
|
788
|
+
"total_tokens": sum(
|
789
|
+
current_agent.estimate_tokens_for_message(m) for m in history
|
790
|
+
),
|
791
|
+
"file_path": str(pickle_file),
|
792
|
+
"auto_saved": True,
|
793
|
+
}
|
794
|
+
|
795
|
+
with open(meta_file, "w") as f:
|
796
|
+
json.dump(metadata, f, indent=2)
|
797
|
+
|
798
|
+
from rich.console import Console
|
799
|
+
console = Console()
|
800
|
+
console.print(
|
801
|
+
f"🐾 [dim]Auto-saved session: {len(history)} messages ({metadata['total_tokens']} tokens)[/dim]"
|
802
|
+
)
|
803
|
+
|
804
|
+
# Cleanup old sessions if limit is set
|
805
|
+
_cleanup_old_sessions()
|
806
|
+
return True
|
807
|
+
|
808
|
+
except Exception as e:
|
809
|
+
from rich.console import Console
|
810
|
+
console = Console()
|
811
|
+
console.print(f"[dim]❌ Failed to auto-save session: {str(e)}[/dim]")
|
812
|
+
return False
|
code_puppy/main.py
CHANGED
@@ -456,11 +456,15 @@ async def interactive_mode(message_renderer, initial_command: str = None) -> Non
|
|
456
456
|
f"\n[bold purple]AGENT RESPONSE: [/bold purple]\n{agent_response}"
|
457
457
|
)
|
458
458
|
|
459
|
+
# Auto-save session if enabled
|
460
|
+
from code_puppy.config import auto_save_session_if_enabled
|
461
|
+
auto_save_session_if_enabled()
|
462
|
+
|
459
463
|
# Ensure console output is flushed before next prompt
|
460
464
|
# This fixes the issue where prompt doesn't appear after agent response
|
461
465
|
display_console.file.flush() if hasattr(
|
462
466
|
display_console.file, "flush"
|
463
|
-
|
467
|
+
) else None
|
464
468
|
import time
|
465
469
|
|
466
470
|
time.sleep(0.1) # Brief pause to ensure all messages are rendered
|
@@ -592,4 +596,4 @@ def main_entry():
|
|
592
596
|
|
593
597
|
|
594
598
|
if __name__ == "__main__":
|
595
|
-
main_entry()
|
599
|
+
main_entry()
|
@@ -1,9 +1,9 @@
|
|
1
1
|
code_puppy/__init__.py,sha256=ehbM1-wMjNmOXk_DBhhJECFyBv2dRHwwo7ucjHeM68E,107
|
2
2
|
code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
|
3
3
|
code_puppy/callbacks.py,sha256=ukSgVFaEO68o6J09qFwDrnmNanrVv3toTLQhS504Meo,6162
|
4
|
-
code_puppy/config.py,sha256=
|
4
|
+
code_puppy/config.py,sha256=5axUM2QHUF9kEBRP7yBzdLhaUNjKzSJhXYdRb5uyxiY,26991
|
5
5
|
code_puppy/http_utils.py,sha256=YLd8Y16idbI32JGeBXG8n5rT4o4X_zxk9FgUvK9XFo8,8248
|
6
|
-
code_puppy/main.py,sha256=
|
6
|
+
code_puppy/main.py,sha256=k91xsgVXOTUFR_njg5k9kbQ7HfprfGYV6u189WoZju0,21828
|
7
7
|
code_puppy/model_factory.py,sha256=ZbIAJWMNKNdTCEMQK8Ig6TDDZlVNyGO9hOLHoLLPMYw,15397
|
8
8
|
code_puppy/models.json,sha256=dClUciCo2RlVDs0ZAQCIur8MOavZUEAXHEecn0uPa-4,1629
|
9
9
|
code_puppy/reopenable_async_client.py,sha256=4UJRaMp5np8cbef9F0zKQ7TPKOfyf5U-Kv-0zYUWDho,8274
|
@@ -29,7 +29,7 @@ code_puppy/agents/agent_typescript_reviewer.py,sha256=EDY1mFkVpuJ1BPXsJFu2wQ2pfA
|
|
29
29
|
code_puppy/agents/base_agent.py,sha256=rJm0xA9kLT_NU9MSZIrN-Z_T5O4Q-QuUmQM8paGZKHQ,39066
|
30
30
|
code_puppy/agents/json_agent.py,sha256=KPS1q-Rr3b5ekem4i3wtu8eLJRDd5nSPiZ8duJ_tn0U,4630
|
31
31
|
code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
|
32
|
-
code_puppy/command_line/command_handler.py,sha256=
|
32
|
+
code_puppy/command_line/command_handler.py,sha256=kTZ6ES35b2xnYxrVR7HZCPKp8S9vomgUKR9QIjPbmgQ,31469
|
33
33
|
code_puppy/command_line/file_path_completion.py,sha256=gw8NpIxa6GOpczUJRyh7VNZwoXKKn-yvCqit7h2y6Gg,2931
|
34
34
|
code_puppy/command_line/load_context_completion.py,sha256=6eZxV6Bs-EFwZjN93V8ZDZUC-6RaWxvtZk-04Wtikyw,2240
|
35
35
|
code_puppy/command_line/model_picker_completion.py,sha256=vYNCZS1QWu6fxF__hTwpc7jwH7h_48wUxrnITawc83E,4140
|
@@ -120,9 +120,9 @@ code_puppy/tui/screens/help.py,sha256=eJuPaOOCp7ZSUlecearqsuX6caxWv7NQszUh0tZJjB
|
|
120
120
|
code_puppy/tui/screens/mcp_install_wizard.py,sha256=vObpQwLbXjQsxmSg-WCasoev1usEi0pollKnL0SHu9U,27693
|
121
121
|
code_puppy/tui/screens/settings.py,sha256=-WLldnKyWVKUYVPJcfOn1UU6eP9t8lLPUAVI317SOOM,10685
|
122
122
|
code_puppy/tui/screens/tools.py,sha256=3pr2Xkpa9Js6Yhf1A3_wQVRzFOui-KDB82LwrsdBtyk,1715
|
123
|
-
code_puppy-0.0.
|
124
|
-
code_puppy-0.0.
|
125
|
-
code_puppy-0.0.
|
126
|
-
code_puppy-0.0.
|
127
|
-
code_puppy-0.0.
|
128
|
-
code_puppy-0.0.
|
123
|
+
code_puppy-0.0.194.data/data/code_puppy/models.json,sha256=dClUciCo2RlVDs0ZAQCIur8MOavZUEAXHEecn0uPa-4,1629
|
124
|
+
code_puppy-0.0.194.dist-info/METADATA,sha256=s2UdXeovRnMviRqdtBhkDuE1Eq_k4g6DClEgoXQ2etA,19987
|
125
|
+
code_puppy-0.0.194.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
126
|
+
code_puppy-0.0.194.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
|
127
|
+
code_puppy-0.0.194.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
128
|
+
code_puppy-0.0.194.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|