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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +456 -0
- data/bin/gsd +8 -0
- data/bin/gsd-core-darwin-amd64 +0 -0
- data/bin/gsd-core-darwin-arm64 +0 -0
- data/bin/gsd-core-linux-amd64 +0 -0
- data/bin/gsd-core-linux-arm64 +0 -0
- data/bin/gsd-core-windows-amd64.exe +0 -0
- data/bin/gsd-core-windows-arm64.exe +0 -0
- data/bin/gsd-core.exe +0 -0
- data/lib/gsd/agents/coordinator.rb +195 -0
- data/lib/gsd/agents/task_manager.rb +158 -0
- data/lib/gsd/agents/worker.rb +162 -0
- data/lib/gsd/agents.rb +30 -0
- data/lib/gsd/ai/chat.rb +486 -0
- data/lib/gsd/ai/cli.rb +248 -0
- data/lib/gsd/ai/command_parser.rb +97 -0
- data/lib/gsd/ai/commands/base.rb +42 -0
- data/lib/gsd/ai/commands/clear.rb +20 -0
- data/lib/gsd/ai/commands/context.rb +30 -0
- data/lib/gsd/ai/commands/cost.rb +30 -0
- data/lib/gsd/ai/commands/export.rb +42 -0
- data/lib/gsd/ai/commands/help.rb +61 -0
- data/lib/gsd/ai/commands/model.rb +67 -0
- data/lib/gsd/ai/commands/reset.rb +22 -0
- data/lib/gsd/ai/config.rb +256 -0
- data/lib/gsd/ai/context.rb +324 -0
- data/lib/gsd/ai/cost_tracker.rb +361 -0
- data/lib/gsd/ai/git_context.rb +169 -0
- data/lib/gsd/ai/history.rb +384 -0
- data/lib/gsd/ai/providers/anthropic.rb +429 -0
- data/lib/gsd/ai/providers/base.rb +282 -0
- data/lib/gsd/ai/providers/lmstudio.rb +279 -0
- data/lib/gsd/ai/providers/ollama.rb +336 -0
- data/lib/gsd/ai/providers/openai.rb +396 -0
- data/lib/gsd/ai/providers/openrouter.rb +429 -0
- data/lib/gsd/ai/reference_resolver.rb +225 -0
- data/lib/gsd/ai/repl.rb +349 -0
- data/lib/gsd/ai/streaming.rb +438 -0
- data/lib/gsd/ai/ui.rb +429 -0
- data/lib/gsd/buddy/cli.rb +284 -0
- data/lib/gsd/buddy/gacha.rb +148 -0
- data/lib/gsd/buddy/renderer.rb +108 -0
- data/lib/gsd/buddy/species.rb +190 -0
- data/lib/gsd/buddy/stats.rb +156 -0
- data/lib/gsd/buddy.rb +28 -0
- data/lib/gsd/cli.rb +455 -0
- data/lib/gsd/commands.rb +198 -0
- data/lib/gsd/config.rb +183 -0
- data/lib/gsd/error.rb +188 -0
- data/lib/gsd/frontmatter.rb +123 -0
- data/lib/gsd/go/bridge.rb +173 -0
- data/lib/gsd/history.rb +76 -0
- data/lib/gsd/milestone.rb +75 -0
- data/lib/gsd/output.rb +184 -0
- data/lib/gsd/phase.rb +102 -0
- data/lib/gsd/plugins/base.rb +92 -0
- data/lib/gsd/plugins/cli.rb +330 -0
- data/lib/gsd/plugins/config.rb +164 -0
- data/lib/gsd/plugins/hooks.rb +132 -0
- data/lib/gsd/plugins/installer.rb +158 -0
- data/lib/gsd/plugins/loader.rb +122 -0
- data/lib/gsd/plugins/manager.rb +187 -0
- data/lib/gsd/plugins/marketplace.rb +142 -0
- data/lib/gsd/plugins/sandbox.rb +114 -0
- data/lib/gsd/plugins/search.rb +131 -0
- data/lib/gsd/plugins/validator.rb +157 -0
- data/lib/gsd/plugins.rb +48 -0
- data/lib/gsd/profile.rb +127 -0
- data/lib/gsd/research.rb +85 -0
- data/lib/gsd/roadmap.rb +90 -0
- data/lib/gsd/skills/bundled/commit.md +58 -0
- data/lib/gsd/skills/bundled/debug.md +28 -0
- data/lib/gsd/skills/bundled/explain.md +41 -0
- data/lib/gsd/skills/bundled/plan.md +42 -0
- data/lib/gsd/skills/bundled/verify.md +26 -0
- data/lib/gsd/skills/loader.rb +189 -0
- data/lib/gsd/state.rb +102 -0
- data/lib/gsd/template.rb +106 -0
- data/lib/gsd/tools/ask_user_question.rb +179 -0
- data/lib/gsd/tools/base.rb +204 -0
- data/lib/gsd/tools/bash.rb +246 -0
- data/lib/gsd/tools/file_edit.rb +297 -0
- data/lib/gsd/tools/file_read.rb +199 -0
- data/lib/gsd/tools/file_write.rb +153 -0
- data/lib/gsd/tools/glob.rb +202 -0
- data/lib/gsd/tools/grep.rb +227 -0
- data/lib/gsd/tools/gsd_frontmatter.rb +165 -0
- data/lib/gsd/tools/gsd_phase.rb +140 -0
- data/lib/gsd/tools/gsd_roadmap.rb +108 -0
- data/lib/gsd/tools/gsd_state.rb +143 -0
- data/lib/gsd/tools/gsd_template.rb +157 -0
- data/lib/gsd/tools/gsd_verify.rb +159 -0
- data/lib/gsd/tools/registry.rb +103 -0
- data/lib/gsd/tools/task.rb +235 -0
- data/lib/gsd/tools/todo_write.rb +290 -0
- data/lib/gsd/tools/web.rb +260 -0
- data/lib/gsd/tui/app.rb +366 -0
- data/lib/gsd/tui/auto_complete.rb +79 -0
- data/lib/gsd/tui/colors.rb +111 -0
- data/lib/gsd/tui/command_palette.rb +126 -0
- data/lib/gsd/tui/header.rb +38 -0
- data/lib/gsd/tui/input_box.rb +199 -0
- data/lib/gsd/tui/spinner.rb +40 -0
- data/lib/gsd/tui/status_bar.rb +51 -0
- data/lib/gsd/tui.rb +17 -0
- data/lib/gsd/validator.rb +216 -0
- data/lib/gsd/verify.rb +175 -0
- data/lib/gsd/version.rb +5 -0
- data/lib/gsd/workstream.rb +91 -0
- 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
|