fantasy-cli 1.2.11 → 1.2.14
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/lib/gsd/tui/app.rb +72 -22
- data/lib/gsd/tui/auto_complete.rb +42 -7
- data/lib/gsd/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5adcd4b07f5da2256106aa2df3bc5ad67ab64d3b62d69dbfb316edc266bf90be
|
|
4
|
+
data.tar.gz: 2115dd7040fff7fe0fcd20bda97b6e28b7462bc06b3e77bf4e3f738870b5e558
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85abb14e63e9ae8c6aec9ea4e84995c4c9d26f2410f92630fb0335fa5b40d5e325b4530415b2ed3e983d54cf19b0376160eb820efab5c3e3907e367d3a466f33
|
|
7
|
+
data.tar.gz: 78e547cdc205b0638de16917d78fd6499060a73095ac4d3a35bb12cce1ec049c75e279da8f8d052b59961e75bb7dd232c934679bed0d401847edb74a05642b81
|
data/lib/gsd/tui/app.rb
CHANGED
|
@@ -8,6 +8,7 @@ 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/ai/config'
|
|
11
12
|
|
|
12
13
|
module Gsd
|
|
13
14
|
module TUI
|
|
@@ -54,7 +55,7 @@ module Gsd
|
|
|
54
55
|
end
|
|
55
56
|
print Colors::HOME
|
|
56
57
|
|
|
57
|
-
@output << { type: :system, text:
|
|
58
|
+
@output << { type: :system, text: "Welcome to Fantasy CLI v#{Gsd::VERSION}!" }
|
|
58
59
|
@output << { type: :system, text: 'Press Ctrl+P for commands, Ctrl+Q to quit.' }
|
|
59
60
|
|
|
60
61
|
full_render
|
|
@@ -171,9 +172,6 @@ module Gsd
|
|
|
171
172
|
|
|
172
173
|
# Input box
|
|
173
174
|
lines << @input_box.render
|
|
174
|
-
|
|
175
|
-
# Agent selector
|
|
176
|
-
lines << render_agent_selector
|
|
177
175
|
end
|
|
178
176
|
|
|
179
177
|
# Status bar
|
|
@@ -310,6 +308,12 @@ module Gsd
|
|
|
310
308
|
text = @input_box.submit
|
|
311
309
|
return if text.strip.empty?
|
|
312
310
|
|
|
311
|
+
# Check for slash commands
|
|
312
|
+
if text.start_with?('/')
|
|
313
|
+
handle_slash_command(text)
|
|
314
|
+
return
|
|
315
|
+
end
|
|
316
|
+
|
|
313
317
|
@history << text
|
|
314
318
|
@history_index = @history.length
|
|
315
319
|
@output << { type: :user, text: text }
|
|
@@ -325,6 +329,70 @@ module Gsd
|
|
|
325
329
|
@spinner.stop
|
|
326
330
|
end
|
|
327
331
|
|
|
332
|
+
# Handle slash commands like /api
|
|
333
|
+
def handle_slash_command(text)
|
|
334
|
+
parts = text.split
|
|
335
|
+
command = parts[0]
|
|
336
|
+
args = parts[1..]
|
|
337
|
+
|
|
338
|
+
case command
|
|
339
|
+
when '/api'
|
|
340
|
+
handle_api_command(args)
|
|
341
|
+
else
|
|
342
|
+
@output << { type: :error, text: "Unknown command: #{command}" }
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Handle /api subcommands
|
|
347
|
+
def handle_api_command(args)
|
|
348
|
+
config_cli = Gsd::AI::ConfigCLI.new(cwd: Dir.pwd)
|
|
349
|
+
subcommand = args[0]
|
|
350
|
+
|
|
351
|
+
case subcommand
|
|
352
|
+
when 'set'
|
|
353
|
+
provider = args[1]
|
|
354
|
+
key = args[2]
|
|
355
|
+
if provider && key
|
|
356
|
+
config_cli.set_key(provider, key)
|
|
357
|
+
@output << { type: :system, text: "✅ API key for #{provider} configured" }
|
|
358
|
+
else
|
|
359
|
+
@output << { type: :error, text: "Usage: /api set <provider> <key>" }
|
|
360
|
+
end
|
|
361
|
+
when 'get'
|
|
362
|
+
provider = args[1]
|
|
363
|
+
if provider
|
|
364
|
+
key = config_cli.get_key(provider)
|
|
365
|
+
if key
|
|
366
|
+
masked = "#{key[0..10]}..."
|
|
367
|
+
@output << { type: :system, text: "#{provider}: #{masked}" }
|
|
368
|
+
else
|
|
369
|
+
@output << { type: :error, text: "❌ API key for #{provider} not configured" }
|
|
370
|
+
end
|
|
371
|
+
else
|
|
372
|
+
@output << { type: :error, text: "Usage: /api get <provider>" }
|
|
373
|
+
end
|
|
374
|
+
when 'list', nil
|
|
375
|
+
@output << { type: :system, text: "🔑 API Keys Status:" }
|
|
376
|
+
providers = %w[anthropic openai openrouter ollama lmstudio groq gemini]
|
|
377
|
+
providers.each do |provider|
|
|
378
|
+
key = config_cli.get_key(provider)
|
|
379
|
+
status = key ? '✅' : '❌'
|
|
380
|
+
@output << { type: :system, text: " #{status} #{provider}" }
|
|
381
|
+
end
|
|
382
|
+
when 'remove', 'rm'
|
|
383
|
+
provider = args[1]
|
|
384
|
+
if provider
|
|
385
|
+
config_cli.remove_key(provider)
|
|
386
|
+
@output << { type: :system, text: "✅ API key for #{provider} removed" }
|
|
387
|
+
else
|
|
388
|
+
@output << { type: :error, text: "Usage: /api remove <provider>" }
|
|
389
|
+
end
|
|
390
|
+
else
|
|
391
|
+
@output << { type: :error, text: "Unknown /api subcommand: #{subcommand}" }
|
|
392
|
+
@output << { type: :system, text: "Usage: /api {set|get|list|remove} [args]" }
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
328
396
|
def handle_up_arrow
|
|
329
397
|
return if @history.empty?
|
|
330
398
|
|
|
@@ -375,24 +443,6 @@ module Gsd
|
|
|
375
443
|
'NORMAL'
|
|
376
444
|
end
|
|
377
445
|
end
|
|
378
|
-
|
|
379
|
-
def render_agent_selector
|
|
380
|
-
t = Colors.theme
|
|
381
|
-
accent = t[:accent]
|
|
382
|
-
white = t[:text]
|
|
383
|
-
dim = t[:dim]
|
|
384
|
-
reset = Colors::RESET
|
|
385
|
-
|
|
386
|
-
parts = @agents.each_with_index.map do |agent, idx|
|
|
387
|
-
if idx == @selected_agent
|
|
388
|
-
"#{accent}[#{reset}#{white}#{agent}#{reset}#{accent}]#{reset}"
|
|
389
|
-
else
|
|
390
|
-
"#{dim}[#{agent}]#{reset}"
|
|
391
|
-
end
|
|
392
|
-
end
|
|
393
|
-
|
|
394
|
-
" #{parts.join(' ')}"
|
|
395
|
-
end
|
|
396
446
|
end
|
|
397
447
|
end
|
|
398
448
|
end
|
|
@@ -14,6 +14,18 @@ module Gsd
|
|
|
14
14
|
get-phase analyze
|
|
15
15
|
].freeze
|
|
16
16
|
|
|
17
|
+
SLASH_COMMANDS = %w[
|
|
18
|
+
/api
|
|
19
|
+
/api set
|
|
20
|
+
/api get
|
|
21
|
+
/api list
|
|
22
|
+
/api remove
|
|
23
|
+
/help
|
|
24
|
+
/quit
|
|
25
|
+
/q
|
|
26
|
+
/clear
|
|
27
|
+
].freeze
|
|
28
|
+
|
|
17
29
|
def initialize
|
|
18
30
|
@suggestions = []
|
|
19
31
|
@selected_index = 0
|
|
@@ -22,7 +34,12 @@ module Gsd
|
|
|
22
34
|
def update(text)
|
|
23
35
|
return [] if text.strip.empty?
|
|
24
36
|
|
|
25
|
-
|
|
37
|
+
# Detectar comandos slash
|
|
38
|
+
if text.start_with?('/')
|
|
39
|
+
@suggestions = SLASH_COMMANDS.select { |cmd| cmd.start_with?(text.downcase) }
|
|
40
|
+
else
|
|
41
|
+
@suggestions = COMMANDS.select { |cmd| cmd.start_with?(text.downcase) }
|
|
42
|
+
end
|
|
26
43
|
@selected_index = 0
|
|
27
44
|
@suggestions
|
|
28
45
|
end
|
|
@@ -55,15 +72,33 @@ module Gsd
|
|
|
55
72
|
dim = t[:dim]
|
|
56
73
|
reset = Colors::RESET
|
|
57
74
|
|
|
58
|
-
display
|
|
75
|
+
# Build display with proper ANSI handling
|
|
76
|
+
parts = @suggestions.first(3).map.with_index do |sug, i|
|
|
77
|
+
# Truncate suggestion text first (before adding ANSI)
|
|
78
|
+
truncated = sug[0...width]
|
|
59
79
|
if i == @selected_index
|
|
60
|
-
" #{bg}#{selected_fg} #{
|
|
80
|
+
" #{bg}#{selected_fg} #{truncated} #{reset}"
|
|
61
81
|
else
|
|
62
|
-
" #{dim}#{
|
|
82
|
+
" #{dim}#{truncated}#{reset}"
|
|
63
83
|
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
result = parts.join(' ')
|
|
87
|
+
# Strip ANSI codes to check visible length
|
|
88
|
+
visible_length = result.gsub(/\e\[\d+m/, '').length
|
|
89
|
+
if visible_length > width
|
|
90
|
+
# Find safe truncation point by stripping ANSI, truncating, then adding reset
|
|
91
|
+
stripped = result.gsub(/\e\[\d+m/, '')
|
|
92
|
+
# Account for the extra spaces from join(' ')
|
|
93
|
+
num_items = [parts.length, 3].min
|
|
94
|
+
extra_spaces = (num_items - 1) * 2
|
|
95
|
+
safe_width = width - extra_spaces - 1
|
|
96
|
+
safe_stripped = stripped[0...safe_width]
|
|
97
|
+
# Return stripped version to avoid broken ANSI
|
|
98
|
+
safe_stripped + reset
|
|
99
|
+
else
|
|
100
|
+
result
|
|
101
|
+
end
|
|
67
102
|
end
|
|
68
103
|
|
|
69
104
|
def has_suggestions?
|
data/lib/gsd/version.rb
CHANGED