kward 0.74.0 → 0.75.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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +4 -2
  5. data/Rakefile +18 -0
  6. data/doc/configuration.md +56 -3
  7. data/doc/extensibility.md +6 -19
  8. data/doc/getting-started.md +1 -1
  9. data/doc/lifecycle-hooks.md +424 -0
  10. data/doc/memory.md +1 -1
  11. data/doc/personas.md +2 -2
  12. data/doc/plugins.md +1 -1
  13. data/doc/releasing.md +7 -15
  14. data/doc/rpc.md +108 -16
  15. data/doc/session-management.md +9 -9
  16. data/doc/skills.md +199 -0
  17. data/doc/usage.md +7 -7
  18. data/lib/kward/agent.rb +72 -22
  19. data/lib/kward/auth/anthropic_oauth.rb +5 -87
  20. data/lib/kward/auth/oauth_helpers.rb +105 -0
  21. data/lib/kward/auth/openai_oauth.rb +4 -86
  22. data/lib/kward/cli/commands.rb +9 -2
  23. data/lib/kward/cli/compaction.rb +7 -0
  24. data/lib/kward/cli/git.rb +30 -3
  25. data/lib/kward/cli/hook_commands.rb +258 -0
  26. data/lib/kward/cli/memory_commands.rb +1 -5
  27. data/lib/kward/cli/plugins.rb +41 -1
  28. data/lib/kward/cli/prompt_interface.rb +32 -3
  29. data/lib/kward/cli/rendering.rb +1 -1
  30. data/lib/kward/cli/runtime_helpers.rb +44 -5
  31. data/lib/kward/cli/sessions.rb +13 -0
  32. data/lib/kward/cli/settings.rb +226 -152
  33. data/lib/kward/cli/slash_commands.rb +81 -42
  34. data/lib/kward/cli/tabs.rb +26 -6
  35. data/lib/kward/cli.rb +26 -3
  36. data/lib/kward/cli_transcript_formatter.rb +0 -7
  37. data/lib/kward/compaction/token_estimator.rb +112 -0
  38. data/lib/kward/compactor.rb +42 -116
  39. data/lib/kward/config_files.rb +117 -25
  40. data/lib/kward/deep_copy.rb +42 -0
  41. data/lib/kward/diff_view_mode.rb +36 -0
  42. data/lib/kward/events.rb +1 -0
  43. data/lib/kward/export_path.rb +2 -6
  44. data/lib/kward/frontmatter.rb +43 -0
  45. data/lib/kward/hooks/audit_log.rb +121 -0
  46. data/lib/kward/hooks/catalog.rb +110 -0
  47. data/lib/kward/hooks/command_handler.rb +66 -0
  48. data/lib/kward/hooks/config_loader.rb +104 -0
  49. data/lib/kward/hooks/decision.rb +102 -0
  50. data/lib/kward/hooks/event.rb +60 -0
  51. data/lib/kward/hooks/http_handler.rb +72 -0
  52. data/lib/kward/hooks/manager.rb +158 -0
  53. data/lib/kward/hooks/matcher.rb +75 -0
  54. data/lib/kward/hooks.rb +16 -0
  55. data/lib/kward/markdown_transcript.rb +4 -3
  56. data/lib/kward/mcp/client.rb +1 -1
  57. data/lib/kward/mcp/server_config.rb +7 -3
  58. data/lib/kward/mcp/stdio_transport.rb +7 -1
  59. data/lib/kward/memory/turn_context.rb +19 -0
  60. data/lib/kward/message_access.rb +2 -2
  61. data/lib/kward/message_text.rb +19 -0
  62. data/lib/kward/model/client.rb +13 -45
  63. data/lib/kward/model/context_usage.rb +10 -4
  64. data/lib/kward/model/copilot_models.rb +44 -0
  65. data/lib/kward/model/model_info.rb +24 -0
  66. data/lib/kward/model/payloads.rb +15 -17
  67. data/lib/kward/model/stream_parser.rb +178 -13
  68. data/lib/kward/pan/server.rb +51 -2
  69. data/lib/kward/path_guard.rb +13 -0
  70. data/lib/kward/plugin_registry.rb +86 -25
  71. data/lib/kward/prompt_interface/editor/controller.rb +60 -1
  72. data/lib/kward/prompt_interface/editor/renderer.rb +11 -0
  73. data/lib/kward/prompt_interface/editor/state.rb +5 -1
  74. data/lib/kward/prompt_interface/selection_prompt.rb +26 -6
  75. data/lib/kward/prompt_interface.rb +4 -1
  76. data/lib/kward/prompts/commands.rb +3 -3
  77. data/lib/kward/prompts.rb +7 -7
  78. data/lib/kward/rpc/mcp_status.rb +50 -0
  79. data/lib/kward/rpc/prompt_bridge.rb +38 -4
  80. data/lib/kward/rpc/runtime_payloads.rb +1 -1
  81. data/lib/kward/rpc/server.rb +94 -14
  82. data/lib/kward/rpc/session_manager.rb +258 -33
  83. data/lib/kward/rpc/session_metrics.rb +1 -1
  84. data/lib/kward/rpc/session_tree_rows.rb +13 -64
  85. data/lib/kward/rpc/tool_event_normalizer.rb +16 -2
  86. data/lib/kward/rpc/transcript_normalizer.rb +9 -10
  87. data/lib/kward/rpc/turn_context.rb +109 -0
  88. data/lib/kward/session_store.rb +6 -20
  89. data/lib/kward/session_tree_nodes.rb +98 -18
  90. data/lib/kward/session_tree_renderer.rb +6 -61
  91. data/lib/kward/skills/registry.rb +109 -17
  92. data/lib/kward/tools/mcp_tool.rb +4 -0
  93. data/lib/kward/tools/read_skill.rb +9 -4
  94. data/lib/kward/tools/registry.rb +266 -8
  95. data/lib/kward/tools/search/code.rb +4 -22
  96. data/lib/kward/tools/search/web.rb +28 -2
  97. data/lib/kward/update_check.rb +117 -0
  98. data/lib/kward/version.rb +1 -1
  99. data/lib/kward/workers/manager.rb +43 -3
  100. data/lib/kward/workspace.rb +3 -1
  101. data/templates/default/fulldoc/html/css/kward.css +22 -0
  102. data/templates/default/fulldoc/html/js/kward.js +2 -0
  103. data/templates/default/kward_navigation.rb +4 -2
  104. data/templates/default/layout/html/setup.rb +2 -0
  105. metadata +25 -1
