A tiny zero-dependency CLI for generating memes via the free memegen.link API. Agent-friendly.
Project description
makememe — a meme CLI your coding agent can drive
A tiny, zero-dependency meme generator for coding agents and CI. No API
key, no signup, stdlib-only — it wraps the free
memegen.link API and hands back a public image URL you
can embed anywhere with .
Quick start
Give your coding agent a meme button. Two commands, then just ask:
uv tool install makememe # or: pipx install makememe / pip install makememe
meme --install-skill # installs the bundled Claude Code skill
Restart Claude Code and talk to it normally:
you: make a "this is fine" meme about prod being down
Claude: → https://api.memegen.link/images/fine/prod_is_down/this_is_fine.png
The skill teaches the agent to pick the template that fits the joke (not just
drake), write the caption, and keep any names you mention — then hand back a URL
that renders inline in a PR, Slack, or anywhere. (Codex and other agents work
out of the box via meme --help / meme --list — no skill needed.)
Prefer to drive it yourself? It's a normal CLI too.
meme drake "manual deploys" "ci/cd" # saves a PNG to a temp folder
meme -t fine "prod is down" "this is fine" --print-url # or just print a shareable URL
meme --list # browse all template ids
No install needed to try it: uvx --from makememe meme drake "a" "b".
Next: meme your CI on every PR with a copy-paste GitHub Action.
Install / upgrade
# install (pick one)
uv tool install makememe # recommended — isolated, puts `meme` on PATH
pipx install makememe
python3 -m pip install makememe
# install the bundled Claude Code skill, then restart Claude Code
meme --install-skill # ~/.claude/skills/meme/ (all projects)
meme --install-skill --project # ./.claude/skills/meme/ (this repo only)
Upgrading:
uv tool upgrade makememe # pipx: pipx upgrade makememe | pip: pip install -U makememe
meme --install-skill # ⚠️ re-run this so the skill updates too
⚠️ Upgrading the package does not refresh the skill already copied into
~/.claude/skills/meme/. Re-runmeme --install-skillafter every upgrade (then restart Claude Code), or the agent keeps using the old skill.
Skip the approval prompt (optional). Claude Code asks once before running
meme. To never be prompted, add this to the permissions → allow list in
your Claude Code settings.json:
"Bash(meme:*)"
Verify:
meme --version # CLI version
meme --list # CLI runs and reaches templates
ls ~/.claude/skills/meme/SKILL.md # skill is installed
Usage
meme <template> "top line" "bottom line" [-o out.png]
| Flag | Meaning |
|---|---|
-t, --template |
template id as a flag (alternative to the positional). Agents should use this so a single permission approval covers every template |
-o, --out |
output file (default: a unique file in a temp folder, so it never writes into your current directory) |
--bg URL |
use a custom background image instead of a template |
--ext |
png (default), jpg, webp, or gif |
--style / --font |
template style variant / font override |
--open |
open the finished image in your default viewer |
--print-url |
print the image URL, don't download |
--json |
machine-readable output (for scripts/agents) |
--list |
list available template ids |
Examples
meme drake "manual deploys" "ci/cd"
meme same "after I sold" "if I held" "same picture"
meme --bg https://example.com/pic.png "_" "DODGED"
meme regret "SOLD @ 620" "NOW 780 (+26%)" --print-url
meme --list
meme --list --json
Integrations
For sharing (CI, chat, comments) you usually want the public URL, not a
local file — --print-url returns a permanent memegen.link URL you can embed
anywhere with . No download, no image hosting.
Meme your CI
Drop this in .github/workflows/pr-meme.yml, change the one test line to your
command, and every PR gets a success / "this is fine" meme based on whether
tests passed. That's the whole setup — no secrets (GITHUB_TOKEN is built in):
name: pr-meme
on: pull_request
permissions:
pull-requests: write # to post the comment
jobs:
meme:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
id: tests
run: echo "replace this with your test command (e.g. pytest, npm test)"
- name: Comment a meme with the result
if: always() # run even when tests fail
run: |
pip install --quiet makememe
if [ "${{ steps.tests.outcome }}" = "success" ]; then
url=$(meme -t success "tests" "passed" --print-url)
else
url=$(meme -t fine "tests failed" "this is fine" --print-url)
fi
gh pr comment "${{ github.event.pull_request.number }}" --body ""
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
(Also saved at examples/pr-meme.yml.)
Meme your release
Celebrate a tag with a meme in the release notes or a Slack announcement — just feed it the version:
ver=$(git describe --tags --abbrev=0)
url=$(meme -t success "shipped" "$ver" --print-url)
# drop $url into your GitHub Release body, changelog, or a Slack post
Or just ask your agent: "create a meme about this release" — it reads the version/changelog and picks a fitting template.
Slack/Discord: post a --print-url URL to a channel; it auto-unfurls into a
preview.
url=$(meme -t success "build" "passed" --print-url) # -> public URL, embed it anywhere
Text that starts with -
If a caption line begins with - (e.g. "-26%"), put -- before your lines so
it isn't parsed as a flag:
meme regret --json -- "-26%" "WHY"
(Put flags like --json/-o before the --.)
For agents (Claude Code / Codex / scripts)
The tool is designed to be parsed:
-
Plain mode prints only the output path to stdout (status goes to stderr), so
path=$(meme drake "a" "b")just works. -
--jsonmode prints a single JSON object:{ "path": "/tmp/makememe/meme-ab12cd.png", "bytes": 12345, "url": "https://api.memegen.link/..." }
--list --jsonreturns the template catalog as JSON;--print-url --jsonreturns{"url": "..."}; failures return{"error": "...", "url": "..."}with a non-zero exit code.
Typical agent flow:
meme --list --json # discover template ids
meme drake "old way" "new way" --json # generate, capture the path
Claude Code skill
The package bundles a Claude Code skill. After installing, run one command to make Claude Code auto-discover the tool:
meme --install-skill # installs into ~/.claude/skills/meme/ (all projects)
meme --install-skill --project # or into ./.claude/skills/meme/ (this repo only)
Restart Claude Code, then just talk to it. You don't name templates or flags — the skill picks the template that fits the joke, writes the caption, and keeps any names you mention. Things you can say:
| You say… | What Claude does |
|---|---|
| "make a 'this is fine' meme about prod being down" | fine template → returns a shareable URL |
| "drake meme: manual deploys vs CI/CD" | drake (reject A / prefer B) |
| "a meme about choosing between fixing the bug and shipping the feature" | picks ds (two-buttons dilemma) on its own |
| "meme about my plan to refactor that keeps breaking tests" | picks gru (plan backfires) |
| "make a meme ragging on Raj for force-pushing to main" | keeps the name Raj in the caption |
| "a 'change my mind' meme that tabs beat spaces" | cmm one-liner |
| "meme that staging and prod are the same picture" | same template |
| "make me a success-kid meme for fixing the flaky test, and save it as a png" | success → downloads a file with --open |
| "drop a meme on this PR based on whether CI passed" | wires up --print-url + gh pr comment |
| "create a meme about this release" | reads the version/changelog → picks a fitting template |
It's conversational — "funnier", "use the two-buttons template instead", or "now make the bottom line shorter" all work as follow-ups.
(Other agents like Codex don't use this skill format — they discover everything
through meme --help and meme --list, which already works out of the box.)
Robustness
The CLI is built to fail gracefully, never with a raw traceback:
- Network errors, dead hosts, bad template ids (404), oversized text (414), and non-image backgrounds (415) all exit non-zero with a one-line message — and no partial/garbage file is written.
Ctrl-Cexits cleanly (code 130); piping intoheadetc. won't spew aBrokenPipeError.- Arbitrary text — emoji, CJK,
% # & / ? " \, tabs, control chars, 10k-char lines — is escaped safely.
Run the test suite (stdlib only, no network needed):
python -m unittest discover -s tests
How it works
It builds a memegen.link URL from your template + text (handling all the fiddly
path-segment escaping — spaces, _, -, ?, /, %, etc.), downloads the
image, and saves it. That's the whole trick.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file makememe-0.1.12.tar.gz.
File metadata
- Download URL: makememe-0.1.12.tar.gz
- Upload date:
- Size: 939.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3eabb1ac52056059df076b70ca2149bb071ff64a6104b1ac853f1c34aebd38b8
|
|
| MD5 |
6f5eb8a3128ecf580275e8d30a11bf9a
|
|
| BLAKE2b-256 |
f741b364f730c505b9b4ef43b4a5ae3aacfdc0f5d445e7760d6de8276e1a8325
|
Provenance
The following attestation bundles were made for makememe-0.1.12.tar.gz:
Publisher:
publish.yml on dhruvmehra/makememe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
makememe-0.1.12.tar.gz -
Subject digest:
3eabb1ac52056059df076b70ca2149bb071ff64a6104b1ac853f1c34aebd38b8 - Sigstore transparency entry: 1763408462
- Sigstore integration time:
-
Permalink:
dhruvmehra/makememe@3086c70dc3ffa3f0793e8d2f0d66ff2c986fa646 -
Branch / Tag:
refs/tags/v0.1.12 - Owner: https://github.com/dhruvmehra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3086c70dc3ffa3f0793e8d2f0d66ff2c986fa646 -
Trigger Event:
release
-
Statement type:
File details
Details for the file makememe-0.1.12-py3-none-any.whl.
File metadata
- Download URL: makememe-0.1.12-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1881042d5199305ea9fca6eaef1bb0143fa037937a072c1b436b17777b50b34
|
|
| MD5 |
2b850c5c747d5c085d3669d89ebe1286
|
|
| BLAKE2b-256 |
7e10a8ac618d7ee43e287c42620c70f4b537964d63e943491735f18784e29a9a
|
Provenance
The following attestation bundles were made for makememe-0.1.12-py3-none-any.whl:
Publisher:
publish.yml on dhruvmehra/makememe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
makememe-0.1.12-py3-none-any.whl -
Subject digest:
d1881042d5199305ea9fca6eaef1bb0143fa037937a072c1b436b17777b50b34 - Sigstore transparency entry: 1763408575
- Sigstore integration time:
-
Permalink:
dhruvmehra/makememe@3086c70dc3ffa3f0793e8d2f0d66ff2c986fa646 -
Branch / Tag:
refs/tags/v0.1.12 - Owner: https://github.com/dhruvmehra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3086c70dc3ffa3f0793e8d2f0d66ff2c986fa646 -
Trigger Event:
release
-
Statement type: