kward 0.70.0 → 0.72.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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pages.yml +1 -1
  3. data/CHANGELOG.md +89 -3
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +90 -2
  6. data/README.md +34 -6
  7. data/Rakefile +96 -0
  8. data/doc/agent-tools.md +52 -0
  9. data/doc/api.md +92 -0
  10. data/doc/authentication.md +58 -23
  11. data/doc/code-search.md +42 -2
  12. data/doc/configuration.md +102 -13
  13. data/doc/context-budgeting.md +136 -0
  14. data/doc/context-tools.md +83 -0
  15. data/doc/editor.md +394 -0
  16. data/doc/extensibility.md +16 -7
  17. data/doc/files.md +100 -0
  18. data/doc/getting-started.md +25 -18
  19. data/doc/git.md +122 -0
  20. data/doc/memory.md +24 -4
  21. data/doc/personas.md +34 -5
  22. data/doc/plugins.md +74 -3
  23. data/doc/releasing.md +45 -8
  24. data/doc/rpc.md +77 -15
  25. data/doc/session-management.md +254 -0
  26. data/doc/shell.md +286 -0
  27. data/doc/tabs.md +122 -0
  28. data/doc/troubleshooting.md +77 -1
  29. data/doc/usage.md +60 -15
  30. data/doc/web-search.md +12 -4
  31. data/doc/workspace-tools.md +144 -0
  32. data/examples/plugins/space_invaders.rb +377 -0
  33. data/lib/kward/agent.rb +1 -1
  34. data/lib/kward/cli/commands.rb +41 -2
  35. data/lib/kward/cli/git.rb +150 -0
  36. data/lib/kward/cli/interactive_turn.rb +73 -9
  37. data/lib/kward/cli/openrouter_commands.rb +55 -0
  38. data/lib/kward/cli/plugins.rb +54 -4
  39. data/lib/kward/cli/prompt_interface.rb +111 -6
  40. data/lib/kward/cli/rendering.rb +11 -6
  41. data/lib/kward/cli/runtime_helpers.rb +133 -3
  42. data/lib/kward/cli/sessions.rb +262 -13
  43. data/lib/kward/cli/settings.rb +216 -37
  44. data/lib/kward/cli/slash_commands.rb +439 -8
  45. data/lib/kward/cli/tabs.rb +695 -0
  46. data/lib/kward/cli.rb +171 -26
  47. data/lib/kward/compactor.rb +4 -1
  48. data/lib/kward/config_files.rb +125 -5
  49. data/lib/kward/context_budget_meter.rb +44 -0
  50. data/lib/kward/conversation.rb +59 -22
  51. data/lib/kward/editor_mode.rb +25 -0
  52. data/lib/kward/ekwsh.rb +362 -0
  53. data/lib/kward/model/client.rb +37 -50
  54. data/lib/kward/model/context_usage.rb +13 -6
  55. data/lib/kward/model/model_info.rb +92 -16
  56. data/lib/kward/model/payloads.rb +2 -0
  57. data/lib/kward/openrouter_model_cache.rb +120 -0
  58. data/lib/kward/plugin_registry.rb +108 -1
  59. data/lib/kward/project_files.rb +52 -0
  60. data/lib/kward/prompt_history.rb +82 -0
  61. data/lib/kward/prompt_interface/banner.rb +16 -51
  62. data/lib/kward/prompt_interface/composer_controller.rb +124 -83
  63. data/lib/kward/prompt_interface/composer_renderer.rb +116 -14
  64. data/lib/kward/prompt_interface/composer_state.rb +96 -27
  65. data/lib/kward/prompt_interface/editor/auto_close_pairs.rb +123 -0
  66. data/lib/kward/prompt_interface/editor/auto_indent.rb +509 -0
  67. data/lib/kward/prompt_interface/editor/buffer.rb +109 -0
  68. data/lib/kward/prompt_interface/editor/controller.rb +1018 -0
  69. data/lib/kward/prompt_interface/editor/endwise.rb +321 -0
  70. data/lib/kward/prompt_interface/editor/file_marker.rb +40 -0
  71. data/lib/kward/prompt_interface/editor/indent_navigation.rb +61 -0
  72. data/lib/kward/prompt_interface/editor/kill_ring.rb +78 -0
  73. data/lib/kward/prompt_interface/editor/modes/emacs.rb +259 -0
  74. data/lib/kward/prompt_interface/editor/modes/modern.rb +353 -0
  75. data/lib/kward/prompt_interface/editor/modes/vibe.rb +1962 -0
  76. data/lib/kward/prompt_interface/editor/renderer.rb +243 -0
  77. data/lib/kward/prompt_interface/editor/search.rb +76 -0
  78. data/lib/kward/prompt_interface/editor/selections.rb +120 -0
  79. data/lib/kward/prompt_interface/editor/state.rb +1249 -0
  80. data/lib/kward/prompt_interface/editor/status_text.rb +23 -0
  81. data/lib/kward/prompt_interface/editor/syntax_highlighter.rb +420 -0
  82. data/lib/kward/prompt_interface/editor/undo_history.rb +46 -0
  83. data/lib/kward/prompt_interface/editor/vibe_state.rb +44 -0
  84. data/lib/kward/prompt_interface/file_overlay.rb +211 -0
  85. data/lib/kward/prompt_interface/git_prompt.rb +299 -0
  86. data/lib/kward/prompt_interface/interactive/controller.rb +186 -0
  87. data/lib/kward/prompt_interface/interactive/renderer.rb +71 -0
  88. data/lib/kward/prompt_interface/interactive/state.rb +62 -0
  89. data/lib/kward/prompt_interface/key_handler.rb +416 -43
  90. data/lib/kward/prompt_interface/layout.rb +2 -2
  91. data/lib/kward/prompt_interface/overlay_renderer.rb +21 -2
  92. data/lib/kward/prompt_interface/project_browser.rb +524 -0
  93. data/lib/kward/prompt_interface/prompt_renderer.rb +32 -13
  94. data/lib/kward/prompt_interface/question_prompt.rb +122 -82
  95. data/lib/kward/prompt_interface/runtime_state.rb +49 -1
  96. data/lib/kward/prompt_interface/screen.rb +17 -0
  97. data/lib/kward/prompt_interface/selection_prompt.rb +511 -58
  98. data/lib/kward/prompt_interface/stream_state.rb +7 -0
  99. data/lib/kward/prompt_interface/transcript_buffer.rb +13 -16
  100. data/lib/kward/prompt_interface/transcript_renderer.rb +3 -3
  101. data/lib/kward/prompt_interface.rb +307 -35
  102. data/lib/kward/prompts/commands.rb +7 -1
  103. data/lib/kward/prompts.rb +4 -2
  104. data/lib/kward/rpc/server.rb +45 -11
  105. data/lib/kward/rpc/session_manager.rb +52 -53
  106. data/lib/kward/rpc/session_tree_rows.rb +9 -115
  107. data/lib/kward/rpc/tool_event_normalizer.rb +1 -1
  108. data/lib/kward/session_store.rb +67 -4
  109. data/lib/kward/session_tree_nodes.rb +136 -0
  110. data/lib/kward/session_tree_renderer.rb +9 -131
  111. data/lib/kward/tab_store.rb +47 -0
  112. data/lib/kward/telemetry/logger.rb +5 -3
  113. data/lib/kward/text_boundary.rb +25 -0
  114. data/lib/kward/tool_output_compactor.rb +127 -0
  115. data/lib/kward/tools/base.rb +8 -2
  116. data/lib/kward/tools/context_budget_stats.rb +54 -0
  117. data/lib/kward/tools/context_for_task.rb +202 -0
  118. data/lib/kward/tools/read_file.rb +8 -4
  119. data/lib/kward/tools/registry.rb +92 -15
  120. data/lib/kward/tools/retrieve_tool_output.rb +71 -0
  121. data/lib/kward/tools/search/web.rb +2 -2
  122. data/lib/kward/tools/summarize_file_structure.rb +29 -0
  123. data/lib/kward/tools/tool_call.rb +12 -0
  124. data/lib/kward/version.rb +1 -1
  125. data/lib/kward/workers/git_guard.rb +68 -0
  126. data/lib/kward/workers/live_view.rb +49 -0
  127. data/lib/kward/workers/manager.rb +288 -0
  128. data/lib/kward/workers/store.rb +72 -0
  129. data/lib/kward/workers/tool_policy.rb +23 -0
  130. data/lib/kward/workers/worker.rb +82 -0
  131. data/lib/kward/workers/write_lock.rb +38 -0
  132. data/lib/kward/workers.rb +7 -0
  133. data/lib/kward/workspace.rb +154 -12
  134. data/templates/default/fulldoc/html/css/kward.css +362 -42
  135. data/templates/default/fulldoc/html/full_list.erb +107 -0
  136. data/templates/default/fulldoc/html/js/kward.js +161 -2
  137. data/templates/default/fulldoc/html/setup.rb +8 -0
  138. data/templates/default/kward_navigation.rb +102 -0
  139. data/templates/default/layout/html/layout.erb +43 -10
  140. data/templates/default/layout/html/setup.rb +39 -38
  141. metadata +65 -3
  142. data/lib/kward/resources/avatar_kward_logo.rb +0 -50
  143. data/lib/kward/resources/pixel_logo.rb +0 -232
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kward
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.70.0
4
+ version: 0.72.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kai Wood
@@ -124,19 +124,31 @@ files:
124
124
  - LICENSE
