ace-bundle 0.40.0
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/.ace-defaults/bundle/config.yml +28 -0
- data/.ace-defaults/bundle/presets/base.md +15 -0
- data/.ace-defaults/bundle/presets/code-review.md +61 -0
- data/.ace-defaults/bundle/presets/development.md +16 -0
- data/.ace-defaults/bundle/presets/documentation-review.md +52 -0
- data/.ace-defaults/bundle/presets/mixed-content-example.md +94 -0
- data/.ace-defaults/bundle/presets/project-context.md +79 -0
- data/.ace-defaults/bundle/presets/project.md +35 -0
- data/.ace-defaults/bundle/presets/section-example-simple.md +27 -0
- data/.ace-defaults/bundle/presets/security-review.md +53 -0
- data/.ace-defaults/bundle/presets/simple-project.md +43 -0
- data/.ace-defaults/bundle/presets/team.md +18 -0
- data/.ace-defaults/nav/protocols/wfi-sources/ace-bundle.yml +19 -0
- data/CHANGELOG.md +384 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +22 -0
- data/exe/ace-bundle +14 -0
- data/handbook/skills/as-bundle/SKILL.md +28 -0
- data/handbook/skills/as-onboard/SKILL.md +33 -0
- data/handbook/workflow-instructions/bundle.wf.md +111 -0
- data/handbook/workflow-instructions/onboard.wf.md +20 -0
- data/lib/ace/bundle/atoms/boundary_finder.rb +122 -0
- data/lib/ace/bundle/atoms/bundle_normalizer.rb +128 -0
- data/lib/ace/bundle/atoms/content_checker.rb +46 -0
- data/lib/ace/bundle/atoms/line_counter.rb +37 -0
- data/lib/ace/bundle/atoms/preset_list_formatter.rb +44 -0
- data/lib/ace/bundle/atoms/preset_validator.rb +69 -0
- data/lib/ace/bundle/atoms/section_validator.rb +215 -0
- data/lib/ace/bundle/atoms/typo_detector.rb +76 -0
- data/lib/ace/bundle/cli/commands/load.rb +347 -0
- data/lib/ace/bundle/cli.rb +26 -0
- data/lib/ace/bundle/models/bundle_data.rb +75 -0
- data/lib/ace/bundle/molecules/bundle_chunker.rb +280 -0
- data/lib/ace/bundle/molecules/bundle_file_writer.rb +269 -0
- data/lib/ace/bundle/molecules/bundle_merger.rb +248 -0
- data/lib/ace/bundle/molecules/preset_manager.rb +331 -0
- data/lib/ace/bundle/molecules/section_compressor.rb +249 -0
- data/lib/ace/bundle/molecules/section_formatter.rb +580 -0
- data/lib/ace/bundle/molecules/section_processor.rb +460 -0
- data/lib/ace/bundle/organisms/bundle_loader.rb +1436 -0
- data/lib/ace/bundle/organisms/pr_bundle_loader.rb +147 -0
- data/lib/ace/bundle/version.rb +7 -0
- data/lib/ace/bundle.rb +251 -0
- metadata +190 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Bundle
|
|
5
|
+
module Molecules
|
|
6
|
+
# Formats sections with XML-style tags for different output formats
|
|
7
|
+
class SectionFormatter
|
|
8
|
+
def initialize(format = "markdown-xml")
|
|
9
|
+
@format = format
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Formats bundle data with sections
|
|
13
|
+
# @param bundle_data [BundleData] bundle data with sections
|
|
14
|
+
# @return [String] formatted output
|
|
15
|
+
def format_with_sections(bundle_data)
|
|
16
|
+
if bundle_data.has_sections?
|
|
17
|
+
format_sections_output(bundle_data)
|
|
18
|
+
else
|
|
19
|
+
# Fallback to regular formatting
|
|
20
|
+
format_legacy_output(bundle_data)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Formats only the sections part (for inclusion in larger documents)
|
|
25
|
+
# @param sections [Hash] sections hash
|
|
26
|
+
# @return [String] formatted sections
|
|
27
|
+
def format_sections_only(sections)
|
|
28
|
+
return "" if sections.nil? || sections.empty?
|
|
29
|
+
|
|
30
|
+
sorted_sections = sections.sort_by { |name, data| data[:priority] || data["priority"] || 999 }
|
|
31
|
+
|
|
32
|
+
case @format
|
|
33
|
+
when "markdown-xml"
|
|
34
|
+
format_sections_markdown_xml(sorted_sections)
|
|
35
|
+
when "markdown"
|
|
36
|
+
format_sections_markdown(sorted_sections)
|
|
37
|
+
when "yaml"
|
|
38
|
+
format_sections_yaml(sorted_sections)
|
|
39
|
+
when "json"
|
|
40
|
+
format_sections_json(sorted_sections)
|
|
41
|
+
else
|
|
42
|
+
format_sections_markdown_xml(sorted_sections)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# Formats bundle data with sections based on format
|
|
49
|
+
def format_sections_output(bundle_data)
|
|
50
|
+
case @format
|
|
51
|
+
when "markdown-xml"
|
|
52
|
+
format_sections_markdown_xml_full(bundle_data)
|
|
53
|
+
when "markdown"
|
|
54
|
+
format_sections_markdown_full(bundle_data)
|
|
55
|
+
when "yaml"
|
|
56
|
+
format_sections_yaml_full(bundle_data)
|
|
57
|
+
when "json"
|
|
58
|
+
format_sections_json_full(bundle_data)
|
|
59
|
+
else
|
|
60
|
+
format_sections_markdown_xml_full(bundle_data)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Formats full bundle data with sections in markdown-xml format
|
|
65
|
+
def format_sections_markdown_xml_full(bundle_data)
|
|
66
|
+
output = []
|
|
67
|
+
|
|
68
|
+
# Add any additional content FIRST (before sections)
|
|
69
|
+
if bundle_data.content && !bundle_data.content.empty?
|
|
70
|
+
output << bundle_data.content
|
|
71
|
+
output << "" # Empty line after content
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Add sections with XML tags
|
|
75
|
+
output << format_sections_markdown_xml(bundle_data.sorted_sections)
|
|
76
|
+
|
|
77
|
+
output.join("\n")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Formats sections in markdown-xml format with XML tags
|
|
81
|
+
def format_sections_markdown_xml(sections)
|
|
82
|
+
output = []
|
|
83
|
+
|
|
84
|
+
sections.each do |name, section_data|
|
|
85
|
+
title = section_data[:title] || section_data["title"] || name.to_s.humanize
|
|
86
|
+
output << "# #{title}"
|
|
87
|
+
|
|
88
|
+
# Add description as plain paragraph if present
|
|
89
|
+
description = section_data[:description] || section_data["description"]
|
|
90
|
+
output << description if description && !description.empty?
|
|
91
|
+
|
|
92
|
+
output << "<#{name}>"
|
|
93
|
+
|
|
94
|
+
# Format all content types that are present in the section
|
|
95
|
+
if has_files_content?(section_data)
|
|
96
|
+
output << format_files_section(section_data)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if has_commands_content?(section_data)
|
|
100
|
+
output << format_commands_section(section_data)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if has_diffs_content?(section_data)
|
|
104
|
+
output << format_diffs_section(section_data)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
if has_content_content?(section_data)
|
|
108
|
+
output << format_content_section(section_data)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
output << "</#{name}>"
|
|
112
|
+
output << "" # Empty line between sections
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
output.join("\n")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Formats files section with XML file tags
|
|
119
|
+
def format_files_section(section_data)
|
|
120
|
+
output = []
|
|
121
|
+
|
|
122
|
+
files = section_data[:_processed_files] || section_data["_processed_files"] || []
|
|
123
|
+
files.each do |file_info|
|
|
124
|
+
language = detect_language(file_info[:path])
|
|
125
|
+
output << " <file path=\"#{file_info[:path]}\" language=\"#{language}\">"
|
|
126
|
+
output << format_file_content(file_info[:content])
|
|
127
|
+
output << " </file>"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
output.join("\n")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Formats commands section with output tags
|
|
134
|
+
def format_commands_section(section_data)
|
|
135
|
+
output = []
|
|
136
|
+
|
|
137
|
+
commands = section_data[:_processed_commands] || section_data["_processed_commands"] || []
|
|
138
|
+
commands.each do |command_data|
|
|
139
|
+
output << " <output command=\"#{command_data[:command]}\">"
|
|
140
|
+
output << format_command_output(command_data[:output])
|
|
141
|
+
output << " </output>"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
output.join("\n")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Formats diffs section with output tags
|
|
148
|
+
def format_diffs_section(section_data)
|
|
149
|
+
output = []
|
|
150
|
+
|
|
151
|
+
diffs = section_data[:_processed_diffs] || section_data["_processed_diffs"] || []
|
|
152
|
+
diffs.each do |diff_data|
|
|
153
|
+
command = diff_command_for(diff_data)
|
|
154
|
+
output << " <output command=\"#{command}\">"
|
|
155
|
+
output << format_diff_output(diff_data[:output])
|
|
156
|
+
output << " </output>"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
output.join("\n")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Returns the appropriate command string for a diff based on its source
|
|
163
|
+
def diff_command_for(diff_data)
|
|
164
|
+
source = diff_data[:source] || diff_data["source"]
|
|
165
|
+
range = diff_data[:range] || diff_data["range"]
|
|
166
|
+
|
|
167
|
+
case source
|
|
168
|
+
when :pr, "pr"
|
|
169
|
+
"gh pr diff #{range.to_s.sub(/^pr:/, "")}"
|
|
170
|
+
else
|
|
171
|
+
"git diff #{range}"
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Formats inline content section
|
|
176
|
+
def format_content_section(section_data)
|
|
177
|
+
content = section_data[:_processed_content] || section_data["_processed_content"] || ""
|
|
178
|
+
format_inline_content(content)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Formats full bundle data with sections in markdown format
|
|
182
|
+
def format_sections_markdown_full(bundle_data)
|
|
183
|
+
output = []
|
|
184
|
+
|
|
185
|
+
# Add any additional content FIRST (before sections)
|
|
186
|
+
if bundle_data.content && !bundle_data.content.empty?
|
|
187
|
+
output << bundle_data.content
|
|
188
|
+
output << "" # Empty line after content
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Add sections without XML tags
|
|
192
|
+
output << format_sections_markdown(bundle_data.sorted_sections)
|
|
193
|
+
|
|
194
|
+
output.join("\n")
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Formats sections in markdown format (no XML tags)
|
|
198
|
+
def format_sections_markdown(sections)
|
|
199
|
+
output = []
|
|
200
|
+
|
|
201
|
+
sections.each do |name, section_data|
|
|
202
|
+
title = section_data[:title] || section_data["title"] || name.to_s.humanize
|
|
203
|
+
output << "# #{title}"
|
|
204
|
+
|
|
205
|
+
# Add description as plain paragraph if present
|
|
206
|
+
description = section_data[:description] || section_data["description"]
|
|
207
|
+
output << description if description && !description.empty?
|
|
208
|
+
|
|
209
|
+
# Format all content types that are present in the section
|
|
210
|
+
if has_files_content?(section_data)
|
|
211
|
+
output << format_files_section_markdown(section_data)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
if has_commands_content?(section_data)
|
|
215
|
+
output << format_commands_section_markdown(section_data)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
if has_diffs_content?(section_data)
|
|
219
|
+
output << format_diffs_section_markdown(section_data)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
if has_content_content?(section_data)
|
|
223
|
+
output << format_content_section_markdown(section_data)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
output << "" # Empty line between sections
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
output.join("\n")
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Formats files section in markdown format
|
|
233
|
+
def format_files_section_markdown(section_data)
|
|
234
|
+
output = []
|
|
235
|
+
|
|
236
|
+
files = section_data[:_processed_files] || section_data["_processed_files"] || []
|
|
237
|
+
files.each do |file_info|
|
|
238
|
+
language = detect_language(file_info[:path])
|
|
239
|
+
output << "### #{file_info[:path]}"
|
|
240
|
+
output << "```#{language}"
|
|
241
|
+
output << file_info[:content]
|
|
242
|
+
output << "```"
|
|
243
|
+
output << ""
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
output.join("\n")
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Formats commands section in markdown format
|
|
250
|
+
def format_commands_section_markdown(section_data)
|
|
251
|
+
output = []
|
|
252
|
+
|
|
253
|
+
commands = section_data[:_processed_commands] || section_data["_processed_commands"] || []
|
|
254
|
+
commands.each do |command_data|
|
|
255
|
+
output << "### Command: `#{command_data[:command]}`"
|
|
256
|
+
output << "```"
|
|
257
|
+
output << command_data[:output]
|
|
258
|
+
output << "```"
|
|
259
|
+
output << ""
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
output.join("\n")
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Formats diffs section in markdown format
|
|
266
|
+
def format_diffs_section_markdown(section_data)
|
|
267
|
+
output = []
|
|
268
|
+
|
|
269
|
+
diffs = section_data[:_processed_diffs] || section_data["_processed_diffs"] || []
|
|
270
|
+
diffs.each do |diff_data|
|
|
271
|
+
output << "### Diff: `#{diff_data[:range]}`"
|
|
272
|
+
output << "```diff"
|
|
273
|
+
output << diff_data[:output]
|
|
274
|
+
output << "```"
|
|
275
|
+
output << ""
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
output.join("\n")
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Formats content section in markdown format
|
|
282
|
+
def format_content_section_markdown(section_data)
|
|
283
|
+
section_data[:_processed_content] || section_data["_processed_content"] || ""
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Formats sections in YAML format
|
|
287
|
+
def format_sections_yaml(sections)
|
|
288
|
+
require "yaml"
|
|
289
|
+
|
|
290
|
+
yaml_data = {}
|
|
291
|
+
sections.each do |name, section_data|
|
|
292
|
+
yaml_data[name] = {
|
|
293
|
+
"title" => section_data[:title] || section_data["title"],
|
|
294
|
+
"content_type" => section_data[:content_type] || section_data["content_type"],
|
|
295
|
+
"priority" => section_data[:priority] || section_data["priority"]
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
# Add processed content
|
|
299
|
+
case section_data[:content_type] || section_data["content_type"]
|
|
300
|
+
when "files"
|
|
301
|
+
yaml_data[name]["files"] = format_files_for_yaml(section_data)
|
|
302
|
+
when "commands"
|
|
303
|
+
yaml_data[name]["commands"] = format_commands_for_yaml(section_data)
|
|
304
|
+
when "diffs"
|
|
305
|
+
yaml_data[name]["diffs"] = format_diffs_for_yaml(section_data)
|
|
306
|
+
when "content"
|
|
307
|
+
yaml_data[name]["content"] = section_data[:_processed_content] || section_data["_processed_content"]
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
YAML.dump({"sections" => yaml_data})
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Formats full bundle data in YAML format
|
|
315
|
+
def format_sections_yaml_full(bundle_data)
|
|
316
|
+
require "yaml"
|
|
317
|
+
|
|
318
|
+
yaml_data = {
|
|
319
|
+
"preset_name" => bundle_data.preset_name,
|
|
320
|
+
"sections" => format_sections_for_yaml(bundle_data.sections),
|
|
321
|
+
"metadata" => bundle_data.metadata
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if bundle_data.content && !bundle_data.content.empty?
|
|
325
|
+
yaml_data["content"] = bundle_data.content
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
YAML.dump(yaml_data)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Formats sections in JSON format
|
|
332
|
+
def format_sections_json(sections)
|
|
333
|
+
json_data = {}
|
|
334
|
+
sections.each do |name, section_data|
|
|
335
|
+
json_data[name] = {
|
|
336
|
+
"title" => section_data[:title] || section_data["title"],
|
|
337
|
+
"content_type" => section_data[:content_type] || section_data["content_type"],
|
|
338
|
+
"priority" => section_data[:priority] || section_data["priority"]
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
# Add processed content
|
|
342
|
+
case section_data[:content_type] || section_data["content_type"]
|
|
343
|
+
when "files"
|
|
344
|
+
json_data[name]["files"] = format_files_for_json(section_data)
|
|
345
|
+
when "commands"
|
|
346
|
+
json_data[name]["commands"] = format_commands_for_json(section_data)
|
|
347
|
+
when "diffs"
|
|
348
|
+
json_data[name]["diffs"] = format_diffs_for_json(section_data)
|
|
349
|
+
when "content"
|
|
350
|
+
json_data[name]["content"] = section_data[:_processed_content] || section_data["_processed_content"]
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
JSON.pretty_generate({"sections" => json_data})
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Formats full bundle data in JSON format
|
|
358
|
+
def format_sections_json_full(bundle_data)
|
|
359
|
+
require "json"
|
|
360
|
+
|
|
361
|
+
json_data = {
|
|
362
|
+
"preset_name" => bundle_data.preset_name,
|
|
363
|
+
"sections" => format_sections_for_json(bundle_data.sections),
|
|
364
|
+
"metadata" => bundle_data.metadata
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if bundle_data.content && !bundle_data.content.empty?
|
|
368
|
+
json_data["content"] = bundle_data.content
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
JSON.pretty_generate(json_data)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Fallback formatting for non-section bundle data
|
|
375
|
+
def format_legacy_output(bundle_data)
|
|
376
|
+
# Use ace-core OutputFormatter as fallback
|
|
377
|
+
require "ace/core/molecules/output_formatter"
|
|
378
|
+
formatter = Ace::Core::Molecules::OutputFormatter.new(@format)
|
|
379
|
+
|
|
380
|
+
data = {
|
|
381
|
+
files: bundle_data.files,
|
|
382
|
+
metadata: bundle_data.metadata,
|
|
383
|
+
commands: bundle_data.commands,
|
|
384
|
+
content: bundle_data.content
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
formatter.format(data)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Helper methods for formatting specific content types
|
|
391
|
+
|
|
392
|
+
def format_files_for_yaml(section_data)
|
|
393
|
+
files = section_data[:_processed_files] || section_data["_processed_files"] || []
|
|
394
|
+
files.map { |f| {"path" => f[:path], "content" => f[:content]} }
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def format_commands_for_yaml(section_data)
|
|
398
|
+
commands = section_data[:_processed_commands] || section_data["_processed_commands"] || []
|
|
399
|
+
commands.map { |c| {"command" => c[:command], "output" => c[:output]} }
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def format_diffs_for_yaml(section_data)
|
|
403
|
+
diffs = section_data[:_processed_diffs] || section_data["_processed_diffs"] || []
|
|
404
|
+
diffs.map { |d| {"range" => d[:range], "output" => d[:output]} }
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def format_files_for_json(section_data)
|
|
408
|
+
format_files_for_yaml(section_data)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def format_commands_for_json(section_data)
|
|
412
|
+
format_commands_for_yaml(section_data)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def format_diffs_for_json(section_data)
|
|
416
|
+
format_diffs_for_yaml(section_data)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def format_sections_for_yaml(sections)
|
|
420
|
+
return {} if sections.nil? || sections.empty?
|
|
421
|
+
|
|
422
|
+
yaml_sections = {}
|
|
423
|
+
sections.each do |name, section_data|
|
|
424
|
+
yaml_sections[name] = {
|
|
425
|
+
"title" => section_data[:title] || section_data["title"],
|
|
426
|
+
"content_type" => section_data[:content_type] || section_data["content_type"],
|
|
427
|
+
"priority" => section_data[:priority] || section_data["priority"]
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
# Add processed content
|
|
431
|
+
case section_data[:content_type] || section_data["content_type"]
|
|
432
|
+
when "files"
|
|
433
|
+
yaml_sections[name]["files"] = format_files_for_yaml(section_data)
|
|
434
|
+
when "commands"
|
|
435
|
+
yaml_sections[name]["commands"] = format_commands_for_yaml(section_data)
|
|
436
|
+
when "diffs"
|
|
437
|
+
yaml_sections[name]["diffs"] = format_diffs_for_yaml(section_data)
|
|
438
|
+
when "content"
|
|
439
|
+
yaml_sections[name]["content"] = section_data[:_processed_content] || section_data["_processed_content"]
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
yaml_sections
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def format_sections_for_json(sections)
|
|
446
|
+
return {} if sections.nil? || sections.empty?
|
|
447
|
+
|
|
448
|
+
json_sections = {}
|
|
449
|
+
sections.each do |name, section_data|
|
|
450
|
+
json_sections[name] = {
|
|
451
|
+
"title" => section_data[:title] || section_data["title"],
|
|
452
|
+
"content_type" => section_data[:content_type] || section_data["content_type"],
|
|
453
|
+
"priority" => section_data[:priority] || section_data["priority"]
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
# Add processed content
|
|
457
|
+
case section_data[:content_type] || section_data["content_type"]
|
|
458
|
+
when "files"
|
|
459
|
+
json_sections[name]["files"] = format_files_for_json(section_data)
|
|
460
|
+
when "commands"
|
|
461
|
+
json_sections[name]["commands"] = format_commands_for_json(section_data)
|
|
462
|
+
when "diffs"
|
|
463
|
+
json_sections[name]["diffs"] = format_diffs_for_json(section_data)
|
|
464
|
+
when "content"
|
|
465
|
+
json_sections[name]["content"] = section_data[:_processed_content] || section_data["_processed_content"]
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
json_sections
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Content formatting helpers
|
|
472
|
+
def format_file_content(content)
|
|
473
|
+
return "" if content.nil? || content.empty?
|
|
474
|
+
|
|
475
|
+
# Indent content for XML formatting
|
|
476
|
+
content.lines.map { |line| " #{line}" }.join.rstrip
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def format_command_output(output)
|
|
480
|
+
return "" if output.nil? || output.empty?
|
|
481
|
+
|
|
482
|
+
# Indent output for XML formatting
|
|
483
|
+
output.lines.map { |line| " #{line}" }.join.rstrip
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def format_diff_output(output)
|
|
487
|
+
return "" if output.nil? || output.empty?
|
|
488
|
+
|
|
489
|
+
# Indent diff output for XML formatting
|
|
490
|
+
output.lines.map { |line| " #{line}" }.join.rstrip
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def format_inline_content(content)
|
|
494
|
+
return "" if content.nil? || content.empty?
|
|
495
|
+
|
|
496
|
+
content
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Language detection for file syntax highlighting
|
|
500
|
+
def detect_language(file_path)
|
|
501
|
+
LANGUAGE_MAP[File.extname(file_path).downcase] || "text"
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
# Language mapping for file extensions
|
|
505
|
+
LANGUAGE_MAP = {
|
|
506
|
+
".rb" => "ruby",
|
|
507
|
+
".py" => "python",
|
|
508
|
+
".js" => "javascript",
|
|
509
|
+
".ts" => "typescript",
|
|
510
|
+
".jsx" => "jsx",
|
|
511
|
+
".tsx" => "tsx",
|
|
512
|
+
".java" => "java",
|
|
513
|
+
".c" => "c",
|
|
514
|
+
".cpp" => "cpp",
|
|
515
|
+
".cc" => "cpp",
|
|
516
|
+
".cxx" => "cpp",
|
|
517
|
+
".h" => "c",
|
|
518
|
+
".hpp" => "cpp",
|
|
519
|
+
".cs" => "csharp",
|
|
520
|
+
".php" => "php",
|
|
521
|
+
".swift" => "swift",
|
|
522
|
+
".kt" => "kotlin",
|
|
523
|
+
".go" => "go",
|
|
524
|
+
".rs" => "rust",
|
|
525
|
+
".sh" => "bash",
|
|
526
|
+
".bash" => "bash",
|
|
527
|
+
".zsh" => "bash",
|
|
528
|
+
".sql" => "sql",
|
|
529
|
+
".html" => "html",
|
|
530
|
+
".htm" => "html",
|
|
531
|
+
".css" => "css",
|
|
532
|
+
".scss" => "scss",
|
|
533
|
+
".sass" => "scss",
|
|
534
|
+
".less" => "less",
|
|
535
|
+
".xml" => "xml",
|
|
536
|
+
".json" => "json",
|
|
537
|
+
".yaml" => "yaml",
|
|
538
|
+
".yml" => "yaml",
|
|
539
|
+
".toml" => "toml",
|
|
540
|
+
".md" => "markdown",
|
|
541
|
+
".markdown" => "markdown",
|
|
542
|
+
".txt" => "text",
|
|
543
|
+
".dockerfile" => "dockerfile",
|
|
544
|
+
".gitignore" => "git",
|
|
545
|
+
".gitattributes" => "git",
|
|
546
|
+
".env" => "env",
|
|
547
|
+
".ini" => "ini",
|
|
548
|
+
".conf" => "apache"
|
|
549
|
+
}.freeze
|
|
550
|
+
|
|
551
|
+
# Helper methods to detect content types in sections
|
|
552
|
+
|
|
553
|
+
# Checks if section has files content
|
|
554
|
+
def has_files_content?(section_data)
|
|
555
|
+
!!(section_data[:files] || section_data["files"] ||
|
|
556
|
+
section_data[:_processed_files] || section_data["_processed_files"])
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# Checks if section has commands content
|
|
560
|
+
def has_commands_content?(section_data)
|
|
561
|
+
!!(section_data[:commands] || section_data["commands"] ||
|
|
562
|
+
section_data[:_processed_commands] || section_data["_processed_commands"])
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Checks if section has diffs content
|
|
566
|
+
def has_diffs_content?(section_data)
|
|
567
|
+
!!(section_data[:ranges] || section_data["ranges"] ||
|
|
568
|
+
section_data[:diffs] || section_data["diffs"] ||
|
|
569
|
+
section_data[:_processed_diffs] || section_data["_processed_diffs"])
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
# Checks if section has inline content
|
|
573
|
+
def has_content_content?(section_data)
|
|
574
|
+
!!(section_data[:content] || section_data["content"] ||
|
|
575
|
+
section_data[:_processed_content] || section_data["_processed_content"])
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
end
|