kward 0.82.0 → 0.83.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/Gemfile.lock +2 -2
  4. data/doc/agent-tools.md +1 -0
  5. data/doc/composer.md +1 -1
  6. data/doc/configuration.md +18 -3
  7. data/doc/editor.md +20 -10
  8. data/doc/files.md +4 -3
  9. data/doc/permissions.md +1 -0
  10. data/doc/releasing.md +12 -4
  11. data/doc/rpc.md +2 -2
  12. data/doc/sandboxing.md +5 -1
  13. data/doc/security.md +1 -1
  14. data/doc/shell.md +40 -16
  15. data/doc/tabs.md +3 -0
  16. data/doc/usage.md +3 -2
  17. data/lib/kward/agent.rb +1 -0
  18. data/lib/kward/auth/anthropic_oauth.rb +7 -7
  19. data/lib/kward/cli/interactive_turn.rb +101 -19
  20. data/lib/kward/cli/runtime_helpers.rb +156 -14
  21. data/lib/kward/cli/slash_commands.rb +12 -1
  22. data/lib/kward/cli/tabs.rb +12 -5
  23. data/lib/kward/cli.rb +10 -0
  24. data/lib/kward/compaction/token_estimator.rb +12 -6
  25. data/lib/kward/config_files.rb +17 -0
  26. data/lib/kward/editor_prompt.rb +46 -0
  27. data/lib/kward/editor_prompt_session.rb +28 -0
  28. data/lib/kward/ekwsh.rb +70 -10
  29. data/lib/kward/model/client.rb +1 -17
  30. data/lib/kward/model/model_info.rb +3 -2
  31. data/lib/kward/model/payloads.rb +0 -2
  32. data/lib/kward/persistent_shell_session.rb +750 -0
  33. data/lib/kward/project_files.rb +18 -5
  34. data/lib/kward/prompt_interface/composer_renderer.rb +1 -1
  35. data/lib/kward/prompt_interface/editor/auto_indent.rb +3 -0
  36. data/lib/kward/prompt_interface/editor/controller.rb +69 -0
  37. data/lib/kward/prompt_interface/editor/modes/modern.rb +4 -0
  38. data/lib/kward/prompt_interface/editor/modes/vibe.rb +10 -4
  39. data/lib/kward/prompt_interface/editor/renderer.rb +1 -0
  40. data/lib/kward/prompt_interface/editor/state.rb +11 -0
  41. data/lib/kward/prompt_interface/editor/word_completion.rb +124 -0
  42. data/lib/kward/prompt_interface/file_overlay.rb +21 -7
  43. data/lib/kward/prompt_interface/key_handler.rb +8 -0
  44. data/lib/kward/prompt_interface/project_browser.rb +30 -5
  45. data/lib/kward/prompt_interface/runtime_state.rb +5 -1
  46. data/lib/kward/prompt_interface.rb +76 -0
  47. data/lib/kward/prompts/commands.rb +1 -0
  48. data/lib/kward/rpc/auth_manager.rb +1 -1
  49. data/lib/kward/rpc/server.rb +2 -1
  50. data/lib/kward/shell_prompt.rb +50 -0
  51. data/lib/kward/shell_prompt_session.rb +58 -0
  52. data/lib/kward/terminal_keys.rb +1 -0
  53. data/lib/kward/tools/prepare_shell_command.rb +28 -0
  54. data/lib/kward/tools/registry.rb +63 -5
  55. data/lib/kward/tools/replace_editor_buffer.rb +30 -0
  56. data/lib/kward/tools/run_shell_command.rb +24 -6
  57. data/lib/kward/version.rb +1 -1
  58. data/templates/default/layout/html/layout.erb +1 -1
  59. metadata +9 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4e40d786a99770eef30a7eb7fec41470d927f9d889160d1c6db9f8fd1ebec39
4
- data.tar.gz: 1cfaf680bf9b5ef313e0a0f71a3fb33ea394eb7bc2f5d19c5bfee4c38898ac49
3
+ metadata.gz: 678c9e9bb188e6c3d065dfde6b97c9ec5c31b29b178b40d69b0397b5200eb2a8
4
+ data.tar.gz: 3e09cb3d11ff6018ecc75e3eb31e8745e72e534dba7a4c3711a488451d353fcc
5
5
  SHA512:
