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/history.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'go/bridge'
|
|
4
|
+
|
|
5
|
+
module Gsd
|
|
6
|
+
# History - Wrapper Ruby para operações de history via Go
|
|
7
|
+
module History
|
|
8
|
+
class << self
|
|
9
|
+
# Agrega todos os SUMMARY.md e cria um digest
|
|
10
|
+
#
|
|
11
|
+
# @param cwd [String] Diretório de trabalho
|
|
12
|
+
# @return [Hash] Digest de history
|
|
13
|
+
def digest(cwd: nil)
|
|
14
|
+
cwd ||= Dir.pwd
|
|
15
|
+
result = Go::Bridge.call('history', { 'digest' => true }, cwd: cwd)
|
|
16
|
+
|
|
17
|
+
if result['success']
|
|
18
|
+
result['data']
|
|
19
|
+
else
|
|
20
|
+
raise HistoryError, result['error']
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Extrai dados de um SUMMARY.md específico
|
|
25
|
+
#
|
|
26
|
+
# @param file [String] Caminho para o arquivo SUMMARY.md
|
|
27
|
+
# @param fields [Array<String>] Campos para extrair
|
|
28
|
+
# @param cwd [String] Diretório de trabalho
|
|
29
|
+
# @return [Hash] Dados extraídos
|
|
30
|
+
def summary_extract(file:, fields: [], cwd: nil)
|
|
31
|
+
cwd ||= Dir.pwd
|
|
32
|
+
args = { 'summary-extract' => file }
|
|
33
|
+
args['fields'] = fields.join(',') unless fields.empty?
|
|
34
|
+
result = Go::Bridge.call('history', args, cwd: cwd)
|
|
35
|
+
|
|
36
|
+
if result['success']
|
|
37
|
+
result['data']
|
|
38
|
+
else
|
|
39
|
+
raise HistoryError, result['error']
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Adiciona uma decisão ao STATE.md
|
|
44
|
+
#
|
|
45
|
+
# @param summary [String] Resumo da decisão
|
|
46
|
+
# @param rationale [String] Racional da decisão
|
|
47
|
+
# @param phase [String] Phase relacionada
|
|
48
|
+
# @param cwd [String] Diretório de trabalho
|
|
49
|
+
def add_decision(summary:, rationale:, phase: nil, cwd: nil)
|
|
50
|
+
# Note: This would need a Go implementation
|
|
51
|
+
# For now, just a placeholder
|
|
52
|
+
raise NotImplementedError, 'add_decision not yet implemented'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Adiciona um blocker ao STATE.md
|
|
56
|
+
#
|
|
57
|
+
# @param text [String] Texto do blocker
|
|
58
|
+
# @param cwd [String] Diretório de trabalho
|
|
59
|
+
def add_blocker(text:, cwd: nil)
|
|
60
|
+
# Note: This would need a Go implementation
|
|
61
|
+
raise NotImplementedError, 'add_blocker not yet implemented'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Remove um blocker do STATE.md
|
|
65
|
+
#
|
|
66
|
+
# @param text [String] Texto do blocker
|
|
67
|
+
# @param cwd [String] Diretório de trabalho
|
|
68
|
+
def resolve_blocker(text:, cwd: nil)
|
|
69
|
+
# Note: This would need a Go implementation
|
|
70
|
+
raise NotImplementedError, 'resolve_blocker not yet implemented'
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class HistoryError < StandardError; end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'go/bridge'
|
|
4
|
+
|
|
5
|
+
module Gsd
|
|
6
|
+
# Milestone - Wrapper Ruby para operações de milestone via Go
|
|
7
|
+
module Milestone
|
|
8
|
+
class << self
|
|
9
|
+
# Completa um milestone
|
|
10
|
+
#
|
|
11
|
+
# @param version [String] Versão do milestone (ex: v1.0.0)
|
|
12
|
+
# @param name [String] Nome do milestone (opcional)
|
|
13
|
+
# @param archive_phases [Boolean] Arquivar phases do milestone
|
|
14
|
+
# @param cwd [String] Diretório de trabalho
|
|
15
|
+
# @return [Hash] Resultado da operação
|
|
16
|
+
def complete(version:, name: nil, archive_phases: false, cwd: nil)
|
|
17
|
+
cwd ||= Dir.pwd
|
|
18
|
+
args = { 'complete' => version }
|
|
19
|
+
args['name'] = name if name
|
|
20
|
+
args['archive-phases'] = 'true' if archive_phases
|
|
21
|
+
result = Go::Bridge.call('milestone', args, cwd: cwd)
|
|
22
|
+
|
|
23
|
+
if result['success']
|
|
24
|
+
result['data']
|
|
25
|
+
else
|
|
26
|
+
raise MilestoneError, result['error']
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Arquiva um milestone
|
|
31
|
+
#
|
|
32
|
+
# @param version [String] Versão do milestone
|
|
33
|
+
# @param cwd [String] Diretório de trabalho
|
|
34
|
+
# @return [Hash] Resultado da operação
|
|
35
|
+
def archive(version:, cwd: nil)
|
|
36
|
+
cwd ||= Dir.pwd
|
|
37
|
+
args = { 'archive' => version }
|
|
38
|
+
result = Go::Bridge.call('milestone', args, cwd: cwd)
|
|
39
|
+
|
|
40
|
+
if result['success']
|
|
41
|
+
result['data']
|
|
42
|
+
else
|
|
43
|
+
raise MilestoneError, result['error']
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Marca requirements como completos
|
|
48
|
+
#
|
|
49
|
+
# @param ids [Array<String>] IDs dos requirements
|
|
50
|
+
# @param cwd [String] Diretório de trabalho
|
|
51
|
+
# @return [Hash] Resultado da operação
|
|
52
|
+
def requirements_mark_complete(ids:, cwd: nil)
|
|
53
|
+
cwd ||= Dir.pwd
|
|
54
|
+
args = { 'requirements' => 'mark-complete', 'ids' => ids.join(',') }
|
|
55
|
+
result = Go::Bridge.call('milestone', args, cwd: cwd)
|
|
56
|
+
|
|
57
|
+
if result['success']
|
|
58
|
+
result['data']
|
|
59
|
+
else
|
|
60
|
+
raise MilestoneError, result['error']
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Helper: Verifica se milestone foi completado com sucesso
|
|
65
|
+
#
|
|
66
|
+
# @param result [Hash] Resultado da operação
|
|
67
|
+
# @return [Boolean] true se completado
|
|
68
|
+
def completed?(result)
|
|
69
|
+
result.is_a?(Hash) && result['success'] == true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class MilestoneError < StandardError; end
|
|
74
|
+
end
|
|
75
|
+
end
|
data/lib/gsd/output.rb
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Gsd
|
|
6
|
+
# Output formatting utilities
|
|
7
|
+
module Output
|
|
8
|
+
class << self
|
|
9
|
+
# JSON output
|
|
10
|
+
#
|
|
11
|
+
# @param data [Hash, Array] Data to output
|
|
12
|
+
# @param pretty [Boolean] Pretty print JSON
|
|
13
|
+
def json(data, pretty: true)
|
|
14
|
+
if pretty
|
|
15
|
+
puts JSON.pretty_generate(data)
|
|
16
|
+
else
|
|
17
|
+
puts JSON.generate(data)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Raw key=value output
|
|
22
|
+
#
|
|
23
|
+
# @param data [Hash, Array, String] Data to output
|
|
24
|
+
def raw(data)
|
|
25
|
+
case data
|
|
26
|
+
when Hash
|
|
27
|
+
data.each { |k, v| puts "#{k}=#{v}" }
|
|
28
|
+
when Array
|
|
29
|
+
puts data.join("\n")
|
|
30
|
+
else
|
|
31
|
+
puts data
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Table output with borders
|
|
36
|
+
#
|
|
37
|
+
# @param headers [Array<String>] Column headers
|
|
38
|
+
# @param rows [Array<Array<String>>] Data rows
|
|
39
|
+
def table(headers, rows)
|
|
40
|
+
return if rows.empty?
|
|
41
|
+
|
|
42
|
+
# Calculate column widths
|
|
43
|
+
col_widths = headers.map(&:length)
|
|
44
|
+
rows.each do |row|
|
|
45
|
+
row.each_with_index do |cell, i|
|
|
46
|
+
col_widths[i] = [col_widths[i], cell.to_s.length].max
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Print header
|
|
51
|
+
puts headers.zip(col_widths).map { |h, w| h.ljust(w) }.join(' │ ')
|
|
52
|
+
puts '─' * (col_widths.sum + (headers.length - 1) * 3)
|
|
53
|
+
|
|
54
|
+
# Print rows
|
|
55
|
+
rows.each do |row|
|
|
56
|
+
puts row.zip(col_widths).map { |v, w| v.to_s.ljust(w) }.join(' │ ')
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Progress bar
|
|
61
|
+
#
|
|
62
|
+
# @param progress [Float] Progress value (0.0 to 1.0)
|
|
63
|
+
# @param width [Integer] Bar width in characters
|
|
64
|
+
def bar(progress, width: 40)
|
|
65
|
+
percentage = (progress * 100).round(1)
|
|
66
|
+
filled = (progress * width).round
|
|
67
|
+
empty = width - filled
|
|
68
|
+
|
|
69
|
+
bar = '█' * filled + '░' * empty
|
|
70
|
+
puts "[#{bar}] #{percentage}%"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Formatted success message
|
|
74
|
+
#
|
|
75
|
+
# @param message [String] Success message
|
|
76
|
+
def success(message)
|
|
77
|
+
puts "✅ #{message}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Formatted warning message
|
|
81
|
+
#
|
|
82
|
+
# @param message [String] Warning message
|
|
83
|
+
def warning(message)
|
|
84
|
+
puts "⚠️ #{message}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Formatted error message
|
|
88
|
+
#
|
|
89
|
+
# @param message [String] Error message
|
|
90
|
+
def error(message)
|
|
91
|
+
puts "❌ #{message}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Formatted info message
|
|
95
|
+
#
|
|
96
|
+
# @param message [String] Info message
|
|
97
|
+
def info(message)
|
|
98
|
+
puts "ℹ️ #{message}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Formatted debug message (only if DEBUG env var is set)
|
|
102
|
+
#
|
|
103
|
+
# @param message [String] Debug message
|
|
104
|
+
def debug(message)
|
|
105
|
+
return unless ENV['GSD_DEBUG']
|
|
106
|
+
puts "🔍 [DEBUG] #{message}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Section header
|
|
110
|
+
#
|
|
111
|
+
# @param title [String] Section title
|
|
112
|
+
def section(title)
|
|
113
|
+
puts "\n#{'=' * 60}"
|
|
114
|
+
puts " #{title}"
|
|
115
|
+
puts "#{'=' * 60}\n"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# List with bullets
|
|
119
|
+
#
|
|
120
|
+
# @param items [Array<String>] List items
|
|
121
|
+
# @param bullet [String] Bullet character
|
|
122
|
+
def list(items, bullet: '•')
|
|
123
|
+
items.each { |item| puts " #{bullet} #{item}" }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Key-value pairs
|
|
127
|
+
#
|
|
128
|
+
# @param pairs [Hash] Key-value pairs
|
|
129
|
+
def kv(pairs)
|
|
130
|
+
max_key_length = pairs.keys.map(&:length).max
|
|
131
|
+
pairs.each do |key, value|
|
|
132
|
+
puts "#{key.to_s.ljust(max_key_length)}: #{value}"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Divider line
|
|
137
|
+
#
|
|
138
|
+
# @param char [String] Divider character
|
|
139
|
+
# @param width [Integer] Line width
|
|
140
|
+
def divider(char: '─', width: 60)
|
|
141
|
+
puts char * width
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Status indicator
|
|
145
|
+
#
|
|
146
|
+
# @param status [Symbol] Status: :success, :warning, :error, :pending
|
|
147
|
+
# @param message [String] Status message
|
|
148
|
+
def status(status, message)
|
|
149
|
+
icon = case status
|
|
150
|
+
when :success then '✅'
|
|
151
|
+
when :warning then '⚠️'
|
|
152
|
+
when :error then '❌'
|
|
153
|
+
when :pending then '⏳'
|
|
154
|
+
else '•'
|
|
155
|
+
end
|
|
156
|
+
puts "#{icon} #{message}"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Phase status display
|
|
160
|
+
#
|
|
161
|
+
# @param phase [String] Phase number
|
|
162
|
+
# @param name [String] Phase name
|
|
163
|
+
# @param status [Symbol] Phase status
|
|
164
|
+
def phase_status(phase, name, status)
|
|
165
|
+
icon = case status
|
|
166
|
+
when :complete then '✅'
|
|
167
|
+
when :in_progress then '🔄'
|
|
168
|
+
when :pending then '⏳'
|
|
169
|
+
else '•'
|
|
170
|
+
end
|
|
171
|
+
puts "#{icon} Phase #{phase}: #{name}"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Plan progress display
|
|
175
|
+
#
|
|
176
|
+
# @param current [Integer] Current plan number
|
|
177
|
+
# @param total [Integer] Total plans
|
|
178
|
+
def plan_progress(current, total)
|
|
179
|
+
percentage = (current.to_f / total * 100).round
|
|
180
|
+
puts "📋 Plan #{current}/#{total} (#{percentage}%)"
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
data/lib/gsd/phase.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'go/bridge'
|
|
4
|
+
|
|
5
|
+
module Gsd
|
|
6
|
+
# Phase - Wrapper Ruby para operações de phases via Go
|
|
7
|
+
class Phase
|
|
8
|
+
class << self
|
|
9
|
+
# Encontra uma phase pelo número ou nome
|
|
10
|
+
def find(phase_num, cwd: nil)
|
|
11
|
+
cwd ||= Dir.pwd
|
|
12
|
+
result = Go::Bridge.phase_find(phase_num, cwd: cwd)
|
|
13
|
+
|
|
14
|
+
if result['success']
|
|
15
|
+
result['data']
|
|
16
|
+
else
|
|
17
|
+
raise PhaseError, result['error']
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Lista todas as phases
|
|
22
|
+
def list(cwd: nil)
|
|
23
|
+
cwd ||= Dir.pwd
|
|
24
|
+
result = Go::Bridge.phase_list(cwd: cwd)
|
|
25
|
+
|
|
26
|
+
if result['success']
|
|
27
|
+
result['data']
|
|
28
|
+
else
|
|
29
|
+
raise PhaseError, result['error']
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Calcula próxima phase decimal
|
|
34
|
+
def next_decimal(phase_num, cwd: nil)
|
|
35
|
+
cwd ||= Dir.pwd
|
|
36
|
+
result = Go::Bridge.phase_next_decimal(phase_num, cwd: cwd)
|
|
37
|
+
|
|
38
|
+
if result['success']
|
|
39
|
+
result['data']
|
|
40
|
+
else
|
|
41
|
+
raise PhaseError, result['error']
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Adiciona nova phase
|
|
46
|
+
def add(description, cwd: nil)
|
|
47
|
+
cwd ||= Dir.pwd
|
|
48
|
+
result = Go::Bridge.phase_add(description, cwd: cwd)
|
|
49
|
+
|
|
50
|
+
if result['success']
|
|
51
|
+
result['data']
|
|
52
|
+
else
|
|
53
|
+
raise PhaseError, result['error']
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Helper: verifica se phase existe
|
|
58
|
+
def exists?(phase_num, cwd: nil)
|
|
59
|
+
find(phase_num, cwd: cwd)
|
|
60
|
+
true
|
|
61
|
+
rescue PhaseError
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Helper: obtém path da phase
|
|
66
|
+
def path(phase_num, cwd: nil)
|
|
67
|
+
info = find(phase_num, cwd: cwd)
|
|
68
|
+
info['path']
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Helper: verifica se phase tem CONTEXT.md
|
|
72
|
+
def has_context?(phase_num, cwd: nil)
|
|
73
|
+
info = find(phase_num, cwd: cwd)
|
|
74
|
+
info['has_context']
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Helper: verifica se phase tem RESEARCH.md
|
|
78
|
+
def has_research?(phase_num, cwd: nil)
|
|
79
|
+
info = find(phase_num, cwd: cwd)
|
|
80
|
+
info['has_research']
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Helper: conta planos na phase
|
|
84
|
+
def plan_count(phase_num, cwd: nil)
|
|
85
|
+
info = find(phase_num, cwd: cwd)
|
|
86
|
+
info['plan_count']
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Helper: detecta próxima phase disponível
|
|
90
|
+
def detect_next(cwd: nil)
|
|
91
|
+
phases = list(cwd: cwd)
|
|
92
|
+
return nil if phases['phases'].empty?
|
|
93
|
+
|
|
94
|
+
last_phase = phases['phases'].last
|
|
95
|
+
next_info = next_decimal(last_phase['number'], cwd: cwd)
|
|
96
|
+
next_info['next']
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class PhaseError < StandardError; end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gsd
|
|
4
|
+
module Plugins
|
|
5
|
+
# Plugin Base Class - Classe base para todos os plugins
|
|
6
|
+
#
|
|
7
|
+
# Todos os plugins devem herdar desta classe
|
|
8
|
+
class Base
|
|
9
|
+
class << self
|
|
10
|
+
attr_accessor :name, :version, :description, :author
|
|
11
|
+
|
|
12
|
+
# Define o nome do plugin
|
|
13
|
+
def plugin_name(name)
|
|
14
|
+
@name = name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Define a versão do plugin
|
|
18
|
+
def plugin_version(version)
|
|
19
|
+
@version = version
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Define a descrição do plugin
|
|
23
|
+
def plugin_description(desc)
|
|
24
|
+
@description = desc
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Define o autor do plugin
|
|
28
|
+
def plugin_author(author)
|
|
29
|
+
@author = author
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Inicializa o plugin
|
|
34
|
+
#
|
|
35
|
+
# @param config [Hash] Configuração do plugin
|
|
36
|
+
def initialize(config = {})
|
|
37
|
+
@config = config
|
|
38
|
+
@enabled = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Ativa o plugin
|
|
42
|
+
#
|
|
43
|
+
# @return [void]
|
|
44
|
+
def enable
|
|
45
|
+
@enabled = true
|
|
46
|
+
on_enable if respond_to?(:on_enable)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Desativa o plugin
|
|
50
|
+
#
|
|
51
|
+
# @return [void]
|
|
52
|
+
def disable
|
|
53
|
+
@enabled = false
|
|
54
|
+
on_disable if respond_to?(:on_disable)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Verifica se plugin está ativo
|
|
58
|
+
#
|
|
59
|
+
# @return [Boolean] true se ativo
|
|
60
|
+
def enabled?
|
|
61
|
+
@enabled
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Retorna informações do plugin
|
|
65
|
+
#
|
|
66
|
+
# @return [Hash] Informações
|
|
67
|
+
def info
|
|
68
|
+
{
|
|
69
|
+
name: self.class.name,
|
|
70
|
+
version: self.class.version,
|
|
71
|
+
description: self.class.description,
|
|
72
|
+
author: self.class.author,
|
|
73
|
+
enabled: @enabled
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Instala dependências do plugin
|
|
78
|
+
#
|
|
79
|
+
# @return [Boolean] true se sucesso
|
|
80
|
+
def install_dependencies
|
|
81
|
+
true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Valida o plugin
|
|
85
|
+
#
|
|
86
|
+
# @return [Boolean] true se válido
|
|
87
|
+
def valid?
|
|
88
|
+
!self.class.name.nil? && !self.class.version.nil?
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|