6.7 KiB
AGENTS.md
Notes for LLM coding agents working on hugo-extended.
What this repo is
hugo-extended is a version-locked Node package that wraps the Hugo CLI:
- Package version == Hugo version (e.g.
0.154.3). - Provides:
- CLI passthrough (
hugo/hugo-extendedbinaries ->dist/cli.mjs) - Programmatic API (type-safe
exec,execWithOutput, and builder-stylehugo.*) - Direct binary path access (default export is callable and resolves to the Hugo binary path)
- CLI passthrough (
Key files / mental model
-
Public API:
src/hugo.tsdefault export: callable function that returns the Hugo binary path and has builder methods attached.- Named exports:
getHugoBinary(binary resolution + auto-install if missing)exec/execWithOutput(spawn Hugo with argv built from options)hugo(builder object)
-
CLI entry:
src/cli.ts- Resolves the binary path via the default export and forwards
process.argv.slice(2)to Hugo.
- Resolves the binary path via the default export and forwards
-
Binary installation:
src/lib/install.ts- Downloads Hugo release assets and verifies SHA-256 checksums.
- macOS v0.153.0+: uses
pkgutil --expand-fullto extract the binary from the.pkgfile (no sudo required). - macOS pre-v0.153.0: extracts
.tar.gzarchive intobin/. - non-macOS: extracts archive into
bin/andchmod +x.
-
Environment variables:
src/lib/env.ts- Centralized handling of all
HUGO_*environment variables. - Exports
getEnvConfig()for reading parsed config,loggerfor quiet-aware logging. - Exports
ENV_VAR_DOCSfor programmatic access to variable metadata.
- Centralized handling of all
-
Postinstall:
postinstall.js- For published packages (where
dist/exists), runs the compiled installer. - For repo/dev/CI (where
dist/may not exist), exits successfully and skips installation.
- For published packages (where
-
Argv builder:
src/lib/args.ts- Builds argv using
src/generated/flags.jsonto understand flag kinds and canonical long names. - Important: when a flag exists in the generated spec, its long name is used as-is (e.g.
--baseURL,--buildDrafts).
- Builds argv using
-
Generated inputs (committed):
src/generated/types.ts: command/options types.src/generated/flags.json: runtime flag spec used by argv building.
Code generation (types + flag spec)
scripts/generate-types.ts:
- Runs Hugo help output traversal (BFS across the command tree).
- Emits:
src/generated/types.tssrc/generated/flags.json
When bumping Hugo versions, regenerate these files and expect downstream changes in:
- Flag names/casing (Hugo sometimes prefers mixed case like
baseURL) - Which commands support which flags
- Integration-test filesystem outputs (Hugo occasionally changes scaffolding)
Testing (concise)
This repo uses Vitest.
Commands
npm test # all tests (vitest run)
npm run test:watch # watch mode
npm run test:unit # unit tests only
npm run test:integration # integration tests only (runs real Hugo)
npm run test:e2e # end-to-end installation tests
npm run test:coverage # coverage via v8
Test layout
-
tests/unit/*- Fast, pure TS/JS (no Hugo execution).
- Example:
tests/unit/args.test.tscovers argv building behavior driven byflags.json. - Example:
tests/unit/types.test.tsusesexpectTypeOfto validate type surfaces. - Example:
tests/unit/utils.test.tscovers platform detection, release filename resolution. - Example:
tests/unit/install.test.tscovers checksum parsing, archive type detection.
-
tests/integration/*- Executes real Hugo commands and does real filesystem work in temp dirs.
- Avoid
process.chdir()in tests: Vitest worker contexts may not support it.- Prefer passing Hugo's global
--sourcevia{ source: sitePath }.
- Prefer passing Hugo's global
-
tests/e2e/*- End-to-end tests for the full installation pipeline.
- Verifies binary installation, permissions, symlinks (macOS), and version matching.
- Platform-specific tests use
it.skipIf()to skip on unsupported platforms.
Integration test expectations to keep in mind
- Hugo output is noisy (e.g. "Congratulations! Your new Hugo site…"). Tests should assert on filesystem results instead of brittle stdout text.
- Hugo scaffolding changes over time:
- Example:
hugo new themein 0.154.x generates a theme skeleton withhugo.toml/hugo.yamlrather thantheme.toml.
- Example:
- Some flags may exist but not behave as you'd intuit for a given command:
- Example:
hugo new site --forcedoes not overwrite an existinghugo.tomlin 0.154.x.
- Example:
Practical tips for agents making changes
-
If you touch argv generation (
src/lib/args.ts):- Re-run
npm run generate-typesif the change depends on spec shape. - Prefer making tests match the committed generated spec, not an assumed kebab-case transform.
- Re-run
-
If you touch installation (
src/lib/install.ts/postinstall.js):- macOS install path uses
sudo installerand will behave differently in CI/sandboxed environments. - Tests are intentionally focused on the wrapper behavior, not on end-to-end installer reliability.
- macOS install path uses
-
If you touch exports in
src/hugo.ts:- Remember: consumers rely on the default export being callable (binary path) and having builder methods attached.
-
If you touch environment variables (
src/lib/env.ts):- All env vars are defined in
ENV_VARSwith name, aliases, parse function, and description. - Boolean env vars accept:
1,true,yes,on(case-insensitive). - Use
getEnvConfig()to read config; uselogger.info/warn/errorfor quiet-aware output. postinstall.jshas its own minimal env parsing (can't import TypeScript modules).
- All env vars are defined in
Environment variables reference
| Variable | Type | Description |
|---|---|---|
HUGO_OVERRIDE_VERSION |
string | Install a different Hugo version (ignores package.json) |
HUGO_NO_EXTENDED |
boolean | Force vanilla Hugo instead of Extended |
HUGO_SKIP_DOWNLOAD |
boolean | Skip postinstall binary download |
HUGO_BIN_PATH |
string | Use a pre-existing Hugo binary |
HUGO_MIRROR_BASE_URL |
string | Custom download mirror URL |
HUGO_SKIP_CHECKSUM |
boolean | Skip SHA-256 verification |
HUGO_QUIET |
boolean | Suppress installation output |
Some variables have aliases (e.g., HUGO_FORCE_STANDARD → HUGO_NO_EXTENDED, HUGO_SILENT → HUGO_QUIET). Check ENV_VARS in src/lib/env.ts for the full list.
Version-dependent behavior
- macOS v0.153.0+: Hugo ships as
.pkginstaller, extracted locally usingpkgutil --expand-full(no sudo required). - macOS pre-v0.153.0: Hugo ships as
.tar.gz, extracted tobin/directly. - The
usesMacOSPkg(version)andcompareVersions(a, b)utilities insrc/lib/utils.tshandle this.