6
- metadata.gz: efe8e4a599791fee93d0329c7d0ec4e87c9d45c9ad9f99165d9172d44410ccc83de8014802cc76481d37952e89427b80cac1022a2f7fc17d0bd3c05356d44391
7
- data.tar.gz: 9b1ad11cfd582d78d4834cf5b6f168ca8e2f987abe99804678d5e58c2e3000fcff6ebbf30448a71aa1f0ca7a94e9e9585be7b0643d6c88e3ed931e3ab6f3eada
6
+ metadata.gz: 7f4247a32300deff0906c95c10d7312aa6303108956596f16d9f2c6d2d4fc444b5ef377751f47dbd2157b041710d855c44a981a1a22f8a562c79203afe8b65ed
7
+ data.tar.gz: d93d09126d221c05d7ef0e931f46ccfaa4a84450e6eb5695dbea653d8b9de99bb27bc7fb070b323c74160477fd328f0be3cd972d544b45e3ea90ffbeb79cb087
data/CHANGELOG.md CHANGED
@@ -4,6 +4,33 @@ All notable changes to Kward will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.83.0] - 2026-08-28
8
+
9
+ ### Added
10
+
11
+ - Added `?` shell prompts for transient, context-aware assistance inside `/shell`, including shared shell-state commands and prepare-without-execute command drafts.
12
+ - Added `Ctrl+.` in Modern editor mode to open an in-editor prompt line for communicating with the dedicated editor agent.
13
+ - Added Vibe `:prompt <instruction>` support, allowing the agent to inspect and replace the active in-memory editor buffer without saving it automatically.
14
+ - Added current-buffer word completion to the integrated editor, with repeated Tab presses cycling nearby matches and existing smart indentation retained when no match exists.
15
+ - Added Claude Opus 5 to the Anthropic model picker with its 1M context window and high-reasoning metadata.
16
+ - Added an `i` toggle to the `/files` browser for showing Git-ignored files, which remain hidden by default.
17
+
18
+ ### Changed
19
+
20
+ - `/shell` now keeps one persistent local shell process per tab so shell variables, functions, aliases, and directory changes are shared by user and agent commands.
21
+ - Shell-agent turns use a transient per-tab context and receive only bounded, sanitized output after an explicit `?` request; ordinary shell output remains outside model context.
22
+ - Shell-agent transcript output is now retained as bounded, tab-local runtime state so it survives tab switches without entering session history.
23
+ - Vibe editor-agent turns now use an isolated, configurable model/reasoning context, remain visible with a status-line spinner, and stay out of normal chat transcripts and session history.
24
+ - Removed the obsolete GPT-5.6 Luna Responses Lite compatibility workaround so Luna requests retain Kward's identity and standard Codex payload.
25
+
26
+ ### Fixed
27
+
28
+ - Made persistent shell command cancellation and timeouts recover by restarting an unresponsive embedded shell, and stabilized raw-input coverage across platforms.
29
+ - Restored full-screen Git pagers and immediate key/Ctrl+C forwarding for interactive `/shell` commands by removing Kward's ambient `GIT_PAGER=cat` default and putting host input in raw mode during persistent-shell handoff, while keeping paging suppressed for captured and shell-agent commands.
30
+ - Fixed Claude subscription OAuth authorization and token-exchange requests by removing unsupported subscription scopes and the nonstandard `code` query parameter, and by forwarding the OAuth state to the token endpoint.
31
+ - Installed and enabled Bubblewrap during release verification so Linux worktree tests use the same sandbox setup as normal CI, added manual recovery for existing release tags, and made publication wait for and attach the canonical RubyGems artifact after trusted-publishing attestation.
32
+ - Fixed automatic context compaction so Codex response items are included in token estimates and long tool-call turns are checked before each model request.
33
+
7
34
  ## [0.82.0] - 2026-08-23
8
35
 
9
36
  ### Added
@@ -91,6 +118,7 @@ All notable changes to Kward will be documented in this file.
91
118
  - Added Git worktree bindings for normal interactive session tabs. `/tab worktree` can move a tab into a strict linked-worktree workspace after research, preserve its transcript, warn about dirty origin changes, and keep the branch available when explicitly detached.
92
119
  - Added the active worktree `git_commit` model tool, allowing explicitly requested agent commits through the trusted host-side Git workflow without widening the shell sandbox.
93
120
  - Added `/tab worktree merge` for explicitly merging a clean worktree branch into the branch checked out in its original workspace, plus `/tab worktree merge abort` for conflicted merges.
121
+ - Added `/worktree` as a concise alias for `/tab worktree` on the active tab.
94
122
  - Added `j`/`k` keyboard navigation to the `/files` project browser.
95
123
  - Added `h`/`l` keyboard navigation for collapsing and expanding `/files` directories.
96
124
  - Added `j`/`k` keyboard navigation to the `/git` changed-file overlay.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kward (0.82.0)
