kward 0.67.0 → 0.68.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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +5 -5
  5. data/doc/authentication.md +24 -1
  6. data/doc/configuration.md +9 -2
  7. data/doc/extensibility.md +1 -1
  8. data/doc/getting-started.md +4 -6
  9. data/doc/plugins.md +0 -2
  10. data/doc/releasing.md +7 -8
  11. data/doc/rpc.md +6 -6
  12. data/doc/usage.md +5 -2
  13. data/doc/web-search.md +2 -2
  14. data/kward.gemspec +4 -0
  15. data/lib/kward/agent.rb +29 -2
  16. data/lib/kward/ansi.rb +3 -0
  17. data/lib/kward/auth/anthropic_oauth.rb +291 -0
  18. data/lib/kward/auth/file.rb +2 -0
  19. data/lib/kward/auth/github_oauth.rb +3 -0
  20. data/lib/kward/auth/openai_oauth.rb +4 -0
  21. data/lib/kward/auth/openrouter_api_key.rb +2 -0
  22. data/lib/kward/cancellation.rb +3 -0
  23. data/lib/kward/cli/auth_commands.rb +82 -0
  24. data/lib/kward/cli/commands.rb +222 -0
  25. data/lib/kward/cli/compaction.rb +25 -0
  26. data/lib/kward/cli/doctor.rb +121 -0
  27. data/lib/kward/cli/interactive_turn.rb +225 -0
  28. data/lib/kward/cli/memory_commands.rb +133 -0
  29. data/lib/kward/cli/plugins.rb +112 -0
  30. data/lib/kward/cli/prompt_interface.rb +132 -0
  31. data/lib/kward/cli/rendering.rb +389 -0
  32. data/lib/kward/cli/runtime_helpers.rb +159 -0
  33. data/lib/kward/cli/sessions.rb +376 -0
  34. data/lib/kward/cli/settings.rb +663 -0
  35. data/lib/kward/cli/slash_commands.rb +112 -0
  36. data/lib/kward/cli/stats.rb +64 -0
  37. data/lib/kward/cli/tool_summaries.rb +153 -0
  38. data/lib/kward/cli.rb +38 -2790
  39. data/lib/kward/cli_transcript_formatter.rb +4 -7
  40. data/lib/kward/clipboard.rb +1 -0
  41. data/lib/kward/compaction/file_operation_tracker.rb +3 -0
  42. data/lib/kward/compactor.rb +29 -7
  43. data/lib/kward/config_files.rb +33 -24
  44. data/lib/kward/conversation.rb +70 -5
  45. data/lib/kward/events.rb +2 -0
  46. data/lib/kward/export_path.rb +2 -0
  47. data/lib/kward/image_attachments.rb +2 -0
  48. data/lib/kward/markdown_transcript.rb +2 -0
  49. data/lib/kward/memory/manager.rb +13 -0
  50. data/lib/kward/message_access.rb +23 -2
  51. data/lib/kward/message_text.rb +45 -0
  52. data/lib/kward/model/chat_invocation.rb +2 -0
  53. data/lib/kward/model/client.rb +295 -77
  54. data/lib/kward/model/context_overflow.rb +2 -0
  55. data/lib/kward/model/context_usage.rb +3 -0
  56. data/lib/kward/model/model_info.rb +143 -4
  57. data/lib/kward/model/payloads.rb +166 -13
  58. data/lib/kward/model/retry_message.rb +2 -0
  59. data/lib/kward/model/stream_parser.rb +129 -0
  60. data/lib/kward/pan/server.rb +3 -1
  61. data/lib/kward/plugin_registry.rb +12 -0
  62. data/lib/kward/private_file.rb +2 -0
  63. data/lib/kward/prompt_interface/banner.rb +3 -0
  64. data/lib/kward/prompt_interface/composer_controller.rb +262 -0
  65. data/lib/kward/prompt_interface/composer_renderer.rb +172 -0
  66. data/lib/kward/prompt_interface/composer_state.rb +221 -0
  67. data/lib/kward/prompt_interface/key_handler.rb +365 -0
  68. data/lib/kward/prompt_interface/layout.rb +31 -0
  69. data/lib/kward/prompt_interface/overlay_renderer.rb +111 -0
  70. data/lib/kward/prompt_interface/prompt_renderer.rb +91 -0
  71. data/lib/kward/prompt_interface/question_prompt.rb +328 -0
  72. data/lib/kward/prompt_interface/runtime_state.rb +59 -0
  73. data/lib/kward/prompt_interface/screen.rb +186 -0
  74. data/lib/kward/prompt_interface/selection_prompt.rb +242 -0
  75. data/lib/kward/prompt_interface/slash_overlay.rb +102 -0
  76. data/lib/kward/prompt_interface/stream_state.rb +65 -0
  77. data/lib/kward/prompt_interface/transcript_buffer.rb +85 -0
  78. data/lib/kward/prompt_interface/transcript_renderer.rb +142 -0
  79. data/lib/kward/prompt_interface.rb +69 -1832
  80. data/lib/kward/prompts/commands.rb +2 -0
  81. data/lib/kward/prompts/templates.rb +3 -0
  82. data/lib/kward/prompts.rb +2 -0
  83. data/lib/kward/question_contract.rb +66 -0
  84. data/lib/kward/resources/avatar_kward_logo.rb +2 -0
  85. data/lib/kward/resources/pixel_logo.rb +2 -0
  86. data/lib/kward/rpc/attachment_normalizer.rb +60 -0
  87. data/lib/kward/rpc/auth_manager.rb +65 -11
  88. data/lib/kward/rpc/config_manager.rb +11 -0
  89. data/lib/kward/rpc/prompt_bridge.rb +5 -26
  90. data/lib/kward/rpc/redactor.rb +3 -0
  91. data/lib/kward/rpc/runtime_payloads.rb +4 -1
  92. data/lib/kward/rpc/server.rb +37 -10
  93. data/lib/kward/rpc/session_manager.rb +123 -347
  94. data/lib/kward/rpc/session_metrics.rb +68 -0
  95. data/lib/kward/rpc/session_tree.rb +48 -0
  96. data/lib/kward/rpc/session_tree_rows.rb +208 -0
  97. data/lib/kward/rpc/tool_event_normalizer.rb +3 -0
  98. data/lib/kward/rpc/tool_metadata.rb +3 -0
  99. data/lib/kward/rpc/transcript_normalizer.rb +3 -0
  100. data/lib/kward/rpc/transport.rb +3 -0
  101. data/lib/kward/session_diff.rb +2 -0
  102. data/lib/kward/session_store.rb +125 -31
  103. data/lib/kward/session_trash.rb +1 -0
  104. data/lib/kward/session_tree_renderer.rb +8 -41
  105. data/lib/kward/session_tree_tool_display.rb +56 -0
  106. data/lib/kward/skills/registry.rb +3 -0
  107. data/lib/kward/starter_pack_installer.rb +1 -0
  108. data/lib/kward/steering.rb +2 -0
  109. data/lib/kward/telemetry/logger.rb +3 -0
  110. data/lib/kward/telemetry/stats.rb +3 -0
  111. data/lib/kward/tools/ask_user_question.rb +20 -32
  112. data/lib/kward/tools/base.rb +8 -0
  113. data/lib/kward/tools/code_search.rb +5 -0
  114. data/lib/kward/tools/edit_file.rb +5 -0
  115. data/lib/kward/tools/list_directory.rb +5 -0
  116. data/lib/kward/tools/read_file.rb +5 -0
  117. data/lib/kward/tools/read_skill.rb +5 -0
  118. data/lib/kward/tools/registry.rb +33 -2
  119. data/lib/kward/tools/run_shell_command.rb +5 -0
  120. data/lib/kward/tools/search/code.rb +7 -0
  121. data/lib/kward/tools/search/web.rb +17 -14
  122. data/lib/kward/tools/tool_call.rb +25 -5
  123. data/lib/kward/tools/web_search.rb +7 -1
  124. data/lib/kward/tools/write_file.rb +5 -0
  125. data/lib/kward/transcript_export.rb +2 -0
  126. data/lib/kward/version.rb +2 -1
  127. data/lib/kward/workspace.rb +45 -5
  128. metadata +43 -1
@@ -0,0 +1,142 @@
1
+ # Namespace for the Kward CLI agent runtime.
2
+ module Kward
3
+ # Terminal transcript rendering helpers.
4
+ class PromptInterface
5
+ # Renderer for transcript entries in the terminal prompt interface.
6
+ module TranscriptRenderer
7
+ private
8
+
9
+ def write_stream_block_locked(label, delta, finish: false)
10
+ with_synchronized_output_locked do
11
+ prepare_transcript_output_locked unless @restoring_transcript
12
+ if label && @stream_state.block != label
13
+ ensure_transcript_block_separator_locked
14
+ write_transcript_text_locked("#{colored("#{transcript_label(label)}>", label_color(label), :bold)}\n")
15
+ @stream_state.start_block(label)
16
+ end
17
+ write_transcript_text_locked(delta) unless delta.empty?
18
+ write_transcript_text_locked("\n") if finish && @stream_state.block
19
+ @stream_state.finish_block if finish
20
+ restore_composer_cursor_locked unless @restoring_transcript
21
+ end
22
+ @output_io.flush unless @restoring_transcript
23
+ end
24
+
25
+ def write_transcript_text_locked(text)
26
+ append_transcript_buffer(text.to_s)
27
+ remember_transcript_viewport_locked unless text.to_s.empty?
28
+ write_visual_transcript_text_locked(text)
29
+ end
30
+
31
+ def write_visual_transcript_text_locked(text)
32
+ width, height = screen_size
33
+ output_text = terminal_newlines(text.to_s)
34
+ advance_pending_stream_wrap_locked(output_text, width: width, height: height)
35
+ @output_io.print(output_text)
36
+ update_stream_position(output_text, width: width)
37
+ end
38
+
39
+ def append_transcript_buffer(text)
40
+ @transcript_buffer.append(text.to_s)
41
+ end
42
+
43
+ def invalidate_transcript_display_rows_cache
44
+ @transcript_buffer.invalidate_display_rows_cache
45
+ end
46
+
47
+ def ensure_transcript_block_separator_locked
48
+ return if @transcript_buffer.empty? || @transcript_buffer.end_with?("\n\n")
49
+
50
+ write_transcript_text_locked(@transcript_buffer.end_with?("\n") ? "\n" : "\n\n")
51
+ end
52
+
53
+ def terminal_newlines(text)
54
+ text.gsub(/\r\n|\r|\n/, "\r\n")
55
+ end
56
+
57
+ def redraw_transcript_locked(width: screen_width, height: screen_height)
58
+ return unless transcript_renderable?
59
+
60
+ rows = transcript_viewport_rows(transcript_redraw_row_count(height), width)
61
+ clear_screen_rows_locked(1, rows.length)
62
+ return if rows.empty?
63
+
64
+ move_to_screen(1, 1)
65
+ @output_io.print(terminal_newlines(rows.join("\n")))
66
+ end
67
+
68
+ def transcript_viewport_text(row_count, width)
69
+ transcript_viewport_rows(row_count, width).join("\n")
70
+ end
71
+
72
+ def transcript_viewport_rows(row_count, width)
73
+ return [] unless row_count.positive?
74
+
75
+ rows = transcript_display_rows(width).last(row_count)
76
+ rows = ([""] * (row_count - rows.length)) + rows if rows.length < row_count
77
+ rows
78
+ end
79
+
80
+ def remember_transcript_viewport_locked(height = screen_height)
81
+ @transcript_viewport_rows = transcript_bottom_row(height)
82
+ end
83
+
84
+ def transcript_renderable?
85
+ @visual_banner_count.positive? || !@transcript_buffer.empty?
86
+ end
87
+
88
+ def transcript_display_rows(width)
89
+ @transcript_buffer.display_rows(width, visual_banner_count: @visual_banner_count, banner_rows: method(:banner_rows))
90
+ end
91
+
92
+ def transcript_text_display_rows(width)
93
+ @transcript_buffer.text_display_rows(width)
94
+ end
95
+
96
+ def reset_stream_position_from_transcript_locked(width = screen_width)
97
+ @stream_state.reset_position_from_rows(transcript_display_rows(width), width)
98
+ end
99
+
100
+ def move_to_transcript_cursor_locked(width: screen_width, height: screen_height)
101
+ if @stream_state.pending_wrap?
102
+ move_to_screen(transcript_bottom_row(height), width)
103
+ else
104
+ move_to_screen(transcript_bottom_row(height), [@stream_state.col + 1, width].min)
105
+ end
106
+ end
107
+
108
+ def advance_pending_stream_wrap_locked(output_text, width: screen_width, height: screen_height)
109
+ return unless @stream_state.pending_wrap?
110
+ return if output_text.empty? || output_text.start_with?("\r", "\n")
111
+
112
+ move_to_screen(transcript_bottom_row(height), width)
113
+ @output_io.print("\r\n")
114
+ @stream_state.clear_pending_wrap
115
+ end
116
+
117
+ def update_stream_position(text, width: screen_width)
118
+ @stream_state.update_position(text, width: width)
119
+ end
120
+
121
+ def transcript_label(label)
122
+ label == "Assistant" ? @assistant_label : label
123
+ end
124
+
125
+ def label_color(label)
126
+ case label
127
+ when "Reasoning"
128
+ :yellow
129
+ when "Assistant", "Kward"
130
+ :green
131
+ when "Tool"
132
+ :magenta
133
+ when "Tool output"
134
+ :cyan
135
+ else
136
+ :blue
137
+ end
138
+ end
139
+
140
+ end
141
+ end
142
+ end