Skip to main content
Glama
README.md
<div align="center">
  <a href="https://witness.zalize.com"><img src="docs/og.png" alt="Witness — You say it. Witness proves it." width="760"></a>
  <br><br>
  <a href="https://github.com/wookat/witness/stargazers"><img src="https://img.shields.io/github/stars/wookat/witness?style=flat-square&color=ff4f00" alt="GitHub stars"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-17181c?style=flat-square" alt="MIT license"></a>
  <img src="https://img.shields.io/badge/node-%E2%89%A518-17181c?style=flat-square" alt="Node ≥ 18">
  <img src="https://img.shields.io/badge/MCP-12%20tools-17181c?style=flat-square" alt="12 MCP tools">
  <br>
  <b>English</b> · <a href="README.zh-CN.md">中文</a> · <a href="https://witness.zalize.com">witness.zalize.com</a> · <a href="VISION.md">VISION.md</a>
</div>

<br>

**Voice-anchored causal evidence recorder for AI coding agents.**

You experience the product and talk — *“this button does nothing.”*
Witness welds every word to the causal evidence of that exact second: clicks, requests,
console errors, DOM mutations, screenshots, full rrweb replay — then hands a structured
evidence package to your AI coding agent.

<p align="center"><img src="docs/demo.gif" alt="Recording a session with wit" width="820"></p>

## Why

AI agents can't see what you see. Describing a visual bug costs ten minutes of
screenshots and prompt crafting — and still loses the *why*. Voice is the cheapest
annotation channel there is; it just never had a recorder.

Three streams run in parallel the moment `wit start` opens a managed browser:

1. **Your voice** — continuous audio + live transcription. One take, many issues.
2. **Operations + causal bursts** — every click captures its ~1.8s aftermath
   (requests, responses, console errors, DOM changes). A click with *no* aftermath
   is itself recorded as evidence.
3. **Page evolution** — rrweb full-DOM replay + event-driven screenshots + periodic
   DOM snapshots.

## Install

```bash
npm i -g witness-recorder        # needs Node 18+ and system Edge/Chrome
wit start http://localhost:5173       # managed browser opens — talk & click
```

`rec` is an alias for `wit` — same binary, shorter to type.

No downloads, no API keys. System Edge/Chrome is preferred; bundled Chromium is the fallback.

## During recording

| Gesture | Effect |
|---|---|
| Browse + talk | Full audio; clicks/scrolls/inputs/navigation auto-logged; live transcript preview under the toolbar |
| `Ctrl+click` | Mark an element — selector, outerHTML, component chain, red-frame screenshot |
| `M` | Inspect mode: hover-highlight, click to mark; **drag to crop a region** |
| `N` | Explicit boundary marker — `⏺ ISSUE #n` divider; a strong grouping hint for the agent |
| `P` | Pause/resume — **all events and audio freeze** (privacy) |
| `Esc` | Exit inspect mode |
| `■` / `Alt+Shift+R` | Stop and finalize the evidence package |

`events.jsonl` streams to disk as you go — kill the process and you lose only the last
few hundred milliseconds. `wit recover` rebuilds everything.

## What the agent reads

Each spoken fragment lands on a timeline as a `SAY` block carrying the causal burst of
that moment — dead clicks, mutations, errors — with linked shots and DOM snapshots.

<p align="center"><img src="docs/evidence.png" alt="session.md — each SAY anchors its causal burst" width="820"></p>

## CLI

```bash
wit start <url> [--lang en-US] [--duration 60] [--no-rr] [--associate]
                [--shot-format jpeg|webp] [--dsf 1|2] [--keyframes]
wit list                          # sessions with url/duration/say count
wit open latest                   # open review.html
wit transcribe <dir>              # re-run cloud ASR
wit correct <dir>                 # re-run LLM text polish
wit associate <dir>               # LLM picks relevant evidence per SAY (ZHIPU_API_KEY)
wit recover <dir>                 # rebuild artifacts after a crash
wit segments <dir>                # regenerate speech-anchor index + bundles
wit keyframes <dir>               # render rrweb → per-segment keyframe jpgs
wit mcp [dir]                     # stdio MCP server over the evidence
wit config asr <engine>           # live ASR: auto|doubao-web|doubao-ime|sr|off
```

## The evidence package — `.witness/sessions/<ts>_<host>/`

Agent-facing artifacts (PROMPT.md / SEGMENTS.md / session.md) are always generated in English — the most reliable corpus for LLMs; your words stay verbatim, never translated.

```
SEGMENTS.md       # speech anchors: verbatim + suspects + shot/dom refs (agents read first)
segments/*.md   # per-segment bundle: verbatim frags, dead clicks, errors, linked shots
keyframes/*.jpg  # rrweb-rendered replay frames per segment (wit keyframes)
KEYFRAMES.md     # keyframe index
segments.json    # machine-readable segment index (what MCP serves)
session.md      # timeline archive: each SAY block carries its causal burst
PROMPT.md       # paste-ready handoff instructions + page vocabulary
review.html     # human view: speech cards + audio seek + filmstrip + embedded rrweb replay
events.jsonl    # complete raw stream, crash-safe
rrweb.jsonl     # full DOM event stream (--no-rr to disable)
dom/*.html      # DOM snapshots on nav/mark/30s
assoc.json      # optional LLM evidence-association suggestions
shots/*.jpg     # click/mark crops + periodic + spoken-segment frames (--shot-format webp for ~35% smaller)
bodies/*        # response bodies ≤64KB (.json for JSON, .txt otherwise)
session.json    # meta: url, duration, asr engine, counts
audio.webm / transcript.json
```

## MCP — evidence as an API

`wit mcp` serves the session over stdio. Point any MCP-capable agent
(Claude Code, Cursor, Devin) at it and it pulls exactly what it needs:

`list_segments` · `segment_detail(n)` · `session_overview` · `list_says` · `session_md` · `events_around(t_ms, radius, kinds)` ·
`get_errors` · `get_shot` · `get_body` · `get_dom_state(t)` · `get_asr` · `set_asr`

## Principles

- **Evidence is never rewritten** — speech lands verbatim; merging/correction/association are display-layer suggestions.
- **Evidence ≠ intent** — behavior without a spoken anchor is never presented as a bug.
- **Local-first** — deterministic recording; live SAY subtitles stream through free cloud ASR
  (`auto` = doubao-web → doubao-ime → browser SR fallback — pick via `wit config asr`, `--asr`, `WIT_ASR`, or the
  `get_asr`/`set_asr` MCP tools); final transcript falls back to bcut (bilibili editor ASR, no key needed)
  when no OPENAI key is set. LLM layers are optional.
- **Privacy engineered in** — password fields masked across four channels, credential fields redacted in POST bodies,
  `WIT_NO_BODIES=1` disables body capture, `P` freezes everything, `.witness/` is auto-gitignored.

## Development

```bash
npm install && npm run build && npm link
npm test        # build + node:test suite (align / speech-run / bursts / prompts / review html)
```

<br>

<div align="center">
  <img src="docs/logo.svg" width="38" alt="Witness">
  <br>
  <sub>Built by <b>zalize</b> (<a href="https://github.com/wookat">@wookat</a>) · <a href="LICENSE">MIT</a></sub>
  <br>
  <sub>Questions welcome — <a href="mailto:wookat@qq.com">wookat@qq.com</a> · <a href="https://github.com/wookat/witness/issues">issues</a></sub>
</div>

TDQS

A3.5/5.0

Scored across 12 tools

Disambiguation4/5

Each tool maps to a distinct evidence artifact (segments, says, errors, shots, DOM, bodies, raw events), and descriptions explicitly route the agent ("READ THIS FIRST" on list_segments, cross-references between list_says and segment_detail). Minor overlap exists between get_errors and events_around(kinds=error), and between session_overview and session_md, but the boundaries are stated clearly enough to avoid misselection.

Naming Consistency4/5

A readable pattern holds: get_* for fetching artifacts (get_shot, get_dom_state, get_body, get_errors), list_* for indexes, session_* for global metadata. Deviations exist (segment_detail, events_around, session_md) which break the verb_noun convention, but the set is still predictable.

Tool Count5/5

12 tools is squarely in the well-scoped range, with each tool covering a distinct layer of the evidence pipeline (overview, segmentation, raw events, artifacts, config). Nothing feels redundant or padded.

Completeness4/5

The surface covers the full investigation lifecycle: session metadata, speech anchors, per-segment evidence bundles, timeline rendering, raw events, errors, and artifact retrieval plus ASR get/set. Minor gaps remain—no session-wide search or filtering across all suspects/dead clicks, and no direct listing of available shots/bodies beyond the file list in session_overview.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues