consolle 0.3.9 → 0.4.1

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.
data/lib/consolle/cli.rb CHANGED
@@ -8,6 +8,8 @@ require 'timeout'
8
8
  require 'securerandom'
9
9
  require 'date'
10
10
  require_relative 'constants'
11
+ require_relative 'session_registry'
12
+ require_relative 'history'
11
13
  require_relative 'adapters/rails_console'
12
14
 
13
15
  module Consolle
@@ -88,7 +90,10 @@ module Consolle
88
90
  shell.say ' cone status # Show Rails console status'
89
91
  shell.say ' cone exec CODE # Execute Ruby code in Rails console'
90
92
  shell.say ' cone rails SUBCOMMAND # Rails convenience commands'
91
- shell.say ' cone ls # List active Rails console sessions'
93
+ shell.say ' cone ls # List active sessions (use -a for all)'
94
+ shell.say ' cone history # Show command history'
95
+ shell.say ' cone rm SESSION # Remove session and its history'
96
+ shell.say ' cone prune # Remove all stopped sessions'
92
97
  shell.say ' cone stop_all # Stop all Rails console sessions'
93
98
  shell.say ' cone rule FILE # Write cone command guide to FILE'
94
99
  shell.say ' cone version # Show version'
@@ -236,18 +241,25 @@ module Consolle
236
241
 
237
242
  begin
238
243
  adapter.start
239
- puts '✓ Rails console started successfully'
244
+
245
+ # Register session in registry
246
+ session = session_registry.create_session(
247
+ target: options[:target],
248
+ socket_path: adapter.socket_path,
249
+ pid: adapter.process_pid,
250
+ rails_env: current_rails_env,
251
+ mode: options[:mode] || 'pty'
252
+ )
253
+
254
+ puts '✓ Rails console started'
255
+ puts " Session ID: #{session['id']} (#{session['short_id']})"
256
+ puts " Target: #{session['target']}"
257
+ puts " Environment: #{current_rails_env}"
240
258
  puts " PID: #{adapter.process_pid}"
241
259
  puts " Socket: #{adapter.socket_path}"
242
260
 
243
- # Save session info
244
- save_session_info(adapter)
245
-
246
- # Log session start
247
- log_session_event(adapter.process_pid, 'session_start', {
248
- rails_env: current_rails_env,
249
- socket_path: adapter.socket_path
250
- })
261
+ # Also save to legacy sessions.json for backward compatibility
262
+ save_session_info(adapter, session['id'])
251
263
  rescue StandardError => e
252
264
  puts "✗ Failed to start Rails console: #{e.message}"
253
265
  exit 1
@@ -259,9 +271,11 @@ module Consolle
259
271
  ensure_rails_project!
260
272
  validate_session_name!(options[:target])
261
273
 
274
+ # Try to find session in registry first
275
+ session = session_registry.find_running_session(target: options[:target])
262
276
  session_info = load_session_info
263
277
 
264
- if session_info.nil?
278
+ if session_info.nil? && session.nil?
265
279
  puts 'No active Rails console session found'
266
280
  return
267
281
  end
@@ -278,85 +292,141 @@ module Consolle
278
292
  if process_running
279
293
  rails_env = server_status['rails_env'] || 'unknown'
280
294
  console_pid = server_status['pid'] || 'unknown'
295
+ uptime = session_info&.dig(:started_at) ? format_uptime(Time.now - Time.at(session_info[:started_at])) : 'unknown'
296
+ command_count = session ? session['command_count'] : 0
281
297
 
282
298
  puts '✓ Rails console is running'
283
- puts " PID: #{console_pid}"
299
+ if session
300
+ puts " Session ID: #{session['id']} (#{session['short_id']})"
301
+ end
302
+ puts " Target: #{options[:target]}"
284
303
  puts " Environment: #{rails_env}"
285
- puts " Session: #{session_info[:socket_path]}"
286
- puts ' Ready for input: Yes'
304
+ puts " PID: #{console_pid}"
305
+ puts " Uptime: #{uptime}"
306
+ puts " Commands: #{command_count}"
307
+ puts " Socket: #{session_info&.dig(:socket_path) || session&.dig('socket_path')}"
287
308
  else
