|
47 | 47 | plugins.load_plugin_callbacks() |
48 | 48 |
|
49 | 49 |
|
| 50 | +def _resume_session_from_path(raw_path: str) -> None: |
| 51 | + """Restore agent message history from a saved .pkl session file. |
| 52 | +
|
| 53 | + Accepts any path (autosaves, contexts, somewhere weird on disk). We don't |
| 54 | + care where it lives — we just decompose into (parent_dir, stem) and reuse |
| 55 | + ``session_storage.load_session`` so we stay DRY. |
| 56 | + """ |
| 57 | + from code_puppy.agents.agent_manager import get_current_agent |
| 58 | + from code_puppy.messaging import emit_error, emit_success |
| 59 | + from code_puppy.session_storage import load_session |
| 60 | + |
| 61 | + session_path = Path(raw_path).expanduser().resolve() |
| 62 | + |
| 63 | + if not session_path.exists(): |
| 64 | + emit_error(f"--resume: session file not found: {session_path}") |
| 65 | + sys.exit(1) |
| 66 | + |
| 67 | + if session_path.suffix != ".pkl": |
| 68 | + emit_error( |
| 69 | + f"--resume: expected a .pkl session file, got '{session_path.suffix}': {session_path}" |
| 70 | + ) |
| 71 | + sys.exit(1) |
| 72 | + |
| 73 | + try: |
| 74 | + history = load_session(session_path.stem, session_path.parent) |
| 75 | + except Exception as exc: |
| 76 | + emit_error(f"--resume: failed to load session: {exc}") |
| 77 | + sys.exit(1) |
| 78 | + |
| 79 | + try: |
| 80 | + agent = get_current_agent() |
| 81 | + agent.set_message_history(history) |
| 82 | + except Exception as exc: |
| 83 | + emit_error(f"--resume: failed to attach history to agent: {exc}") |
| 84 | + sys.exit(1) |
| 85 | + |
| 86 | + # Rotate autosave id so we don't clobber the original file we just resumed. |
| 87 | + try: |
| 88 | + from code_puppy.config import rotate_autosave_id |
| 89 | + |
| 90 | + rotate_autosave_id() |
| 91 | + except Exception: |
| 92 | + pass # autosave rotation is best-effort |
| 93 | + |
| 94 | + total_tokens = sum(agent.estimate_tokens_for_message(m) for m in history) |
| 95 | + emit_success( |
| 96 | + f"✅ Resumed session: {len(history)} messages ({total_tokens} tokens)\n" |
| 97 | + f"📁 From: {session_path}" |
| 98 | + ) |
| 99 | + |
| 100 | + |
50 | 101 | async def main(): |
51 | 102 | """Main async entry point for Code Puppy CLI.""" |
52 | 103 | parser = argparse.ArgumentParser(description="Code Puppy - A code generation agent") |
@@ -81,6 +132,13 @@ async def main(): |
81 | 132 | type=str, |
82 | 133 | help="Specify which model to use (e.g., --model gpt-5)", |
83 | 134 | ) |
| 135 | + parser.add_argument( |
| 136 | + "--resume", |
| 137 | + "-r", |
| 138 | + type=str, |
| 139 | + metavar="PATH", |
| 140 | + help="Resume a saved session from a .pkl file (e.g. ~/.code_puppy/contexts/foo.pkl)", |
| 141 | + ) |
84 | 142 | parser.add_argument( |
85 | 143 | "command", nargs="*", help="Run a single command (deprecated, use -p instead)" |
86 | 144 | ) |
@@ -282,6 +340,9 @@ def _uvx_protective_sigint_handler(_sig, _frame): |
282 | 340 |
|
283 | 341 | await callbacks.on_startup() |
284 | 342 |
|
| 343 | + if args.resume: |
| 344 | + _resume_session_from_path(args.resume) |
| 345 | + |
285 | 346 | global shutdown_flag |
286 | 347 | shutdown_flag = False |
287 | 348 | try: |
|
0 commit comments