This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A chezmoi source tree that provisions developer workstations: asdf-managed CLI tool versions, git config, zsh plugins, a reusable Taskfile collection, and Python venvs. It does not ship application code — edits here change the generated dotfiles on machines that run chezmoi apply against this repo.
.chezmoiroot is set to chezmoi_config, so chezmoi_config/ is the actual chezmoi source directory. Everything outside it (docs/, .github/, README.md, mkdocs.yml, .vale.ini, .pre-commit-config.yaml, renovate.json5) is repo-level tooling, not content delivered to target machines.
Chezmoi (applied on a target machine after chezmoi init --apply https://github.com/nolte/workstation.git):
chezmoi apply— render templates and write files into$HOMEchezmoi update— pull latest from this repo, then applychezmoi diff— preview changes before applying
Repo-level checks (run from the workstation repo root via the root Taskfile.yml):
task lint→pre-commit run --all-files(check-yaml,end-of-file-fixer,trailing-whitespace; see.pre-commit-config.yaml)task test→vale .(prose linting against the upstreamnolte/vale-stylepackage pinned in.vale.ini)task docs→mkdocs serve(requires thedocsvenv; see below)task docs:build→mkdocs build --strict(used by CI on release)
The root Taskfile.yml is the CI entry point — do not conflate it with the generated ~/taskfile.yaml that a user gets after chezmoi apply, which pulls task includes from ~/.local/share/taskfile-collection/src (the nolte/taskfiles repo, fetched via .chezmoiexternal.toml). Tasks like task mkdocs:serve and task pre-commit:run come from that target-machine Taskfile, not from this repo.
The MkDocs requirements exist in two places and must stay in sync: docs/requirements.txt (used by the docs-delivery CI workflow) and chezmoi_config/requirements-mkdocs.txt (installed into ~/.venvs/docs on provisioned workstations). Renovate bumps both.
dot_<name>→~/.<name>(e.g.dot_gitconfig.tmpl→~/.gitconfig,dot_tool-versions→~/.tool-versions)*.tmpl→ Go-template rendered with data from~/.config/chezmoi/chezmoi.toml(required keys:git_email,git_name)run_onchange_before_*/run_onchange_after_*→ scripts chezmoi re-runs whenever their rendered content changes. The convention for gating on another file is an embedded{{ include "<file>" | sha256sum }}comment — changing the referenced file changes the script's hash and triggers re-execution.
run_onchange_before_install-add-plugins.sh— idempotently adds the asdf plugins this workstation needs.run_onchange_after_install-asdf-packages.sh.tmpl— runsasdf install; gated ondot_tool-versionsvia embedded hash, so tool-version bumps re-trigger it.run_onchange_prepare-py-virtual-envs.sh.tmpl— creates~/.venvs/development(pre-commit, cookiecutter) and~/.venvs/docs(mkdocs-material); gated on bothrequirements-*.txtfiles.
The requirements-*.txt files are listed in chezmoi_config/.chezmoiignore so they are bundled into the source tree (and readable via chezmoi's include) but are not deposited into $HOME.
.chezmoiexternal.toml clones three zsh plugins under ~/.oh-my-zsh/custom/plugins/ and the taskfile collection under ~/.local/share/taskfile-collection/. These are not vendored — removing them here doesn't remove them from users until they delete them locally.
- Dependencies (tool versions, Python reqs, GitHub Action refs, Vale style package) are bumped by Renovate, which extends the shared config
github>nolte/gh-plumbing//renovate-configs/common(seerenovate.json5). renovate.json5also teaches the asdf manager to look atchezmoi_config/dot_tool-versions— if you add another*tool-versionsfile, extendasdf.managerFilePatternsthere or Renovate will not see it.- Vale styles come from a GitHub release URL in
.vale.ini, not from local files — do not commit to.github/styles/other thanVocab/Technical(enforced via.gitignore).
All workflows in .github/workflows/ are thin wrappers that delegate to reusable workflows in nolte/gh-plumbing, each pinned to an immutable release tag (Renovate bumps them together):
build-static-tests.yaml— pre-commit + Trivy + chain-bench on push/PR todevelop/mainautomerge.yaml— label-driven auto-mergerelease-drafter.yml/release-cd-refresh-master.yml— drafts releases fromdevelop; on publish, fast-forwardsmainto the release tagrelease-cd-deliver-docs.yml— on release publish, rebuilds MkDocs viareusable-mkdocs.yamlagainstdocs/requirements.txt
The repo intentionally does not follow the src/ / custom_components/ / .claude-plugin/ source-layout convention from the project-structure spec — .chezmoiroot forces the source tree into chezmoi_config/, and that is a deliberate, documented exception.
Similarly, .github/settings.yml, release-drafter.yml, and boring-cyborg.yml are single-line _extends: gh-plumbing:... references — repo-specific overrides go in those files, portfolio-wide defaults live in gh-plumbing.
- To add a new CLI tool: add a plugin in
run_onchange_before_install-add-plugins.shand a version indot_tool-versions. Renovate keeps the version current after that. - To add a new templated dotfile: place it under
chezmoi_config/with adot_prefix and.tmplsuffix if it needschezmoi.tomldata; otherwise no suffix. - MkDocs pages under
docs/configurations/pull snippets out ofREADME.mdandchezmoi_config/*viainclude-markdown/include. When editing README sections wrapped in<!--X-start-->/<!--X-end-->sentinels, keep the sentinels intact — they are the include boundaries. - The
developbranch is the integration branch;mainis fast-forwarded only on release publish.