kward 0.80.1 → 0.82.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +2 -2
  5. data/Rakefile +44 -4
  6. data/doc/composer.md +6 -9
  7. data/doc/configuration.md +13 -3
  8. data/doc/files.md +8 -2
  9. data/doc/permissions.md +1 -1
  10. data/doc/releasing.md +71 -38
  11. data/doc/rpc.md +2 -1
  12. data/doc/sandboxing.md +4 -4
  13. data/doc/security.md +6 -9
  14. data/doc/shell.md +161 -196
  15. data/doc/skills.md +17 -5
  16. data/doc/tabs.md +1 -1
  17. data/doc/usage.md +6 -5
  18. data/kward.gemspec +1 -1
  19. data/lib/kward/adaptive_pty_output_sink.rb +183 -0
  20. data/lib/kward/ansi.rb +9 -1
  21. data/lib/kward/cli/commands.rb +7 -0
  22. data/lib/kward/cli/git.rb +5 -1
  23. data/lib/kward/cli/interactive_turn.rb +1 -1
  24. data/lib/kward/cli/plugins.rb +1 -1
  25. data/lib/kward/cli/project_skills.rb +99 -0
  26. data/lib/kward/cli/project_skills_commands.rb +87 -0
  27. data/lib/kward/cli/prompt_interface.rb +18 -4
  28. data/lib/kward/cli/rendering.rb +41 -3
  29. data/lib/kward/cli/runtime_helpers.rb +222 -21
  30. data/lib/kward/cli/sessions.rb +6 -4
  31. data/lib/kward/cli/settings.rb +5 -13
  32. data/lib/kward/cli/slash_commands.rb +6 -0
  33. data/lib/kward/cli/tabs.rb +62 -5
  34. data/lib/kward/cli.rb +21 -1
  35. data/lib/kward/config_files.rb +9 -4
  36. data/lib/kward/conversation.rb +8 -5
  37. data/lib/kward/ekwsh.rb +37 -16
  38. data/lib/kward/image_attachments.rb +98 -15
  39. data/lib/kward/interactive_pty_runner.rb +102 -30
  40. data/lib/kward/prompt_interface/composer_controller.rb +15 -3
  41. data/lib/kward/prompt_interface/composer_renderer.rb +27 -0
  42. data/lib/kward/prompt_interface/editor/controller.rb +10 -6
  43. data/lib/kward/prompt_interface/editor/modes/emacs.rb +2 -2
  44. data/lib/kward/prompt_interface/editor/modes/vibe.rb +2 -2
  45. data/lib/kward/prompt_interface/git_prompt.rb +1 -1
  46. data/lib/kward/prompt_interface/key_handler.rb +100 -43
  47. data/lib/kward/prompt_interface/overlay_renderer.rb +13 -0
  48. data/lib/kward/prompt_interface/project_browser.rb +162 -3
  49. data/lib/kward/prompt_interface/prompt_renderer.rb +15 -6
  50. data/lib/kward/prompt_interface/question_prompt.rb +1 -1
  51. data/lib/kward/prompt_interface/screen.rb +40 -15
  52. data/lib/kward/prompt_interface/selection_prompt.rb +5 -5
  53. data/lib/kward/prompt_interface/transcript_renderer.rb +4 -4
  54. data/lib/kward/prompt_interface.rb +167 -44
  55. data/lib/kward/prompts/commands.rb +2 -0
  56. data/lib/kward/prompts.rb +6 -6
  57. data/lib/kward/pty_output_sink.rb +45 -0
  58. data/lib/kward/pty_transcript_normalizer.rb +93 -0
  59. data/lib/kward/rpc/server.rb +1 -0
  60. data/lib/kward/session_catalog.rb +87 -0
  61. data/lib/kward/session_store.rb +97 -7
  62. data/lib/kward/skills/registry.rb +52 -2
  63. data/lib/kward/skills/trust_coordinator.rb +45 -0
  64. data/lib/kward/skills/trust_store.rb +107 -0
  65. data/lib/kward/terminal_image_support.rb +116 -0
  66. data/lib/kward/terminal_sequences.rb +43 -0
  67. data/lib/kward/version.rb +1 -1
  68. metadata +11 -4
  69. data/.github/workflows/ci.yml +0 -48
  70. data/.github/workflows/pages.yml +0 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dceefb01256daa642d6fe1d06d2e332cac1154c93072524b4aa5e98779a26807
4
- data.tar.gz: cc8b3c0f226c0b58ea5704a613f84a39d93c5da3ae4d4b9769c891aad2d704d0
3
+ metadata.gz: e4e40d786a99770eef30a7eb7fec41470d927f9d889160d1c6db9f8fd1ebec39
4
+ data.tar.gz: 1cfaf680bf9b5ef313e0a0f71a3fb33ea394eb7bc2f5d19c5bfee4c38898ac49
5
5
  SHA512:
6
- metadata.gz: 4e433643946a04c43fc833edb1a7a550d4333a4184e5c68e5a54b8ab8f24317821587176fc85337a5682fee8caa87cc0f404cf2717490338fc3d9f4988c6e3c4
7
- data.tar.gz: 3ea451ab6978d7e726270de4d0e6bc14e1c5a5b6792bb00de551cc137d0e1875579da03437eb2687d8e1d8d93e099ba87a1bfd42fdd18070ddf215fc7ad88ed7
6
+ metadata.gz: efe8e4a599791fee93d0329c7d0ec4e87c9d45c9ad9f99165d9172d44410ccc83de8014802cc76481d37952e89427b80cac1022a2f7fc17d0bd3c05356d44391
7
+ data.tar.gz: 9b1ad11cfd582d78d4834cf5b6f168ca8e2f987abe99804678d5e58c2e3000fcff6ebbf30448a71aa1f0ca7a94e9e9585be7b0643d6c88e3ed931e3ab6f3eada
data/CHANGELOG.md CHANGED
@@ -2,6 +2,68 @@
2
2
 
3
3
  All notable changes to Kward will be documented in this file.
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ## [0.82.0] - 2026-08-23
8
+
9
+ ### Added
10
+
11
+ - Added `script/release` for validated, one-command release preparation from an argument or standard input, including changelog/version updates, preflight checks, an annotated tag, and an atomic push.
12
+ - Added a tag-triggered GitHub Actions release workflow using RubyGems trusted publishing, published-artifact checksum verification, changelog-based GitHub Releases, and safe reruns after partial publication.
13
+ - Added `+` and `-` controls for resizing inline `/files` image previews within the available overlay space.
14
+ - Added inline read-only previews for PNG, JPEG, GIF, and WebP files opened from `/files` in terminals that support Kitty or iTerm2 image sequences, with local conversion for Kitty-compatible terminals when needed.
15
+
16
+ ### Changed
17
+
18
+ - Excluded GitHub workflow and local release-script files from packaged gems and added explicit release metadata and package-content checks.
19
+ - Changed interactive PTY handoff to render conservative line/progress output above a frozen composer, with a permanent switch to exclusive full-terminal passthrough when the child emits screen-oriented or unknown controls.
20
+ - Kept safe, normalized shell output as explicit tab-local transient state so completed `/shell` and `!command` output can be reconstructed after tab switches without entering session history or model context.
21
+
22
+ ### Fixed
23
+
24
+ - Made the test environment explicitly disable terminal color and made unified diff-viewer tests select that mode explicitly, keeping release preflight assertions deterministic across terminal capabilities and widths.
25
+ - Kept `/files` image previews bounded within a fully cleared, bordered file-list overlay above the visible composer and preserved their aspect ratio in Kitty-compatible terminals.
26
+ - Fixed inline image protocol detection and viewer handoff for Ghostty and other Kitty-compatible terminals by retrying inconclusive probes, honoring recognized Kitty hints, suppressing Kitty acknowledgements, and using valid, chunked graphics sequences.
27
+ - Kept synchronized-output updates such as Homebrew downloads in the inline PTY region instead of resetting the cursor through an unnecessary exclusive handoff.
28
+ - Retained the final visible state of carriage-return and horizontal-cursor progress output from commands such as `git push` and Homebrew when reconstructing the transcript after PTY handback.
29
+ - Cached rebuildable session-list summaries so opening `/session` no longer reparses every unchanged conversation log, while retaining automatic fallback for existing, changed, or corrupt session data.
30
+ - Refreshed composer status immediately after model runtime changes and `/git` returns, so selected model details and Git dirty-state color do not remain stale until another input.
31
+ - Kept echoed passwords, OTPs, and other child input out of transient tab state by stopping safe PTY output retention before the first forwarded input byte.
32
+ - Reconstructed safe transient shell output as part of the PTY ownership handback frame, avoiding an extra clear and redraw after commands such as `ls`.
33
+ - Preserved the active `/shell` prompt and composer when Ctrl+L clears transient shell output, and avoided a redundant preliminary redraw.
34
+ - Reconstructed the complete Kward screen after interactive PTY handoff, including failure paths, instead of relying on the child process cursor position.
35
+
36
+ ## [0.81.0] - 2026-08-20
37
+
38
+ ### Added
39
+
40
+ - Added `capture <command>` inside `/shell` and `/capture <command>` in the normal composer for bounded, sanitized, transcript-friendly command execution.
41
+ - Added Tab completion for shell commands and paths when the normal interactive prompt input begins with `!`.
42
+ - Added workspace-scoped, digest-aware project skill trust decisions with interactive Allow, Deny, and Review prompts plus `/skills` and `kward skills` management commands.
43
+
44
+ ### Changed
45
+
46
+ - Displayed packaged plugins with their folder name in the startup plugin list, such as `folder/plugin.rb`.
47
+ - Added a frozen composer display for line-oriented Git PTY commands while preserving the full-terminal handoff for full-screen programs.
48
+ - Changed external `/shell` commands and one-shot `!command` input to use interactive PTY terminal handoff by default, while retaining `pty` and `/pty` for compatibility.
49
+ - Made aliases configured in `ekwsh.yml` available to leading-`!` execution and command completion.
50
+ - Removed interactive PTY start and exit-status messages while retaining the submitted command echo.
51
+ - Changed shell and leading-`!` completion so the candidate list appears in an interactive overlay while repeated Tab presses cycle through candidates in the composer.
52
+ - Removed the `Tab` and `Shift+Tab` reasoning-effort shortcuts so the composer keeps its normal completion behavior.
53
+
54
+ ### Fixed
55
+
56
+ - Refreshed composer status immediately after reasoning selection and interactive PTY commands, so reasoning levels and Git branch state no longer remain stale until the next input.
57
+ - Prevented composer flicker during interactive prompt, bang-command, and slash-command submission by deferring the submitted composer's repaint to the next command state.
58
+ - Preserved the busy composer while writing normal-turn transcript output so user-submission rendering does not clear it.
59
+ - Bound CSI-u `Ctrl+Q` input to close the read-only `/git diff` viewer, matching the existing raw-key binding and ESC behavior.
60
+ - Routed leading-`!` commands and aliases resolving to `kward edit <filename>` into the integrated editor instead of spawning a nested Kward process.
61
+ - Preserved safe, line-oriented PTY output such as `ls` by redrawing it into the transient transcript when returning from `/shell`, `!command`, or `/pty`, while leaving full-screen and keyboard-driven sessions terminal-owned.
62
+ - Removed legacy `pty` and `capture` mode markers from configured aliases expanded by `!command`, preventing valid aliases such as `glog: pty git log` from failing with status 127.
63
+ - Hardened interactive PTY cleanup, final-output draining, input EOF handling, resize propagation, and stopped-child recovery so terminal handoff reliably returns control to Kward.
64
+ - Normalized binary-tagged UTF-8 shell output before transcript rendering, preventing interactive `!` commands such as `tree` from crashing the composer.
65
+ - Deferred interactive warning output until startup transcript replay completes, keeping Runtime diagnostics visible instead of immediately overwriting them with the replayed transcript.
66
+
5
67
  ## [0.80.1] - 2026-07-27
6
68
 
7
69
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kward (0.80.1)
4
+ kward (0.82.0)
5
5
  base64
6
6
  nokogiri
7
7
  tiktoken_ruby
@@ -146,7 +146,7 @@ CHECKSUMS
146
146
  html-proofer (5.2.1) sha256=fdd958a7cbf9c3255fb96fe7cfc4e611f64e2706e469488a3326309ad007d2fd
147
147
  io-event (1.16.2) sha256=9f9cb0a96ea5c3850a672606c65f27bc96d7621399ef6196acbfe2be0cd1279c
148
148
  json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a
149
- kward (0.80.1)
149
+ kward (0.82.0)
150
150
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
151
151
  metrics (0.15.0) sha256=61ded5bac95118e995b1bc9ed4a5f19bc9814928a312a85b200abbdac9039072
152
152
  minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
data/README.md CHANGED
@@ -72,7 +72,7 @@ Work safely:
72
72
  Everyday workflows:
73
73
 
74
74
  - [Sessions](doc/session-management.md): resume, clone, fork, rewind, compact, and navigate saved work.
75
- - [Interactive composer](doc/composer.md): use multiline input, completion, history, files, reasoning shortcuts, busy input, and images.
75
+ - [Interactive composer](doc/composer.md): use multiline input, completion, history, files, reasoning selection, busy input, and images.
76
76
  - [Tabs](doc/tabs.md): keep several conversations open and run work in another tab.
77
77
  - [Project files](doc/files.md): browse, search, mention, open, and edit workspace files.
78
78
  - [Integrated editor](doc/editor.md): open files from the shell or composer, edit in-place, and choose editor keybindings.
@@ -92,7 +92,7 @@ Extend and integrate:
92
92
  - [Plugins](doc/plugins.md): trusted Ruby plugins for commands, footer UI, prompt context, transcript events, and RPC clients.
93
93
  - [Lifecycle hooks](doc/lifecycle-hooks.md): deterministic runtime hooks for policy, approvals, automation, and command-hook integrations.
94
94
  - [RPC protocol](doc/rpc.md): JSON-RPC backend mode for trusted local UI clients.
95
- - [Releasing](doc/releasing.md): release checklist for RubyGems publishing.
95
+ - [Releasing](doc/releasing.md): prepare a version and publish it through RubyGems and GitHub Releases.
96
96
 
97
97
  Reference guides:
98
98
 
data/Rakefile CHANGED
@@ -1,5 +1,7 @@
1
+ require "bundler/gem_tasks"
1
2
  require "fileutils"
2
3
  require "html-proofer"
4
+ require "open3"
3
5
  require "rdoc/task"
4
6
  require "rubygems/package"
5
7
  require "webrick"
@@ -33,6 +35,36 @@ def packaged_gem_files(gem_name)
33
35
  gem.spec.files.sort
34
36
  end
35
37
 
38
+ def verify_release_metadata
39
+ version = Kward::VERSION
40
+ tag = "v#{version}"
41
+ changelog = File.read("CHANGELOG.md")
42
+ abort("CHANGELOG.md is missing a [#{version}] release heading") unless changelog.match?(/^## \[#{Regexp.escape(version)}\] - \d{4}-\d{2}-\d{2}$/)
43
+
44
+ if ENV["GITHUB_REF_TYPE"] == "tag" && ENV["GITHUB_REF_NAME"] != tag
45
+ abort("GitHub tag #{ENV["GITHUB_REF_NAME"]} does not match gem version #{version}")
46
+ end
47
+
48
+ tag_commit, tag_status = Open3.capture2e("git", "rev-parse", "--verify", "#{tag}^{commit}")
49
+ return unless tag_status.success?
50
+
51
+ head_commit = `git rev-parse HEAD`.strip
52
+ abort("Tag #{tag} does not point at HEAD") unless tag_commit.strip == head_commit
53
+ end
54
+
55
+ def verify_packaged_gem(gem_name)
56
+ files = packaged_gem_files(gem_name)
57
+ required = ["exe/kward", "lib/kward/version.rb"]
58
+ missing = required - files
59
+ abort("Packaged gem is missing: #{missing.join(", ")}") unless missing.empty?
60
+
61
+ forbidden_prefixes = [".github/", "script/", "test/", "plan/"]
62
+ forbidden = files.select { |file| forbidden_prefixes.any? { |prefix| file.start_with?(prefix) } }
63
+ abort("Packaged gem includes development files: #{forbidden.join(", ")}") unless forbidden.empty?
64
+
65
+ files
66
+ end
67
+
36
68
  def rewrite_yard_markdown_links
37
69
  guide_names = Dir.glob("doc/*.md").map { |path| File.basename(path, ".md") }
38
70
 
@@ -75,15 +107,23 @@ YARD::Rake::YardocTask.new do |yard|
75
107
  end
76
108
 
77
109
  namespace :release do
110
+ desc "Verify the version, changelog, and release tag agree"
111
+ task :verify do
112
+ verify_release_metadata
113
+ end
114
+
78
115
  desc "Run release checks and build a local gem"
79
- task preflight: [:test, "docs:check"] do
80
- gem_name = "kward-#{Kward::VERSION}.gem"
116
+ task preflight: ["release:verify", :test, "docs:check"] do
117
+ gem_name = File.join("pkg", "kward-#{Kward::VERSION}.gem")
81
118
  FileUtils.rm_f(gem_name)
82
- sh "gem", "build", "kward.gemspec"
83
- puts packaged_gem_files(gem_name)
119
+ Rake::Task["build"].invoke
120
+ puts verify_packaged_gem(gem_name)
84
121
  end
85
122
  end
86
123
 
124
+ Rake::Task["release:source_control_push"].enhance(["release:verify"])
125
+ Rake::Task["release:rubygem_push"].enhance(["release:verify"])
126
+
87
127
  namespace :docs do
88
128
  desc "Serve the built YARD documentation site locally and rebuild on changes"
89
129
  task serve: :build do
data/doc/composer.md CHANGED
@@ -38,7 +38,7 @@ The composer supports familiar line-editing keys:
38
38
  | `Alt+D` | Delete the word after the cursor |
39
39
  | `Ctrl+U` / `Ctrl+K` | Kill from the cursor to the start or end of the line |
40
40
  | `Ctrl+Y` | Yank the most recently killed composer text |
41
- | `Ctrl+L` | Redraw the terminal UI |
41
+ | `Ctrl+L` | Redraw the terminal UI and remove transient current-view output |
42
42
  | `Ctrl+D` | Delete at the cursor, or exit when the composer is empty |
43
43
 
44
44
  These are composer bindings. The integrated editor, shell, Git view, file browser, and plugin interfaces have their own keymaps.
@@ -115,15 +115,12 @@ History survives restarts. It contains submitted prompt text and successful `$pa
115
115
 
116
116
  ## Change reasoning effort
117
117
 
118
- When no slash or file picker is open, Tab cycles forward through reasoning efforts supported by the current model. Shift+Tab cycles backward. The selection wraps and is persisted for later turns.
118
+ The composer status line shows the current provider, model, and reasoning effort. Use `/reasoning` or `/model` to choose the reasoning effort explicitly.
119
119
 
120
- The composer status line shows the current provider, model, and reasoning effort. Use `/reasoning` or `/model` when you prefer the explicit picker.
121
-
122
- Tab is context-sensitive:
120
+ Tab follows the normal completion behavior:
123
121
 
124
122
  - in slash or `@path` completion, it accepts the selected match,
125
123
  - in `$path` completion, it opens the selected file,
126
- - otherwise, it changes reasoning effort,
127
124
  - in other modal interfaces, it follows that interface's own controls.
128
125
 
129
126
  ## Work with tabs
@@ -189,7 +186,7 @@ Example:
189
186
 
190
187
  If the cursor is at the start of an otherwise empty draft, Backspace removes the most recently added pending attachment. Submitting the draft clears its pending attachments.
191
188
 
192
- The active model must support image input. iTerm2 and Kitty-compatible terminals can render submitted images inline; other terminals still show attachment badges and send the image to the model without an inline preview.
189
+ The active model must support image input. iTerm2 and Kitty-compatible terminals can render submitted images inline; Kward uses active Kitty capability detection when running on a real TTY and falls back to conservative terminal hints otherwise. Other terminals still show attachment badges and send the image to the model without an inline preview.
193
190
 
194
191
  ## Understand composer status
195
192
 
@@ -214,6 +211,6 @@ If rendering becomes corrupted after resize or after an external program writes
214
211
  /redraw
215
212
  ```
216
213
 
217
- You can also press `Ctrl+L`. If a shortcut never reaches Kward, prefer its slash-command equivalent or change `composer.tab_keybindings` in `/settings`.
214
+ You can also press `Ctrl+L`; it rebuilds the durable current-tab view without deleting the conversation transcript, removing transient shell output. If a shortcut never reaches Kward, prefer its slash-command equivalent or change `composer.tab_keybindings` in `/settings`.
218
215
 
219
- For full-screen interactive programs such as Vim or `less`, use `/pty <command>` or `pty <command>` inside `/shell` so the program temporarily owns the terminal instead of fighting the composer.
216
+ External commands inside `/shell` and one-shot `!command` input receive an interactive PTY by default. They begin inline above a frozen composer, then switch to full-terminal passthrough if they emit screen-oriented or unknown terminal controls; full-screen programs such as Vim or `less` can therefore temporarily own the terminal. Use `capture <command>` inside `/shell` or `/capture <command>` from the normal composer for bounded, sanitized transcript output.
data/doc/configuration.md CHANGED
@@ -81,7 +81,17 @@ long-polling adapter and setup instructions.
81
81
 
82
82
  ### Project skills
83
83
 
84
- Kward loads user-level skills but skips project-level skills by default. Enable them only for repositories you trust, either through `/settings` Tools & Search Trust project skills or with:
84
+ Kward loads user-level skills automatically. Project-level skills require an explicit workspace trust decision. In the interactive TUI, Kward asks when a new or changed skill appears; use `/skills status`, `/skills trust`, `/skills untrust`, and `/new` to inspect, manage, and activate decisions.
85
+
86
+ For non-interactive use:
87
+
88
+ ```bash
89
+ kward --working-directory /path/to/project skills status
90
+ kward --working-directory /path/to/project skills review
91
+ kward --working-directory /path/to/project skills trust
92
+ ```
93
+
94
+ Trust records are stored in `~/.kward/trusted_project_skills.json`. The legacy global override remains available:
85
95
 
86
96
  ```json
87
97
  {
@@ -176,7 +186,7 @@ aliases:
176
186
 
177
187
  `env` values are applied when shell mode starts, after Kward's conservative color defaults. Keys must look like environment variable names (`A_Z`, digits after the first character, and underscores); invalid keys are ignored. Values are converted to strings.
178
188
 
179
- `aliases` expand the first word of a command once. For example, `ll lib` runs `ls -la lib`. Built-in `ekwsh` commands such as `cd`, `pwd`, `export`, `unset`, `alias`, `clear`, `pty`, and `exit` take precedence over aliases. Run `alias` inside `ekwsh` to list configured aliases. Aliases are also included in command-name Tab completion.
189
+ `aliases` expand the first word of a command once. For example, `ll lib` runs `ls -la lib`. Configured aliases are available both inside `/shell` and after the normal composer's `!` prefix, including command-name Tab completion. Built-in `ekwsh` commands such as `cd`, `pwd`, `export`, `unset`, `alias`, `capture`, `clear`, `pty`, and `exit` take precedence over aliases inside `/shell`. External commands receive an interactive PTY by default. An alias value can begin with `capture` when its `/shell` output should use the configured timeout, output limit, and transcript sanitization. Leading-`!` alias invocations are always interactive, so Kward removes a leading `capture` or legacy `pty` mode marker from the expanded alias before execution. Run `alias` inside `ekwsh` to list configured aliases. Aliases created at runtime with that built-in belong only to the current `/shell` session and are not available to `!command` input.
180
190
 
181
191
  ## Provider and model settings
182
192
 
@@ -355,7 +365,7 @@ The busy composer shows a short Ctrl+C cancellation hint by default. To hide it:
355
365
 
356
366
  This only hides the hint text; Ctrl+C still stops the current running response.
357
367
 
358
- In the normal composer prompt, `Tab` cycles forward through the current model's reasoning efforts and `Shift+Tab` cycles backward. The shortcuts wrap around and update the persisted reasoning setting; file and slash-command completion overlays keep their existing `Tab` completion behavior.
368
+ In the normal composer prompt, choose reasoning effort with `/reasoning` or `/model`; `Tab` keeps its normal file and slash-command completion behavior.
359
369
 
360
370
  `tab_keybindings` controls how the composer handles tab navigation shortcuts:
361
371
 
data/doc/files.md CHANGED
@@ -12,9 +12,9 @@ From an interactive Kward session, run:
12
12
  /files
13
13
  ```
14
14
 
15
- Kward opens the project file browser. Use the arrow keys or `j`/`k` to move through the tree, then press `Enter` on a file to open it in the integrated editor.
15
+ Kward opens the project file browser. Use the arrow keys or `j`/`k` to move through the tree, then press `Enter` on a file to open it in the integrated editor. Supported images (PNG, JPEG, GIF, and WebP) open as read-only inline previews when the terminal supports Kitty or iTerm2 image sequences.
16
16
 
17
- When you quit the editor, Kward returns to the file browser at the same position so you can keep browsing nearby files.
17
+ When you quit the editor or close an image preview, Kward returns to the file browser at the same position so you can keep browsing nearby files.
18
18
 
19
19
  ## What appears in the browser
20
20
 
@@ -40,6 +40,8 @@ Outside Git, Kward scans the workspace directory and skips common noisy director
40
40
  | `/` | Start search |
41
41
  | `Backspace` | Delete the last search character |
42
42
  | `Esc` | Leave search; press again to close the browser |
43
+ | `Q` | Close an image preview |
44
+ | `+` / `-` | Zoom an image preview in / out |
43
45
  | `@` | Insert the selected file as an `@path` mention |
44
46
 
45
47
  Directories use `▸` and `▾` markers to show collapsed and expanded state. Files are shown under their containing directory with indentation. File-type icons are off by default; users with a compatible Nerd Font can enable them under Interface in `/settings`. See [Configuration](configuration.md#project-browser-icons).
@@ -92,6 +94,10 @@ Kward remembers the expanded folders and selected path for each workspace. The n
92
94
 
93
95
  Search itself is temporary. Closing search returns to the normal tree, and closing the browser leaves your chat session intact.
94
96
 
97
+ ## Image previews
98
+
99
+ Image previews are read-only and replace the file-list overlay while leaving the prompt composer and tabs visible. Press `+` or `-` to make the image larger or smaller within the available overlay space. Press `Esc` or `Q` to return to `/files`. Kward probes Kitty-compatible terminals when it has a real interactive TTY, uses recognized terminal identity hints when probing is inconclusive, and retries transient detection failures. Terminals without reachable Kitty or iTerm2 inline-image support keep the selection in the browser and show a status message instead of opening binary data in the text editor. Kitty-compatible terminals use PNG data; JPEG, GIF, and WebP previews require an available local image converter.
100
+
95
101
  ## Notes and limitations
96
102
 
97
103
  - `/files` is only available in the interactive prompt.
data/doc/permissions.md CHANGED
@@ -175,7 +175,7 @@ The policy runs before Kward dispatches a model-requested tool. It does not cons
175
175
 
176
176
  - a permitted shell command can access files, processes, credentials, and network services available to your user account;
177
177
  - command-text rules are useful review controls, not a reliable way to enforce network destinations or all subprocess behavior;
178
- - direct commands that you type yourself—`!command`, `/shell`, and `/pty`—are treated as your actions and are outside this first policy scope;
178
+ - direct commands that you type yourself—`!command`, `/capture`, `/shell`, and `/pty`—are treated as your actions and are outside this first policy scope;
179
179
  - plugins, hooks, and MCP servers are trusted local extensions with their own process access.
180
180
 
181
181
  For sensitive work, use a restricted operating-system account, container, virtual machine, or disposable checkout. [Command sandboxing](sandboxing.md) is a separate, opt-in operating-system boundary for model-requested `run_shell_command` workers. It can add filesystem and child-network restrictions, but does not cover Kward's host process, extensions, or direct interactive commands.
data/doc/releasing.md CHANGED
@@ -1,74 +1,107 @@
1
1
  # Releasing Kward
2
2
 
3
- ## Before you begin
3
+ Kward releases are prepared locally and published by GitHub Actions. The local command updates the version and changelog, runs every release check, creates the release commit and annotated tag, then pushes both atomically. The tag-triggered workflow publishes the gem to RubyGems.org and creates a GitHub Release with the same gem attached.
4
4
 
5
- Kward requires Ruby >= 3.4 (`spec.required_ruby_version` in `kward.gemspec`). If you develop with a newer Ruby, verify tests pass against the minimum supported version before releasing.
5
+ ## One-time setup
6
6
 
7
- ## Prepare the release
7
+ Kward publishes through [RubyGems trusted publishing](https://guides.rubygems.org/trusted-publishing/), so the repository does not need a long-lived RubyGems API key.
8
8
 
9
- Before publishing:
9
+ Before the first automated release:
10
10
 
11
- 1. Update `CHANGELOG.md` for the version. Move `[Unreleased]` entries under a new version heading.
12
- 2. Update `Kward::VERSION` in `lib/kward/version.rb`.
13
- 3. Run the release preflight:
11
+ 1. In the GitHub repository, create an Actions environment named `release`. Add required reviewers if releases should have a manual approval gate.
12
+ 2. On the RubyGems.org page for the `kward` gem, add a trusted publisher with:
13
+ - Repository owner: `kaiwood`
14
+ - Repository name: `kward`
15
+ - Workflow filename: `release.yml`
16
+ - Environment: `release`
17
+ 3. Confirm GitHub Actions can write repository contents. The release job requests only `contents: write` for the GitHub Release and `id-token: write` for RubyGems OIDC authentication.
14
18
 
15
- ```bash
16
- bundle exec rake release:preflight
17
- ```
19
+ GitHub Packages is intentionally not used. RubyGems.org remains the canonical package registry; the built `.gem` is also attached to each GitHub Release.
18
20
 
19
- This runs the full test suite, builds and checks the generated docs, builds a local gem, and prints the packaged file list for review.
21
+ ## Prepare a release
20
22
 
21
- 4. Run focused tests for any areas you changed during the release prep itself (docs, config, etc.):
23
+ Keep notable changes under the appropriate `Added`, `Changed`, `Fixed`, or `Removed` heading in the `[Unreleased]` section of `CHANGELOG.md`. Do not add the version heading by hand.
22
24
 
23
- ```bash
24
- ruby -Itest test/test_cli.rb
25
- ```
25
+ Before releasing, make sure `main` is clean, pushed, and synchronized with `origin/main`. Then run:
26
26
 
27
- 5. Preview docs locally if you changed documentation or public APIs:
27
+ ```bash
28
+ script/release 0.82.0
29
+ ```
30
+
31
+ The version can also come from standard input:
32
+
33
+ ```bash
34
+ printf '0.82.0\n' | script/release
35
+ ```
36
+
37
+ The command fails closed unless:
28
38
 
29
- ```bash
30
- bundle exec rake docs:serve
31
- ```
39
+ - The version is a valid RubyGems version newer than `Kward::VERSION`.
40
+ - The working tree is clean and checked out on `main`.
41
+ - Local `main` exactly matches `origin/main`.
42
+ - The tag does not exist locally or on GitHub.
43
+ - RubyGems.org does not already contain the version.
44
+ - `[Unreleased]` contains at least one changelog entry.
32
45
 
33
- The preview builds `_yardoc/`, serves it with WEBrick, and rebuilds in a fresh process when documentation sources, library code, or templates change. Refresh your browser after rebuilds.
46
+ If validation succeeds, it:
34
47
 
35
- 6. If you want to run the documentation check separately:
48
+ 1. Updates `Kward::VERSION` in `lib/kward/version.rb`.
49
+ 2. Refreshes `Gemfile.lock` with `bundle lock --local`.
50
+ 3. Moves the unreleased changelog entries under a dated version heading.
51
+ 4. Runs `bundle exec rake release:preflight`.
52
+ 5. Commits the three release files as `Release v0.82.0`.
53
+ 6. Creates an annotated `v0.82.0` tag.
54
+ 7. Atomically pushes `main` and the tag to `origin`.
36
55
 
37
- ```bash
38
- bundle exec rake docs:build
39
- bundle exec rake docs:check
40
- ```
56
+ If preparation fails before the commit, the command restores the version, lockfile, and changelog. If tagging or pushing fails after the commit, it leaves the release commit in place and prints the failed command; inspect the repository before retrying rather than creating another release commit.
41
57
 
42
- `docs:check` validates generated internal links, images, and scripts. Pushes to `main` deploy the generated YARD site to GitHub Pages. You can also run `bundle exec rake rdoc` to generate a separate RDoc site as a sanity check, but it is not deployed.
58
+ ## What the release workflow does
43
59
 
44
- 7. Inspect the packaged files printed by `release:preflight` and confirm no local config, sessions, logs, or secrets are included. The gemspec uses `git ls-files` and excludes `test/`, `plan/`, `.ruby-lsp/`, `.gitignore`, and `AGENTS.md`.
60
+ A pushed `v*` tag starts `.github/workflows/release.yml`. The workflow:
45
61
 
46
- 8. Install the built gem locally and smoke test the `kward` executable in a clean workspace.
62
+ 1. Checks that the tag, gem version, and changelog heading agree.
63
+ 2. Runs the full test suite and generated-documentation checks against Ruby 3.4.
64
+ 3. Builds the gem and verifies its packaged files.
65
+ 4. Publishes through RubyGems trusted publishing.
66
+ 5. Verifies the local gem checksum against the artifact served by RubyGems.org.
67
+ 6. Creates `Kward VERSION` as a GitHub Release using that version's changelog section and attaches the verified gem.
47
68
 
48
- ## Tag and publish
69
+ The publishing job uses the protected `release` environment. If RubyGems.org already has the version after a partially completed workflow, a rerun rebuilds the gem and verifies that it exactly matches the published checksum before continuing. For an existing GitHub Release, the workflow downloads and compares the gem, uploads it when missing, and publishes an unfinished draft. This makes normal workflow reruns safe without silently replacing mismatched artifacts.
49
70
 
50
- Commit the version bump and create a Git tag:
71
+ Follow the run from the repository's **Actions Release** page. Installation can be checked after publication with:
51
72
 
52
73
  ```bash
53
- git commit -am "Bump to VERSION"
54
- git tag vVERSION
55
- git push && git push --tags
74
+ gem install kward --version 0.82.0
75
+ kward --version
56
76
  ```
57
77
 
58
- Publish the built gem from the release checkout:
78
+ ## Run checks without releasing
79
+
80
+ After updating to an unreleased version, run the complete preflight directly with:
81
+
82
+ ```bash
83
+ bundle exec rake release:preflight
84
+ ```
85
+
86
+ This verifies release metadata, runs tests, builds and checks the generated documentation, builds `pkg/kward-VERSION.gem`, rejects development-only packaged files, and prints the final gem contents.
87
+
88
+ Individual checks remain available:
59
89
 
60
90
  ```bash
61
- gem push kward-VERSION.gem
91
+ bundle exec rake test
92
+ bundle exec rake docs:check
93
+ bundle exec rake release:verify
94
+ bundle exec rake build
62
95
  ```
63
96
 
64
- RubyGems MFA is required for publishing. Prefer RubyGems trusted publishing for automated releases if CI publishing is added later, so long-lived API keys do not need to be stored in CI secrets.
97
+ Use `bundle exec rake docs:serve` to preview documentation locally.
65
98
 
66
99
  ## If you need to yank a release
67
100
 
68
- If a published gem has a serious problem, you can yank it within 24 hours of pushing:
101
+ If a published gem has a serious problem, yank it within 24 hours of pushing:
69
102
 
70
103
  ```bash
71
104
  gem yank kward --version VERSION
72
105
  ```
73
106
 
74
- Yanking removes the gem from the default install index but does not delete the version entirely. After yanking, fix the issue, bump the version, and release again.
107
+ Yanking removes the gem from the default install index but does not delete the version. Fix the issue, choose a new version, and run the normal release command again. Never move or reuse a published version tag.
data/doc/rpc.md CHANGED
@@ -76,6 +76,7 @@ Read `capabilities` at runtime instead of assuming every feature is available. I
76
76
  - `memory`: opt-in structured memory support, interactive prompt injection only, JSON/JSONL local storage, and dedicated `memory/*` methods.
77
77
  - `commands`: supported `commands/list` capability for prompt, skill, and plugin command sources, plus plugin execution through `commands/run` or plugin slash turns.
78
78
  - `skillCapture`: capture a reviewed personal `SKILL.md` from any saved session’s active branch through `skills/captureSessions`, `skills/captureDraft`, and `skills/saveCapturedDraft`.
79
+ - `projectSkillTrust`: explicitly unsupported over RPC. RPC clients cannot answer the interactive Allow/Deny/Review decision, so project skills remain skipped unless the global `skills.trust_project` override is enabled.
79
80
  - `mcp`: local stdio MCP server support through the shared `mcpServers` config. RPC exposes MCP tools to turns and advertises discovery with `methods: ["tools/list", "mcp/status"]`, `toolMetadata: true`, and `serverStatus: true`. It does not support MCP resources, prompts, sampling, or Streamable HTTP.
80
81
  - `startupResources`: supported startup resource listing for context, skills, prompts, and plugins.
81
82
  - `extensionUi`: question bridge support via `ui/question` and `ui/answerQuestion`, plus plugin footer updates via `ui/footer`; other UI primitives are explicitly unsupported.
@@ -83,7 +84,7 @@ Read `capabilities` at runtime instead of assuming every feature is available. I
83
84
  - `security`: trusted-local behavior and optional per-turn tool approval. By default, RPC turns have no workspace mutation guard or tool approval, so shell commands and file changes can run. Clients can inspect file-tool guardrails through `capabilities.events.tools.workspaceGuardrails` and `runtime/state.workspaceGuardrailsEnabled`. `security.sandbox` reports the command sandbox mode, enforcement backend, and filesystem and network capabilities; session pinning and one-time elevation are unsupported. See [Command sandboxing](sandboxing.md) for the boundary and its limits.
84
85
  - `export`: supported transcript export formats. Currently `markdown` and `html`; default is `markdown`.
85
86
  - `starterPack`: explicitly unsupported (`supported: false`, reason `cliOnlyInstallCommand`). Use `kward init` from the shell.
86
- - `shell`: explicitly unsupported (`supported: false`, reason `interactiveTuiOnly`) because `/shell` is the local embedded TUI shell.
87
+ - `shell`: explicitly unsupported (`supported: false`, reason `interactiveTuiOnly`) because `/shell`, `!command`, `/capture`, and PTY handoff require the local TUI. Model-requested `run_shell_command` tools remain bounded, captured, and noninteractive over RPC.
87
88
  - `logging`: local redacted telemetry, its directory and enabled categories, `logging/stats` and `logging/tokenCsv`, bucketed `usageCsv` support, JSONL storage, and 10 MB rotation with manual retention. Logs contain redacted metadata only. Configuration uses the `logging` key and `KWARD_LOGGING` environment prefix.
88
89
 
89
90
  ### `shutdown`
data/doc/sandboxing.md CHANGED
@@ -77,9 +77,9 @@ worker. The strict agent also disables configured MCP clients and lifecycle
77
77
  hooks because they run in the Kward host process rather than inside the command
78
78
  sandbox.
79
79
 
80
- This does not contain the interactive `/shell`, `!command`, or `/pty` features;
81
- those are user-directed host-process operations. Generic model-requested shell
82
- commands still cannot write Git metadata. Active worktree tabs additionally
80
+ This does not contain the user-directed `/shell`, `!command`, `/capture`, or
81
+ `/pty` features; those are user-directed host-process operations. Generic
82
+ model-requested shell commands still cannot write Git metadata. Active worktree tabs additionally
83
83
  expose a narrow `git_commit` tool for explicit agent-requested commits; it runs
84
84
  through the trusted host-side Git workflow rather than widening the shell
85
85
  sandbox. The interactive `/git` flow remains available for manual review and
@@ -111,7 +111,7 @@ It does not sandbox:
111
111
  - the Kward Ruby host process;
112
112
  - model-provider, search-provider, or RPC traffic;
113
113
  - trusted Ruby plugins;
114
- - MCP servers, lifecycle hooks, `/shell`, `!command`, or `/pty`.
114
+ - MCP servers, lifecycle hooks, `/shell`, `!command`, `/capture`, or `/pty`.
115
115
 
116
116
  Sandboxed command workers receive a minimal environment: Kward preserves only
117
117
  basic terminal, locale, and path variables, then supplies a private `HOME` and
data/doc/security.md CHANGED
@@ -56,8 +56,9 @@ Kward's built-in file tools normally resolve paths inside the active workspace.
56
56
 
57
57
  These protections reduce accidental edits. They do not contain the whole process:
58
58
 
59
- - With `sandbox.mode: off` (the default), `run_shell_command`, `!command`, `/shell`, and `/pty` run with your user permissions.
60
- - A non-off [command sandbox](sandboxing.md) restricts only model-requested `run_shell_command` workers and their descendants. It does not cover `!command`, `/shell`, or `/pty`.
59
+ - With `sandbox.mode: off` (the default), `run_shell_command`, `!command`, `/capture`, `/shell`, and `/pty` run with your user permissions.
60
+ - A non-off [command sandbox](sandboxing.md) restricts only model-requested `run_shell_command` workers and their descendants. It does not cover `!command`, `/capture`, `/shell`, or `/pty`.
61
+ - External `/shell` commands, `!command`, and `/pty` receive an interactive PTY. Kward forwards a conservative set of line-oriented controls into the inline region, then grants full-terminal passthrough when a child emits screen-oriented or unknown controls. Full passthrough bypasses transcript control-sequence sanitization, so run only commands you trust with terminal access. `capture <command>` and `/capture <command>` sanitize their captured output.
61
62
  - Plugins, command hooks, and MCP servers are local processes with the same general operating-system access.
62
63
  - Read-before-edit applies to Kward's file tools, not to arbitrary shell commands or extension code.
63
64
 
@@ -67,7 +68,7 @@ You can disable the file boundary with `tools.workspace_guardrails: false`, but
67
68
 
68
69
  A worktree-backed tab uses its linked worktree as the active workspace and requires an OS-enforced `workspace_write` command sandbox. If the platform cannot provide filesystem enforcement, Kward refuses to activate the worktree rather than running model-requested shell commands unrestricted.
69
70
 
70
- The strict worktree agent disables configured MCP clients and lifecycle hooks for that tab because those extensions run outside the command-worker sandbox. The interactive `/shell`, `!command`, and `/pty` features remain host-process features and are not covered by the worktree boundary. Use them only when you intentionally want to run a user-directed command outside the model command sandbox.
71
+ The strict worktree agent disables configured MCP clients and lifecycle hooks for that tab because those extensions run outside the command-worker sandbox. The interactive `/shell`, `!command`, `/capture`, and `/pty` features remain host-process features and are not covered by the worktree boundary. Use them only when you intentionally want to run a user-directed command outside the model command sandbox.
71
72
 
72
73
  Git worktrees share repository metadata. Kward keeps Git metadata protection enabled for generic model-requested commands. Active worktree tabs provide a narrow host-side `git_commit` tool for explicit agent commits; the interactive `/git` flow remains the manual review, staging, and commit path.
73
74
 
@@ -95,13 +96,9 @@ A repository-level `AGENTS.md` is guidance for the model, not executable code. K
95
96
 
96
97
  ### Project skills
97
98
 
98
- Project skills under `.kward/skills/` and `.agents/skills/` are skipped by default. Enable them only after reviewing their `SKILL.md` instructions and supporting files:
99
+ Project skills under `.kward/skills/` and `.agents/skills/` require an explicit trust decision. In the interactive TUI, Kward asks for `Allow`, `Deny`, or `Review` when a new or changed skill appears. Review shows bounded `SKILL.md` content and lists referenced resources without executing them. Decisions are stored per canonical workspace and skill digest in `~/.kward/trusted_project_skills.json`, so changes require another review.
99
100
 
100
- ```text
101
- /settings → Tools & Search → Trust project skills
102
- ```
103
-
104
- This setting trusts project skills generally; it is not a per-repository digest. A skill's `allowed-tools` metadata does not grant permissions or constrain tools in Kward.
101
+ Use `/skills status`, `/skills trust`, `/skills untrust`, or the non-interactive `kward skills` command to manage the records. The legacy `skills.trust_project` setting remains a global override. A skill's `allowed-tools` metadata does not grant permissions or constrain tools in Kward.
105
102
 
106
103
  ### Workspace hooks
107
104