# render-session

Single-binary tool (Rust + rmcp + axum) for viewing AI session output.
Provides an MCP server for write operations and an HTTP viewer for read-only browsing.

Release notes are tracked in [CHANGELOG.md](CHANGELOG.md).

## Phase status

| Phase | Status | Scope |
|---|---|---|
| 1 (scaffold) | done | Workspace layout, crate skeletons, binary entry points |
| 1 (HTTP serve) | done | `serve --port N --dir D`; axum router, rust-embed index.html, `/api/list` + `/api/md/{id}` |
| 1 (MCP tools) | done | viewer.lease / list / release / report.write / draft.write |
| 2 (config-gen) | done | 3-layer config merger (`extends:` chains, cycle detection), `gen --project D` emitting `list.json` + `render-lanes.md` |
| 3 (block IF) | done | `Item.block_type` announce + `X-Block-Type` HTTP header. Concrete plugins deferred to frontend. |
| 4 (a) source (session) | done | `sources/session` module + `capture` subcommand. Legacy `render-session.py` session / recent modes ported. |
| 4 (b) filter middleware | done | `filters` module with `Filter` trait + 4 built-in (`HasTable` / `HasMermaid` / `KeywordMatch` / `RegexMatch`) + `FilterRegistry` + `FilterChainConfig`. `capture_recent` supports config-driven filter chain via `categories.recent.filter` yaml field. Legacy `has_visual_artifact` default preserved when filter is None. |
| 4 (c) viewer watcher | done | `serve --watch-tick <secs>` and `viewer.lease(tick=<secs>)` enable a background `tokio::spawn` watcher loop that calls `auto_capture_once` every <tick> seconds. Idempotent emit (existing files skipped). Hook-based capture (`UserPromptSubmit`) DEPRECATED in favor of viewer-watcher. |
| 4 (d) config strategy | done | figment-based 3-layer config (user-global → project → lane) + env overrides. `config::load` + `LoadedConfig` + `config show/info/doctor` CLI. `RENDER_SESSION_CONFIG` path override + `RENDER_SESSION_<KEY>` value overrides with `__` path separator. See `docs/runbook/config-strategy.md`. |
| 5+ | pending | rss / jira / github / linear / slack-export source plugins; `SourcePlugin` trait concrete impls. Frontend block plugins (markdown-it / mermaid.js), SPA viewer integration. |

## Build

```sh
cargo build
```

## Run

```sh
# Show available subcommands
cargo run -p render-session -- --help

# Start MCP server (stdio) — called by .mcp.json clients
# render-session-mcp reads stdin / writes stdout following MCP protocol
cargo run -p render-session-mcp
# or via the CLI alias (thin exec wrapper):
cargo run -p render-session -- mcp

# Start HTTP viewer
# Serves GET / (index.html), GET /api/list, GET /api/md/{id}
# Reads markdown from <dir>/render-site/{drafts,reports,session,recent}/
cargo run -p render-session -- serve --port 8000 --dir /path/to/project

# Write a report file (reads body from stdin)
echo "## Summary\n\nContent here." | cargo run -p render-session -- report --dir /path/to/project --title "My Report"

# Generate list.json + render-lanes.md from project config
# Emits: <D>/render-site/list.json and <D>/render-site/rules/render-lanes.md
cargo run -p render-session -- gen --project /path/to/project

# Capture Claude Code session output to render-site/
# Reads from ~/.claude/projects/<slug>/*.jsonl (slug derived from project path if omitted)
# session mode: emits <D>/render-site/session/latest.md
cargo run -p render-session -- capture --project /path/to/project --mode session
# recent mode: emits <D>/render-site/recent/<turn_id>-<ts>.md for last N visual-artifact turns
cargo run -p render-session -- capture --project /path/to/project --mode recent --n 5
# both (default): session + recent
cargo run -p render-session -- capture --project /path/to/project

# Phase 4 (c): start viewer with built-in watcher (auto-captures every 5 seconds)
render-session serve --port 8000 --dir /path/to/project --watch-tick 5
# or via MCP: { "tool": "viewer.lease", "args": { "dir": "/path/to/project", "tick": 5 } }
```

## Config

render-session loads configuration from 3 layers + env overrides (Outline rust §1-9-1 F-2):

- **user-global**: `~/.config/render-session/config.yaml`
  (override path with `RENDER_SESSION_CONFIG=/path/to/config.yaml`)
- **project**: `<project>/.render-session.yaml`
- **lane**: `<project>/workspace/render-session/lane.yaml`
- **env override**: `RENDER_SESSION_<KEY>` with `__` for nested keys
  (e.g. `RENDER_SESSION_CATEGORIES__RECENT__ENABLED=false`)

Later layers override earlier ones. Missing files are silently skipped.

### Inspect config

```sh
# Show effective merged config
render-session config show --project ./my-project

# Show effective config with per-value provenance (which layer set each value)
render-session config show --project ./my-project --origin

# List layer paths and active env overrides
render-session config info --project ./my-project

# Health check (path existence + env override value validation)
render-session config doctor --project ./my-project
```

See [`docs/runbook/config-strategy.md`](docs/runbook/config-strategy.md) for full details.

## License

Licensed under either of

- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)

at your option.
