kward 0.72.0 → 0.73.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/CHANGELOG.md +59 -0
- data/Gemfile.lock +2 -2
- data/doc/configuration.md +1 -1
- data/doc/editor.md +23 -2
- data/doc/git.md +1 -0
- data/doc/rpc.md +2 -2
- data/doc/shell.md +56 -10
- data/doc/usage.md +27 -1
- data/lib/kward/ansi.rb +62 -23
- data/lib/kward/cli/plugins.rb +1 -1
- data/lib/kward/cli/rendering.rb +4 -1
- data/lib/kward/cli/runtime_helpers.rb +141 -7
- data/lib/kward/cli/settings.rb +0 -1
- data/lib/kward/cli/slash_commands.rb +213 -0
- data/lib/kward/cli/tabs.rb +34 -4
- data/lib/kward/cli/tool_summaries.rb +6 -0
- data/lib/kward/cli.rb +4 -12
- data/lib/kward/clipboard.rb +2 -3
- data/lib/kward/compactor.rb +7 -19
- data/lib/kward/config_files.rb +26 -4
- data/lib/kward/ekwsh.rb +239 -42
- data/lib/kward/image_attachments.rb +3 -1
- data/lib/kward/interactive_pty_runner.rb +151 -0
- data/lib/kward/local_command_runner.rb +155 -0
- data/lib/kward/local_pty_command_runner.rb +171 -0
- data/lib/kward/model/context_usage.rb +2 -2
- data/lib/kward/model/payloads.rb +2 -5
- data/lib/kward/prompt_history.rb +5 -3
- data/lib/kward/prompt_interface/editor/auto_indent.rb +5 -4
- data/lib/kward/prompt_interface/editor/controller.rb +262 -62
- data/lib/kward/prompt_interface/editor/modes/emacs.rb +21 -21
- data/lib/kward/prompt_interface/editor/modes/modern.rb +38 -37
- data/lib/kward/prompt_interface/editor/modes/vibe.rb +23 -173
- data/lib/kward/prompt_interface/editor/modes/vibe_insert_readline.rb +166 -0
- data/lib/kward/prompt_interface/editor/renderer.rb +6 -5
- data/lib/kward/prompt_interface/editor/state.rb +28 -6
- data/lib/kward/prompt_interface/editor/syntax_highlighter.rb +5 -3
- data/lib/kward/prompt_interface/git_prompt.rb +12 -23
- data/lib/kward/prompt_interface/interactive/controller.rb +1 -1
- data/lib/kward/prompt_interface/key_handler.rb +93 -51
- data/lib/kward/prompt_interface/question_prompt.rb +1 -6
- data/lib/kward/prompt_interface/screen.rb +3 -3
- data/lib/kward/prompt_interface/selection_prompt.rb +12 -6
- data/lib/kward/prompt_interface/slash_overlay.rb +2 -0
- data/lib/kward/prompt_interface.rb +87 -221
- data/lib/kward/prompts/commands.rb +4 -0
- data/lib/kward/rpc/memory_methods.rb +83 -0
- data/lib/kward/rpc/server.rb +130 -83
- data/lib/kward/rpc/session_manager.rb +10 -74
- data/lib/kward/rpc/tool_metadata.rb +11 -0
- data/lib/kward/rpc/transcript_normalizer.rb +4 -39
- data/lib/kward/scratchpad_runner.rb +56 -0
- data/lib/kward/session_diff.rb +20 -3
- data/lib/kward/session_naming.rb +11 -0
- data/lib/kward/terminal_keys.rb +84 -0
- data/lib/kward/terminal_sequences.rb +42 -0
- data/lib/kward/tools/context_for_task.rb +2 -0
- data/lib/kward/version.rb +1 -1
- data/lib/kward/workers/git_guard.rb +25 -0
- data/lib/kward/workers/job.rb +99 -0
- data/lib/kward/workers/queue_runner.rb +166 -0
- data/lib/kward/workers/queue_store.rb +112 -0
- data/lib/kward/workers.rb +3 -0
- data/lib/kward/workspace.rb +15 -63
- data/templates/default/fulldoc/html/css/kward.css +33 -0
- data/templates/default/fulldoc/html/images/kward_screen_1.png +0 -0
- data/templates/default/fulldoc/html/setup.rb +1 -0
- data/templates/default/layout/html/layout.erb +19 -32
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cfccb9f2d7d186ac01b3d8d9029b1782072171acfcaeeecafa85d8d030ee3bbb
|
|
4
|
+
data.tar.gz: bd7f40e1913ad63bde53002b07b9172622b83e72f8884319e55495ab55c06a10
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13bda62de913cc4e3b97384fe6ca980e61bb6c4c6f6e640193e8dc3a34d27db6958dc6a46f365056636181290d0e7d333f65c86c207903890481ffad83e54952
|
|
7
|
+
data.tar.gz: e80fb5f0d668f619ed231c929e7c9a30d654ed4c8404035f04c114d0fe1567b480afc0bb7d3e149768db1c2b9ad241a94c7cbaf90b04ba10c59c01c45accfce0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Ruby
|
|
21
|
+
uses: ruby/setup-ruby@v1
|
|
22
|
+
with:
|
|
23
|
+
ruby-version: '3.3'
|
|
24
|
+
bundler-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Check syntax
|
|
27
|
+
run: find lib test -name '*.rb' -print0 | xargs -0 ruby -c
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Kward will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.73.1] - 2026-06-30
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Fixed session picker delete confirmation in terminals that send printable keys as CSI-u escape sequences.
|
|
12
|
+
|
|
13
|
+
## [0.73.0] - 2026-06-29
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added persistent session-backed worker queue job metadata as the first step toward tab-based worker queues.
|
|
18
|
+
- Added experimental `/queue add` and `/queue list` commands for enqueueing the current tab session into the worker queue.
|
|
19
|
+
- Added a first session-backed worker queue runner that executes one queued job and marks it ready for review after committing changes.
|
|
20
|
+
- Added clean-workspace blocking for queued workers so jobs do not start on top of existing local changes.
|
|
21
|
+
- Added `/queue run` to manually drain queued tab worker jobs sequentially until the queue is empty or a job needs attention.
|
|
22
|
+
- Added worker git stash helpers as groundwork for cooperative queue suspension.
|
|
23
|
+
- Added explicit queue runner suspend/resume primitives that stash and restore a running job's workspace changes.
|
|
24
|
+
- Added `/queue suspend <id>` and `/queue resume <id>` commands for manually parking and resuming queued worker jobs.
|
|
25
|
+
- Added `/queue open <id>` to open a queued worker's session for review or follow-up.
|
|
26
|
+
- Added `/diff` to open the chronological file changes recorded in the current session in the integrated diff viewer.
|
|
27
|
+
- Added `/scratchpad [text|markdown|ruby]` for opening unsaved editor buffers, including Vibe `:w filename` save-as support and Ruby `:run`/Modern `Ctrl+R` output written after `__END__`.
|
|
28
|
+
- Added `/pty <command>` and the `ekwsh` `pty <command>` built-in for explicit interactive PTY passthrough sessions, enabling terminal-owned tools such as pagers to run from Kward.
|
|
29
|
+
- Added minimal PTY execution for external `ekwsh` commands so terminal-aware tools can detect a TTY and terminal width.
|
|
30
|
+
- Added Ctrl+C cancellation for running `ekwsh` commands and preserved tab-switch actions while shell commands are active.
|
|
31
|
+
- Added quoted path completion and cached `$PATH` executable completion for `ekwsh`.
|
|
32
|
+
- Added streaming `ekwsh` command output in the TUI transcript while commands run.
|
|
33
|
+
- Added separate workspace-scoped `ekwsh` command history so embedded shell input no longer shares normal prompt history.
|
|
34
|
+
- Added structured RPC `runtime/updateSetting` `defaultModel` values so clients can send provider and model separately while keeping the existing string format.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Changed PTY-backed `ekwsh` commands to refresh terminal window size while running so long-lived commands can adapt to resizes.
|
|
39
|
+
- Changed `ekwsh` to default `GIT_PAGER` to `cat`, while preserving user-provided values, so Git commands do not unexpectedly enter an interactive pager under PTY execution.
|
|
40
|
+
- Improved `ekwsh` POSIX-oriented built-ins, including `exit [status]`, stricter `cd`/`pwd`, `export NAME`, assignment persistence, `unalias`, and shared alias-name validation.
|
|
41
|
+
- Changed `ekwsh` configuration to prefer a POSIX `/bin/sh` default shell and validate runtime settings for command timeout, output cap, and shell history size.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Normalized ordinary PTY line endings in `ekwsh` command output so transcripts avoid stray carriage returns.
|
|
46
|
+
- Added `ekwsh` timeout and output-limit enforcement for external commands using the shared local command runner.
|
|
47
|
+
- Consolidated workspace shell command execution on a shared local command runner with timeout, cancellation, bounded capture, and optional streaming support.
|
|
48
|
+
- Fixed `ekwsh` shell output sanitization so unsafe terminal controls are stripped before command output is shown while SGR color is preserved.
|
|
49
|
+
- Split Vibe editor insert/readline key handling into a focused mixin without changing editor behavior.
|
|
50
|
+
- Consolidated compaction message-field reads through the shared message access helper.
|
|
51
|
+
- Consolidated RPC transcript tool metadata normalization with tool event metadata so tool names, args, diffs, and changed files stay aligned.
|
|
52
|
+
- Fixed RPC tool capabilities so `changedFiles` is advertised when emitted in tool results.
|
|
53
|
+
- Removed a stale `count-tests` CLI branch that could crash instead of treating the input as a prompt.
|
|
54
|
+
- Fixed tab failures and cancellations so red tab states always emit a runtime message explaining what happened.
|
|
55
|
+
- Fixed model and reasoning changes from CLI/RPC settings so active session runtime metadata is persisted before the next turn.
|
|
56
|
+
- Fixed `context_for_task` so candidate files with no task matches return a clear no-match message instead of only a header.
|
|
57
|
+
- Fixed workspace file tools so expected filesystem permission and path-type errors are returned as tool errors instead of aborting a turn.
|
|
58
|
+
- Fixed composer `Ctrl+C` so it no longer exits the app when no process is running.
|
|
59
|
+
- Fixed the Git diff viewer so `Ctrl+C` and terminal-forwarded `Cmd+C` copy selected text.
|
|
60
|
+
- Fixed pasted or dropped shell-escaped image paths so the composer hides the path text after adding the image badge.
|
|
61
|
+
- Fixed `read_skill` tool transcript rendering so skill frontmatter starts on the line after the tool label.
|
|
62
|
+
- Fixed built-in editor soft-wrap vertical movement so moving up or down preserves the visual column across wrapped visual rows and logical line boundaries.
|
|
63
|
+
|
|
5
64
|
## [0.72.0] - 2026-06-28
|
|
6
65
|
|
|
7
66
|
### Added
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
kward (0.
|
|
4
|
+
kward (0.73.1)
|
|
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.
|
|
149
|
+
kward (0.73.1)
|
|
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/configuration.md
CHANGED
|
@@ -58,7 +58,7 @@ aliases:
|
|
|
58
58
|
|
|
59
59
|
`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.
|
|
60
60
|
|
|
61
|
-
`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`, and `exit` take precedence over aliases. Run `alias` inside `ekwsh` to list configured aliases. Aliases are also included in command-name Tab completion.
|
|
61
|
+
`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.
|
|
62
62
|
|
|
63
63
|
## Provider and model settings
|
|
64
64
|
|
data/doc/editor.md
CHANGED
|
@@ -32,12 +32,33 @@ For a nested project tree, run:
|
|
|
32
32
|
|
|
33
33
|
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.
|
|
34
34
|
|
|
35
|
+
For an unsaved buffer, open a scratchpad:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
/scratchpad
|
|
39
|
+
/scratchpad markdown
|
|
40
|
+
/scratchpad ruby
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Scratchpads start as virtual editor buffers named `scratchpad.txt`, `scratchpad.md`, or `scratchpad.rb`. In Vibe mode, save one to a real file with `:w filename`.
|
|
44
|
+
Ruby scratchpads can run with `:run` in Vibe mode or `Ctrl+R` in Modern mode; Kward executes the buffer and writes combined output after `__END__`, replacing any previous output there.
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
puts "foo"
|
|
48
|
+
|
|
49
|
+
__END__
|
|
50
|
+
foo
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The next run receives the current `__END__` section as Ruby `DATA`, then replaces it with the new output.
|
|
54
|
+
|
|
35
55
|
You can also type a relative path yourself and press `Enter`. If the file does not exist, Kward asks whether to create it.
|
|
36
56
|
|
|
37
57
|
A few things to know:
|
|
38
58
|
|
|
39
59
|
- `$` only opens the editor when it is the first character in the composer.
|
|
40
|
-
-
|
|
60
|
+
- `/scratchpad` opens a plain-text scratchpad; pass `markdown` or `ruby` to pick another mode.
|
|
61
|
+
- Once a file or scratchpad opens, the composer becomes the editor.
|
|
41
62
|
- Save or quit to return to normal chat.
|
|
42
63
|
- If the file changed on disk while you were editing, Kward asks before overwriting it.
|
|
43
64
|
- If you quit with unsaved changes, Kward asks before discarding them.
|
|
@@ -51,7 +72,7 @@ $doc/editor.md
|
|
|
51
72
|
1. Type `$doc/editor.md` in the composer.
|
|
52
73
|
2. Pick the file from the matching results, or press `Enter` if the path is already complete.
|
|
53
74
|
3. Edit the file.
|
|
54
|
-
4. Save with `Ctrl+S` in Modern mode, `C-x C-s` in Emacs mode, or `:w` in Vibe mode.
|
|
75
|
+
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`.
|
|
55
76
|
5. Quit with `Ctrl+Q`, `C-x C-c`, or `:q`.
|
|
56
77
|
6. Continue chatting with Kward.
|
|
57
78
|
|
data/doc/git.md
CHANGED
|
@@ -54,6 +54,7 @@ Useful keys in the diff viewer:
|
|
|
54
54
|
| `Page Up` / `Page Down` | Scroll by a page. |
|
|
55
55
|
| `Home` / `End` | Move within the current line. |
|
|
56
56
|
| `/` or `Ctrl+F` | Search within the diff. |
|
|
57
|
+
| `Ctrl+C` / `Cmd+C` | Copy the current selection. |
|
|
57
58
|
| `Enter` | Confirm the current search. |
|
|
58
59
|
| `Esc` | Cancel search, or close the diff viewer. |
|
|
59
60
|
| `Ctrl+Q` | Close the diff viewer. |
|
data/doc/rpc.md
CHANGED
|
@@ -50,7 +50,7 @@ Result fields:
|
|
|
50
50
|
Detailed capability fields include:
|
|
51
51
|
|
|
52
52
|
- `transcript`: Tauren transcript format support, including normalized messages, image/tool support, compaction summaries, and restored assistant reasoning as Pi-compatible `thinking` content blocks.
|
|
53
|
-
- `sessions`: explicit RPC session mode, JSONL persistence, supported session methods, startup auto-resume capability/default, immediate transcript support for auto-resume, RPC list support, supported linear-session fork methods, supported compaction, supported tree navigation with labels and branch summarization,
|
|
53
|
+
- `sessions`: explicit RPC session mode, JSONL persistence, supported session methods, startup auto-resume capability/default, immediate transcript support for auto-resume, RPC list support, supported linear-session fork methods, supported compaction, supported tree navigation with labels and branch summarization, explicit unsupported import support, and unsupported live session updates reported with `notification: "session/updated"`.
|
|
54
54
|
- `turns`: async turn mode, per-session concurrency, provider-gated native busy-input steering, queued follow-up input, best-effort cancellation, and recent in-memory event replay behavior.
|
|
55
55
|
- `events`: `turn/event` notification details, assistant/reasoning event names, normalized tool metadata, diff result support, configured workspace guardrail status, focused context and context-budget stats tool support, and explicit unsupported shell changed-file detection/session update flags.
|
|
56
56
|
- `attachments`: supported input attachment contract for `turns/start`, with accepted base64 image MIME types and a stable max byte value.
|
|
@@ -405,7 +405,7 @@ Params:
|
|
|
405
405
|
|
|
406
406
|
- `sessionId`: active RPC session ID.
|
|
407
407
|
- `settingId`: currently `defaultModel` or `defaultThinkingLevel`.
|
|
408
|
-
- `value`: setting value. `defaultModel` accepts `Provider/model-id` and preserves slashes after the provider separator.
|
|
408
|
+
- `value`: setting value. `defaultModel` accepts a structured object such as `{ "provider": "OpenRouter", "model": "anthropic/claude-sonnet-4.5" }`. For compatibility, it also accepts `Provider/model-id` strings and preserves slashes after the provider separator.
|
|
409
409
|
|
|
410
410
|
Applies the setting live by updating config and refreshing client config. Unsupported setting IDs are rejected.
|
|
411
411
|
|
data/doc/shell.md
CHANGED
|
@@ -12,7 +12,7 @@ This makes it good for:
|
|
|
12
12
|
- using project aliases,
|
|
13
13
|
- keeping command output visible beside the current Kward session.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Use `/pty <command>` when you intentionally want to hand the terminal to a full-screen interactive command such as `less`.
|
|
16
16
|
|
|
17
17
|
## Start shell mode
|
|
18
18
|
|
|
@@ -47,7 +47,7 @@ or press Ctrl+D on an empty shell prompt.
|
|
|
47
47
|
|
|
48
48
|
## How commands run
|
|
49
49
|
|
|
50
|
-
`ekwsh` runs each command through
|
|
50
|
+
`ekwsh` is POSIX-oriented and runs each command through `/bin/sh` using the shell's `-c` mode by default. You can configure another POSIX-compatible shell in `ekwsh.yml`. The shell is intentionally not started as a login shell so Kward-managed environment values, such as configured PATH entries, are not overwritten by login startup files.
|
|
51
51
|
|
|
52
52
|
The current directory and exported environment are tracked by Kward between commands, so this works as expected:
|
|
53
53
|
|
|
@@ -61,7 +61,9 @@ unset FOO
|
|
|
61
61
|
|
|
62
62
|
`cd` changes only the embedded shell's current directory. It does not change Kward's workspace root or the process directory used by the rest of Kward.
|
|
63
63
|
|
|
64
|
-
Shell output
|
|
64
|
+
Shell output streams into the transcript area as commands run, but it is not added to the AI conversation history. Simple assignment-only commands such as `FOO=bar` persist into the embedded shell environment for later commands.
|
|
65
|
+
|
|
66
|
+
Shell commands are persisted in a separate workspace-scoped shell history. They do not share the normal Kward prompt history used for chat prompts. The shell history limit is controlled by `history_limit` in `ekwsh.yml`.
|
|
65
67
|
|
|
66
68
|
## Built-ins
|
|
67
69
|
|
|
@@ -71,9 +73,10 @@ Shell output is shown in the transcript area, but it is not added to the AI conv
|
|
|
71
73
|
| --- | --- |
|
|
72
74
|
| `cd [dir]` | Change the embedded shell directory. Supports `cd`, `cd -`, and normal relative paths. |
|
|
73
75
|
| `pwd` | Print the embedded shell directory. |
|
|
74
|
-
| `export KEY=value` | Set an environment variable for later commands. |
|
|
76
|
+
| `export KEY[=value]` | Set or mark an environment variable for later commands. `export` and `export -p` list variables. |
|
|
75
77
|
| `unset KEY` | Remove an environment variable from later commands. |
|
|
76
|
-
| `alias [name]` | List configured aliases,
|
|
78
|
+
| `alias [name]` | List configured aliases, show specific configured aliases, or set `alias name=value`. |
|
|
79
|
+
| `unalias name` / `unalias -a` | Remove configured aliases. |
|
|
77
80
|
| `clear` | Clear Kward's visible transcript. |
|
|
78
81
|
| `exit` / `logout` | Leave shell mode. |
|
|
79
82
|
|
|
@@ -92,7 +95,7 @@ This means each tab can have its own:
|
|
|
92
95
|
- visible shell transcript,
|
|
93
96
|
- command prompt state.
|
|
94
97
|
|
|
95
|
-
Kward's normal tab-switching shortcuts continue to work in shell mode.
|
|
98
|
+
Kward's normal tab-switching shortcuts continue to work in shell mode. If you switch tabs while a shell command is running, Kward cancels that foreground command and requeues the tab action so the TUI can switch cleanly.
|
|
96
99
|
|
|
97
100
|
## Tab completion
|
|
98
101
|
|
|
@@ -123,7 +126,14 @@ cat my<Tab>
|
|
|
123
126
|
# => cat my\ file.txt
|
|
124
127
|
```
|
|
125
128
|
|
|
126
|
-
|
|
129
|
+
Quoted path tokens are completed in the same quoting style:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
cat "my<Tab>
|
|
133
|
+
# => cat "my file.txt
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
If there are multiple candidates, Kward applies any common prefix. When there is no longer common prefix, it prints a compact candidate list in the transcript. Command-name completion caches `$PATH` executables and refreshes when `PATH` changes through shell assignment, `export`, or `unset`.
|
|
127
137
|
|
|
128
138
|
## Colors and ANSI output
|
|
129
139
|
|
|
@@ -168,6 +178,11 @@ If `KWARD_CONFIG_PATH` points to another config file, `ekwsh.yml` is read from t
|
|
|
168
178
|
Example:
|
|
169
179
|
|
|
170
180
|
```yaml
|
|
181
|
+
shell: /bin/sh
|
|
182
|
+
# timeout_seconds: 300
|
|
183
|
+
# max_output_bytes: 1048576
|
|
184
|
+
# history_limit: 1000
|
|
185
|
+
|
|
171
186
|
env:
|
|
172
187
|
FORCE_COLOR: "1"
|
|
173
188
|
CLICOLOR_FORCE: "1"
|
|
@@ -181,6 +196,19 @@ aliases:
|
|
|
181
196
|
t: "bundle exec ruby -Itest"
|
|
182
197
|
```
|
|
183
198
|
|
|
199
|
+
### Runtime settings
|
|
200
|
+
|
|
201
|
+
`ekwsh` accepts these top-level runtime settings:
|
|
202
|
+
|
|
203
|
+
| Setting | Default | What it does |
|
|
204
|
+
| --- | --- | --- |
|
|
205
|
+
| `shell` | `/bin/sh` | Absolute path to the POSIX-compatible shell used with `-c`. Invalid or relative paths fall back to `/bin/sh`. |
|
|
206
|
+
| `timeout_seconds` | `300` | Maximum runtime for one command. |
|
|
207
|
+
| `max_output_bytes` | `1048576` | Maximum captured output for one command. |
|
|
208
|
+
| `history_limit` | `1000` | Maximum persisted shell history entries per workspace. |
|
|
209
|
+
|
|
210
|
+
When a command exceeds `timeout_seconds`, `ekwsh` terminates it and reports the timeout. When command output exceeds `max_output_bytes`, `ekwsh` terminates the command, keeps bounded output, and reports the output limit. Press Ctrl+C while a command is running to terminate that command and return to the shell prompt without exiting Kward.
|
|
211
|
+
|
|
184
212
|
### Environment variables
|
|
185
213
|
|
|
186
214
|
`env` values are applied when shell mode starts, after Kward's conservative color defaults.
|
|
@@ -252,6 +280,25 @@ alias ll gs
|
|
|
252
280
|
|
|
253
281
|
Aliases also appear in command-name Tab completion.
|
|
254
282
|
|
|
283
|
+
## Interactive PTY passthrough
|
|
284
|
+
|
|
285
|
+
Use `/pty <command>` for commands that need to own the terminal temporarily, such as pagers:
|
|
286
|
+
|
|
287
|
+
```text
|
|
288
|
+
/pty git log
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Inside `/shell`, use the `pty` built-in so the command inherits the embedded shell's current directory and environment:
|
|
292
|
+
|
|
293
|
+
```sh
|
|
294
|
+
pty git log
|
|
295
|
+
pty vim README.md
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Kward runs the command in a PTY, forwards your keyboard input to the process, and streams the process output directly to the terminal. This lets tools such as `less` receive keys like Space, `/`, `n`, and `q` normally. When the command exits, Kward restores its prompt and records only a short session summary in the transcript instead of raw full-screen terminal control output.
|
|
299
|
+
|
|
300
|
+
`/pty` and shell `pty` are explicit on purpose. Normal `/shell` commands remain captured and transcript-friendly; `pty` is for commands where the child process should temporarily own the terminal.
|
|
301
|
+
|
|
255
302
|
## `/shell` versus `!command`
|
|
256
303
|
|
|
257
304
|
Kward has two ways to run local commands yourself:
|
|
@@ -279,8 +326,7 @@ Current limitations:
|
|
|
279
326
|
- no job control,
|
|
280
327
|
- no persistent shell functions,
|
|
281
328
|
- no shell startup file sourcing,
|
|
282
|
-
- no full-screen terminal applications,
|
|
283
329
|
- no native readline from your login shell,
|
|
284
|
-
- command output is
|
|
330
|
+
- normal `/shell` command output is transcript-sanitized rather than treated as a full terminal UI.
|
|
285
331
|
|
|
286
|
-
|
|
332
|
+
External `/shell` commands run under a minimal PTY so terminal-aware tools can detect TTY output and terminal width. Full-screen interactive programs should still use explicit `/pty <command>` passthrough.
|
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. Kward keeps the tab bar, composer editing, and transcript rendering while each command runs through your configured shell. Built-ins such as `cd`, `pwd`, `export`, `unset`, `alias`, `clear`, and `exit` maintain shell-mode state between commands. Plain Tab completes built-in command names, configured aliases, executables from `$PATH`, and file paths from the shell's current directory; `cd` completion suggests directories only. `ekwsh` preserves safe ANSI SGR color/style output while stripping terminal-control sequences that could corrupt the TUI, and sets conservative color-friendly environment defaults such as `CLICOLOR=1`, `COLORTERM=truecolor`, and `TERM=xterm-256color` when needed.
|
|
81
|
+
`/shell` opens `ekwsh`, a Kward-native command mode. Kward keeps the tab bar, composer editing, and transcript rendering while each command runs through your configured shell. Built-ins such as `cd`, `pwd`, `export`, `unset`, `alias`, `clear`, `pty`, and `exit` maintain shell-mode state between commands. Plain Tab completes built-in command names, configured aliases, executables from `$PATH`, and file paths from the shell's current directory; `cd` completion suggests directories only. `ekwsh` preserves safe ANSI SGR color/style output while stripping terminal-control sequences that could corrupt the TUI, and sets conservative color-friendly environment defaults such as `CLICOLOR=1`, `COLORTERM=truecolor`, and `TERM=xterm-256color` when needed. Use `pty git log` or `/pty git log` when you intentionally want to hand the terminal to an interactive tool such as `less` or `vim`. You can set global shell env vars and aliases in `~/.kward/ekwsh.yml`; see [Configuration](configuration.md).
|
|
82
82
|
|
|
83
83
|
## Shell commands
|
|
84
84
|
|
|
@@ -114,8 +114,10 @@ Slash commands run local actions in the current session. Most do not send a prom
|
|
|
114
114
|
| `/model` | choose the active model. |
|
|
115
115
|
| `/reasoning` | choose reasoning effort. |
|
|
116
116
|
| `/git` | review uncommitted changes, stage files, and commit. |
|
|
117
|
+
| `/diff` | open the file changes recorded in the current session. |
|
|
117
118
|
| `/files` | browse project files in a nested tree and open them in the editor. |
|
|
118
119
|
| `/shell` | run workspace commands in the embedded Kward shell. |
|
|
120
|
+
| `/pty <command>` | hand the terminal to an interactive command such as `git log`/`less` or `vim`. |
|
|
119
121
|
| `/settings` | configure prompt overlays. |
|
|
120
122
|
| `/status` | see session, model, and context status. |
|
|
121
123
|
| `/new` | start a fresh session in the current tab. |
|
|
@@ -141,10 +143,34 @@ Slash commands run local actions in the current session. Most do not send a prom
|
|
|
141
143
|
| `/redraw` | fix terminal drawing after resize or glitches. |
|
|
142
144
|
| `/reload` | reload installed plugins. |
|
|
143
145
|
| `/workers` | open the experimental worker pipeline (`new`, `do <task>`, or `list`). |
|
|
146
|
+
| `/queue` | manage the experimental tab-backed worker queue (`add`, `list`, `open <id>`, `run`, `suspend <id>`, or `resume <id>`). |
|
|
144
147
|
| `/exit` | leave Kward. |
|
|
145
148
|
|
|
146
149
|
Prompt templates and plugins can add more slash commands.
|
|
147
150
|
|
|
151
|
+
### Experimental tab-backed worker queue
|
|
152
|
+
|
|
153
|
+
Start Kward with `--experimental-workers` to try the tab-backed worker queue. The queue is an MVP for turning an existing tab/session into implementation work that can be reviewed later.
|
|
154
|
+
|
|
155
|
+
Typical flow:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
/plan Add retry handling to webhook delivery
|
|
159
|
+
/queue add
|
|
160
|
+
/queue run
|
|
161
|
+
/queue open <id>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`/queue add` stores the current tab session as a queued worker job. `/queue run` drains queued jobs one at a time. Each job continues its saved session as an implementation worker, starts only from a clean git workspace, commits any resulting changes, and then becomes `ready_for_review`. Use `/queue open <id>` to inspect the worker session, test the feature yourself, and continue the same session if a follow-up fix is needed.
|
|
165
|
+
|
|
166
|
+
Current MVP limitations:
|
|
167
|
+
|
|
168
|
+
- The queue is manual: run it with `/queue run` when you want it to work.
|
|
169
|
+
- Jobs run sequentially and stop when one becomes `blocked` or `failed`.
|
|
170
|
+
- A job will not start if the workspace is dirty; clean, commit, or stash your changes first.
|
|
171
|
+
- `/queue suspend <id>` and `/queue resume <id>` provide explicit stash-based parking primitives, but automatic foreground-tool-triggered yielding is not wired yet.
|
|
172
|
+
- The queue is experimental and stores metadata locally in Kward's config directory.
|
|
173
|
+
|
|
148
174
|
## Prompt history
|
|
149
175
|
|
|
150
176
|
In interactive mode, Kward keeps prompt history per workspace under `~/.kward/history/`. Press Up/Down to recall previous prompts across restarts.
|
data/lib/kward/ansi.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# Namespace for the Kward CLI agent runtime.
|
|
2
2
|
module Kward
|
|
3
|
-
# ANSI
|
|
3
|
+
# ANSI SGR styling and terminal-text helpers.
|
|
4
|
+
#
|
|
5
|
+
# Terminal control output sequences live in `TerminalSequences`, and input key
|
|
6
|
+
# sequences live in `TerminalKeys`. This module owns text-level concerns:
|
|
7
|
+
# colorizing strings, stripping/sanitizing escape sequences, visible wrapping,
|
|
8
|
+
# and lightweight Markdown rendering for terminal output.
|
|
4
9
|
module ANSI
|
|
5
|
-
ESCAPE_PATTERN = /\e\[[0-9;?]*[ -\/]*[@-~]/.freeze
|
|
6
10
|
SGR_PATTERN = /\e\[[0-9;:]*m/.freeze
|
|
7
|
-
OSC_PATTERN = /\e\][^\a]*(?:\a|\e\\)/m.freeze
|
|
8
|
-
STRING_ESCAPE_PATTERN = /\e[P_X^][\s\S]*?\e\\/m.freeze
|
|
9
11
|
STYLES = {
|
|
10
12
|
reset: 0,
|
|
11
13
|
bold: 1,
|
|
@@ -52,13 +54,24 @@ module Kward
|
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def strip(text)
|
|
55
|
-
text
|
|
57
|
+
strip_control_sequences(text)
|
|
56
58
|
end
|
|
57
59
|
|
|
60
|
+
# Removes terminal escape/control sequences while preserving visible text.
|
|
61
|
+
def strip_control_sequences(text)
|
|
62
|
+
scan_escape_tokens(text).each_with_object(+"") do |token, stripped|
|
|
63
|
+
stripped << token[:text] unless token[:escape]
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Drops unsafe terminal controls from transcript text while preserving SGR color.
|
|
58
68
|
def sanitize_transcript(text)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
scan_escape_tokens(text).each_with_object(+"") do |token, sanitized|
|
|
70
|
+
if token[:escape]
|
|
71
|
+
sanitized << token[:text] if token[:text].match?(SGR_PATTERN)
|
|
72
|
+
else
|
|
73
|
+
sanitized << token[:text]
|
|
74
|
+
end
|
|
62
75
|
end
|
|
63
76
|
end
|
|
64
77
|
|
|
@@ -67,28 +80,27 @@ module Kward
|
|
|
67
80
|
rows = []
|
|
68
81
|
current = +""
|
|
69
82
|
visible_width = 0
|
|
70
|
-
string = text.to_s
|
|
71
|
-
index = 0
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
if
|
|
84
|
+
scan_escape_tokens(text).each do |token|
|
|
85
|
+
if token[:escape]
|
|
86
|
+
next unless token[:text].match?(SGR_PATTERN)
|
|
87
|
+
|
|
75
88
|
if current.empty? && rows.any?
|
|
76
|
-
rows[-1] <<
|
|
89
|
+
rows[-1] << token[:text]
|
|
77
90
|
else
|
|
78
|
-
current <<
|
|
91
|
+
current << token[:text]
|
|
79
92
|
end
|
|
80
|
-
index += match[0].length
|
|
81
93
|
next
|
|
82
94
|
end
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
token[:text].each_char do |char|
|
|
97
|
+
current << char
|
|
98
|
+
visible_width += 1
|
|
99
|
+
if visible_width >= line_width
|
|
100
|
+
rows << current
|
|
101
|
+
current = +""
|
|
102
|
+
visible_width = 0
|
|
103
|
+
end
|
|
92
104
|
end
|
|
93
105
|
end
|
|
94
106
|
|
|
@@ -96,6 +108,33 @@ module Kward
|
|
|
96
108
|
rows
|
|
97
109
|
end
|
|
98
110
|
|
|
111
|
+
# Splits text into visible chunks and terminal escape sequence chunks.
|
|
112
|
+
def scan_escape_tokens(text)
|
|
113
|
+
string = text.to_s
|
|
114
|
+
tokens = []
|
|
115
|
+
index = 0
|
|
116
|
+
while index < string.length
|
|
117
|
+
if string[index] == "\e" && (escape = escape_sequence_at(string, index))
|
|
118
|
+
tokens << { text: escape, escape: true }
|
|
119
|
+
index += escape.length
|
|
120
|
+
next
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
next_escape = string.index("\e", index) || string.length
|
|
124
|
+
tokens << { text: string[index...next_escape], escape: false } if next_escape > index
|
|
125
|
+
index = next_escape
|
|
126
|
+
end
|
|
127
|
+
tokens
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def escape_sequence_at(string, index)
|
|
131
|
+
chunk = string[index..]
|
|
132
|
+
chunk.match(/\A\e\][^\a]*(?:\a|\e\\)/m)&.[](0) ||
|
|
133
|
+
chunk.match(/\A\e[P_X^][\s\S]*?\e\\/m)&.[](0) ||
|
|
134
|
+
chunk.match(/\A\e\[[0-9;:?]*[ -\/]*[@-~]/)&.[](0) ||
|
|
135
|
+
chunk[0, 2]
|
|
136
|
+
end
|
|
137
|
+
|
|
99
138
|
def markdown(text, enabled: enabled?)
|
|
100
139
|
string = text.to_s
|
|
101
140
|
lines = string.lines(chomp: true)
|
data/lib/kward/cli/plugins.rb
CHANGED
|
@@ -88,7 +88,7 @@ module Kward
|
|
|
88
88
|
def builtin_slash_commands
|
|
89
89
|
return BUILTIN_SLASH_COMMANDS if experimental_workers_enabled?
|
|
90
90
|
|
|
91
|
-
BUILTIN_SLASH_COMMANDS.reject { |command| command[:name]
|
|
91
|
+
BUILTIN_SLASH_COMMANDS.reject { |command| %w[workers queue].include?(command[:name]) }
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def builtin_slash_command_names
|
data/lib/kward/cli/rendering.rb
CHANGED
|
@@ -318,7 +318,10 @@ module Kward
|
|
|
318
318
|
end
|
|
319
319
|
|
|
320
320
|
def tool_summary_display_text(summary)
|
|
321
|
-
summary.to_s
|
|
321
|
+
text = summary.to_s
|
|
322
|
+
return text if text.start_with?("read_skill:\n")
|
|
323
|
+
|
|
324
|
+
text.sub("\n", "\n\n")
|
|
322
325
|
end
|
|
323
326
|
|
|
324
327
|
def start_stream_block(label)
|