
Baoyu Compress Image
Batch-compress PNG/JPEG/WebP assets for faster pages by auto-picking sips, cwebp, ImageMagick, or sharp via a Bun CLI.
Overview
Baoyu Compress Image is an agent skill for the Ship phase that shrinks image payloads using sips, cwebp, ImageMagick, or sharp through a Bun compression script.
Install
npx skills add https://github.com/ideacco/baoyu-skills-openclaw --skill baoyu-compress-imageWhat is this skill?
- Auto-detects compressor: sips on macOS, cwebp for WebP, ImageMagick convert, or sharp fallback
- Supports png, jpg, jpeg, webp, gif, tiff with quality flag and optional keep-original
- Recursive directory mode with JSON output for agent-parseable CI logs
- Bun script with per-file input/output size ratio reporting
- Format targets: webp, png, jpeg
- 5 compressor/format paths: sips, cwebp, imagemagick, sharp with webp/png/jpeg targets
- 6 supported input extensions including gif and tiff
Adoption & trust: 1 installs on skills.sh; 12 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
What problem does it solve?
Your landing page or app ships multi-megabyte PNGs and you lack a repeatable local compress pass that works on macOS and Linux agents.
Who is it for?
Indie builders optimizing static assets on Mac or Linux CI runners with mixed ImageMagick/cwebp availability.
Skip if: Vector SVG workflow, responsive srcset generation only (no bytes written), or cloud CDN auto-optimization you already pay for.
When should I use this skill?
User needs to compress, convert, or batch-shrink images for a web project.
What do I get? / Deliverables
You get smaller WebP/PNG/JPEG files plus ratio stats (and optional JSON) ready to commit before deploy.
- Compressed image files at chosen format and quality
- Optional JSON manifest with input/output sizes and ratios
Recommended Skills
Journey fit
Image weight is a ship-time performance problem that shows up in Lighthouse and real-user load, not only during initial UI coding. Perf is the canonical shelf for byte reduction, format conversion (WebP), and recursive directory compression before launch.
How it compares
CLI skill package for batch local compression—not a hosted image CDN or browser-only canvas tweak.
Common Questions / FAQ
Who is baoyu-compress-image for?
Developers shipping web frontends who want an agent-invokable compressor instead of manual Preview or ad-hoc sharp one-liners.
When should I use baoyu-compress-image?
Before release when bundle or LCP audits fail on images, when converting heroes to WebP, or when batch-processing a public/ or assets/ folder.
Is baoyu-compress-image safe to install?
It runs shell commands and rewrites image files—check Security Audits on this page and use --keep or backups on production asset trees.
SKILL.md
READMESKILL.md - Baoyu Compress Image
#!/usr/bin/env bun import { existsSync, statSync, readdirSync, unlinkSync, renameSync } from "fs"; import { basename, dirname, extname, join, resolve } from "path"; import { spawn } from "child_process"; type Compressor = "sips" | "cwebp" | "imagemagick" | "sharp"; type Format = "webp" | "png" | "jpeg"; interface Options { input: string; output?: string; format: Format; quality: number; keep: boolean; recursive: boolean; json: boolean; } interface Result { input: string; output: string; inputSize: number; outputSize: number; ratio: number; compressor: Compressor; } const SUPPORTED_EXTS = [".png", ".jpg", ".jpeg", ".webp", ".gif", ".tiff"]; async function commandExists(cmd: string): Promise<boolean> { try { const proc = spawn("which", [cmd], { stdio: "pipe" }); return new Promise((res) => { proc.on("close", (code) => res(code === 0)); proc.on("error", () => res(false)); }); } catch { return false; } } async function detectCompressor(format: Format): Promise<Compressor> { if (format === "webp") { if (await commandExists("cwebp")) return "cwebp"; if (await commandExists("convert")) return "imagemagick"; return "sharp"; } if (process.platform === "darwin") return "sips"; if (await commandExists("convert")) return "imagemagick"; return "sharp"; } function runCmd(cmd: string, args: string[]): Promise<{ code: number; stderr: string }> { return new Promise((res) => { const proc = spawn(cmd, args, { stdio: ["ignore", "ignore", "pipe"] }); let stderr = ""; proc.stderr?.on("data", (d) => (stderr += d.toString())); proc.on("close", (code) => res({ code: code ?? 1, stderr })); proc.on("error", (e) => res({ code: 1, stderr: e.message })); }); } async function compressWithSips(input: string, output: string, format: Format, quality: number): Promise<void> { const fmt = format === "jpeg" ? "jpeg" : format; const args = ["-s", "format", fmt, "-s", "formatOptions", String(quality), input, "--out", output]; const { code, stderr } = await runCmd("sips", args); if (code !== 0) throw new Error(`sips failed: ${stderr}`); } async function compressWithCwebp(input: string, output: string, quality: number): Promise<void> { const args = ["-q", String(quality), input, "-o", output]; const { code, stderr } = await runCmd("cwebp", args); if (code !== 0) throw new Error(`cwebp failed: ${stderr}`); } async function compressWithImagemagick(input: string, output: string, quality: number): Promise<void> { const args = [input, "-quality", String(quality), output]; const { code, stderr } = await runCmd("convert", args); if (code !== 0) throw new Error(`convert failed: ${stderr}`); } async function compressWithSharp(input: string, output: string, format: Format, quality: number): Promise<void> { const sharp = (await import("sharp")).default; let pipeline = sharp(input); if (format === "webp") pipeline = pipeline.webp({ quality }); else if (format === "png") pipeline = pipeline.png({ quality }); else if (format === "jpeg") pipeline = pipeline.jpeg({ quality }); await pipeline.toFile(output); } async function compress( compressor: Compressor, input: string, output: string, format: Format, quality: number ): Promise<void> { switch (compressor) { case "sips": await compressWithSips(input, output, format, quality); break; case "cwebp": if (format !== "webp") { await compressWithSharp(input, output, format, quality); } else { await compressWithCwebp(input, output, quality); } break; case "imagemagick": await compressWithImagemagick(input, output, quality); break; case "sharp": await compressWithSharp(input, output, format, quality); break; } } function getOutputPath(input: string, format: Format, keep: boolean, customOutput?: string): string { if (customOutput) return resolve(customOutput); const dir = dirname(input