125
125
  - README.md
126
126
  - Rakefile
127
+ - doc/agent-tools.md
128
+ - doc/api.md
127
129
  - doc/authentication.md
128
130
  - doc/code-search.md
129
131
  - doc/configuration.md
132
+ - doc/context-budgeting.md
133
+ - doc/context-tools.md
134
+ - doc/editor.md
130
135
  - doc/extensibility.md
136
+ - doc/files.md
131
137
  - doc/getting-started.md
138
+ - doc/git.md
132
139
  - doc/memory.md
133
140
  - doc/personas.md
134
141
  - doc/plugins.md
135
142
  - doc/releasing.md
136
143
  - doc/rpc.md
144
+ - doc/session-management.md
145
+ - doc/shell.md
146
+ - doc/tabs.md
137
147
  - doc/troubleshooting.md
138
148
  - doc/usage.md
139
149
  - doc/web-search.md
150
+ - doc/workspace-tools.md
151
+ - examples/plugins/space_invaders.rb
140
152
  - exe/kward
141
153
  - kward.gemspec
142
154
  - lib/kward.rb
@@ -153,8 +165,10 @@ files:
153
165
  - lib/kward/cli/commands.rb
154
166
  - lib/kward/cli/compaction.rb
155
167
  - lib/kward/cli/doctor.rb
