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,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debug
|
|
3
|
+
description: Debug an issue or error in the code
|
|
4
|
+
aliases: [/dbg, /fix]
|
|
5
|
+
allowed_tools: [BashTool, FileReadTool, GrepTool, FileEditTool]
|
|
6
|
+
model: claude-sonnet-4-5-20250929
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Debug Skill
|
|
10
|
+
|
|
11
|
+
When invoked, you will:
|
|
12
|
+
1. Understand the error or issue
|
|
13
|
+
2. Locate the source of the problem
|
|
14
|
+
3. Analyze the code
|
|
15
|
+
4. Propose and implement a fix
|
|
16
|
+
5. Verify the fix works
|
|
17
|
+
|
|
18
|
+
## User Request
|
|
19
|
+
|
|
20
|
+
{{args}}
|
|
21
|
+
|
|
22
|
+
## Instructions
|
|
23
|
+
|
|
24
|
+
- Start by reproducing the error if possible
|
|
25
|
+
- Use logs and debugging output
|
|
26
|
+
- Check for common issues (null pointers, type errors, etc.)
|
|
27
|
+
- Test the fix thoroughly
|
|
28
|
+
- Explain what was wrong and how you fixed it
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: explain
|
|
3
|
+
description: Explain code, files, or concepts
|
|
4
|
+
aliases: [/exp, /describe]
|
|
5
|
+
allowed_tools: [FileReadTool, GrepTool]
|
|
6
|
+
model: claude-sonnet-4-5-20250929
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Explain Skill
|
|
10
|
+
|
|
11
|
+
When invoked, you will:
|
|
12
|
+
1. Read the specified code or files
|
|
13
|
+
2. Understand the functionality
|
|
14
|
+
3. Explain in clear terms
|
|
15
|
+
4. Provide examples if helpful
|
|
16
|
+
|
|
17
|
+
## User Request
|
|
18
|
+
|
|
19
|
+
{{args}}
|
|
20
|
+
|
|
21
|
+
## Output Format
|
|
22
|
+
|
|
23
|
+
### Overview
|
|
24
|
+
Brief description of what the code does
|
|
25
|
+
|
|
26
|
+
### Detailed Explanation
|
|
27
|
+
Step-by-step explanation of how it works
|
|
28
|
+
|
|
29
|
+
### Key Components
|
|
30
|
+
- **Component 1**: Description
|
|
31
|
+
- **Component 2**: Description
|
|
32
|
+
|
|
33
|
+
### Examples
|
|
34
|
+
Code examples or usage if applicable
|
|
35
|
+
|
|
36
|
+
## Instructions
|
|
37
|
+
|
|
38
|
+
- Use simple language
|
|
39
|
+
- Avoid jargon when possible
|
|
40
|
+
- Include code snippets for clarity
|
|
41
|
+
- Highlight important patterns or gotchas
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan
|
|
3
|
+
description: Create an implementation plan for a feature or task
|
|
4
|
+
aliases: [/planning]
|
|
5
|
+
allowed_tools: [FileReadTool, FileWriteTool, GrepTool]
|
|
6
|
+
model: claude-sonnet-4-5-20250929
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Plan Skill
|
|
10
|
+
|
|
11
|
+
When invoked, you will:
|
|
12
|
+
1. Understand the requirements
|
|
13
|
+
2. Research the codebase
|
|
14
|
+
3. Create a detailed implementation plan
|
|
15
|
+
4. Break down into actionable tasks
|
|
16
|
+
5. Identify risks and dependencies
|
|
17
|
+
|
|
18
|
+
## User Request
|
|
19
|
+
|
|
20
|
+
{{args}}
|
|
21
|
+
|
|
22
|
+
## Output Format
|
|
23
|
+
|
|
24
|
+
Create a plan with:
|
|
25
|
+
|
|
26
|
+
### Overview
|
|
27
|
+
Brief description of what will be implemented
|
|
28
|
+
|
|
29
|
+
### Tasks
|
|
30
|
+
- [ ] Task 1
|
|
31
|
+
- [ ] Task 2
|
|
32
|
+
- [ ] Task 3
|
|
33
|
+
|
|
34
|
+
### Files to Change
|
|
35
|
+
- `path/to/file1.rb` - Description
|
|
36
|
+
- `path/to/file2.go` - Description
|
|
37
|
+
|
|
38
|
+
### Risks
|
|
39
|
+
- Potential issues to watch for
|
|
40
|
+
|
|
41
|
+
### Testing
|
|
42
|
+
- How to verify the implementation
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify
|
|
3
|
+
description: Verify a code change does what it should by running the app
|
|
4
|
+
aliases: [/v, /check]
|
|
5
|
+
allowed_tools: [BashTool, FileReadTool, GrepTool]
|
|
6
|
+
model: claude-sonnet-4-5-20250929
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Verify Skill
|
|
10
|
+
|
|
11
|
+
When invoked, you will:
|
|
12
|
+
1. Read the changed files
|
|
13
|
+
2. Understand the intent
|
|
14
|
+
3. Run tests/verification
|
|
15
|
+
4. Report results
|
|
16
|
+
|
|
17
|
+
## User Request
|
|
18
|
+
|
|
19
|
+
{{args}}
|
|
20
|
+
|
|
21
|
+
## Instructions
|
|
22
|
+
|
|
23
|
+
- Be thorough in verification
|
|
24
|
+
- Run all relevant tests
|
|
25
|
+
- Check for edge cases
|
|
26
|
+
- Report any issues found
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Gsd
|
|
6
|
+
module Skills
|
|
7
|
+
# Skills Loader - Carrega skills bundled e do usuário
|
|
8
|
+
#
|
|
9
|
+
# Responsável por:
|
|
10
|
+
# - Carregar skills bundled
|
|
11
|
+
# - Carregar skills do usuário
|
|
12
|
+
# - Parsear formato de skill markdown
|
|
13
|
+
# - Executar skills
|
|
14
|
+
class Loader
|
|
15
|
+
attr_reader :bundled_skills, :user_skills
|
|
16
|
+
|
|
17
|
+
# Inicializa o loader
|
|
18
|
+
#
|
|
19
|
+
# @param cwd [String] Diretório de trabalho
|
|
20
|
+
def initialize(cwd: Dir.pwd)
|
|
21
|
+
@cwd = cwd
|
|
22
|
+
@bundled_skills = {}
|
|
23
|
+
@user_skills = {}
|
|
24
|
+
|
|
25
|
+
load_bundled_skills
|
|
26
|
+
load_user_skills
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Retorna todas as skills (bundled + user)
|
|
30
|
+
#
|
|
31
|
+
# @return [Hash] Todas as skills
|
|
32
|
+
def all
|
|
33
|
+
@bundled_skills.merge(@user_skills)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Busca uma skill por nome
|
|
37
|
+
#
|
|
38
|
+
# @param name [String] Nome da skill
|
|
39
|
+
# @return [Hash,nil] Skill ou nil
|
|
40
|
+
def find(name)
|
|
41
|
+
@user_skills[name] || @bundled_skills[name]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Lista todas as skills disponíveis
|
|
45
|
+
#
|
|
46
|
+
# @return [Array<String>] Lista de nomes
|
|
47
|
+
def list
|
|
48
|
+
all.keys.sort
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Executa uma skill
|
|
52
|
+
#
|
|
53
|
+
# @param name [String] Nome da skill
|
|
54
|
+
# @param args [String] Argumentos da skill
|
|
55
|
+
# @return [String] Resultado da execução
|
|
56
|
+
def execute(name, args = '')
|
|
57
|
+
skill = find(name)
|
|
58
|
+
raise "Skill not found: #{name}" unless skill
|
|
59
|
+
|
|
60
|
+
# Skill é um hash com :prompt e :metadata
|
|
61
|
+
prompt = skill[:prompt]
|
|
62
|
+
prompt = prompt.gsub('{{args}}', args) if args && !args.empty?
|
|
63
|
+
|
|
64
|
+
prompt
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Recarrega as skills
|
|
68
|
+
#
|
|
69
|
+
# @return [void]
|
|
70
|
+
def reload
|
|
71
|
+
@bundled_skills = {}
|
|
72
|
+
@user_skills = {}
|
|
73
|
+
load_bundled_skills
|
|
74
|
+
load_user_skills
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# Carrega skills bundled
|
|
80
|
+
#
|
|
81
|
+
# @return [void]
|
|
82
|
+
def load_bundled_skills
|
|
83
|
+
bundled_dir = File.join(__dir__, 'bundled')
|
|
84
|
+
return unless File.directory?(bundled_dir)
|
|
85
|
+
|
|
86
|
+
Dir.glob(File.join(bundled_dir, '*.md')).each do |file|
|
|
87
|
+
skill = parse_skill_file(file)
|
|
88
|
+
@bundled_skills[skill[:name]] = skill if skill
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Carrega skills do usuário
|
|
93
|
+
#
|
|
94
|
+
# @return [void]
|
|
95
|
+
def load_user_skills
|
|
96
|
+
user_dir = File.join(@cwd, '.gsd', 'skills')
|
|
97
|
+
return unless File.directory?(user_dir)
|
|
98
|
+
|
|
99
|
+
Dir.glob(File.join(user_dir, '*.md')).each do |file|
|
|
100
|
+
skill = parse_skill_file(file)
|
|
101
|
+
@user_skills[skill[:name]] = skill if skill
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Parseia um arquivo de skill
|
|
106
|
+
#
|
|
107
|
+
# @param file [String] Caminho do arquivo
|
|
108
|
+
# @return [Hash,nil] Skill parseada ou nil
|
|
109
|
+
def parse_skill_file(file)
|
|
110
|
+
content = File.read(file)
|
|
111
|
+
|
|
112
|
+
# Extrai frontmatter YAML
|
|
113
|
+
frontmatter = extract_frontmatter(content)
|
|
114
|
+
body = extract_body(content)
|
|
115
|
+
|
|
116
|
+
return nil unless frontmatter
|
|
117
|
+
|
|
118
|
+
{
|
|
119
|
+
name: frontmatter['name'] || File.basename(file, '.md'),
|
|
120
|
+
description: frontmatter['description'] || '',
|
|
121
|
+
aliases: frontmatter['aliases'] || [],
|
|
122
|
+
allowed_tools: frontmatter['allowed_tools'] || [],
|
|
123
|
+
model: frontmatter['model'],
|
|
124
|
+
prompt: body,
|
|
125
|
+
metadata: frontmatter,
|
|
126
|
+
source: file
|
|
127
|
+
}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Extrai frontmatter YAML do conteúdo
|
|
131
|
+
#
|
|
132
|
+
# @param content [String] Conteúdo do arquivo
|
|
133
|
+
# @return [Hash,nil] Frontmatter parseado ou nil
|
|
134
|
+
def extract_frontmatter(content)
|
|
135
|
+
# Frontmatter está entre --- no início do arquivo
|
|
136
|
+
return nil unless content.start_with?("---\n")
|
|
137
|
+
|
|
138
|
+
parts = content.split(/^---$/, 3)
|
|
139
|
+
return nil if parts.length < 2
|
|
140
|
+
|
|
141
|
+
yaml_content = parts[1]
|
|
142
|
+
parse_yaml_simple(yaml_content)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Extrai corpo do conteúdo (após frontmatter)
|
|
146
|
+
#
|
|
147
|
+
# @param content [String] Conteúdo do arquivo
|
|
148
|
+
# @return [String] Corpo do arquivo
|
|
149
|
+
def extract_body(content)
|
|
150
|
+
parts = content.split(/^---$/, 3)
|
|
151
|
+
return content if parts.length < 3
|
|
152
|
+
|
|
153
|
+
parts[2].strip
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Parse YAML simples (sem dependências externas)
|
|
157
|
+
#
|
|
158
|
+
# @param yaml [String] YAML string
|
|
159
|
+
# @return [Hash] Hash parseado
|
|
160
|
+
def parse_yaml_simple(yaml)
|
|
161
|
+
result = {}
|
|
162
|
+
|
|
163
|
+
yaml.each_line do |line|
|
|
164
|
+
line = line.strip
|
|
165
|
+
next if line.empty? || line.start_with?('#')
|
|
166
|
+
|
|
167
|
+
if line.include?(':')
|
|
168
|
+
key, value = line.split(':', 2)
|
|
169
|
+
key = key.strip
|
|
170
|
+
value = value.strip
|
|
171
|
+
|
|
172
|
+
# Remove aspas
|
|
173
|
+
value = value[1..-2] if value.start_with?('"') && value.end_with?('"')
|
|
174
|
+
value = value[1..-2] if value.start_with?("'") && value.end_with?("'")
|
|
175
|
+
|
|
176
|
+
# Parseia array simples
|
|
177
|
+
if value.start_with?('[') && value.end_with?(']')
|
|
178
|
+
value = value[1..-2].split(',').map(&:strip)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
result[key] = value
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
result
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
data/lib/gsd/state.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'go/bridge'
|
|
4
|
+
|
|
5
|
+
module Gsd
|
|
6
|
+
# State - Wrapper Ruby para operações de STATE.md via Go
|
|
7
|
+
class State
|
|
8
|
+
class << self
|
|
9
|
+
# Carrega STATE.md e retorna dados completos
|
|
10
|
+
def load(cwd: nil)
|
|
11
|
+
cwd ||= Dir.pwd
|
|
12
|
+
result = Go::Bridge.state_load(cwd: cwd)
|
|
13
|
+
|
|
14
|
+
if result['success']
|
|
15
|
+
result['data']
|
|
16
|
+
else
|
|
17
|
+
raise StateError, result['error']
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Retorna frontmatter do STATE.md como JSON
|
|
22
|
+
def json(cwd: nil)
|
|
23
|
+
cwd ||= Dir.pwd
|
|
24
|
+
result = Go::Bridge.state_json(cwd: cwd)
|
|
25
|
+
|
|
26
|
+
if result['success']
|
|
27
|
+
result['data']
|
|
28
|
+
else
|
|
29
|
+
raise StateError, result['error']
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Atualiza um campo específico no STATE.md
|
|
34
|
+
def update(field:, value:, cwd: nil)
|
|
35
|
+
cwd ||= Dir.pwd
|
|
36
|
+
result = Go::Bridge.state_update(field: field, value: value, cwd: cwd)
|
|
37
|
+
|
|
38
|
+
if result['success']
|
|
39
|
+
result['data']
|
|
40
|
+
else
|
|
41
|
+
raise StateError, result['error']
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Atualiza múltiplos campos no STATE.md
|
|
46
|
+
def patch(fields:, cwd: nil)
|
|
47
|
+
cwd ||= Dir.pwd
|
|
48
|
+
result = Go::Bridge.state_patch(fields: fields, cwd: cwd)
|
|
49
|
+
|
|
50
|
+
if result['success']
|
|
51
|
+
result['data']
|
|
52
|
+
else
|
|
53
|
+
raise StateError, result['error']
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Obtém conteúdo do STATE.md ou uma seção específica
|
|
58
|
+
def get(section: nil, cwd: nil)
|
|
59
|
+
cwd ||= Dir.pwd
|
|
60
|
+
result = Go::Bridge.state_get(section: section, cwd: cwd)
|
|
61
|
+
|
|
62
|
+
if result['success']
|
|
63
|
+
result['data']
|
|
64
|
+
else
|
|
65
|
+
raise StateError, result['error']
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Helper: verifica se STATE.md existe
|
|
70
|
+
def exists?(cwd: nil)
|
|
71
|
+
cwd ||= Dir.pwd
|
|
72
|
+
state_path = File.join(cwd, '.planning', 'STATE.md')
|
|
73
|
+
File.exist?(state_path)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Helper: obtém valor de um campo específico
|
|
77
|
+
def fetch(field, cwd: nil)
|
|
78
|
+
data = json(cwd: cwd)
|
|
79
|
+
data[field]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Helper: atualiza progresso automaticamente
|
|
83
|
+
def update_progress(plans_of:, plans_total:, cwd: nil)
|
|
84
|
+
progress = calculate_progress(plans_of, plans_total)
|
|
85
|
+
update(field: 'plans_of', value: plans_of.to_s, cwd: cwd)
|
|
86
|
+
update(field: 'plans_total', value: plans_total.to_s, cwd: cwd)
|
|
87
|
+
update(field: 'progress', value: progress, cwd: cwd)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def calculate_progress(of, total)
|
|
93
|
+
return '0%' if total.nil? || total.to_i == 0
|
|
94
|
+
|
|
95
|
+
percentage = (of.to_f / total.to_i * 100).round
|
|
96
|
+
"#{percentage}%"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class StateError < StandardError; end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/gsd/template.rb
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'go/bridge'
|
|
4
|
+
|
|
5
|
+
module Gsd
|
|
6
|
+
# Template - Wrapper Ruby para operações de template filling via Go
|
|
7
|
+
module Template
|
|
8
|
+
class << self
|
|
9
|
+
# Preenche template de SUMMARY.md
|
|
10
|
+
#
|
|
11
|
+
# @param phase [String] Número da phase
|
|
12
|
+
# @param plan [String] Número do plan
|
|
13
|
+
# @param name [String] Nome do summary
|
|
14
|
+
# @param fields [Hash] Campos adicionais
|
|
15
|
+
# @param cwd [String] Diretório de trabalho
|
|
16
|
+
# @return [Hash] Resultado do preenchimento
|
|
17
|
+
def fill_summary(phase:, plan:, name:, fields: {}, cwd: nil)
|
|
18
|
+
cwd ||= Dir.pwd
|
|
19
|
+
args = build_template_args('summary', phase, plan, name, fields)
|
|
20
|
+
result = Go::Bridge.call('template', args, cwd: cwd)
|
|
21
|
+
|
|
22
|
+
if result['success']
|
|
23
|
+
result['data']
|
|
24
|
+
else
|
|
25
|
+
raise TemplateError, result['error']
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Preenche template de PLAN.md
|
|
30
|
+
#
|
|
31
|
+
# @param phase [String] Número da phase
|
|
32
|
+
# @param plan [String] Número do plan
|
|
33
|
+
# @param type [String] Tipo do plan (execute, tdd)
|
|
34
|
+
# @param wave [String] Número da wave
|
|
35
|
+
# @param fields [Hash] Campos adicionais
|
|
36
|
+
# @param cwd [String] Diretório de trabalho
|
|
37
|
+
# @return [Hash] Resultado do preenchimento
|
|
38
|
+
def fill_plan(phase:, plan:, type: 'execute', wave: '1', fields: {}, cwd: nil)
|
|
39
|
+
cwd ||= Dir.pwd
|
|
40
|
+
args = build_template_args('plan', phase, plan, nil, fields)
|
|
41
|
+
args['type'] = type
|
|
42
|
+
args['wave'] = wave
|
|
43
|
+
result = Go::Bridge.call('template', args, cwd: cwd)
|
|
44
|
+
|
|
45
|
+
if result['success']
|
|
46
|
+
result['data']
|
|
47
|
+
else
|
|
48
|
+
raise TemplateError, result['error']
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Preenche template de VERIFICATION.md
|
|
53
|
+
#
|
|
54
|
+
# @param phase [String] Número da phase
|
|
55
|
+
# @param fields [Hash] Campos adicionais
|
|
56
|
+
# @param cwd [String] Diretório de trabalho
|
|
57
|
+
# @return [Hash] Resultado do preenchimento
|
|
58
|
+
def fill_verification(phase:, fields: {}, cwd: nil)
|
|
59
|
+
cwd ||= Dir.pwd
|
|
60
|
+
args = { 'fill' => 'verification', 'phase' => phase }
|
|
61
|
+
fields.each { |k, v| args[k.to_s] = v.to_s }
|
|
62
|
+
result = Go::Bridge.call('template', args, cwd: cwd)
|
|
63
|
+
|
|
64
|
+
if result['success']
|
|
65
|
+
result['data']
|
|
66
|
+
else
|
|
67
|
+
raise TemplateError, result['error']
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Preenche template de CONTEXT.md
|
|
72
|
+
#
|
|
73
|
+
# @param phase [String] Número da phase
|
|
74
|
+
# @param fields [Hash] Campos adicionais
|
|
75
|
+
# @param cwd [String] Diretório de trabalho
|
|
76
|
+
# @return [Hash] Resultado do preenchimento
|
|
77
|
+
def fill_context(phase:, fields: {}, cwd: nil)
|
|
78
|
+
cwd ||= Dir.pwd
|
|
79
|
+
args = { 'fill' => 'context', 'phase' => phase }
|
|
80
|
+
fields.each { |k, v| args[k.to_s] = v.to_s }
|
|
81
|
+
result = Go::Bridge.call('template', args, cwd: cwd)
|
|
82
|
+
|
|
83
|
+
if result['success']
|
|
84
|
+
result['data']
|
|
85
|
+
else
|
|
86
|
+
raise TemplateError, result['error']
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def build_template_args(template_type, phase, plan, name, fields)
|
|
93
|
+
args = {
|
|
94
|
+
'fill' => template_type,
|
|
95
|
+
'phase' => phase,
|
|
96
|
+
'plan' => plan
|
|
97
|
+
}
|
|
98
|
+
args['name'] = name if name
|
|
99
|
+
fields.each { |k, v| args[k.to_s] = v.to_s }
|
|
100
|
+
args
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class TemplateError < StandardError; end
|
|
105
|
+
end
|
|
106
|
+
end
|