Skip to content

Repository files navigation

codex-micro-status — A one-shot CLI that reports AI agent slot status directly to Codex Micro from Hooks. No Desktop Bridge required.

English | 日本語

codex-micro-status

Notify the agent slot lights on Codex Micro with a one-shot command-line interface (CLI). It sends AI agent status directly from Hooks over HID, without ChatGPT Desktop or a resident Bridge process.

Messages are sent in the same order as Desktop: v.oai.rgbcfg → v.oai.thstatus (6 slots).

Current status: Linux hidraw and native Windows HID/SetupAPI backends · No cgo or libhidapi required

Quick start

Windows (PowerShell)

go build -buildvcs=false -o codex-micro-status.exe ./cmd/codex-micro-status
.\codex-micro-status.exe doctor
.\codex-micro-status.exe set working --slot 0

Linux

make build
# Or: go build -buildvcs=false -o codex-micro-status ./cmd/codex-micro-status

./codex-micro-status set working --slot 0   # AG00 turns blue (working)
./codex-micro-status set error --slot 1     # AG01 turns pink (error)
./codex-micro-status set unread --slot 2    # AG02 turns green (unread); other slots stay unchanged
./codex-micro-status set off --all-slots
./codex-micro-status doctor

The caller must always specify a slot. There is no default slot; use --slot / -s or --all-slots.

Independent slot lighting

Slots 0..5 correspond to AG00–AG05 in the OAI keymap. The CLI sends all six slot colors through v.oai.thstatus, allowing multiple agents to light up simultaneously in different colors.

Slot state is stored in the OS user cache directory. The usual locations are %LocalAppData%\codex-micro-status\slots.json on Windows and ~/.cache/codex-micro-status/slots.json on Linux. Setting agents one at a time from Hooks therefore preserves the other slots. Use --no-persist to disable this behavior.

Optional appearance Flag
Whole-key wash --keys (default; v.oai.rgbcfg keys)
Ambient ring --ambient (snake animation while working)
Both --both
No wash (thstatus only) --agent-only

Messages are sent in the same order as Desktop: rgbcfg → thstatus (6 slots).

Status colors: idle white, working blue, unread green, awaiting amber, error pink, and off

Agent-name wrapper

scripts/agent-status.sh maps agent names to fixed slots and calls set.

The canonical mapping is SLOT_MAP in the script. Run ./scripts/agent-status.sh --list to inspect it.

agent slot Notes
codex 0
claude 1
grok 2
opencode 3
agy 4
zcode 5
pi 5 Shared with zcode

Name aliases are resolved after conversion to lowercase:

Input Resolves to
codex-cli, openai codex
antigravity, gemini agy
./scripts/agent-status.sh codex working
./scripts/agent-status.sh agy awaiting-approval
CMS_BEST_EFFORT=1 ./scripts/agent-status.sh claude error   # Recommended for Hooks
./scripts/agent-status.sh grok off --json                  # Extra flags are forwarded unchanged
./scripts/agent-status.sh --list
Environment variable Meaning
CMS_BEST_EFFORT=1 (or true) Always add --best-effort
CMS_EXTRA_FLAGS Extra flags to add every time, such as --agent-only

Prerequisite: the device must be on an OAI keymap layer, such as Layer 1 with AG00–AG05.

Requirements

  • Windows 10/11 or Linux, such as Ubuntu
  • Go 1.22+ for building
  • Codex Micro connected over USB or Bluetooth
  • Linux only: access to /dev/hidraw* through a udev rule or root privileges (VID 303A / PID 8360)

No cgo compiler or external HID library is required. Windows uses the built-in HID and SetupAPI services; Linux uses hidraw directly.

Build targets and verification

Target Backend Build Runtime verification
Windows 10/11 Native HID/SetupAPI Native build and tests pass Bluetooth enumeration, open, request, response, and lighting verified on real hardware
Linux hidraw/sysfs Linux amd64 build and hidraw tests pass USB and Bluetooth use the existing hidraw backend; install the udev rule below

Build for the current OS with go build. Because both backends are pure Go, cross-compilation does not require cgo:

# Run on Windows: produce a Linux amd64 binary
$env:GOOS = "linux"
$env:GOARCH = "amd64"
$env:CGO_ENABLED = "0"
go build -buildvcs=false -o codex-micro-status-linux-amd64 ./cmd/codex-micro-status
# Run on Linux: produce a Windows amd64 executable
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \
  go build -buildvcs=false -o codex-micro-status.exe ./cmd/codex-micro-status

Cross-compilation confirms that the target builds, but hardware access must be tested on the target OS with doctor and set.

Linux device permissions

# udev
sudo cp udev/60-codex-micro.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger

The rule uses MODE=0660, GROUP=plugdev, and TAG+=uaccess. It does not use 0666. Buses: USB HID 0003 / Bluetooth HID 0005.

Usage

Commands

codex-micro-status set <state> --slot <0..5> [flags]
codex-micro-status set <state> --all-slots [flags]
codex-micro-status doctor [--json]
codex-micro-status help
# Mark slot 0 as working (blue)
./codex-micro-status set working --slot 0

# For Hooks: do not fail when the device is disconnected
./codex-micro-status set awaiting-approval --slot 0 --best-effort

# Turn off all slots and clear the cache
./codex-micro-status set off --all-slots

# Diagnose: enumerate platform HID devices and attempt to open them
# `doctor` also calls `device.status` after opening each device and displays every field returned by the firmware, including battery level, charging state, firmware, profile, and layer. With `--json`, the raw response is stored in each device`s `status` field.
./codex-micro-status doctor
./codex-micro-status doctor --json

Hook configuration examples

In normal use, a shared dispatcher such as ~/.agents/hooks/codex-micro-status.sh accepts <agent> <event> and calls this repository's scripts/agent-status.sh, which then invokes codex-micro-status. Keep the dispatcher outside this repository. Set AGENT_STATUS to the path of agent-status.sh, or place it at one of the dispatcher's default lookup paths.

# Direct call from this repository
CMS_BEST_EFFORT=1 ./scripts/agent-status.sh codex working

# Shared Hook: agent name + lifecycle event
bash ~/.agents/hooks/codex-micro-status.sh codex prompt
bash ~/.agents/hooks/codex-micro-status.sh claude stop

Dispatcher defaults:

Setting Value
CMS_BEST_EFFORT 1 (do not fail Hooks when disconnected)
CMS_EXTRA_FLAGS --agent-only (per-slot lighting via thstatus; can be changed to --keys)

Event-to-state mapping (excerpt):

event state
session / prompt / working / thinking working
approval / awaiting-approval / needs-input awaiting-approval
awaiting-response awaiting-response
stop / complete off (turn off at the end of a turn)
off off
error error
idle idle

The agent-to-slot mapping is the same as SLOT_MAP in scripts/agent-status.sh.

Codex CLI — ~/.codex/hooks.json

{
  "hooks": {
    "SessionStart": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh codex session",
        "timeout": 3
      }]
    }],
    "UserPromptSubmit": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh codex prompt",
        "timeout": 3
      }]
    }],
    "PreToolUse": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh codex thinking",
        "timeout": 3
      }]
    }],
    "PostToolUse": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh codex working",
        "timeout": 3
      }]
    }],
    "PermissionRequest": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh codex awaiting-approval",
        "timeout": 3
      }]
    }],
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh codex stop",
        "timeout": 3
      }]
    }],
    "SessionEnd": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh codex off",
        "timeout": 3
      }]
    }]
  }
}

After changing the configuration, Codex may require you to approve the Hooks again with /hooks.

Claude Code — hooks in ~/.claude/settings.json

{
  "hooks": {
    "SessionStart": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh claude session",
        "timeout": 3
      }]
    }],
    "UserPromptSubmit": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh claude prompt",
        "timeout": 3
      }]
    }],
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [{
          "type": "command",
          "command": "bash $HOME/.agents/hooks/codex-micro-status.sh claude approval",
          "timeout": 3
        }]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [{
          "type": "command",
          "command": "bash $HOME/.agents/hooks/codex-micro-status.sh claude awaiting-response",
          "timeout": 3
        }]
      }
    ],
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh claude stop",
        "timeout": 3
      }]
    }],
    "StopFailure": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh claude error",
        "timeout": 3
      }]
    }],
    "SessionEnd": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh claude off",
        "timeout": 3
      }]
    }]
  }
}

Grok — ~/.grok/hooks/codex-micro-status.json

{
  "hooks": {
    "SessionStart": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh grok session",
        "timeout": 3
      }]
    }],
    "UserPromptSubmit": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh grok prompt",
        "timeout": 3
      }]
    }],
    "PostToolUse": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh grok working",
        "timeout": 3
      }]
    }],
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh grok stop",
        "timeout": 3
      }]
    }],
    "SessionEnd": [{
      "hooks": [{
        "type": "command",
        "command": "bash $HOME/.agents/hooks/codex-micro-status.sh grok off",
        "timeout": 3
      }]
    }]
  }
}

Grok does not provide PermissionRequest, so add approval waiting through a notification Hook in ~/.grok/config.toml:

[ui.notifications]
events = ["turn_complete", "approval_required"]

[[ui.notifications.hooks]]
command = "bash $HOME/.agents/hooks/codex-micro-status.sh grok awaiting-approval"
events = ["approval_required"]
only_unfocused = false
timeout_secs = 3

OpenCode — ~/.config/opencode/plugins/codex-micro-status.js

OpenCode fires status updates through its plugin event API. Place this file in plugins/:

// maps → agent "opencode" (slot 3)
import { spawn } from "node:child_process";

const HOOK = `${process.env.HOME}/.agents/hooks/codex-micro-status.sh`;
const AGENT = "opencode";

function fire(event) {
  return new Promise((resolve) => {
    try {
      const child = spawn("bash", [HOOK, AGENT, event], {
        stdio: "ignore",
        detached: true,
      });
      child.unref();
      resolve();
    } catch {
      resolve();
    }
  });
}

export const CodexMicroStatusPlugin = async () => ({
  dispose: async () => { await fire("off"); },
  "chat.message": async () => { await fire("prompt"); },
  event: async ({ event }) => {
    const type = event?.type;
    const properties = event?.properties ?? {};
    switch (type) {
      case "permission.asked":
      case "question.asked":
        await fire("awaiting-approval");
        break;
      case "permission.replied":
      case "question.replied":
        await fire("prompt");
        break;
      case "session.status": {
        const status =
          typeof properties.status === "string"
            ? properties.status
            : properties.status?.type;
        if (status === "busy" || status === "retry") await fire("prompt");
        else if (status === "idle") await fire("stop");
        break;
      }
      case "session.idle":
        await fire("stop");
        break;
      case "session.deleted":
        await fire("off");
        break;
    }
  },
});

Gemini CLI / Antigravity (agy) — ~/.gemini/config/hooks.json

{
  "codex-micro-status": {
    "enabled": true,
    "PreInvocation": [{
      "type": "command",
      "command": "bash $HOME/.agents/hooks/codex-micro-status.sh agy prompt",
      "timeout": 3
    }],
    "Stop": [{
      "type": "command",
      "command": "bash $HOME/.agents/hooks/codex-micro-status.sh agy stop",
      "timeout": 3
    }]
  }
}

Minimal direct calls for any agent

# Start
CMS_BEST_EFFORT=1 /path/to/codex-micro-status/scripts/agent-status.sh <agent> working
# Await approval
CMS_BEST_EFFORT=1 /path/to/codex-micro-status/scripts/agent-status.sh <agent> awaiting-approval
# Finish
CMS_BEST_EFFORT=1 /path/to/codex-micro-status/scripts/agent-status.sh <agent> off

<agent>: codex / claude / grok / opencode / agy / zcode / pi (scripts/agent-status.sh --list)

States

Name Color Notes
idle White 0xFFFFFF
working Blue 0x304FFE alias: thinking
unread Green 0x00FF4C alias: complete
awaiting-approval Amber 0xFF6D00 alias: needs-input
awaiting-response Amber 0xFF6D00
error Pink 0xFF0033
off Off

set flags

Flag Description
--slot <0..5> / -s Target slot; mutually exclusive with --all-slots, and one is required
--all-slots Apply the same state to all slots 0..5
--keys Wash all keys through v.oai.rgbcfg using the color of the slot updated by this call (default)
--ambient Ambient ring only
--both Keys + ambient ring
--agent-only v.oai.thstatus only, without an rgbcfg wash
--no-persist Ignore saved states for other slots and treat non-target slots as off
--best-effort Return exit code 0 with a warning on not found, busy, timeout, or lock contention
--timeout <dur> HID response timeout; default: 2s
--device <path> Select a specific hidraw device, for example when multiple devices exist
--json Machine-readable output

doctor additionally accepts only --json.

Exit codes

Code Meaning
0 Success, or a soft warning with --best-effort
1 Usage or invalid input; never softened by --best-effort
2 Device not found
3 Device busy or permission denied
4 Response timeout
5 Other input/output error
6 Inter-process lock contention

Architecture

Pipeline: Hook to codex-micro-status to /dev/hidraw* to Codex Micro

  • Pure Go Linux hidraw implementation; no cgo or libhidapi
  • Protocol-compatible with the existing Linux Bridge, codex-micro-linux-bridge
  • Concurrent use with a resident Bridge is outside the initial scope and fails when the device is already in use
  • The operating system manages Bluetooth connections; the CLI only enumerates connected devices
  • When multiple interfaces are available, the CLI opens the first one unless --device selects another

Testing

make test
# Or: go test ./...

Related resources

About

Single-shot CLI: notify Codex Micro agent-slot lighting from AI agent Hooks over HID

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages