# screenshare
A Vellum assistant plugin that ships an app with two buttons:
- **Share screen** — opens the browser's own screen picker, exactly like sharing
into Google Meet. You choose a screen, window, or tab.
- **Take screenshot** — counts down from 3, grabs a single frame of whatever you
are sharing, and saves it as a PNG in your assistant's workspace.
The difference from Meet is what happens in between: nothing. No video is
streamed to the assistant, and no frames leave the browser until you press the
second button. The only thing that ever reaches the workspace is a still you
asked for.
While a share is live the first button becomes **Stop sharing**, so the whole
surface stays at two buttons.
## Install
```
assistant plugins install https://github.com/vellum-ai/screenshare
```
Then open the **screen-share** app from the workspace panel.
## What's in the plugin
| Surface | What it does |
| -------------------------------- | -------------------------------------------------------------- |
| `apps/screen-share/` | The two-button UI, compiled from `src/` to `dist/` by the host |
| `routes/screenshots.ts` | `POST` saves a captured frame; `GET` lists what's stored |
| `routes/settings.ts` | Serves the `config.json` values the app needs at runtime |
| `skills/screen-share-screenshots/` | Teaches the assistant to find and look at a capture |
| `hooks/init.ts` | Creates the screenshot directory on boot |
### Where screenshots go
`<workspaceDir>/plugins/screenshare/data/screenshots/`, named for when they were
taken and what was shared:
```
screenshot-20260801-161500-screen-1.png
screenshot-20260801-162238-chrome-vellum-docs.png
```
The filename carries everything, so there is no sidecar metadata and no index to
fall out of sync — the directory listing is the store. Per the plugin contract
all durable state lives in `data/`, so uninstalling the plugin takes the
screenshots with it.
### Getting the assistant to look at one
Saving a file into the workspace does not put it in front of the model, so the
plugin ships a **skill** for that. Ask your assistant something like "what's on
my screen?" or "read the error in the screenshot I just took" and it loads
`screen-share-screenshots`, which tells it to list the captures and read the one
you mean with the built-in `file_read` tool — that already returns PNGs as images
for visual analysis.
A skill rather than a tool, deliberately. A tool's name, description, and input
schema sit in the model's context on *every* turn whether or not it is ever
called, and looking at a screenshot is only occasionally relevant. A skill costs
nothing until the conversation matches it, and it can carry judgement a schema
can't: which capture "what's on my screen" means, when to read several, to quote
error text verbatim, and not to guess when nothing has been captured yet.
The skill's `scripts/list-screenshots.ts` prints the store newest-first as
`<captured-at> <size> <absolute-path>`, so the assistant can hand a path
straight to `file_read`. It reuses `src/screenshots.ts` for the listing rather
than reimplementing the ordering, which is why that module takes its directory as
an argument and imports nothing from the host — a skill script runs as a plain
`bun` process, where `@vellumai/plugin-api` would not resolve.
## Configuration
`config.json` at the plugin root. Edit it in place — it is a preserved entry, so
your changes survive upgrades.
| Key | Default | Meaning |
| ------------------ | ------- | ----------------------------------------------------------------- |
| `countdownSeconds` | `3` | Seconds before the shutter. `0` captures immediately; max `60`. |
| `maxWidth` | `1920` | Screenshots wider than this are scaled down before being saved. |
| `retainCount` | `200` | How many screenshots to keep; older ones are pruned after a save. |
Every value is clamped to a sane range and falls back to its default if it is
missing or malformed, so a bad edit degrades rather than breaking capture.
## Development
The host's plugin source watcher compiles `apps/screen-share/src/` into a sibling
`dist/` on change — there is no build step to run, and `dist/` is not tracked.
The bundler is esbuild with `react`/`react-dom` aliased onto `preact/compat`; the
entry point is `src/main.tsx` and `src/index.html` is the shell.
`apps/screen-share/tsconfig.json` mirrors the host's compiler flags so editors and
`tsc --noEmit` agree with the real build.