Skip to main content

All-in-one CLI toolbox for office automation: PDF, Word, Excel, images, QR codes, Markdown, LLM and more, with AI-agent-friendly JSON output

Project description

🧰 etool

One install. Dozens of everyday automation commands. Built for humans and AI agents.

PyPI Python Downloads CI License

English | 中文

etool turns everyday office and developer chores into one-line shell commands (or Python calls): merge / split / encrypt PDFs, extract images from Word documents, convert Markdown to Word / HTML / Excel, batch-convert photos to WebP, generate and decode QR codes, merge Jupyter notebooks, test network / disk speed, chat with any OpenAI-compatible LLM, render cheat-sheet wallpapers, and more.

Add --json to any command and it prints exactly one machine-readable JSON envelope — which makes etool a drop-in tool layer for AI agents, scripts, and CI pipelines.

Why etool?

  • 🧩 All-in-one — PDF, Word, Excel, image, QR code, Markdown, Jupyter, web, LLM, and network utilities behind a single etool command. Every feature is also importable as a plain Python API.
  • 🤖 AI-agent friendly — with --json, every command emits one envelope: {"ok": true, "data": ...} or {"ok": false, "error": {code, message, details}}, with stable machine-readable error codes. Ideal for function calling and automated pipelines.
  • 🪶 Light by default — the default install is ~15 MB. Heavy binary dependencies (PyMuPDF, OpenCV) are opt-in extras: etool[all].
  • 🖥️ Cross-platform — one codebase for Windows, macOS, and Linux, CI-tested on all three. Python 3.10+.

Install

pip install -U etool          # lightweight core (~15 MB) — covers almost everything
pip install -U "etool[all]"   # + PDF → PNG rasterization and QR decoding
Extra Enables Adds
etool[pdf-images] etool pdf to-images (PDF → PNG) PyMuPDF
etool[qr-decode] etool qrcode decode (offline QR recognition) OpenCV (headless)
etool[all] everything above both

Run it without installing anything (via uv), or install it as an isolated CLI app:

uvx etool qrcode generate --text "https://example.com" --out qr.png
pipx install etool

After install, the etool entry point is on your PATH; python -m etool ... also works.

60-second tour

# Merge two PDFs
etool pdf merge --out merged.pdf part1.pdf part2.pdf

# Turn a Markdown note into a Word document
etool md to-docx notes.md --out notes.docx

# Batch-convert a folder of photos to lossless WebP
etool image rename-webp ./photos

# Generate a QR code
etool qrcode generate --text "https://example.com" --out qr.png

# Fetch a web page as clean, readable text
etool web fetch-text https://example.com

# Chat with any OpenAI-compatible model (stdlib HTTP, no SDK needed)
etool llm chat "Why is the sky blue?" --system "Answer in one sentence."

Need structured output for a script or an agent? Add --json:

$ etool --json web mask-ip 8.8.4.4
{
  "ok": true,
  "data": {
    "masked": "8.8.x.4",
    "is_public": true
  }
}

etool can even render a cheat-sheet wallpaper for any tool — this image was generated by etool cheatsheet generate:

Git cheat sheet wallpaper generated by etool

What's inside

Domain Command What you get
PDF etool pdf merge, split, encrypt / decrypt, watermark, insert, PDF → PNG
Word etool docx replace text, swap page orientation, extract embedded images
Excel etool excel copy workbook formatting to a new file
Images etool image stitch left-right / top-bottom, pad to square, 3×3 grid crop, batch → WebP
QR codes etool qrcode generate, decode offline
Markdown etool md → Word, → HTML, tables → Excel
Jupyter etool ipynb merge notebooks, notebook → Markdown
Web etool web page → readable text, RSS / Atom parsing, IP masking
LLM etool llm chat, summarize, outline via any OpenAI-compatible API
Cheat sheets etool cheatsheet render a command cheat-sheet wallpaper PNG
Speed etool speed network / disk / memory speed tests
Passwords etool password random passwords, arbitrary base conversion
Misc etool stdlib / install-reqs / scheduler / email stdlib usage analysis, bulk pip install, schedule parsing, SMTP mail

Command reference

