@kubestellar/pluk
v0.8.6
Published
Pluk structured events from AI agent terminal output — classify, subscribe, and react to JSONL event streams
Maintainers
Readme
@kubestellar/pluk
Pluk structured events from AI agent terminal output.
TypeScript port of kubestellar/pluk — classify, subscribe, and react to JSONL event streams from AI coding agents (Claude Code, GitHub Copilot CLI, Gemini CLI, Goose, etc.).
Install
npm install -g @kubestellar/plukQuick Start
One command to create a tmux session, start an AI CLI, attach pluk event capture, and wire up rationguard for real-time excuse detection:
pluk attach my-agent --cli=claude --rationguard --rebuttal=sendThis does four things:
- Creates a tmux session named
my-agent - Starts
claudeinside it - Attaches pluk via
tmux pipe-paneto classify all terminal output - Starts
rationguard watchto detect rationalizations and send rebuttals back
To just attach pluk without rationguard:
pluk attach my-agent --cli=claudeTo attach to an existing tmux session (e.g., one already running goose):
pluk attach my-agent --cli=gooseNote: When attaching to an existing session with
--dangerous, the CLI may not have permission-skip enabled (the flag only applies when creating a new session). pluk will print a warning with the command to restart the CLI with the right flag.
A new terminal window is always opened for the tmux session (unless --no-open is set), so you can interact with the agent directly.
See What's Running
pluk sessionsSESSION CLI STATE TMUX LAST ACTIVITY EVENTS
scanner claude working ● 2s ago 1204
helper claude idle ● 45s ago 892
goose-exp goose idle ○ 3m ago 156Manual Setup
If you prefer to wire things up yourself:
# 1. Start an AI agent in tmux
tmux new-session -d -s my-agent
tmux send-keys -t my-agent "claude" Enter
# 2. Attach pluk to capture output
export PLUK_RUN_DIR=/tmp/pluk-run
tmux pipe-pane -t my-agent -o "pluk watch my-agent --cli=claude"
# 3. Subscribe to events (another terminal)
pluk subscribe my-agent --filter=state_change,rate_limit,error
# 4. Or start rationguard for real-time detection
rationguard watch my-agent --rebuttal=send
# 5. View the agent
tmux attach -t my-agentCLI Commands
| Command | What it does |
|---------|-------------|
| pluk attach <session> | Create tmux + start CLI + wire pluk (+ rationguard) |
| pluk sessions | List active pluk-monitored sessions |
| pluk subscribe <session> | Tail a pluk JSONL log (like tail -f) |
| pluk watch <session> | Classify stdin line-by-line |
| pluk send <session> --text="..." --enter | Send text to a tmux session |
| pluk patterns --cli=claude | Show loaded patterns for a CLI |
Attach Flags
| Flag | What it does |
|------|-------------|
| --cli=claude | CLI type: claude, copilot, gemini, goose, codex, aider |
| --rationguard | Start rationguard watcher alongside pluk |
| --rebuttal=send | Auto-send rebuttals when rationguard detects excuses |
| --dangerous | Skip CLI permission prompts (--dangerously-skip-permissions for claude, --full-auto for codex, --non-interactive for goose) |
| --dir=/path | Working directory for the agent |
| --cli-args="..." | Extra arguments to pass to the CLI |
| --no-open | Don't open a terminal window |
| --verbose | Show debug output |
Programmatic API
import { Classifier, getPatterns, subscribe, watch, discoverSessions, attach, send } from '@kubestellar/pluk';
// One-command setup: tmux + CLI + pluk + rationguard
attach({
session: 'my-agent',
cli: 'claude',
rationguard: true,
rebuttal: 'send',
dangerouslySkipPermissions: true,
});
// Send text to a tmux session
send({ session: 'my-agent', text: 'check the build', enter: true });
// Discover running sessions
const sessions = discoverSessions('/tmp/pluk-run');
for (const s of sessions) {
console.log(`${s.session}: ${s.cli} (${s.state}, ${s.lastActivityAgo})`);
}
// Classify individual lines
const patterns = getPatterns('claude');
const classifier = new Classifier({ session: 'my-agent', patterns });
const event = classifier.classify('● Read main.go');
// → { type: 'tool_call_started', data: { tool: 'Read', ... } }
// Subscribe to a JSONL log file (tail -f behavior)
const sub = subscribe('my-agent', (event) => {
console.log(event.type, event.data);
}, { filter: ['rate_limit', 'error'] });
// Watch stdin for events
const watcher = watch({
session: 'my-agent',
cli: 'claude',
onEvent(event) {
if (event.type === 'rate_limit') {
console.warn('Rate limited!', event.data.message);
}
},
});Event Types
| Type | Meaning |
|------|---------|
| raw_output | Every non-empty terminal line |
| state_change | Agent went idle or started working |
| rate_limit | Usage limit / quota exhausted |
| login_required | Authentication needed |
| trust_dialog | Folder trust prompt |
| bypass_permissions | Permission bypass prompt |
| tool_call_started | Agent invoked a tool |
| tool_call_completed | Tool finished |
| error | Error in output |
| model_changed | Model was switched |
| session_ended | CLI session ended |
| command_received | Command sent via pluk-send |
Send Command
Inject text into a running tmux session — used by rationguard to send rebuttals, or by you to send commands:
# Send text and press Enter
pluk send my-agent --text="check the build logs" --enter
# Send literal text (no key interpretation)
pluk send my-agent --text="hello world" --literal
# Also available as a standalone binary
pluk-send --session=my-agent --text="test" --enterSupported CLIs
Built-in pattern files for: Claude Code, GitHub Copilot CLI, Gemini CLI, Goose CLI, Codex, Aider.
Custom patterns can be loaded from a directory with --patterns-dir or getPatterns(cli, patternsDir).
Works With
- @kubestellar/rationguard — real-time rationalization detection and rebuttal
- @kubestellar/promptargs — template variable substitution for AI prompts
- kubestellar/pluk — the Go binary (this package is the TypeScript port)
License
Apache-2.0