288
309
  puts '✗ Rails console is not running'
310
+ # Mark session as stopped in registry
311
+ session_registry.stop_session(target: options[:target], reason: 'process_died') if session
289
312
  clear_session_info
290
313
  end
291
314
  end
292
315
 
293
- desc 'ls', 'List active Rails console sessions'
316
+ desc 'ls', 'List Rails console sessions'
294
317
  long_desc <<-LONGDESC
295
- Lists all active Rails console sessions in the current project.
318
+ Lists Rails console sessions in the current project.
319
+
320
+ By default, shows only active (running) sessions.
321
+ Use -a/--all to include stopped sessions.
296
322
 
297
323
  Shows information about each session including:
298
- - Session name (target)
299
- - Process ID (PID)
324
+ - Session ID (short)
325
+ - Target name
300
326
  - Rails environment
301
327
  - Status (running/stopped)
328
+ - Uptime or stop time
329
+ - Command count
302
330
 
303
331
  Example output:
304
- Active sessions:
305
- - cone (default) [PID: 12345, ENV: development, STATUS: running]
306
- - api [PID: 12346, ENV: production, STATUS: running]
307
- - worker [PID: 12347, ENV: development, STATUS: stopped]
332
+ ID TARGET ENV STATUS UPTIME COMMANDS
333
+ a1b2 cone development running 2h 15m 42
334
+ e5f6 api production running 1h 30m 15
308
335
  LONGDESC
336
+ method_option :all, type: :boolean, aliases: '-a', desc: 'Include stopped sessions'
309
337
  def ls
310
338
  ensure_rails_project!
311
339
 
312
- sessions = load_sessions
340
+ include_stopped = options[:all]
341
+ sessions = session_registry.list_sessions(include_stopped: include_stopped)
313
342
 
314
- if sessions.empty? || sessions.size == 1 && sessions.key?('_schema')
315
- puts 'No active sessions'
343
+ # Also check legacy sessions.json for backward compatibility
344
+ legacy_sessions = load_sessions
345
+ legacy_sessions.each do |name, info|
346
+ next if name == '_schema'
347
+ next unless info['process_pid'] && process_alive?(info['process_pid'])
348
+
349
+ # Check if already in registry
350
+ existing = sessions.find { |s| s['target'] == name && s['status'] == 'running' }
351
+ next if existing
352
+
353
+ # Add legacy session (will be migrated on next start)
354
+ sessions << {
355
+ 'short_id' => '----',
356
+ 'target' => name,
357
+ 'rails_env' => 'development',
358
+ 'status' => 'running',
359
+ 'pid' => info['process_pid'],
360
+ 'created_at' => info['started_at'] ? Time.at(info['started_at']).iso8601 : Time.now.iso8601,
361
+ 'command_count' => 0,
362
+ '_legacy' => true
363
+ }
364
+ end
365
+
366
+ if sessions.empty?
367
+ if include_stopped
368
+ puts 'No sessions found'
369
+ else
370
+ puts 'No active sessions'
371
+ puts "Use 'cone ls -a' to see stopped sessions"
372
+ end
316
373
  return
317
374
  end
318
375
 
319
- active_sessions = []
320
- stale_sessions = []
376
+ # Verify running sessions are actually running
377
+ sessions.each do |session|
378
+ next unless session['status'] == 'running'
379
+ next if session['_legacy']
380
+
381
+ unless session['pid'] && process_alive?(session['pid'])
382
+ session_registry.stop_session(session_id: session['id'], reason: 'process_died')
383
+ session['status'] = 'stopped'
384
+ end
385
+ end
321
386
 
322
- sessions.each do |name, info|
323
- next if name == '_schema' # Skip schema field
387
+ # Re-filter if needed
388
+ sessions = sessions.select { |s| s['status'] == 'running' } unless include_stopped
324
389
 
325
- # Check if process is alive
326
- if info['process_pid'] && process_alive?(info['process_pid'])
327
- # Try to get server status
328
- adapter = create_rails_adapter(current_rails_env, name)
329
- server_status = begin
330
- adapter.get_status
331
- rescue StandardError
332
- nil
333
- end
390
+ if sessions.empty?
391
+ puts 'No active sessions'
392
+ puts "Use 'cone ls -a' to see stopped sessions"
393
+ return
394
+ end
334
395
 
335
- if server_status && server_status['success'] && server_status['running']
336
- rails_env = server_status['rails_env'] || 'development'
337
- console_pid = server_status['pid'] || info['process_pid']
338
- active_sessions << "#{name} (#{rails_env}) - PID: #{console_pid}"
339
- else
340
- stale_sessions << name
341
- end
396
+ # Display header
397
+ if include_stopped
398
+ puts 'ALL SESSIONS:'
399
+ else
400
+ puts 'ACTIVE SESSIONS:'
401
+ end
402
+ puts
403
+ puts format(' %-8s %-12s %-12s %-9s %-10s %s', 'ID', 'TARGET', 'ENV', 'STATUS', 'UPTIME', 'COMMANDS')
404
+
405
+ sessions.each do |session|
406
+ short_id = session['short_id'] || session['id']&.[](0, 4) || '----'
407
+ target = session['target'] || 'unknown'
408
+ env = session['rails_env'] || 'dev'
409
+ status = session['status'] || 'unknown'
410
+ commands = session['command_count'] || 0
411
+
412
+ if session['status'] == 'running'
413
+ started = session['started_at'] || session['created_at']
414
+ uptime = started ? format_uptime(Time.now - Time.parse(started)) : '---'
342
415
  else
343
- stale_sessions << name
416
+ stopped = session['stopped_at']
417
+ uptime = stopped ? format_time_ago(Time.now - Time.parse(stopped)) : '---'
344
418
  end
345
- end
346
419
 
347
- # Clean up stale sessions
348
- if stale_sessions.any?
349
- with_sessions_lock do
350
- sessions = load_sessions
351
- stale_sessions.each { |name| sessions.delete(name) }
352
- save_sessions(sessions)
353
- end
420
+ puts format(' %-8s %-12s %-12s %-9s %-10s %d', short_id, target, env, status, uptime, commands)
354
421
  end
355
422
 
356
- if active_sessions.empty?
357
- puts 'No active sessions'
423
+ puts
424
+ if include_stopped
425
+ puts "Use 'cone history --session ID' to view session history"
426
+ puts "Use 'cone rm ID' to remove session and history"
358
427
  else
359
- active_sessions.each { |session| puts session }
428
+ puts 'Usage: cone exec -t TARGET CODE'
429
+ puts ' cone exec --session ID CODE'
360
430
  end
361
431
  end
362
432
 
@@ -373,18 +443,15 @@ module Consolle
373
443
  if adapter.stop
374
444
  puts '✓ Rails console stopped'
375
445
 
376
- # Log session stop
377
- session_info = load_session_info
378
- if session_info && session_info[:process_pid]
379
- log_session_event(session_info[:process_pid], 'session_stop', {
380
- reason: 'user_requested'
381
- })
382
- end
446
+ # Mark session as stopped in registry (preserves history)
447
+ session_registry.stop_session(target: options[:target], reason: 'user_requested')
383
448
  else
384
449
  puts '✗ Failed to stop Rails console'
385
450
  end
386
451
  else
387
452
  puts 'Rails console is not running'
453
+ # Mark as stopped anyway in case registry is out of sync
454
+ session_registry.stop_session(target: options[:target], reason: 'not_running')
388
455
  end
389
456
 
390
457
  clear_session_info
@@ -395,27 +462,31 @@ module Consolle
395
462
  def stop_all
396
463
  ensure_rails_project!
397
464
 
398
- sessions = load_sessions
399
- active_sessions = []
465
+ # Get running sessions from registry
466
+ running_sessions = session_registry.list_sessions(include_stopped: false)
400
467
 
401
- # Filter active sessions (excluding schema)
402
- sessions.each do |name, info|
468
+ # Also check legacy sessions
469
+ legacy_sessions = load_sessions
470
+ legacy_sessions.each do |name, info|
403
471
  next if name == '_schema'
472
+ next unless info['process_pid'] && process_alive?(info['process_pid'])
404
473
 
405
- active_sessions << { name: name, info: info } if info['process_pid'] && process_alive?(info['process_pid'])
474
+ existing = running_sessions.find { |s| s['target'] == name }
475
+ next if existing
476
+
477
+ running_sessions << { 'target' => name, 'pid' => info['process_pid'], '_legacy' => true }
406
478
  end
407
479
 
408
- if active_sessions.empty?
480
+ if running_sessions.empty?
409
481
  puts 'No active sessions to stop'
410
482
  return
411
483
  end
412
484
 
413
- puts "Found #{active_sessions.size} active session(s)"
485
+ puts "Found #{running_sessions.size} active session(s)"
414
486
 
415
487
  # Stop each active session
416
- active_sessions.each do |session|
417
- name = session[:name]
418
- info = session[:info]
488
+ running_sessions.each do |session|
489
+ name = session['target']
419
490
 
420
491
  puts "\nStopping session '#{name}'..."
421
492
 
@@ -424,14 +495,10 @@ module Consolle
424
495
  if adapter.stop
425
496
  puts "✓ Session '#{name}' stopped"
426
497
 
427
- # Log session stop
428
- if info['process_pid']
429
- log_session_event(info['process_pid'], 'session_stop', {
430
- reason: 'stop_all_requested'
431
- })
432
- end
498
+ # Mark session as stopped in registry
499
+ session_registry.stop_session(target: name, reason: 'stop_all_requested') unless session['_legacy']
433
500
 
434
- # Clear session info
501
+ # Clear from legacy sessions.json
435
502
  with_sessions_lock do
436
503
  sessions = load_sessions
437
504
  sessions.delete(name)
@@ -652,6 +719,211 @@ module Consolle
652
719
  end
653
720
  end
654
721
 
722
+ desc 'rm SESSION_ID', 'Remove session and its history'
723
+ long_desc <<-LONGDESC
724
+ Removes a stopped session and all its history.
725
+
726
+ The SESSION_ID can be:
727
+ - Full session ID (8 characters, e.g., a1b2c3d4)
728
+ - Short session ID (4 characters, e.g., a1b2)
729
+ - Target name (e.g., cone, api)
730
+
731
+ Running sessions cannot be removed. Stop them first with 'cone stop -t TARGET'.
732
+
733
+ Use -f/--force to skip confirmation prompt.
734
+ Use -f/--force with a running session to stop and remove it.
735
+
736
+ Examples:
737
+ cone rm a1b2 # Remove by short ID
738
+ cone rm a1b2c3d4 # Remove by full ID
739
+ cone rm -f a1b2 # Remove without confirmation
740
+ LONGDESC
741
+ method_option :force, type: :boolean, aliases: '-f', desc: 'Skip confirmation (or force stop running session)'
742
+ def rm(session_id)
743
+ ensure_rails_project!
744
+
745
+ # Try to find session
746
+ session = session_registry.find_session(session_id: session_id) ||
747
+ session_registry.find_session(target: session_id)
748
+
749
+ unless session
750
+ puts "✗ Session not found: #{session_id}"
751
+ puts "Use 'cone ls -a' to see all sessions"
752
+ exit 1
753
+ end
754
+
755
+ # Check if running
756
+ if session['status'] == 'running'
757
+ if options[:force]
758
+ # Force stop first
759
+ puts "Stopping running session '#{session['target']}'..."
760
+ adapter = create_rails_adapter('development', session['target'])
761
+ adapter.stop
762
+ session_registry.stop_session(session_id: session['id'], reason: 'force_remove')
763
+ clear_session_info if options[:target] == session['target']
764
+ else
765
+ puts "✗ Session #{session['short_id']} (#{session['target']}) is still running"
766
+ puts " Use 'cone stop -t #{session['target']}' first, or 'cone rm -f #{session_id}' to force"
767
+ exit 1
768
+ end
769
+ end
770
+
771
+ # Confirm deletion
772
+ unless options[:force]
773
+ command_count = session['command_count'] || 0
774
+ print "Remove session #{session['id']} (#{session['target']}, #{command_count} commands)?\n"
775
+ print 'This will permanently delete all history. [y/N]: '
776
+ response = $stdin.gets&.strip&.downcase
777
+ unless response == 'y' || response == 'yes'
778
+ puts 'Cancelled'
779
+ return
780
+ end
781
+ end
782
+
783
+ # Remove session
784
+ result = session_registry.remove_session(session_id: session['id'])
785
+
786
+ if result && !result.is_a?(Hash)
787
+ puts "✓ Session #{session['id']} removed"
788
+ else
789
+ puts "✗ Failed to remove session"
790
+ exit 1
791
+ end
792
+ end
793
+
794
+ desc 'prune', 'Remove all stopped sessions'
795
+ long_desc <<-LONGDESC
796
+ Removes all stopped sessions and their history.
797
+
798
+ By default, only removes sessions from the current project.
799
+
800
+ Use --yes to skip confirmation prompt.
801
+
802
+ Examples:
803
+ cone prune # Remove stopped sessions (with confirmation)
804
+ cone prune --yes # Remove without confirmation
805
+ LONGDESC
806
+ method_option :yes, type: :boolean, aliases: '-y', desc: 'Skip confirmation'
807
+ def prune
808
+ ensure_rails_project!
809
+
810
+ stopped = session_registry.list_stopped_sessions
811
+
812
+ if stopped.empty?
813
+ puts 'No stopped sessions to remove'
814
+ return
815
+ end
816
+
817
+ # Show what will be removed
818
+ total_commands = stopped.sum { |s| s['command_count'] || 0 }
819
+
820
+ puts "Found #{stopped.size} stopped session(s):"
821
+ stopped.each do |session|
822
+ stopped_at = session['stopped_at'] ? Time.parse(session['stopped_at']).strftime('%Y-%m-%d') : '---'
823
+ commands = session['command_count'] || 0
824
+ puts " #{session['short_id']} #{session['target'].ljust(12)} stopped #{stopped_at} #{commands} commands"
825
+ end
826
+ puts
827
+
828
+ # Confirm
829
+ unless options[:yes]
830
+ print "Remove all stopped sessions and their history? [y/N]: "
831
+ response = $stdin.gets&.strip&.downcase
832
+ unless response == 'y' || response == 'yes'
833
+ puts 'Cancelled'
834
+ return
835
+ end
836
+ end
837
+
838
+ # Remove all stopped sessions
839
+ removed = session_registry.prune_sessions
840
+
841
+ puts "✓ Removed #{removed.size} sessions (#{total_commands} commands)"
842
+ end
843
+
844
+ desc 'history', 'Show command history'
845
+ long_desc <<-LONGDESC
846
+ Shows command history for sessions.
847
+
848
+ By default, shows history from the current active session (target).
849
+
850
+ Options:
851
+ --session ID Show history for specific session (by ID or short ID)
852
+ -t, --target Show history for specific target name
853
+ -n, --limit Limit number of entries shown
854
+ --today Show only today's commands
855
+ --date DATE Show commands from specific date (YYYY-MM-DD)
856
+ --success Show only successful commands
857
+ --failed Show only failed commands
858
+ --grep PATTERN Filter by code or result matching pattern
859
+ --all Include history from stopped sessions with same target
860
+ -v, --verbose Show detailed output
861
+ --json Output as JSON
862
+
863
+ Examples:
864
+ cone history # Current session history
865
+ cone history -t api # History for 'api' target
866
+ cone history --session a1b2 # History for specific session
867
+ cone history -n 10 # Last 10 commands
868
+ cone history --today # Today's commands only
869
+ cone history --failed # Failed commands only
870
+ cone history --grep User # Filter by pattern
871
+ LONGDESC
872
+ method_option :session, type: :string, aliases: '-s', desc: 'Session ID or short ID'
873
+ method_option :limit, type: :numeric, aliases: '-n', desc: 'Limit number of entries'
874
+ method_option :today, type: :boolean, desc: 'Show only today'
875
+ method_option :date, type: :string, desc: 'Show specific date (YYYY-MM-DD)'
876
+ method_option :success, type: :boolean, desc: 'Show only successful commands'
877
+ method_option :failed, type: :boolean, desc: 'Show only failed commands'
878
+ method_option :grep, type: :string, aliases: '-g', desc: 'Filter by pattern'
879
+ method_option :all, type: :boolean, desc: 'Include stopped sessions'
880
+ method_option :json, type: :boolean, desc: 'Output as JSON'
881
+ def history
882
+ ensure_rails_project!
883
+
884
+ history_manager = Consolle::History.new
885
+
886
+ entries = history_manager.query(
887
+ session_id: options[:session],
888
+ target: options[:target],
889
+ limit: options[:limit],
890
+ today: options[:today],
891
+ date: options[:date],
892
+ success_only: options[:success],
893
+ failed_only: options[:failed],
894
+ grep: options[:grep],
895
+ all_sessions: options[:all]
896
+ )
897
+
898
+ if entries.empty?
899
+ puts 'No history found'
900
+ if options[:session] || options[:target]
901
+ puts "Try 'cone history' without filters to see all history"
902
+ else
903
+ puts "Execute some commands first with 'cone exec'"
904
+ end
905
+ return
906
+ end
907
+
908
+ if options[:json]
909
+ puts history_manager.format_json(entries)
910
+ elsif options[:verbose]
911
+ entries.each do |entry|
912
+ puts history_manager.format_entry_verbose(entry)
913
+ puts
914
+ end
915
+ else
916
+ entries.each do |entry|
917
+ puts history_manager.format_entry(entry)
918
+ puts
919
+ end
920
+ end
921
+
922
+ unless options[:json]
923
+ puts "Showing #{entries.size} entries"
924
+ end
925
+ end
926
+
655
927
  private
656
928
 
657
929
  def current_rails_env
@@ -788,7 +1060,7 @@ module Consolle
788
1060
  )
789
1061
  end
790
1062
 
791
- def save_session_info(adapter)
1063
+ def save_session_info(adapter, session_id = nil)
792
1064
  target = options[:target]
793
1065
 
794
1066
  with_sessions_lock do
@@ -800,7 +1072,8 @@ module Consolle
800
1072
  'pid_path' => project_pid_path(target),
801
1073
  'log_path' => project_log_path(target),
802
1074
  'started_at' => Time.now.to_f,
803
- 'rails_root' => Dir.pwd
1075
+ 'rails_root' => Dir.pwd,
1076
+ 'session_id' => session_id
804
1077
  }
805
1078
 
806
1079
  save_sessions(sessions)
@@ -821,7 +1094,8 @@ module Consolle
821
1094
  socket_path: session['socket_path'],
822
1095
  process_pid: session['process_pid'],
823
1096
  started_at: session['started_at'],
824
- rails_root: session['rails_root']
1097
+ rails_root: session['rails_root'],
1098
+ session_id: session['session_id']
825
1099
  }
826
1100
  end
827
1101
 
@@ -836,47 +1110,44 @@ module Consolle
836
1110
  end
837
1111
 
838
1112
  def log_session_activity(process_pid, code, result)
839
- # Create log filename based on date and PID
840
- log_file = File.join(project_session_dir, "session_#{Date.today.strftime('%Y%m%d')}_pid#{process_pid}.log")
841
-
842
- # Create log entry
843
- log_entry = {
844
- timestamp: Time.now.iso8601,
845
- request_id: result['request_id'],
846
- code: code,
847
- success: result['success'],
848
- result: result['result'],
849
- error: result['error'],
850
- message: result['message'],
851
- execution_time: result['execution_time']
852
- }
1113
+ # Try to use new History class if session_id is available
1114
+ session_info = load_session_info
1115
+ if session_info&.dig(:session_id)
1116
+ history_manager = Consolle::History.new
1117
+ history_manager.log_command(
1118
+ session_id: session_info[:session_id],
1119
+ target: options[:target],
1120
+ code: code,
1121
+ result: result
1122
+ )
1123
+ else
1124
+ # Fallback to legacy logging
1125
+ log_file = File.join(project_session_dir, "session_#{Date.today.strftime('%Y%m%d')}_pid#{process_pid}.log")
1126
+
1127
+ log_entry = {
1128
+ timestamp: Time.now.iso8601,
1129
+ target: options[:target],
1130
+ request_id: result['request_id'],
1131
+ code: code,
1132
+ success: result['success'],
1133
+ result: result['result'],
1134
+ error: result['error'],
1135
+ message: result['message'],
1136
+ execution_time: result['execution_time']
1137
+ }
853
1138
 
854
- # Append to log file
855
- File.open(log_file, 'a') do |f|
856
- f.puts JSON.generate(log_entry)
1139
+ File.open(log_file, 'a') do |f|
1140
+ f.puts JSON.generate(log_entry)
1141
+ end
857
1142
  end
858
1143
  rescue StandardError => e
859
1144
  # Log errors should not crash the command
860
1145
  puts "Warning: Failed to log session activity: #{e.message}" if options[:verbose]
861
1146
  end
862
1147
 
863
- def log_session_event(process_pid, event_type, details = {})
864
- # Create log filename based on date and PID
865
- log_file = File.join(project_session_dir, "session_#{Date.today.strftime('%Y%m%d')}_pid#{process_pid}.log")
866
-
867
- # Create log entry
868
- log_entry = {
869
- timestamp: Time.now.iso8601,
870
- event: event_type
871
- }.merge(details)
872
-
873
- # Append to log file
874
- File.open(log_file, 'a') do |f|
875
- f.puts JSON.generate(log_entry)
876
- end
877
- rescue StandardError => e
878
- # Log errors should not crash the command
879
- puts "Warning: Failed to log session event: #{e.message}" if options[:verbose]
1148
+ def log_session_event(_process_pid, _event_type, _details = {})
1149
+ # Legacy method kept for backward compatibility with tests
1150
+ # Session events are now tracked in registry metadata
880
1151
  end
881
1152
 
882
1153
  def load_sessions
@@ -957,6 +1228,41 @@ module Consolle
957
1228
  end
958
1229
  end
959
1230
 
1231
+ def session_registry
1232
+ @session_registry ||= Consolle::SessionRegistry.new
1233
+ end
1234
+
1235
+ def format_uptime(seconds)
1236
+ seconds = seconds.to_i
1237
+ if seconds < 60
1238
+ "#{seconds}s"
1239
+ elsif seconds < 3600
1240
+ "#{seconds / 60}m #{seconds % 60}s"
1241
+ elsif seconds < 86400
1242
+ hours = seconds / 3600
1243
+ mins = (seconds % 3600) / 60
1244
+ "#{hours}h #{mins}m"
1245
+ else
1246
+ days = seconds / 86400
1247
+ hours = (seconds % 86400) / 3600
1248
+ "#{days}d #{hours}h"
1249
+ end
1250
+ end
1251
+
1252
+ def format_time_ago(seconds)
1253
+ seconds = seconds.to_i
1254
+ if seconds < 60
1255
+ 'just now'
1256
+ elsif seconds < 3600
1257
+ "#{seconds / 60}m ago"
1258
+ elsif seconds < 86400
1259
+ "#{seconds / 3600}h ago"
1260
+ else
1261
+ days = seconds / 86400
1262
+ "#{days}d ago"
1263
+ end
1264
+ end
1265
+
960
1266
  def process_alive?(pid)
961
1267
  return false unless pid
962
1268