168
+ - lib/kward/cli/git.rb
156
169
  - lib/kward/cli/interactive_turn.rb
157
170
  - lib/kward/cli/memory_commands.rb
171
+ - lib/kward/cli/openrouter_commands.rb
158
172
  - lib/kward/cli/plugins.rb
159
173
  - lib/kward/cli/prompt_interface.rb
160
174
  - lib/kward/cli/rendering.rb
@@ -164,13 +178,17 @@ files:
164
178
  - lib/kward/cli/slash_commands.rb
165
179
  - lib/kward/cli/stats.rb
166
180
  - lib/kward/cli/sysprompt.rb
181
+ - lib/kward/cli/tabs.rb
167
182
  - lib/kward/cli/tool_summaries.rb
168
183
  - lib/kward/cli_transcript_formatter.rb
169
184
  - lib/kward/clipboard.rb
170
185
  - lib/kward/compaction/file_operation_tracker.rb
171
186
  - lib/kward/compactor.rb
172
187
  - lib/kward/config_files.rb
188
+ - lib/kward/context_budget_meter.rb
173
189
  - lib/kward/conversation.rb
190
+ - lib/kward/editor_mode.rb
191
+ - lib/kward/ekwsh.rb
174
192
  - lib/kward/events.rb
175
193
  - lib/kward/export_path.rb
176
194
  - lib/kward/image_attachments.rb
@@ -186,18 +204,46 @@ files:
186
204
  - lib/kward/model/payloads.rb
187
205
  - lib/kward/model/retry_message.rb
188
206
  - lib/kward/model/stream_parser.rb
207
+ - lib/kward/openrouter_model_cache.rb
189
208
  - lib/kward/pan/index.html.erb
190
209
  - lib/kward/pan/server.rb
191
210
  - lib/kward/plugin_registry.rb
192
211
  - lib/kward/private_file.rb
212
+ - lib/kward/project_files.rb
213
+ - lib/kward/prompt_history.rb
193
214
  - lib/kward/prompt_interface.rb
194
215
  - lib/kward/prompt_interface/banner.rb
195
216
  - lib/kward/prompt_interface/composer_controller.rb
196
217
  - lib/kward/prompt_interface/composer_renderer.rb
197
218
  - lib/kward/prompt_interface/composer_state.rb
219
+ - lib/kward/prompt_interface/editor/auto_close_pairs.rb
220
+ - lib/kward/prompt_interface/editor/auto_indent.rb
221
+ - lib/kward/prompt_interface/editor/buffer.rb
222
+ - lib/kward/prompt_interface/editor/controller.rb
223
+ - lib/kward/prompt_interface/editor/endwise.rb
224
+ - lib/kward/prompt_interface/editor/file_marker.rb
225
+ - lib/kward/prompt_interface/editor/indent_navigation.rb
226
+ - lib/kward/prompt_interface/editor/kill_ring.rb
227
+ - lib/kward/prompt_interface/editor/modes/emacs.rb
228
+ - lib/kward/prompt_interface/editor/modes/modern.rb
229
+ - lib/kward/prompt_interface/editor/modes/vibe.rb
230
+ - lib/kward/prompt_interface/editor/renderer.rb
231
+ - lib/kward/prompt_interface/editor/search.rb
232
+ - lib/kward/prompt_interface/editor/selections.rb
233
+ - lib/kward/prompt_interface/editor/state.rb
234
+ - lib/kward/prompt_interface/editor/status_text.rb
235
+ - lib/kward/prompt_interface/editor/syntax_highlighter.rb
236
+ - lib/kward/prompt_interface/editor/undo_history.rb
237
+ - lib/kward/prompt_interface/editor/vibe_state.rb
238
+ - lib/kward/prompt_interface/file_overlay.rb
239
+ - lib/kward/prompt_interface/git_prompt.rb
240
+ - lib/kward/prompt_interface/interactive/controller.rb
241
+ - lib/kward/prompt_interface/interactive/renderer.rb
242
+ - lib/kward/prompt_interface/interactive/state.rb
198
243
  - lib/kward/prompt_interface/key_handler.rb
199
244
  - lib/kward/prompt_interface/layout.rb
200
245
  - lib/kward/prompt_interface/overlay_renderer.rb
246
+ - lib/kward/prompt_interface/project_browser.rb
201
247
  - lib/kward/prompt_interface/prompt_renderer.rb
202
248
  - lib/kward/prompt_interface/question_prompt.rb
203
249
  - lib/kward/prompt_interface/runtime_state.rb
@@ -211,8 +257,6 @@ files:
211
257
  - lib/kward/prompts/commands.rb
212
258
  - lib/kward/prompts/templates.rb
213
259
  - lib/kward/question_contract.rb
214
- - lib/kward/resources/avatar_kward_logo.rb
215
- - lib/kward/resources/pixel_logo.rb
216
260
  - lib/kward/rpc/attachment_normalizer.rb
217
261
  - lib/kward/rpc/auth_manager.rb
218
262
  - lib/kward/rpc/config_manager.rb
@@ -231,16 +275,22 @@ files:
231
275
  - lib/kward/session_diff.rb
