kward 0.66.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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +9 -0
  3. data/CHANGELOG.md +12 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +90 -0
  6. data/LICENSE +21 -0
  7. data/README.md +101 -0
  8. data/Rakefile +20 -0
  9. data/doc/authentication.md +105 -0
  10. data/doc/code-search.md +56 -0
  11. data/doc/configuration.md +310 -0
  12. data/doc/extensibility.md +186 -0
  13. data/doc/getting-started.md +127 -0
  14. data/doc/memory.md +192 -0
  15. data/doc/plugins.md +223 -0
  16. data/doc/releasing.md +36 -0
  17. data/doc/rpc.md +635 -0
  18. data/doc/usage.md +179 -0
  19. data/doc/web-search.md +28 -0
  20. data/exe/kward +5 -0
  21. data/kward.gemspec +33 -0
  22. data/lib/kward/agent.rb +234 -0
  23. data/lib/kward/ansi.rb +276 -0
  24. data/lib/kward/auth/file.rb +11 -0
  25. data/lib/kward/auth/github_oauth.rb +222 -0
  26. data/lib/kward/auth/openai_oauth.rb +323 -0
  27. data/lib/kward/auth/openrouter_api_key.rb +40 -0
  28. data/lib/kward/cancellation.rb +54 -0
  29. data/lib/kward/cli.rb +2122 -0
  30. data/lib/kward/clipboard.rb +84 -0
  31. data/lib/kward/compactor.rb +998 -0
  32. data/lib/kward/config_files.rb +564 -0
  33. data/lib/kward/conversation.rb +148 -0
  34. data/lib/kward/events.rb +13 -0
  35. data/lib/kward/export_path.rb +28 -0
  36. data/lib/kward/image_attachments.rb +331 -0
  37. data/lib/kward/markdown_transcript.rb +72 -0
  38. data/lib/kward/memory/manager.rb +652 -0
  39. data/lib/kward/message_access.rb +42 -0
  40. data/lib/kward/model/chat_invocation.rb +23 -0
  41. data/lib/kward/model/client.rb +875 -0
  42. data/lib/kward/model/context_overflow.rb +55 -0
  43. data/lib/kward/model/context_usage.rb +104 -0
  44. data/lib/kward/model/model_info.rb +188 -0
  45. data/lib/kward/model/retry_message.rb +11 -0
  46. data/lib/kward/model/stream_parser.rb +205 -0
  47. data/lib/kward/pan/index.html.erb +143 -0
  48. data/lib/kward/pan/server.rb +397 -0
  49. data/lib/kward/plugin_registry.rb +327 -0
  50. data/lib/kward/private_file.rb +18 -0
  51. data/lib/kward/prompt_interface.rb +2437 -0
  52. data/lib/kward/prompts/commands.rb +50 -0
  53. data/lib/kward/prompts/templates.rb +60 -0
  54. data/lib/kward/prompts.rb +58 -0
  55. data/lib/kward/resources/avatar_kward_logo.rb +48 -0
  56. data/lib/kward/resources/pixel_logo.rb +230 -0
  57. data/lib/kward/rpc/auth_manager.rb +265 -0
  58. data/lib/kward/rpc/config_manager.rb +58 -0
  59. data/lib/kward/rpc/prompt_bridge.rb +104 -0
  60. data/lib/kward/rpc/redactor.rb +47 -0
  61. data/lib/kward/rpc/server.rb +639 -0
  62. data/lib/kward/rpc/session_manager.rb +1122 -0
  63. data/lib/kward/rpc/tool_event_normalizer.rb +68 -0
  64. data/lib/kward/rpc/tool_metadata.rb +80 -0
  65. data/lib/kward/rpc/transcript_normalizer.rb +307 -0
  66. data/lib/kward/rpc/transport.rb +58 -0
  67. data/lib/kward/session_diff.rb +125 -0
  68. data/lib/kward/session_store.rb +493 -0
  69. data/lib/kward/skills/registry.rb +76 -0
  70. data/lib/kward/starter_pack_installer.rb +110 -0
  71. data/lib/kward/steering.rb +56 -0
  72. data/lib/kward/telemetry/logger.rb +195 -0
  73. data/lib/kward/telemetry/stats.rb +466 -0
  74. data/lib/kward/tools/ask_user_question.rb +107 -0
  75. data/lib/kward/tools/base.rb +45 -0
  76. data/lib/kward/tools/code_search.rb +65 -0
  77. data/lib/kward/tools/edit_file.rb +41 -0
  78. data/lib/kward/tools/list_directory.rb +21 -0
  79. data/lib/kward/tools/read_file.rb +30 -0
  80. data/lib/kward/tools/read_skill.rb +27 -0
  81. data/lib/kward/tools/registry.rb +117 -0
  82. data/lib/kward/tools/run_shell_command.rb +28 -0
  83. data/lib/kward/tools/search/code.rb +445 -0
  84. data/lib/kward/tools/search/web.rb +747 -0
  85. data/lib/kward/tools/tool_call.rb +87 -0
  86. data/lib/kward/tools/web_search.rb +48 -0
  87. data/lib/kward/tools/write_file.rb +29 -0
  88. data/lib/kward/transcript_export.rb +40 -0
  89. data/lib/kward/version.rb +4 -0
  90. data/lib/kward/workspace.rb +377 -0
  91. data/lib/kward.rb +6 -0
  92. data/lib/main.rb +3 -0
  93. metadata +232 -0
@@ -0,0 +1,84 @@
1
+ require "base64"
2
+ require "open3"
3
+ require "rbconfig"
4
+
5
+ module Kward
6
+ # Best-effort local clipboard writer used by explicit user copy commands.
7
+ class Clipboard
8
+ Result = Struct.new(:success?, :method, :message, keyword_init: true)
9
+
10
+ def initialize(output: $stdout, env: ENV, command_runner: nil)
11
+ @output = output
12
+ @env = env
13
+ @command_runner = command_runner || method(:run_command)
14
+ end
15
+
16
+ def copy(text)
17
+ content = text.to_s
18
+ return Result.new(success?: false, message: "nothing to copy") if content.empty?
19
+
20
+ if osc52_available?
21
+ write_osc52(content)
22
+ return Result.new(success?: true, method: "osc52", message: "copied")
23
+ end
24
+
25
+ clipboard_commands.each do |name, command|
26
+ next unless executable?(name)
27
+
28
+ return Result.new(success?: true, method: name, message: "copied") if @command_runner.call(command, content)
29
+ end
30
+
31
+ Result.new(success?: false, message: "no supported clipboard mechanism found")
32
+ end
33
+
34
+ private
35
+
36
+ def osc52_available?
37
+ @output.respond_to?(:tty?) && @output.tty?
38
+ end
39
+
40
+ def write_osc52(content)
41
+ encoded = Base64.strict_encode64(content)
42
+ @output.print("\e]52;c;#{encoded}\a")
43
+ @output.flush if @output.respond_to?(:flush)
44
+ end
45
+
46
+ def clipboard_commands
47
+ if windows?
48
+ [["clip", ["clip"]]]
49
+ else
50
+ [
51
+ ["pbcopy", ["pbcopy"]],
52
+ ["wl-copy", ["wl-copy"]],
53
+ ["xclip", ["xclip", "-selection", "clipboard"]],
54
+ ["xsel", ["xsel", "--clipboard", "--input"]]
55
+ ]
56
+ end
57
+ end
58
+
59
+ def executable?(name)
60
+ paths = @env.fetch("PATH", "").split(File::PATH_SEPARATOR)
61
+ extensions = windows? ? @env.fetch("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(";") : [""]
62
+ paths.any? do |path|
63
+ extensions.any? do |extension|
64
+ candidate = File.join(path, name + extension)
65
+ File.file?(candidate) && File.executable?(candidate)
66
+ end
67
+ end
68
+ end
69
+
70
+ def windows?
71
+ RbConfig::CONFIG["host_os"].match?(/mswin|mingw|cygwin/i)
72
+ end
73
+
74
+ def run_command(command, content)
75
+ Open3.popen3(*command) do |stdin, _stdout, _stderr, wait_thread|
76
+ stdin.write(content)
77
+ stdin.close
78
+ wait_thread.value.success?
79
+ end
80
+ rescue StandardError
81
+ false
82
+ end
83
+ end
84
+ end