Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ rtk init --agent cline # Cline / Roo Code
rtk init --agent kilocode # Kilo Code
rtk init --agent antigravity # Google Antigravity
rtk init --agent hermes # Hermes
rtk init --agent omp # Oh My Pi (OMP)

# 2. Restart your AI tool, then test
git status # Automatically rewritten to rtk git status
Expand Down Expand Up @@ -366,6 +367,7 @@ RTK supports 13 AI coding tools. Each integration rewrites shell commands to `rt
| **OpenCode** | `rtk init -g --opencode` | Plugin TS (tool.execute.before) |
| **OpenClaw** | `openclaw plugins install ./openclaw` | Plugin TS (before_tool_call) |
| **Hermes** | `rtk init --agent hermes` | Python plugin adapter (terminal command mutation via `rtk rewrite`) |
| **Oh My Pi (OMP)** | `rtk init --agent omp` / `rtk init -g --agent omp` | OMP extension (`tool_call`) |
| **Mistral Vibe** | Planned ([#800](https://github.com/rtk-ai/rtk/issues/800)) | Blocked on upstream |
| **Kilo Code** | `rtk init --agent kilocode` | .kilocode/rules/rtk-rules.md (project-scoped) |
| **Google Antigravity** | `rtk init --agent antigravity` | .agents/rules/antigravity-rtk-rules.md (project-scoped) |
Expand Down
8 changes: 8 additions & 0 deletions docs/guide/getting-started/supported-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Agent runs "cargo test"
| OpenClaw | TypeScript plugin (`before_tool_call`) | Yes |
| Pi | TypeScript extension (`tool_call` event) | Yes |
| Hermes | Python plugin (`terminal` command mutation) | Yes |
| Oh My Pi (OMP) | TypeScript extension (`tool_call`) | Yes |
| Cline / Roo Code | Rules file (prompt-level) | N/A |
| Windsurf | Rules file (prompt-level) | N/A |
| Codex CLI | AGENTS.md instructions | N/A |
Expand Down Expand Up @@ -125,6 +126,13 @@ Creates `~/.hermes/plugins/rtk-rewrite/` and enables it through `plugins.enabled

The plugin fails open. If `rtk` is missing at load time, the hook is not registered. If `rtk rewrite` errors, the tool is not `terminal`, the payload has no string `command`, or the plugin raises an exception, Hermes runs the original command unchanged. The same `rtk rewrite` limitations apply: already-prefixed `rtk` commands, compound shell commands, heredocs, and commands without filters are not rewritten.

### Oh My Pi

```bash
rtk init --agent omp # creates ./.omp/extensions/rtk.ts
rtk init -g --agent omp # creates ~/.omp/agent/extensions/rtk.ts
```

### Cline / Roo Code

```bash
Expand Down
4 changes: 3 additions & 1 deletion hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

**Deployed hook artifacts** — the actual files installed on user machines by `rtk init`. These are shell scripts, TypeScript plugins, and rules files that run outside the Rust binary. They are **thin delegates**: parse agent-specific JSON, call `rtk rewrite` as a subprocess, format agent-specific response. Zero filtering logic lives here.

Owns: per-agent hook scripts and configuration files for 9 supported agents (Claude Code, Copilot, Cursor, Cline, Windsurf, Codex, OpenCode, Hermes, Pi).
Owns: per-agent hook scripts and configuration files for supported agents (Claude Code, Copilot, Cursor, Cline, Windsurf, Codex, OpenCode, Hermes, Pi, Oh My Pi).

Does **not** own: hook installation/uninstallation (that's `src/hooks/init.rs`), the rewrite pattern registry (that's `discover/registry`), or integrity verification (that's `src/hooks/integrity.rs`).

Expand Down Expand Up @@ -41,6 +41,7 @@ Each agent subdirectory has its own README with hook-specific details:
- **[`codex/`](codex/README.md)** — Awareness document, `AGENTS.md` integration, `$CODEX_HOME` or `~/.codex/` location
- **[`opencode/`](opencode/README.md)** — TypeScript plugin, `zx` library, `tool.execute.before` event, in-place mutation
- **[`pi/`](pi/README.md)** — TypeScript extension, `tool_call` event, `isToolCallEventType` guard, in-place mutation, `~/.pi/agent/extensions/`
- **[`omp/`](omp/README.md)** — TypeScript extension, OMP `tool_call` rewrite via `./.omp/extensions/rtk.ts` or `~/.omp/agent/extensions/rtk.ts`
- **[`hermes/`](hermes/README.md)** — Python plugin, `pre_tool_call` hook, in-place terminal command mutation

## Supported Agents
Expand All @@ -58,6 +59,7 @@ Each agent subdirectory has its own README with hook-specific details:
| OpenCode | TypeScript plugin (`tool.execute.before`) | In-place mutation | Yes |
| Pi | TypeScript extension (`tool_call` event) | In-place mutation | Yes |
| Hermes | Python plugin (`pre_tool_call`) | In-place mutation | Yes |
| Oh My Pi (OMP) | TypeScript extension (`tool_call`) | In-place mutation | Yes (`event.input.command`) |

## JSON Formats by Agent

Expand Down
12 changes: 12 additions & 0 deletions hooks/omp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Oh My Pi Hooks

> Part of [`hooks/`](../README.md) — see also [`src/hooks/`](../../src/hooks/README.md) for installation code

## Specifics

- TypeScript extension module, not a shell hook or rules file
- Sets its OMP extension display label to `RTK`
- Installs to `./.omp/extensions/rtk.ts` with `rtk init --agent omp`, or to `~/.omp/agent/extensions/rtk.ts` with `rtk init -g --agent omp`
- Intercepts OMP `tool_call` events for the `bash` tool and delegates rewrite decisions to `rtk rewrite`
- Requires Bun runtime (uses `Bun.which` and `Bun.spawn`); OMP currently ships with Bun
- Multi-extension chaining: OMP dispatches `tool_call` handlers sequentially. Downstream handlers observe the RTK-rewritten `event.input.command` only when RTK actually rewrites it
101 changes: 101 additions & 0 deletions hooks/omp/rtk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// RTK - Rust Token Killer
// OMP extension: rewrite bash tool calls through `rtk rewrite`.
//
// This is a thin delegating extension. All rewrite logic lives in RTK's Rust
// registry via `rtk rewrite`, which remains the single source of truth.

type BashToolCallEvent = {
toolName: string;
input: { command: string };
};

type ExtensionContext = {
ui: {
setStatus(key: string, text: string | undefined): void;
};
};

type ExtensionAPI = {
setLabel(label: string): void;
on(
event: "session_start",
handler: (
event: unknown,
ctx: ExtensionContext,
) => Promise<void> | void,
): void;
on(
event: "tool_call",
handler: (
event: BashToolCallEvent,
) => Promise<void> | void,
): void;
};

type RewriteDecision = { kind: "rewrite"; rewritten: string } | { kind: "skip" };

function readText(stream: ReadableStream<Uint8Array> | null | undefined, name: string): Promise<string> {
if (!stream) {
throw new Error(`rtk rewrite ${name} stream was unavailable`);
}
return new Response(stream).text().then((text) => text.trim());
}

async function rewriteWithRtk(command: string): Promise<RewriteDecision> {
const proc = Bun.spawn(["rtk", "rewrite", command], {
stdout: "pipe",
stderr: "pipe",
});

const [exitCode, stdout] = await Promise.all([
proc.exited,
readText(proc.stdout, "stdout"),
proc.stderr?.cancel(),
]);

switch (exitCode) {
case 0:
case 3:
if (!stdout || stdout === command) {
return { kind: "skip" };
}
return { kind: "rewrite", rewritten: stdout };
default:
return { kind: "skip" };
}
}

export default function rtkOmpExtension(pi: ExtensionAPI) {
pi.setLabel("RTK");

const hasRtk = Boolean(Bun.which("rtk"));

if (!hasRtk) {
pi.on("session_start", (_event, ctx) => {
ctx.ui.setStatus("rtk", "RTK extension disabled: rtk binary not found in PATH.");
});
return;
}

pi.on("tool_call", async (event) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, why not just skip registering the tool_call hook if RTK isn't available?
How could we alert the user about that?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I moved the RTK availability check to extension setup: when rtk is missing, the extension now registers only a session_start warning via OMP's UI and skips the
tool_call handler; when rtk is available, it registers the rewrite hook as before.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially tried ctx.ui.notify(..., "warning"), but session_start notifications did not appear reliably during OMP startup, likely because the initial chat container is rebuilt afterward.

So I chose the persistent hook status line instead, as shown in the screenshots below.

image 屏幕截图 2026-05-19 052602

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to align this with the OpenCode plugin behavior: when rtk is missing, don't register the tool hook and surface a warning.

For OMP, it's a little werid.

 mode.init()
    -> UI start
    -> initHooksAndCustomTools()
    -> emit session_start
    -> extension: ctx.ui.notify(...)
    -> showWarning -->chatContainer

  main.ts 
    -> mode.renderInitialMessages()
    -> chatContainer.clear()
    -> warning clear (not show)

As a result, I cant see any warning expected.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does persistent hook status works flawlessly? It seems enough. I'm not sure to understand your latest message.

if (event.toolName !== "bash") {
return;
}

const original = event.input.command;
if (original.trim() === "") {
return;
}

try {
const decision = await rewriteWithRtk(original);
if (decision.kind === "skip") {
return;
}

event.input.command = decision.rewritten;
} catch {
return;
}
});
}
3 changes: 2 additions & 1 deletion src/hooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

The **lifecycle management** layer for LLM agent hooks: install, uninstall, verify integrity, audit usage, and manage trust. This component creates and maintains the hook artifacts that live in `hooks/` (root), but does **not** execute rewrite logic itself — that lives in `discover/registry`.

Owns: `rtk init` installation flows (5 agents via `AgentTarget` enum + 3 special modes: Gemini, Codex, OpenCode), SHA-256 integrity verification, hook version checking, audit log analysis, `rtk rewrite` CLI entry point, and TOML filter trust management.
Owns: `rtk init` installation flows for AgentTarget-based agents and special modes (Gemini, Codex, OpenCode, Oh My Pi), SHA-256 integrity verification, hook version checking, audit log analysis, `rtk rewrite` CLI entry point, and TOML filter trust management.

Does **not** own: the deployed hook scripts themselves (that's `hooks/`), the rewrite pattern registry (that's `discover/`), or command filtering (that's `cmds/`).

Expand All @@ -32,6 +32,7 @@ LLM agent integration layer that installs, validates, and executes command-rewri
| Cursor | `rtk init -g --agent cursor` | Cursor hook | hooks.json |
| Pi | `rtk init --agent pi` | `.pi/extensions/rtk.ts` | -- |
| Hermes | `rtk init --agent hermes` | Python plugin in `~/.hermes/plugins/rtk-rewrite/` | `config.yaml` `plugins.enabled` |
| Oh My Pi | `rtk init --agent omp` / `rtk init -g --agent omp` | OMP extension | -- |


## Integrity Verification
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ pub const HERMES_PLUGINS_SUBDIR: &str = "plugins";
pub const HERMES_PLUGIN_NAME: &str = "rtk-rewrite";
pub const HERMES_PLUGIN_INIT_FILE: &str = "__init__.py";
pub const HERMES_PLUGIN_MANIFEST_FILE: &str = "plugin.yaml";
pub const OMP_GLOBAL_EXTENSION_PATH: &str = ".omp/agent/extensions/rtk.ts";
pub const OMP_PROJECT_EXTENSION_PATH: &str = ".omp/extensions/rtk.ts";
Loading