With --json, stdout is one JSON document per invocation (pretty-printed, 2-space indent):

  • Success: {"ok": true, "data": { ... }}
  • Failure: {"ok": false, "error": {"code", "message", "details"}}

Without --json, output is human-readable (errors go to stderr). Below, Input is the command; Output shows typical --json stdout (values such as paths, passwords, and timings are illustrative).

Versionetool version

Input

etool --json version

Output

{"ok": true, "data": {"version": "2.2.0"}}
PDF — merge · split · encrypt · watermark · rasterize — etool pdf

Merge

etool --json pdf merge --out merged.pdf part1.pdf part2.pdf
{"ok": true, "data": {"merged": "merged.pdf", "log": "merged: part1.pdf\nmerged: part2.pdf\nmerged file saved as: merged.pdf"}}

Split by page chunk size

etool --json pdf split-pages --pages 3 document.pdf
{"ok": true, "data": {"source": "document.pdf", "log": "generated: document_part_by_page1.pdf\n..."}}

Split into N parts

etool --json pdf split-num --parts 2 document.pdf
{"ok": true, "data": {"source": "document.pdf", "log": "..."}}

Encrypt / decrypt

etool --json pdf encrypt --password secret doc.pdf --out doc_encrypted.pdf
etool --json pdf decrypt --password secret doc_encrypted.pdf --out doc_clear.pdf
{"ok": true, "data": {"log": "encrypted file saved as: doc_encrypted.pdf"}}

Insert another PDF after a page index

etool --json pdf insert --pdf1 a.pdf --pdf2 b.pdf --after-page 0 --out out.pdf
{"ok": true, "data": {"output": "out.pdf", "log": "inserted file saved as: out.pdf"}}

Watermark

etool --json pdf watermark --target folder_or_file.pdf --watermark wm.pdf --out-dir watermarked
{"ok": true, "data": {"log": "..."}}

PDF → PNG images (requires etool[pdf-images])

etool --json pdf to-images --input doc.pdf --out-dir png_out --dpi 2
{"ok": true, "data": {"log": "found 1 PDF file(s)\n..."}}
Word — replace text · swap orientation · extract images — etool docx

Replace text

etool --json docx replace --path report.docx --old foo --new bar
{"ok": true, "data": {"path": "report.docx"}}

Swap page dimensions (landscape ↔ portrait style)

etool --json docx swap-dimensions --input in.docx --output out.docx
{"ok": true, "data": {"path": "out.docx"}}

Extract embedded images

etool --json docx extract-images --input in.docx --out-dir ./img_out
{"ok": true, "data": {"path": "./img_out"}}
Excel — copy template formatting — etool excel

Copy formatting from a template workbook

etool --json excel copy-format --source template.xlsx --output out.xlsx
{"ok": true, "data": {"path": "out.xlsx"}}
Images — stitch · pad · grid crop · WebP — etool image

Merge left–right / top–bottom

etool --json image merge-lr left.png right.png --out lr.png
etool --json image merge-ud top.png bottom.png --out ud.png
{"ok": true, "data": {"path": "lr.png"}}

Pad to square / 3×3 grid crop / batch rename to WebP

etool --json image fill-square photo.jpg --out square.jpg
etool --json image cut-grid photo.jpg
etool --json image rename-webp ./shots --remove-original
{"ok": true, "data": {"paths": ["photo_cut00.jpg", "..."]}}
QR codes — generate · decode — etool qrcode

Generate

etool --json qrcode generate --text "https://example.com" --out qr.png
{"ok": true, "data": {"path": "qr.png"}}

Decode (local OpenCV; requires etool[qr-decode])

etool --json qrcode decode qr.png
{"ok": true, "data": {"text": "https://example.com"}}
Jupyter — merge notebooks · to Markdown — etool ipynb

Merge all .ipynb in a directory

etool --json ipynb merge-dir ./notebooks/
{"ok": true, "data": {"path": "./notebooks.ipynb"}}

Notebook → Markdown file

etool --json ipynb to-markdown analysis.ipynb --out-dir ./md_out
{"ok": true, "data": {"path": "analysis.md"}}
Markdown — to Word · to HTML · tables to Excel — etool md
etool --json md to-docx notes.md --out notes.docx
etool --json md to-html notes.md --out notes.html
etool --json md tables-to-xlsx tables.md --out tables.xlsx
{"ok": true, "data": {"message": "Converted Markdown to Word document: notes.docx"}}
LLM — chat · summarize · outline — etool llm

Works with any OpenAI-compatible endpoint, using stdlib HTTP only (no SDK dependency). Credentials come from --api-key / --base-url / --model, or the ETOOL_LLM_API_KEY / ETOOL_LLM_BASE_URL / ETOOL_LLM_MODEL environment variables (standard OPENAI_* variables also work). Reasoning-model <think>...</think> blocks are stripped automatically.

Chat

etool --json llm chat "Why is the sky blue?" --system "Answer in one sentence."
{"ok": true, "data": {"text": "Because air molecules scatter blue light more strongly than red."}}

Summarize (replies in the same language as the input; text inline or via --file)

etool --json llm summarize --file article.txt --min-words 50 --max-words 150
{"ok": true, "data": {"summary": "..."}}

Outline (structure text into main_title / sections / points JSON)

etool --json llm outline --file article.txt
{"ok": true, "data": {"outline": {"main_title": "...", "sections": [{"title": "...", "points": ["...", "..."]}]}}}
Web — page to text · RSS / Atom · IP masking — etool web

Fetch a page as readable text (drops script/style noise)

etool --json web fetch-text https://example.com
{"ok": true, "data": {"text": "Example Domain\n..."}}

Parse an RSS 2.0 / Atom feed (URL, local XML file, or raw XML string)

etool --json web rss https://example.com/feed.xml --limit 2
{"ok": true, "data": {"entries": [{"title": "...", "link": "...", "published": "...", "summary": "..."}]}}

Mask an IP for display

etool --json web mask-ip 8.8.4.4
{"ok": true, "data": {"masked": "8.8.x.4", "is_public": true}}
Cheat-sheet wallpaper — render a command cheat sheet PNG — etool cheatsheet

Render a command cheat-sheet PNG (up to 3×3 category cards; by default the left quarter is kept clear for desktop icons — tune with --left-margin-ratio, 0 disables). Data comes from a JSON file (--data) or is generated by an LLM (--keyword, needs the LLM configuration above).

etool --json cheatsheet generate --keyword git --out git.png --width 1920 --height 1080
etool --json cheatsheet generate --data uv.json --title "UV Cheat Sheet" --out uv.png

uv.json shape:

{"categories": [{"name": "Basics", "commands": [{"command": "uv sync", "description": "install deps"}]}]}
{"ok": true, "data": {"path": "git.png"}}
Speed — network · disk · memory — etool speed

Network (uses speedtest-cli; needs internet, can be slow)

etool --json speed network
{"ok": true, "data": {"report": "\n network test result:\ndownload speed: ... Mbps\n..."}}

Disk

etool --json speed disk --file-size-mb 10
{"ok": true, "data": {"report": "\n disk test result:\nread speed: ... MB/s\nwrite speed: ... MB/s\n"}}

Memory (stdlib buffer test)

etool --json speed memory --size-mb 32
{"ok": true, "data": {"report": "\n memory test result:\nread speed: ... MB/s\nwrite speed: ... MB/s"}}
Passwords — random · base conversion — etool password

Random password

etool --json password random --length 16
{"ok": true, "data": {"password": "xYz9...16chars"}}

Base conversion

etool --json password convert-base --from-base 16 --to-base 2 A1F
{"ok": true, "data": {"result": "101000011111"}}
Stdlib usage analysisetool stdlib

One subcommand: stdlib analyze DIR. By default the envelope puts the nested counts under data.result (JSON object). With --json-string, the same analysis is returned as a single formatted JSON text under data.json (useful when you want one string field instead of nested JSON).

etool --json stdlib analyze ./src
etool --json stdlib analyze ./src --json-string
{"ok": true, "data": {"result": {"os": {"path.join": 12, "listdir": 3}}}}
{"ok": true, "data": {"json": "{\n  \"os\": {\n    \"path.join\": 12\n  }\n}"}}
Install requirementsetool install-reqs

