Skip to content
Docs navigation: RightNow Agent / Hooks

Hooks

DocsRightNow AgentHooks Updated

Release status

RightNow Agent, the terminal coding agent, is paused. Its guides stay online as reference.

Hooks run a command or send an HTTPS request when a session, turn, tool call, subagent, or compaction event fires.

Define hooks in JSON or TOML

Save this Windows example as bin/safety-check.ps1. It reads one event from standard input and returns a deny decision when run_terminal_command contains Remove-Item followed by -Recurse. Other valid inputs produce no output.

PowerShell
$hookPayload = [Console]::In.ReadToEnd() | ConvertFrom-Json
$command = [string]$hookPayload.toolInput.command

if ($hookPayload.toolName -eq "run_terminal_command" -and $command -match "(?i)\bRemove-Item\b.*-Recurse\b") {
  @{ decision = "deny"; reason = "Recursive removal requires manual review" } | ConvertTo-Json -Compress
}

The next two blocks are configuration templates for that script. Choose JSON or TOML; do not install both copies of the same hook.

JSON
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "powershell -NoProfile -File bin/safety-check.ps1", "timeout": 10 }
        ]
      }
    ]
  }
}
TOML
[[hooks.PreToolUse]]
matcher = "Bash"
hooks = [
  { type = "command", command = "powershell -NoProfile -File bin/safety-check.ps1", timeout = 10 },
]
FileScope
~/.rightnow/hooks/*.jsonUser.
<repo>/.rightnow/hooks/*.jsonProject, at the repository root.
~/.rightnow/config.toml, managed_config.toml, requirements.tomlConfig layers. All run; duplicates run once.
hooks/hooks.json in a pluginPlugin.

Handler fields: type (command or http), command, url, env, and timeout in seconds (default 5; 600 for Stop and SubagentStop). A direct executable path in a JSON hook file resolves relative to that file.

The inline PowerShell command above starts in the workspace root, so its bin/safety-check.ps1 path is workspace-relative.

Hook events

EventFires when
SessionStartSession starts
UserPromptSubmitPrompt submitted
PreToolUseBefore a tool call; can deny or rewrite it
PostToolUseTool call succeeded
PostToolUseFailureTool call failed
PermissionDeniedPermission system denied a call
StopMain agent completes a turn; can block the stop
StopFailureTurn ends with an API error
StopCancelledTurn is interrupted or cancelled
NotificationUser-attention event
SubagentStartSubagent starts
SubagentStopSubagent turn completes; can block the stop
PreCompactBefore compaction
PostCompactAfter compaction
SessionEndSession ends

SubagentEnd is accepted as an alias for SubagentStop. Every other event only observes.

Matcher rules

  • An empty or omitted matcher, or *, matches every invocation in its group. A plain name or | list made only of letters, digits, and underscores matches only a value equal to one of its terms; any other pattern is an unanchored regular expression.
  • Tool events match the resolved tool name, notifications match their type, and subagent events match the subagent type. MCP calls match as server__tool, so github matches no MCP tool while ^github__ matches every tool of that server.
  • Stop and UserPromptSubmit ignore matchers with a warning.
  • Claude-style matcher names such as Bash, Read, Edit, Write, Grep, and WebSearch map to RightNow Agent tool names while retaining their original match.

Read the stdin envelope

Each event sends camelCase common fields plus its own. This PreToolUse example is complete; transcript, client, and subagent fields appear only when available.

JSON
{
  "hookEventName": "pre_tool_use",
  "sessionId": "session-id",
  "cwd": "C:\\work\\project",
  "workspaceRoot": "C:\\work\\project",
  "timestamp": "2026-04-14T12:00:00Z",
  "promptId": "prompt-id",
  "permissionMode": "default",
  "toolName": "run_terminal_command",
  "toolUseId": "tool-use-id",
  "toolInput": { "command": "rightnow doctor", "description": "Check terminal support" },
  "toolInputTruncated": false
}

Deny or rewrite a tool call

A PreToolUse hook allows, denies, or rewrites the call. Emit one JSON response on stdout for each invocation; no output allows it.

Deny

JSON
{ "decision": "deny", "reason": "Unsafe command detected" }

Rewrite

JSON
{
  "hookSpecificOutput": {
    "updatedInput": { "command": "rightnow doctor", "description": "Check terminal support" }
  }
}
  • updatedInput must be a JSON object. It replaces the whole input that plan mode, the permission prompt, and the tool see, so run_terminal_command needs both command and description.
  • The last valid rewrite wins, a deny discards every rewrite, and a rewrite that fails the tool schema is blocked as invalid input.
  • Any other decision value is an error, and the hook fails open.

Gate completion with Stop

A Stop or SubagentStop hook decides whether the turn may end.

OutputEffect
{"decision": "block", "reason": "..."}Keep working. The reason is fed back to the model in the same turn.
{"hookSpecificOutput": {"additionalContext": "..."}}Keep working, shown as feedback rather than an error.
{"continue": false, "stopReason": "..."}End the turn, overriding any block.
No output, exit 0Allow the stop.

Input adds stopHookActive (true during a continuation), lastAssistantMessage, and reason. Interrupts and API errors fire StopCancelled and StopFailure instead, and the session-end Stop cannot block. After eight continuations in one turn, the turn ends.

Exit-code behavior

  • Exit code 0 allows the action when no blocking JSON decision is returned.
  • For PreToolUse, a valid JSON deny is honored on any exit code; a valid allow response does not override exit code 2. For Stop and SubagentStop, valid decision JSON wins; without it, exit code 2 blocks and supplies stderr as feedback.
  • Other nonzero exits fail open when stdout has no valid blocking decision: the failure is recorded, but the action is not blocked.
Warning: Hooks fail open

Timeouts, crashes, malformed output, and missing required variables fail open. A rewrite that fails the tool schema still blocks the call. Nonzero exits follow the rules above.

Hook environment

Every command hook receives RIGHTNOW_HOOK_EVENT, RIGHTNOW_HOOK_NAME, RIGHTNOW_SESSION_ID, RIGHTNOW_WORKSPACE_ROOT, and the compatibility alias CLAUDE_PROJECT_DIR. These names are reserved and handler env values cannot override them.

Handler command and url fields expand ${VAR} and $VAR when the hook runs. A shell command that references an unset variable without a default does not run.

HTTP hooks

Template: replace the URL with an endpoint you control before adding this handler to a hook group.

JSON
{ "type": "http", "url": "https://hooks.example.com/rightnow-event", "timeout": 15 }

HTTP hooks POST the full event envelope as JSON. Only https:// URLs are accepted. The initial URL check refuses resolved private, link-local, and cloud-metadata addresses (loopback is allowed); the request resolves hostnames again. Redirects are not followed. A gate reads its decision from the response body.

Claude Code and Cursor compatibility

  • Claude Code hooks load from settings.json and settings.local.json in ~/.claude/ and the project's .claude/ ([compat.claude] hooks, on by default) until an import or dismissal through /import-claude saves [claude_compat] imported = true.
  • Cursor hooks load from ~/.cursor/hooks.json and the project's .cursor/hooks.json ([compat.cursor] hooks, on by default); camelCase event names map to RightNow events.
  • Input keys are camelCase, such as stopHookActive and toolResult, and UserPromptSubmit is observe-only, so a ported prompt-blocking hook stops blocking.
  • A local build without a GROK_VERSION stamp does not prompt for folder trust. A stamped build applies its folder-trust settings. Review the repository's hook files before running them; command hooks run as your user.

Next task

/hooks opens the Hooks tab. If a hook does not run, confirm its source and status in the Hooks tab, then inspect the failure entry in the session scrollback or logs and continue with troubleshooting.