aicliobserve is a Go library that lets services observe AI coding CLI sessions through one normalized event/state model.
Supported CLIs in v1:
- Claude Code CLI — official
Notificationhook integration - Codex CLI — official
notifyintegration - Gemini CLI — official
Notification+AfterAgenthook integration - OpenCode CLI — plugin event stream integration
When official hooks are unavailable, the library falls back to a multi-layer PTY detection pipeline (spinner bytes, BEL, OSC titles, prompt regex, EOF sentinel).
go get github.com/glonlas/aicliobserveimport (
"github.com/glonlas/aicliobserve"
"github.com/glonlas/aicliobserve/adapter/claude"
)
mgr := claude.New("aicliobserve-receiver")
sv := aicliobserve.NewSupervisor(
aicliobserve.WithClaudeManager(mgr),
)
events, _ := sv.Subscribe(ctx)
resp, _ := sv.Launch(ctx, aicliobserve.LaunchRequest{
Adapter: aicliobserve.AdapterClaude,
WorkDir: ".",
PreferHybrid: true,
})
for evt := range events {
fmt.Printf("[%s] %s confidence=%s\n", evt.State, evt.Adapter, evt.Confidence)
if evt.State == aicliobserve.StateExited { break }
}| State | Meaning |
|---|---|
starting |
Session process launched |
running |
Agent is actively processing |
waiting_for_user |
Agent needs user permission or input |
turn_complete_waiting |
Agent finished a turn, awaiting next prompt |
exited |
Session process has terminated |
error |
Session ended with an error |
| Adapter | Config Inspect | Config Mutate | Official Events | Hybrid | PTY Fallback |
|---|---|---|---|---|---|
| Claude | Yes | Yes | Yes | Yes | Yes |
| Codex | Yes | Yes | Yes | Yes | Yes |
| Gemini | Yes | Yes | Yes | Yes | Yes |
| OpenCode | Yes | No (v1) | Yes | Yes | Yes |
mgr := claude.New("aicliobserve-receiver")
// Inspect config readiness
status, _ := mgr.Inspect(ctx, workDir)
// Install Notification hook (idempotent, supports dry-run + backup)
mgr.InstallNotificationHook(ctx, workDir, aicliobserve.ClaudeHookInstallOptions{
Scope: aicliobserve.ScopeProject,
Command: "aicliobserve-receiver",
AllowCreateFile: true,
Backup: true,
})
// Remove hook
mgr.RemoveNotificationHook(ctx, workDir, "aicliobserve-receiver", false)
// Normalize an incoming hook payload
evt := claude.NormalizeHookPayload(sessionID, mode, rawJSON)Event mapping:
permission_prompt→waiting_for_userelicitation_dialog→waiting_for_useridle_prompt→turn_complete_waiting
mgr := codex.NewManager()
// Inspect config readiness
status, _ := mgr.Inspect(ctx, workDir)
// Install notify entry (idempotent, supports dry-run + backup)
mgr.InstallNotify(ctx, workDir, aicliobserve.CodexNotifyInstallOptions{
Scope: aicliobserve.ScopeUser,
Command: []string{"aicliobserve-receiver"},
AllowCreateFile: true,
Backup: true,
})
// Remove notify
mgr.RemoveNotify(ctx, workDir, false)
// Normalize an incoming notify payload
evt, _ := codex.NormalizeNotifyPayload(rawJSON, sessionID, mode)Event mapping:
agent-turn-complete→turn_complete_waitingerror→error
mgr := gemini.New("aicliobserve-receiver")
// Inspect config (checks both Notification and AfterAgent hooks)
status, _ := mgr.Inspect(ctx, workDir)
// Install both hooks required for full observability
mgr.InstallNotificationHook(ctx, workDir, opts)
mgr.InstallAfterAgentHook(ctx, workDir, opts)
// Remove all hook entries matching the receiver command
mgr.RemoveHookCommand(ctx, workDir, "aicliobserve-receiver", false)
// Normalize incoming payloads
evt := gemini.NormalizeHookPayload(sessionID, mode, rawJSON)Event mapping:
Notificationwithreason=ToolPermission→waiting_for_userAfterAgent→turn_complete_waiting
OpenCode does not expose a config mutation API in v1. Register the plugin manually:
// ~/.opencode/plugins.json
{
"plugins": [{ "command": "aicliobserve-receiver" }]
}mgr := opencode.New("aicliobserve-receiver")
status, _ := mgr.Inspect(ctx, workDir)
// Normalize incoming plugin events
evt := opencode.NormalizePluginEvent(sessionID, mode, rawJSON)Event mapping:
permission.asked→waiting_for_usersession.idle→turn_complete_waitingsession.error→error
When hooks are not installed, the library infers state from the raw PTY byte stream through a layered pipeline:
| Layer | Signal | Confidence | Works with |
|---|---|---|---|
| Spinner bytes | Claude ✳ (0xE2 0x9C 0xB3), OpenCode Braille |
High | Claude, OpenCode |
| BEL character | Bare 0x07 (not OSC terminator) |
High | Claude |
| OSC title | ESC]0;title\x07 with "OC | " prefix strip |
Low (metadata) | OpenCode |
| Prompt regex | 300 ms debounced: (y/n), proceed?, allow/deny, esc to cancel |
Medium | All |
| Keyword | permission, agent-turn-complete, done, etc. |
Low–High | All |
| EOF sentinel | io.EOF from PTY reader |
High | All |
Every PTY event carries Meta["detector"] so consumers can decide how to
act on different confidence levels.
See docs/pty-detection.md for full details.
| Mode | Description |
|---|---|
config |
Official hook/notify integration only |
hybrid |
Official signals + PTY inference |
pty_fallback |
PTY heuristic inference only |
Set PreferHybrid: true for the best balance — hooks are used when present,
PTY fills gaps when they are not.
| Scope | File |
|---|---|
user |
~/.claude/settings.json / ~/.gemini/settings.json / ~/.codex/config.toml |
project |
.claude/settings.json / .gemini/settings.json / .codex/config.toml |
project_local |
.claude/settings.local.json |
- No config mutation during
Inspect()orLaunch(). - Mutations only via explicit mutator calls (
Install*,Remove*). DryRunmode available on every mutation — no files are written.Backupoption creates a timestamped copy before overwriting.- Idempotent patch behavior — duplicate hooks/notify entries are never added.
- Post-write validation re-reads and parses the file; reports warnings in
PatchResult.
go run ./docs/guides/examples/claude
go run ./docs/guides/examples/codex
go run ./docs/guides/examples/gemini
go run ./docs/guides/examples/opencodeSee docs/guides/examples/ for full details.
Full documentation is in docs/:
| Page | Contents |
|---|---|
| Getting Started | Install, first session |
| Architecture | Normalized model, mode selection |
| PTY Detection | Fallback detection pipeline |
| Claude Adapter | Hook install/remove |
| Codex Adapter | Notify install/remove, trust gating |
| Gemini Adapter | Dual-hook setup |
| OpenCode Adapter | Plugin readiness |
| Config Safety | Dry-run, backup, idempotency |
| Multi-Session | Concurrent sessions |
| Migrating from Polling | Replace tmux + regex |
| API Reference | Full type listing |