fantasy-cli 1.2.14 → 1.3.0
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.
- checksums.yaml +4 -4
- data/README.md +8 -1
- data/lib/gsd/agents/communication.rb +260 -0
- data/lib/gsd/agents/swarm.rb +341 -0
- data/lib/gsd/lsp/client.rb +394 -0
- data/lib/gsd/lsp/completion.rb +266 -0
- data/lib/gsd/lsp/diagnostics.rb +259 -0
- data/lib/gsd/lsp/hover.rb +244 -0
- data/lib/gsd/lsp/protocol.rb +434 -0
- data/lib/gsd/lsp/server_manager.rb +290 -0
- data/lib/gsd/lsp/symbols.rb +368 -0
- data/lib/gsd/plugins/api.rb +340 -0
- data/lib/gsd/plugins/hooks.rb +117 -95
- data/lib/gsd/plugins/hot_reload.rb +293 -0
- data/lib/gsd/plugins/registry.rb +273 -0
- data/lib/gsd/tui/agent_panel.rb +182 -0
- data/lib/gsd/tui/animations.rb +320 -0
- data/lib/gsd/tui/app.rb +442 -2
- data/lib/gsd/tui/colors.rb +15 -0
- data/lib/gsd/tui/effects.rb +263 -0
- data/lib/gsd/tui/header.rb +13 -5
- data/lib/gsd/tui/input_box.rb +10 -7
- data/lib/gsd/tui/mouse.rb +388 -0
- data/lib/gsd/tui/persistence.rb +192 -0
- data/lib/gsd/tui/session.rb +273 -0
- data/lib/gsd/tui/status_bar.rb +63 -15
- data/lib/gsd/tui/tab.rb +112 -0
- data/lib/gsd/tui/tab_manager.rb +191 -0
- data/lib/gsd/tui/transitions.rb +262 -0
- data/lib/gsd/version.rb +1 -1
- metadata +22 -1
data/lib/gsd/tui/app.rb
CHANGED
|
@@ -8,12 +8,33 @@ require 'gsd/tui/status_bar'
|
|
|
8
8
|
require 'gsd/tui/auto_complete'
|
|
9
9
|
require 'gsd/tui/command_palette'
|
|
10
10
|
require 'gsd/tui/spinner'
|
|
11
|
+
require 'gsd/tui/agent_panel'
|
|
12
|
+
require 'gsd/tui/session'
|
|
13
|
+
require 'gsd/tui/tab_manager'
|
|
14
|
+
require 'gsd/tui/animations'
|
|
15
|
+
require 'gsd/tui/transitions'
|
|
16
|
+
require 'gsd/tui/mouse'
|
|
17
|
+
require 'gsd/tui/effects'
|
|
18
|
+
require 'gsd/agents/swarm'
|
|
19
|
+
require 'gsd/agents/communication'
|
|
11
20
|
require 'gsd/ai/config'
|
|
21
|
+
require 'gsd/plugins/registry'
|
|
22
|
+
require 'gsd/plugins/loader'
|
|
23
|
+
require 'gsd/plugins/hooks'
|
|
24
|
+
require 'gsd/plugins/hot_reload'
|
|
25
|
+
require 'gsd/plugins/api'
|
|
26
|
+
require 'gsd/lsp/protocol'
|
|
27
|
+
require 'gsd/lsp/client'
|
|
28
|
+
require 'gsd/lsp/server_manager'
|
|
29
|
+
require 'gsd/lsp/diagnostics'
|
|
30
|
+
require 'gsd/lsp/completion'
|
|
31
|
+
require 'gsd/lsp/hover'
|
|
32
|
+
require 'gsd/lsp/symbols'
|
|
12
33
|
|
|
13
34
|
module Gsd
|
|
14
35
|
module TUI
|
|
15
36
|
class App
|
|
16
|
-
def initialize(theme: :
|
|
37
|
+
def initialize(theme: :cyber_wave, header_style: :pixel)
|
|
17
38
|
@theme = theme
|
|
18
39
|
@header_style = header_style
|
|
19
40
|
@running = false
|
|
@@ -26,6 +47,44 @@ module Gsd
|
|
|
26
47
|
@command_palette = CommandPalette.new
|
|
27
48
|
@spinner = Spinner.new
|
|
28
49
|
|
|
50
|
+
# Initialize Agent Swarm (v1.3.0)
|
|
51
|
+
@swarm = Gsd::Agents::Swarm.new(max_concurrent: 5, auto_recovery: true)
|
|
52
|
+
@agent_panel = AgentPanel.new(@swarm, width: 60, visible: false)
|
|
53
|
+
@communication = Gsd::Agents::Communication.new(persist_messages: true)
|
|
54
|
+
|
|
55
|
+
# Start swarm orchestration
|
|
56
|
+
@swarm.start
|
|
57
|
+
|
|
58
|
+
# Subscribe to swarm events
|
|
59
|
+
setup_swarm_observers
|
|
60
|
+
|
|
61
|
+
# Initialize Session (v1.3.0)
|
|
62
|
+
@session = Session.new
|
|
63
|
+
@session.start_auto_save
|
|
64
|
+
|
|
65
|
+
# Initialize TabManager (v1.3.0)
|
|
66
|
+
@tab_manager = TabManager.new(max_tabs: 10)
|
|
67
|
+
|
|
68
|
+
# Initialize Advanced UI components (v1.3.0)
|
|
69
|
+
@animation_manager = AnimationManager.new
|
|
70
|
+
@transition_manager = TransitionManager.new
|
|
71
|
+
@mouse_integration = MouseIntegration.new(self)
|
|
72
|
+
@view_transition = ViewTransition.new(self)
|
|
73
|
+
@effects_enabled = true
|
|
74
|
+
@animations_enabled = true
|
|
75
|
+
|
|
76
|
+
# Initialize Plugin System (v1.3.0)
|
|
77
|
+
@plugin_registry = Gsd::Plugins::Registry.instance
|
|
78
|
+
@plugin_loader = Gsd::Plugins::Loader.new(registry: @plugin_registry)
|
|
79
|
+
@plugin_hooks = Gsd::Plugins::Hooks.instance
|
|
80
|
+
@hot_reload = Gsd::Plugins::HotReload.new(registry: @plugin_registry, loader: @plugin_loader)
|
|
81
|
+
setup_plugin_system
|
|
82
|
+
|
|
83
|
+
# Initialize LSP System (v1.3.0)
|
|
84
|
+
@lsp_manager = Gsd::LSP::ServerManager.new
|
|
85
|
+
@lsp_diagnostics = Gsd::LSP::Diagnostics.new
|
|
86
|
+
setup_lsp_system
|
|
87
|
+
|
|
29
88
|
@agents = ['Code', 'Kilo Auto Free', 'Kilo Gateway']
|
|
30
89
|
@selected_agent = 1
|
|
31
90
|
|
|
@@ -37,6 +96,16 @@ module Gsd
|
|
|
37
96
|
|
|
38
97
|
# Initialize status bar with current agent mode
|
|
39
98
|
update_status_mode
|
|
99
|
+
|
|
100
|
+
# Show session info in welcome message
|
|
101
|
+
add_welcome_message
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def add_welcome_message
|
|
105
|
+
@output << { type: :system, text: "Welcome to Fantasy CLI v#{Gsd::VERSION}!" }
|
|
106
|
+
@output << { type: :system, text: "Session: #{@session.name}" }
|
|
107
|
+
@output << { type: :system, text: 'Press Ctrl+P for commands, Ctrl+Q to quit.' }
|
|
108
|
+
@output << { type: :system, text: 'Commands: /save, /restore, /api' }
|
|
40
109
|
end
|
|
41
110
|
|
|
42
111
|
def run
|
|
@@ -72,6 +141,7 @@ module Gsd
|
|
|
72
141
|
|
|
73
142
|
def stop
|
|
74
143
|
@running = false
|
|
144
|
+
@swarm&.stop
|
|
75
145
|
end
|
|
76
146
|
|
|
77
147
|
# Add a message to the output
|
|
@@ -143,6 +213,9 @@ module Gsd
|
|
|
143
213
|
# Header
|
|
144
214
|
lines << @header.render
|
|
145
215
|
|
|
216
|
+
# Tab bar
|
|
217
|
+
lines << @tab_manager.render_tab_bar(width: 80) if @tab_manager.tab_count > 1
|
|
218
|
+
|
|
146
219
|
if @command_palette.visible?
|
|
147
220
|
lines << @command_palette.render(width: 58)
|
|
148
221
|
else
|
|
@@ -212,6 +285,10 @@ module Gsd
|
|
|
212
285
|
char = "\cP"
|
|
213
286
|
when "\x11" # Ctrl+Q (17 = 0x11)
|
|
214
287
|
char = "\cQ"
|
|
288
|
+
when "\x14" # Ctrl+T (20 = 0x14) - New Tab
|
|
289
|
+
char = :new_tab
|
|
290
|
+
when "\x17" # Ctrl+W (23 = 0x17) - Close Tab
|
|
291
|
+
char = :close_tab
|
|
215
292
|
end
|
|
216
293
|
|
|
217
294
|
if @command_palette.visible?
|
|
@@ -236,6 +313,12 @@ module Gsd
|
|
|
236
313
|
when "\cP"
|
|
237
314
|
@command_palette.show
|
|
238
315
|
full_render
|
|
316
|
+
when :new_tab
|
|
317
|
+
handle_new_tab
|
|
318
|
+
full_render
|
|
319
|
+
when :close_tab
|
|
320
|
+
handle_close_tab
|
|
321
|
+
full_render
|
|
239
322
|
when "\t"
|
|
240
323
|
handle_tab
|
|
241
324
|
full_render
|
|
@@ -290,7 +373,7 @@ module Gsd
|
|
|
290
373
|
case action
|
|
291
374
|
when :quit
|
|
292
375
|
stop
|
|
293
|
-
when :theme_fantasy, :theme_kilo, :theme_dark, :theme_light, :theme_nord
|
|
376
|
+
when :theme_fantasy, :theme_kilo, :theme_dark, :theme_light, :theme_nord, :theme_cyber_wave
|
|
294
377
|
theme_name = action.to_s.split('_').last.to_sym
|
|
295
378
|
set_theme(theme_name)
|
|
296
379
|
else
|
|
@@ -338,6 +421,16 @@ module Gsd
|
|
|
338
421
|
case command
|
|
339
422
|
when '/api'
|
|
340
423
|
handle_api_command(args)
|
|
424
|
+
when '/save'
|
|
425
|
+
handle_save_command(args)
|
|
426
|
+
when '/restore'
|
|
427
|
+
handle_restore_command(args)
|
|
428
|
+
when '/tab'
|
|
429
|
+
handle_tab_command(args)
|
|
430
|
+
when '/plugin'
|
|
431
|
+
handle_plugin_command(args)
|
|
432
|
+
when '/lsp'
|
|
433
|
+
handle_lsp_command(args)
|
|
341
434
|
else
|
|
342
435
|
@output << { type: :error, text: "Unknown command: #{command}" }
|
|
343
436
|
end
|
|
@@ -393,6 +486,85 @@ module Gsd
|
|
|
393
486
|
end
|
|
394
487
|
end
|
|
395
488
|
|
|
489
|
+
# Handle /save subcommands
|
|
490
|
+
def handle_save_command(args)
|
|
491
|
+
subcommand = args[0]
|
|
492
|
+
|
|
493
|
+
case subcommand
|
|
494
|
+
when 'checkpoint', nil
|
|
495
|
+
checkpoint_name = args[1] || "checkpoint-#{Time.now.to_i}"
|
|
496
|
+
checkpoint_id = @session.checkpoint(app: self, name: checkpoint_name)
|
|
497
|
+
@output << { type: :system, text: "💾 Checkpoint created: #{checkpoint_name}" }
|
|
498
|
+
@output << { type: :system, text: " ID: #{checkpoint_id}" }
|
|
499
|
+
when 'export'
|
|
500
|
+
format = args[1] || 'markdown'
|
|
501
|
+
content = @session.export(format: format.to_sym)
|
|
502
|
+
if content
|
|
503
|
+
filename = "#{@session.name.gsub(/[^a-zA-Z0-9]/, '_')}.#{format}"
|
|
504
|
+
File.write(filename, content)
|
|
505
|
+
@output << { type: :system, text: "📤 Exported to: #{filename}" }
|
|
506
|
+
else
|
|
507
|
+
@output << { type: :error, text: "Failed to export session" }
|
|
508
|
+
end
|
|
509
|
+
when 'info'
|
|
510
|
+
info = @session.info
|
|
511
|
+
@output << { type: :system, text: "📊 Session Info:" }
|
|
512
|
+
@output << { type: :system, text: " Name: #{info[:name]}" }
|
|
513
|
+
@output << { type: :system, text: " ID: #{info[:id]}" }
|
|
514
|
+
@output << { type: :system, text: " Created: #{info[:created_at]}" }
|
|
515
|
+
@output << { type: :system, text: " Last saved: #{info[:last_saved_at] || 'Never'}" }
|
|
516
|
+
else
|
|
517
|
+
@output << { type: :error, text: "Unknown /save subcommand: #{subcommand}" }
|
|
518
|
+
@output << { type: :system, text: "Usage: /save [checkpoint [name]|export [format]|info]" }
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Handle /restore subcommands
|
|
523
|
+
def handle_restore_command(args)
|
|
524
|
+
subcommand = args[0]
|
|
525
|
+
|
|
526
|
+
case subcommand
|
|
527
|
+
when 'session', nil
|
|
528
|
+
if args[1]
|
|
529
|
+
session = Session.load(session_id: args[1])
|
|
530
|
+
if session
|
|
531
|
+
@session.stop_auto_save
|
|
532
|
+
@session = session
|
|
533
|
+
@session.restore(app: self)
|
|
534
|
+
@session.start_auto_save
|
|
535
|
+
@output << { type: :system, text: "📂 Restored session: #{@session.name}" }
|
|
536
|
+
else
|
|
537
|
+
@output << { type: :error, text: "Session not found: #{args[1]}" }
|
|
538
|
+
end
|
|
539
|
+
else
|
|
540
|
+
# List available sessions
|
|
541
|
+
sessions = Session.list_all
|
|
542
|
+
@output << { type: :system, text: "📂 Available sessions:" }
|
|
543
|
+
sessions.first(5).each do |s|
|
|
544
|
+
@output << { type: :system, text: " #{s[:id]} - #{s[:name]} (#{s[:message_count]} msgs)" }
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
when 'checkpoint'
|
|
548
|
+
if args[1]
|
|
549
|
+
if @session.restore_checkpoint(app: self, checkpoint_id: args[1])
|
|
550
|
+
@output << { type: :system, text: "⏪ Restored checkpoint: #{args[1]}" }
|
|
551
|
+
else
|
|
552
|
+
@output << { type: :error, text: "Checkpoint not found: #{args[1]}" }
|
|
553
|
+
end
|
|
554
|
+
else
|
|
555
|
+
# List checkpoints
|
|
556
|
+
checkpoints = @session.checkpoints
|
|
557
|
+
@output << { type: :system, text: "📋 Checkpoints:" }
|
|
558
|
+
checkpoints.first(5).each do |c|
|
|
559
|
+
@output << { type: :system, text: " #{c[:id]} - #{c[:name]}" }
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
else
|
|
563
|
+
@output << { type: :error, text: "Unknown /restore subcommand: #{subcommand}" }
|
|
564
|
+
@output << { type: :system, text: "Usage: /restore [session [id]|checkpoint [id]]" }
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
|
|
396
568
|
def handle_up_arrow
|
|
397
569
|
return if @history.empty?
|
|
398
570
|
|
|
@@ -443,6 +615,274 @@ module Gsd
|
|
|
443
615
|
'NORMAL'
|
|
444
616
|
end
|
|
445
617
|
end
|
|
618
|
+
|
|
619
|
+
# Tab handling methods
|
|
620
|
+
def handle_new_tab
|
|
621
|
+
@tab_manager.create_tab(title: "Tab #{@tab_manager.tab_count + 1}")
|
|
622
|
+
@output << { type: :system, text: "📂 New tab created" }
|
|
623
|
+
switch_to_tab_state(@tab_manager.current_tab)
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def handle_close_tab
|
|
627
|
+
if @tab_manager.close_current_tab
|
|
628
|
+
@output << { type: :system, text: "📂 Tab closed" }
|
|
629
|
+
switch_to_tab_state(@tab_manager.current_tab)
|
|
630
|
+
else
|
|
631
|
+
@output << { type: :error, text: "Cannot close last tab" }
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def handle_next_tab
|
|
636
|
+
@tab_manager.next_tab
|
|
637
|
+
@output << { type: :system, text: "📂 Switched to tab: #{@tab_manager.current_tab.title}" }
|
|
638
|
+
switch_to_tab_state(@tab_manager.current_tab)
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def handle_tab_command(args)
|
|
642
|
+
subcommand = args[0]
|
|
643
|
+
|
|
644
|
+
case subcommand
|
|
645
|
+
when 'new', nil
|
|
646
|
+
handle_new_tab
|
|
647
|
+
when 'close'
|
|
648
|
+
handle_close_tab
|
|
649
|
+
when 'next'
|
|
650
|
+
handle_next_tab
|
|
651
|
+
when 'list'
|
|
652
|
+
tabs = @tab_manager.list_tabs
|
|
653
|
+
@output << { type: :system, text: "📂 Tabs:" }
|
|
654
|
+
tabs.each do |t|
|
|
655
|
+
prefix = t[:is_active] ? '>' : ' '
|
|
656
|
+
@output << { type: :system, text: " #{prefix} [#{t[:index]}] #{t[:title]} (#{t[:message_count]} msgs)" }
|
|
657
|
+
end
|
|
658
|
+
when 'switch'
|
|
659
|
+
index = args[1]&.to_i
|
|
660
|
+
if index && @tab_manager.switch_to_tab(index)
|
|
661
|
+
@output << { type: :system, text: "📂 Switched to tab: #{@tab_manager.current_tab.title}" }
|
|
662
|
+
switch_to_tab_state(@tab_manager.current_tab)
|
|
663
|
+
else
|
|
664
|
+
@output << { type: :error, text: "Usage: /tab switch <index>" }
|
|
665
|
+
end
|
|
666
|
+
else
|
|
667
|
+
@output << { type: :error, text: "Unknown /tab subcommand: #{subcommand}" }
|
|
668
|
+
@output << { type: :system, text: "Usage: /tab {new|close|next|list|switch <index>}" }
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
def switch_to_tab_state(tab)
|
|
673
|
+
@output = tab.output.dup
|
|
674
|
+
@history = tab.history.dup
|
|
675
|
+
@history_index = tab.history_index
|
|
676
|
+
@input_box.clear
|
|
677
|
+
@input_box.add_char(tab.input_text) unless tab.input_text.empty?
|
|
678
|
+
@selected_agent = tab.selected_agent
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# Setup observers for swarm events
|
|
682
|
+
def setup_swarm_observers
|
|
683
|
+
@swarm.add_observer do |event, *args|
|
|
684
|
+
case event
|
|
685
|
+
when :agent_spawned
|
|
686
|
+
agent_id, type = args
|
|
687
|
+
add_message(:system, "🚀 Agent #{type} spawned (#{agent_id[0..12]})")
|
|
688
|
+
when :agent_terminated
|
|
689
|
+
agent_id = args.first
|
|
690
|
+
add_message(:system, "💀 Agent terminated (#{agent_id[0..12]})")
|
|
691
|
+
when :agent_crashed
|
|
692
|
+
agent_id = args.first
|
|
693
|
+
add_message(:error, "💥 Agent crashed (#{agent_id[0..12]})")
|
|
694
|
+
when :agent_restarted
|
|
695
|
+
agent_id = args.first
|
|
696
|
+
add_message(:system, "🔄 Agent restarted (#{agent_id[0..12]})")
|
|
697
|
+
when :task_completed
|
|
698
|
+
agent_id, task = args
|
|
699
|
+
add_message(:system, "✅ Task completed by #{agent_id[0..12]}")
|
|
700
|
+
when :task_queued
|
|
701
|
+
task = args.first
|
|
702
|
+
add_message(:system, "📋 Task queued: #{task[:type]}")
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
# Plugin System setup
|
|
708
|
+
def setup_plugin_system
|
|
709
|
+
# Load all discovered plugins
|
|
710
|
+
@plugin_loader.load_all
|
|
711
|
+
|
|
712
|
+
# Enable all loaded plugins
|
|
713
|
+
@plugin_registry.all.each do |plugin|
|
|
714
|
+
begin
|
|
715
|
+
plugin.enable!
|
|
716
|
+
@plugin_hooks.trigger("plugin.enabled", plugin.name)
|
|
717
|
+
rescue StandardError => e
|
|
718
|
+
warn "[Plugins] Failed to enable '#{plugin.name}': #{e.message}"
|
|
719
|
+
end
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
# Start hot-reload watcher
|
|
723
|
+
@hot_reload.start
|
|
724
|
+
|
|
725
|
+
# Register hook for TUI messages from plugins
|
|
726
|
+
@plugin_hooks.on('tui.message') do |data|
|
|
727
|
+
if data && data[:text]
|
|
728
|
+
add_message(data[:type] || :system, "[#{data[:plugin]}] #{data[:text]}")
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
@plugin_hooks.trigger('plugins.loaded', @plugin_registry.all.map(&:name))
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
# Handle /plugin subcommands
|
|
736
|
+
def handle_plugin_command(args)
|
|
737
|
+
subcommand = args[0]
|
|
738
|
+
|
|
739
|
+
case subcommand
|
|
740
|
+
when 'list', nil
|
|
741
|
+
plugins = @plugin_registry.all
|
|
742
|
+
@output << { type: :system, text: "📦 Plugins (#{plugins.length}):" }
|
|
743
|
+
plugins.each do |p|
|
|
744
|
+
status = p.enabled ? '✅' : '⭕'
|
|
745
|
+
@output << { type: :system, text: " #{status} #{p.name} v#{p.version}" }
|
|
746
|
+
end
|
|
747
|
+
when 'enable'
|
|
748
|
+
name = args[1]
|
|
749
|
+
if name
|
|
750
|
+
begin
|
|
751
|
+
@plugin_registry.enable(name)
|
|
752
|
+
@output << { type: :system, text: "✅ Plugin '#{name}' enabled" }
|
|
753
|
+
rescue StandardError => e
|
|
754
|
+
@output << { type: :error, text: "❌ #{e.message}" }
|
|
755
|
+
end
|
|
756
|
+
else
|
|
757
|
+
@output << { type: :error, text: "Usage: /plugin enable <name>" }
|
|
758
|
+
end
|
|
759
|
+
when 'disable'
|
|
760
|
+
name = args[1]
|
|
761
|
+
if name
|
|
762
|
+
begin
|
|
763
|
+
@plugin_registry.disable(name)
|
|
764
|
+
@output << { type: :system, text: "⭕ Plugin '#{name}' disabled" }
|
|
765
|
+
rescue StandardError => e
|
|
766
|
+
@output << { type: :error, text: "❌ #{e.message}" }
|
|
767
|
+
end
|
|
768
|
+
else
|
|
769
|
+
@output << { type: :error, text: "Usage: /plugin disable <name>" }
|
|
770
|
+
end
|
|
771
|
+
when 'reload'
|
|
772
|
+
name = args[1]
|
|
773
|
+
if name
|
|
774
|
+
if @hot_reload.reload_plugin(name)
|
|
775
|
+
@output << { type: :system, text: "🔄 Plugin '#{name}' reloaded" }
|
|
776
|
+
else
|
|
777
|
+
@output << { type: :error, text: "❌ Failed to reload '#{name}'" }
|
|
778
|
+
end
|
|
779
|
+
else
|
|
780
|
+
@output << { type: :error, text: "Usage: /plugin reload <name>" }
|
|
781
|
+
end
|
|
782
|
+
when 'info'
|
|
783
|
+
name = args[1]
|
|
784
|
+
if name
|
|
785
|
+
info = @plugin_registry.plugin_info(name)
|
|
786
|
+
if info
|
|
787
|
+
@output << { type: :system, text: "📦 #{info[:name]}:" }
|
|
788
|
+
@output << { type: :system, text: " Version: #{info[:version]}" }
|
|
789
|
+
@output << { type: :system, text: " Author: #{info[:author]}" }
|
|
790
|
+
@output << { type: :system, text: " Status: #{info[:enabled] ? 'enabled' : 'disabled'}" }
|
|
791
|
+
@output << { type: :system, text: " Path: #{info[:path]}" }
|
|
792
|
+
else
|
|
793
|
+
@output << { type: :error, text: "Plugin '#{name}' not found" }
|
|
794
|
+
end
|
|
795
|
+
else
|
|
796
|
+
@output << { type: :error, text: "Usage: /plugin info <name>" }
|
|
797
|
+
end
|
|
798
|
+
when 'status'
|
|
799
|
+
status = @hot_reload.status
|
|
800
|
+
@output << { type: :system, text: "🔌 Plugin System Status:" }
|
|
801
|
+
@output << { type: :system, text: " Watching: #{status[:watching]}" }
|
|
802
|
+
@output << { type: :system, text: " Files watched: #{status[:files_watched]}" }
|
|
803
|
+
@output << { type: :system, text: " Paused: #{status[:paused]}" }
|
|
804
|
+
else
|
|
805
|
+
@output << { type: :error, text: "Unknown /plugin subcommand: #{subcommand}" }
|
|
806
|
+
@output << { type: :system, text: "Usage: /plugin {list|enable|disable|reload|info|status} [args]" }
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
# Setup LSP system and register hooks
|
|
811
|
+
def setup_lsp_system
|
|
812
|
+
# Register hook for LSP diagnostics
|
|
813
|
+
@plugin_hooks.on('lsp.diagnostics') do |uri, diagnostics|
|
|
814
|
+
@lsp_diagnostics.update(uri, diagnostics)
|
|
815
|
+
counts = @lsp_diagnostics.count_by_severity
|
|
816
|
+
if counts[:error] > 0 || counts[:warning] > 0
|
|
817
|
+
@status_bar.update_lsp_status("LSP E:#{counts[:error]} W:#{counts[:warning]}")
|
|
818
|
+
end
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
# Handle /lsp subcommands
|
|
823
|
+
def handle_lsp_command(args)
|
|
824
|
+
subcommand = args[0]
|
|
825
|
+
|
|
826
|
+
case subcommand
|
|
827
|
+
when 'start', nil
|
|
828
|
+
language = args[1]&.to_sym || :ruby
|
|
829
|
+
client = @lsp_manager.client_for_language(language)
|
|
830
|
+
if client
|
|
831
|
+
@output << { type: :system, text: "🔌 LSP server for #{language} started" }
|
|
832
|
+
else
|
|
833
|
+
@output << { type: :error, text: "Failed to start LSP server for #{language}" }
|
|
834
|
+
end
|
|
835
|
+
when 'stop'
|
|
836
|
+
language = args[1]&.to_sym
|
|
837
|
+
if language
|
|
838
|
+
@lsp_manager.stop_language(language)
|
|
839
|
+
@output << { type: :system, text: "🔌 LSP server for #{language} stopped" }
|
|
840
|
+
else
|
|
841
|
+
@lsp_manager.stop_all
|
|
842
|
+
@output << { type: :system, text: "🔌 All LSP servers stopped" }
|
|
843
|
+
end
|
|
844
|
+
when 'status'
|
|
845
|
+
running = @lsp_manager.running
|
|
846
|
+
available = @lsp_manager.available_servers
|
|
847
|
+
@output << { type: :system, text: "🔌 LSP Status:" }
|
|
848
|
+
@output << { type: :system, text: " Running: #{running.empty? ? 'none' : running.join(', ')}" }
|
|
849
|
+
@output << { type: :system, text: " Available: #{available.empty? ? 'none' : available.join(', ')}" }
|
|
850
|
+
when 'info'
|
|
851
|
+
language = args[1]&.to_sym || :ruby
|
|
852
|
+
info = @lsp_manager.server_info(language)
|
|
853
|
+
if info
|
|
854
|
+
@output << { type: :system, text: "📦 #{info[:name]}:" }
|
|
855
|
+
@output << { type: :system, text: " Running: #{info[:running]}" }
|
|
856
|
+
@output << { type: :system, text: " State: #{info[:state]}" }
|
|
857
|
+
@output << { type: :system, text: " Server: #{info[:server_info]&.[]('name') || 'unknown'}" }
|
|
858
|
+
else
|
|
859
|
+
@output << { type: :error, text: "No LSP server for #{language}" }
|
|
860
|
+
end
|
|
861
|
+
when 'diagnostics'
|
|
862
|
+
file_path = args[1] || Dir.pwd
|
|
863
|
+
diagnostics = @lsp_diagnostics.for_file(file_path)
|
|
864
|
+
if diagnostics.empty?
|
|
865
|
+
@output << { type: :system, text: "✅ No diagnostics for #{file_path}" }
|
|
866
|
+
else
|
|
867
|
+
@output << { type: :system, text: "📋 Diagnostics for #{file_path} (#{diagnostics.length}):" }
|
|
868
|
+
diagnostics.first(10).each do |d|
|
|
869
|
+
line = d.range.start.line + 1
|
|
870
|
+
severity = d.severity == 1 ? '🔴' : '🟡'
|
|
871
|
+
@output << { type: :system, text: " #{severity} L#{line}: #{d.message.lines.first.chomp}" }
|
|
872
|
+
end
|
|
873
|
+
end
|
|
874
|
+
when 'list'
|
|
875
|
+
languages = @lsp_manager.configured_languages
|
|
876
|
+
@output << { type: :system, text: "📋 Configured LSP servers:" }
|
|
877
|
+
languages.each do |lang|
|
|
878
|
+
running = @lsp_manager.running?(lang) ? '✅' : '⭕'
|
|
879
|
+
@output << { type: :system, text: " #{running} #{lang}" }
|
|
880
|
+
end
|
|
881
|
+
else
|
|
882
|
+
@output << { type: :error, text: "Unknown /lsp subcommand: #{subcommand}" }
|
|
883
|
+
@output << { type: :system, text: "Usage: /lsp {start|stop|status|info|diagnostics|list} [args]" }
|
|
884
|
+
end
|
|
885
|
+
end
|
|
446
886
|
end
|
|
447
887
|
end
|
|
448
888
|
end
|
data/lib/gsd/tui/colors.rb
CHANGED
|
@@ -95,6 +95,20 @@ module Gsd
|
|
|
95
95
|
error: BRIGHT_RED,
|
|
96
96
|
bg: BG_BLACK,
|
|
97
97
|
bg_accent: BG_BLUE
|
|
98
|
+
},
|
|
99
|
+
cyber_wave: {
|
|
100
|
+
name: 'Cyber Wave',
|
|
101
|
+
accent: BRIGHT_CYAN, # Ciano neon
|
|
102
|
+
accent2: BRIGHT_MAGENTA, # Roxo neon
|
|
103
|
+
accent3: BRIGHT_RED, # Rosa neon (usando RED brilhante)
|
|
104
|
+
text: WHITE,
|
|
105
|
+
dim: BRIGHT_BLACK,
|
|
106
|
+
success: BRIGHT_GREEN,
|
|
107
|
+
warning: BRIGHT_YELLOW,
|
|
108
|
+
error: BRIGHT_RED,
|
|
109
|
+
bg: BG_BLACK,
|
|
110
|
+
bg_accent: BG_CYAN,
|
|
111
|
+
bg_accent2: BG_MAGENTA
|
|
98
112
|
}
|
|
99
113
|
}.freeze
|
|
100
114
|
|
|
@@ -109,3 +123,4 @@ module Gsd
|
|
|
109
123
|
end
|
|
110
124
|
end
|
|
111
125
|
end
|
|
126
|
+
|