Managing Chezmoi Packages
This skill handles all aspects of package and dependency management in chezmoi dotfiles repositories, with emphasis on security, reproducibility, and automated updates.
Core Principles
- Version Pinning: Always pin to immutable references (commit SHAs, specific versions, digests)
- Renovate Automation: Ensure all dependencies have corresponding Renovate rules
- Organization: Group externals by primary program using
.chezmoiexternals/directory - Minimal Edits: Make focused changes without unrelated modifications
- Preview First: Show proposed changes before applying
When to Use This Skill
Invoke this skill when the user requests:
- Adding/updating/removing packages or tools
- Managing chezmoi external dependencies
- Pinning versions or updating SHAs
- Organizing external files by program
- Setting up Renovate automation for dependencies
- Choosing between package ecosystems (Homebrew, mise, etc.)
Workflow
1. Identify the Request
Determine what the user wants:
- Add: New package or external dependency
- Update: Change version/SHA of existing package
- Remove: Delete package or external
- Organize: Migrate externals to
.chezmoiexternals/structure - Pin: Add immutable version/SHA reference
2. Select the Ecosystem
Choose the appropriate package manager (see ./ecosystem-guide.md for details):
| Use Case | Ecosystem | Location |
|---|---|---|
| macOS packages (Homebrew) | brew/cask/mas | home/.chezmoidata/packages.yaml |
| CLI developer tools | mise (Aqua) | .mise.toml or home/dot_config/mise/config.toml |
| Python tools | pip | home/dot_config/dotfiles/requirements.txt |
| Docker/devcontainer | docker | home/dot_config/docker-compose/*.yml, .devcontainer/ |
| External files/repos | chezmoi externals | home/.chezmoiexternals/*.toml[.tmpl] |
| Install script CLIs | cli-versions | home/dot_config/dotfiles/cli-versions.toml |
3. For Chezmoi Externals: Use .chezmoiexternals/ Directory
IMPORTANT: Organize externals by primary program using the .chezmoiexternals/ directory structure.
Directory Structure
home/.chezmoiexternals/
├── zsh.externals.toml # Zsh plugins and tools
├── bat.externals.toml # Bat themes and syntaxes
├── tmux.externals.toml # Tmux configurations
└── nvim.externals.toml # Neovim plugins
How It Works
- Files in
home/.chezmoiexternals/are treated as.chezmoiexternal.<format>files - Each file defines externals for a specific program
- Use
.tmplsuffix for templating support (OS conditionals, variables) - Paths in each file are relative to the home directory
Example: zsh.externals.toml
{{ if lookPath "zsh" }}
# Zsh plugins and tools
[".zsh/znap/zsh-snap"]
type = "git-repo"
url = "https://github.com/marlonrichert/zsh-snap.git"
revision = "25754a45d9ceafe6d7d082c9ebe40a08cb85a4f0"
refreshPeriod = "168h"
[".zsh/plugins/zsh-autosuggestions"]
{{ end }}type = "git-repo"
url = "https://github.com/zsh-users/zsh-autosuggestions.git"
revision = "85919cd1ffa7d2d5412f6d3fe437ebdbeeec4fc5"
refreshPeriod = "168h"
4. External Types and Patterns
Chezmoi supports four external types (detailed in ./externals-reference.md):
git-repo
For complete Git repositories (plugins, frameworks):
[".zsh/plugins/plugin-name"]
type = "git-repo"
url = "https://github.com/user/repo.git"
revision = "<commit-sha>" # MUST pin to SHA
refreshPeriod = "168h"
file
For single files (themes, configs):
[".config/tool/theme.tmTheme"]
type = "file"
url = "https://github.com/user/repo/raw/<commit-sha>/file.ext"
refreshPeriod = "168h"
archive-file
For extracting single binary from release archive:
[".local/bin/tool"]
type = "archive-file"
url = "https://github.com/user/repo/releases/download/v1.2.3/tool.tar.gz"
executable = true
path = "tool"
checksum = "sha256:abc123..."
archive
For extracting entire archive into directory:
[".config/tool"]
type = "archive"
url = "https://github.com/user/repo/archive/<commit-sha>.tar.gz"
exact = true
stripComponents = 1
refreshPeriod = "168h"
5. Version Pinning Requirements
CRITICAL: Always use immutable references:
| Type | Pinning Method | Example |
|---|---|---|
| Git repo | Commit SHA in revision |
revision = "abc123..." |
| GitHub file | Commit SHA in URL path | raw/<sha>/file.ext |
| Release binary | Version tag + checksum | v1.2.3 + checksum = "sha256:..." |
| Docker image | Digest | image:tag@sha256:... |
NEVER use:
latesttags- Mutable branch names without SHA pinning
- Version ranges or wildcards
- URLs without commit SHAs or checksums
6. Ensure Renovate Integration
For each external dependency, verify or add corresponding Renovate rule in renovate.json5.
See ./renovate-integration.md for detailed patterns.
Quick reference:
{
customType: 'regex',
fileMatch: ['^home/\\.chezmoiexternals/.*\\.toml(\\.tmpl)?$'],
matchStrings: [
'revision = "(?<currentDigest>[a-f0-9]{40})".*?# renovate: repo=(?<depName>.*?) branch=(?<currentValue>.*?)\\n',
],
datasourceTemplate: 'git-refs',
}
7. Apply Changes
For .chezmoiexternals/ files
- Create or update the appropriate
.externals.tomlfile - Add comments explaining each dependency
- Include Renovate annotations for tracking:
# renovate: repo=user/repo branch=main [".path/to/dependency"] type = "git-repo" url = "https://github.com/user/repo.git" revision = "<commit-sha>"
For packages.yaml
- Edit
home/.chezmoidata/packages.yaml - Add to appropriate section (darwin/linux, brews/casks/mas)
- Follow existing patterns
8. Verification Commands
Preview changes:
chezmoi diff
Check external URLs:
# Verify latest commit SHA
gh api repos/USER/REPO/commits/BRANCH --jq .sha
# Check latest release tag
gh api repos/USER/REPO/releases/latest --jq .tag_name
# Verify file exists at SHA
curl -fsSL https://github.com/USER/REPO/raw/SHA/path/to/file
Apply changes:
chezmoi apply
Test external updates:
chezmoi update --force
Common Workflows
Adding a New Zsh Plugin
# 1. Get latest commit SHA
gh api repos/zsh-users/zsh-history-substring-search/commits/master --jq .sha
# 2. Add to home/.chezmoiexternals/zsh.externals.toml
# 3. Add Renovate rule to renovate.json5
# 4. Preview and apply
chezmoi diff
chezmoi apply
Adding a Homebrew Package
# 1. Edit home/.chezmoidata/packages.yaml
# 2. Add to appropriate section (brews/casks)
# 3. Renovate will track automatically (homebrew datasource)
Migrating Existing Externals to .chezmoiexternals/
- Identify program groupings in
home/.chezmoiexternal.toml - Search for references to the old file before migration:
Common references to fix:grep -r "\.chezmoiexternal\.toml\.tmpl" home/- Scripts using
{{ include ".chezmoiexternal.toml" }}for hashing - Documentation or comments mentioning the file
- Scripts using
- Create program-specific files in
home/.chezmoiexternals/ - Move related externals to appropriate file
- Update any references found in step 2 to point to new files (e.g.,
.chezmoiexternals/bat.externals.toml) - Keep conditionals (e.g.,
{{ if lookPath "zsh" }}) - Verify with
chezmoi diff(will error if references are broken) - Remove old file only after all references are updated and
chezmoi diffsucceeds
Updating External to Latest SHA
# 1. Fetch current SHA
gh api repos/USER/REPO/commits/BRANCH --jq .sha
# 2. Update revision in .chezmoiexternals/*.toml
# 3. Apply
chezmoi apply
Output Format
When making changes, provide:
Decision:
- Ecosystem: <brew|mise|python|docker|chezmoi-external|cli-versions>
- Rationale: <1-2 sentences>
- Organization: <which .chezmoiexternals/ file or other manifest>
Files To Edit:
- <path1>
- <path2>
Proposed Changes:
<concise diff-like hunks showing additions/modifications>
Renovate Integration:
- Rule Status: <exists|needs-addition|needs-update>
- Pattern: <brief description of regex or datasource>
Verification Commands:
- chezmoi diff
- <optional gh api commands to verify versions>
Next Steps:
- [ ] Review proposed changes
- [ ] Preview with chezmoi diff
- [ ] Apply with chezmoi apply
Important Constraints
- Never execute package managers without explicit permission
- Always preview with
chezmoi diffbefore applying - Use minimal edits: Change only what's necessary
- Maintain comments: Explain why each dependency exists
- Follow patterns: Match existing style and structure
- Version pinning: No mutable references
Platform-Specific Handling
Use Go templates for platform-specific externals:
{{ if eq .chezmoi.os "darwin" }}
[".local/bin/tool-mac"]
{{ else if eq .chezmoi.os "linux" }}
type = "archive-file"
url = "https://github.com/user/tool/releases/download/v1.0.0/tool-darwin-{{ .chezmoi.arch }}.tar.gz"
executable = true
[".local/bin/tool-linux"]
{{ end }}type = "archive-file"
url = "https://github.com/user/tool/releases/download/v1.0.0/tool-linux-{{ .chezmoi.arch }}.tar.gz"
executable = true
Troubleshooting
External fails to download:
- Verify URL accessibility:
curl -fsSL <url> - Check commit SHA exists:
gh api repos/USER/REPO/commits/SHA - Review chezmoi logs:
chezmoi apply -v
Renovate not detecting dependency:
- Verify regex pattern matches your format
- Check fileMatch glob is correct
- Test regex: use online regex tester with actual file content
- Ensure annotations are present if required
Checksum mismatch:
- Download file and compute SHA:
curl -fsSL <url> | shasum -a 256 - Update checksum in external definition
- Verify URL points to correct version
Best Practices
- Group by program: Use
.chezmoiexternals/PROGRAM.externals.toml - Document dependencies: Add comments explaining purpose
- Annotate for Renovate: Include
# renovate:comments - Use templates: Leverage
{{ if }}for conditional installs - Set refreshPeriod: Balance freshness vs. performance (168h = weekly)
- Test on clean machine: Verify externals work from scratch
- Pin everything: No mutable references, ever
Anti-Patterns
NEVER use mutable references (latest, branch names, version ranges) in externals. WHY: Mutable refs make reproductions non-deterministic — the same chezmoi apply produces different results on different days, breaking machine parity.
NEVER execute package managers (brew install, mise use, chezmoi apply) without explicit user permission. WHY: Package installs modify system state; the agent's role is to propose changes, not apply them unilaterally.
NEVER create a new .chezmoiexternal.toml at the repo root when .chezmoiexternals/ already exists. WHY: Mixing the legacy single-file format with the directory-based format confuses chezmoi and leads to duplicate or shadowed external definitions.
NEVER add a new external ecosystem when an existing one already covers the use case. WHY: Every additional ecosystem (Homebrew + mise + pip + externals + cli-versions) multiplies maintenance burden; always consult ./references/ecosystem-guide.md before introducing a new one.
NEVER omit a Renovate rule when adding a pinned external. WHY: A pinned dependency with no automation becomes stale immediately — security vulnerabilities and breaking changes accumulate silently until someone notices.
NEVER skip chezmoi diff before proposing chezmoi apply. WHY: Externals can overwrite local modifications; showing the diff first lets the user confirm no unexpected files will be changed.
Eval Scenarios
- Scenario 0: Add a Zsh plugin as a chezmoi external
- Scenario 1: Ecosystem selection across three tool types
- Scenario 2: Cross-platform release binary via archive-file external
- Scenario 3: Renovate automation rules for existing externals
- Scenario 4: Migrate legacy .chezmoiexternal.toml to .chezmoiexternals/
References
references/externals-reference.md— Complete external type specificationsreferences/renovate-integration.md— Renovate automation patternsreferences/ecosystem-guide.md— Package manager selection guide