Skip to content

glonlas/aicliobserve

Repository files navigation

aicliobserve

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 Notification hook integration
  • Codex CLI — official notify integration
  • Gemini CLI — official Notification + AfterAgent hook 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).

Installation

go get github.com/glonlas/aicliobserve

Quick start

import (
    "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 }
}

Normalized state model

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 capability matrix

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

Adapters

Claude

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_promptwaiting_for_user
  • elicitation_dialogwaiting_for_user
  • idle_promptturn_complete_waiting

Codex

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-completeturn_complete_waiting
  • errorerror

Gemini

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:

  • Notification with reason=ToolPermissionwaiting_for_user
  • AfterAgentturn_complete_waiting

OpenCode

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.askedwaiting_for_user
  • session.idleturn_complete_waiting
  • session.errorerror

PTY fallback detection

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.

Runtime modes

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.

Config scopes

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

Safety guarantees

  • No config mutation during Inspect() or Launch().
  • Mutations only via explicit mutator calls (Install*, Remove*).
  • DryRun mode available on every mutation — no files are written.
  • Backup option 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.

Examples

go run ./docs/guides/examples/claude
go run ./docs/guides/examples/codex
go run ./docs/guides/examples/gemini
go run ./docs/guides/examples/opencode

See docs/guides/examples/ for full details.

Documentation

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

About

AI Cli Observe: a Golang lib to receive notification from Claude code, Codex CLI when they need an input from the user

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages