fantasy-cli 1.2.6

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 (112) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +456 -0
  4. data/bin/gsd +8 -0
  5. data/bin/gsd-core-darwin-amd64 +0 -0
  6. data/bin/gsd-core-darwin-arm64 +0 -0
  7. data/bin/gsd-core-linux-amd64 +0 -0
  8. data/bin/gsd-core-linux-arm64 +0 -0
  9. data/bin/gsd-core-windows-amd64.exe +0 -0
  10. data/bin/gsd-core-windows-arm64.exe +0 -0
  11. data/bin/gsd-core.exe +0 -0
  12. data/lib/gsd/agents/coordinator.rb +195 -0
  13. data/lib/gsd/agents/task_manager.rb +158 -0
  14. data/lib/gsd/agents/worker.rb +162 -0
  15. data/lib/gsd/agents.rb +30 -0
  16. data/lib/gsd/ai/chat.rb +486 -0
  17. data/lib/gsd/ai/cli.rb +248 -0
  18. data/lib/gsd/ai/command_parser.rb +97 -0
  19. data/lib/gsd/ai/commands/base.rb +42 -0
  20. data/lib/gsd/ai/commands/clear.rb +20 -0
  21. data/lib/gsd/ai/commands/context.rb +30 -0
  22. data/lib/gsd/ai/commands/cost.rb +30 -0
  23. data/lib/gsd/ai/commands/export.rb +42 -0
  24. data/lib/gsd/ai/commands/help.rb +61 -0
  25. data/lib/gsd/ai/commands/model.rb +67 -0
  26. data/lib/gsd/ai/commands/reset.rb +22 -0
  27. data/lib/gsd/ai/config.rb +256 -0
  28. data/lib/gsd/ai/context.rb +324 -0
  29. data/lib/gsd/ai/cost_tracker.rb +361 -0
  30. data/lib/gsd/ai/git_context.rb +169 -0
  31. data/lib/gsd/ai/history.rb +384 -0
  32. data/lib/gsd/ai/providers/anthropic.rb +429 -0
  33. data/lib/gsd/ai/providers/base.rb +282 -0
  34. data/lib/gsd/ai/providers/lmstudio.rb +279 -0
  35. data/lib/gsd/ai/providers/ollama.rb +336 -0
  36. data/lib/gsd/ai/providers/openai.rb +396 -0
  37. data/lib/gsd/ai/providers/openrouter.rb +429 -0
  38. data/lib/gsd/ai/reference_resolver.rb +225 -0
  39. data/lib/gsd/ai/repl.rb +349 -0
  40. data/lib/gsd/ai/streaming.rb +438 -0
  41. data/lib/gsd/ai/ui.rb +429 -0
  42. data/lib/gsd/buddy/cli.rb +284 -0
  43. data/lib/gsd/buddy/gacha.rb +148 -0
  44. data/lib/gsd/buddy/renderer.rb +108 -0
  45. data/lib/gsd/buddy/species.rb +190 -0
  46. data/lib/gsd/buddy/stats.rb +156 -0
  47. data/lib/gsd/buddy.rb +28 -0
  48. data/lib/gsd/cli.rb +455 -0
  49. data/lib/gsd/commands.rb +198 -0
  50. data/lib/gsd/config.rb +183 -0
  51. data/lib/gsd/error.rb +188 -0
  52. data/lib/gsd/frontmatter.rb +123 -0
  53. data/lib/gsd/go/bridge.rb +173 -0
  54. data/lib/gsd/history.rb +76 -0
  55. data/lib/gsd/milestone.rb +75 -0
  56. data/lib/gsd/output.rb +184 -0
  57. data/lib/gsd/phase.rb +102 -0
  58. data/lib/gsd/plugins/base.rb +92 -0
  59. data/lib/gsd/plugins/cli.rb +330 -0
  60. data/lib/gsd/plugins/config.rb +164 -0
  61. data/lib/gsd/plugins/hooks.rb +132 -0
  62. data/lib/gsd/plugins/installer.rb +158 -0
  63. data/lib/gsd/plugins/loader.rb +122 -0
  64. data/lib/gsd/plugins/manager.rb +187 -0
  65. data/lib/gsd/plugins/marketplace.rb +142 -0
  66. data/lib/gsd/plugins/sandbox.rb +114 -0
  67. data/lib/gsd/plugins/search.rb +131 -0
  68. data/lib/gsd/plugins/validator.rb +157 -0
  69. data/lib/gsd/plugins.rb +48 -0
  70. data/lib/gsd/profile.rb +127 -0
  71. data/lib/gsd/research.rb +85 -0
  72. data/lib/gsd/roadmap.rb +90 -0
  73. data/lib/gsd/skills/bundled/commit.md +58 -0
  74. data/lib/gsd/skills/bundled/debug.md +28 -0
  75. data/lib/gsd/skills/bundled/explain.md +41 -0
  76. data/lib/gsd/skills/bundled/plan.md +42 -0
  77. data/lib/gsd/skills/bundled/verify.md +26 -0
  78. data/lib/gsd/skills/loader.rb +189 -0
  79. data/lib/gsd/state.rb +102 -0
  80. data/lib/gsd/template.rb +106 -0
  81. data/lib/gsd/tools/ask_user_question.rb +179 -0
  82. data/lib/gsd/tools/base.rb +204 -0
  83. data/lib/gsd/tools/bash.rb +246 -0
  84. data/lib/gsd/tools/file_edit.rb +297 -0
  85. data/lib/gsd/tools/file_read.rb +199 -0
  86. data/lib/gsd/tools/file_write.rb +153 -0
  87. data/lib/gsd/tools/glob.rb +202 -0
  88. data/lib/gsd/tools/grep.rb +227 -0
  89. data/lib/gsd/tools/gsd_frontmatter.rb +165 -0
  90. data/lib/gsd/tools/gsd_phase.rb +140 -0
  91. data/lib/gsd/tools/gsd_roadmap.rb +108 -0
  92. data/lib/gsd/tools/gsd_state.rb +143 -0
  93. data/lib/gsd/tools/gsd_template.rb +157 -0
  94. data/lib/gsd/tools/gsd_verify.rb +159 -0
  95. data/lib/gsd/tools/registry.rb +103 -0
  96. data/lib/gsd/tools/task.rb +235 -0
  97. data/lib/gsd/tools/todo_write.rb +290 -0
  98. data/lib/gsd/tools/web.rb +260 -0
  99. data/lib/gsd/tui/app.rb +366 -0
  100. data/lib/gsd/tui/auto_complete.rb +79 -0
  101. data/lib/gsd/tui/colors.rb +111 -0
  102. data/lib/gsd/tui/command_palette.rb +126 -0
  103. data/lib/gsd/tui/header.rb +38 -0
  104. data/lib/gsd/tui/input_box.rb +199 -0
  105. data/lib/gsd/tui/spinner.rb +40 -0
  106. data/lib/gsd/tui/status_bar.rb +51 -0
  107. data/lib/gsd/tui.rb +17 -0
  108. data/lib/gsd/validator.rb +216 -0
  109. data/lib/gsd/verify.rb +175 -0
  110. data/lib/gsd/version.rb +5 -0
  111. data/lib/gsd/workstream.rb +91 -0
  112. metadata +231 -0
