A collection of CLI tools for Hugo bloggers. Run them directly from the root of your Hugo site via npx or install globally.
- Node.js 18 or later
Run without installing (recommended):
npx -p @chriswiegman/hugo-tools <command> [args]For example:
npx -p @chriswiegman/hugo-tools draft
npx -p @chriswiegman/hugo-tools publish content/drafts/my-post.md nowInstall locally in your Hugo project:
npm install --save-dev @chriswiegman/hugo-toolsThen run commands via npx (no -p flag needed once installed) or add them as scripts in your package.json:
{
"scripts": {
"publish": "publish",
"extract-tags": "extract-tags"
}
}Or install globally and run from any Hugo site root:
npm install -g @chriswiegman/hugo-tools
publish content/drafts/my-post.md nowAll commands must be run from the root of your Hugo site.
Run config from your Hugo site root to generate the file with all defaults:
configOr create .hugo-tools.json manually:
{
"timezone": "America/Chicago",
"postsDir": "content/posts",
"draftsDir": "content/drafts",
"notesDir": "content/notes",
"booksDir": "content/books",
"imagesDir": "assets/images",
"vscodeDir": ".vscode"
}All fields are optional. Shown values are the defaults.
Generates a .hugo-tools.json config file in the current directory with all default values.
configRun this once after installing to get a config file you can edit. Exits with an error if .hugo-tools.json already exists.
Scaffolds VS Code configuration for your Hugo site in your vscodeDir (default .vscode/).
vscodeCreates three files if they don't already exist — existing files are left untouched:
| File | Purpose |
|---|---|
tasks.json |
Tasks for draft, add-tags, publish (now & later), and pick-image |
extensions.json |
Recommends the markdown word-count and spell-checker extensions |
settings.json |
Markdown editor settings (snippet suggestions, word-based suggestions off) |
Creates a new draft file in your draftsDir and opens it in VS Code.
draftGenerates a timestamp-based filename (YYYYMMDD-HHMMSS.md) using your configured timezone, writes the standard draft front matter with a blank title and placeholder lists, and prints the path. No date: line is written — fill in the title before publishing.
Generated front matter:
---
title: ""
description: ""
draft: true
images:
-
categories:
-
tags:
-
---Publishes a Hugo draft post by moving it from your drafts directory to the appropriate content directory and updating its front matter.
publish <draft-path> [now|later] [YYYY-MM-DD]Examples:
# Publish immediately
publish content/drafts/my-post.md now
# Schedule for a future date (prompts if date omitted)
publish content/drafts/my-post.md later 2026-06-01What it does:
- Sets
draft: falseanddate:to now or 08:00 on the scheduled date (in your configured timezone) - Moves the file to
content/posts/YYYY/MM-DD-slug.md - Short posts (under 200 words) without categories are treated as notes and moved to
content/notes/instead - Warns if the draft already had a different
date:value
Requirements for posts (not notes):
- A
title:in front matter - At least one tag
Scans all posts in your postsDir and writes two files to your vscodeDir:
tags-categories.json— sorted list of all tags and categories with usage countsmarkdown.code-snippets— VSCode snippet definitions for autocomplete
extract-tagsRun this periodically to keep your tag/category list fresh. The snippet abbreviation map in src/extract-tags.js is pre-populated with some common shorthands — edit it to match your own taxonomy.
Interactive prompt to select categories and tags from your existing taxonomy, then prints the formatted front matter block to paste into a draft.
add-tagsRequires extract-tags to have been run at least once.
Moves an image into your imagesDir under the current year/month and inserts a reference into a Hugo content file.
pick-image <image-file> [content-file]What it does:
- Moves
<image-file>toassets/images/YYYY/MM/<filename>(creates the directory if needed) - Derives the Hugo URL path by stripping the
assets/prefix (e.g./images/YYYY/MM/filename) - If a
[content-file]is given:- Appends the path to the
images:array in front matter when that field is present (replacing any placeholder-entry) - Otherwise appends a
markdown tag to the end of the body
- Appends the path to the
- If no
[content-file]is given, copies the URL path to the clipboard instead
Examples:
# Move photo.jpg and insert into the currently open draft
pick-image ~/Downloads/photo.jpg content/drafts/my-post.md
# Move photo.jpg and copy the path to the clipboard
pick-image ~/Downloads/photo.jpgThe VS Code "Pick Image" task prompts for the image path and automatically passes the currently open file as the content target — drag the image file into the terminal prompt to fill the path.
Clipboard support (no-content-file mode): macOS (pbcopy), Windows (clip), Linux (xclip, xsel, or wl-copy — whichever is available).
Imports books from a Goodreads CSV export into Hugo content files under booksDir.
import-books path/to/goodreads_library_export.csvWhat it does:
- Creates
content/books/<author-slug>/<title-slug>.mdfor each book on your "read" shelf - Front matter includes title, author, star rating, finish date(s), and links to Amazon, Open Library, and Goodreads
- Any Goodreads review text becomes the file body
- Safe to re-run: existing files are matched by Goodreads ID or ISBN, finish dates are merged, and unchanged files are skipped
- If a book's title changed in Goodreads, the file is renamed automatically
To export from Goodreads: Account → My Books → Import and Export → Export Library.
Issues and PRs welcome at https://github.com/ChrisWiegman/hugo-tools.