232
276
  - lib/kward/session_store.rb
233
277
  - lib/kward/session_trash.rb
278
+ - lib/kward/session_tree_nodes.rb
234
279
  - lib/kward/session_tree_renderer.rb
235
280
  - lib/kward/session_tree_tool_display.rb
236
281
  - lib/kward/skills/registry.rb
237
282
  - lib/kward/starter_pack_installer.rb
238
283
  - lib/kward/steering.rb
284
+ - lib/kward/tab_store.rb
239
285
  - lib/kward/telemetry/logger.rb
240
286
  - lib/kward/telemetry/stats.rb
287
+ - lib/kward/text_boundary.rb
288
+ - lib/kward/tool_output_compactor.rb
241
289
  - lib/kward/tools/ask_user_question.rb
242
290
  - lib/kward/tools/base.rb
243
291
  - lib/kward/tools/code_search.rb
292
+ - lib/kward/tools/context_budget_stats.rb
293
+ - lib/kward/tools/context_for_task.rb
244
294
  - lib/kward/tools/edit_file.rb
245
295
  - lib/kward/tools/fetch_content.rb
246
296
  - lib/kward/tools/fetch_raw.rb
@@ -248,21 +298,33 @@ files:
248
298
  - lib/kward/tools/read_file.rb
249
299
  - lib/kward/tools/read_skill.rb
250
300
  - lib/kward/tools/registry.rb
301
+ - lib/kward/tools/retrieve_tool_output.rb
251
302
  - lib/kward/tools/run_shell_command.rb
252
303
  - lib/kward/tools/search/code.rb
253
304
  - lib/kward/tools/search/web.rb
254
305
  - lib/kward/tools/search/web_fetch.rb
306
+ - lib/kward/tools/summarize_file_structure.rb
255
307
  - lib/kward/tools/tool_call.rb
256
308
  - lib/kward/tools/web_search.rb
257
309
  - lib/kward/tools/write_file.rb
258
310
  - lib/kward/transcript_export.rb
259
311
  - lib/kward/version.rb
312
+ - lib/kward/workers.rb
313
+ - lib/kward/workers/git_guard.rb
314
+ - lib/kward/workers/live_view.rb
315
+ - lib/kward/workers/manager.rb
316
+ - lib/kward/workers/store.rb
317
+ - lib/kward/workers/tool_policy.rb
318
+ - lib/kward/workers/worker.rb
319
+ - lib/kward/workers/write_lock.rb
260
320
  - lib/kward/workspace.rb
261
321
  - lib/main.rb
262
322
  - templates/default/fulldoc/html/css/kward.css
323
+ - templates/default/fulldoc/html/full_list.erb
263
324
  - templates/default/fulldoc/html/images/kward_logo.png
264
325
  - templates/default/fulldoc/html/js/kward.js
265
326
  - templates/default/fulldoc/html/setup.rb
327
+ - templates/default/kward_navigation.rb
266
328
  - templates/default/layout/html/breadcrumb.erb
267
329
  - templates/default/layout/html/layout.erb
268
330
  - templates/default/layout/html/setup.rb
