swarm_memory 2.1.4 → 2.1.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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/lib/swarm_memory/version.rb +1 -1
  3. data/lib/swarm_memory.rb +7 -2
  4. metadata +6 -185
  5. data/lib/claude_swarm/base_executor.rb +0 -133
  6. data/lib/claude_swarm/claude_code_executor.rb +0 -349
  7. data/lib/claude_swarm/claude_mcp_server.rb +0 -78
  8. data/lib/claude_swarm/cli.rb +0 -697
  9. data/lib/claude_swarm/commands/ps.rb +0 -215
  10. data/lib/claude_swarm/commands/show.rb +0 -139
  11. data/lib/claude_swarm/configuration.rb +0 -373
  12. data/lib/claude_swarm/hooks/session_start_hook.rb +0 -42
  13. data/lib/claude_swarm/json_handler.rb +0 -91
  14. data/lib/claude_swarm/mcp_generator.rb +0 -243
  15. data/lib/claude_swarm/openai/chat_completion.rb +0 -256
  16. data/lib/claude_swarm/openai/executor.rb +0 -256
  17. data/lib/claude_swarm/openai/responses.rb +0 -319
  18. data/lib/claude_swarm/orchestrator.rb +0 -878
  19. data/lib/claude_swarm/process_tracker.rb +0 -78
  20. data/lib/claude_swarm/session_cost_calculator.rb +0 -209
  21. data/lib/claude_swarm/session_path.rb +0 -42
  22. data/lib/claude_swarm/settings_generator.rb +0 -77
  23. data/lib/claude_swarm/system_utils.rb +0 -46
  24. data/lib/claude_swarm/templates/generation_prompt.md.erb +0 -230
  25. data/lib/claude_swarm/tools/reset_session_tool.rb +0 -24
  26. data/lib/claude_swarm/tools/session_info_tool.rb +0 -24
  27. data/lib/claude_swarm/tools/task_tool.rb +0 -63
  28. data/lib/claude_swarm/version.rb +0 -5
  29. data/lib/claude_swarm/worktree_manager.rb +0 -475
  30. data/lib/claude_swarm/yaml_loader.rb +0 -22
  31. data/lib/claude_swarm.rb +0 -67
  32. data/lib/swarm_cli/cli.rb +0 -201
  33. data/lib/swarm_cli/command_registry.rb +0 -61
  34. data/lib/swarm_cli/commands/mcp_serve.rb +0 -130
  35. data/lib/swarm_cli/commands/mcp_tools.rb +0 -148
  36. data/lib/swarm_cli/commands/migrate.rb +0 -55
  37. data/lib/swarm_cli/commands/run.rb +0 -173
  38. data/lib/swarm_cli/config_loader.rb +0 -98
  39. data/lib/swarm_cli/formatters/human_formatter.rb +0 -781
  40. data/lib/swarm_cli/formatters/json_formatter.rb +0 -51
  41. data/lib/swarm_cli/interactive_repl.rb +0 -924
  42. data/lib/swarm_cli/mcp_serve_options.rb +0 -44
  43. data/lib/swarm_cli/mcp_tools_options.rb +0 -59
  44. data/lib/swarm_cli/migrate_options.rb +0 -54
  45. data/lib/swarm_cli/migrator.rb +0 -132
  46. data/lib/swarm_cli/options.rb +0 -151
  47. data/lib/swarm_cli/ui/components/agent_badge.rb +0 -33
  48. data/lib/swarm_cli/ui/components/content_block.rb +0 -120
  49. data/lib/swarm_cli/ui/components/divider.rb +0 -57
  50. data/lib/swarm_cli/ui/components/panel.rb +0 -62
  51. data/lib/swarm_cli/ui/components/usage_stats.rb +0 -70
  52. data/lib/swarm_cli/ui/formatters/cost.rb +0 -49
  53. data/lib/swarm_cli/ui/formatters/number.rb +0 -58
  54. data/lib/swarm_cli/ui/formatters/text.rb +0 -77
  55. data/lib/swarm_cli/ui/formatters/time.rb +0 -73
  56. data/lib/swarm_cli/ui/icons.rb +0 -36
  57. data/lib/swarm_cli/ui/renderers/event_renderer.rb +0 -188
  58. data/lib/swarm_cli/ui/state/agent_color_cache.rb +0 -45
  59. data/lib/swarm_cli/ui/state/depth_tracker.rb +0 -40
  60. data/lib/swarm_cli/ui/state/spinner_manager.rb +0 -170
  61. data/lib/swarm_cli/ui/state/usage_tracker.rb +0 -62
  62. data/lib/swarm_cli/version.rb +0 -5
  63. data/lib/swarm_cli.rb +0 -46
  64. data/lib/swarm_sdk/agent/RETRY_LOGIC.md +0 -127
  65. data/lib/swarm_sdk/agent/builder.rb +0 -552
  66. data/lib/swarm_sdk/agent/chat.rb +0 -774
  67. data/lib/swarm_sdk/agent/chat_helpers/context_tracker.rb +0 -268
  68. data/lib/swarm_sdk/agent/chat_helpers/event_emitter.rb +0 -204
  69. data/lib/swarm_sdk/agent/chat_helpers/hook_integration.rb +0 -480
  70. data/lib/swarm_sdk/agent/chat_helpers/instrumentation.rb +0 -78
  71. data/lib/swarm_sdk/agent/chat_helpers/llm_configuration.rb +0 -233
  72. data/lib/swarm_sdk/agent/chat_helpers/logging_helpers.rb +0 -116
  73. data/lib/swarm_sdk/agent/chat_helpers/serialization.rb +0 -83
  74. data/lib/swarm_sdk/agent/chat_helpers/system_reminder_injector.rb +0 -136
  75. data/lib/swarm_sdk/agent/chat_helpers/system_reminders.rb +0 -79
  76. data/lib/swarm_sdk/agent/chat_helpers/token_tracking.rb +0 -98
  77. data/lib/swarm_sdk/agent/context.rb +0 -116
  78. data/lib/swarm_sdk/agent/context_manager.rb +0 -315
  79. data/lib/swarm_sdk/agent/definition.rb +0 -477
  80. data/lib/swarm_sdk/agent/llm_instrumentation_middleware.rb +0 -182
  81. data/lib/swarm_sdk/agent/system_prompt_builder.rb +0 -161
  82. data/lib/swarm_sdk/builders/base_builder.rb +0 -409
  83. data/lib/swarm_sdk/claude_code_agent_adapter.rb +0 -205
  84. data/lib/swarm_sdk/concerns/cleanupable.rb +0 -39
  85. data/lib/swarm_sdk/concerns/snapshotable.rb +0 -67
  86. data/lib/swarm_sdk/concerns/validatable.rb +0 -55
  87. data/lib/swarm_sdk/configuration/parser.rb +0 -353
  88. data/lib/swarm_sdk/configuration/translator.rb +0 -255
  89. data/lib/swarm_sdk/configuration.rb +0 -135
  90. data/lib/swarm_sdk/context_compactor/metrics.rb +0 -147
  91. data/lib/swarm_sdk/context_compactor/token_counter.rb +0 -106
  92. data/lib/swarm_sdk/context_compactor.rb +0 -335
  93. data/lib/swarm_sdk/context_management/builder.rb +0 -128
  94. data/lib/swarm_sdk/context_management/context.rb +0 -328
  95. data/lib/swarm_sdk/defaults.rb +0 -196
  96. data/lib/swarm_sdk/events_to_messages.rb +0 -199
  97. data/lib/swarm_sdk/hooks/adapter.rb +0 -359
  98. data/lib/swarm_sdk/hooks/context.rb +0 -197
  99. data/lib/swarm_sdk/hooks/definition.rb +0 -80
  100. data/lib/swarm_sdk/hooks/error.rb +0 -29
  101. data/lib/swarm_sdk/hooks/executor.rb +0 -146
  102. data/lib/swarm_sdk/hooks/registry.rb +0 -147
  103. data/lib/swarm_sdk/hooks/result.rb +0 -150
  104. data/lib/swarm_sdk/hooks/shell_executor.rb +0 -255
  105. data/lib/swarm_sdk/hooks/tool_call.rb +0 -35
  106. data/lib/swarm_sdk/hooks/tool_result.rb +0 -62
  107. data/lib/swarm_sdk/log_collector.rb +0 -227
  108. data/lib/swarm_sdk/log_stream.rb +0 -127
  109. data/lib/swarm_sdk/markdown_parser.rb +0 -75
  110. data/lib/swarm_sdk/model_aliases.json +0 -8
  111. data/lib/swarm_sdk/models.json +0 -1
  112. data/lib/swarm_sdk/models.rb +0 -120
  113. data/lib/swarm_sdk/node_context.rb +0 -245
  114. data/lib/swarm_sdk/observer/builder.rb +0 -81
  115. data/lib/swarm_sdk/observer/config.rb +0 -45
  116. data/lib/swarm_sdk/observer/manager.rb +0 -236
  117. data/lib/swarm_sdk/patterns/agent_observer.rb +0 -160
  118. data/lib/swarm_sdk/permissions/config.rb +0 -239
  119. data/lib/swarm_sdk/permissions/error_formatter.rb +0 -121
  120. data/lib/swarm_sdk/permissions/path_matcher.rb +0 -35
  121. data/lib/swarm_sdk/permissions/validator.rb +0 -173
  122. data/lib/swarm_sdk/permissions_builder.rb +0 -122
  123. data/lib/swarm_sdk/plugin.rb +0 -309
  124. data/lib/swarm_sdk/plugin_registry.rb +0 -101
  125. data/lib/swarm_sdk/proc_helpers.rb +0 -53
  126. data/lib/swarm_sdk/prompts/base_system_prompt.md.erb +0 -117
  127. data/lib/swarm_sdk/restore_result.rb +0 -65
  128. data/lib/swarm_sdk/result.rb +0 -123
  129. data/lib/swarm_sdk/snapshot.rb +0 -156
  130. data/lib/swarm_sdk/snapshot_from_events.rb +0 -397
  131. data/lib/swarm_sdk/state_restorer.rb +0 -476
  132. data/lib/swarm_sdk/state_snapshot.rb +0 -334
  133. data/lib/swarm_sdk/swarm/agent_initializer.rb +0 -683
  134. data/lib/swarm_sdk/swarm/all_agents_builder.rb +0 -167
  135. data/lib/swarm_sdk/swarm/builder.rb +0 -249
  136. data/lib/swarm_sdk/swarm/executor.rb +0 -213
  137. data/lib/swarm_sdk/swarm/hook_triggers.rb +0 -150
  138. data/lib/swarm_sdk/swarm/logging_callbacks.rb +0 -340
  139. data/lib/swarm_sdk/swarm/mcp_configurator.rb +0 -154
  140. data/lib/swarm_sdk/swarm/swarm_registry_builder.rb +0 -67
  141. data/lib/swarm_sdk/swarm/tool_configurator.rb +0 -358
  142. data/lib/swarm_sdk/swarm.rb +0 -717
  143. data/lib/swarm_sdk/swarm_loader.rb +0 -145
  144. data/lib/swarm_sdk/swarm_registry.rb +0 -136
  145. data/lib/swarm_sdk/tools/bash.rb +0 -282
  146. data/lib/swarm_sdk/tools/clock.rb +0 -44
  147. data/lib/swarm_sdk/tools/delegate.rb +0 -267
  148. data/lib/swarm_sdk/tools/document_converters/base_converter.rb +0 -83
  149. data/lib/swarm_sdk/tools/document_converters/docx_converter.rb +0 -99
  150. data/lib/swarm_sdk/tools/document_converters/html_converter.rb +0 -101
  151. data/lib/swarm_sdk/tools/document_converters/pdf_converter.rb +0 -78
  152. data/lib/swarm_sdk/tools/document_converters/xlsx_converter.rb +0 -194
  153. data/lib/swarm_sdk/tools/edit.rb +0 -145
  154. data/lib/swarm_sdk/tools/glob.rb +0 -166
  155. data/lib/swarm_sdk/tools/grep.rb +0 -235
  156. data/lib/swarm_sdk/tools/image_extractors/docx_image_extractor.rb +0 -43
  157. data/lib/swarm_sdk/tools/image_extractors/pdf_image_extractor.rb +0 -163
  158. data/lib/swarm_sdk/tools/image_formats/tiff_builder.rb +0 -65
  159. data/lib/swarm_sdk/tools/multi_edit.rb +0 -236
  160. data/lib/swarm_sdk/tools/path_resolver.rb +0 -92
  161. data/lib/swarm_sdk/tools/read.rb +0 -261
  162. data/lib/swarm_sdk/tools/registry.rb +0 -205
  163. data/lib/swarm_sdk/tools/scratchpad/scratchpad_list.rb +0 -117
  164. data/lib/swarm_sdk/tools/scratchpad/scratchpad_read.rb +0 -97
  165. data/lib/swarm_sdk/tools/scratchpad/scratchpad_write.rb +0 -108
  166. data/lib/swarm_sdk/tools/stores/read_tracker.rb +0 -96
  167. data/lib/swarm_sdk/tools/stores/scratchpad_storage.rb +0 -272
  168. data/lib/swarm_sdk/tools/stores/storage.rb +0 -142
  169. data/lib/swarm_sdk/tools/stores/todo_manager.rb +0 -65
  170. data/lib/swarm_sdk/tools/think.rb +0 -98
  171. data/lib/swarm_sdk/tools/todo_write.rb +0 -235
  172. data/lib/swarm_sdk/tools/web_fetch.rb +0 -262
  173. data/lib/swarm_sdk/tools/write.rb +0 -112
  174. data/lib/swarm_sdk/utils.rb +0 -68
  175. data/lib/swarm_sdk/validation_result.rb +0 -33
  176. data/lib/swarm_sdk/version.rb +0 -5
  177. data/lib/swarm_sdk/workflow/agent_config.rb +0 -79
  178. data/lib/swarm_sdk/workflow/builder.rb +0 -143
  179. data/lib/swarm_sdk/workflow/executor.rb +0 -497
  180. data/lib/swarm_sdk/workflow/node_builder.rb +0 -555
  181. data/lib/swarm_sdk/workflow/transformer_executor.rb +0 -249
  182. data/lib/swarm_sdk/workflow.rb +0 -554
  183. data/lib/swarm_sdk.rb +0 -524
  184. /data/lib/swarm_memory/{errors.rb → error.rb} +0 -0
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module Components
6
- # Renders highlighted panels for warnings, info, errors
7
- # Uses top/bottom borders only (no sides per design constraint)
8
- class Panel
9
- TYPE_CONFIGS = {
10
- warning: { color: :yellow, icon: UI::Icons::WARNING },
11
- error: { color: :red, icon: UI::Icons::ERROR },
12
- info: { color: :cyan, icon: UI::Icons::INFO },
13
- success: { color: :green, icon: UI::Icons::SUCCESS },
14
- }.freeze
15
-
16
- def initialize(pastel:, terminal_width: 80)
17
- @pastel = pastel
18
- @terminal_width = terminal_width
19
- end
20
-
21
- # Render panel with top/bottom borders
22
- #
23
- # ⚠️ CONTEXT WARNING
24
- # Context usage: 81.4% (threshold: 80%)
25
- # Tokens remaining: 74,523
26
- #
27
- def render(type:, title:, lines:, indent: 0)
28
- config = TYPE_CONFIGS[type] || TYPE_CONFIGS[:info]
29
- prefix = " " * indent
30
-
31
- output = []
32
-
33
- # Title line with icon
34
- icon = config[:icon]
35
- colored_title = @pastel.public_send(config[:color], "#{icon} #{title}")
36
- output << "#{prefix}#{colored_title}"
37
-
38
- # Content lines
39
- lines.each do |line|
40
- output << "#{prefix} #{line}"
41
- end
42
-
43
- output << "" # Blank line after panel
44
-
45
- output.join("\n")
46
- end
47
-
48
- # Render compact panel (single line)
49
- # ⚠️ Context approaching limit (81.4%)
50
- def render_compact(type:, message:, indent: 0)
51
- config = TYPE_CONFIGS[type] || TYPE_CONFIGS[:info]
52
- prefix = " " * indent
53
-
54
- icon = config[:icon]
55
- colored_msg = @pastel.public_send(config[:color], "#{icon} #{message}")
56
-
57
- "#{prefix}#{colored_msg}"
58
- end
59
- end
60
- end
61
- end
62
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module Components
6
- # Renders usage statistics (tokens, cost, context percentage)
7
- class UsageStats
8
- def initialize(pastel:)
9
- @pastel = pastel
10
- end
11
-
12
- # Render usage line with all available metrics
13
- # 5,922 tokens │ $0.0016 │ 1.5% used, 394,078 remaining
14
- def render(tokens:, cost:, context_pct: nil, remaining: nil, cumulative: nil)
15
- parts = []
16
-
17
- # Token count (always shown)
18
- parts << "#{Formatters::Number.format(tokens)} tokens"
19
-
20
- # Cost (always shown if > 0)
21
- parts << Formatters::Cost.format(cost, pastel: @pastel) if cost > 0
22
-
23
- # Context tracking (if available)
24
- if context_pct
25
- colored_pct = color_context_percentage(context_pct)
26
-
27
- parts << if remaining
28
- "#{colored_pct} used, #{Formatters::Number.compact(remaining)} remaining"
29
- else
30
- "#{colored_pct} used"
31
- end
32
- elsif cumulative
33
- # Model doesn't have context limit, show cumulative
34
- parts << "#{Formatters::Number.compact(cumulative)} cumulative"
35
- end
36
-
37
- @pastel.dim(parts.join(" #{@pastel.dim("│")} "))
38
- end
39
-
40
- # Render compact stats for prompt display
41
- # 15.2K tokens • $0.045 • 3.8% context
42
- def render_compact(tokens:, cost:, context_pct: nil)
43
- parts = []
44
-
45
- parts << "#{Formatters::Number.compact(tokens)} tokens" if tokens > 0
46
- parts << Formatters::Cost.format_plain(cost) if cost > 0
47
- parts << "#{context_pct} context" if context_pct
48
-
49
- parts.join(" • ")
50
- end
51
-
52
- private
53
-
54
- def color_context_percentage(percentage_string)
55
- percentage = percentage_string.to_s.gsub("%", "").to_f
56
-
57
- color = if percentage < 50
58
- :green
59
- elsif percentage < 80
60
- :yellow
61
- else
62
- :red
63
- end
64
-
65
- @pastel.public_send(color, percentage_string)
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module Formatters
6
- # Cost formatting with color coding
7
- class Cost
8
- class << self
9
- # Format cost with appropriate precision and color
10
- # Small costs: green, $0.001234
11
- # Medium costs: yellow, $0.1234
12
- # Large costs: red, $12.34
13
- def format(cost, pastel:)
14
- return pastel.dim("$0.0000") if cost.nil? || cost.zero?
15
-
16
- formatted = if cost < 0.01
17
- Kernel.format("%.6f", cost)
18
- elsif cost < 1.0
19
- Kernel.format("%.4f", cost)
20
- else
21
- Kernel.format("%.2f", cost)
22
- end
23
-
24
- if cost < 0.01
25
- pastel.green("$#{formatted}")
26
- elsif cost < 1.0
27
- pastel.yellow("$#{formatted}")
28
- else
29
- pastel.red("$#{formatted}")
30
- end
31
- end
32
-
33
- # Format cost without color (for plain text)
34
- def format_plain(cost)
35
- return "$0.0000" if cost.nil? || cost.zero?
36
-
37
- if cost < 0.01
38
- Kernel.format("$%.6f", cost)
39
- elsif cost < 1.0
40
- Kernel.format("$%.4f", cost)
41
- else
42
- Kernel.format("$%.2f", cost)
43
- end
44
- end
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module Formatters
6
- # Number formatting utilities for terminal display
7
- class Number
8
- class << self
9
- # Format number with thousand separators
10
- # 5922 → "5,922"
11
- # 1500000 → "1,500,000"
12
- def format(num)
13
- return "0" if num.nil? || num.zero?
14
-
15
- num.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
16
- end
17
-
18
- # Format number with compact units (K, M, B)
19
- # 5922 → "5.9K"
20
- # 1500000 → "1.5M"
21
- # 1500000000 → "1.5B"
22
- def compact(num)
23
- return "0" if num.nil? || num.zero?
24
-
25
- case num
26
- when 0...1_000
27
- num.to_s
28
- when 1_000...1_000_000
29
- "#{(num / 1_000.0).round(1)}K"
30
- when 1_000_000...1_000_000_000
31
- "#{(num / 1_000_000.0).round(1)}M"
32
- else
33
- "#{(num / 1_000_000_000.0).round(1)}B"
34
- end
35
- end
36
-
37
- # Format bytes with units (KB, MB, GB)
38
- # 1024 → "1.0 KB"
39
- # 1500000 → "1.4 MB"
40
- def bytes(num)
41
- return "0 B" if num.nil? || num.zero?
42
-
43
- case num
44
- when 0...1024
45
- "#{num} B"
46
- when 1024...1024**2
47
- "#{(num / 1024.0).round(1)} KB"
48
- when 1024**2...1024**3
49
- "#{(num / 1024.0**2).round(1)} MB"
50
- else
51
- "#{(num / 1024.0**3).round(1)} GB"
52
- end
53
- end
54
- end
55
- end
56
- end
57
- end
58
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module Formatters
6
- # Text manipulation utilities for clean display
7
- class Text
8
- class << self
9
- # Strip <system-reminder> tags from content
10
- def strip_system_reminders(text)
11
- return "" if text.nil?
12
-
13
- text.gsub(%r{<system-reminder>.*?</system-reminder>}m, "").strip
14
- end
15
-
16
- # Truncate text to specified character/line limits
17
- # Returns [display_text, truncation_message]
18
- def truncate(text, chars: nil, lines: nil)
19
- return [text, nil] if text.nil? || text.empty?
20
-
21
- text_lines = text.split("\n")
22
- truncated = false
23
- truncation_parts = []
24
-
25
- # Apply line limit
26
- if lines && text_lines.length > lines
27
- text_lines = text_lines.first(lines)
28
- hidden_lines = text.split("\n").length - lines
29
- truncation_parts << "#{hidden_lines} more lines"
30
- truncated = true
31
- end
32
-
33
- result_text = text_lines.join("\n")
34
-
35
- # Apply character limit
36
- if chars && result_text.length > chars
37
- result_text = result_text[0...chars]
38
- hidden_chars = text.length - chars
39
- truncation_parts << "#{hidden_chars} more chars"
40
- truncated = true
41
- end
42
-
43
- truncation_msg = truncated ? "... (#{truncation_parts.join(", ")})" : nil
44
-
45
- [result_text, truncation_msg]
46
- end
47
-
48
- # Wrap text to specified width
49
- def wrap(text, width:)
50
- return "" if text.nil? || text.empty?
51
-
52
- text.split("\n").flat_map do |line|
53
- wrap_line(line, width)
54
- end.join("\n")
55
- end
56
-
57
- # Indent all lines in text
58
- def indent(text, level: 0, char: " ")
59
- return "" if text.nil? || text.empty?
60
-
61
- prefix = char * level
62
- text.split("\n").map { |line| "#{prefix}#{line}" }.join("\n")
63
- end
64
-
65
- private
66
-
67
- # Word wrap a single line
68
- def wrap_line(line, width)
69
- return [line] if line.length <= width
70
-
71
- line.scan(/.{1,#{width}}(?:\s+|$)/).map(&:strip)
72
- end
73
- end
74
- end
75
- end
76
- end
77
- end
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module Formatters
6
- # Time and duration formatting utilities
7
- class Time
8
- class << self
9
- # Format timestamp as [HH:MM:SS]
10
- # Time.now → "[12:34:56]"
11
- def timestamp(time)
12
- return "" if time.nil?
13
-
14
- case time
15
- when ::Time
16
- time.strftime("[%H:%M:%S]")
17
- when String
18
- parsed = ::Time.parse(time)
19
- parsed.strftime("[%H:%M:%S]")
20
- else
21
- ""
22
- end
23
- rescue StandardError
24
- ""
25
- end
26
-
27
- # Format duration in human-readable form
28
- # 0.5 → "500ms"
29
- # 2.3 → "2.3s"
30
- # 65 → "1m 5s"
31
- # 3665 → "1h 1m 5s"
32
- def duration(seconds)
33
- return "0ms" if seconds.nil? || seconds.zero?
34
-
35
- if seconds < 1
36
- "#{(seconds * 1000).round}ms"
37
- elsif seconds < 60
38
- "#{seconds.round(2)}s"
39
- elsif seconds < 3600
40
- minutes = (seconds / 60).floor
41
- secs = (seconds % 60).round
42
- "#{minutes}m #{secs}s"
43
- else
44
- hours = (seconds / 3600).floor
45
- minutes = ((seconds % 3600) / 60).floor
46
- secs = (seconds % 60).round
47
- "#{hours}h #{minutes}m #{secs}s"
48
- end
49
- end
50
-
51
- # Format relative time (future enhancement)
52
- # Time.now - 120 → "2 minutes ago"
53
- def relative(time)
54
- return "" if time.nil?
55
-
56
- seconds_ago = ::Time.now - time
57
-
58
- case seconds_ago
59
- when 0...60
60
- "#{seconds_ago.round}s ago"
61
- when 60...3600
62
- "#{(seconds_ago / 60).round}m ago"
63
- when 3600...86400
64
- "#{(seconds_ago / 3600).round}h ago"
65
- else
66
- "#{(seconds_ago / 86400).round}d ago"
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- # Icon definitions for terminal UI
6
- # Centralized so all components use the same icons
7
- module Icons
8
- # Event type icons
9
- THINKING = "💭"
10
- RESPONSE = "💬"
11
- SUCCESS = "✓"
12
- ERROR = "✗"
13
- INFO = "ℹ"
14
- WARNING = "⚠️"
15
-
16
- # Entity icons
17
- AGENT = "🤖"
18
- TOOL = "🔧"
19
- DELEGATE = "📨"
20
- RESULT = "📥"
21
- HOOK = "🪝"
22
-
23
- # Metric icons
24
- LLM = "🧠"
25
- TOKENS = "📊"
26
- COST = "💰"
27
- TIME = "⏱"
28
-
29
- # Visual elements
30
- SPARKLES = "✨"
31
- ARROW_RIGHT = "→"
32
- BULLET = "•"
33
- COMPRESS = "🗜️"
34
- end
35
- end
36
- end
@@ -1,188 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module Renderers
6
- # High-level event rendering by composing lower-level components
7
- # Returns formatted strings for each event type
8
- class EventRenderer
9
- def initialize(pastel:, agent_badge:, depth_tracker:)
10
- @pastel = pastel
11
- @agent_badge = agent_badge
12
- @depth_tracker = depth_tracker
13
- @usage_stats = Components::UsageStats.new(pastel: pastel)
14
- @content_block = Components::ContentBlock.new(pastel: pastel)
15
- @panel = Components::Panel.new(pastel: pastel)
16
- end
17
-
18
- # Render agent thinking event
19
- # [12:34:56] 💭 architect (gpt-5-mini)
20
- def agent_thinking(agent:, model:, timestamp:)
21
- indent = @depth_tracker.indent(agent)
22
- time = Formatters::Time.timestamp(timestamp)
23
- agent_name = @agent_badge.render(agent, icon: UI::Icons::THINKING)
24
- model_info = @pastel.dim("(#{model})")
25
-
26
- "#{indent}#{@pastel.dim(time)} #{agent_name} #{model_info}"
27
- end
28
-
29
- # Render agent response event
30
- # [12:34:56] 💬 architect responded:
31
- def agent_response(agent:, timestamp:)
32
- indent = @depth_tracker.indent(agent)
33
- time = Formatters::Time.timestamp(timestamp)
34
- agent_name = @agent_badge.render(agent, icon: UI::Icons::RESPONSE)
35
-
36
- "#{indent}#{@pastel.dim(time)} #{agent_name} responded:"
37
- end
38
-
39
- # Render agent completion
40
- # ✓ architect completed
41
- def agent_completed(agent:)
42
- indent = @depth_tracker.indent(agent)
43
- agent_name = @agent_badge.render(agent)
44
-
45
- "#{indent}#{@pastel.green("#{UI::Icons::SUCCESS} #{agent_name} completed")}"
46
- end
47
-
48
- # Render tool call event
49
- # [12:34:56] architect 🔧 uses tool Read
50
- def tool_call(agent:, tool:, timestamp:)
51
- indent = @depth_tracker.indent(agent)
52
- time = Formatters::Time.timestamp(timestamp)
53
- agent_name = @agent_badge.render(agent)
54
- tool_name = @pastel.bold.blue(tool)
55
-
56
- "#{indent}#{@pastel.dim(time)} #{agent_name} #{@pastel.blue("#{UI::Icons::TOOL} uses tool")} #{tool_name}"
57
- end
58
-
59
- # Render tool result received
60
- # [12:34:56] 📥 Tool result received by architect
61
- def tool_result(agent:, timestamp:, tool: nil)
62
- indent = @depth_tracker.indent(agent)
63
- time = Formatters::Time.timestamp(timestamp)
64
-
65
- "#{indent}#{@pastel.dim(time)} #{@pastel.green("#{UI::Icons::RESULT} Tool result")} received by #{agent}"
66
- end
67
-
68
- # Render delegation event
69
- # [12:34:56] architect 📨 delegates to worker
70
- def delegation(from:, to:, timestamp:)
71
- indent = @depth_tracker.indent(from)
72
- time = Formatters::Time.timestamp(timestamp)
73
- from_name = @agent_badge.render(from)
74
- to_name = @agent_badge.render(to)
75
-
76
- "#{indent}#{@pastel.dim(time)} #{from_name} #{@pastel.yellow("#{UI::Icons::DELEGATE} delegates to")} #{to_name}"
77
- end
78
-
79
- # Render delegation result
80
- # [12:34:56] 📥 Delegation result from worker → architect
81
- def delegation_result(from:, to:, timestamp:)
82
- indent = @depth_tracker.indent(to)
83
- time = Formatters::Time.timestamp(timestamp)
84
- from_name = @agent_badge.render(from)
85
- to_name = @agent_badge.render(to)
86
-
87
- "#{indent}#{@pastel.dim(time)} #{@pastel.green("#{UI::Icons::RESULT} Delegation result")} from #{from_name} #{@pastel.dim("→")} #{to_name}"
88
- end
89
-
90
- # Render hook execution
91
- # [12:34:56] 🪝 Hook executed PreToolUse architect
92
- def hook_executed(hook_event:, agent:, timestamp:, success:, blocked:)
93
- indent = @depth_tracker.indent(agent)
94
- time = Formatters::Time.timestamp(timestamp)
95
- hook_display = @pastel.cyan(hook_event)
96
- agent_name = @agent_badge.render(agent)
97
-
98
- status = if blocked
99
- @pastel.red("BLOCKED")
100
- elsif success
101
- @pastel.green("executed")
102
- else
103
- @pastel.yellow("warning")
104
- end
105
-
106
- color = if blocked
107
- :red
108
- else
109
- (success ? :green : :yellow)
110
- end
111
- icon_colored = @pastel.public_send(color, UI::Icons::HOOK)
112
-
113
- "#{indent}#{@pastel.dim(time)} #{icon_colored} Hook #{status} #{hook_display} #{agent_name}"
114
- end
115
-
116
- # Render usage stats line
117
- # 5,922 tokens │ $0.0016 │ 1.5% used, 394,078 remaining
118
- def usage_stats(tokens:, cost:, context_pct: nil, remaining: nil, cumulative: nil, indent: 0)
119
- prefix = " " * indent
120
- stats = @usage_stats.render(
121
- tokens: tokens,
122
- cost: cost,
123
- context_pct: context_pct,
124
- remaining: remaining,
125
- cumulative: cumulative,
126
- )
127
-
128
- "#{prefix} #{stats}"
129
- end
130
-
131
- # Render tool list
132
- # Tools available: Read, Write, Bash
133
- def tools_available(tools, indent: 0)
134
- return "" if tools.nil? || tools.empty?
135
-
136
- prefix = " " * indent
137
- tools_list = tools.join(", ")
138
-
139
- "#{prefix} #{@pastel.dim("Tools available: #{tools_list}")}"
140
- end
141
-
142
- # Render delegation list
143
- # Can delegate to: frontend_dev, backend_dev
144
- def delegates_to(agents, indent: 0, color_cache:)
145
- return "" if agents.nil? || agents.empty?
146
-
147
- prefix = " " * indent
148
- agent_badge = Components::AgentBadge.new(pastel: @pastel, color_cache: color_cache)
149
- delegates_list = agent_badge.render_list(agents)
150
-
151
- "#{prefix} #{@pastel.dim("Can delegate to:")} #{delegates_list}"
152
- end
153
-
154
- # Render thinking text (italic, indented)
155
- def thinking_text(content, indent: 0)
156
- return "" if content.nil? || content.empty?
157
-
158
- # Strip system reminders
159
- text = Formatters::Text.strip_system_reminders(content)
160
- return "" if text.empty?
161
-
162
- prefix = " " * indent
163
-
164
- text.split("\n").map do |line|
165
- "#{prefix} #{@pastel.italic(line)}"
166
- end.join("\n")
167
- end
168
-
169
- # Render tool arguments
170
- def tool_arguments(args, indent: 0, truncate: false)
171
- @content_block.render_hash(args, indent: indent, label: "Arguments", truncate: truncate)
172
- end
173
-
174
- # Render tool result content
175
- def tool_result_content(content, indent: 0, truncate: false)
176
- @content_block.render_text(
177
- content,
178
- indent: indent,
179
- color: :bright_green,
180
- truncate: truncate,
181
- max_lines: 2,
182
- max_chars: 300,
183
- )
184
- end
185
- end
186
- end
187
- end
188
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module State
6
- # Caches agent name → color assignments for consistent coloring
7
- class AgentColorCache
8
- # Professional color palette inspired by modern CLIs
9
- PALETTE = [
10
- :cyan,
11
- :magenta,
12
- :yellow,
13
- :blue,
14
- :green,
15
- :bright_cyan,
16
- :bright_magenta,
17
- ].freeze
18
-
19
- def initialize
20
- @cache = {}
21
- @next_index = 0
22
- end
23
-
24
- # Get color for agent (cached)
25
- def get(agent_name)
26
- @cache[agent_name] ||= assign_next_color
27
- end
28
-
29
- # Reset cache (for testing)
30
- def reset
31
- @cache.clear
32
- @next_index = 0
33
- end
34
-
35
- private
36
-
37
- def assign_next_color
38
- color = PALETTE[@next_index % PALETTE.size]
39
- @next_index += 1
40
- color
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SwarmCLI
4
- module UI
5
- module State
6
- # Tracks agent depth for hierarchical indentation display
7
- class DepthTracker
8
- def initialize
9
- @depths = {}
10
- @seen_agents = []
11
- end
12
-
13
- # Get indentation depth for agent
14
- def get(agent_name)
15
- @depths[agent_name] ||= calculate_depth(agent_name)
16
- end
17
-
18
- # Get indent string for agent
19
- def indent(agent_name, char: " ")
20
- char * get(agent_name)
21
- end
22
-
23
- # Reset tracker (for testing)
24
- def reset
25
- @depths.clear
26
- @seen_agents.clear
27
- end
28
-
29
- private
30
-
31
- def calculate_depth(agent_name)
32
- @seen_agents << agent_name unless @seen_agents.include?(agent_name)
33
-
34
- # First agent is depth 0, all others are depth 1
35
- @seen_agents.size == 1 ? 0 : 1
36
- end
37
- end
38
- end
39
- end
40
- end