npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@black-magic/wp-site-maintenance

v0.0.10

Published

CLI tool to backup and update WordPress sites on SiteGround

Readme

wp-site-maintenance

CLI tool to automate WordPress maintenance on SiteGround-hosted sites. Connects via SSH, creates backups (DB + wp-config.php + per-plugin compressed tars), updates plugins one by one and WordPress core, and automatically rolls back on failure.

Installation

# Run without installing
pnpm dlx @black-magic/wp-site-maintenance

# Or install globally
pnpm add -g @black-magic/wp-site-maintenance

Setup

Create a .wp-maintenance.json file in the directory where you'll run the command, or in ~/ for global use:

{
  "localBackupDir": "~/wp-backups",
  "sites": [
    {
      "sshAlias": "my-wp-site",
      "siteDir": "example.com"
    }
  ]
}

localBackupDir is optional — defaults to ~/wp-backups. Backups are saved as {localBackupDir}/{siteDir}/{timestamp}.tar.gz.

You can also specify a config file explicitly:

wp-maintenance --config /path/to/config.json

Non-standard paths

Some WordPress installations (e.g. Bedrock/Roots) keep plugins and wp-config.php outside the standard locations. Use pluginsDir and wpConfigPath to override:

{
  "sites": [
    {
      "sshAlias": "my-bedrock-site",
      "siteDir": "example.com",
      "pluginsDir": "public_html/app/plugins",
      "wpConfigPath": "../wp-config.php"
    }
  ]
}

Both are relative to the site root ($HOME/www/{siteDir}/) and optional:

  • pluginsDir defaults to wp-content/plugins
  • wpConfigPath defaults to wp-config.php

SSH connection options

Option A — SSH config alias (recommended)

{ "sshAlias": "my-wp-site", "siteDir": "example.com" }

~/.ssh/config entry:

Host my-wp-site
  HostName ssh.example.siteground.com
  User u1234-abc12def34gh
  Port 18765
  IdentityFile ~/.ssh/id_rsa_siteground

If IdentityFile is omitted, the SSH agent (SSH_AUTH_SOCK) is used automatically.

Option B — explicit credentials

{
  "ssh": {
    "host": "ssh.example.siteground.com",
    "username": "u1234-abc12def34gh",
    "port": 18765,
    "privateKeyPath": "~/.ssh/id_rsa_siteground"
  },
  "siteDir": "example.com"
}

Usage

wp-maintenance             # interactive update
wp-maintenance --dry-run   # preview pending updates without making changes
wp-maintenance --status    # check last run status for all configured sites
wp-maintenance --version   # print the installed version
wp-maintenance --config /path/to/config.json

During development:

pnpm dev
pnpm dev -- --dry-run
pnpm dev -- --status

Normal mode

  1. Select which sites to update (multi-select)
  2. Confirm
  3. For each site, sequentially:
    • Shows working directory on the remote server
    • Creates a timestamped backup: DB, wp-config.php, and a .tar.gz per updated plugin
    • Compresses the entire backup and downloads it locally via SCP
    • Deletes the remote backup folder only after all updates succeed
    • Updates plugins one by one, then WordPress core
    • On failure: automatically rolls back to the pre-update state
  4. Shows a summary with local backup path per site

Dry-run mode (--dry-run)

Connects via SSH, checks for pending plugin and core updates, and prints what would be updated — without making any changes or requiring confirmation.

Status mode (--status)

Connects to each configured site and reads .wp-maintenance-status.json to show the result of the last maintenance run. No changes are made.

✓  example.com      success        2026-04-03 10:15  Yoast SEO 22.1→22.2, WordPress 6.4.1→6.5.0
⚠  other.com        failed_update  2026-03-28 09:12  Failed on: woocommerce
✗  broken.com       failed_rollback 2026-03-15 09:12  ← REQUIRES MANUAL INTERVENTION
–  new.com          never run

What gets backed up

Before any updates, the tool creates a backup on the remote server at $HOME/www/{siteDir}/.wp-backups/{timestamp}/:

| File | Description | |------|-------------| | db.sql | Full database dump via wp db export | | wp-config.php | Copy of the site's config file | | plugins/{slug}.tar.gz | Compressed backup of each plugin being updated |

Only plugins that are actually being updated are backed up — not the entire wp-content/plugins/ directory.

The backup folder is then compressed into a .tar.gz and downloaded to your local machine at {localBackupDir}/{siteDir}/{timestamp}.tar.gz. The remote folder is deleted after a successful download — if the download fails, the remote copy is kept.

After a successful run, each site also has a status file at $HOME/www/{siteDir}/.wp-maintenance-status.json with the result of the last operation.

License errors

If a plugin update fails because the license is expired or invalid (WP-CLI outputs "Check your license details first"), the plugin is skipped — no rollback is triggered and the remaining updates continue. The summary shows:

⚠ Advanced Custom Fields Pro  6.2.0  skipped — license error

Auto-rollback

If any plugin or core update fails for any other reason, the tool immediately rolls back:

Plugin failure:

  1. Restore each backed-up plugin directory from its .tar.gz
  2. Restore the database via wp db import

Core failure:

  1. Restore all backed-up plugin directories
  2. Re-download the previous core version via wp core download --version={old} --force
  3. Restore the database

The final summary distinguishes three states:

| State | Meaning | |-------|---------| | ✓ Updated | All updates succeeded | | ⚠ Rolled back | An update failed, site restored to pre-update state | | ✗ Critical | An update failed and rollback also failed — manual intervention required |

Update order

Plugins are updated first, then WordPress core. If a plugin fails, core is still at the old version and the rollback is clean.

When multiple core updates are available (e.g. a minor 6.8.5 and a major 6.9.4), the tool always applies the latest available version. Dry-run lists all of them with their type so you have full visibility before committing.

Build

pnpm build   # Compiles to dist/index.js (ESM bundle)

Commit conventions

This project uses Conventional Commits, enforced via commitlint on every commit:

feat: add dry-run flag
fix: handle SSH timeout gracefully
chore: update dependencies

To regenerate CHANGELOG.md from commit history:

pnpm changelog        # from last tag to HEAD
pnpm changelog:all    # entire history
pnpm changelog v1.0.0 # from a specific tag

SiteGround SSH notes

  • SSH port is 18765, not 22
  • Home directory: /home/{username}/, sites under /home/{username}/www/{domain}/
  • WP-CLI is available as wp — no PHP binary prefix needed
  • WordPress files at $HOME/www/{domain}/, database connection from wp-config.php