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
@@ -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