Paulblish (pb) is a CLI tool that converts an Obsidian vault directory into a static HTML site for GitHub Pages deployment. See spec.md for the full specification.
All structural decisions (layout, tooling, conventions) follow phalt/clientele. When in doubt about "how should this be structured?" — do it the way clientele does it.
- Package layout: Flat layout with
paulblish/at repo root (notsrc/). - Dependency management:
uvfor everything.uv.lockcommitted..python-versionpinning Python version. - Build backend:
hatchlinginpyproject.toml. - Linting/formatting:
ruffconfigured inpyproject.tomlunder[tool.ruff]. Line length 120, target Python 3.13. - Tests:
pytestintests/at repo root. Fixtures intests/fixtures/. - Makefile:
make install,make test,make lint,make format,make clean.
-
Every implementation change must include tests. Before marking a phase step as done, either confirm existing test coverage is sufficient and adapt it, or write new tests. No untested code gets checked off.
-
_site/is NOT in.gitignore— it is committed and deployed. -
site.tomlis required in the source directory. Missing = exit code 1 with clear error. -
Articles require
publish: truein YAML frontmatter to be included. -
Directory structure of the source vault is preserved in output URL paths.
-
Home.md(case-insensitive) at source root maps tooutput/index.html. -
Templates use
{{ article.body_html | safe }}— the| safefilter is mandatory. -
The
<div class="article-body">wrapper must not be removed — it's the CSS styling hook. -
CLI output must show every
.mdfile as picked up or skipped with reason.
make install # uv sync
make test # uv run pytest
make lint # uv run ruff check .
make format # uv run ruff format .
make clean # rm -rf _site/ + __pycache__ + egg-info
uv run pb build -s /path/to/vault -o ./_site # build the siteCurrent phase: Phase 8 — Extra Features
After completing each phase action, check it off in spec.md (change - [ ] to - [x]) and update the current phase note here if the phase changes.
Build pipeline sequence: validate source -> validate config -> load config -> scan -> report scan -> build path map -> render -> collect assets -> copy assets -> template -> generate listings -> generate CNAME -> write -> report.
Key modules:
cli.py— click entry pointconfig.py— SiteConfig loading + site.toml validationmodels.py— Article and SiteConfig dataclassesscanner.py— directory walk, frontmatter parsing, filteringrenderer.py— markdown-it-py setup + plugin chainlinker.py— path lookup table, wikilink resolutionassets.py— asset discovery, copy, path rewritingtemplating.py— Jinja2 environment setup + renderwriter.py— output directory creation + file writingmanifest.py— incremental build manifest (load/save.pb-manifest.json)