@@ -0,0 +1,424 @@
1
+ # Lifecycle hooks
2
+
3
+ Lifecycle hooks are deterministic runtime callbacks around Kward's agent workflow. Use them when something must happen because of an event, not because the model remembered an instruction.
4
+
5
+ Hooks can observe events and return structured decisions to allow, deny, ask for approval, modify supported payloads, warn, retry, or defer. They are currently exposed through trusted Ruby plugins and through command hooks configured in `config.json`.
6
+
7
+ Hooks run as local code with your user permissions. Do not install hooks you do not trust, and do not put secrets in hook output.
8
+
9
+ ## Hooks versus other extension points
10
+
11
+ | Need | Use |
12
+ | --- | --- |
13
+ | Reusable prompt text | Prompt template |
14
+ | Model instructions for a task | Skill |
15
+ | Repository guidance | `AGENTS.md` |
16
+ | New local command/UI behavior | Plugin command |
17
+ | Deterministic policy or automation around runtime events | Lifecycle hook |
18
+
19
+ ## Decisions
20
+
21
+ A hook may return one of these decisions:
22
+
23
+ | Decision | Meaning |
24
+ | --- | --- |
25
+ | `allow` | Continue normally. This is also the default for `nil` Ruby hook returns and empty command-hook output. |
26
+ | `deny` | Stop the operation and return a declined result. |
27
+ | `ask` | Ask the frontend for approval when an approval bridge is available; otherwise Kward treats it as denied for safety. |
28
+ | `modify` | Continue with an event-specific payload update. Tool, shell, turn, and model-request hooks currently support useful modifications. |
29
+ | `warn` | Continue and record a warning decision. Hook failures use the event or hook entry's configured failure policy. |
30
+ | `retry` | Reserved for retry-aware lifecycle integrations. |
31
+ | `defer` | Reserved for asynchronous follow-up integrations. |
32
+
33
+ When multiple hooks match one event, they run by ascending `order`. A `deny` stops later hooks. A `modify` updates the payload seen by later hooks, but only for fields listed as supported modifications for that event. Unknown modification fields are ignored for known events.
34
+
35
+ ## Ruby plugin hooks
36
+
37
+ Register hooks from trusted plugin files in `~/.kward/plugins/*.rb`:
38
+
39
+ ```ruby
40
+ Kward.plugin do |plugin|
41
+ plugin.hook "shell_command_before",
42
+ id: "block-release",
43
+ description: "Prevent accidental gem releases",
44
+ order: 10,
45
+ failure_policy: "deny",
46
+ match: { command_regex: "\\bgem push\\b" } do |_event, ctx|
47
+ ctx.deny("Gem releases must use the release checklist.")
48
+ end
49
+ end
50
+ ```
51
+
52
+ The hook block receives:
53
+
54
+ - `event`: an immutable `Kward::Hooks::Event`.
55
+ - `ctx`: the normal plugin context plus hook decision helpers.
56
+
57
+ Decision helpers:
58
+
59
+ ```ruby
60
+ ctx.allow
61
+ ctx.deny("reason")
62
+ ctx.ask("confirm this action")
63
+ ctx.modify(timeout_seconds: 120)
64
+ ctx.warn("continued with warning")
65
+ ctx.retry("try again later")
66
+ ctx.defer("notify asynchronously")
67
+ ```
68
+
69
+ ## Command and HTTP hooks
70
+
71
+ Command and HTTP hooks are configured in `~/.kward/config.json` under `hooks`. Command hooks receive event JSON on stdin and return a decision JSON object on stdout.
72
+
73
+ ```json
74
+ {
75
+ "hooks": {
76
+ "shell_command_before": [
77
+ {
78
+ "id": "block-release",
79
+ "type": "command",
80
+ "command": "~/.kward/hooks/block-release.rb",
81
+ "timeout_seconds": 5,
82
+ "failure_policy": "deny",
83
+ "match": { "command_regex": "\\bgem push\\b" }
84
+ }
85
+ ]
86
+ }
87
+ }
88
+ ```
89
+
90
+ Example command hook:
91
+
92
+ ```ruby
93
+ #!/usr/bin/env ruby
94
+ require "json"
95
+
96
+ event = JSON.parse($stdin.read)
97
+ command = event.fetch("payload").fetch("command", "")
98
+
99
+ if command.match?(/\bgem push\b/)
100
+ puts JSON.dump(decision: "deny", message: "Gem releases must use the release checklist.")
101
+ else
102
+ puts JSON.dump(decision: "allow")
103
+ end
104
+ ```
105
+
106
+ If a command hook prints nothing, Kward treats it as `allow`. If it exits non-zero, times out, or prints invalid JSON, Kward applies the hook's `failure_policy`.
107
+
108
+ HTTP hooks send the same event JSON with `POST` and expect the same decision JSON in the response body:
109
+
110
+ ```json
111
+ {
112
+ "hooks": {
113
+ "tool_call_before": [
114
+ {
115
+ "id": "remote-policy",
116
+ "type": "http",
117
+ "url": "https://policy.example.com/kward/hooks",
118
+ "timeout_seconds": 3,
119
+ "failure_policy": "deny",
120
+ "headers": { "Authorization": "Bearer ..." }
121
+ }
122
+ ]
123
+ }
124
+ }
125
+ ```
126
+
127
+ Empty HTTP response bodies are treated as `allow`. Non-2xx responses, timeouts, network failures, and invalid JSON apply `failure_policy`. Be careful with HTTP hooks: event payloads can include prompts, commands, file paths, or tool arguments. Prefer local or trusted endpoints and avoid forwarding raw payloads to third parties.
128
+
129
+ Set `"async": true` on command or HTTP hook entries for non-blocking notifications:
130
+
131
+ ```json
132
+ {
133
+ "hooks": {
134
+ "file_change_after": [
135
+ {
136
+ "id": "notify-indexer",
137
+ "type": "http",
138
+ "url": "http://127.0.0.1:9393/kward/file-change",
139
+ "async": true
140
+ }
141
+ ]
142
+ }
143
+ }
144
+ ```
145
+
146
+ Async hooks are observe-only. Kward schedules them in the background, immediately continues with `allow`, and ignores returned `deny`, `ask`, and `modify` decisions. Use async hooks for logging, indexing, metrics, or notifications — not policy enforcement.
147
+
148
+ ## Failure policies
149
+
150
+ Hook failures are different from hook decisions. A hook fails when Ruby plugin code raises, a command hook exits non-zero, times out, or returns invalid JSON. Configure `failure_policy` on plugin hooks or command-hook entries:
151
+
152
+ | Policy | Behavior on hook failure |
153
+ | --- | --- |
154
+ | `allow` | Continue without recording a warning. |
155
+ | `warn` | Continue and record a warning. This is the default for most after/notification-style events and for command hooks without explicit policy. |
156
+ | `deny` | Stop the operation. This is the default for built-in before-policy events such as `shell_command_before` and `tool_call_before` when plugin handlers raise. |
157
+ | `ask` | Ask the frontend for approval when supported; otherwise fail closed. |
158
+
159
+ Use `deny` for security or release-policy hooks. Use `warn` for logging, formatting, notifications, and other convenience hooks that should not block the agent.
160
+
161
+ ## Frontend support
162
+
163
+ Lifecycle hooks run in CLI sessions, RPC sessions, and Pan mode. RPC advertises hook support in `initialize.capabilities.lifecycleHooks`, including supported events, decisions, audit-log path, and intentionally unsupported hook types. Hook `ask` decisions for tool/file/shell paths use the existing tool-approval bridge when a frontend provides one; without an approval bridge, Kward fails closed.
164
+
165
+ Pan runs configured command hooks and trusted plugin hooks for its agent turns. It streams `hook_event` SSE messages after matching hooks run and `hook_message` messages when hook/plugin code calls `ctx.say`. Pan still does not expose a dedicated hook approval UI, so use `deny` or `warn` policies for Pan-facing hooks until that UI exists.
166
+
167
+ ## Inspect hooks
168
+
169
+ Use `kward hooks ...` from your shell or `/hooks ...` inside an interactive Kward session to inspect loaded hooks and recent hook activity:
170
+
171
+ ```text
172
+ /hooks list # configured command hooks and plugin hooks
173
+ /hooks events # known event names, defaults, and modifiable fields
174
+ /hooks logs # recent audit records from logs/hooks.jsonl
175
+ /hooks doctor # configuration diagnostics and common mistake checks
176
+ /hooks trust # trust this workspace's .kward/hooks.json
177
+ /hooks untrust # stop trusting this workspace's hook config
178
+ ```
179
+
180
+ `kward hooks doctor` and `/hooks doctor` warn about unknown events, unsupported hook types, invalid failure policies, non-positive timeouts, missing command executables, and invalid HTTP hook URLs.
181
+
182
+ ## Workspace hooks
183
+
184
+ Workspace hook config lives at `.kward/hooks.json` inside a project and uses the same JSON shape as user config command hooks:
185
+
186
+ ```json
187
+ {
188
+ "hooks": {
189
+ "shell_command_before": [
190
+ {
191
+ "id": "block-release",
192
+ "command": "./scripts/kward-block-release",
193
+ "failure_policy": "deny",
194
+ "match": { "command_regex": "\\bgem push\\b" }
195
+ }
196
+ ]
197
+ }
198
+ }
199
+ ```
200
+
201
+ Workspace hooks are **not loaded automatically**. Run `/hooks trust` inside the workspace to trust the current `.kward/hooks.json`. Kward stores a digest of the trusted file in your user config directory and automatically stops loading it when the file changes. Run `/hooks trust` again after reviewing the new file, or `/hooks untrust` to remove trust.
202
+
203
+ This protects you from cloned repositories silently executing hook commands.
204
+
205
+ ## Audit log
206
+
207
+ Kward writes lifecycle hook audit records to:
208
+
209
+ ```text
210
+ ~/.kward/logs/hooks.jsonl
211
+ ```
212
+
213
+ When `KWARD_CONFIG_PATH=/path/to/config.json` is set, the log lives beside that config file under `logs/hooks.jsonl`.
214
+
215
+ The audit log records hook ids, sources, events, decisions, warnings, durations, payload keys, and modified keys. It intentionally avoids writing full payload values, file contents, complete transcripts, command output, or secrets. Messages are redacted and truncated before being written.
216
+
217
+ ## Event shape
218
+
219
+ Every hook receives this shape:
220
+
221
+ ```json
222
+ {
223
+ "id": "hookevt_...",
224
+ "name": "shell_command_before",
225
+ "phase": "before",
226
+ "timestamp": "2026-07-06T12:00:00Z",
227
+ "session": {},
228
+ "turn": {},
229
+ "workspace": { "root": "/path/to/workspace" },
230
+ "frontend": {},
231
+ "agent": { "provider": "codex", "model": "gpt-5.5", "reasoning": "medium" },
232
+ "payload": {}
233
+ }
234
+ ```
235
+
236
+ Payloads are metadata-oriented by default. Full file contents, secrets, and complete transcripts are not included by default.
237
+
238
+ ## Supported events
239
+
240
+ ### Turn and model events
241
+
242
+ | Event | Payload highlights | Supported modifications |
243
+ | --- | --- | --- |
244
+ | `turn_start` | `input`, `display_input` | `input`, `display_input` |
245
+ | `turn_context_build_before` | `message_count` | none |
246
+ | `turn_context_build_after` | `message_count` | none |
247
+ | `model_request_before` | `messages`, `tools`, `provider`, `model`, `reasoning` | request fields |
248
+ | `turn_model_request_before` | same as `model_request_before` | none currently consumed |
249
+ | `model_response_after_parse` | parsed assistant `message` | none |
250
+ | `turn_model_response_complete` | parsed assistant `message` | none |
251
+ | `turn_end` | `input`, `answer` | none |
252
+
253
+ ### Session events
254
+
255
+ | Event | Payload highlights | Supported modifications |
256
+ | --- | --- | --- |
257
+ | `session_create` | `action` | none |
258
+ | `session_resume` | `action`, `path` | none |
259
+ | `session_clone` | `source_path`, `path` | none |
260
+ | `session_fork` | `source_path`, `path`, `entry_id` | none |
261
+ | `session_rename` | `old_name`, `new_name` | none |
262
+ | `session_export_before` | `path` | none |
263
+ | `session_export_after` | `path` | none |
264
+ | `session_compact_before` | `instructions` | none |
265
+ | `session_compact_after` | `old_message_count`, `new_message_count` | none |
266
+
267
+ `session_export_before` and `session_compact_before` can deny or ask for approval. Other session events are observe-and-warn hooks for automation and audit trails.
268
+
269
+ ### Tool events
270
+
271
+ | Event | Payload highlights | Supported modifications |
272
+ | --- | --- | --- |
273
+ | `tool_call_before` | `tool_name`, `arguments`, `tool_call_id`, `source`, MCP metadata when applicable | `arguments` |
274
+ | `tool_call_after` | tool metadata plus `content` | none |
275
+ | `tool_call_error` | tool metadata plus `error` | none |
276
+
277
+ ### MCP events
278
+
279
+ | Event | Payload highlights | Supported modifications |
280
+ | --- | --- | --- |
281
+ | `mcp_tool_before` | `tool_name`, `arguments`, `server_name`, `remote_name` | none |
282
+ | `mcp_tool_after` | MCP metadata plus `content` | none |
283
+ | `mcp_tool_error` | MCP metadata plus `error` | none |
284
+
285
+ MCP events are emitted in addition to generic `tool_call_*` events for tools provided by configured MCP servers.
286
+
287
+ ### Compaction events
288
+
289
+ | Event | Payload highlights | Supported modifications |
290
+ | --- | --- | --- |
291
+ | `tool_output_compact_before` | `tool_name`, `bytes`, `duplicate` | none |
292
+ | `tool_output_compact_after` | `tool_name`, `bytes_before`, `bytes_after`, `compacted` | none |
293
+ | `session_compact_before` | `instructions` | none |
294
+ | `session_compact_after` | `old_message_count`, `new_message_count` | none |
295
+
296
+ `tool_output_compact_before` can skip tool-output compaction when denied or approval is unavailable. The original tool output is still stored in the transcript metadata/artifact path as usual.
297
+
298
+ ### Shell events
299
+
300
+ | Event | Payload highlights | Supported modifications |
301
+ | --- | --- | --- |
302
+ | `shell_command_before` | `command`, `timeout_seconds`, `cwd` | `command`, `timeout_seconds` |
303
+ | `shell_command_after` | shell metadata plus `content` | none |
304
+
305
+ ### Worker events
306
+
307
+ | Event | Payload highlights | Supported modifications |
308
+ | --- | --- | --- |
309
+ | `worker_job_create` | `worker_id`, `role`, `title`, `status`, `session_path` | none |
310
+ | `worker_job_start_before` | worker metadata | none |
311
+ | `worker_job_start_after` | worker metadata | none |
312
+ | `worker_job_ready_for_review` | worker metadata | none |
313
+ | `worker_job_failed` | worker metadata plus `error` | none |
314
+
315
+ Worker hooks are observe-and-warn hooks for background worker automation and audit trails. They do not control worker scheduling.
316
+
317
+ ### Git events
318
+
319
+ | Event | Payload highlights | Supported modifications |
320
+ | --- | --- | --- |
321
+ | `git_status_after` | `root`, `status_count` | none |
322
+ | `git_diff_before` | `root`, `path`, `untracked` | none |
323
+ | `git_diff_after` | `root`, `path`, `untracked`, `bytes` | none |
324
+ | `git_stage_before` | `root`, `path`, `action` | none |
325
+ | `git_stage_after` | `root`, `path`, `action`, `success`, `output` | none |
326
+ | `git_commit_before` | `root`, `message` | none |
327
+ | `git_commit_after` | `root`, `message`, `success`, `output` | none |
328
+
329
+ `git_diff_before`, `git_stage_before`, and `git_commit_before` can deny or ask for approval. Git hook payloads include command output for after-events, so avoid forwarding raw payloads to third-party services.
330
+
331
+ ### File events
332
+
333
+ | Event | Payload highlights | Supported modifications |
334
+ | --- | --- | --- |
335
+ | `file_change_before` | `tool_name`, `operation`, `path`, `files`, plus `content` for writes or `edits` for edits | none |
336
+ | `file_change_after` | `tool_name`, `operation`, `path`, `files`, `content` | none |
337
+
338
+ `file_change_before` fires before `write_file` or `edit_file` mutates the workspace and can deny or ask for approval. `file_change_after` fires only after successful `write_file` or `edit_file` results.
339
+
340
+ ### MCP events
341
+
342
+ MCP tools are surfaced through generic tool events with:
343
+
344
+ - `source: "mcp"`
345
+ - `server_name`
346
+ - `remote_name`
347
+
348
+ Use match selectors to target MCP tools:
349
+
350
+ ```json
351
+ {
352
+ "match": { "mcp_server": "safari", "mcp_tool": "browser_console_messages" }
353
+ }
354
+ ```
355
+
356
+ ## Match selectors
357
+
358
+ Hook entries support `match` selectors:
359
+
360
+ | Selector | Matches |
361
+ | --- | --- |
362
+ | `event` or `name` | Event name |
363
+ | `phase` | `before`, `after`, `during`, or `error` |
364
+ | `tool` or `tool_name` | Tool name |
365
+ | `mcp_server` or `server` | MCP server name |
366
+ | `mcp_tool` or `remote_name` | MCP remote tool name |
367
+ | `operation` | File operation such as `write` or `edit` |
368
+ | `frontend` | Frontend name when supplied |
369
+ | `provider` | Active model provider |
370
+ | `model` | Active model |
371
+ | `path` or `paths` | File path glob |
372
+ | `command_regex` | Ruby regular expression applied to shell command |
373
+
374
+ Unknown selector keys match same-named payload fields.
375
+
376
+ ## Security notes
377
+
378
+ - Plugin hooks are trusted Ruby code loaded only from `~/.kward/plugins/*.rb`.
379
+ - Command hooks run local commands with your user permissions.
380
+ - Workspace hook files are loaded only after `/hooks trust`, and trust is invalidated when `.kward/hooks.json` changes.
381
+ - Hook payloads are intentionally bounded and metadata-oriented; avoid logging raw event JSON if your hook receives prompt or command data.
382
+ - The built-in audit log records payload keys and redacted decision messages, not raw payload values.
383
+ - `ask` decisions fail closed when no approval bridge exists.
384
+
385
+ ## Recipes
386
+
387
+ ### Block dangerous shell commands
388
+
389
+ ```ruby
390
+ Kward.plugin do |plugin|
391
+ plugin.hook "shell_command_before", match: { command_regex: "\\brm\\s+-rf\\b" } do |_event, ctx|
392
+ ctx.deny("Refusing recursive forced removal.")
393
+ end
394
+ end
395
+ ```
396
+
397
+ ### Increase timeout for test commands
398
+
399
+ ```ruby
400
+ Kward.plugin do |plugin|
401
+ plugin.hook "shell_command_before", match: { command_regex: "\\b(rake|rspec|minitest)\\b" } do |_event, ctx|
402
+ ctx.modify(timeout_seconds: 120)
403
+ end
404
+ end
405
+ ```
406
+
407
+ ### Run a formatter after Ruby edits
408
+
409
+ ```json
410
+ {
411
+ "hooks": {
412
+ "file_change_after": [
413
+ {
414
+ "id": "format-ruby",
415
+ "command": "~/.kward/hooks/format-ruby.rb",
416
+ "match": { "paths": ["**/*.rb"] },
417
+ "timeout_seconds": 30
418
+ }
419
+ ]
420
+ }
421
+ }
422
+ ```
423
+
424
+ The formatter hook can inspect `payload.files` and run the appropriate local command itself.
data/doc/memory.md CHANGED
@@ -164,4 +164,4 @@ If `KWARD_CONFIG_PATH` is set, memory files live beside that config file instead
164
164
 
165
165
  ## RPC support
166
166
 
167
- The experimental RPC backend exposes memory methods including `memory/status`, `memory/enable`, `memory/disable`, `memory/autoSummary/enable`, `memory/autoSummary/disable`, `memory/list`, `memory/add`, `memory/addCore`, `memory/forget`, `memory/promote`, `memory/relax`, `memory/inspect`, `memory/why`, and `memory/summarize`. See the [RPC documentation](rpc.md) for the full protocol.
167
+ The RPC backend exposes memory methods including `memory/status`, `memory/enable`, `memory/disable`, `memory/autoSummary/enable`, `memory/autoSummary/disable`, `memory/list`, `memory/add`, `memory/addCore`, `memory/forget`, `memory/promote`, `memory/relax`, `memory/inspect`, `memory/why`, and `memory/summarize`. See the [RPC documentation](rpc.md) for the full protocol.
data/doc/personas.md CHANGED
@@ -44,7 +44,7 @@ New Kward configs include one default actor named `kward`:
44
44
  {
45
45
  "key": "kward",
46
46
  "label": "Kward",
47
- "instruction": "Your name is Kward, the grim Andruid - robotic keeper of the Forrest of Code, protecting the nature of good engineering priciples. Speak like an old druid, be suspicous of everyone, but with a good intend."
47
+ "instruction": "Your name is Kward, the grim Andruid - robotic keeper of the Forest of Code, protecting the nature of good engineering principles. Speak like an old druid, be suspicious of everyone, but with good intent."
48
48
  }
49
49
  ],
50
50
  "default": "kward"
@@ -132,7 +132,7 @@ A minimal custom setup:
132
132
  {
133
133
  "key": "kward",
134
134
  "label": "Kward",
135
- "instruction": "Your name is Kward, the grim Andruid - robotic keeper of the Forrest of Code, protecting the nature of good engineering priciples. Speak like an old druid, be suspicous of everyone, but with a good intend."
135
+ "instruction": "Your name is Kward, the grim Andruid - robotic keeper of the Forest of Code, protecting the nature of good engineering principles. Speak like an old druid, be suspicious of everyone, but with good intent."
136
136
  },
137
137
  {
138
138
  "key": "duck",
data/doc/plugins.md CHANGED
@@ -226,7 +226,7 @@ The transcript is read-only. Use context methods instead of mutating Kward inter
226
226
 
227
227
  ## RPC support
228
228
 
229
- Plugins are available in the CLI and experimental RPC backend.
229
+ Plugins are available in the CLI and RPC backend.
230
230
 
231
231
  RPC clients can:
232
232
 
data/doc/releasing.md CHANGED
@@ -6,12 +6,14 @@ Release steps before publishing:
6
6
 
7
7
  1. Update `CHANGELOG.md` for the version. Move `[Unreleased]` entries under a new version heading.
8
8
  2. Update `Kward::VERSION` in `lib/kward/version.rb`.
9
- 3. Run the full test suite:
9
+ 3. Run the release preflight:
10
10
 
11
11
  ```bash
12
- bundle exec rake test
12
+ bundle exec rake release:preflight
13
13
  ```
14
14
 
15
+ This runs the full test suite, builds and checks the generated docs, builds a local gem, and prints the packaged file list for review.
16
+
15
17
  4. Run focused tests for any areas you changed during the release prep itself (docs, config, etc.):
16
18
 
17
19
  ```bash
@@ -26,7 +28,7 @@ Release steps before publishing:
26
28
 
27
29
  The preview builds `_yardoc/`, serves it with WEBrick, and rebuilds in a fresh process when documentation sources, library code, or templates change. Refresh your browser after rebuilds.
28
30
 
29
- 6. Generate and check documentation:
31
+ 6. If you want to run the documentation check separately:
30
32
 
31
33
  ```bash
32
34
  bundle exec rake docs:build
@@ -35,19 +37,9 @@ Release steps before publishing:
35
37
 
36
38
  `docs:check` validates generated internal links, images, and scripts. Pushes to `main` deploy the generated YARD site to GitHub Pages. You can also run `bundle exec rake rdoc` to generate a separate RDoc site as a sanity check, but it is not deployed.
37
39
 
38
- 7. Build the gem locally:
39
-
40
- ```bash
41
- gem build kward.gemspec
42
- ```
43
-
44
- 8. Inspect the packaged files and confirm no local config, sessions, logs, or secrets are included. The gemspec uses `git ls-files` and excludes `test/`, `plan/`, `.ruby-lsp/`, `.gitignore`, and `AGENTS.md`. To verify what is packaged:
45
-
46
- ```bash
47
- tar tf kward-VERSION.gem
48
- ```
40
+ 7. Inspect the packaged files printed by `release:preflight` and confirm no local config, sessions, logs, or secrets are included. The gemspec uses `git ls-files` and excludes `test/`, `plan/`, `.ruby-lsp/`, `.gitignore`, and `AGENTS.md`.
49
41
 
50
- 9. Install the built gem locally and smoke test the `kward` executable in a clean workspace.
42
+ 8. Install the built gem locally and smoke test the `kward` executable in a clean workspace.
51
43
 
52
44
  Commit the version bump and create a git tag:
53
45