Metadata-Version: 2.4
Name: paar
Version: 0.0.1
Summary: live lazy variable inspector for python
Project-URL: Repository, https://github.com/vedicreader/paar
Project-URL: Documentation, https://vedicreader.github.io/paar/
Author-email: Karthik <karthik.rajgopal@hotmail.com>
License: Apache-2.0
License-File: LICENSE
Keywords: nbdev
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.11
Requires-Dist: fastcore>=2.1.3
Requires-Dist: httpx>=0.27.0
Requires-Dist: ipython>=8.39.0
Requires-Dist: litesearch>=0.0.34
Requires-Dist: mcp>=1.2
Requires-Dist: numpy>=2.2.6
Requires-Dist: pandas>=2.3.3
Requires-Dist: python-fasthtml>=0.14.9
Requires-Dist: textual>=8.0.0
Requires-Dist: websockets>=12.0
Description-Content-Type: text/markdown

# paar


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

`paar` is a **live, lazy variable inspector** for Python: a PyCharm-style variables pane
for any running interpreter — Jupyter, a plain script, or a training run — served over HTTP
from inside the process, with web, notebook, and terminal frontends.

- **Live**: the view refreshes after every cell (IPython) or by poll+diff (any process).
- **Lazy**: children are resolved one level at a time, on click — a million-row DataFrame
  costs nothing until you open it, and then only one page.
- **Everywhere**: the same server feeds the inline Jupyter panel, the browser, `paar-tui`
  in a terminal, and — new — a **restricted MCP surface for AI agents**, so Claude Code or
  Codex can work inside your live session without being able to break it.

## Installation

``` sh
pip install paar          # from pypi
pip install paar[mcp]     # + the paar-mcp server for agent access (Claude Code, Codex, ...)
pip install git+https://github.com/vedicreader/paar.git   # latest from GitHub
```

Docs are hosted on this repo’s [pages](https://vedicreader.github.io/paar/).

## Live demo

``` python
from paar.fasthtml import inspector
inspector()          # panel renders inline; keep this cell's output visible
```

<script>
document.body.addEventListener('htmx:configRequest', (event) => {
    if(event.detail.path.includes('://')) return;
    htmx.config.selfRequestsOnly=false;
    event.detail.path = `${location.protocol}//${location.hostname}:8000${event.detail.path}`;
});
</script>

<a href="http://localhost:8000/" target="_blank">Open in new tab</a>

<iframe src="http://localhost:8000/" style="width: 100%; height: 520px; border: none;" onload="" allow="accelerometer; autoplay; camera; clipboard-read; clipboard-write; display-capture; encrypted-media; fullscreen; gamepad; geolocation; gyroscope; hid; identity-credentials-get; idle-detection; magnetometer; microphone; midi; payment; picture-in-picture; publickey-credentials-get; screen-wake-lock; serial; usb; web-share; xr-spatial-tracking"></iframe> 

``` python
import math, numpy as np, pandas as pd
data = {'a': 1, 'b': [1, 2, {'deep': [10, 20]}], 'c': 'hello', 'pi': math.pi}
arr = np.arange(12).reshape(3, 4)
df = pd.DataFrame({'x': range(5), 'y': list('abcde')})
```

``` python
data['d'] = list(range(1000))   # run this; the panel above updates without re-running inspector()
```

**Viewing modes** (all from the one in-kernel server):
- **Inline:** the [`inspector()`](https://vedicreader.github.io/paar/fasthtml.html#inspector) cell output above updates live after every cell — you do not re-run it.
- **Docked side panel (JupyterLab):** right-click the [`inspector()`](https://vedicreader.github.io/paar/fasthtml.html#inspector) output → *Create New View for Cell Output* → drag into a split pane.
- **Full window:** open <http://localhost:8000/> or click the *open in browser* link.

Click the ▸ toggle on any container row (dict / list / tuple / set / object) to load its
children one level at a time. Nested containers keep their own toggles, so you can drill in
as deep as the data goes — e.g. `data` → `'b'` → `[2]` → `'deep'`.

`arr` (numpy) and `df` (pandas) show a **▦ grid** toggle instead of a tree — click it to open a
scrollable, paged table (use the row/col ◀ ▶ controls for data larger than one page). Plain
containers still expand as a tree.

**Exec, inline edit, and filtering:**
- **Exec box:** type an expression or statement and press Run — executes in the kernel; tick *Isolated* to evaluate side-effect-free (result shown without writing back to the namespace).
- **Inline edit:** click any scalar value in the tree to edit it in place; the new value is written back into the kernel namespace immediately.
- **Filter bar:** narrow the variable list by name substring or type (use the name field and the type dropdown above the table).

## Across processes and environments

The inspector is not limited to Jupyter. Any plain Python program (in any package or virtualenv)
can start a background paar server and expose its own live namespace:

``` python
import paar
paar.fasthtml.serve()   # inspects the caller's module globals; returns the URL, e.g. http://127.0.0.1:8000
```

[`serve()`](https://vedicreader.github.io/paar/fasthtml.html#serve) runs the server on a daemon thread and refreshes the view by polling the namespace, so no
Jupyter kernel or manual nudging is needed. Each server registers itself (by repo/package name) in a
small local registry, so **multiple environments can run paar at once** and any frontend can discover
and switch between them.

### From the command line

Run any script under a live inspector — the view updates while the script runs, and the server stays
up afterwards so you can inspect the final state:

``` sh
$ uv run paar-serve train.py            # or: uvx --from paar paar-serve train.py
paar inspector live at http://127.0.0.1:8000   (connect a terminal with: paar-tui)
```

### Terminal frontend

`paar-tui` is a gruvbox terminal inspector (built with Textual) that connects to any running server —
useful across environments since it only needs HTTP/WS:

``` sh
$ uv run paar-tui                       # auto-discovers a running server from the local registry
$ uv run paar-tui --env train           # pick a specific env by name
$ uv run paar-tui --url http://host:8000
```

Inside the TUI: `/` filters variables, `x` opens a code-exec bar, `1/2/3` switch profile, `g` opens
the grid, `e` switches between live environments, `r` refreshes, `q` quits.

## Sharing your session with AI agents

Your session is yours; an agent’s access to it should not be. [`serve()`](https://vedicreader.github.io/paar/fasthtml.html#serve) and [`inspector()`](https://vedicreader.github.io/paar/fasthtml.html#inspector)
also publish a **restricted agent surface** (`/agent/api/*`) built on two mechanisms in
`paar.agent`:

- **An overlay namespace** ([`AgentSession`](https://vedicreader.github.io/paar/agent.html#agentsession)). Agent code *reads* your variables directly, but
  every name it binds lands in its own persistent layer — assigning to one of your names
  merely shadows it, and deleting one is impossible by construction. Your namespace is never
  written.
- **A restriction list** ([clikernel](https://github.com/AnswerDotAI/clikernel)-compatible
  inspectors). Before a cell runs, its AST is checked: in-place mutation of your objects
  (`.append`, `df.drop(inplace=True)`, `x += ...`), `exec`/`eval`-style escapes, and
  destructive file/shell calls are refused with an error that tells the agent what to do
  instead.

The net contract: **agents can read anything and create anything new; they can never change
or delete what you made.** It works standalone too:

``` python
from paar.agent import AgentSession

yours = {'rows': [1, 2, 3], 'threshold': 2}          # stand-in for your live namespace
agent = AgentSession(owner=lambda: yours, rules=[])  # rules=[]: skip any local inspectors.py

agent.run('kept = [r for r in rows if r >= threshold]')   # reads yours, creates its own
agent.layer
```

``` python
for cell in ('rows.append(99)', 'del threshold', 'rows = []'):
    r = agent.run(cell)
    print(r.error or f'{cell!r} ran; owner still sees {yours}')
```

`'rows = []'` succeeded — as a *shadow*: the agent now has its own [`rows`](https://vedicreader.github.io/paar/fasthtml.html#rows), while `yours`
is untouched and still what you see. You can watch everything the agent has made via
[`paar.fasthtml.agent_layer()`](https://vedicreader.github.io/paar/fasthtml.html#agent_layer) (assign it to a variable and it shows up, expandable, in your
own panel). Server modes: `serve(agent='restricted')` (default), `'readonly'` (every agent
exec is isolated — nothing persists), or `'off'`. Add your own rules in
`~/.config/paar/inspectors.py` (or `$PAAR_INSPECTORS`) using clikernel’s file contract:
define `inspect(tree[, code])` and/or an `inspectors` list; raise
[`paar.agent.RuleBlock`](https://vedicreader.github.io/paar/agent.html#ruleblock) to refuse a cell.

Owner endpoints (`/exec`, `/set`, `/api/exec`) are gated separately by a per-server **owner
token** (`<reg_dir>/token-<port>`, mode 0600). Your web panel and `paar-tui` pick it up
automatically; agents on `/agent/api` never see it.

### Connecting Claude Code or Codex

`paar-mcp` is a stdio MCP server exposing the agent surface
as tools — `list_vars`, [`expand`](https://vedicreader.github.io/paar/snapshot.html#expand), `grid`, `execute`, `session_info` — and finds your server
through the local registry:

``` sh
# your session (Jupyter or script):        # Claude Code:
from paar.fasthtml import inspector        claude mcp add my-session -- paar-mcp --env myrepo
inspector()                                # Codex (config.toml):
                                           # [mcp_servers.my-session]
                                           # command = "paar-mcp"
                                           # args = ["--env", "myrepo"]
```

`--env` picks a registry entry by name (defaults to the only live one); `--url` pins a base
URL instead. The server resolves its target per call, so start order doesn’t matter.
`skills/paar-session/SKILL.md` documents the contract for agents — copy it into
`~/.claude/skills/` (or your agent’s skill directory) so the agent knows the etiquette
before its first call.

## Pairing with clikernel: watching the agent’s own session

The mirror-image setup: while `paar-mcp` lets an agent into *your* session, paar can also ride
inside the agent’s **[clikernel](https://github.com/AnswerDotAI/clikernel)** worker — the
persistent Python process Claude Code/Codex use as their workbench — so *you* can watch and
steer *their* session live.

clikernel runs `~/.config/clikernel/startup.py` inside the worker via `%run -i`, which is all
paar needs:

``` python
# ~/.config/clikernel/startup.py — runs inside every clikernel worker
import os
import paar.fasthtml
paar.fasthtml.serve(port=8001, name=os.environ.get('PAAR_ENV_NAME', 'claude'))
```

- **Two sessions, two ports**: your kernel on `inspector(port=8000)`, the agent’s worker on
  8001 ([`serve`](https://vedicreader.github.io/paar/fasthtml.html#serve) auto-bumps if taken). Both register in the local registry, so `paar-tui`’s
  `e` key or the web env dropdown flips between *you* and *claude*.
- **Naming**: set `PAAR_ENV_NAME=claude` / `PAAR_ENV_NAME=codex` in each client’s MCP server
  config for clikernel, so the one startup.py labels every worker correctly.
- **Restarts survive**: clikernel’s `restart` tool spawns a fresh worker, which re-runs
  startup.py — the inspector comes back by itself.
- **One rules file, two enforcers**: paar’s inspectors use clikernel’s exact contract
  (`inspect(tree[, code])`, `inspectors`, [`RuleBlock`](https://vedicreader.github.io/paar/agent.html#ruleblock)), so the restriction list you write for
  the agent’s own kernel (`~/.config/clikernel/inspectors.py`) can be reused for its access
  to your session (`~/.config/paar/inspectors.py`).

Full circle: Claude works in its clikernel on 8001 while reaching into your 8000 session
read-and-create-only via `paar-mcp` — and you see both, live, from one TUI.

## Security model (honest edition)

These are **guardrails for well-behaved agents, not a sandbox**. Concretely:

- The overlay gives a *structural* guarantee for name bindings: agent code cannot rebind or
  delete your variables no matter what it writes.
- The AST restriction list guards in-place mutation and escapes, but static analysis can be
  evaded by sufficiently creative code (`getattr` chains, new mutator names, …).
- The owner token keeps well-behaved tools off the owner endpoints, but any same-user local
  process with shell access could read the token file or the served page.

The realistic threat here is an agent *accidentally* clobbering your state — which this
stops cold. A hard boundary against adversarial code would need a separate process on a copy
of the data, which defeats the point of a live shared session.

## Developer guide

paar is an [nbdev](https://nbdev.fast.ai/) project: the notebooks in `nbs/` are the source of
truth, `paar/*.py` is generated. To hack on it:

``` sh
pip install -e .        # editable install
# edit notebooks under nbs/ ...
nbdev_prepare           # export modules, run tests, clean notebooks
```
