claude-mpm 3.3.0__py3-none-any.whl → 3.4.0__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.
Files changed (58) hide show
  1. claude_mpm/agents/templates/data_engineer.json +1 -1
  2. claude_mpm/agents/templates/documentation.json +1 -1
  3. claude_mpm/agents/templates/engineer.json +1 -1
  4. claude_mpm/agents/templates/ops.json +1 -1
  5. claude_mpm/agents/templates/pm.json +1 -1
  6. claude_mpm/agents/templates/qa.json +1 -1
  7. claude_mpm/agents/templates/research.json +1 -1
  8. claude_mpm/agents/templates/security.json +1 -1
  9. claude_mpm/agents/templates/test_integration.json +112 -0
  10. claude_mpm/agents/templates/version_control.json +1 -1
  11. claude_mpm/cli/commands/memory.py +749 -26
  12. claude_mpm/cli/commands/run.py +115 -14
  13. claude_mpm/cli/parser.py +89 -1
  14. claude_mpm/constants.py +6 -0
  15. claude_mpm/core/claude_runner.py +74 -11
  16. claude_mpm/core/config.py +1 -1
  17. claude_mpm/core/session_manager.py +46 -0
  18. claude_mpm/core/simple_runner.py +74 -11
  19. claude_mpm/hooks/builtin/mpm_command_hook.py +5 -5
  20. claude_mpm/hooks/claude_hooks/hook_handler.py +213 -30
  21. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +9 -2
  22. claude_mpm/hooks/memory_integration_hook.py +51 -5
  23. claude_mpm/services/__init__.py +23 -5
  24. claude_mpm/services/agent_memory_manager.py +800 -71
  25. claude_mpm/services/memory_builder.py +823 -0
  26. claude_mpm/services/memory_optimizer.py +619 -0
  27. claude_mpm/services/memory_router.py +445 -0
  28. claude_mpm/services/project_analyzer.py +771 -0
  29. claude_mpm/services/socketio_server.py +649 -45
  30. claude_mpm/services/version_control/git_operations.py +26 -0
  31. claude_mpm-3.4.0.dist-info/METADATA +183 -0
  32. {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/RECORD +36 -52
  33. claude_mpm/agents/agent-template.yaml +0 -83
  34. claude_mpm/agents/templates/test-integration-agent.md +0 -34
  35. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +0 -6
  36. claude_mpm/cli/README.md +0 -109
  37. claude_mpm/cli_module/refactoring_guide.md +0 -253
  38. claude_mpm/core/agent_registry.py.bak +0 -312
  39. claude_mpm/core/base_service.py.bak +0 -406
  40. claude_mpm/core/websocket_handler.py +0 -233
  41. claude_mpm/hooks/README.md +0 -97
  42. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +0 -66
  43. claude_mpm/schemas/README_SECURITY.md +0 -92
  44. claude_mpm/schemas/agent_schema.json +0 -395
  45. claude_mpm/schemas/agent_schema_documentation.md +0 -181
  46. claude_mpm/schemas/agent_schema_security_notes.md +0 -165
  47. claude_mpm/schemas/examples/standard_workflow.json +0 -505
  48. claude_mpm/schemas/ticket_workflow_documentation.md +0 -482
  49. claude_mpm/schemas/ticket_workflow_schema.json +0 -590
  50. claude_mpm/services/framework_claude_md_generator/README.md +0 -92
  51. claude_mpm/services/parent_directory_manager/README.md +0 -83
  52. claude_mpm/services/version_control/VERSION +0 -1
  53. claude_mpm/services/websocket_server.py +0 -376
  54. claude_mpm-3.3.0.dist-info/METADATA +0 -432
  55. {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/WHEEL +0 -0
  56. {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/entry_points.txt +0 -0
  57. {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/licenses/LICENSE +0 -0
  58. {claude_mpm-3.3.0.dist-info → claude_mpm-3.4.0.dist-info}/top_level.txt +0 -0
@@ -11,12 +11,13 @@ import sys
11
11
  import time
12
12
  import webbrowser
13
13
  from pathlib import Path
14
+ from datetime import datetime
14
15
 
15
16
  from ...core.logger import get_logger
16
17
  from ...constants import LogLevel
17
18
  from ..utils import get_user_input, list_agent_versions_at_startup
18
19
  from ...utils.dependency_manager import ensure_socketio_dependencies
19
- from ...deployment_paths import get_monitor_html_path, get_scripts_dir, get_package_root
20
+ from ...deployment_paths import get_scripts_dir, get_package_root
20
21
 
21
22
 
22
23
  def filter_claude_mpm_args(claude_args):
@@ -80,10 +81,19 @@ def filter_claude_mpm_args(claude_args):
80
81
  # Also skip the next argument if this flag expects a value
81
82
  value_expecting_flags = {
82
83
  '--websocket-port', '--launch-method', '--logging', '--log-dir',
83
- '--framework-path', '--agents-dir', '-i', '--input', '--resume'
84
+ '--framework-path', '--agents-dir', '-i', '--input'
84
85
  }
86
+ optional_value_flags = {
87
+ '--resume' # These flags can have optional values (nargs="?")
88
+ }
89
+
85
90
  if arg in value_expecting_flags and i < len(claude_args):
86
91
  i += 1 # Skip the value too
92
+ elif arg in optional_value_flags and i < len(claude_args):
93
+ # For optional value flags, only skip next arg if it doesn't start with --
94
+ next_arg = claude_args[i]
95
+ if not next_arg.startswith('--'):
96
+ i += 1 # Skip the value
87
97
  else:
88
98
  # This is not a claude-mpm flag, keep it
89
99
  filtered_args.append(arg)
@@ -92,6 +102,58 @@ def filter_claude_mpm_args(claude_args):
92
102
  return filtered_args
93
103
 
94
104
 
105
+ def create_session_context(session_id, session_manager):
106
+ """
107
+ Create enhanced context for resumed sessions.
108
+
109
+ WHY: When resuming a session, we want to provide Claude with context about
110
+ the previous session including what agents were used and when it was created.
111
+ This helps maintain continuity across session boundaries.
112
+
113
+ Args:
114
+ session_id: Session ID being resumed
115
+ session_manager: SessionManager instance
116
+
117
+ Returns:
118
+ Enhanced context string with session information
119
+ """
120
+ try:
121
+ from ...core.claude_runner import create_simple_context
122
+ except ImportError:
123
+ from claude_mpm.core.claude_runner import create_simple_context
124
+
125
+ base_context = create_simple_context()
126
+
127
+ session_data = session_manager.get_session_by_id(session_id)
128
+ if not session_data:
129
+ return base_context
130
+
131
+ # Add session resumption information
132
+ session_info = f"""
133
+
134
+ # Session Resumption
135
+
136
+ You are resuming session {session_id[:8]}... which was:
137
+ - Created: {session_data.get('created_at', 'unknown')}
138
+ - Last used: {session_data.get('last_used', 'unknown')}
139
+ - Context: {session_data.get('context', 'default')}
140
+ - Use count: {session_data.get('use_count', 0)}
141
+ """
142
+
143
+ # Add information about agents previously run in this session
144
+ agents_run = session_data.get('agents_run', [])
145
+ if agents_run:
146
+ session_info += "\n- Previous agent activity:\n"
147
+ for agent_info in agents_run[-5:]: # Show last 5 agents
148
+ session_info += f" • {agent_info.get('agent', 'unknown')}: {agent_info.get('task', 'no description')[:50]}...\n"
149
+ if len(agents_run) > 5:
150
+ session_info += f" (and {len(agents_run) - 5} other agent interactions)\n"
151
+
152
+ session_info += "\nContinue from where you left off in this session."
153
+
154
+ return base_context + session_info
155
+
156
+
95
157
  def run_session(args):
96
158
  """
97
159
  Run a simplified Claude session.
@@ -112,8 +174,44 @@ def run_session(args):
112
174
 
113
175
  try:
114
176
  from ...core.claude_runner import ClaudeRunner, create_simple_context
177
+ from ...core.session_manager import SessionManager
115
178
  except ImportError:
116
179
  from claude_mpm.core.claude_runner import ClaudeRunner, create_simple_context
180
+ from claude_mpm.core.session_manager import SessionManager
181
+
182
+ # Handle session resumption
183
+ session_manager = SessionManager()
184
+ resume_session_id = None
185
+ resume_context = None
186
+
187
+ if hasattr(args, 'resume') and args.resume:
188
+ if args.resume == "last":
189
+ # Resume the last interactive session
190
+ resume_session_id = session_manager.get_last_interactive_session()
191
+ if resume_session_id:
192
+ session_data = session_manager.get_session_by_id(resume_session_id)
193
+ if session_data:
194
+ resume_context = session_data.get("context", "default")
195
+ logger.info(f"Resuming session {resume_session_id} (context: {resume_context})")
196
+ print(f"🔄 Resuming session {resume_session_id[:8]}... (created: {session_data.get('created_at', 'unknown')})")
197
+ else:
198
+ logger.warning(f"Session {resume_session_id} not found")
199
+ else:
200
+ logger.info("No recent interactive sessions found")
201
+ print("ℹ️ No recent interactive sessions found to resume")
202
+ else:
203
+ # Resume specific session by ID
204
+ resume_session_id = args.resume
205
+ session_data = session_manager.get_session_by_id(resume_session_id)
206
+ if session_data:
207
+ resume_context = session_data.get("context", "default")
208
+ logger.info(f"Resuming session {resume_session_id} (context: {resume_context})")
209
+ print(f"🔄 Resuming session {resume_session_id[:8]}... (context: {resume_context})")
210
+ else:
211
+ logger.error(f"Session {resume_session_id} not found")
212
+ print(f"❌ Session {resume_session_id} not found")
213
+ print("💡 Use 'claude-mpm sessions' to list available sessions")
214
+ return
117
215
 
118
216
  # Skip native agents if disabled
119
217
  if getattr(args, 'no_native_agents', False):
@@ -189,8 +287,19 @@ def run_session(args):
189
287
  # Pass information about whether we already opened the browser in run.py
190
288
  runner._browser_opened_by_cli = getattr(args, '_browser_opened_by_cli', False)
191
289
 
192
- # Create basic context
193
- context = create_simple_context()
290
+ # Create context - use resumed session context if available
291
+ if resume_session_id and resume_context:
292
+ # For resumed sessions, create enhanced context with session information
293
+ context = create_session_context(resume_session_id, session_manager)
294
+ # Update session usage
295
+ session_manager.active_sessions[resume_session_id]["last_used"] = datetime.now().isoformat()
296
+ session_manager.active_sessions[resume_session_id]["use_count"] += 1
297
+ session_manager._save_sessions()
298
+ else:
299
+ # Create a new session for tracking
300
+ new_session_id = session_manager.create_session("default")
301
+ context = create_simple_context()
302
+ logger.info(f"Created new session {new_session_id}")
194
303
 
195
304
  # For monitor mode, we handled everything in launch_socketio_monitor
196
305
  # No need for ClaudeRunner browser delegation
@@ -260,16 +369,8 @@ def launch_socketio_monitor(port, logger):
260
369
 
261
370
  socketio_port = port
262
371
 
263
- # Get path to monitor HTML using deployment paths
264
- html_file_path = get_monitor_html_path()
265
-
266
- if not html_file_path.exists():
267
- logger.error(f"Monitor HTML file not found: {html_file_path}")
268
- print(f"❌ Monitor HTML file not found: {html_file_path}")
269
- return False, False
270
-
271
- # Create file:// URL with port parameter
272
- dashboard_url = f'file://{html_file_path.absolute()}?port={socketio_port}'
372
+ # Use HTTP URL to access dashboard from Socket.IO server
373
+ dashboard_url = f'http://localhost:{socketio_port}'
273
374
 
274
375
  # Check if Socket.IO server is already running
275
376
  server_running = _check_socketio_server_running(socketio_port, logger)
claude_mpm/cli/parser.py CHANGED
@@ -119,6 +119,13 @@ def add_run_arguments(parser: argparse.ArgumentParser) -> None:
119
119
  default=8765,
120
120
  help="WebSocket server port (default: 8765)"
121
121
  )
122
+ run_group.add_argument(
123
+ "--resume",
124
+ type=str,
125
+ nargs="?",
126
+ const="last",
127
+ help="Resume a session (last session if no ID specified, or specific session ID)"
128
+ )
122
129
 
123
130
  # Input/output options
124
131
  io_group = parser.add_argument_group('input/output options')
@@ -214,6 +221,13 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
214
221
  default=8765,
215
222
  help="WebSocket server port (default: 8765)"
216
223
  )
224
+ run_group.add_argument(
225
+ "--resume",
226
+ type=str,
227
+ nargs="?",
228
+ const="last",
229
+ help="Resume a session (last session if no ID specified, or specific session ID)"
230
+ )
217
231
 
218
232
  # Input/output options
219
233
  io_group = parser.add_argument_group('input/output options (when no command specified)')
@@ -352,6 +366,12 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
352
366
  metavar="SUBCOMMAND"
353
367
  )
354
368
 
369
+ # Init command
370
+ init_parser = memory_subparsers.add_parser(
371
+ MemoryCommands.INIT.value,
372
+ help="Initialize project-specific memories via PM agent"
373
+ )
374
+
355
375
  # Status command
356
376
  status_parser = memory_subparsers.add_parser(
357
377
  MemoryCommands.STATUS.value,
@@ -365,7 +385,8 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
365
385
  )
366
386
  view_parser.add_argument(
367
387
  "agent_id",
368
- help="Agent ID to view memory for"
388
+ nargs="?",
389
+ help="Agent ID to view memory for (optional - shows all agents if not provided)"
369
390
  )
370
391
 
371
392
  # Add command
@@ -393,6 +414,73 @@ def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argp
393
414
  help="Clean up old/unused memory files"
394
415
  )
395
416
 
417
+ # Optimize command
418
+ optimize_parser = memory_subparsers.add_parser(
419
+ MemoryCommands.OPTIMIZE.value,
420
+ help="Optimize memory files by removing duplicates and consolidating similar items"
421
+ )
422
+ optimize_parser.add_argument(
423
+ "agent_id",
424
+ nargs="?",
425
+ help="Agent ID to optimize (optimize all if not specified)"
426
+ )
427
+
428
+ # Build command
429
+ build_parser = memory_subparsers.add_parser(
430
+ MemoryCommands.BUILD.value,
431
+ help="Build agent memories from project documentation"
432
+ )
433
+ build_parser.add_argument(
434
+ "--force-rebuild",
435
+ action="store_true",
436
+ help="Force rebuild even if docs haven't changed"
437
+ )
438
+
439
+ # Cross-reference command
440
+ cross_ref_parser = memory_subparsers.add_parser(
441
+ MemoryCommands.CROSS_REF.value,
442
+ help="Find cross-references and common patterns across agent memories"
443
+ )
444
+ cross_ref_parser.add_argument(
445
+ "--query",
446
+ type=str,
447
+ help="Optional search query to filter cross-references"
448
+ )
449
+
450
+ # Route command
451
+ route_parser = memory_subparsers.add_parser(
452
+ MemoryCommands.ROUTE.value,
453
+ help="Test memory command routing logic"
454
+ )
455
+ route_parser.add_argument(
456
+ "--content",
457
+ type=str,
458
+ required=True,
459
+ help="Content to analyze for routing"
460
+ )
461
+
462
+ # Show command
463
+ show_parser = memory_subparsers.add_parser(
464
+ MemoryCommands.SHOW.value,
465
+ help="Show agent memories in user-friendly format with cross-references"
466
+ )
467
+ show_parser.add_argument(
468
+ "agent_id",
469
+ nargs="?",
470
+ help="Agent ID to show memory for (show all if not specified)"
471
+ )
472
+ show_parser.add_argument(
473
+ "--format",
474
+ choices=["summary", "detailed", "full"],
475
+ default="summary",
476
+ help="Display format: summary (default), detailed, or full"
477
+ )
478
+ show_parser.add_argument(
479
+ "--raw",
480
+ action="store_true",
481
+ help="Output raw memory content in JSON format for programmatic processing"
482
+ )
483
+
396
484
  return parser
397
485
 
398
486
 
claude_mpm/constants.py CHANGED
@@ -58,10 +58,16 @@ class AgentCommands(str, Enum):
58
58
 
59
59
  class MemoryCommands(str, Enum):
60
60
  """Memory subcommand constants."""
61
+ INIT = "init"
61
62
  STATUS = "status"
62
63
  VIEW = "view"
63
64
  ADD = "add"
64
65
  CLEAN = "clean"
66
+ OPTIMIZE = "optimize"
67
+ BUILD = "build"
68
+ CROSS_REF = "cross-ref"
69
+ ROUTE = "route"
70
+ SHOW = "show"
65
71
 
66
72
 
67
73
  class CLIFlags(str, Enum):
@@ -13,10 +13,14 @@ import uuid
13
13
  try:
14
14
  from claude_mpm.services.agent_deployment import AgentDeploymentService
15
15
  from claude_mpm.services.ticket_manager import TicketManager
16
+ from claude_mpm.services.hook_service import HookService
17
+ from claude_mpm.core.config import Config
16
18
  from claude_mpm.core.logger import get_logger, get_project_logger, ProjectLogger
17
19
  except ImportError:
18
20
  from claude_mpm.services.agent_deployment import AgentDeploymentService
19
21
  from claude_mpm.services.ticket_manager import TicketManager
22
+ from claude_mpm.services.hook_service import HookService
23
+ from claude_mpm.core.config import Config
20
24
  from claude_mpm.core.logger import get_logger, get_project_logger, ProjectLogger
21
25
 
22
26
 
@@ -76,6 +80,11 @@ class ClaudeRunner:
76
80
  self.ticket_manager = None
77
81
  self.enable_tickets = False
78
82
 
83
+ # Initialize hook service and register memory hooks
84
+ self.config = Config()
85
+ self.hook_service = HookService(self.config)
86
+ self._register_memory_hooks()
87
+
79
88
  # Load system instructions
80
89
  self.system_instructions = self._load_system_instructions()
81
90
 
@@ -95,7 +104,7 @@ class ClaudeRunner:
95
104
  except Exception as e:
96
105
  self.logger.debug(f"Failed to create session log file: {e}")
97
106
 
98
- # Initialize WebSocket server reference
107
+ # Initialize Socket.IO server reference
99
108
  self.websocket_server = None
100
109
 
101
110
  def setup_agents(self) -> bool:
@@ -152,13 +161,14 @@ class ClaudeRunner:
152
161
 
153
162
  def run_interactive(self, initial_context: Optional[str] = None):
154
163
  """Run Claude in interactive mode."""
155
- # Start WebSocket server if enabled
164
+ # Connect to Socket.IO server if enabled
156
165
  if self.enable_websocket:
157
166
  try:
158
- # Lazy import to avoid circular dependencies
159
- from claude_mpm.services.websocket_server import WebSocketServer
160
- self.websocket_server = WebSocketServer(port=self.websocket_port)
167
+ # Use Socket.IO client proxy to connect to monitoring server
168
+ from claude_mpm.services.socketio_server import SocketIOClientProxy
169
+ self.websocket_server = SocketIOClientProxy(port=self.websocket_port)
161
170
  self.websocket_server.start()
171
+ self.logger.info("Connected to Socket.IO monitoring server")
162
172
 
163
173
  # Generate session ID
164
174
  session_id = str(uuid.uuid4())
@@ -171,7 +181,7 @@ class ClaudeRunner:
171
181
  working_dir=working_dir
172
182
  )
173
183
  except Exception as e:
174
- self.logger.warning(f"Failed to start WebSocket server: {e}")
184
+ self.logger.warning(f"Failed to connect to Socket.IO server: {e}")
175
185
  self.websocket_server = None
176
186
 
177
187
  # Get version
@@ -329,13 +339,14 @@ class ClaudeRunner:
329
339
  """Run Claude with a single prompt and return success status."""
330
340
  start_time = time.time()
331
341
 
332
- # Start WebSocket server if enabled
342
+ # Connect to Socket.IO server if enabled
333
343
  if self.enable_websocket:
334
344
  try:
335
- # Lazy import to avoid circular dependencies
336
- from claude_mpm.services.websocket_server import WebSocketServer
337
- self.websocket_server = WebSocketServer(port=self.websocket_port)
345
+ # Use Socket.IO client proxy to connect to monitoring server
346
+ from claude_mpm.services.socketio_server import SocketIOClientProxy
347
+ self.websocket_server = SocketIOClientProxy(port=self.websocket_port)
338
348
  self.websocket_server.start()
349
+ self.logger.info("Connected to Socket.IO monitoring server")
339
350
 
340
351
  # Generate session ID
341
352
  session_id = str(uuid.uuid4())
@@ -348,7 +359,7 @@ class ClaudeRunner:
348
359
  working_dir=working_dir
349
360
  )
350
361
  except Exception as e:
351
- self.logger.warning(f"Failed to start WebSocket server: {e}")
362
+ self.logger.warning(f"Failed to connect to Socket.IO server: {e}")
352
363
  self.websocket_server = None
353
364
 
354
365
  # Check for /mpm: commands
@@ -739,6 +750,58 @@ class ClaudeRunner:
739
750
  except Exception as e:
740
751
  self.logger.debug(f"Failed to log session event: {e}")
741
752
 
753
+ def _register_memory_hooks(self):
754
+ """Register memory integration hooks with the hook service.
755
+
756
+ WHY: This activates the memory system by registering hooks that automatically
757
+ inject agent memory before delegation and extract learnings after delegation.
758
+ This is the critical connection point between the memory system and the CLI.
759
+
760
+ DESIGN DECISION: We register hooks here instead of in __init__ to ensure
761
+ all services are initialized first. Hooks are only registered if the memory
762
+ system is enabled in configuration.
763
+ """
764
+ try:
765
+ # Only register if memory system is enabled
766
+ if not self.config.get('memory.enabled', True):
767
+ self.logger.debug("Memory system disabled - skipping hook registration")
768
+ return
769
+
770
+ # Import hook classes (lazy import to avoid circular dependencies)
771
+ from claude_mpm.hooks.memory_integration_hook import (
772
+ MemoryPreDelegationHook,
773
+ MemoryPostDelegationHook
774
+ )
775
+
776
+ # Register pre-delegation hook for memory injection
777
+ pre_hook = MemoryPreDelegationHook(self.config)
778
+ success = self.hook_service.register_hook(pre_hook)
779
+ if success:
780
+ self.logger.info(f"✅ Registered memory pre-delegation hook (priority: {pre_hook.priority})")
781
+ else:
782
+ self.logger.warning("❌ Failed to register memory pre-delegation hook")
783
+
784
+ # Register post-delegation hook if auto-learning is enabled
785
+ if self.config.get('memory.auto_learning', True): # Default to True now
786
+ post_hook = MemoryPostDelegationHook(self.config)
787
+ success = self.hook_service.register_hook(post_hook)
788
+ if success:
789
+ self.logger.info(f"✅ Registered memory post-delegation hook (priority: {post_hook.priority})")
790
+ else:
791
+ self.logger.warning("❌ Failed to register memory post-delegation hook")
792
+ else:
793
+ self.logger.info("ℹ️ Auto-learning disabled - skipping post-delegation hook")
794
+
795
+ # Log summary of registered hooks
796
+ hooks = self.hook_service.list_hooks()
797
+ pre_count = len(hooks.get('pre_delegation', []))
798
+ post_count = len(hooks.get('post_delegation', []))
799
+ self.logger.info(f"📋 Hook Service initialized: {pre_count} pre-delegation, {post_count} post-delegation hooks")
800
+
801
+ except Exception as e:
802
+ self.logger.error(f"❌ Failed to register memory hooks: {e}")
803
+ # Don't fail the entire initialization - memory system is optional
804
+
742
805
  def _launch_subprocess_interactive(self, cmd: list, env: dict):
743
806
  """Launch Claude as a subprocess with PTY for interactive mode."""
744
807
  import pty
claude_mpm/core/config.py CHANGED
@@ -231,7 +231,7 @@ class Config:
231
231
  # Agent Memory System configuration
232
232
  "memory": {
233
233
  "enabled": True, # Master switch for memory system
234
- "auto_learning": False, # Automatic learning extraction
234
+ "auto_learning": True, # Automatic learning extraction (changed default to True)
235
235
  "limits": {
236
236
  "default_size_kb": 8, # Default file size limit
237
237
  "max_sections": 10, # Maximum sections per file
@@ -117,6 +117,52 @@ class SessionManager:
117
117
  if expired:
118
118
  self._save_sessions()
119
119
 
120
+ def get_recent_sessions(self, limit: int = 10, context: Optional[str] = None) -> list:
121
+ """Get recent sessions sorted by last used time.
122
+
123
+ Args:
124
+ limit: Maximum number of sessions to return
125
+ context: Filter by context (optional)
126
+
127
+ Returns:
128
+ List of session data dictionaries sorted by last_used descending
129
+ """
130
+ sessions = list(self.active_sessions.values())
131
+
132
+ # Filter by context if specified
133
+ if context:
134
+ sessions = [s for s in sessions if s.get("context") == context]
135
+
136
+ # Sort by last_used descending (most recent first)
137
+ sessions.sort(key=lambda s: datetime.fromisoformat(s["last_used"]), reverse=True)
138
+
139
+ return sessions[:limit]
140
+
141
+ def get_session_by_id(self, session_id: str) -> Optional[Dict[str, Any]]:
142
+ """Get session data by ID.
143
+
144
+ Args:
145
+ session_id: Session ID to look up
146
+
147
+ Returns:
148
+ Session data dictionary or None if not found
149
+ """
150
+ return self.active_sessions.get(session_id)
151
+
152
+ def get_last_interactive_session(self) -> Optional[str]:
153
+ """Get the most recently used interactive session ID.
154
+
155
+ WHY: For --resume without arguments, we want to resume the last
156
+ interactive session (context="default" for regular Claude runs).
157
+
158
+ Returns:
159
+ Session ID of most recent interactive session, or None if none found
160
+ """
161
+ recent_sessions = self.get_recent_sessions(limit=1, context="default")
162
+ if recent_sessions:
163
+ return recent_sessions[0]["id"]
164
+ return None
165
+
120
166
  def _save_sessions(self):
121
167
  """Save sessions to disk."""
122
168
  session_file = self.session_dir / "active_sessions.json"