data/lib/gsd/ai/cli.rb ADDED
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/chat'
4
+ require 'gsd/ai/repl'
5
+ require 'gsd/ai/config'
6
+
7
+ module Gsd
8
+ module AI
9
+ # AI CLI Command - Comando de linha de comando para IA
10
+ #
11
+ # Uso:
12
+ # gsd ai # Inicia REPL interativo
13
+ # gsd ai "sua mensagem" # One-shot command
14
+ # gsd ai --provider openai # Usa OpenAI
15
+ # gsd ai --model claude-3 # Modelo específico
16
+ # gsd ai --help # Ajuda
17
+ class CLI
18
+ attr_reader :options
19
+
20
+ # Inicializa o comando AI
21
+ #
22
+ # @param args [Array] Argumentos de linha de comando
23
+ # @param cwd [String] Diretório de trabalho
24
+ def initialize(args = [], cwd: Dir.pwd)
25
+ @args = args
26
+ @cwd = cwd
27
+ @options = parse_options
28
+ end
29
+
30
+ # Executa o comando AI
31
+ #
32
+ # @return [void]
33
+ def run
34
+ if @options[:help]
35
+ print_help
36
+ return
37
+ end
38
+
39
+ if @options[:version]
40
+ print_version
41
+ return
42
+ end
43
+
44
+ # Verifica se é comando config
45
+ if @args.first == 'config'
46
+ run_config(@args[1..])
47
+ return
48
+ end
49
+
50
+ # Cria o chat
51
+ chat = create_chat
52
+
53
+ # Verifica se é one-shot ou REPL
54
+ if @options[:message]
55
+ # One-shot command
56
+ run_one_shot(chat, @options[:message])
57
+ else
58
+ # REPL interativo
59
+ run_repl(chat)
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ # Parseia as opções de linha de comando
66
+ #
67
+ # @return [Hash] Opções parseadas
68
+ def parse_options
69
+ options = {
70
+ provider: :anthropic,
71
+ model: nil,
72
+ debug: false,
73
+ verbose: false,
74
+ help: false,
75
+ version: false,
76
+ message: nil
77
+ }
78
+
79
+ i = 0
80
+ while i < @args.length
81
+ arg = @args[i]
82
+
83
+ case arg
84
+ when '--provider', '-p'
85
+ options[:provider] = @args[i + 1]&.to_sym || :anthropic
86
+ i += 1
87
+ when '--model', '-m'
88
+ options[:model] = @args[i + 1]
89
+ i += 1
90
+ when '--debug', '-d'
91
+ options[:debug] = true
92
+ when '--verbose', '-v'
93
+ options[:verbose] = true
94
+ when '--help', '-h'
95
+ options[:help] = true
96
+ when '--version'
97
+ options[:version] = true
98
+ when /^--(.+)=/
99
+ # Named arguments like --provider=anthropic
100
+ key, value = arg[2..-1].split('=', 2)
101
+ options[key.to_sym] = value
102
+ when /^-/
103
+ # Unknown flag, ignore
104
+ else
105
+ # First non-flag argument is the message
106
+ options[:message] ||= arg
107
+ end
108
+
109
+ i += 1
110
+ end
111
+
112
+ options
113
+ end
114
+
115
+ # Cria uma instância de Chat
116
+ #
117
+ # @return [Chat] Instância do chat
118
+ def create_chat
119
+ Gsd::AI::Chat.new(
120
+ provider: @options[:provider],
121
+ model: @options[:model],
122
+ cwd: @cwd,
123
+ debug: @options[:debug]
124
+ )
125
+ rescue => e
126
+ puts "❌ Erro ao criar chat: #{e.message}"
127
+ puts "\nDicas:"
128
+ puts " - Verifique se a API key está configurada"
129
+ puts " - Use --provider ollama para LLMs locais"
130
+ puts " - Use --help para ver todas as opções"
131
+ exit 1
132
+ end
133
+
134
+ # Executa comando one-shot
135
+ #
136
+ # @param chat [Chat] Instância do chat
137
+ # @param message [String] Mensagem do usuário
138
+ # @return [void]
139
+ def run_one_shot(chat, message)
140
+ begin
141
+ response = chat.send(message, stream: false)
142
+ puts response
143
+ rescue => e
144
+ puts "❌ Erro: #{e.message}"
145
+ exit 1
146
+ end
147
+ end
148
+
149
+ # Executa REPL interativo
150
+ #
151
+ # @param chat [Chat] Instância do chat
152
+ # @return [void]
153
+ def run_repl(chat)
154
+ repl = Gsd::AI::Repl.new(chat, verbose: @options[:verbose])
155
+ repl.start
156
+ end
157
+
158
+ # Imprime ajuda
159
+ #
160
+ # @return [void]
161
+ def print_help
162
+ puts <<~HELP
163
+
164
+ 🤖 GSD AI Chat - Interface de Conversa com IA
165
+
166
+ USO:
167
+ gsd ai [opções] [mensagem]
168
+ gsd ai config <subcommand> # Gerenciar API keys
169
+
170
+ COMANDOS:
171
+ gsd ai # Inicia REPL interativo
172
+ gsd ai "sua mensagem" # One-shot command
173
+ gsd ai config list # Listar API keys
174
+ gsd ai config set <p> <key> # Configurar API key
175
+ gsd ai --help # Mostra esta ajuda
176
+
177
+ OPÇÕES:
178
+ -p, --provider <name> Provider: anthropic, openai, ollama, openrouter
179
+ -m, --model <name> Modelo do LLM
180
+ -d, --debug Habilita modo debug
181
+ -v, --verbose Modo verbose
182
+ --help, -h Mostra esta ajuda
183
+
184
+ CONFIG (API Keys):
185
+ gsd ai config list # Listar todas API keys
186
+ gsd ai config set openrouter <key> # Configurar OpenRouter
187
+ gsd ai config set anthropic <key> # Configurar Anthropic
188
+ gsd ai config set openai <key> # Configurar OpenAI
189
+ gsd ai config remove <provider> # Remover API key
190
+
191
+ EXEMPLOS:
192
+ # Iniciar REPL interativo
193
+ gsd ai
194
+
195
+ # One-shot command
196
+ gsd ai "qual o estado atual do projeto?"
197
+
198
+ # Configurar API key (OpenRouter - gratuito)
199
+ gsd ai config set openrouter sk-or-v1-...
200
+
201
+ # Usar OpenRouter com Qwen (gratuito)
202
+ gsd ai --provider openrouter --model qwen/qwen-3.6-plus-preview:free
203
+
204
+ # Usar OpenAI
205
+ gsd ai --provider openai "explique este código"
206
+
207
+ # Usar Ollama local
208
+ gsd ai --provider ollama --model llama2 "o que é este projeto?"
209
+
210
+ VARIÁVEIS DE AMBIENTE:
211
+ OPENROUTER_API_KEY API key para OpenRouter (gratuito)
212
+ ANTHROPIC_API_KEY API key para Anthropic
213
+ OPENAI_API_KEY API key para OpenAI
214
+
215
+ PROVIDERS:
216
+ anthropic Anthropic Claude (pago)
217
+ openai OpenAI GPT (pago)
218
+ openrouter OpenRouter (Qwen, Llama - gratuito)
219
+ ollama Ollama (local, gratuito)
220
+ lmstudio LM Studio (local, gratuito)
221
+
222
+ MODELOS GRATUITOS (OpenRouter):
223
+ qwen/qwen-3.6-plus-preview:free - Qwen 3.6 Plus (1M contexto)
224
+ qwen/qwen-2.5-72b-instruct - Qwen 2.5 72B
225
+ meta-llama/llama-3-70b-instruct - Llama 3 70B
226
+ mistralai/mistral-7b-instruct - Mistral 7B
227
+
228
+ HELP
229
+ end
230
+
231
+ # Executa comando config
232
+ #
233
+ # @param args [Array] Argumentos
234
+ # @return [void]
235
+ def run_config(args)
236
+ config = Gsd::AI::ConfigCLI.new(cwd: @cwd)
237
+ config.run(args)
238
+ end
239
+
240
+ # Imprime versão
241
+ #
242
+ # @return [void]
243
+ def print_version
244
+ puts "GSD AI Chat v0.1.0"
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gsd
4
+ module AI
5
+ # CommandParser - Parser de comandos (/) e referências (@)
6
+ #
7
+ # Responsável por:
8
+ # - Detectar comandos que começam com /
9
+ # - Detectar referências que começam com @
10
+ # - Extrair argumentos e opções
11
+ class CommandParser
12
+ # Resultado do parsing
13
+ ParseResult = Struct.new(:type, :command, :args, :references, :raw, keyword_init: true)
14
+
15
+ # Parse input do usuário
16
+ #
17
+ # @param input [String] Texto digitado
18
+ # @return [ParseResult] Resultado estruturado
19
+ def self.parse(input)
20
+ return ParseResult.new(type: :empty, raw: input) if input.nil? || input.strip.empty?
21
+
22
+ trimmed = input.strip
23
+
24
+ # Comando: começa com /
25
+ if trimmed.start_with?('/')
26
+ return parse_command(trimmed)
27
+ end
28
+
29
+ # Referência: contém @
30
+ if trimmed.include?('@')
31
+ return parse_references(trimmed)
32
+ end
33
+
34
+ # Mensagem normal
35
+ ParseResult.new(type: :message, raw: input)
36
+ end
37
+
38
+ # Extrai referências de um texto
39
+ #
40
+ # @param text [String] Texto com @referências
41
+ # @return [Array<String>] Lista de referências sem o @
42
+ def self.extract_references(text)
43
+ return [] unless text.include?('@')
44
+
45
+ # Pattern: @path (até espaço, vírgula, ou fim)
46
+ text.scan(/@([^\s,]+)/).flatten
47
+ end
48
+
49
+ # Verifica se texto tem referências
50
+ #
51
+ # @param text [String] Texto a verificar
52
+ # @return [Boolean] true se tem @
53
+ def self.has_references?(text)
54
+ text&.include?('@') || false
55
+ end
56
+
57
+ # Verifica se é comando
58
+ #
59
+ # @param text [String] Texto a verificar
60
+ # @return [Boolean] true se começa com /
61
+ def self.command?(text)
62
+ text&.strip&.start_with?('/') || false
63
+ end
64
+
65
+ class << self
66
+ private
67
+
68
+ def parse_command(input)
69
+ # Remove / inicial e split
70
+ parts = input[1..].split
71
+ command = parts.first&.downcase
72
+ args = parts[1..] || []
73
+
74
+ ParseResult.new(
75
+ type: :command,
76
+ command: command,
77
+ args: args,
78
+ references: [],
79
+ raw: input
80
+ )
81
+ end
82
+
83
+ def parse_references(input)
84
+ refs = extract_references(input)
85
+
86
+ ParseResult.new(
87
+ type: :reference,
88
+ command: nil,
89
+ args: [],
90
+ references: refs,
91
+ raw: input
92
+ )
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gsd
4
+ module AI
5
+ module Commands
6
+ # Base class para comandos /
7
+ class Base
8
+ attr_reader :chat, :args
9
+
10
+ # Inicializa comando
11
+ #
12
+ # @param chat [Chat] Instância do chat
13
+ # @param args [Array<String>] Argumentos do comando
14
+ def initialize(chat, args = [])
15
+ @chat = chat
16
+ @args = args
17
+ end
18
+
19
+ # Executa o comando
20
+ #
21
+ # @return [String] Resposta do comando
22
+ def execute
23
+ raise NotImplementedError
24
+ end
25
+
26
+ # Descrição do comando para help
27
+ #
28
+ # @return [String] Descrição
29
+ def description
30
+ self.class::DESCRIPTION
31
+ end
32
+
33
+ # Uso do comando para help
34
+ #
35
+ # @return [String] Uso
36
+ def usage
37
+ self.class::USAGE
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/commands/base'
4
+
5
+ module Gsd
6
+ module AI
7
+ module Commands
8
+ # /clear - Limpa histórico
9
+ class Clear < Base
10
+ DESCRIPTION = 'Limpa o histórico atual'
11
+ USAGE = '/clear'
12
+
13
+ def execute
14
+ chat.clear_history
15
+ '🧹 Histórico limpo.'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/commands/base'
4
+
5
+ module Gsd
6
+ module AI
7
+ module Commands
8
+ # /context - Mostra tamanho do contexto
9
+ class Context < Base
10
+ DESCRIPTION = 'Mostra tamanho do contexto'
11
+ USAGE = '/context'
12
+
13
+ def execute
14
+ ctx = chat.context
15
+ info = ctx.cache_info rescue { size: 'unknown', age: 'unknown' }
16
+ size = ctx.size rescue 0
17
+
18
+ <<~CONTEXT
19
+ 📊 Contexto atual:
20
+
21
+ Mensagens: #{chat.history.messages.count}
22
+ Tamanho cache: #{size} bytes
23
+ Cache info: #{info[:size]}
24
+ Idade cache: #{info[:age]}s
25
+ CONTEXT
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/commands/base'
4
+
5
+ module Gsd
6
+ module AI
7
+ module Commands
8
+ # /cost - Mostra estatísticas de custo
9
+ class Cost < Base
10
+ DESCRIPTION = 'Mostra estatísticas de custo'
11
+ USAGE = '/cost'
12
+
13
+ def execute
14
+ stats = chat.cost_tracker.stats
15
+
16
+ <<~COST
17
+ 💰 Estatísticas de custo:
18
+
19
+ Total gasto: $#{stats[:total_cost]&.round(4) || 0}
20
+ Total tokens: #{stats[:total_tokens] || 0}
21
+ Orçamento: $#{stats[:budget] || 100}
22
+ Restante: $#{stats[:budget_remaining]&.round(4) || 100}
23
+ Usado: #{stats[:budget_used_percent]&.round(2) || 0}%
24
+ Requisições: #{stats[:requests_count] || 0}
25
+ COST
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/commands/base'
4
+ require 'json'
5
+ require 'time'
6
+
7
+ module Gsd
8
+ module AI
9
+ module Commands
10
+ # /export - Exporta histórico
11
+ class Export < Base
12
+ DESCRIPTION = 'Exporta histórico para arquivo'
13
+ USAGE = '/export [arquivo.json]'
14
+
15
+ DEFAULT_FILE = '.gsd/ai/history_export.json'
16
+
17
+ def execute
18
+ filename = args.first || DEFAULT_FILE
19
+ path = File.expand_path(filename, chat.instance_variable_get(:@cwd))
20
+
21
+ export_data = {
22
+ exported_at: Time.now.iso8601,
23
+ model: begin
24
+ chat.provider.model
25
+ rescue
26
+ 'unknown'
27
+ end,
28
+ cost: chat.cost_tracker.stats,
29
+ messages: chat.history.messages
30
+ }
31
+
32
+ FileUtils.mkdir_p(File.dirname(path))
33
+ File.write(path, JSON.pretty_generate(export_data))
34
+
35
+ "💾 Histórico exportado para: #{path}"
36
+ rescue => e
37
+ "❌ Erro ao exportar: #{e.message}"
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/commands/base'
4
+
5
+ module Gsd
6
+ module AI
7
+ module Commands
8
+ # /help - Lista comandos disponíveis
9
+ class Help < Base
10
+ DESCRIPTION = 'Lista comandos disponíveis'
11
+ USAGE = '/help [comando]'
12
+
13
+ # Comandos registrados
14
+ COMMANDS = {
15
+ 'help' => 'Lista comandos disponíveis',
16
+ 'clear' => 'Limpa o histórico atual',
17
+ 'reset' => 'Reseta contexto e histórico',
18
+ 'model' => 'Muda ou lista modelos',
19
+ 'cost' => 'Mostra estatísticas de custo',
20
+ 'context' => 'Mostra tamanho do contexto',
21
+ 'export' => 'Exporta histórico para arquivo'
22
+ }.freeze
23
+
24
+ def execute
25
+ if args.any?
26
+ cmd = args.first.downcase
27
+ return "Comando desconhecido: /#{cmd}" unless COMMANDS.key?(cmd)
28
+
29
+ <<~HELP
30
+ /#{cmd} - #{COMMANDS[cmd]}
31
+ Uso: #{usage_for(cmd)}
32
+ HELP
33
+ else
34
+ list = COMMANDS.map { |cmd, desc| " /#{cmd} - #{desc}" }.join("\n")
35
+ <<~HELP
36
+ Comandos disponíveis:
37
+ #{list}
38
+
39
+ Referências:
40
+ @arquivo.rb - Injeta conteúdo do arquivo
41
+ @pasta/ - Lista arquivos da pasta
42
+ @imagem.png - Anexa imagem (se suportado)
43
+
44
+ Use /help <comando> para detalhes.
45
+ HELP
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def usage_for(cmd)
52
+ case cmd
53
+ when 'model' then '/model [nome] (sem nome lista disponíveis)'
54
+ when 'export' then '/export [arquivo.json]'
55
+ else "/#{cmd}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/commands/base'
4
+
5
+ module Gsd
6
+ module AI
7
+ module Commands
8
+ # /model - Muda ou lista modelos
9
+ class Model < Base
10
+ DESCRIPTION = 'Muda ou lista modelos disponíveis'
11
+ USAGE = '/model [nome]'
12
+
13
+ # Modelos disponíveis por provider
14
+ MODELS = {
15
+ 'anthropic' => %w[claude-sonnet-4-5-20250929 claude-3-opus-20240229 claude-3-haiku-20240307],
16
+ 'openai' => %w[gpt-4-turbo gpt-4 gpt-3.5-turbo],
17
+ 'ollama' => %w[llama3.3 mistral qwen2.5],
18
+ 'openrouter' => %w[qwen/qwen-3.6-plus-preview:free meta-llama/llama-3-70b-instruct]
19
+ }.freeze
20
+
21
+ def execute
22
+ if args.any?
23
+ change_model(args.first)
24
+ else
25
+ list_models
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def change_model(model_name)
32
+ provider = chat.provider
33
+
34
+ if provider.respond_to?(:model=)
35
+ provider.model = model_name
36
+ "✅ Modelo alterado para: #{model_name}"
37
+ else
38
+ "⚠️ Provider atual não suporta troca de modelo em runtime.\nUse --model ao iniciar."
39
+ end
40
+ rescue => e
41
+ "❌ Erro ao alterar modelo: #{e.message}"
42
+ end
43
+
44
+ def list_models
45
+ current = begin
46
+ chat.provider.model
47
+ rescue
48
+ 'unknown'
49
+ end
50
+
51
+ lines = MODELS.map do |provider, models|
52
+ list = models.map { |m| m == current ? "→ #{m}" : " #{m}" }.join("\n")
53
+ "#{provider}:\n#{list}"
54
+ end
55
+
56
+ <<~MODELS
57
+ Modelos disponíveis (atual: #{current}):
58
+
59
+ #{lines.join("\n\n")}
60
+
61
+ Use /model <nome> para trocar.
62
+ MODELS
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'gsd/ai/commands/base'
4
+
5
+ module Gsd
6
+ module AI
7
+ module Commands
8
+ # /reset - Reseta tudo
9
+ class Reset < Base
10
+ DESCRIPTION = 'Reseta contexto e histórico'
11
+ USAGE = '/reset'
12
+
13
+ def execute
14
+ chat.clear_history
15
+ chat.context.clear_cache if chat.context.respond_to?(:clear_cache)
16
+ chat.cost_tracker.reset if chat.cost_tracker.respond_to?(:reset)
17
+ '🔄 Contexto e histórico resetados.'
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end