Uses python -m pip install internally.

etool --json install-reqs --file requirements.txt --failed-file failed.txt --retry 2
{"ok": true, "data": {"success": true}}

On failure:

{"ok": false, "error": {"code": "RUNTIME_ERROR", "message": "some packages failed to install", "details": {}}}
Scheduler — parse schedule expressions — etool scheduler
etool --json scheduler parse 120
etool --json scheduler parse '"08:00"'
{"ok": true, "data": {"log": "Execute every 120 seconds"}}
Email — send via SMTP — etool email

Do not paste real passwords into shell history; prefer environment-specific secrets in automation.

etool --json email send \
  --sender you@example.com \
  --password "$SMTP_PASSWORD" \
  --recipient other@example.com \
  --message "Hello" \
  --subject "Test"
{"ok": true, "data": {"result": "send success"}}

Use it from Python

Every CLI feature maps to a Manager* class with plain static methods:

from etool import ManagerPdf, ManagerImage, ManagerQrcode, ManagerMd

ManagerPdf.merge_pdfs(["part1.pdf", "part2.pdf"], "merged.pdf")
ManagerImage.fill_image("photo.jpg")                      # pad to square
ManagerQrcode.generate_qrcode("https://example.com", "qr.png")
ManagerMd.convert_md_to_docx("notes.md", "notes.docx")

For structured envelopes in your own code:

from etool import ok, err, EtoolError, ErrorCode

payload = ok({"path": "/tmp/out.pdf"})
failure = err(EtoolError(ErrorCode.VALIDATION_ERROR, "bad input", {"field": "x"}))

Missing optional dependencies never break the package: each manager is imported defensively, and etool.get_import_status() reports what is available.

For AI agents

etool --json <command> is designed to be called by agents and scripts:

  • stdout always carries exactly one JSON document (valid JSON, 2-space indent);
  • ok is the single success flag to branch on;
  • error codes are a stable contract:
Code Meaning
VALIDATION_ERROR bad or missing input
NOT_FOUND file or resource not found
IO_ERROR read / write failure
DEPENDENCY_ERROR optional dependency missing (details.install tells you what to install)
RUNTIME_ERROR any other failure
{"ok": false, "error": {"code": "DEPENDENCY_ERROR", "message": "QR decoding requires OpenCV", "details": {"install": "pip install \"etool[qr-decode]\""}}}

Development

With uv (uv.lock is committed; the dev group includes the heavy optional deps so the full test suite runs):

uv sync
uv run pytest tests/test_etool.py -v

With pip:

pip install -e ".[all,dev]"
pytest tests/test_etool.py -v

See CHANGELOG.md for release history (including the platform-specific features deliberately removed in 2.0 to keep etool fully cross-platform).

Contributing

Issues and pull requests are welcome: github.com/jiangyangcreate/etool. If etool saves you time, a ⭐ helps more people discover it.

Star History Chart

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

etool-2.2.0.tar.gz (57.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

etool-2.2.0-py3-none-any.whl (53.4 kB view details)

Uploaded Python 3

File details

Details for the file etool-2.2.0.tar.gz.

File metadata

  • Download URL: etool-2.2.0.tar.gz
  • Upload date:
  • Size: 57.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for etool-2.2.0.tar.gz
Algorithm Hash digest
SHA256 904eadfad37821e70091ceee0e2e6af50375577b0819d0ccde81a248f874d808
MD5 5131d576b5025ee21181d902aa389a99
BLAKE2b-256 f7179721f79bdbb22800a2740c9419ab8a83ddfe623a53708e13b6b0d99638ab

See more details on using hashes here.

File details

Details for the file etool-2.2.0-py3-none-any.whl.

File metadata

  • Download URL: etool-2.2.0-py3-none-any.whl
  • Upload date:
  • Size: 53.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.25

File hashes

Hashes for etool-2.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fdabbc0f3fc249eecdc70a0240a40bb3821b6ff9be32912ab5228e5af0f7d09c
MD5 add29a6330814655f25968a4b9797748
BLAKE2b-256 a687405beff807353bf7754b6a5ddd443c6fee2cf2cf84a7f7b97ce56fdeaad8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page