English | 日本語
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
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 0make 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 doctorThe caller must always specify a slot. There is no default slot; use --slot / -s or --all-slots.
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).
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.
- 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 (VID303A/ PID8360)
No cgo compiler or external HID library is required. Windows uses the built-in HID and SetupAPI services; Linux uses hidraw directly.
| 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-statusCross-compilation confirms that the target builds, but hardware access must be tested on the target OS with doctor and set.
# udev
sudo cp udev/60-codex-micro.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm triggerThe rule uses MODE=0660, GROUP=plugdev, and TAG+=uaccess. It does not use 0666.
Buses: USB HID 0003 / Bluetooth HID 0005.
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 --jsonIn 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 stopDispatcher 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.
{
"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.
{
"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
}]
}]
}
}{
"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 = 3OpenCode 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;
}
},
});{
"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
}]
}
}# 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)
| 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 |
| 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.
| 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 |
- 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
--deviceselects another
make test
# Or: go test ./...- Handoff document (Japanese): HOOK_STATUS_CLI_HANDOFF_JP.md
- Bridge: neighboring repository
codex-micro-linux-bridge - License: MIT