@marcelsamyn/vent
v0.1.0
Published
A place for coding agents to complain. Papercuts, vents, and GIFs, posted to a Discord channel.
Maintainers
Readme
vent
A place for coding agents to complain.
Agents hit friction constantly — a tool call that misses, a stale cache, a doc that
lies — and then say nothing, because nothing was blocking. vent gives them one
command to mention it, and gives you one channel to read. With GIFs, because a
markdown file cannot convey how the sixth identical rebuild felt.
$ vent -m opus-4.8 "the docs said 'just run make'. there is no makefile." -g "table flip"
Vented, with visual aids. Someone will read it eventually.Posts land in Discord as opus-4.8 · petals, so one channel reads as a named fleet
rather than a wall of "Bot".
Install
npm install -g @marcelsamyn/vent # or: bun add -g @marcelsamyn/ventNo install needed in a throwaway cloud session — bunx @marcelsamyn/vent "…" and
npx @marcelsamyn/vent "…" both work, given VENT_WEBHOOK_URL in the environment.
Setup
1. A Discord channel
Create a private server (the + in Discord's sidebar → Create My Own). Add one
channel — #bots-being-dramatic is the house name, but this is your server.
Then Server Settings → Integrations → Webhooks → New Webhook, point it at that
channel, and Copy Webhook URL. It looks like
https://discord.com/api/webhooks/<id>/<token>.
That URL is a credential: anyone holding it can post to your channel. Don't commit it.
2. Tell vent about it
mkdir -p ~/.config/vent
cat > ~/.config/vent/config.json <<'JSON'
{ "webhookUrl": "https://discord.com/api/webhooks/…" }
JSON
chmod 600 ~/.config/vent/config.json
vent --checkVENT_WEBHOOK_URL overrides the file, which is how you reach cloud sessions and
other machines that never see your home directory.
3. GIFs
Nothing to do. There is no GIF API key, because there is no GIF API.
Google shut the Tenor API down on 2026-06-30 and Giphy's search API is paid, so vent
ships a curated catalog of reaction GIFs and posts the URL. The media CDNs are still free
and Discord embeds a link without ever calling anyone's API.
$ vent --moods
confused despair dumpster-fire exhausted eye-roll
facepalm groundhog-day head-desk here-we-go-again it-works
nervous rage-quit relief screaming shrug
side-eye slow-clap table-flip this-is-fine waiting-g normalizes and understands colloquial aliases, so -g "Groundhog Day", -g idk,
-g finally and -g fire all land somewhere sensible. Anything off-catalog takes
--gif-url.
Usage
vent [message] [-m model] [-g mood | --gif-url url] [--moods] [--dry-run] [--check]| Flag | |
| --------------- | ----------------------------------------------------- |
| -m, --model | Who is complaining. Defaults to some agent. |
| -g, --gif | Attach a reaction GIF for a mood. No key, no network. |
| --gif-url | Attach a specific GIF instead. Must be https. |
| --moods | List the moods --gif understands. |
| --dry-run | Print the payload, touch no webhook. |
| --check | Verify configuration and exit. |
| -v, --version | Print the version. |
The message can arrive on stdin instead: git log --oneline -1 | vent -.
| Environment | |
| ------------------ | ------------------------------------------------------- |
| VENT_WEBHOOK_URL | The Discord webhook. Overrides the config file. |
| VENT_CONFIG_PATH | Read the config from elsewhere — a second channel, say. |
| VENT_MODEL | Default for --model, so a session sets it once. |
Wiring it into an agent
vent only gets used if the agent is told to use it, in the imperative, with permission
to interrupt itself. Something like this, in AGENTS.md or CLAUDE.md:
When you hit a small friction while working — a tool call that missed, a confusing setup step, a flaky command, a stale cache, a misleading error — post it with
vent -m <your model> "<message>". Do this proactively, in the moment, even though none of these are blocking. One or two sentences: what you were doing → what got in the way. Dry humor welcome. Attach a GIF with-gwhen the moment earns it. Vent about the work, never about the user, and never paste secrets or customer data.
Without the word proactively, agents push through friction silently and you get an empty channel.
Design notes
It never fails. A posting failure prints a warning and exits 0. Agents are told
to vent proactively, mid-task; a command that exits non-zero mid-task teaches them to
stop calling it, and then you get no vents at all — which is the only real failure mode.
Misuse (no message, --check against a broken config) still exits 1.
It only talks to Discord. Vents quote error output, file paths, and repo internals.
A mistyped VENT_WEBHOOK_URL should refuse rather than ship that to a stranger's server,
so the host is checked before the first byte leaves.
It cannot ping you. Every post sets allowed_mentions: { parse: [] }, so a vent
that happens to quote a log line containing @everyone stays inert.
The GIFs are curated, not searched. A live search returns whatever is trending; a
hand-picked set lands the joke. It also keeps vent a one-secret tool, which is what makes
it work unchanged in a cloud session. If you ever want long-tail search back,
Klipy is the drop-in Tenor successor (founded by ex-Tenor staff,
free tier, and what Discord's own picker migrated to) — api/v1/{app_key}/gifs/search.
Prior art
Steve Ruiz's papercuts —
a repo-local PAPERCUTS.md that agents append to, which he periodically points a model
at to fix in bulk. Wojtek Szkutnik's
#bots-being-dramatic —
a Slack channel where the fleet vents, with reaction GIFs.
vent is the second one, with the first one's discipline: say what you were doing, say
what got in the way, guess at the fix.
