Plugins
Plugins are trusted local Ruby extensions for Kward. Use them when you need behavior that prompts, skills, or instructions cannot provide.
Good plugin use cases:
- add a slash command for a personal workflow,
- show project/session status in the terminal footer,
- add concise local context to prompts,
- log or observe transcript events,
- expose local commands to an RPC client.
Plugins run inside the Kward process with your user permissions. Install only plugins you trust.
When to use a plugin
| Need | Better choice |
|---|---|
| Reusable prompt text | prompt template |
| Reusable model instructions | skill |
| Repository rules | AGENTS.md |
| Local Ruby code or integration | plugin |
Where plugins live
Kward loads top-level Ruby files from:
~/.kward/plugins/*.rb
Plugins are not loaded from the current workspace or a custom KWARD_CONFIG_PATH directory. This prevents a project checkout from silently adding executable Ruby code to Kward.
A first plugin
Create the plugin directory:
mkdir -p ~/.kward/plugins
Create ~/.kward/plugins/hello.rb:
Kward.plugin do |plugin|
plugin.command "hello", description: "Say hello", argument_hint: "[name]" do |args, ctx|
name = args.strip.empty? ? "there" : args.strip
ctx.say("Hello, #{name}.")
end
end
Start Kward and run:
/hello Kai
Add a slash command
Use plugin commands for local actions that should not call the model.
Kward.plugin do |plugin|
plugin.command "session-info", description: "Show session details" do |_args, ctx|
ctx.say("Session: #{ctx.session_name || ctx.session_id || 'unnamed'}")
ctx.say("Workspace: #{ctx.workspace_root}")
end
end
Command names do not include /. They must start with a letter or number and may contain letters, numbers, _, and -.
A plugin command cannot replace a built-in command or prompt-template command.
Add prompt context
Prompt context is short text injected into future model requests.
Use it for stable facts the model should know, not for large files or secrets.
Kward.plugin do |plugin|
plugin.prompt_context do |ctx|
next unless File.exist?(File.join(ctx.workspace_root, "Gemfile"))
"Workspace note: this project uses Ruby. Prefer bundle exec for project commands."
end
end
If plugin state changes and Kward should rebuild the active system message, call:
ctx.
Add a footer
A footer can show compact local status in the terminal UI:
Kward.plugin do |plugin|
plugin. do |ctx|
"#{ctx.session_name || 'unnamed'} • #{ctx.transcript..length} messages"
end
end
Only one footer is active. If multiple plugins register footers, the later one replaces the earlier one and Kward prints a warning.
Observe transcript events
Use transcript events when you need to log or react to live activity:
Kward.plugin do |plugin|
plugin.on_transcript_event do |event, ctx|
next unless event.type == "assistant_delta"
File.open(File.join(ctx.workspace_root, ".assistant-stream.log"), "a") do |file|
file.write(event.payload[:delta])
end
end
end
Event payloads are read-only copies. Handler errors are caught and printed as warnings.
Common event types include:
reasoning_deltaassistant_deltaassistant_messagemodel_retryturn_steeredtool_calltool_resultanswer
Plugin context
Handlers receive a ctx object. Common methods:
ctx.workspace_rootctx.argsctx.say(message)ctx.transcript.messagesctx.session_idctx.session_namectx.session_pathctx.refresh_system_message!
The transcript is read-only. Use context methods instead of mutating Kward internals.
RPC support
Plugins are available in the CLI and experimental RPC backend.
RPC clients can:
- list plugin commands through
commands/list, - run plugin commands through
commands/run, - run plugin slash commands through
turns/startinput such as/hello Kai.
Plugin command output is emitted through normal turn events without calling the model.
Security
Plugins are local Ruby code. They can read files, write files, run commands, make network requests, and read environment variables as your user.
Recommended practices:
- Install plugins only from sources you trust.
- Keep plugins in your personal
~/.kward/pluginsdirectory. - Do not put secrets in shared plugin files.
- Prefer environment variables or private config for credentials.
- Keep prompt context short and never inject secrets into model prompts.
- Be careful with transcript observers that persist conversation content.