4
+ kward (0.83.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.82.0)
149
+ kward (0.83.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/doc/agent-tools.md CHANGED
@@ -15,6 +15,7 @@ Tools also enforce important boundaries:
15
15
  | Category | Tools | Guide |
16
16
  | --- | --- | --- |
17
17
  | Workspace tools | `list_directory`, `read_file`, `context_for_task`, `context_budget_stats`, `summarize_file_structure`, `write_file`, `edit_file`, `run_shell_command` | [Workspace tools](workspace-tools.md) |
18
+ | Shell prompt tools | `run_shell_command`, `prepare_shell_command` (available only to the transient `?` shell assistant) | [Embedded shell](shell.md) |
18
19
  | Web tools | `web_search`, `fetch_content`, `fetch_raw` | [Web search](web-search.md) |
19
20
  | Code search | `code_search` | [Code search](code-search.md) |
20
21
  | Context and interaction tools | `read_skill`, `retrieve_tool_output`, `ask_user_question` | [Context tools](context-tools.md) |
data/doc/composer.md CHANGED
@@ -213,4 +213,4 @@ If rendering becomes corrupted after resize or after an external program writes
213
213
 
214
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`.
215
215
 
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.
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. While Kward owns the shell composer, a leading `?` asks the transient shell assistant; it receives only bounded, sanitized output from the latest command. Use `capture <command>` inside `/shell` or `/capture <command>` from the normal composer for bounded, sanitized transcript output.
data/doc/configuration.md CHANGED
@@ -184,9 +184,9 @@ aliases:
184
184
  gd: "git diff --color=always"
185
185
  ```
186
186
 
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.
187
+ `env` values are applied when shell mode starts, after Kward's conservative color defaults. `/shell` keeps one persistent local interactive shell process per tab. 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.
188
188
 
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.
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 shell 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. Prefix a submitted line with `?` inside `/shell` to ask the transient shell assistant about the current command output or state. 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.
190
190
 
191
191
  ## Provider and model settings
192
192
 
@@ -248,7 +248,7 @@ Defaults:
248
248
  - Copilot: `gpt-5-mini`
249
249
  - Reasoning effort: `medium`
250
250
 
251
- The Anthropic model choices include `claude-fable-5`, but Fable availability depends on the logged-in account and organization. Selecting it without access returns an Anthropic provider error.
251
+ The Anthropic model choices include `claude-fable-5`, `claude-opus-5`, and `claude-sonnet-5`. Fable and Opus availability depends on the logged-in account and organization. Selecting a model without access returns an Anthropic provider error. Kward keeps Sonnet 5 as its default because it supports both Pro and Max subscriptions; select Opus 5 explicitly when it is available on the account.
252
252
 
253
253
  The interactive `/model` picker reads cached OpenRouter models when available. Run `kward openrouter refresh` to fetch text-capable models available to the configured OpenRouter API key and cache them under `~/.kward/cache/openrouter_models.json`. Run `kward openrouter list` to inspect the cached model ids.
254
254
 
@@ -393,6 +393,21 @@ The built-in TUI file editor supports three keybinding modes. Modern is the defa
393
393
 
394
394
  `mode` can be `modern`, `emacs`, or `vibe`. The old `default` value is still accepted as an alias for `modern`. You can change this from `/settings` → Interface → Editor mode; newly opened editor buffers pick up the setting immediately.
395
395
 
396
+ Vibe `:prompt` uses a dedicated transient editor agent. Configure its model and reasoning effort under `editor.agent` when it should differ from the active tab:
397
+
398
+ ```json
399
+ {
400
+ "editor": {
401
+ "agent": {
402
+ "model": "gpt-5.5",
403
+ "reasoning_effort": "medium"
404
+ }
405
+ }
406
+ }
407
+ ```
408
+
409
+ These settings use the active tab's provider. If either value is omitted, Kward falls back to the active tab's value and then the client default. Editor-agent prompts and tool activity are kept out of the normal transcript and session history; the editor remains visible with a spinner while the transient turn runs.
410
+
396
411
  The integrated Git and session diff viewers support unified and side-by-side layouts:
397
412
 
398
413
  ```json
data/doc/editor.md CHANGED
@@ -43,7 +43,7 @@ For a nested project tree, run:
43
43
  /files
44
44
  ```
45
45
 
46
- In the tree browser, use `↑`/`↓` to move, `←`/`→` to collapse or expand directories, `Enter` to toggle a directory or open a file, `Tab` or `/` to search, `@` to insert the selected file as an `@path` mention, and `Esc` to close. When you open a file from `/files`, quitting the editor returns to the browser at the same position.
46
+ In the tree browser, use `↑`/`↓` to move, `←`/`→` to collapse or expand directories, `Enter` to toggle a directory or open a file, `Tab` or `/` to search, `i` to show or hide Git-ignored files, `@` to insert the selected file as an `@path` mention, and `Esc` to close. When you open a file from `/files`, quitting the editor returns to the browser at the same position.
47
47
 
48
48
  For an unsaved buffer, open a scratchpad:
49
49
 
@@ -85,21 +85,26 @@ $doc/editor.md
85
85
  1. Type `$doc/editor.md` in the composer.
86
86
  2. Pick the file from the matching results, or press `Enter` if the path is already complete.
87
87
  3. Edit the file.
88
- 4. Save with `Ctrl+S` in Modern mode, `C-x C-s` in Emacs mode, or `:w` in Vibe mode. For an unsaved scratchpad in Vibe mode, use `:w filename`.
89
- 5. Quit with `Ctrl+Q`, `C-x C-c`, or `:q`.
90
- 6. Continue chatting with Kward.
88
+ 4. In Vibe normal mode, use `:prompt <instruction>` to ask the agent to update the current in-memory buffer. In Modern mode, press `Ctrl+.` and type the instruction in the editor prompt line. For example, `write a HelloWorld class`.
89
+ 5. The editor stays visible while the agent works; its status line shows a spinner. Review the generated buffer and save with `Ctrl+S` in Modern mode, `C-x C-s` in Emacs mode, or `:w` in Vibe mode. For an unsaved scratchpad in Vibe mode, use `:w filename`.
90
+ 6. Quit with `Ctrl+Q`, `C-x C-c`, or `:q`.
91
+ 7. Continue chatting with Kward.
91
92
 
92
93
  ## What the editor supports
93
94
 
94
95
  The editor is intentionally compact, but it covers the basics you need for quick changes:
95
96
 
96
97
  - Syntax highlighting for common languages, including Ruby, ERB templates, Crystal, Elixir, Julia, JavaScript, TypeScript, JSON, Markdown, YAML, Shell, Makefile, HTML, CSS, SCSS, Python, Go, Rust, Java, C#, C, C++, Swift, Kotlin, Lua, and SQL. ERB highlights template HTML outside ERB tags and Ruby inside `<% ... %>` tags. Unknown file types render as plain text.
97
- - Auto-indent, enabled by default. New lines inherit indentation, Tab jumps to the expected indentation or the next indentation stop, Shift+Tab moves indentation back, obvious closing tokens are re-indented, and Backspace in leading whitespace removes one indentation unit when possible. ERB recognizes common inline Ruby control tags and HTML opening tags when calculating the next indentation. For Ruby, Crystal, Elixir, Julia, Lua, Makefiles, and shell scripts, Enter after a block opener inserts the matching closing keyword; Ctrl+Enter also works from the middle of the line in terminals that report modified Enter keys.
98
+ - Current-buffer word completion. After a partial word, Tab completes from words elsewhere in the active buffer; repeated Tab presses cycle nearby matches. Completion is case-sensitive and recognizes letters, numbers, and underscores. No project files or external sources are searched.
99
+ - Auto-indent, enabled by default. New lines inherit indentation. When word completion does not apply, Tab jumps to the expected indentation or the next indentation stop. Shift+Tab moves indentation back, obvious closing tokens are re-indented, and Backspace in leading whitespace removes one indentation unit when possible. ERB recognizes common inline Ruby control tags and HTML opening tags when calculating the next indentation. For Ruby, Crystal, Elixir, Julia, Lua, Makefiles, and shell scripts, Enter after a block opener inserts the matching closing keyword; Ctrl+Enter also works from the middle of the line in terminals that report modified Enter keys.
98
100
  - Undo and redo, with up to 100 history entries per buffer.
99
101
  - Incremental search forward and backward.
100
102
  - Selection, copy, cut, and paste. Copy and cut also write to the terminal clipboard through OSC 52 when the terminal supports it.
101
103
  - A line-number gutter and a status line that shows the current mode and prompts.
102
104
  - Soft-wrap, enabled by default so long lines wrap within the editor width instead of scrolling sideways. Disable it with `editor.soft_wrap: false`.
105
+ - Vibe `:prompt <instruction>` and Modern `Ctrl+.` prompt lines send the complete current buffer and document metadata to a dedicated, transient editor agent. The agent can replace the in-memory buffer through an explicit editor tool; it does not save files automatically.
106
+ - Editor prompts and their tool activity are not added to the normal chat transcript or session history.
107
+ - The editor remains visible and locked for editing while the agent runs. Its status line displays progress and a spinner; `Ctrl+C` cancels the request.
103
108
 
104
109
  ## Choosing an editor mode
105
110
 
@@ -122,12 +127,16 @@ You can also configure it in `config.json`:
122
127
  ```json
123
128
  {
124
129
  "editor": {
125
- "mode": "modern"
130
+ "mode": "modern",
131
+ "agent": {
132
+ "model": "gpt-5.5",
133
+ "reasoning_effort": "medium"
134
+ }
126
135
  }
127
136
  }
128
137
  ```
129
138
 
130
- `mode` can be `modern`, `emacs`, or `vibe`. The old `default` value is still accepted as an alias for `modern`.
139
+ `mode` can be `modern`, `emacs`, or `vibe`. The optional `editor.agent.model` and `editor.agent.reasoning_effort` settings configure the dedicated Vibe editor agent. They use the active tab's provider. When either value is omitted, it falls back to the active tab's setting and then the client default. Editor-agent turns use a dedicated system prompt and are not persisted in the active tab transcript. The old `default` value is still accepted as an alias for `modern`.
131
140
 
132
141
  To disable auto-indent or auto-close pairs:
133
142
 
@@ -156,6 +165,7 @@ Modern mode is the default and is the easiest place to start. It uses common ter
156
165
  | --------------------- | ------------------------------------------------- |
157
166
  | `Ctrl+S` | Save |
158
167
  | `Ctrl+Q` | Quit; press again to discard unsaved changes |
168
+ | `Ctrl+.` | Open the editor-agent prompt line |
159
169
  | `/` | Search forward |
160
170
  | `?` | Search backward |
161
171
  | `Ctrl+C` | Copy selection, or cancel search |
@@ -192,7 +202,7 @@ Modern mode is the default and is the easiest place to start. It uses common ter
192
202
  | `Home` / `End` | Move to start / end of line |
193
203
  | `PageUp` / `PageDown` | Scroll |
194
204
  | `Enter` | Insert newline, or confirm search |
195
- | `Tab` | Smart-indent to the expected indentation or next stop |
205
+ | `Tab` | Complete a buffer word, or smart-indent when no match exists |
196
206
  | `Shift+Tab` | Move indentation back by one stop |
197
207
  | `Backspace` | Delete before cursor |
198
208
  | `Delete` | Delete character at cursor |
@@ -233,7 +243,7 @@ Emacs mode is for users who prefer classic Emacs-style non-modal editing. Save a
233
243
  | `Home` / `End` | Move to start / end of line |
234
244
  | `PageUp` / `PageDown` | Scroll |
235
245
  | `Enter` | Insert newline, or confirm search |
236
- | `Tab` | Smart-indent to the expected indentation or next stop |
246
+ | `Tab` | Complete a buffer word, or smart-indent when no match exists |
237
247
  | `Shift+Tab` | Move indentation back by one stop |
238
248
  | `Backspace` | Delete before cursor |
239
249
  | `Delete` / `C-d` | Delete character at cursor |
@@ -370,7 +380,7 @@ Vibe insert mode also supports readline-style shortcuts for efficient editing wi
370
380
  | ---------------- | ---------------------------------------- |
371
381
  | type text | Insert characters |
372
382
  | `Enter` | Insert newline |
373
- | `Tab` | Smart-indent to the expected indentation or next stop |
383
+ | `Tab` | Complete a buffer word, or smart-indent when no match exists |
374
384
  | `Shift+Tab` | Move indentation back by one stop |
375
385
  | `Backspace` | Delete before cursor |
376
386
  | `Delete` | Delete character at cursor |
data/doc/files.md CHANGED
@@ -24,7 +24,7 @@ Inside a Git repository, `/files` uses Git's project view:
24
24
  git ls-files --cached --others --exclude-standard
25
25
  ```
26
26
 
27
- That means tracked files and normal untracked files appear, while ignored files stay out of the list.
27
+ That means tracked files and normal untracked files appear by default, while ignored files stay out of the list. Press `i` in the tree view to show or hide Git-ignored files; Kward remembers this choice for the workspace.
28
28
 
29
29
  Outside Git, Kward scans the workspace directory and skips common noisy directories such as `.git`, `.yardoc`, `_yardoc`, `node_modules`, `rdoc`, `tmp`, and `vendor/bundle`.
30
30
 
@@ -39,6 +39,7 @@ Outside Git, Kward scans the workspace directory and skips common noisy director
39
39
  | `Tab` | Start or stop search |
40
40
  | `/` | Start search |
41
41
  | `Backspace` | Delete the last search character |
42
+ | `i` | Show or hide Git-ignored files |
42
43
  | `Esc` | Leave search; press again to close the browser |
43
44
  | `Q` | Close an image preview |
44
45
  | `+` / `-` | Zoom an image preview in / out |
@@ -90,7 +91,7 @@ See [Integrated editor](editor.md) for editor modes, save/quit keys, search, sel
90
91
 
91
92
  ## Remembered state
92
93
 
93
- Kward remembers the expanded folders and selected path for each workspace. The next time you open `/files` in the same project, it restores the browser close to where you left it.
94
+ Kward remembers the expanded folders, selected path, and Git-ignored file visibility for each workspace. The next time you open `/files` in the same project, it restores the browser close to where you left it.
94
95
 
95
96
  Search itself is temporary. Closing search returns to the normal tree, and closing the browser leaves your chat session intact.
96
97
 
@@ -103,4 +104,4 @@ Image previews are read-only and replace the file-list overlay while leaving the
103
104
  - `/files` is only available in the interactive prompt.
104
105
  - It opens files inside the current workspace.
105
106
  - It is a focused project browser, not a full file manager: it does not rename, move, copy, or delete files.
106
- - Ignored Git files are intentionally hidden when Git can provide the file list.
107
+ - Ignored Git files are hidden by default when Git can provide the file list; press `i` in the tree view to show them.
data/doc/permissions.md CHANGED
@@ -176,6 +176,7 @@ The policy runs before Kward dispatches a model-requested tool. It does not cons
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
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
+ - commands run by the transient `?` shell assistant go through the normal permission and hook checks but deliberately use the user-owned `/shell` process, so the command sandbox cannot contain them;
179
180
  - plugins, hooks, and MCP servers are trusted local extensions with their own process access.
180
181
 
181
182
  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
@@ -60,13 +60,21 @@ If preparation fails before the commit, the command restores the version, lockfi
60
60
  A pushed `v*` tag starts `.github/workflows/release.yml`. The workflow:
61
61
 
62
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.
63
+ 2. Installs and enables Bubblewrap, then runs the full test suite and generated-documentation checks against Ruby 3.4 using the same Linux sandbox setup as normal CI.
64
64
  3. Builds the gem and verifies its packaged files.
65
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.
66
+ 5. Downloads the canonical published gem from RubyGems.org, waiting for propagation when necessary, and verifies its checksum against the RubyGems API.
67
+ 6. Creates `Kward VERSION` as a GitHub Release using that version's changelog section and attaches the verified RubyGems artifact.
68
68
 
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.
69
+ The publishing job uses the protected `release` environment. If RubyGems.org already has the version after a partially completed workflow, a rerun downloads and verifies the canonical published gem before continuing. This also accommodates trusted-publishing attestations that can change the published gem bytes. 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.
70
+
71
+ If the workflow itself needs a fix after a tag has already been pushed, commit and push the fix to `main`, then recover the existing tag with the updated workflow:
72
+
73
+ ```bash
74
+ gh workflow run Release --ref main -f tag=v0.82.0
75
+ ```
76
+
77
+ Manual recovery still checks out and verifies the tagged source before publishing. The `release` environment permits automatic version-tag runs and manual recovery runs from `main` only.
70
78
 
71
79
  Follow the run from the repository's **Actions → Release** page. Installation can be checked after publication with:
72
80
 
data/doc/rpc.md CHANGED
@@ -80,11 +80,11 @@ Read `capabilities` at runtime instead of assuming every feature is available. I
80
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.
81
81
  - `startupResources`: supported startup resource listing for context, skills, prompts, and plugins.
82
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
- - `composer`: composer-only UI features. Interactive session diff totals are explicitly unsupported over RPC (`composer.sessionDiff.supported: false`) because RPC clients already receive per-tool diff results and no live composer status payload is exposed. Clipboard copy is also unsupported over RPC (`composer.copy.supported: false`) because UI clients own clipboard access.
83
+ - `composer`: composer-only UI features. Interactive session diff totals are explicitly unsupported over RPC (`composer.sessionDiff.supported: false`) because RPC clients already receive per-tool diff results and no live composer status payload is exposed. Clipboard copy is also unsupported over RPC (`composer.copy.supported: false`) because UI clients own clipboard access. Vibe editor prompts are unsupported over RPC (`composer.editorPrompt.supported: false`) because RPC has no live integrated editor buffer.
84
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.
85
85
  - `export`: supported transcript export formats. Currently `markdown` and `html`; default is `markdown`.
86
86
  - `starterPack`: explicitly unsupported (`supported: false`, reason `cliOnlyInstallCommand`). Use `kward init` from the 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
+ - `shell`: explicitly unsupported (`supported: false`, reason `interactiveTuiOnly`) because `/shell`, its transient `?` shell assistant, `!command`, `/capture`, and PTY handoff require the local TUI. Model-requested `run_shell_command` tools remain bounded, captured, and noninteractive over RPC.
88
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.
89
89
 
90
90
  ### `shutdown`
data/doc/sandboxing.md CHANGED
@@ -106,12 +106,16 @@ this interface.
106
106
  ## Boundaries and limits
107
107
 
108
108
  The current implementation protects **model-requested command workers only**.
109
+ The transient `?` shell assistant is intentionally not one of those workers: its
110
+ `run_shell_command` calls use the active user-owned `/shell` process so shell
111
+ state can persist.
112
+
109
113
  It does not sandbox:
110
114
 
111
115
  - the Kward Ruby host process;
112
116
  - model-provider, search-provider, or RPC traffic;
113
117
  - trusted Ruby plugins;
114
- - MCP servers, lifecycle hooks, `/shell`, `!command`, `/capture`, or `/pty`.
118
+ - MCP servers, lifecycle hooks, `/shell`, `?` shell-agent commands, `!command`, `/capture`, or `/pty`.
115
119
 
116
120
  Sandboxed command workers receive a minimal environment: Kward preserves only
117
121
  basic terminal, locale, and path variables, then supplies a private `HOME` and
data/doc/security.md CHANGED
@@ -57,7 +57,7 @@ Kward's built-in file tools normally resolve paths inside the active workspace.
57
57
  These protections reduce accidental edits. They do not contain the whole process:
58
58
 
59
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`.
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`, including commands that the transient `?` shell assistant runs through the user's persistent `/shell` process.
61
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.
62
62
  - Plugins, command hooks, and MCP servers are local processes with the same general operating-system access.
63
63
  - Read-before-edit applies to Kward's file tools, not to arbitrary shell commands or extension code.
data/doc/shell.md CHANGED
@@ -4,6 +4,7 @@ Kward lets you run your own shell commands without leaving the interactive TUI.
4
4
 
5
5
  - **For one quick command, use `!command`.** It runs from the active workspace and returns you to the normal composer when it finishes.
6
6
  - **For a longer stretch of shell work, use `/shell`.** It keeps its directory, environment, aliases, and history between commands.
7
+ - **To ask Kward for help from inside `/shell`, prefix the request with `?`.** It can inspect the latest shell output, run an explicitly requested state change, or prepare a command in the shell prompt for your confirmation.
7
8
  - **For bounded output in the transcript view, use `/capture <command>`.** Inside `/shell`, use `capture <command>` for the same kind of readable output while keeping the shell's current state.
8
9
 
9
10
  Ordinary `!command` input and external commands inside `/shell` receive an interactive PTY. Pagers, Vim, SSH, REPLs, password prompts, and other interactive tools therefore work without a `pty` prefix.
@@ -20,7 +21,7 @@ Prefix a command with `!` in the normal composer:
20
21
 
21
22
  The command runs from the active workspace root and begins in an inline PTY region above a frozen composer. Line-oriented output, single-line carriage-return progress, and synchronized-output update brackets scroll the transcript area naturally while keyboard input belongs to the child process. If the child emits alternate-screen, clear-screen, absolute cursor, or unknown terminal controls, Kward conservatively hides the composer and switches permanently to full-terminal passthrough for the rest of that command. Pagers and full-screen applications therefore retain the complete terminal without relying on a command-name allowlist.
22
23
 
23
- When an inline command exits without reading input, safe output is mirrored into the transient transcript view so a repaint cannot hide it. Carriage-return and horizontal-cursor progress redraws are reduced to their final visible lines, while an unterminated synchronized-output update is closed before Kward redraws. If the child reads input, Kward retains only output captured before the first forwarded input byte; this prevents echoed passwords, OTPs, or other input from entering tab state. Shell output is never added to the AI conversation or sent to the model.
24
+ When an inline command exits without reading input, safe output is mirrored into the transient transcript view so a repaint cannot hide it. Carriage-return and horizontal-cursor progress redraws are reduced to their final visible lines, while an unterminated synchronized-output update is closed before Kward redraws. If the child reads input, Kward retains only output captured before the first forwarded input byte; this prevents echoed passwords, OTPs, or other input from entering tab state. Output from one-off commands is never added to the AI conversation or sent to the model.
24
25
 
25
26
  Shell output can leave transient text in the transcript area. **After the command finishes, press Ctrl+L to redraw the durable conversation and clear that transient `!command` output.** While an interactive command is still running, the composer remains frozen and keyboard input—including Ctrl+L and Kward's tab shortcuts—belongs to the child process.
26
27
 
@@ -90,6 +91,29 @@ Leave shell mode with `exit`, `logout`, or Ctrl+D on an empty prompt.
90
91
 
91
92
  `cd` changes only the embedded shell's directory. It does not change Kward's workspace root or the directory used by the model's other tools.
92
93
 
94
+ ## Ask the shell agent
95
+
96
+ While `/shell` is active, start a submitted line with `?` to ask the transient shell assistant:
97
+
98
+ ```text
99
+ ? why did the last command fail?
100
+ ? show me which process is listening on port 3000
101
+ ? prepare a command to find Ruby files changed today
102
+ ```
103
+
104
+ The assistant receives the current shell directory, the last command, its exit status, and bounded output from that command. Output is sent to the model only because you explicitly asked with `?`; it is sanitized and bounded before being included, and shell-agent turns are not added to the normal session history.
105
+
106
+ If you explicitly ask the assistant to change shell state, it can use the active shell session:
107
+
108
+ ```text
109
+ ? cd into test
110
+ ? set RAILS_ENV to test
111
+ ```
112
+
113
+ For a suggestion or prepared command, the assistant uses `prepare_shell_command`. The command is placed in the shell composer but is not run until you press `Enter`. Running a command directly and preparing one are deliberately separate actions.
114
+
115
+ The shell assistant cannot safely run commands that require terminal input. Ask it to prepare those commands instead. The local `/shell` session keeps one interactive shell process alive, so directory changes, variables, functions, aliases, and other shell state persist between commands. The one-off `!command` and `/capture` workflows remain separate. SSH remains available through the normal interactive PTY handoff, but shell-agent prompting resumes after that SSH session exits.
116
+
93
117
  ## Interactive and captured commands
94
118
 
95
119
  External commands inside `/shell` are interactive by default:
@@ -101,7 +125,7 @@ ruby
101
125
  ssh example.com
102
126
  ```
103
127
 
104
- Kward gives each command the terminal, forwards keyboard input, and restores the shell prompt when the command exits. It prints the submitted command but no PTY start message or exit-status summary. The line-oriented Git commands `git fetch`, `git ls-remote`, `git push`, `git remote`, and `git status` keep the shell prompt visible as a frozen display. Safe, line-oriented output from commands that did not read keyboard input is kept in the transient transcript view; full-screen and genuinely interactive output stays terminal-owned.
128
+ Kward gives each command the terminal, forwards keyboard input, and restores the shell prompt when the command exits. Interactive commands inherit your normal pager configuration, so commands such as `git log` can open `less` in full-screen mode. Kward suppresses Git paging only for noninteractive shell-agent and `capture` executions. It prints the submitted command but no PTY start message or exit-status summary. The line-oriented Git commands `git fetch`, `git ls-remote`, `git push`, `git remote`, and `git status` keep the shell prompt visible as a frozen display. Safe, line-oriented output from commands that did not read keyboard input is kept in the transient transcript view; full-screen and genuinely interactive output stays terminal-owned.
105
129
 
106
130
  Use `capture` inside `/shell` when you want ordinary, readable output in Kward's transcript area instead of direct terminal control:
107
131
 
@@ -141,7 +165,7 @@ Each Kward tab owns its `/shell` state. Switching away and back restores that ta
141
165
 
142
166
  Shell commands use a separate, workspace-scoped history rather than the normal chat-prompt history. Configure its size with `history_limit` in `ekwsh.yml`.
143
167
 
144
- Kward's tab shortcuts work at the shell prompt and while a captured command is running. During an interactive command, the child owns every key; exit or interrupt it before switching Kward tabs.
168
+ Kward's tab shortcuts work at the shell prompt and while a captured command is running. During an interactive command, the child owns every key; exit or interrupt it before switching Kward tabs. Bounded output from shell-agent `?` turns is also retained in the tab's transient runtime view, so it is restored when you switch away and back without being added to session history. Ctrl+L clears this transient shell and shell-agent output.
145
169
 
146
170
  ## Completion
147
171
 
@@ -179,7 +203,7 @@ When several candidates match, repeated Tab presses cycle through them and wrap
179
203
 
180
204
  ## Built-ins
181
205
 
182
- `ekwsh` handles a small set of commands itself so their state can persist:
206
+ The persistent `/shell` process handles these commands in-session so their state can persist:
183
207
 
184
208
  | Built-in | What it does |
185
209
  | --- | --- |
@@ -232,12 +256,12 @@ aliases:
232
256
 
233
257
  | Setting | Default | What it does |
234
258
  | --- | --- | --- |
235
- | `shell` | `/bin/sh` | POSIX-compatible shell used with `-c`. It must be an absolute executable path. |
236
- | `timeout_seconds` | `300` | Maximum runtime for one captured command. |
237
- | `max_output_bytes` | `1048576` | Maximum output retained for one captured command. |
259
+ | `shell` | `/bin/sh` | Absolute executable path for the persistent `/shell` process and interactive shell commands. |
260
+ | `timeout_seconds` | `300` | Maximum runtime for one captured or shell-agent command. |
261
+ | `max_output_bytes` | `1048576` | Maximum output retained for one captured or shell-agent command. |
238
262
  | `history_limit` | `1000` | Maximum shell-history entries per workspace. |
239
263
 
240
- Invalid or relative `shell` paths fall back to `/bin/sh`. These timeout and output limits apply only to `capture` inside `/shell`, not interactive commands or the separate `/capture` slash command.
264
+ Invalid or relative `shell` paths fall back to `/bin/sh`. These timeout and output limits apply to `capture` inside `/shell` and shell-agent commands, not user-owned interactive commands or the separate `/capture` slash command.
241
265
 
242
266
  ### Environment
243
267
 
@@ -253,7 +277,7 @@ TERM=xterm-256color # only when TERM is missing or dumb
253
277
 
254
278
  It does not force color. Set `FORCE_COLOR`, `CLICOLOR_FORCE`, or a command-specific option such as `--color=always` when needed.
255
279
 
256
- When rbenv is available, Kward adds its shims and bin directories to `PATH` and supplies `RBENV_ROOT` if it was missing. This lets `ruby`, `bundle`, and `./exe/kward` use the selected Ruby without sourcing shell startup files.
280
+ When rbenv is available, Kward adds its shims and bin directories to `PATH` and supplies `RBENV_ROOT` if it was missing before starting `/shell`. The configured interactive shell may also load its normal startup files.
257
281
 
258
282
  ### Aliases
259
283
 
@@ -281,17 +305,17 @@ Aliases are intentionally simple: they do not expand recursively and are not she
281
305
 
282
306
  ## Terminal output and safety
283
307
 
284
- Interactive commands write directly to your terminal so full-screen tools can work. When a command does not read keyboard input and emits only line-oriented text plus safe color sequences, Kward mirrors that output into the transient transcript view after the command exits. Other interactive output is not sanitized and may contain terminal control sequences, so run only commands you trust with terminal access.
308
+ Interactive commands write directly to your terminal so full-screen tools can work. When a command does not read keyboard input and emits only line-oriented text plus safe color sequences, Kward mirrors that output into the transient transcript view after the command exits. Other interactive output is not sanitized and may contain terminal control sequences, so run only commands you trust with terminal access. Safe bounded output is included in a shell-agent request only when you explicitly use `?`; it is never sent to the model for ordinary shell commands.
285
309
 
286
310
  Commands run with `capture` inside `/shell` preserve safe ANSI color and style sequences while removing cursor movement, clear-screen controls, title changes, alternate-screen controls, and similar sequences that could damage the TUI transcript.
287
311
 
288
312
  ## Limitations
289
313
 
290
- `ekwsh` manages shell-like state, but it is not a persistent login shell or terminal emulator:
314
+ The local `/shell` session is persistent, but it is not a complete terminal emulator or a remote-shell protocol:
291
315
 
292
- - each external command runs separately through the configured shell,
293
- - there is no job control; a stopped child is terminated rather than leaving the terminal stranded,
294
- - shell functions do not persist and shell startup files are not sourced,
295
- - there is no login-shell readline integration,
316
+ - the one-off `!command` and `/capture` workflows do not share `/shell` state,
317
+ - shell state is held by the live process and is not serialized across Kward restarts,
318
+ - a stopped or unresponsive command can still require Ctrl+C or shell-session cleanup,
296
319
  - full-screen terminal state is not retained after an interactive command exits,
297
- - safe line-oriented output may remain in the transient TUI transcript, but full-screen terminal state is not retained and no shell output becomes part of the AI conversation.
320
+ - while an interactive SSH session owns the terminal, Kward cannot safely intercept `?` or provide remote cwd/completion context; shell-agent prompting resumes after SSH exits,
321
+ - safe line-oriented output may remain in the transient TUI transcript; only bounded safe output from an explicit `?` request enters the transient shell-agent context and none of it is added to the normal session history.
data/doc/tabs.md CHANGED
@@ -39,6 +39,8 @@ A normal session tab can be activated in a linked Git worktree after you have re
39
39
 
40
40
  ```text
41
41
  /tab worktree
42
+ # or
43
+ /worktree
42
44
  ```
43
45
 
44
46
  When enabled, Kward keeps the same tab and transcript but rebuilds its agent against a new worktree. The tab label includes the worktree branch. The worktree is created from `HEAD`, so Kward warns when the original workspace is dirty and leaves those existing changes in the original checkout; it does not copy them automatically. `/tab worktree activate` is an explicit alias for the same action.
@@ -98,6 +100,7 @@ Tabs keep the conversations separate, so context from one tab does not automatic
98
100
  | `/tab worktree merge` | Merge the current worktree branch into the branch checked out in its original workspace |
99
101
  | `/tab worktree merge abort` | Abort a conflicted worktree merge in the original workspace |
100
102
  | `/tab worktree remove` | Remove a clean linked worktree and keep its branch |
103
+ | `/worktree …` | Alias for `/tab worktree …` on the active tab |
101
104
  | `/tab move left` | Move the current tab one slot left |
102
105
  | `/tab move right` | Move the current tab one slot right |
103
106
  | `/tab move <number>` | Move the current tab to a numbered position |
data/doc/usage.md CHANGED
@@ -78,7 +78,7 @@ For several commands, enter the embedded Kward shell:
78
78
  /shell
79
79
  ```
80
80
 
81
- `/shell` opens `ekwsh`, a Kward-native command mode that preserves state such as the current directory, environment variables, and aliases between commands. External commands receive an interactive PTY by default, so `git log`, `less`, Vim, SSH, and REPLs work without a prefix. Use `capture <command>` inside `/shell` or `/capture <command>` from the normal composer for bounded, transcript-friendly output. See [Embedded shell](shell.md) for built-ins, completion, configuration, ANSI handling, PTY passthrough, and limitations.
81
+ `/shell` opens `ekwsh`, a Kward-native command mode with one persistent local shell process. It preserves state such as the current directory, environment variables, functions, and aliases between commands. Prefix a line with `?` inside `/shell` to ask a transient shell assistant about the latest output, execute an explicit state change, or prepare a command without running it. External commands receive an interactive PTY by default, so `git log`, `less`, Vim, SSH, and REPLs work without a prefix. Use `capture <command>` inside `/shell` or `/capture <command>` from the normal composer for bounded, transcript-friendly output. See [Embedded shell](shell.md) for built-ins, completion, configuration, ANSI handling, PTY passthrough, and limitations.
82
82
 
83
83
  ## Shell commands
84
84
 
@@ -123,7 +123,7 @@ Slash commands run local actions in the current session. Most do not send a prom
123
123
  | `/git` | review uncommitted changes, stage files, and commit. |
124
124
  | `/diff` | open the file changes recorded in the current session. |
125
125
  | `/files` | browse project files in a nested tree and open them in the editor. |
126
- | `/shell` | run stateful workspace commands with interactive PTY support. |
126
+ | `/shell` | run stateful workspace commands with interactive PTY support and ask the shell agent with `?`. |
127
127
  | `/capture <command>` | run a bounded one-shot command and retain its output in the transcript. |
128
128
  | `/pty <command>` | explicitly hand the terminal to an interactive command; retained for compatibility with existing workflows. |
129
129
  | `/settings` | configure models, accounts, memory, interface, tools, context, personalization, and logging. |
@@ -143,6 +143,7 @@ Slash commands run local actions in the current session. Most do not send a prom
143
143
  | `/tab worktree merge` | merge a clean worktree branch into the branch checked out in its original workspace. |
144
144
  | `/tab worktree merge abort` | abort a conflicted worktree merge in the original workspace. |
145
145
  | `/tab worktree remove` | remove a clean linked worktree while keeping its branch. |
146
+ | `/worktree …` | alias for `/tab worktree …` on the active tab. |
146
147
  | `/session` | open the saved sessions picker or continue a previous session by path. |
147
148
  | `/resume` | alias for `/session`. |
148
149
  | `/session name <name>` | name or clear the current session. |
data/lib/kward/agent.rb CHANGED
@@ -84,6 +84,7 @@ module Kward
84
84
  end
85
85
  loop do
86
86
  cancellation&.raise_if_cancelled!
87
+ auto_compact_if_needed
87
88
  begin
88
89
  message = chat(on_reasoning_delta: on_reasoning_delta, on_retry: on_retry, cancellation: cancellation, steering: steering, options: options, tool_registry: tool_registry) do |event|
89
90
  yield event if block_given?
@@ -17,7 +17,7 @@ module Kward
17
17
  DEFAULT_PORT = 53_692
18
18
  CALLBACK_PATH = "/callback"
19
19
  DEFAULT_CLIENT_ID = Base64.decode64("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl")
20
- SCOPE = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload"
20
+ SCOPE = "user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload"
21
21
 
22
22
  attr_reader :auth_path
23
23
 
@@ -60,7 +60,7 @@ module Kward
60
60
  end
61
61
  raise "Missing authorization code" if code.to_s.empty?
62
62
 
63
- complete_login_flow(code: code, redirect_uri: redirect_uri, code_verifier: pkce[:verifier])
63
+ complete_login_flow(code: code, redirect_uri: redirect_uri, code_verifier: pkce[:verifier], state: state)
64
64
  auth_path
65
65
  ensure
66
66
  server&.close unless server&.closed?
@@ -68,7 +68,6 @@ module Kward
68
68
 
69
69
  def authorization_url(redirect_uri:, code_challenge:, state:)
70
70
  query = URI.encode_www_form(
71
- code: "true",
72
71
  client_id: client_id,
73
72
  response_type: "code",
74
73
  redirect_uri: redirect_uri,
@@ -98,8 +97,8 @@ module Kward
98
97
  wait_for_callback(server, expected_state: expected_state, timeout_seconds: timeout_seconds)
99
98
  end
100
99
 
101
- def complete_login_flow(code:, redirect_uri:, code_verifier:)
102
- tokens = exchange_code_for_tokens(code: code, redirect_uri: redirect_uri, code_verifier: code_verifier)
100
+ def complete_login_flow(code:, redirect_uri:, code_verifier:, state:)
101
+ tokens = exchange_code_for_tokens(code: code, redirect_uri: redirect_uri, code_verifier: code_verifier, state: state)
103
102
  save_auth(tokens: tokens)
104
103
  tokens
105
104
  end
@@ -192,13 +191,14 @@ module Kward
192
191
  oauth_callback_server(port_env: "KWARD_ANTHROPIC_OAUTH_PORT", default_port: DEFAULT_PORT)
193
192
  end
194
193
 
195
- def exchange_code_for_tokens(code:, redirect_uri:, code_verifier:)
194
+ def exchange_code_for_tokens(code:, redirect_uri:, code_verifier:, state:)
196
195
  response = post_json(TOKEN_URL,
197
196
  grant_type: "authorization_code",
198
197
  client_id: client_id,
199
198
  code: code,
200
199
  redirect_uri: redirect_uri,
201
- code_verifier: code_verifier)
200
+ code_verifier: code_verifier,
201
+ state: state)
202
202
  parse_successful_json(response, "Anthropic OAuth token exchange")
203
203
  end
204
204