@@ -1,50 +0,0 @@
1
- # Generated from avatar_kward_32x32.png as RGB terminal cells.
2
- # The interactive banner uses this data instead of decoding a PNG at runtime.
3
- module Kward
4
- # Static avatar logo data used by generated resources.
5
- module Resources
6
- # Static avatar logo data used by generated resources.
7
- module AvatarKwardLogo
8
- PIXELS = [
9
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
10
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [36, 40, 24], [39, 42, 25], [39, 42, 25], [33, 36, 21], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
11
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [33, 36, 23], [78, 86, 52], [85, 93, 56], [84, 93, 55], [65, 72, 43], [31, 33, 19], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
12
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [49, 54, 33], [78, 86, 52], [85, 93, 56], [85, 93, 56], [85, 93, 56], [84, 93, 55], [75, 83, 49], [35, 38, 23], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
13
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [61, 68, 40], [80, 88, 53], [85, 93, 56], [75, 83, 49], [72, 80, 48], [70, 78, 46], [78, 86, 52], [84, 93, 55], [76, 85, 51], [50, 55, 34], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
14
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [42, 46, 27], [80, 88, 53], [84, 93, 55], [70, 78, 46], [23, 25, 15], [37, 38, 29], [34, 35, 26], [33, 37, 22], [74, 81, 48], [83, 92, 55], [78, 86, 52], [24, 26, 15], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
15
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, [34, 37, 22], [83, 92, 55], [85, 93, 56], [69, 77, 46], [18, 19, 12], [53, 53, 45], [135, 132, 115], [128, 125, 111], [40, 40, 36], [21, 23, 14], [72, 80, 48], [80, 88, 53], [39, 44, 26], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
16
- [nil, nil, nil, nil, nil, nil, nil, nil, [18, 20, 13], [58, 63, 38], [85, 93, 56], [55, 61, 36], [23, 25, 15], [43, 42, 35], [146, 142, 129], [176, 173, 156], [179, 175, 159], [146, 142, 129], [67, 65, 58], [27, 30, 19], [62, 68, 41], [80, 88, 53], [54, 60, 35], nil, nil, nil, nil, nil, nil, nil, nil, nil],
17
- [nil, nil, nil, nil, nil, nil, nil, nil, [23, 26, 16], [74, 81, 48], [69, 77, 46], [21, 23, 14], [31, 31, 25], [108, 105, 91], [153, 150, 134], [134, 132, 118], [173, 169, 154], [183, 179, 163], [134, 132, 118], [27, 26, 21], [25, 28, 16], [70, 78, 46], [72, 80, 48], [21, 23, 14], nil, nil, nil, nil, nil, nil, nil, nil],
18
- [nil, nil, nil, nil, nil, nil, nil, [8, 10, 6], [66, 73, 45], [58, 63, 37], [14, 15, 9], [3, 3, 2], [87, 85, 74], [160, 157, 139], [160, 157, 139], [134, 132, 118], [80, 78, 69], [173, 169, 154], [175, 171, 155], [76, 74, 65], [5, 5, 4], [14, 15, 9], [63, 70, 42], [66, 73, 45], [2, 3, 2], nil, nil, nil, nil, nil, nil, nil],
19
- [nil, nil, nil, nil, nil, nil, nil, [18, 19, 12], [59, 65, 40], [23, 23, 21], [0, 0, 0], [100, 98, 86], [160, 157, 139], [163, 160, 143], [138, 136, 121], [70, 68, 58], [173, 169, 154], [175, 171, 155], [183, 179, 163], [179, 175, 159], [92, 90, 82], [1, 1, 1], [27, 30, 19], [62, 68, 41], [16, 17, 11], nil, nil, nil, nil, nil, nil, nil],
20
- [nil, nil, nil, nil, nil, nil, [15, 15, 11], [62, 68, 42], [72, 70, 61], [87, 85, 74], [44, 43, 36], [77, 76, 65], [108, 105, 91], [149, 146, 130], [123, 119, 105], [158, 155, 140], [168, 164, 149], [144, 140, 126], [160, 156, 142], [118, 115, 104], [77, 76, 65], [53, 53, 45], [103, 102, 85], [76, 74, 65], [66, 72, 43], [20, 20, 13], nil, nil, nil, nil, nil, nil],
21
- [nil, nil, nil, nil, nil, nil, [35, 38, 23], [78, 86, 52], [135, 132, 115], [113, 109, 94], [129, 128, 109], [23, 25, 15], [33, 55, 8], [96, 108, 72], [87, 85, 74], [168, 164, 149], [158, 155, 140], [97, 94, 83], [96, 108, 72], [24, 40, 6], [30, 33, 20], [142, 141, 122], [104, 102, 90], [153, 150, 134], [75, 83, 49], [37, 41, 25], nil, nil, nil, nil, nil, nil],
22
- [nil, nil, nil, nil, nil, [2, 2, 1], [63, 70, 42], [87, 95, 60], [134, 132, 118], [77, 76, 65], [104, 102, 90], [5, 5, 4], [72, 133, 6], [100, 184, 10], [39, 44, 26], [160, 156, 142], [146, 142, 129], [47, 70, 19], [103, 188, 10], [56, 105, 5], [10, 10, 8], [128, 125, 111], [72, 70, 61], [149, 146, 130], [84, 93, 55], [54, 60, 36], [2, 3, 2], nil, nil, nil, nil, nil],
23
- [nil, nil, nil, nil, nil, [46, 51, 30], [67, 74, 44], [44, 48, 30], [114, 111, 97], [80, 78, 69], [103, 102, 85], [34, 35, 26], [33, 55, 8], [59, 97, 13], [82, 84, 65], [166, 162, 147], [156, 152, 138], [76, 85, 51], [59, 97, 13], [29, 46, 8], [43, 42, 35], [134, 132, 118], [92, 90, 82], [128, 125, 111], [39, 44, 26], [65, 72, 43], [43, 48, 29], nil, nil, nil, nil, nil],
24
- [nil, nil, nil, nil, [17, 19, 12], [63, 70, 42], [31, 35, 21], [5, 5, 4], [39, 39, 33], [70, 68, 58], [53, 53, 45], [103, 102, 85], [58, 56, 48], [77, 76, 65], [113, 109, 94], [171, 167, 151], [163, 160, 143], [108, 105, 91], [72, 70, 61], [64, 62, 54], [108, 105, 91], [59, 58, 50], [84, 82, 71], [39, 39, 33], [3, 3, 2], [39, 44, 26], [58, 64, 39], nil, nil, nil, nil, nil],
25
- [nil, nil, nil, [2, 3, 2], [43, 47, 28], [68, 75, 45], [15, 17, 11], [0, 0, 0], [0, 0, 0], [8, 8, 7], [12, 12, 11], [59, 58, 50], [92, 89, 75], [27, 26, 21], [100, 96, 82], [171, 167, 151], [163, 160, 143], [70, 68, 58], [44, 43, 36], [114, 111, 97], [58, 56, 48], [15, 14, 13], [5, 5, 4], [0, 0, 0], [0, 0, 0], [23, 25, 15], [69, 77, 46], [30, 33, 20], nil, nil, nil, nil],
26
- [nil, nil, nil, [37, 41, 26], [63, 70, 42], [27, 30, 19], [1, 1, 1], [0, 0, 0], [8, 8, 7], [32, 33, 30], [20, 20, 18], [6, 6, 5], [64, 62, 54], [31, 31, 25], [100, 96, 82], [171, 167, 151], [163, 160, 143], [70, 68, 58], [51, 49, 43], [70, 68, 58], [6, 6, 5], [23, 23, 21], [35, 35, 33], [6, 6, 5], [0, 0, 0], [1, 1, 1], [33, 37, 22], [58, 63, 37], [32, 35, 21], nil, nil, nil],
27
- [nil, nil, nil, [52, 58, 35], [80, 88, 53], [52, 58, 34], [10, 10, 8], [5, 5, 4], [32, 33, 30], [23, 23, 21], [0, 0, 0], [23, 23, 21], [59, 58, 50], [70, 68, 58], [72, 70, 61], [166, 162, 147], [153, 150, 134], [67, 65, 58], [76, 74, 65], [62, 61, 57], [19, 19, 17], [0, 0, 0], [31, 31, 25], [32, 33, 30], [3, 3, 2], [15, 17, 11], [55, 61, 36], [80, 88, 53], [39, 42, 25], nil, nil, nil],
28
- [nil, nil, nil, [23, 24, 16], [54, 60, 36], [83, 92, 55], [27, 30, 19], [44, 44, 40], [19, 19, 17], [1, 1, 1], [1, 1, 1], [49, 48, 45], [44, 44, 40], [103, 102, 85], [44, 44, 40], [158, 155, 140], [144, 140, 126], [64, 62, 54], [92, 89, 75], [49, 48, 45], [40, 40, 36], [0, 0, 0], [3, 3, 2], [19, 19, 17], [49, 48, 45], [36, 40, 24], [83, 92, 55], [44, 48, 30], [23, 25, 16], nil, nil, nil],
29
- [nil, nil, nil, [50, 55, 34], [49, 54, 33], [62, 68, 41], [67, 74, 44], [30, 33, 20], [12, 12, 11], [30, 30, 28], [20, 20, 18], [30, 30, 28], [12, 12, 11], [92, 89, 75], [51, 49, 43], [160, 156, 142], [144, 140, 126], [64, 62, 54], [77, 76, 65], [12, 12, 11], [35, 35, 33], [17, 17, 16], [35, 35, 33], [10, 10, 8], [34, 35, 26], [68, 75, 45], [58, 63, 37], [51, 56, 34], [43, 48, 29], nil, nil, nil],
30
- [nil, nil, [30, 33, 21], [68, 75, 45], [70, 78, 46], [39, 44, 26], [59, 65, 40], [67, 74, 44], [21, 23, 14], [8, 8, 7], [19, 19, 17], [32, 33, 30], [10, 10, 8], [15, 14, 13], [92, 89, 75], [168, 164, 149], [160, 157, 139], [70, 68, 58], [12, 12, 11], [10, 10, 8], [35, 35, 33], [17, 17, 16], [10, 10, 8], [21, 23, 14], [72, 80, 48], [55, 61, 36], [47, 52, 31], [72, 80, 48], [66, 72, 43], [25, 26, 18], nil, nil],
31
- [nil, nil, [22, 24, 14], [27, 30, 18], [55, 61, 36], [84, 93, 55], [44, 48, 30], [54, 60, 36], [74, 81, 48], [25, 28, 16], [1, 1, 1], [49, 48, 45], [32, 33, 30], [1, 1, 1], [92, 89, 75], [123, 119, 105], [118, 115, 104], [67, 63, 54], [1, 1, 1], [44, 44, 40], [40, 40, 36], [1, 1, 1], [30, 33, 20], [76, 85, 51], [52, 58, 34], [51, 56, 34], [81, 90, 54], [47, 52, 31], [26, 28, 17], nil, nil, nil],
32
- [nil, [30, 33, 21], [66, 72, 43], [80, 88, 53], [80, 88, 53], [45, 50, 29], [33, 37, 22], [51, 56, 34], [42, 47, 27], [68, 75, 45], [38, 42, 25], [19, 19, 17], [35, 35, 33], [12, 12, 11], [37, 35, 30], [70, 68, 58], [67, 63, 54], [27, 26, 21], [17, 17, 16], [35, 35, 33], [17, 17, 16], [39, 44, 26], [70, 78, 46], [42, 47, 27], [54, 60, 36], [31, 35, 21], [31, 35, 21], [63, 70, 42], [78, 86, 52], [65, 71, 42], nil, nil],
33
- [nil, [14, 15, 9], [36, 39, 23], [63, 70, 42], [81, 90, 54], [62, 68, 41], [51, 56, 34], [39, 44, 26], [14, 15, 9], [49, 54, 33], [69, 77, 46], [25, 28, 16], [20, 20, 18], [8, 8, 7], [6, 6, 5], [43, 42, 35], [37, 35, 30], [3, 3, 2], [12, 12, 11], [19, 19, 17], [30, 33, 20], [70, 78, 46], [44, 48, 30], [14, 15, 9], [44, 48, 30], [49, 54, 33], [52, 58, 34], [72, 80, 48], [54, 60, 36], [36, 39, 23], [12, 14, 11], nil],
34
- [nil, [5, 5, 5], nil, [53, 58, 35], [84, 93, 55], [84, 93, 55], [81, 90, 54], [69, 77, 46], [55, 61, 36], [15, 17, 11], [39, 44, 26], [68, 75, 45], [59, 65, 40], [18, 19, 12], [10, 10, 8], [62, 61, 57], [56, 55, 53], [6, 6, 5], [23, 25, 15], [63, 70, 42], [69, 77, 46], [36, 40, 24], [15, 17, 11], [58, 63, 37], [67, 74, 44], [80, 88, 53], [84, 93, 55], [83, 92, 55], [39, 43, 26], nil, [2, 2, 1], nil],
35
- [nil, nil, nil, [25, 29, 18], [45, 50, 29], [27, 30, 19], [22, 25, 15], [75, 83, 50], [84, 93, 55], [54, 60, 36], [15, 17, 11], [18, 19, 12], [42, 47, 27], [74, 81, 48], [12, 12, 11], [23, 23, 21], [19, 19, 17], [30, 33, 20], [74, 81, 48], [38, 42, 25], [15, 17, 11], [15, 17, 11], [62, 68, 41], [83, 92, 55], [21, 23, 14], [33, 37, 22], [49, 54, 33], [21, 23, 14], nil, nil, nil, [4, 4, 4]],
36
- [nil, nil, nil, [64, 71, 42], [43, 47, 28], [7, 7, 4], nil, [51, 56, 35], [83, 92, 55], [70, 78, 46], [47, 52, 31], [38, 42, 25], [31, 35, 21], [54, 60, 36], [17, 17, 16], [51, 51, 48], [46, 46, 43], [27, 30, 19], [52, 58, 34], [30, 33, 20], [39, 44, 26], [49, 54, 33], [76, 85, 51], [85, 93, 56], [43, 48, 29], [14, 14, 10], [8, 8, 7], [49, 54, 33], [62, 67, 41], nil, nil, nil],
37
- [nil, nil, nil, [34, 38, 24], [37, 39, 24], nil, nil, [34, 37, 22], [75, 83, 50], [35, 39, 24], [1, 1, 1], [43, 48, 29], [66, 72, 43], [20, 21, 12], [3, 1, 1], [2, 2, 2], [2, 2, 2], [3, 3, 3], [27, 30, 18], [66, 72, 43], [35, 38, 23], [2, 1, 1], [42, 46, 28], [76, 85, 51], [5, 5, 4], nil, [2, 3, 2], [36, 40, 24], [22, 25, 15], nil, nil, nil],
38
- [nil, nil, nil, nil, nil, nil, [14, 15, 10], [67, 74, 44], [29, 32, 19], nil, nil, nil, [25, 29, 18], nil, nil, nil, nil, nil, nil, [23, 25, 16], nil, nil, nil, [29, 32, 19], [52, 58, 35], nil, nil, nil, nil, nil, nil, nil],
39
- [nil, nil, nil, nil, nil, nil, nil, [22, 24, 15], nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, [23, 26, 16], nil, nil, nil, nil, nil, nil, nil],
40
- [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
41
- ]
42
-
43
- PIXELS.each do |row|
44
- row.each { |color| color.freeze if color }
45
- row.freeze
46
- end
47
- PIXELS.freeze
48
- end
49
- end
50
- end
@@ -1,232 +0,0 @@
1
- require "zlib"
2
-
3
- # Namespace for the Kward CLI agent runtime.
4
- module Kward
5
- # Pixel-art logo data and rendering helpers.
6
- module PixelLogo
7
- PNG_SIGNATURE = "\x89PNG\r\n\x1a\n".b.freeze
8
- TRANSPARENT_ALPHA = 128
9
-
10
- module_function
11
-
12
- def rows_from_png(path, width:, height:)
13
- rows_from_pixels(indexed_png_pixels(path), width: width, height: height)
14
- rescue StandardError
15
- []
16
- end
17
-
18
- def rows_from_pixels(pixels, width:, height:)
19
- scaled = scale_pixels(pixels, width: width, height: height)
20
- render_rows(scaled)
21
- rescue StandardError
22
- []
23
- end
24
-
25
- def half_block_rows_from_png(path, width:, pixel_height:)
26
- half_block_rows_from_pixels(indexed_png_pixels(path), width: width, pixel_height: pixel_height)
27
- rescue StandardError
28
- []
29
- end
30
-
31
- def half_block_rows_from_pixels(pixels, width:, pixel_height:)
32
- scaled = scale_pixels(pixels, width: width, height: pixel_height)
33
- render_half_block_rows(scaled)
34
- rescue StandardError
35
- []
36
- end
37
-
38
- def indexed_png_pixels(path)
39
- data = File.binread(path)
40
- raise "Invalid PNG" unless data.start_with?(PNG_SIGNATURE)
41
-
42
- png_width = nil
43
- png_height = nil
44
- bit_depth = nil
45
- color_type = nil
46
- interlace = nil
47
- palette = nil
48
- transparency = []
49
- idat = +"".b
50
-
51
- each_chunk(data) do |type, chunk|
52
- case type
53
- when "IHDR"
54
- png_width, png_height, bit_depth, color_type, _compression, _filter, interlace = chunk.unpack("NNCCCCC")
55
- when "PLTE"
56
- palette = chunk.bytes.each_slice(3).map { |red, green, blue| [red, green, blue, 255] }
57
- when "tRNS"
58
- transparency = chunk.bytes
59
- when "IDAT"
60
- idat << chunk
61
- end
62
- end
63
-
64
- raise "Unsupported PNG" unless png_width && png_height && palette
65
- raise "Unsupported PNG" unless bit_depth == 8 && color_type == 3 && interlace == 0
66
-
67
- transparency.each_with_index do |alpha, index|
68
- palette[index] = palette[index][0, 3] + [alpha] if palette[index]
69
- end
70
-
71
- unfilter_indexed_rows(Zlib::Inflate.inflate(idat), png_width, png_height).map do |row|
72
- row.map { |index| palette[index] || [0, 0, 0, 0] }
73
- end
74
- end
75
-
76
- def each_chunk(data)
77
- offset = PNG_SIGNATURE.bytesize
78
- while offset < data.bytesize
79
- length = data[offset, 4].unpack1("N")
80
- type = data[offset + 4, 4]
81
- chunk = data[offset + 8, length]
82
- yield type, chunk
83
- offset += length + 12
84
- end
85
- end
86
-
87
- def unfilter_indexed_rows(raw, width, height)
88
- rows = []
89
- previous = Array.new(width, 0)
90
- offset = 0
91
-
92
- height.times do
93
- filter = raw.getbyte(offset)
94
- offset += 1
95
- scanline = raw.byteslice(offset, width).bytes
96
- offset += width
97
- row = unfilter_scanline(scanline, previous, filter)
98
- rows << row
99
- previous = row
100
- end
101
-
102
- rows
103
- end
104
-
105
- def unfilter_scanline(scanline, previous, filter)
106
- row = []
107
- scanline.each_with_index do |byte, index|
108
- left = index.zero? ? 0 : row[index - 1]
109
- up = previous[index] || 0
110
- up_left = index.zero? ? 0 : previous[index - 1]
111
- row << case filter
112
- when 0 then byte
113
- when 1 then (byte + left) & 0xff
114
- when 2 then (byte + up) & 0xff
115
- when 3 then (byte + ((left + up) / 2)) & 0xff
116
- when 4 then (byte + paeth(left, up, up_left)) & 0xff
117
- else byte
118
- end
119
- end
120
- row
121
- end
122
-
123
- def paeth(left, up, up_left)
124
- estimate = left + up - up_left
125
- left_distance = (estimate - left).abs
126
- up_distance = (estimate - up).abs
127
- up_left_distance = (estimate - up_left).abs
128
- return left if left_distance <= up_distance && left_distance <= up_left_distance
129
- return up if up_distance <= up_left_distance
130
-
131
- up_left
132
- end
133
-
134
- def scale_pixels(pixels, width:, height:)
135
- source_height = pixels.length
136
- source_width = pixels.first.length
137
-
138
- height.times.map do |target_y|
139
- source_y_start = target_y * source_height / height
140
- source_y_end = [(target_y + 1) * source_height / height, source_y_start + 1].max
141
- width.times.map do |target_x|
142
- source_x_start = target_x * source_width / width
143
- source_x_end = [(target_x + 1) * source_width / width, source_x_start + 1].max
144
- dominant_color(pixels, source_x_start...source_x_end, source_y_start...source_y_end)
145
- end
146
- end
147
- end
148
-
149
- def dominant_color(pixels, x_range, y_range)
150
- counts = Hash.new(0)
151
- y_range.each do |y|
152
- x_range.each do |x|
153
- counts[visible_color(pixels[y][x])] += 1
154
- end
155
- end
156
- counts.max_by { |_color, count| count }&.first
157
- end
158
-
159
- def visible_color(color)
160
- return nil unless color
161
-
162
- red, green, blue, alpha = color
163
- return nil if !alpha.nil? && alpha.to_i < TRANSPARENT_ALPHA
164
-
165
- [red, green, blue]
166
- end
167
-
168
- def render_rows(rows)
169
- rows.map do |row|
170
- current = nil
171
- rendered = +""
172
- row.each do |color|
173
- if color != current
174
- rendered << background_sgr(color)
175
- current = color
176
- end
177
- rendered << " "
178
- end
179
- rendered << reset_sgr if current
180
- rendered
181
- end
182
- end
183
-
184
- def render_half_block_rows(rows)
185
- rows.each_slice(2).map do |top_row, bottom_row|
186
- bottom_row ||= Array.new(top_row.length)
187
- current_foreground = nil
188
- current_background = nil
189
- rendered = +""
190
- top_row.each_with_index do |top, index|
191
- bottom = bottom_row[index]
192
- foreground, background, glyph = half_block_cell(top, bottom)
193
- if background != current_background
194
- rendered << background_sgr(background)
195
- current_background = background
196
- end
197
- if foreground != current_foreground
198
- rendered << foreground_sgr(foreground)
199
- current_foreground = foreground
200
- end
201
- rendered << glyph
202
- end
203
- rendered << reset_sgr if current_foreground || current_background
204
- rendered
205
- end
206
- end
207
-
208
- def half_block_cell(top, bottom)
209
- if top && bottom
210
- [top, bottom, "▀"]
211
- elsif top
212
- [top, nil, "▀"]
213
- elsif bottom
214
- [bottom, nil, "▄"]
215
- else
216
- [nil, nil, " "]
217
- end
218
- end
219
-
220
- def foreground_sgr(color)
221
- color ? "\e[38;2;#{color.join(";")}m" : "\e[39m"
222
- end
223
-
224
- def background_sgr(color)
225
- color ? "\e[48;2;#{color.join(";")}m" : "\e[49m"
226
- end
227
-
228
- def reset_sgr
229
- "\e[0m"
230
- end
231
- end
232
- end