← Back to Plugins
Channels

Gemini Claw

nicshik By nicshik 👁 46 views ▲ 0 votes

OpenClaw /antigravity plugin: an instant Telegram control panel for the Antigravity CLI (agy) under Google AI Pro. One-command install.

GitHub

Install

openclaw plugins install <this

README

# gemini-claw

[![CI](https://github.com/nicshik/gemini-claw/actions/workflows/preflight.yml/badge.svg)](https://github.com/nicshik/gemini-claw/actions/workflows/preflight.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Install the **Antigravity CLI (`agy`)** on an OpenClaw server and register an
`antigravity` **control-panel plugin** for it — under a Google **AI Pro** session.

> **TL;DR** — on the OpenClaw host, one line as root:
> `curl -fsSL https://raw.githubusercontent.com/nicshik/gemini-claw/main/scripts/bootstrap.sh | sudo bash`
> — fetches the repo and runs the whole setup (deps → preflight → install → login
> → buttons → restart → healthcheck). The one human step is the Google AI Pro
> OAuth. Then send `/antigravity` in Telegram. Prerequisites and step-by-step below.

Google retired the standalone `gemini` CLI for individual tiers (it fails with
`IneligibleTierError: UNSUPPORTED_CLIENT → "migrate to Antigravity"`). The
successor is the **Antigravity CLI** (`agy`). This repo installs `agy`, puts it on
the gateway's `PATH`, and installs a native OpenClaw plugin that exposes a
`/antigravity` control panel in chat (Telegram) — no translation shims, no
pretending to be the old `gemini` CLI.

Works on any **OpenClaw host** running the gateway as a dedicated service user. It
is OpenClaw-specific glue — it installs into the OpenClaw gateway via `openclaw
plugins install`, so it does not apply to non-OpenClaw agent stacks.

## Why a plugin (not a skill)

The panel is an OpenClaw **plugin command**, so `/antigravity` is handled *before
the LLM agent*. Menu navigation, model switching, and status are drawn in code in
milliseconds — no model is invoked to render a menu, so it is instant and immune
to a slow/flaky agent backend. (An earlier version was an agent *skill*: the bot's
LLM had to interpret every tap, which was slow and stalled whenever the model
backend flapped.) The only slow parts left are the real `agy` calls (`ask` /
`image`), which are slow because `agy` itself is — with no LLM overhead on top.

The plugin is a first-class, portable OpenClaw extension: installed via
`openclaw plugins install`, it lives under the service user's home and survives
`openclaw update` — and drops into any host running a compatible OpenClaw with no
dist patching or harness wiring.

## What it does

- Installs `agy` for the OpenClaw service user (official `curl … | bash` installer).
- Symlinks `agy` onto the gateway `PATH` (`/usr/local/bin/agy`) — a stable location
  that survives `openclaw update` / node bumps.
- Installs a model-list cache helper (`agy-models`) + a daily refresh timer so the
  `/antigravity model` menu is instant and self-updating.
- Installs the **`antigravity`** plugin (`openclaw plugins install`) into
  `~/.openclaw/extensions/`, separate from the git-synced workspace.

## The `/antigravity` panel

For users, everything lives in Telegram — no setup on their side. Send
`/antigravity` for the panel, `/antigravity_ask <вопрос>` to ask, and
`/antigravity_image <описание>` to generate an image. The operator installs it
once (below).

- `/antigravity` — main menu: **Модель · Статус** buttons, plus the two action
  commands printed in the body as tap-to-copy monospace (`/antigravity_ask` /
  `/antigravity_image`). They live in the body text, not as buttons — see
  [Command & rendering model](#command--rendering-model) for why.
- **Модель** → pick from the live model list (current marked `•`); the choice is
  the default `--model` for later `ask`/`image` calls.
- **Статус** → the current default model.
- `/antigravity_ask <вопрос>` — one-shot prompt, returns the answer. Sent with no
  argument, it replies with a monospace, tap-to-copy hint. The subcommand form
  `/antigravity ask <вопрос>` also works.
- `/antigravity_image <описание>` — generates an image (`generate_image` / Nano
  Banana 2) and returns it as a photo. Subcommand form `/antigravity image <описание>`
  also works.
- `/antigravity ping` — quick auth probe. `/antigravity reset` — clear default model.

Navigation is edit-in-place: tapping a button rewrites the same panel message
instead of posting a new one (`action.type:"callback"` + a `registerInteractiveHandler`
that calls `editMessage`). Typed subcommands still work as a fallback.

## Command & rendering model

This is the non-obvious part — read it before changing how commands or the panel
text render. It is the result of testing the real Telegram behavior on prod, not
guesswork.

**Two command shapes, on purpose.**

- `/antigravity` (+ subcommands `ask`, `image`, `model`, `status`, `ping`, `reset`,
  `continue`) — the panel and its typed fallbacks.
- `/antigravity_ask` and `/antigravity_image` — single-token commands. A whole-word
  command is one clickable token, and it is registered in the Telegram command menu
  (`commandAliases` with `kind: "runtime-slash"` in `openclaw.plugin.json`). The
  point of the single token: in Telegram's `/` autocomplete list, tapping a command
  **inserts it into the input** (you then type the argument and send). With the
  two-word `/antigravity ask …`, only `/antigravity` is a token and the ` ask …`
  tail is plain text.
  - Caveat (Telegram platform behavior, not fixable here): tapping a `/command`
    **link inside a message** SENDS it immediately (bare). So a bare tap lands on
    the no-argument hint. Only the `/` autocomplete path fills-then-lets-you-type.
  - Both single-token handlers and the `ask`/`image` subcommands share `doAsk` /
    `doImage`; the argument must be sent **together with the command in one
    message** — see "capture-next-message" below for why we can't prompt-then-read.

**Two render paths, with different formatting power — this drives every panel
decision.**

| Path | How | Markdown? | Tap-to-copy monospace? |
|------|-----|-----------|------------------------|
| Command-reply | handler returns `{ text, presentation }` | **Yes** (rich/HTML pipeline) | **Yes** — `` `…` `` → Telegram `code` entity |
| Interactive edit-in-place | `ctx.respond.editMessage` / `reply` inside `registerInteractiveHandler` | **No** — sent with no `parse_mode` (plain) | No |

Verified in the OpenClaw runtime: the interactive Telegram `respond` exposes only
`reply` / `editMessage` / `editButtons` / `clearButtons` / `deleteMessage`, and all
send plain text. So **a button tap can never render a monospace/tap-to-copy
command.** Only a real command reply can.

Consequences baked into the plugin:

- The two action commands are printed **in the menu body as `code` spans**, not as
  inline buttons — so they render monospace/tap-to-copy on the `/antigravity`
  command reply. (The old "Спросить"/"Картинка" buttons were removed: they were a
  dead-end that could only ever show a non-copyable, plain hint.)
- `menuMain(defaultModel, { withCommands })`: the command lines are shown **only**
  on the command-reply path (`withCommands: true`). Edit-in-place back-navigation
  passes `withCommands: false` and omits them, so you never see a plain,
  non-monospace copy of the commands — they appear only where they are actually
  tap-to-copy.
- `plainText()` strips backticks on the edit-in-place path as a defensive measure
  (literal backticks would otherwise show through there).
- The no-argument hints have two variants: `*_HINT_MD` (backticked, for the
  command-reply path) and plain `*_HINT` (edit-in-place fallback).
- A `presentation`-only reply (no top-level `text`) is treated as "no response" —
  hint replies must set `text`.

**Investigated and deliberately NOT built: "capture-next-message"** (tap command →
"Введите запрос:" → your next bare message becomes the argument). It is not cleanly
possible for this plugin:

- Reliable interception uses the `inbound_claim` hook, which only fires for a plugin
  that **owns a conversation binding** — channel/bundled-plugin machinery, no simple
  create-on-demand API for a command plugin.
- Persistent per-chat pending state via `openKeyedStore` is gated to trusted plugins
  (`origin === "bundled" || trustedOfficialInstall === true`); this plugin is
  `Origin: global`, so it throws. (It already uses a plain JSON file for the default
  model for the same reason.)
- The one inbound hook we can register (`message_received`) is observational
  (`=> void`) — it can't stop a message reaching the LLM agent, so it can't consume
  the input.
- Telegram `force_reply` is not exposed by the plugin reply layer either.

So the argument is always sent in one message with the command; the single-token
commands + `/` autocomplete are the best native affordance available.

Buttons need `channels.telegram.capabilities.inlineButtons` set to `dm` (or
`all`); the installer leaves this to you (it is a channel policy):

```bash
openclaw config set channels.telegram.capabilities.inlineButtons dm
```

## Prerequisites

The host must be an OpenClaw gateway you administer as root:

- **OpenClaw >= 2026.6.11** on the gateway (the plugin SDK with `registerCommand` /
  `registerInteractiveHandler`). Check: `openclaw --version`.
- The gateway runs as a **dedicated service user** with a real home dir, under a
  systemd unit. Defaults assumed by the scripts: user `openclaw`, unit
  `openclaw-gateway`, CLI `/opt/openclaw/bin/openclaw`. Other hosts differ —
  override via env (see *Install on another host*).
- A **Google AI Pro** account for the agy OAuth. Login is **per host** and needs a
  browser once (`scripts/login.sh`); the token is machine-local and never copied
  between servers.
- **Inline buttons enabled** for the Telegram channel — the panel is button-driven,
  and without this the buttons silently do not render:
  ```bash
  openclaw config set channels.telegram.capabilities.inlineButtons dm
  ```
  The installer does **not** flip this — it is your channel policy.
- Host tooling: `bash`, `curl`, `tmux` (login only), `systemd`.

## Install

On the target server, as root — one line:

```bash
curl -fsSL https://raw.githubus

... (truncated)
channels

Comments

Sign in to leave a comment

Loading comments...