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
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'gsd/plugins/manager'
|
|
4
|
+
require 'gsd/plugins/installer'
|
|
5
|
+
require 'gsd/plugins/marketplace'
|
|
6
|
+
require 'gsd/plugins/search'
|
|
7
|
+
|
|
8
|
+
module Gsd
|
|
9
|
+
module Plugins
|
|
10
|
+
# Plugin CLI - Interface de linha de comando para plugins
|
|
11
|
+
#
|
|
12
|
+
# Comandos:
|
|
13
|
+
# gsd plugins list
|
|
14
|
+
# gsd plugins install <name|url>
|
|
15
|
+
# gsd plugins uninstall <name>
|
|
16
|
+
# gsd plugins update <name>
|
|
17
|
+
# gsd plugins search <query>
|
|
18
|
+
# gsd plugins info <name>
|
|
19
|
+
# gsd plugins enable <name>
|
|
20
|
+
# gsd plugins disable <name>
|
|
21
|
+
class CLI
|
|
22
|
+
attr_reader :manager, :installer, :marketplace, :search
|
|
23
|
+
|
|
24
|
+
# Inicializa o CLI
|
|
25
|
+
#
|
|
26
|
+
# @param args [Array] Argumentos de linha de comando
|
|
27
|
+
def initialize(args = [])
|
|
28
|
+
@args = args
|
|
29
|
+
@manager = Manager.new
|
|
30
|
+
@installer = Installer.new
|
|
31
|
+
@marketplace = MarketplaceClient.new
|
|
32
|
+
@search = Search.new(client: @marketplace)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Executa o comando
|
|
36
|
+
#
|
|
37
|
+
# @return [Integer] Exit code
|
|
38
|
+
def run
|
|
39
|
+
command = @args.first
|
|
40
|
+
|
|
41
|
+
case command
|
|
42
|
+
when 'list'
|
|
43
|
+
cmd_list
|
|
44
|
+
when 'install'
|
|
45
|
+
cmd_install
|
|
46
|
+
when 'uninstall'
|
|
47
|
+
cmd_uninstall
|
|
48
|
+
when 'update'
|
|
49
|
+
cmd_update
|
|
50
|
+
when 'search'
|
|
51
|
+
cmd_search
|
|
52
|
+
when 'info'
|
|
53
|
+
cmd_info
|
|
54
|
+
when 'enable'
|
|
55
|
+
cmd_enable
|
|
56
|
+
when 'disable'
|
|
57
|
+
cmd_disable
|
|
58
|
+
when 'help', '--help', '-h', nil
|
|
59
|
+
print_help
|
|
60
|
+
0
|
|
61
|
+
else
|
|
62
|
+
warn "Unknown command: #{command}"
|
|
63
|
+
print_help
|
|
64
|
+
1
|
|
65
|
+
end
|
|
66
|
+
rescue => e
|
|
67
|
+
warn "Error: #{e.message}"
|
|
68
|
+
1
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# Comando: list
|
|
74
|
+
#
|
|
75
|
+
# @return [Integer] Exit code
|
|
76
|
+
def cmd_list
|
|
77
|
+
plugins = @manager.list
|
|
78
|
+
|
|
79
|
+
if plugins.empty?
|
|
80
|
+
puts "No plugins installed."
|
|
81
|
+
puts "\nInstall plugins with:"
|
|
82
|
+
puts " gsd plugins install <name|url>"
|
|
83
|
+
return 0
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
puts "Installed plugins:"
|
|
87
|
+
puts "=" * 60
|
|
88
|
+
|
|
89
|
+
plugins.each do |plugin|
|
|
90
|
+
status = plugin[:enabled] ? '✓' : '✗'
|
|
91
|
+
puts "#{status} #{plugin[:name]} v#{plugin[:version]}"
|
|
92
|
+
puts " #{plugin[:description]}"
|
|
93
|
+
puts " Author: #{plugin[:author]}" if plugin[:author]
|
|
94
|
+
puts
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
stats = @manager.stats
|
|
98
|
+
puts "=" * 60
|
|
99
|
+
puts "Total: #{stats[:total]} | Enabled: #{stats[:enabled]} | Disabled: #{stats[:disabled]}"
|
|
100
|
+
|
|
101
|
+
0
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Comando: install
|
|
105
|
+
#
|
|
106
|
+
# @return [Integer] Exit code
|
|
107
|
+
def cmd_install
|
|
108
|
+
source = @args[1]
|
|
109
|
+
|
|
110
|
+
unless source
|
|
111
|
+
warn "Usage: gsd plugins install <name|url>"
|
|
112
|
+
return 1
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
puts "Installing plugin: #{source}..."
|
|
116
|
+
|
|
117
|
+
if @installer.install(source)
|
|
118
|
+
puts "✓ Plugin installed successfully!"
|
|
119
|
+
0
|
|
120
|
+
else
|
|
121
|
+
warn "✗ Failed to install plugin"
|
|
122
|
+
1
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Comando: uninstall
|
|
127
|
+
#
|
|
128
|
+
# @return [Integer] Exit code
|
|
129
|
+
def cmd_uninstall
|
|
130
|
+
name = @args[1]
|
|
131
|
+
|
|
132
|
+
unless name
|
|
133
|
+
warn "Usage: gsd plugins uninstall <name>"
|
|
134
|
+
return 1
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
unless @installer.installed?(name)
|
|
138
|
+
warn "Plugin not installed: #{name}"
|
|
139
|
+
return 1
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
print "Uninstalling plugin #{name}... "
|
|
143
|
+
|
|
144
|
+
if @installer.uninstall(name)
|
|
145
|
+
puts "✓"
|
|
146
|
+
0
|
|
147
|
+
else
|
|
148
|
+
puts "✗"
|
|
149
|
+
1
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Comando: update
|
|
154
|
+
#
|
|
155
|
+
# @return [Integer] Exit code
|
|
156
|
+
def cmd_update
|
|
157
|
+
name = @args[1]
|
|
158
|
+
|
|
159
|
+
unless name
|
|
160
|
+
warn "Usage: gsd plugins update <name>"
|
|
161
|
+
return 1
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
puts "Updating plugin: #{name}..."
|
|
165
|
+
|
|
166
|
+
if @installer.update(name)
|
|
167
|
+
puts "✓ Plugin updated successfully!"
|
|
168
|
+
0
|
|
169
|
+
else
|
|
170
|
+
warn "✗ Failed to update plugin"
|
|
171
|
+
1
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Comando: search
|
|
176
|
+
#
|
|
177
|
+
# @return [Integer] Exit code
|
|
178
|
+
def cmd_search
|
|
179
|
+
query = @args[1]
|
|
180
|
+
|
|
181
|
+
unless query
|
|
182
|
+
warn "Usage: gsd plugins search <query>"
|
|
183
|
+
return 1
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
puts "Searching for: #{query}..."
|
|
187
|
+
puts "=" * 60
|
|
188
|
+
|
|
189
|
+
begin
|
|
190
|
+
results = @search.search(query)
|
|
191
|
+
|
|
192
|
+
if results.empty?
|
|
193
|
+
puts "No plugins found."
|
|
194
|
+
return 0
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
results.each do |plugin|
|
|
198
|
+
puts "#{plugin['name']} v#{plugin['version']}"
|
|
199
|
+
puts " #{plugin['description']}"
|
|
200
|
+
puts " Rating: #{plugin['rating']} ⭐ | Downloads: #{plugin['downloads']}"
|
|
201
|
+
puts
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
puts "=" * 60
|
|
205
|
+
puts "Found #{results.count} plugin(s)"
|
|
206
|
+
|
|
207
|
+
rescue => e
|
|
208
|
+
warn "Search error: #{e.message}"
|
|
209
|
+
return 1
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
0
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Comando: info
|
|
216
|
+
#
|
|
217
|
+
# @return [Integer] Exit code
|
|
218
|
+
def cmd_info
|
|
219
|
+
name = @args[1]
|
|
220
|
+
|
|
221
|
+
unless name
|
|
222
|
+
warn "Usage: gsd plugins info <name>"
|
|
223
|
+
return 1
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
begin
|
|
227
|
+
plugin = @marketplace.get_plugin(name)
|
|
228
|
+
|
|
229
|
+
unless plugin
|
|
230
|
+
warn "Plugin not found: #{name}"
|
|
231
|
+
return 1
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
puts "Plugin: #{plugin['name']} v#{plugin['version']}"
|
|
235
|
+
puts "=" * 60
|
|
236
|
+
puts "Description: #{plugin['description']}"
|
|
237
|
+
puts "Author: #{plugin['author']}" if plugin['author']
|
|
238
|
+
puts "Category: #{plugin['category']}" if plugin['category']
|
|
239
|
+
puts "Tags: #{plugin['tags'].join(', ')}" if plugin['tags']
|
|
240
|
+
puts "Rating: #{plugin['rating']} ⭐ (#{plugin['downloads']} downloads)"
|
|
241
|
+
puts "Source: #{plugin['source_url']}" if plugin['source_url']
|
|
242
|
+
puts "Updated: #{plugin['updated_at']}" if plugin['updated_at']
|
|
243
|
+
puts "=" * 60
|
|
244
|
+
|
|
245
|
+
rescue => e
|
|
246
|
+
warn "Error: #{e.message}"
|
|
247
|
+
return 1
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
0
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Comando: enable
|
|
254
|
+
#
|
|
255
|
+
# @return [Integer] Exit code
|
|
256
|
+
def cmd_enable
|
|
257
|
+
name = @args[1]
|
|
258
|
+
|
|
259
|
+
unless name
|
|
260
|
+
warn "Usage: gsd plugins enable <name>"
|
|
261
|
+
return 1
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
if @manager.enable(name)
|
|
265
|
+
puts "✓ Plugin #{name} enabled"
|
|
266
|
+
0
|
|
267
|
+
else
|
|
268
|
+
warn "✗ Failed to enable plugin #{name}"
|
|
269
|
+
1
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Comando: disable
|
|
274
|
+
#
|
|
275
|
+
# @return [Integer] Exit code
|
|
276
|
+
def cmd_disable
|
|
277
|
+
name = @args[1]
|
|
278
|
+
|
|
279
|
+
unless name
|
|
280
|
+
warn "Usage: gsd plugins disable <name>"
|
|
281
|
+
return 1
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
if @manager.disable(name)
|
|
285
|
+
puts "✓ Plugin #{name} disabled"
|
|
286
|
+
0
|
|
287
|
+
else
|
|
288
|
+
warn "✗ Failed to disable plugin #{name}"
|
|
289
|
+
1
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Imprime ajuda
|
|
294
|
+
#
|
|
295
|
+
# @return [void]
|
|
296
|
+
def print_help
|
|
297
|
+
puts <<~HELP
|
|
298
|
+
|
|
299
|
+
GSD Plugins CLI
|
|
300
|
+
|
|
301
|
+
USAGE:
|
|
302
|
+
gsd plugins <command> [args]
|
|
303
|
+
|
|
304
|
+
COMMANDS:
|
|
305
|
+
list List installed plugins
|
|
306
|
+
install <name|url> Install plugin
|
|
307
|
+
uninstall <name> Uninstall plugin
|
|
308
|
+
update <name> Update plugin
|
|
309
|
+
search <query> Search marketplace
|
|
310
|
+
info <name> Plugin information
|
|
311
|
+
enable <name> Enable plugin
|
|
312
|
+
disable <name> Disable plugin
|
|
313
|
+
help Show this help
|
|
314
|
+
|
|
315
|
+
EXAMPLES:
|
|
316
|
+
gsd plugins list
|
|
317
|
+
gsd plugins install github-plugin
|
|
318
|
+
gsd plugins install https://github.com/user/plugin.zip
|
|
319
|
+
gsd plugins uninstall github-plugin
|
|
320
|
+
gsd plugins update github-plugin
|
|
321
|
+
gsd plugins search github
|
|
322
|
+
gsd plugins info github-plugin
|
|
323
|
+
gsd plugins enable github-plugin
|
|
324
|
+
gsd plugins disable github-plugin
|
|
325
|
+
|
|
326
|
+
HELP
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module Gsd
|
|
7
|
+
module Plugins
|
|
8
|
+
# Plugin Config - Gerencia configurações de plugins
|
|
9
|
+
#
|
|
10
|
+
# Responsável por:
|
|
11
|
+
# - Armazenar configurações por plugin
|
|
12
|
+
# - Carregar configurações salvas
|
|
13
|
+
# - Validar configurações
|
|
14
|
+
class Config
|
|
15
|
+
attr_reader :config_dir
|
|
16
|
+
|
|
17
|
+
# Inicializa o Config
|
|
18
|
+
#
|
|
19
|
+
# @param config_dir [String] Diretório de configurações
|
|
20
|
+
def initialize(config_dir: nil)
|
|
21
|
+
@config_dir = config_dir || File.join(Dir.home, '.gsd', 'plugins', 'config')
|
|
22
|
+
ensure_config_dir
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Obtém configuração de um plugin
|
|
26
|
+
#
|
|
27
|
+
# @param plugin_name [String] Nome do plugin
|
|
28
|
+
# @param key [String, nil] Chave específica
|
|
29
|
+
# @return [Hash, Object] Configuração ou valor
|
|
30
|
+
def get(plugin_name, key = nil)
|
|
31
|
+
config = load_config(plugin_name)
|
|
32
|
+
key ? config[key] : config
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Define configuração de um plugin
|
|
36
|
+
#
|
|
37
|
+
# @param plugin_name [String] Nome do plugin
|
|
38
|
+
# @param key [String, Hash] Chave ou hash de configurações
|
|
39
|
+
# @param value [Object] Valor (se key for string)
|
|
40
|
+
# @return [void]
|
|
41
|
+
def set(plugin_name, key, value = nil)
|
|
42
|
+
config = load_config(plugin_name)
|
|
43
|
+
|
|
44
|
+
if key.is_a?(Hash)
|
|
45
|
+
config.merge!(key)
|
|
46
|
+
else
|
|
47
|
+
config[key] = value
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
save_config(plugin_name, config)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Remove configuração de um plugin
|
|
54
|
+
#
|
|
55
|
+
# @param plugin_name [String] Nome do plugin
|
|
56
|
+
# @param key [String, nil] Chave específica
|
|
57
|
+
# @return [void]
|
|
58
|
+
def delete(plugin_name, key = nil)
|
|
59
|
+
return unless config_exists?(plugin_name)
|
|
60
|
+
|
|
61
|
+
if key
|
|
62
|
+
config = load_config(plugin_name)
|
|
63
|
+
config.delete(key)
|
|
64
|
+
save_config(plugin_name, config)
|
|
65
|
+
else
|
|
66
|
+
remove_config(plugin_name)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Verifica se plugin tem configuração
|
|
71
|
+
#
|
|
72
|
+
# @param plugin_name [String] Nome do plugin
|
|
73
|
+
# @return [Boolean] true se existe
|
|
74
|
+
def config_exists?(plugin_name)
|
|
75
|
+
File.exist?(config_file(plugin_name))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Lista todos os plugins com configuração
|
|
79
|
+
#
|
|
80
|
+
# @return [Array<String>] Nomes dos plugins
|
|
81
|
+
def list_plugins
|
|
82
|
+
Dir.children(@config_dir).select do |child|
|
|
83
|
+
File.file?(File.join(@config_dir, child)) && child.end_with?('.json')
|
|
84
|
+
end.map { |f| f.gsub('.json', '') }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Reseta configuração de um plugin
|
|
88
|
+
#
|
|
89
|
+
# @param plugin_name [String] Nome do plugin
|
|
90
|
+
# @return [void]
|
|
91
|
+
def reset(plugin_name)
|
|
92
|
+
remove_config(plugin_name)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Exporta configurações
|
|
96
|
+
#
|
|
97
|
+
# @return [Hash] Todas as configurações
|
|
98
|
+
def export
|
|
99
|
+
result = {}
|
|
100
|
+
list_plugins.each do |plugin_name|
|
|
101
|
+
result[plugin_name] = load_config(plugin_name)
|
|
102
|
+
end
|
|
103
|
+
result
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Importa configurações
|
|
107
|
+
#
|
|
108
|
+
# @param configs [Hash] Configurações para importar
|
|
109
|
+
# @return [void]
|
|
110
|
+
def import(configs)
|
|
111
|
+
configs.each do |plugin_name, config|
|
|
112
|
+
save_config(plugin_name, config)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
# Garante que diretório existe
|
|
119
|
+
#
|
|
120
|
+
# @return [void]
|
|
121
|
+
def ensure_config_dir
|
|
122
|
+
FileUtils.mkdir_p(@config_dir) unless File.directory?(@config_dir)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Caminho do arquivo de configuração
|
|
126
|
+
#
|
|
127
|
+
# @param plugin_name [String] Nome do plugin
|
|
128
|
+
# @return [String] Caminho do arquivo
|
|
129
|
+
def config_file(plugin_name)
|
|
130
|
+
File.join(@config_dir, "#{plugin_name}.json")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Carrega configuração
|
|
134
|
+
#
|
|
135
|
+
# @param plugin_name [String] Nome do plugin
|
|
136
|
+
# @return [Hash] Configuração
|
|
137
|
+
def load_config(plugin_name)
|
|
138
|
+
return {} unless config_exists?(plugin_name)
|
|
139
|
+
|
|
140
|
+
JSON.parse(File.read(config_file(plugin_name)))
|
|
141
|
+
rescue => e
|
|
142
|
+
warn "[Plugin Config] Error loading config for #{plugin_name}: #{e.message}"
|
|
143
|
+
{}
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Salva configuração
|
|
147
|
+
#
|
|
148
|
+
# @param plugin_name [String] Nome do plugin
|
|
149
|
+
# @param config [Hash] Configuração
|
|
150
|
+
# @return [void]
|
|
151
|
+
def save_config(plugin_name, config)
|
|
152
|
+
File.write(config_file(plugin_name), JSON.pretty_generate(config))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Remove configuração
|
|
156
|
+
#
|
|
157
|
+
# @param plugin_name [String] Nome do plugin
|
|
158
|
+
# @return [void]
|
|
159
|
+
def remove_config(plugin_name)
|
|
160
|
+
File.delete(config_file(plugin_name)) if config_exists?(plugin_name)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gsd
|
|
4
|
+
module Plugins
|
|
5
|
+
# Plugin Hooks - Sistema de hooks para plugins
|
|
6
|
+
#
|
|
7
|
+
# Responsável por:
|
|
8
|
+
# - Registrar hooks de plugins
|
|
9
|
+
# - Executar hooks em eventos
|
|
10
|
+
# - Gerenciar prioridade de hooks
|
|
11
|
+
class Hooks
|
|
12
|
+
attr_reader :hooks
|
|
13
|
+
|
|
14
|
+
# Hook types
|
|
15
|
+
HOOK_TYPES = [
|
|
16
|
+
:pre_command,
|
|
17
|
+
:post_command,
|
|
18
|
+
:pre_tool,
|
|
19
|
+
:post_tool,
|
|
20
|
+
:pre_commit,
|
|
21
|
+
:post_phase,
|
|
22
|
+
:on_enable,
|
|
23
|
+
:on_disable
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
# Inicializa o Hooks
|
|
27
|
+
def initialize
|
|
28
|
+
@hooks = {}
|
|
29
|
+
HOOK_TYPES.each { |type| @hooks[type] = [] }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Registra um hook
|
|
33
|
+
#
|
|
34
|
+
# @param type [Symbol] Tipo do hook
|
|
35
|
+
# @param plugin [String] Nome do plugin
|
|
36
|
+
# @param callback [Proc] Callback para executar
|
|
37
|
+
# @param priority [Integer] Prioridade (maior = primeiro)
|
|
38
|
+
# @return [void]
|
|
39
|
+
def register(type, plugin, callback, priority: 0)
|
|
40
|
+
unless HOOK_TYPES.include?(type)
|
|
41
|
+
raise ArgumentError, "Invalid hook type: #{type}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
@hooks[type] << {
|
|
45
|
+
plugin: plugin,
|
|
46
|
+
callback: callback,
|
|
47
|
+
priority: priority,
|
|
48
|
+
registered_at: Time.now
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Ordena por prioridade
|
|
52
|
+
@hooks[type].sort_by! { |h| -h[:priority] }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Remove hook de um plugin
|
|
56
|
+
#
|
|
57
|
+
# @param plugin [String] Nome do plugin
|
|
58
|
+
# @return [void]
|
|
59
|
+
def unregister(plugin)
|
|
60
|
+
@hooks.each do |type, hook_list|
|
|
61
|
+
@hooks[type] = hook_list.reject { |h| h[:plugin] == plugin }
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Executa hooks de um tipo
|
|
66
|
+
#
|
|
67
|
+
# @param type [Symbol] Tipo do hook
|
|
68
|
+
# @param context [Hash] Contexto para o hook
|
|
69
|
+
# @return [Array] Resultados dos hooks
|
|
70
|
+
def run(type, context = {})
|
|
71
|
+
unless HOOK_TYPES.include?(type)
|
|
72
|
+
raise ArgumentError, "Invalid hook type: #{type}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
results = []
|
|
76
|
+
@hooks[type].each do |hook|
|
|
77
|
+
begin
|
|
78
|
+
result = hook[:callback].call(context)
|
|
79
|
+
results << {
|
|
80
|
+
plugin: hook[:plugin],
|
|
81
|
+
result: result,
|
|
82
|
+
success: true
|
|
83
|
+
}
|
|
84
|
+
rescue => e
|
|
85
|
+
results << {
|
|
86
|
+
plugin: hook[:plugin],
|
|
87
|
+
error: e.message,
|
|
88
|
+
success: false
|
|
89
|
+
}
|
|
90
|
+
warn "[Hooks] Error in #{type} hook (#{hook[:plugin]}): #{e.message}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
results
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Lista hooks registrados
|
|
98
|
+
#
|
|
99
|
+
# @param type [Symbol, nil] Tipo específico
|
|
100
|
+
# @return [Array<Hash>] Lista de hooks
|
|
101
|
+
def list(type = nil)
|
|
102
|
+
if type
|
|
103
|
+
@hooks[type] || []
|
|
104
|
+
else
|
|
105
|
+
@hooks
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Conta hooks por tipo
|
|
110
|
+
#
|
|
111
|
+
# @return [Hash] Contagem por tipo
|
|
112
|
+
def count
|
|
113
|
+
@hooks.transform_values(&:count)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Limpa todos os hooks
|
|
117
|
+
#
|
|
118
|
+
# @return [void]
|
|
119
|
+
def clear
|
|
120
|
+
HOOK_TYPES.each { |type| @hooks[type] = [] }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Limpa hooks de um tipo
|
|
124
|
+
#
|
|
125
|
+
# @param type [Symbol] Tipo do hook
|
|
126
|
+
# @return [void]
|
|
127
|
+
def clear_type(type)
|
|
128
|
+
@hooks[type] = [] if HOOK_TYPES.include?(type)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|