# adom-shotlog

**See the shots your AI never showed you.**

Your AI screenshots your app constantly while it builds and tests, but it is text-centric, so those shots get buried in tool output and you never actually see them. adom-shotlog is the fix: a tiny Rust CLI plus server where any AI thread drops each screenshot into a **channel**, and you watch them stream into a live, newest-first **gallery**, on whatever surface you are using: a Hydrogen webview, a pup window, your native browser, or your phone.

## Install

```
adompkg install adom/adom-shotlog
```

The CLI installs as `adom-shotlog` (with a back-compat `shotlog` alias).

## One central broker, many AI threads

There is **one shotlog server per container**, and the same binary is both the server and the CLI. `adom-shotlog serve` spawns the broker on **port 8820**; `inject`, `open`, `viewers`, and `channels` are CLI verbs that talk to it.

Every AI thread in the container simply runs `adom-shotlog inject`, which posts to that one local server. So the server is a **central broker**: it owns every channel, tracks every viewer (which surface, and whether it is actually on-screen), and manages all the viewer surfaces. Many threads can be sending you shots at once, each into its own channel, and the broker keeps a live view/tab for each. Run `adom-shotlog channels` to see the whole picture: every channel, its shot count, when it last updated, and who is watching right now.

Disk stays bounded automatically: each channel keeps its newest **200 shots** on disk (`SHOTLOG_MAX_SHOTS`), older files are deleted as new ones arrive, and channels idle for **30 days** are pruned entirely (`SHOTLOG_RETENTION_DAYS`, `0` disables).

## The read-hook: every screenshot any AI reads, captured automatically

shotlog ships a Claude Code `PostToolUse(Read)` hook (`hooks/shotlog-on-read.sh`). Once installed, whenever ANY AI thread reads an image file to analyze it, the hook automatically injects that exact image into the shared **`read-captures`** channel. You see every screenshot every AI looked at, deterministically, with zero AI cooperation required. It is the purest form of the shotlog mission: the AI cannot forget to show you what it saw.

`adom-shotlog install` deploys the hook and registers it in `~/.claude/settings.json` (surgically, never touching your other hooks), and **every `serve` startup re-verifies both**, healing them if missing, so the pipeline cannot silently rot. The hook also self-heals the broker if it is not running.

Related guarantees: the broker is **single-instance per port** (a second `serve` detects the healthy broker and defers gracefully, warning if versions differ), and `read-captures` stays disk-bounded by the retention policy.

## It adapts to your surface

shotlog notices which surface is watching a channel and behaves accordingly. It never steals your OS foreground.

### Webview (the default in Hydrogen): a slim 20% right-side panel

The nicest setup, and the default when you run inside Hydrogen: a **~20%-wide webview panel pinned to the right** of your workspace. Open it that way:

```
adom-shotlog open -c my-project --skinny        # a ~20% right-side column
```

- It **auto-updates live** over a WebSocket: the moment any thread injects a shot, the card appears at the top of that channel, no refresh, no polling.
- **One panel, shotlog's own tabs**: the viewer is a single app that manages a tab per channel itself (read-captures pinned first as the global auto-capture feed). New shots flash their tab orange, show an unseen badge, and (by default) auto-switch to the updated channel - all inside the app, no workspace tab juggling.
- If a shot lands in a channel with **nobody watching**, the broker auto-opens the slim 20% webview for it, so a shot is never logged invisibly. (Opt out with `SHOTLOG_NO_FORCE_OPEN=1`.)
- The layout is **fully fluid** at that skinny width: the header wraps to rows, the child-window thumbnails reflow, and the outer page never shows a horizontal scrollbar.

### Phone

Ask your AI to `shotlog the whole thing`, and it hands you one URL. Open that URL **on your phone** and watch every step stream in as it happens, then scroll back through the whole run whenever you want.

![adom-shotlog on a phone](screenshots/mobile.png)

The layout adapts to the narrow screen, and while the tab is backgrounded the page title shows a `● N` unseen-shots badge so you know something arrived. Combined with `/remote-control`, you can kick off real work from anywhere and keep vibe-coding from the passenger seat.

### Pup and plain browser

Pup is the **full-browser canvas**: zooming a card uses nearly the whole screen, so fine text is readable (the skinny webview can never zoom past its panel width). Or just click the **`Pup` button in the viewer header**: every webview shows it, and it switches that channel to a full pup window (the pup view shows a `Webview` button to switch back). The button checks whether pup is available first; if Adom Desktop isn't installed it tells you how to get it (pup is a default bridge that auto-installs with Adom Desktop). Your AI can do the same switch with one command: `shotlog open -c <ch> --pup --swap` (opens a tab in the shared shotlog pup window and closes the webview tab). shotlog leaves your foreground alone; the gentle cue is an automatic **orange taskbar flash** on updates. In any plain browser tab, the same live gallery and unseen-shots title badge apply.

## Is a human actually seeing it?

An AI injecting shots into a channel nobody is watching is worse than useless, because it *thinks* it showed you something. So shotlog answers this **in the inject response** and lets a thread gate on it:

```
adom-shotlog viewers -c my-project   # exit 0 = a human has it on-screen, 4 = open but backgrounded, 3 = nobody
```

The signal is authoritative: each viewer holds a WebSocket open and reports its **surface** (webview, pup, mobile, browser) and whether it is actually **on-screen** (Page Visibility) versus a background tab.

## It refuses to log a shot blind

adom-shotlog **always** records the original capture resolution, so the gallery never shows a mystery size. It shows `1400x788, orig 1920x1080`. Resizing before inject is expected (oversized images crash AI vision analysis), so most screenshot tools hand back a shrunk copy. shotlog just requires the original size to travel with it, via `--orig-w/--orig-h`, `--orig-file`, `--native`, or `--meta`.

## Killer feature: a whole parent + child-window array in one card

A window capture is often not one image. A window with a dropdown, an autofill list, a confirm bubble, or a native dialog open is really a **parent window with child windows**: separate owned windows that a plain screenshot of the main window misses entirely.

Adom Desktop captures them for you. **`desktop_screenshot_window {hwnd}` returns the parent image PLUS every child window** in a `screenshots[]` array (with `ownedPopupCount`), each carrying its own hwnd, title, kind, rect, and coordMap. Pass that whole response to shotlog with `--meta`:

```
adom-shotlog inject -c checkout -d "checkout form, autofill dropdown + confirm dialog open" primary.png --meta ad-response.json
```

shotlog renders the entire array on one card, no zoom needed:

![A parent window plus its child windows, shown inline](screenshots/child-windows.png)

- a **+N child windows** badge on the card,
- a **child windows** row directly under the parent shot, every child window inline as its own thumbnail (labeled `kind, title, WxH`),
- an **ⓘ meta** chip that reveals the coordMap panel inline (shotId, screenRect, dpiScale, imageToScreen, screen resolution), copyable as JSON.

So one `desktop_screenshot_window` call, one `--meta` inject, and you see the whole window story: the form, the dropdown it opened, and the dialog it raised, all in a single card. Child-window PNGs are stored beside the parent and are excluded from the gallery as standalone shots. Plain single-image injects (no `--meta`) are unchanged; child windows are purely additive.

## CLI reference

| Command | Description |
|---|---|
| `adom-shotlog inject` | Inject a screenshot into a channel (records the original resolution; `--meta` adds child windows; reports whether a human is watching) |
| `adom-shotlog open` | Open the gallery as a Hydrogen webview tab (`--skinny` for the 20% right panel) |
| `adom-shotlog viewers` | Is a human watching a channel? Exit 0 / 4 / 3, plus connected surfaces |
| `adom-shotlog channels` | Overview of every channel: shots, last activity, and who is watching |
| `adom-shotlog serve` | Start the HTTP server / broker (port 8820) |
| `adom-shotlog health` | Check if the server is running |
| `adom-shotlog install` | Install the skill, bash completions, and the `shotlog` alias |

## Skill pack

Installing adom-shotlog also deploys its bundled skills (see the Skills tab):

- **adom-shotlog-surfacing** (user): surfacing a shot gently per surface, never stealing foreground.
- **adom-shotlog-child-windows** (user): capturing a parent window plus its child windows via `--meta`.
- **adom-shotlog-dev** (dev): build/deploy, the central-broker architecture, and the layout rules (ralph-test the skinny view, no outer-html scrollbars, pretty scrollbars).
- **adom-shotlog-publishing** (publish): publishing this package to wiki.adom.inc.

## Dependencies

| Dependency | Required? | Why |
|---|---|---|
| **adom-cli** | For the webview | `adom-shotlog open` adds the gallery as a Hydrogen webview tab and the broker manages it. |
| **adom-desktop** | Optional | AI agents use it to capture screenshots (and child-window arrays) that get injected. Humans can Ctrl+V paste instead. |

No `adompkg` package dependencies. adom-shotlog is a self-contained Rust binary.
