kward 0.83.0 → 0.85.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 (193) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +102 -15
  3. data/CONTRIBUTING.md +74 -0
  4. data/Gemfile.lock +8 -2
  5. data/README.md +21 -1
  6. data/Rakefile +46 -2
  7. data/SECURITY.md +31 -0
  8. data/doc/agent-tools.md +13 -1
  9. data/doc/api.md +21 -2
  10. data/doc/composer.md +2 -2
  11. data/doc/configuration.md +95 -25
  12. data/doc/editor.md +28 -13
  13. data/doc/extensibility.md +2 -1
  14. data/doc/files.md +8 -4
  15. data/doc/getting-started.md +3 -0
  16. data/doc/git.md +3 -1
  17. data/doc/pan.md +25 -15
  18. data/doc/permissions.md +4 -4
  19. data/doc/platform-support.md +48 -0
  20. data/doc/plugins.md +464 -15
  21. data/doc/rpc.md +154 -16
  22. data/doc/sandboxing.md +11 -5
  23. data/doc/security.md +10 -3
  24. data/doc/session-management.md +5 -4
  25. data/doc/shell.md +62 -45
  26. data/doc/tabs.md +6 -2
  27. data/doc/transports.md +15 -0
  28. data/doc/troubleshooting.md +12 -2
  29. data/doc/usage.md +9 -6
  30. data/doc/workspace-tools.md +9 -0
  31. data/examples/plugins/space_invaders.rb +1 -1
  32. data/examples/plugins/stardate_footer.rb +2 -2
  33. data/examples/plugins/telegram/plugin.rb +1 -1
  34. data/kward.gemspec +5 -4
  35. data/lib/kward/agent.rb +30 -14
  36. data/lib/kward/cli/auth_commands.rb +34 -13
  37. data/lib/kward/cli/commands.rb +83 -62
  38. data/lib/kward/cli/compaction.rb +9 -3
  39. data/lib/kward/cli/doctor.rb +39 -17
  40. data/lib/kward/cli/hook_commands.rb +22 -12
  41. data/lib/kward/cli/interactive_turn.rb +48 -7
  42. data/lib/kward/cli/plugins.rb +81 -12
  43. data/lib/kward/cli/project_skills_commands.rb +8 -4
  44. data/lib/kward/cli/prompt_interface.rb +52 -5
  45. data/lib/kward/cli/rendering.rb +18 -9
  46. data/lib/kward/cli/runtime_helpers.rb +228 -71
  47. data/lib/kward/cli/sessions.rb +9 -5
  48. data/lib/kward/cli/settings/menus.rb +745 -0
  49. data/lib/kward/cli/settings/model.rb +327 -0
  50. data/lib/kward/cli/settings.rb +6 -1055
  51. data/lib/kward/cli/slash_commands.rb +56 -17
  52. data/lib/kward/cli/tabs.rb +244 -33
  53. data/lib/kward/cli/tool_summaries.rb +14 -0
  54. data/lib/kward/{cli_transcript_formatter.rb → cli/transcript_formatter.rb} +14 -7
  55. data/lib/kward/cli/worktrees.rb +65 -2
  56. data/lib/kward/cli.rb +70 -30
  57. data/lib/kward/compactor.rb +18 -7
  58. data/lib/kward/config/core.rb +389 -0
  59. data/lib/kward/config/extensions.rb +96 -0
  60. data/lib/kward/config/prompts.rb +313 -0
  61. data/lib/kward/config/settings.rb +250 -0
  62. data/lib/kward/config_files.rb +14 -994
  63. data/lib/kward/conversation.rb +31 -2
  64. data/lib/kward/image_attachments.rb +1 -1
  65. data/lib/kward/model/client.rb +36 -24
  66. data/lib/kward/model/copilot_models.rb +2 -2
  67. data/lib/kward/model/model_info.rb +20 -3
  68. data/lib/kward/{openrouter_model_cache.rb → model/openrouter_model_cache.rb} +3 -3
  69. data/lib/kward/model/payloads.rb +12 -3
  70. data/lib/kward/model/provider_catalog.rb +5 -0
  71. data/lib/kward/model/stream_parser.rb +20 -4
  72. data/lib/kward/model/typesafe_client.rb +78 -0
  73. data/lib/kward/pan/index.html.erb +3 -3
  74. data/lib/kward/pan/server.rb +33 -10
  75. data/lib/kward/permissions/policy.rb +6 -2
  76. data/lib/kward/plugin_registry.rb +2 -659
  77. data/lib/kward/plugins/actions.rb +453 -0
  78. data/lib/kward/plugins/chat_contract.rb +121 -0
  79. data/lib/kward/{plugin_chat_runtime.rb → plugins/chat_runtime.rb} +62 -18
  80. data/lib/kward/plugins/host.rb +232 -0
  81. data/lib/kward/plugins/registry.rb +1190 -0
  82. data/lib/kward/plugins/resources.rb +206 -0
  83. data/lib/kward/plugins/turn_request.rb +36 -0
  84. data/lib/kward/plugins/ui.rb +219 -0
  85. data/lib/kward/prompt_interface/composer_renderer.rb +44 -40
  86. data/lib/kward/prompt_interface/composer_state.rb +33 -24
  87. data/lib/kward/prompt_interface/editor/auto_indent.rb +24 -22
  88. data/lib/kward/prompt_interface/editor/controller.rb +30 -33
  89. data/lib/kward/prompt_interface/editor/endwise.rb +13 -4
  90. data/lib/kward/prompt_interface/editor/markdown_code_block.rb +136 -0
  91. data/lib/kward/prompt_interface/editor/modes/vibe.rb +289 -44
  92. data/lib/kward/prompt_interface/editor/renderer.rb +108 -6
  93. data/lib/kward/prompt_interface/editor/runner.rb +362 -0
  94. data/lib/kward/prompt_interface/editor/runner_state.rb +78 -0
  95. data/lib/kward/prompt_interface/editor/scratchpad_languages.rb +74 -0
  96. data/lib/kward/prompt_interface/editor/scratchpad_runner.rb +182 -0
  97. data/lib/kward/prompt_interface/editor/state.rb +11 -11
  98. data/lib/kward/prompt_interface/editor/syntax_highlighter.rb +68 -6
  99. data/lib/kward/prompt_interface/editor/vibe_state.rb +3 -3
  100. data/lib/kward/prompt_interface/file_overlay.rb +71 -15
  101. data/lib/kward/prompt_interface/key_handler.rb +67 -0
  102. data/lib/kward/prompt_interface/layout.rb +1 -1
  103. data/lib/kward/prompt_interface/overlay_renderer.rb +7 -5
  104. data/lib/kward/prompt_interface/plugin_ui_requests.rb +82 -0
  105. data/lib/kward/prompt_interface/project_browser.rb +415 -14
  106. data/lib/kward/prompt_interface/runtime_state.rb +56 -2
  107. data/lib/kward/prompt_interface/screen.rb +11 -4
  108. data/lib/kward/prompt_interface/selection_prompt.rb +3 -1
  109. data/lib/kward/prompt_interface/slash_overlay.rb +19 -4
  110. data/lib/kward/prompt_interface/transcript_renderer.rb +12 -7
  111. data/lib/kward/prompt_interface.rb +151 -27
  112. data/lib/kward/prompts/commands.rb +3 -2
  113. data/lib/kward/prompts.rb +1 -1
  114. data/lib/kward/{adaptive_pty_output_sink.rb → pty/adaptive_output_sink.rb} +1 -1
  115. data/lib/kward/pty/detached_run.rb +44 -0
  116. data/lib/kward/{interactive_pty_runner.rb → pty/interactive_runner.rb} +104 -30
  117. data/lib/kward/{local_command_runner.rb → pty/local_command_runner.rb} +1 -1
  118. data/lib/kward/{local_pty_command_runner.rb → pty/local_pty_runner.rb} +3 -9
  119. data/lib/kward/{pty_output_sink.rb → pty/output_sink.rb} +52 -0
  120. data/lib/kward/{pty_transcript_normalizer.rb → pty/transcript_normalizer.rb} +1 -1
  121. data/lib/kward/rpc/plugin_chat_manager.rb +30 -10
  122. data/lib/kward/rpc/prompt_bridge.rb +25 -0
  123. data/lib/kward/rpc/server.rb +91 -12
  124. data/lib/kward/rpc/session_manager.rb +147 -44
  125. data/lib/kward/rpc/session_tree_rows.rb +2 -2
  126. data/lib/kward/rpc/tool_metadata.rb +1 -1
  127. data/lib/kward/rpc/transcript_normalizer.rb +7 -3
  128. data/lib/kward/sandbox/command_runner.rb +1 -1
  129. data/lib/kward/{session_catalog.rb → sessions/catalog.rb} +1 -1
  130. data/lib/kward/{session_store.rb → sessions/store.rb} +9 -9
  131. data/lib/kward/{session_tree_nodes.rb → sessions/tree_nodes.rb} +3 -3
  132. data/lib/kward/{session_tree_renderer.rb → sessions/tree_renderer.rb} +4 -4
  133. data/lib/kward/{session_tree_tool_display.rb → sessions/tree_tool_display.rb} +1 -1
  134. data/lib/kward/{ekwsh.rb → shell/kwsh.rb} +38 -19
  135. data/lib/kward/shell/kwshrc.rb +233 -0
  136. data/lib/kward/{persistent_shell_session.rb → shell/persistent_session.rb} +121 -28
  137. data/lib/kward/{shell_prompt.rb → shell/prompt.rb} +2 -0
  138. data/lib/kward/{shell_prompt_session.rb → shell/prompt_session.rb} +1 -1
  139. data/lib/kward/skills/trust_store.rb +1 -1
  140. data/lib/kward/tabs/driver.rb +194 -0
  141. data/lib/kward/{tab_store.rb → tabs/store.rb} +2 -2
  142. data/lib/kward/{ansi.rb → terminal/ansi.rb} +110 -10
  143. data/lib/kward/{clipboard.rb → terminal/clipboard.rb} +1 -1
  144. data/lib/kward/{terminal_image_support.rb → terminal/image_support.rb} +1 -1
  145. data/lib/kward/{terminal_keys.rb → terminal/keys.rb} +12 -0
  146. data/lib/kward/terminal/text.rb +121 -0
  147. data/lib/kward/text_matcher.rb +18 -0
  148. data/lib/kward/tools/base.rb +18 -0
  149. data/lib/kward/tools/context_for_task.rb +15 -6
  150. data/lib/kward/tools/edit_file.rb +9 -6
  151. data/lib/kward/tools/git_commit.rb +13 -7
  152. data/lib/kward/tools/list_directory.rb +4 -4
  153. data/lib/kward/tools/open_editor.rb +41 -0
  154. data/lib/kward/tools/plugin_tool.rb +41 -0
  155. data/lib/kward/tools/prepare_shell_command.rb +1 -1
  156. data/lib/kward/tools/read_file.rb +7 -6
  157. data/lib/kward/tools/registry.rb +109 -17
  158. data/lib/kward/tools/run_shell_command.rb +10 -8
  159. data/lib/kward/tools/search/code.rb +1 -1
  160. data/lib/kward/tools/summarize_file_structure.rb +5 -5
  161. data/lib/kward/tools/tool_call.rb +3 -1
  162. data/lib/kward/tools/typesafe_evaluate.rb +81 -0
  163. data/lib/kward/tools/workspace_targets.rb +58 -0
  164. data/lib/kward/tools/write_file.rb +9 -6
  165. data/lib/kward/{export_path.rb → transcripts/export_path.rb} +1 -1
  166. data/lib/kward/{markdown_transcript.rb → transcripts/markdown_transcript.rb} +2 -2
  167. data/lib/kward/transport/contracts.rb +200 -0
  168. data/lib/kward/transport/gateway.rb +79 -35
  169. data/lib/kward/transport/plugin_chat_gateway.rb +3 -2
  170. data/lib/kward/transport.rb +1 -200
  171. data/lib/kward/version.rb +1 -1
  172. data/lib/kward/{workspace_factory.rb → workspace/factory.rb} +2 -2
  173. data/lib/kward/{project_files.rb → workspace/files.rb} +2 -2
  174. data/lib/kward/{git_worktree_manager.rb → workspace/git_worktree_manager.rb} +28 -0
  175. data/lib/kward/{workspace.rb → workspace/workspace.rb} +3 -3
  176. data/templates/default/kward_navigation.rb +1 -0
  177. data/templates/default/layout/html/footer.erb +10 -0
  178. data/templates/default/layout/html/headers.erb +23 -0
  179. data/templates/default/layout/html/layout.erb +2 -2
  180. data/templates/default/layout/html/setup.rb +41 -2
  181. metadata +94 -47
  182. data/lib/kward/scratchpad_runner.rb +0 -56
  183. data/lib/kward/tab_driver.rb +0 -90
  184. /data/lib/kward/{editor_prompt.rb → cli/editor_prompt.rb} +0 -0
  185. /data/lib/kward/{editor_prompt_session.rb → cli/editor_prompt_session.rb} +0 -0
  186. /data/lib/kward/{diff_view_mode.rb → prompt_interface/editor/diff_view_mode.rb} +0 -0
  187. /data/lib/kward/{editor_mode.rb → prompt_interface/editor/editor_mode.rb} +0 -0
  188. /data/lib/kward/{session_diff.rb → sessions/diff.rb} +0 -0
  189. /data/lib/kward/{session_naming.rb → sessions/naming.rb} +0 -0
  190. /data/lib/kward/{session_trash.rb → sessions/trash.rb} +0 -0
  191. /data/lib/kward/{terminal_sequences.rb → terminal/sequences.rb} +0 -0
  192. /data/lib/kward/{transcript_export.rb → transcripts/transcript_export.rb} +0 -0
  193. /data/lib/kward/{path_guard.rb → workspace/path_guard.rb} +0 -0
data/doc/rpc.md CHANGED
@@ -15,7 +15,7 @@ A typical client launches the server, sends `initialize`, creates or resumes a s
15
15
  | Connect and negotiate capabilities | [Launch](#Launch), [Framing](#Framing), and [Initialization](#Initialization) |
16
16
  | Open, resume, branch, or export a conversation | [Session methods](#Session_methods) |
17
17
  | Send input and render a response | [Turn methods](#Turn_methods) and [Turn notifications](#Turn_notifications) |
18
- | Handle tool approval or structured questions | [Tool approval bridge](#Tool_approval_bridge) and [UI question bridge](#UI_question_bridge) |
18
+ | Handle tool approval or structured plugin UI | [Tool approval bridge](#Tool_approval_bridge) and [Structured UI bridge](#Structured_UI_bridge) |
19
19
  | Show model, runtime, auth, or configuration controls | [Runtime methods](#Runtime_methods), [Model methods](#Model_methods), and [Config and auth methods](#Config_and_auth_methods) |
20
20
  | Discover tools, MCP servers, prompts, skills, or plugins | [Tool and prompt methods](#Tool_and_prompt_methods) |
21
21
 
@@ -65,8 +65,10 @@ Read `capabilities` at runtime instead of assuming every feature is available. I
65
65
  - `transcript`: Kward transcript format support, including normalized messages, image/tool support, compaction summaries, and restored assistant reasoning as Pi-compatible `thinking` content blocks.
66
66
  - `sessions`: explicit RPC session mode, JSONL persistence, and methods for listing, auto-resume, live-session discovery, linear forking, compaction, and labeled tree navigation with branch summaries. Import is unsupported. Live session updates are also unsupported but reserve the `session/updated` notification name. Git worktree bindings are reported as interactive-TUI-only.
67
67
  - `turns`: asynchronous turns, per-session concurrency, active and recent turn lists, busy-input steering when the provider supports it, queued follow-ups, best-effort cancellation, and recent in-memory event replay. Per-turn options cover model, reasoning, tool scope, and tool approval, with structured client context for editor integrations.
68
- - `pluginChats`: optional plugin-owned chats. The capability lists opted-in chat types and methods. Clients must explicitly subscribe before receiving `pluginChat/event` notifications; plugin chats are independent from workspace sessions. A type may also report `transport: true` when a trusted external transport is allowed to target it; RPC opt-in and external transport opt-in remain separate.
69
- - `events`: the `turn/event` contract, assistant and reasoning events, normalized tool metadata, tool updates and results, diff support, workspace guardrail status, focused-context and context-budget statistics tools, and explicitly unsupported shell changed-file and session-update flags.
68
+ - `plugins`: the supported plugin API version and public `id`, `version`, and `apiVersion` metadata for identified plugins. Private plugin configuration is never included.
69
+ - `pluginTools`: model-callable tools registered by trusted local plugins, including the registered count, `tools/list` discovery, execution-profile filtering, and permission-policy enforcement.
70
+ - `pluginChats`: optional plugin-owned chats. The capability lists opted-in chat types and methods. Versioned types report declared attachment, steering, and transcript-paging capabilities. Clients must explicitly subscribe before receiving `pluginChat/event` notifications; plugin chats are independent from workspace sessions. A type may also report `transport: true` when a trusted external transport is allowed to target it; RPC opt-in and external transport opt-in remain separate.
71
+ - `events`: the `turn/event` contract, assistant and reasoning events, typed plugin-command results, normalized tool metadata, tool updates and results, diff support, workspace guardrail status, focused-context and context-budget statistics tools, and explicitly unsupported shell changed-file and session-update flags.
70
72
  - `attachments`: supported input attachment contract for `turns/start`, with accepted base64 image MIME types and a stable max byte value.
71
73
  - `models`: model listing, refresh, selection, and exposed metadata across providers. Scoped model selection is not supported.
72
74
  - `runtime`: runtime state, message-count statistics, and OpenAI/Codex context usage. Kward does not yet compute cumulative token or cost statistics.
@@ -74,12 +76,13 @@ Read `capabilities` at runtime instead of assuming every feature is available. I
74
76
  - `runtimeSettings`: live `runtime/updateSetting` support for `defaultModel` and `defaultThinkingLevel`, plus `runtime/reload`.
75
77
  - `auth`: available providers and authentication methods, private API-key storage, sanitized status, and logout. OpenAI and Anthropic OAuth are supported; Copilot OAuth is CLI-only, OpenRouter PKCE is not implemented, and xAI has no supported stable third-party flow.
76
78
  - `memory`: opt-in structured memory support, interactive prompt injection only, JSON/JSONL local storage, and dedicated `memory/*` methods.
77
- - `commands`: supported `commands/list` capability for prompt, skill, and plugin command sources, plus plugin execution through `commands/run` or plugin slash turns.
79
+ - `commands`: supported `commands/list` capability for prompt, skill, and plugin command sources, plus plugin execution through `commands/run` or plugin slash turns. Plugin commands may advertise validated JSON Schema arguments, shell-style text parsing, and structured results while legacy raw-string commands remain supported.
80
+ - `pluginActions`: namespaced typed actions discovered through `pluginActions/list` and synchronously invoked through `pluginActions/run`. They use object arguments and structured results, are not local TUI commands, and cannot make blocking UI requests.
78
81
  - `skillCapture`: capture a reviewed personal `SKILL.md` from any saved session’s active branch through `skills/captureSessions`, `skills/captureDraft`, and `skills/saveCapturedDraft`.
79
82
  - `projectSkillTrust`: explicitly unsupported over RPC. RPC clients cannot answer the interactive Allow/Deny/Review decision, so project skills remain skipped unless the global `skills.trust_project` override is enabled.
80
83
  - `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
84
  - `startupResources`: supported startup resource listing for context, skills, prompts, and plugins.
82
- - `extensionUi`: question bridge support via `ui/question` and `ui/answerQuestion`, plus plugin footer updates via `ui/footer`; other UI primitives are explicitly unsupported.
85
+ - `extensionUi`: structured question support plus plugin `select`, `confirm`, `input`, `progress`, and `notify` UI through `ui/request`, `ui/answerRequest`, `ui/progress`, and `ui/notification`; plugin footer updates use `ui/footer`. Editor, widget, custom-canvas, and raw terminal-input primitives remain explicitly unsupported.
83
86
  - `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
87
  - `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
88
  - `export`: supported transcript export formats. Currently `markdown` and `html`; default is `markdown`.
@@ -294,7 +297,11 @@ Returns `{ "session": {}, "editorText": "...", "cancelled": false, "aborted": fa
294
297
 
295
298
  ## Plugin chat methods
296
299
 
297
- Plugin chats are optional trusted-plugin capabilities, not Kward workspace sessions. When `initialize.capabilities.pluginChats.supported` is true, use `pluginChats/list` to discover available types.
300
+ Plugin chats are optional trusted-plugin capabilities, not Kward workspace
301
+ sessions. When `initialize.capabilities.pluginChats.supported` is true, use
302
+ `pluginChats/list` to discover available types. Types using the versioned chat
303
+ contract also report `apiVersion` and `capabilities` with `attachments`,
304
+ `steering`, and `transcriptPaging`; legacy types omit those fields.
298
305
 
299
306
  ### `pluginChats/open`
300
307
 
@@ -330,7 +337,10 @@ Params:
330
337
  - `input`;
331
338
  - `attachments`: optional base64 image attachments using the same MIME and size limits as `turns/start`.
332
339
 
333
- Queues a plugin-chat turn and returns `{ id, chatId, status, ... }`. Plugin chat turns are serialized per chat and do not use workspace sessions, agents, or model overrides.
340
+ Queues a plugin-chat turn and returns `{ id, chatId, status, ... }`. Plugin chat
341
+ turns are serialized per chat and do not use workspace sessions, agents, or
342
+ model overrides. A versioned chat whose declared `attachments` capability omits
343
+ `image` rejects image attachments before invoking its driver.
334
344
 
335
345
  ### `pluginChats/turns/cancel`, `pluginChats/turns/status`, `pluginChats/turns/events`, `pluginChats/turns/list`, `pluginChats/turns/listActive`
336
346
 
@@ -439,6 +449,7 @@ Known event types:
439
449
  - `toolCall`
440
450
  - `toolUpdate`
441
451
  - `toolResult`
452
+ - `pluginCommandResult`
442
453
  - `answer`
443
454
  - `turnCancelRequested`
444
455
  - `error`
@@ -462,6 +473,10 @@ Lifecycle payloads include `status` for `turnQueued`, `turnStarted`, and `turnFi
462
473
 
463
474
  `toolResult` additionally includes `result` with `content`, `isError`, optional unified `diff`, optional `changedFiles`, and `images`. Failed or declined tools set `isError: true`.
464
475
 
476
+ A typed plugin slash command emits `pluginCommandResult` with `command` and a
477
+ structured `result` containing optional `message` and `data` fields. It then
478
+ emits the usual text events when the command produced user-facing output.
479
+
465
480
  Examples:
466
481
 
467
482
  - `edit_file`: `toolName: "edit"`, `args: { "path": "...", "edits": [{ "oldText": "...", "newText": "..." }] }`.
@@ -490,11 +505,11 @@ Params:
490
505
 
491
506
  Denied tools are returned to the model as error-like tool results instead of executing the local operation.
492
507
 
493
- ## UI question bridge
508
+ ## Structured UI bridge
494
509
 
495
- Kward supports the structured question bridge and plugin footer updates over RPC. The `extensionUi` capability reports `question.supported: true` with `notification: "ui/question"`, `method: "ui/answerQuestion"`, `maxQuestions: 4`, `multiSelect: false`, and `preview: false`. It also reports `footer.supported: true` with `notification: "ui/footer"`. Other Pi-style extension UI primitives (`select`, `confirm`, `input`, `editor`, `widgets`, `custom`, and `terminalInput`) are explicitly reported as unsupported until Kward has a real plugin/extension consumer for them.
510
+ Kward supports model questions and frontend-neutral plugin UI over RPC. The `extensionUi` capability reports the notification and answer method for each operation. `question` continues to use `ui/question` and `ui/answerQuestion`; plugin `select`, `confirm`, and `input` requests use `ui/request` and `ui/answerRequest`. Non-blocking plugin updates use `ui/progress` and `ui/notification`, while plugin footers use `ui/footer`. Editor, widget, custom-canvas, and raw terminal-input primitives remain explicitly unsupported.
496
511
 
497
- Question requests are validated before notification. Kward accepts 1-4 questions, each with 2-4 options, and rejects unsupported `multiSelect` or option `preview` requests.
512
+ Question requests are validated before notification. Kward accepts 1-4 questions, each with 2-4 options, and rejects unsupported `multiSelect` or option `preview` requests. Plugin selections accept at most 100 options, and plugin text input is bounded to 16,384 bytes.
498
513
 
499
514
  When the model calls `ask_user_question`, RPC emits a `ui/question` notification:
500
515
 
@@ -506,16 +521,37 @@ When the model calls `ask_user_question`, RPC emits a `ui/question` notification
506
521
  }
507
522
  ```
508
523
 
509
- When a loaded Kward plugin registers a footer, RPC emits `ui/footer` after session creation/resume/clone and after each completed turn:
524
+ When loaded Kward plugins contribute footer status, RPC emits `ui/footer` after
525
+ session creation/resume/clone, after each completed turn, and when the status
526
+ changes during its periodic refresh:
510
527
 
511
528
  ```json
512
529
  {
513
530
  "sessionId": "...",
514
- "text": "custom footer text"
531
+ "text": "Bridge · 2 messages",
532
+ "segments": [
533
+ {
534
+ "id": "com.example.session/session",
535
+ "text": "Bridge",
536
+ "tooltip": "Current Kward session",
537
+ "priority": "high",
538
+ "order": 10
539
+ },
540
+ {
541
+ "id": "com.example.session/messages",
542
+ "text": "2 messages",
543
+ "priority": "low",
544
+ "order": 20
545
+ }
546
+ ]
515
547
  }
516
548
  ```
517
549
 
518
- An empty `text` value clears the client footer.
550
+ `text` is the combined fallback for clients that do not render segments.
551
+ `segments` remains display-ordered and lets richer clients show tooltips or
552
+ apply their own width policy. An empty `text` with an empty `segments` array
553
+ clears the client footer. The `extensionUi.footer` capability advertises
554
+ segment, tooltip, and priority support.
519
555
 
520
556
  The UI must respond with `ui/answerQuestion`:
521
557
 
@@ -525,6 +561,40 @@ Params:
525
561
  - `questionRequestId`
526
562
  - `answers`: answer array returned to the tool.
527
563
 
564
+ When a plugin calls `ctx.ui.select`, `ctx.ui.confirm`, or `ctx.ui.input` during an asynchronous plugin-command turn or plugin tool call, RPC emits:
565
+
566
+ ```json
567
+ {
568
+ "method": "ui/request",
569
+ "params": {
570
+ "sessionId": "...",
571
+ "requestId": "...",
572
+ "kind": "select",
573
+ "payload": {
574
+ "title": "Environment",
575
+ "message": "Choose a target",
576
+ "options": [
577
+ { "label": "Staging", "value": "staging", "description": "Deploy for testing." }
578
+ ]
579
+ }
580
+ }
581
+ }
582
+ ```
583
+
584
+ Answer it with `ui/answerRequest`:
585
+
586
+ ```json
587
+ {
588
+ "sessionId": "...",
589
+ "requestId": "...",
590
+ "value": "staging"
591
+ }
592
+ ```
593
+
594
+ For `confirm`, `value` must be a boolean. For `input`, it is a string or `null` when cancelled. For `select`, it is one of the advertised option values or `null`. Submit interactive plugin commands through `turns/start`; synchronous `commands/run` deliberately reports blocking plugin UI as unavailable so the RPC reader never waits for an answer it cannot read.
595
+
596
+ `ctx.ui.progress` emits `ui/progress` with `sessionId`, stable `id`, `message`, optional `percent`, and `done`. `ctx.ui.notify` emits `ui/notification` with `sessionId`, `message`, and `level` (`info`, `success`, `warning`, or `error`). These notifications do not require answers.
597
+
528
598
  ## Runtime methods
529
599
 
530
600
  ### `runtime/state`
@@ -737,11 +807,11 @@ Params:
737
807
 
738
808
  Returns current tool schemas. The existing model-facing schema shape is preserved: each entry still has `type: "function"` and `function` with `name`, `description`, and `parameters`. Entries also include additive metadata for UI discovery:
739
809
 
740
- - `metadata.source`: one of practical source labels such as `builtin`, `mcp`, `web`, `skill`, `ui`, or `unknown`.
810
+ - `metadata.source`: one of practical source labels such as `builtin`, `plugin`, `mcp`, `web`, `skill`, `ui`, or `unknown`.
741
811
  - `metadata.displayName`: human-readable tool label.
742
812
  - MCP tools also include `metadata.serverName` and `metadata.remoteName`. The callable name remains sanitized with a double underscore, for example `github__search_issues`, while `displayName` is `github.search_issues`.
743
813
 
744
- Clients that only read `tools[].function` remain compatible.
814
+ Clients that only read `tools[].function` remain compatible. Model-callable tools registered by trusted local plugins appear here with `metadata.source: "plugin"` and follow the same per-session execution-profile filtering as built-in tools.
745
815
 
746
816
  ### `mcp/status`
747
817
 
@@ -766,7 +836,74 @@ Params:
766
836
 
767
837
  - `sessionId`: active RPC session ID.
768
838
 
769
- Returns frontend-neutral slash command metadata for configured prompt templates, skills, and plugins. Prompt command names omit the leading slash. Skill command names use `skill:<name>`. Plugin command names omit the leading slash and include `executable: true`. Builtin terminal-only commands are omitted. Prompt commands can be submitted directly to `turns/start` as slash commands or expanded first with `prompts/expand`; plugin commands can be submitted to `turns/start` or run explicitly with `commands/run`.
839
+ Returns frontend-neutral slash command metadata for configured prompt templates, skills, and plugins. Prompt command names omit the leading slash. Skill command names use `skill:<name>`. Plugin command names omit the leading slash and include `executable: true`. Typed plugin entries additionally include `typed: true`, their strict object `schema`, declared `positionals`, and `pluginId` when the plugin has stable identity. Builtin terminal-only commands are omitted. Prompt commands can be submitted directly to `turns/start` as slash commands or expanded first with `prompts/expand`; plugin commands can be submitted to `turns/start` or run explicitly with `commands/run`.
840
+
841
+ ### `commands/run`
842
+
843
+ Params:
844
+
845
+ - `sessionId`: active RPC session ID.
846
+ - `name`: plugin command name, with or without the leading slash.
847
+ - `arguments`: shell-style text or, for typed commands, an object matching the advertised schema.
848
+
849
+ Legacy plugin commands return their existing string-or-null `result`. Typed
850
+ commands return `result` as an object containing optional user-facing `message`
851
+ and JSON-compatible `data`; text emitted through `ctx.say` remains in `output`.
852
+ Blocking structured UI requests are unavailable on this synchronous path. Use a
853
+ slash-command `turns/start` call when the command needs interactive UI.
854
+
855
+ Plugin commands can also stage a model response with `ctx.request_turn(system: text)`.
856
+ Submit these through `turns/start`, for example:
857
+
858
+ ```json
859
+ {"sessionId":"session-id","input":"/iddqd Answer in French and explain the current design."}
860
+ ```
861
+
862
+ The same turn emits the normal model, tool, answer, error, and completion events.
863
+ The host retains cancellation, execution-profile restrictions, and tool approvals.
864
+ The instructions supplement the system prompt for this turn only; they are not
865
+ submitted as user text and are not reactivated by restoring the session.
866
+ `commands/run` and `pluginActions/run` reject model-turn requests. Discover this
867
+ contract under `initialize.capabilities.commands.modelTurns`, including the
868
+ 65,536-byte limit, `scope: "turn"`, and `synchronous: false`.
869
+ `inputRole: "system"` describes Kward's internal instruction role;
870
+ `codexInputRole: "developer"` reports the Codex subscription backend's supported
871
+ wire role. Codex receives the command as a new ordered developer message, while
872
+ direct OpenAI Responses receives an ordered system message. Base instructions
873
+ stay separate from these turn-scoped input messages.
874
+
875
+ The invocation is saved as a user-visible history entry with `display_content`
876
+ and `plugin_system_turn` metadata (command, plugin ID, instruction text, request ID,
877
+ and scope). Later model requests omit these history entries; compaction uses an
878
+ informational placeholder, not the expired instructions. Anthropic and Gemini
879
+ use separate system fields rather than ordered system messages and require
880
+ existing dialogue; a system-only request in an empty session fails explicitly
881
+ rather than inserting a synthetic user prompt.
882
+
883
+ ### `pluginActions/list`
884
+
885
+ Params:
886
+
887
+ - `sessionId`: active RPC session ID.
888
+
889
+ Returns identified-plugin actions with `id`, local `name`, `pluginId`,
890
+ `description`, and strict object `schema`. Action IDs use
891
+ `<plugin-id>/<action-name>`.
892
+
893
+ ### `pluginActions/run`
894
+
895
+ Params:
896
+
897
+ - `sessionId`: active RPC session ID.
898
+ - `id`: complete namespaced action ID.
899
+ - `arguments`: object matching the action schema; defaults to an empty object.
900
+
901
+ Returns `action`, `output`, and a structured `result` with optional `message` and
902
+ `data`. Actions run synchronously and therefore expose notifications and progress
903
+ but not blocking questions, selections, confirmations, or input. They are
904
+ rejected when the session execution profile disables plugin commands. Actions
905
+ are intentionally not exposed in local slash completion; a plugin should also
906
+ register a typed command when it needs a human-facing TUI entry point.
770
907
 
771
908
  ### `skills/captureSessions`, `skills/captureDraft`, and `skills/saveCapturedDraft`
772
909
 
@@ -884,4 +1021,5 @@ Returns login status for a login ID.
884
1021
  - RPC is intended for a trusted local UI and can read/write files, run shell commands, update secrets, and use OAuth.
885
1022
  - Workspace roots may be any existing local directory accessible to the process.
886
1023
  - Tool execution matches current CLI behavior; mutating tools are not approval-gated by RPC.
1024
+ - `commands/run` and `pluginActions/run` execute trusted local plugin code. Action schemas validate data shape but are not an authorization boundary; use execution profiles to disable plugin operations for restricted sessions.
887
1025
  - Responses and diagnostics redact secret-looking fields, but clients should still avoid logging full protocol traffic unless necessary.
data/doc/sandboxing.md CHANGED
@@ -67,9 +67,12 @@ after changing the mode.
67
67
 
68
68
  An active worktree-backed tab uses a strict `workspace_write` policy for its
69
69
  model-requested command workers, regardless of the global `sandbox.mode` or
70
- `tools.workspace_guardrails` settings. The writable root is exactly the linked
71
- worktree and no configured additional writable roots are carried into the tab.
72
- The configured child-network setting is preserved.
70
+ `tools.workspace_guardrails` settings. The default writable root is exactly the
71
+ linked worktree and no configured additional writable roots are carried into the
72
+ tab. When a workspace tool explicitly selects the verified `origin` target,
73
+ Kward uses a separate strict command worker whose writable root is exactly that
74
+ original worktree. Arbitrary target paths are not accepted. The configured
75
+ child-network setting is preserved.
73
76
 
74
77
  If the current platform cannot provide filesystem enforcement, Kward refuses to
75
78
  activate the worktree instead of falling back to an unrestricted command
@@ -82,8 +85,11 @@ This does not contain the user-directed `/shell`, `!command`, `/capture`, or
82
85
  model-requested shell commands still cannot write Git metadata. Active worktree tabs additionally
83
86
  expose a narrow `git_commit` tool for explicit agent-requested commits; it runs
84
87
  through the trusted host-side Git workflow rather than widening the shell
85
- sandbox. The interactive `/git` flow remains available for manual review and
86
- commit.
88
+ sandbox. The verified original worktree is available as `target: "origin"` on
89
+ core workspace tools so an agent can inspect and resolve merge conflicts without
90
+ switching tabs. Selecting that target does not add an approval layer; the normal
91
+ configured tool-permission policy applies equally to both worktrees. The
92
+ interactive `/git` flow remains available for manual review and commit.
87
93
 
88
94
  ## Platform support
89
95
 
data/doc/security.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Kward can read code, edit files, run commands, call model and search providers, and load local extensions. That makes it useful, but it also means you should treat it like a developer tool running with your account—not like a sandbox.
4
4
 
5
- This guide explains the trust boundaries and gives you a safe way to start work in an unfamiliar repository.
5
+ This guide explains the trust boundaries and gives you a safe way to start work in an unfamiliar repository. Report suspected vulnerabilities privately through the [security policy](https://github.com/kaiwood/kward/blob/main/SECURITY.md).
6
6
 
7
7
  ## The short version
8
8
 
@@ -56,7 +56,8 @@ Kward's built-in file tools normally resolve paths inside the active workspace.
56
56
 
57
57
  These protections reduce accidental edits. They do not contain the whole process:
58
58
 
59
- - With `sandbox.mode: off` (the default), `run_shell_command`, `!command`, `/capture`, `/shell`, and `/pty` run with your user permissions.
59
+ - With `sandbox.mode: off` (the default), `run_shell_command`, `!command`, `/capture`, `/shell`, `/pty`, and editor buffer runners run with your user permissions.
60
+ - Editor buffer runners are user-directed and execute the current in-memory buffer, including normal files and scratchpads, through the configured `editor.runners` binary without a shell. They are not covered by the command sandbox.
60
61
  - 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
62
  - 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
63
  - Plugins, command hooks, and MCP servers are local processes with the same general operating-system access.
@@ -86,7 +87,7 @@ See [Lifecycle hooks](lifecycle-hooks.md) for policy examples and [RPC protocol]
86
87
 
87
88
  ## Opt-in permission policy
88
89
 
89
- `permissions.enabled` is off by default. When enabled, Kward can allow, ask, or deny model-requested file changes, shell commands, web tools, and MCP tools before execution. The interactive CLI uses an approval overlay; an unavailable approval bridge fails closed. The policy is not a sandbox: after Kward permits a shell command, it still runs with the permissions of your user account. See [Permissions](permissions.md) for workflows, modes, rules, and limits.
90
+ `permissions.enabled` is off by default. When enabled, Kward can allow, ask, or deny model-requested file changes, shell commands, web tools, MCP tools, and plugin tools before execution. The interactive CLI uses an approval overlay; an unavailable approval bridge fails closed. The policy is not a sandbox: after Kward permits a shell command, it still runs with the permissions of your user account. See [Permissions](permissions.md) for workflows, modes, rules, and limits.
90
91
 
91
92
  ## Know what you are trusting
92
93
 
@@ -112,6 +113,11 @@ They can read files and environment variables, write files, run commands, and
112
113
  make network requests. Kward intentionally does not load plugins from a
113
114
  workspace directory.
114
115
 
116
+ Identified-plugin configuration and state remain local to the active config
117
+ directory. RPC capability reports include only plugin IDs and versions, never
118
+ plugin configuration or secret values. Treat `host.logger` output as
119
+ user-visible diagnostics and never write credentials to it.
120
+
115
121
  ### MCP servers
116
122
 
117
123
  Configured MCP servers are local child processes. Their tools can expose application state such as browser pages, console messages, network traffic, or screenshots. Kward currently supports local stdio servers, but local transport does not make an untrusted server safe.
@@ -167,6 +173,7 @@ Kward keeps user data under `~/.kward` by default, or mostly beside `KWARD_CONFI
167
173
  | Memory | `~/.kward/memory/` | Off by default; directory `0700` and files `0600`. |
168
174
  | Telemetry logs | `~/.kward/logs/` | Off by default; redacted metadata, not intentional prompt or file-content logging. |
169
175
  | Plugins | `~/.kward/plugins/` | Trusted Ruby code, not private data storage. |
176
+ | Plugin state | `~/.kward/plugin_state/` | Namespaced by stable plugin ID; state files use mode `0600`. |
170
177
  | Hook audit log and trust records | `~/.kward/logs/`, `~/.kward/trusted_workspace_hooks.json` | Audit records use redacted metadata rather than raw event values. |
171
178
 
172
179
  Private file modes help on normal Unix-like systems but do not protect data from your own account, privileged users, backups, malware, or a compromised machine. Session exports are written to the path you choose and should be protected separately.
@@ -25,13 +25,13 @@ To automatically resume the last active session for the current workspace on sta
25
25
 
26
26
  ## A normal session workflow
27
27
 
28
- Start a chat and give it a useful name:
28
+ Start a chat and give the current session and tab a useful name:
29
29
 
30
30
  ```text
31
- /rename oauth cleanup
31
+ /name oauth cleanup
32
32
  ```
33
33
 
34
- `/rename` requires a name. Use `/session name` without an argument to clear the current session name.
34
+ `/name` requires a name and updates both the active session and its tab label. It can also be used while the active tab's agent is running; it is unavailable in plugin tabs. Use `/rename` when you only want to rename the session. Use `/session name` without an argument to clear the current session name.
35
35
 
36
36
  Work normally:
37
37
 
@@ -227,7 +227,8 @@ This is useful when you want to confirm which session you are in or check whethe
227
227
  | Need | Use |
228
228
  | --- | --- |
229
229
  | Continue earlier work | `/session` or `/resume` |
230
- | Give the current session a better name | `/rename <name>` |
230
+ | Give the current session and tab a better name | `/name <name>` |
231
+ | Give only the current session a better name | `/rename <name>` |
231
232
  | Clear the current session name | `/session name` |
232
233
  | Keep the current state but try another future | `/clone` |
233
234
  | Start a separate session from before an earlier prompt | `/fork` |
data/doc/shell.md CHANGED
@@ -23,13 +23,12 @@ The command runs from the active workspace root and begins in an inline PTY regi
23
23
 
24
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.
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
+ 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 owns the terminal, the composer remains frozen and ordinary keyboard input belongs to the child process. Kward's tab shortcuts are intercepted separately; switching tabs detaches the command and lets it continue in the originating tab's bounded background state.
27
27
 
28
- Configured `ekwsh.yml` aliases also work after `!`:
28
+ Configured `kwshrc` aliases also work after `!`:
29
29
 
30
- ```yaml
31
- aliases:
32
- glog: "git log --decorate --stat --graph"
30
+ ```sh
31
+ alias glog='git log --decorate --stat --graph'
33
32
  ```
34
33
 
35
34
  ```text
@@ -38,9 +37,8 @@ aliases:
38
37
 
39
38
  An alias that resolves to `kward edit <filename>` opens Kward's integrated editor in the current session instead of starting a nested Kward process:
40
39
 
41
- ```yaml
42
- aliases:
43
- vibe: "kward edit"
40
+ ```sh
41
+ alias vibe='kward edit'
44
42
  ```
45
43
 
46
44
  ```text
@@ -112,6 +110,8 @@ If you explicitly ask the assistant to change shell state, it can use the active
112
110
 
113
111
  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
112
 
113
+ If you explicitly ask it to open an existing workspace file, the assistant uses `open_editor` to open Kward's integrated editor. Opening the editor does not modify or save the file.
114
+
115
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
116
 
117
117
  ## Interactive and captured commands
@@ -131,13 +131,13 @@ Use `capture` inside `/shell` when you want ordinary, readable output in Kward's
131
131
 
132
132
  ```sh
133
133
  capture git status --short
134
- capture bundle exec ruby -Itest test/test_ekwsh.rb
134
+ capture bundle exec ruby -Itest test/test_kwsh.rb
135
135
  ```
136
136
 
137
- An `ekwsh` `capture` command:
137
+ An `kwsh` `capture` command:
138
138
 
139
139
  - does not receive keyboard input,
140
- - uses the timeout and output-size limit from `ekwsh.yml`,
140
+ - uses kwsh's built-in timeout and output-size limits,
141
141
  - preserves safe color and styling,
142
142
  - strips controls that could corrupt Kward's TUI,
143
143
  - can be cancelled with Ctrl+C.
@@ -163,9 +163,9 @@ Each Kward tab owns its `/shell` state. Switching away and back restores that ta
163
163
  - runtime aliases,
164
164
  - shell prompt and transcript view.
165
165
 
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`.
166
+ Shell commands use a separate, workspace-scoped history rather than the normal chat-prompt history. The shell-history limit is a built-in kwsh default.
167
167
 
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.
168
+ Kward's tab shortcuts work at the shell prompt and while a shell command is running. Switching tabs detaches the running command instead of interrupting it; bounded output and completion state remain owned by the originating tab and are restored when you return. While detached, the command no longer receives terminal input. Explicit cancellation or shutdown still terminates detached work. 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.
169
169
 
170
170
  ## Completion
171
171
 
@@ -210,6 +210,7 @@ The persistent `/shell` process handles these commands in-session so their state
210
210
  | `cd [dir]` | Change the shell directory. Supports `cd`, `cd -`, and relative paths. |
211
211
  | `pwd` | Print the shell directory. |
212
212
  | `export KEY[=value]` | Set an environment variable. `export` and `export -p` list variables. |
213
+ | `source <file>` / `. <file>` | Parse and apply aliases and exports from an rc file immediately. |
213
214
  | `unset KEY` | Remove an environment variable. |
214
215
  | `alias [name]` | List, inspect, or create aliases. |
215
216
  | `unalias name` / `unalias -a` | Remove aliases. |
@@ -220,39 +221,56 @@ The persistent `/shell` process handles these commands in-session so their state
220
221
 
221
222
  Built-ins take precedence over aliases and executables.
222
223
 
223
- ## Configure ekwsh
224
+ ## Configure kwsh
224
225
 
225
- Global shell configuration lives at:
226
+ Global shell configuration lives in these optional rc files, loaded in order:
226
227
 
227
228
  ```text
228
- ~/.kward/ekwsh.yml
229
+ ~/.kward/kwshrc
230
+ ~/.kwshrc
229
231
  ```
230
232
 
231
- When `KWARD_CONFIG_PATH` selects another main config file, Kward reads `ekwsh.yml` from the same directory.
233
+ When `KWARD_CONFIG_PATH` selects another main config file, Kward reads the first file beside that config file instead. The later file overrides earlier aliases and exported variables with the same names. The rc format currently supports declarative `alias`, `export`, and `source` directives:
232
234
 
233
- A practical configuration might look like this:
235
+ ```sh
236
+ alias ll='ls -la'
237
+ alias gs="git status --short"
238
+ export BUNDLE_WITHOUT=production
239
+ export PATH="$HOME/bin:$PATH"
240
+ source ~/.kward/kwsh-aliases
241
+ ```
234
242
 
235
- ```yaml
236
- shell: /bin/sh
237
- timeout_seconds: 300
238
- max_output_bytes: 1048576
239
- history_limit: 1000
243
+ `source` reads another rc file without executing it; relative paths in rc-file directives are resolved from the file containing the directive. When entered as a `/shell` builtin, the path is resolved from the current shell directory and its aliases and exports are applied immediately, without restarting Kward. Unsupported shell scripting is ignored for now.
240
244
 
241
- env:
242
- FORCE_COLOR: "1"
243
- BUNDLE_WITHOUT: "production"
244
- RAILS_ENV: "test"
245
+ The transient shell assistant normally follows the active conversation's model and reasoning effort. Configure it in the main JSON file:
245
246
 
246
- aliases:
247
- ll: "ls -la"
248
- gs: "git status --short"
249
- gd: "git diff --color=always"
250
- glog: "git log --decorate --stat --graph"
251
- be: "bundle exec"
252
- t: "bundle exec ruby -Itest"
247
+ ```json
248
+ {
249
+ "shell": {
250
+ "agent": {
251
+ "provider": "openrouter",
252
+ "model": "openai/gpt-5.6-sol",
253
+ "reasoning_effort": "none"
254
+ }
255
+ }
256
+ }
253
257
  ```
254
258
 
255
- ### Settings
259
+ The optional `provider` uses the lowercase configuration IDs listed in [Model providers](providers.md). When omitted, the shell assistant follows the active conversation. If a provider is explicitly configured without a model or reasoning effort, Kward uses that provider's defaults instead of inheriting the active conversation's values.
260
+
261
+ Override those settings with environment variables:
262
+
263
+ ```sh
264
+ export KWSH_PROVIDER="openrouter"
265
+ export KWSH_MODE="openai/gpt-5.6-sol"
266
+ export KWSH_REASONING="none"
267
+ ```
268
+
269
+ Environment variables take precedence over the JSON settings, and empty values are ignored.
270
+
271
+ ### Runtime defaults
272
+
273
+ These runtime settings are built into kwsh and are not configurable through rc files:
256
274
 
257
275
  | Setting | Default | What it does |
258
276
  | --- | --- | --- |
@@ -265,14 +283,14 @@ Invalid or relative `shell` paths fall back to `/bin/sh`. These timeout and outp
265
283
 
266
284
  ### Environment
267
285
 
268
- Configured `env` values are applied when `/shell` starts. Keys must be valid environment-variable names; nil values and invalid keys are ignored, and other values are converted to strings.
286
+ `export` values from rc files are applied when `/shell` starts and are also available to leading-`!` commands. Keys must be valid environment-variable names; invalid keys are ignored. Values support shell quoting and simple `$VAR`/`${VAR}` expansion.
269
287
 
270
288
  Kward also supplies conservative terminal defaults:
271
289
 
272
290
  ```sh
273
- CLICOLOR=1
274
- COLORTERM=truecolor
275
- TERM=xterm-256color # only when TERM is missing or dumb
291
+ export CLICOLOR=1
292
+ export COLORTERM=truecolor
293
+ export TERM=xterm-256color # only when TERM is missing or dumb
276
294
  ```
277
295
 
278
296
  It does not force color. Set `FORCE_COLOR`, `CLICOLOR_FORCE`, or a command-specific option such as `--color=always` when needed.
@@ -283,18 +301,17 @@ When rbenv is available, Kward adds its shims and bin directories to `PATH` and
283
301
 
284
302
  Aliases replace the first command word once and append any remaining arguments:
285
303
 
286
- ```yaml
287
- aliases:
288
- ll: "ls -la"
289
- t: "bundle exec ruby -Itest"
304
+ ```sh
305
+ alias ll='ls -la'
306
+ alias t='bundle exec ruby -Itest'
290
307
  ```
291
308
 
292
309
  ```sh
293
310
  ll lib
294
311
  # runs: ls -la lib
295
312
 
296
- t test/test_ekwsh.rb
297
- # runs: bundle exec ruby -Itest test/test_ekwsh.rb
313
+ t test/test_kwsh.rb
314
+ # runs: bundle exec ruby -Itest test/test_kwsh.rb
298
315
  ```
299
316
 
300
317
  Configured aliases work inside `/shell` and after `!`. Aliases created with the `alias` built-in belong only to the current `/shell` session and are not shared with one-shot `!command` input.
data/doc/tabs.md CHANGED
@@ -59,11 +59,13 @@ Inspect, merge, or remove the binding explicitly:
59
59
  /tab worktree remove
60
60
  ```
61
61
 
62
- `/tab worktree merge` merges the active worktree's clean, committed branch directly into the branch currently checked out in its original workspace. Kward shows the source and target revisions and requires confirmation. Both worktrees must be clean. If Git reports conflicts, Kward leaves the original workspace in its normal merge state; resolve the conflicts there or cancel them with `/tab worktree merge abort`.
62
+ `/tab worktree merge` merges the active worktree's clean, committed branch directly into the branch currently checked out in its original workspace. Kward shows the source and target revisions and requires confirmation. Both worktrees must be clean. If Git reports conflicts, Kward leaves the original workspace in its normal merge state while keeping the current tab on the linked branch. The composer shows the target branch and conflict count. Run `/tab worktree merge resolve` (or `/worktree merge resolve`) to start an agent turn that inspects and edits the verified original workspace with `target: "origin"`; the turn leaves committing for review. After the conflicts are resolved, run `/tab worktree merge continue` to stage the resolutions and complete the merge, or use `/tab worktree merge abort` to cancel it.
63
63
 
64
64
  Removal refuses a dirty worktree and keeps its branch. A worktree that is missing or no longer points at the recorded branch is restored as unavailable rather than silently falling back to the original workspace.
65
65
 
66
- Worktree tabs are available for normal session tabs, not plugin-owned tabs. Kward's file tools, `@`/`$` completion, `/files` browser, integrated editor, and model-requested shell workers use the active worktree root. Model operations retain strict workspace guardrails. The user-directed `/shell`, `!command`, `/capture`, and `/pty` features remain host-process operations and are not contained by the model command sandbox; use them only when that is intentional. Generic shell Git writes remain protected. When explicitly asked to commit, the agent can use the active tab's narrow `git_commit` tool; use the interactive `/git` flow when you want to review and commit changes yourself.
66
+ Worktree tabs are available for normal session tabs, not plugin-owned tabs. Kward's file tools, `@`/`$` completion, `/files` browser, integrated editor, and model-requested shell workers use the active worktree root by default. Core model workspace tools advertise an optional `target` selector in an active worktree tab: `active` keeps the normal linked-worktree root, while `origin` selects only that tab's verified original repository worktree. Selecting `origin` does not add a separate approval prompt; the normal configured tool-permission policy applies equally to both targets. Paths remain relative to the selected root, and arbitrary filesystem targets are never accepted.
67
+
68
+ Both targets retain strict workspace guardrails and separate command sandboxes. The user-directed `/shell`, `!command`, `/capture`, and `/pty` features remain host-process operations and are not contained by the model command sandbox; use them only when that is intentional. Generic shell Git writes remain protected. When explicitly asked to commit, the agent can use `git_commit` for the active worktree or set `target: "origin"` for the original worktree; use the interactive `/git` flow when you want to review and commit changes yourself.
67
69
 
68
70
  ## Common workflow
69
71
 
@@ -98,6 +100,8 @@ Tabs keep the conversations separate, so context from one tab does not automatic
98
100
  | `/tab worktree detach` | Return to the original workspace while keeping the linked worktree and branch |
99
101
  | `/tab worktree status` | Show the current tab's worktree binding and Git status |
100
102
  | `/tab worktree merge` | Merge the current worktree branch into the branch checked out in its original workspace |
103
+ | `/tab worktree merge resolve` | Ask the agent to resolve an in-progress merge in the original workspace from the same tab |
104
+ | `/tab worktree merge continue` | Stage resolutions and complete an in-progress merge after confirmation |
101
105
  | `/tab worktree merge abort` | Abort a conflicted worktree merge in the original workspace |
102
106
  | `/tab worktree remove` | Remove a clean linked worktree and keep its branch |
103
107
  | `/worktree …` | Alias for `/tab worktree …` on the active tab |