/chezmoi — Dotfile Management with Chezmoi
Operational dotfile management: detect drift, discover new config, and health-check your chezmoi setup.
Tools
- Bash — Run
chezmoi status, chezmoi diff, chezmoi add, chezmoi git --, chezmoi doctor, chezmoi verify, op account list, and the scripts/filter-unmanaged.sh helper script
- Read — Read file content for sensitivity scanning, read exclusion YAML files
- Edit — Update
references/excludes.local.yaml when user excludes a file
- Write — Create
.tmpl files in chezmoi source directory for sensitive file templates
- AskUserQuestion — Ask which subcommand to run, which files to add/exclude/skip, how to handle sensitive files
Arguments
The skill accepts an optional subcommand:
- No argument: Show an overview of available subcommands and ask the user which to run
status: Detect managed files with local drift
discover: Find unmanaged config files worth tracking
doctor: Run health checks on the chezmoi setup
Subcommand Overview (no args)
When invoked without arguments, present this overview and ask which subcommand to run:
Chezmoi — Dotfile Management
Available subcommands:
/chezmoi status — Check for local changes to managed files that
haven't been captured back to the source repo yet
/chezmoi discover — Find config files in your home directory that
aren't managed by chezmoi, filtered to reduce noise
/chezmoi doctor — Health check: chezmoi doctor, verify, 1Password
connectivity, and source repo sync state
Which would you like to run?
Use AskUserQuestion to let the user pick a subcommand.
Shared: Add Workflow
This workflow is used by both status and discover whenever a file needs to be added to chezmoi. It handles sensitivity detection, 1Password template scaffolding, and the git pipeline.
Step 1: Sensitivity Scan
Before adding any file, scan it for secrets.
For drifted files (from status): run chezmoi diff <file> via Bash and scan the diff output for secret values.
For new files (from discover): read the full file content via Read and scan for secret values.
Content-based detection (always applied):
- Long random strings (high entropy, 20+ characters)
- Base64-encoded blobs
- Values adjacent to keys named
token, key, password, secret, auth, credential, api_key, apikey, access_token in structured formats (JSON, YAML, TOML, XML, INI, env files)
Path-based detection (for new files from discover):
- Path contains:
ssh, gnupg, gpg, kube, aws, credentials
- Filename contains:
secret, credential, token, key, password, auth, private
- File permissions: check with
stat -f '%Lp' <file> on macOS — mode 600 or more restrictive
If no secrets are detected, proceed directly to Step 3 (Git Pipeline).
Step 2: Sensitive File Handling
When secrets are detected, use AskUserQuestion to ask the user:
This file appears to contain sensitive data. How should it be handled?
1. Contains individual secrets (API keys, tokens) → Template with onepasswordRead
2. Entire file is a secret (key, certificate) → Template with onepasswordDocument
3. Just needs restricted permissions → Standard chezmoi add
4. Not sensitive, false positive → Standard chezmoi add
Option 1: Template with onepasswordRead
For config files containing embedded secrets:
- Read the file content
- Identify which values are secrets
- Generate a
.tmpl version of the file. Replace each secret with a {{ onepasswordRead "op://Vault/Item/Field" }} placeholder. Keep all non-secret content exactly as-is.
- Present the template to the user, listing each placeholder with its detected secret value (masked: show first 4 and last 4 characters only)
- Ask the user to:
- Store the secrets in 1Password (if not already there)
- Provide the actual
op:// URI for each placeholder
- Update the template with the real URIs
- Determine the correct chezmoi source path:
- Run
chezmoi source-path to find the source directory
- Convert the target path to chezmoi naming: replace leading
. with dot_, use private_ prefix for files with 600 permissions, add .tmpl suffix
- Look at existing files in the source directory for naming convention reference
- Write the template file to the source directory using Write
- Proceed to Step 3
Option 2: Template with onepasswordDocument
For whole-file secrets (SSH private keys, certificates, PEM files):
- Tell the user: "This file should be stored as a 1Password document. Please upload it to 1Password and provide the document UUID."
- Wait for the user to provide the UUID
- Create the template content:
{{- onepasswordDocument "<UUID>" }}
- Determine the correct source path (same logic as Option 1, with
private_ or private_readonly_ prefix as appropriate)
- Write the template file to the source directory using Write
- Proceed to Step 3
Options 3 & 4: Standard add
Run via Bash:
chezmoi add ~/<file>
Chezmoi automatically detects file permissions and applies the private_ prefix for 0600 files. Proceed to Step 3.
Step 3: Git Pipeline
Run all git operations via Bash using chezmoi's native git integration:
- Stage:
chezmoi git -- add .
- Commit:
chezmoi git -- commit -m "<message>"
- Generate a descriptive commit message from the files being added (e.g., "Add fish shell config and fzf bindings" or "Update zed editor settings")
- For multiple files, summarize what changed rather than listing every file
- Confirm with user before push (use AskUserQuestion: "Push to remote? [yes/no]")
- Push:
chezmoi git -- push
For multiple files added together, batch them into a single commit with a summary message.
Subcommand: status
Execution Steps
Detect drift: Run via Bash:
chezmoi status
Parse the output. Each line has a two-character status code and a file path (e.g., MM .gitconfig).
Handle empty results: If no output, report "All managed files are in sync" and stop.
Group findings by category based on the file path:
- Shell — paths containing
fish, bash, zsh, or shell config files (.profile, .bashrc, etc.)
- Git —
.gitconfig, .gitignore_global
- Config — paths under
.config/
- Sensitive — paths under
.ssh/, .gnupg/, or files that have private_ prefix in the chezmoi source directory (check with chezmoi source-path ~/<file>)
- Other — everything else
Display summary table showing each group with file paths and sensitivity flags.
Offer actions using AskUserQuestion:
- Add all — run the shared Add Workflow for every drifted file
- Add selected — present the file list and let the user pick which to add
- Diff — ask which file to diff, then run
chezmoi diff ~/<file> via Bash and display the output
- Skip — do nothing, end the subcommand
Execute chosen action: For each file being added, run the shared Add Workflow (sensitivity scan on the diff → handling → git pipeline). Batch all files into a single commit when adding multiple.
Subcommand: discover
Execution Steps
Locate the skill directory: The skill directory is the parent directory of this SKILL.md file. It contains scripts/filter-unmanaged.sh, references/excludes.yaml, and references/excludes.local.yaml.
Run the filter script via Bash:
<skill-dir>/scripts/filter-unmanaged.sh <skill-dir>/references/excludes.yaml <skill-dir>/references/excludes.local.yaml
Capture the output — one unmanaged file path per line (relative to home directory).
Handle empty results: If no output, report "No unmanaged config files found (after applying exclusion filters)" and stop.
Group findings by top-level directory (e.g., .config/, .cargo/, .ssh/).
Present findings grouped by directory with a count per group.
For each finding, use AskUserQuestion to offer:
- Add — triggers the shared Add Workflow (sensitivity scan on full file content → handling → git pipeline)
- Exclude — append the file's path pattern to
references/excludes.local.yaml using Edit (add to the patterns: list). If the file is in a directory with other excluded siblings, suggest excluding the parent directory pattern (e.g., .some-app/* instead of .some-app/cache.json).
- Skip — move to the next finding
Batch adds: Collect all files the user chose to add, then run the shared Add Workflow for the batch. Commit all together with a summary message.
Subcommand: doctor
Execution Steps
Run chezmoi doctor via Bash:
chezmoi doctor
Parse output for ok, warning, and error lines.
Run chezmoi verify via Bash:
chezmoi verify 2>&1; echo "EXIT:$?"
Exit code 0 means all files match. Non-zero means drift exists. Capture stderr for the list of differing files.
Check 1Password connectivity via Bash:
op account list --format=json 2>/dev/null
Parse for account names. If the command fails, report 1Password CLI is not authenticated.
Check source repo state via Bash:
chezmoi git -- status --porcelain
Empty output means clean. Non-empty means uncommitted changes.
Check remote sync via Bash:
chezmoi git -- fetch --quiet 2>/dev/null
DEFAULT_BRANCH=$(chezmoi git -- rev-parse --abbrev-ref origin/HEAD 2>/dev/null | sed 's|origin/||')
AHEAD=$(chezmoi git -- rev-list --count "origin/${DEFAULT_BRANCH}..HEAD" 2>/dev/null)
BEHIND=$(chezmoi git -- rev-list --count "HEAD..origin/${DEFAULT_BRANCH}" 2>/dev/null)
echo "branch:${DEFAULT_BRANCH} ahead:${AHEAD} behind:${BEHIND}"
Display results as a summary table with pass/warn/fail indicators for each check.
Show recommendations based on findings:
- If verify found drift → suggest
/chezmoi status
- If source repo has uncommitted changes → suggest
chezmoi git -- add . && chezmoi git -- commit
- If local is ahead of remote → suggest
chezmoi git -- push
- If local is behind remote → suggest
chezmoi update
- If 1Password is not authenticated → suggest
eval $(op signin)
Error Handling
Handle these conditions gracefully:
- chezmoi not installed: Check with
command -v chezmoi before any operation. Report and stop.
- Not initialized: If
chezmoi status fails, suggest chezmoi init.
- 1Password CLI not installed: If
op is not found, skip 1Password checks in doctor and skip sensitivity Option 1/2 in the Add Workflow (fall back to standard add with a warning).
- filter-unmanaged.sh not found: If
scripts/filter-unmanaged.sh can't be located, report the error and suggest reinstalling the skill.
- Skill directory is read-only: If Edit fails on
references/excludes.local.yaml, copy it to ~/.config/chezmoi/excludes.local.yaml and use that path for all future writes. Update the filter script invocation to pass the new path as the second argument.
- yq not installed: The filter script handles this internally (grep fallback). No action needed.
- Git push fails: Report the error. Common causes: no remote configured, auth failure. Don't retry.
- Empty chezmoi source directory: If
chezmoi source-path fails, suggest chezmoi init.
Anti-Patterns
- Never read secrets into conversation output: When showing detected secrets during sensitivity scan, always mask them (show first 4 and last 4 characters only, e.g.,
ghp_****...x7Qm)
- Never run
chezmoi add --encrypt: The user uses 1Password templates, not file encryption
- Never use raw
cd + git: Always use chezmoi git -- for git operations
- Never modify
references/excludes.yaml: Only references/excludes.local.yaml gets modified at runtime (user's personal exclusions). If the skill directory is read-only (e.g., installed as a plugin), copy references/excludes.local.yaml to ~/.config/chezmoi/excludes.local.yaml on first write and use that path going forward.
- Never auto-push: Always confirm with the user before running
chezmoi git -- push
- Never create directories manually: Let chezmoi handle directory creation in the source state
- Never skip the sensitivity scan: Even for files that don't trigger path-based heuristics, always scan content before adding
1---2name: chezmoi3description: This skill should be used when the user asks to "check dotfiles", "update dotfiles", "chezmoi status", "find new config", "discover config", "dotfile health check", "chezmoi doctor", "what config files changed", "add config to chezmoi", "sync dotfiles", "manage dotfiles", "dotfile drift", or says /chezmoi. It detects drift in managed dotfiles, discovers unmanaged config files worth tracking, and runs health checks on the chezmoi setup. Supports 1Password template scaffolding for sensitive files.4license: Apache-2.05---67# /chezmoi — Dotfile Management with Chezmoi89Operational dotfile management: detect drift, discover new config, and health-check your chezmoi setup.1011## Tools1213- **Bash** — Run `chezmoi status`, `chezmoi diff`, `chezmoi add`, `chezmoi git --`, `chezmoi doctor`, `chezmoi verify`, `op account list`, and the `scripts/filter-unmanaged.sh` helper script14- **Read** — Read file content for sensitivity scanning, read exclusion YAML files15- **Edit** — Update `references/excludes.local.yaml` when user excludes a file16- **Write** — Create `.tmpl` files in chezmoi source directory for sensitive file templates17- **AskUserQuestion** — Ask which subcommand to run, which files to add/exclude/skip, how to handle sensitive files1819## Arguments2021The skill accepts an optional subcommand:2223- **No argument**: Show an overview of available subcommands and ask the user which to run24- **`status`**: Detect managed files with local drift25- **`discover`**: Find unmanaged config files worth tracking26- **`doctor`**: Run health checks on the chezmoi setup2728## Subcommand Overview (no args)2930When invoked without arguments, present this overview and ask which subcommand to run:3132```33Chezmoi — Dotfile Management3435Available subcommands:3637 /chezmoi status — Check for local changes to managed files that38 haven't been captured back to the source repo yet3940 /chezmoi discover — Find config files in your home directory that41 aren't managed by chezmoi, filtered to reduce noise4243 /chezmoi doctor — Health check: chezmoi doctor, verify, 1Password44 connectivity, and source repo sync state4546Which would you like to run?47```4849Use **AskUserQuestion** to let the user pick a subcommand.5051---5253## Shared: Add Workflow5455This workflow is used by both `status` and `discover` whenever a file needs to be added to chezmoi. It handles sensitivity detection, 1Password template scaffolding, and the git pipeline.5657### Step 1: Sensitivity Scan5859Before adding any file, scan it for secrets.6061**For drifted files** (from `status`): run `chezmoi diff <file>` via Bash and scan the diff output for secret values.6263**For new files** (from `discover`): read the full file content via Read and scan for secret values.6465**Content-based detection** (always applied):66- Long random strings (high entropy, 20+ characters)67- Base64-encoded blobs68- Values adjacent to keys named `token`, `key`, `password`, `secret`, `auth`, `credential`, `api_key`, `apikey`, `access_token` in structured formats (JSON, YAML, TOML, XML, INI, env files)6970**Path-based detection** (for new files from discover):71- Path contains: `ssh`, `gnupg`, `gpg`, `kube`, `aws`, `credentials`72- Filename contains: `secret`, `credential`, `token`, `key`, `password`, `auth`, `private`73- File permissions: check with `stat -f '%Lp' <file>` on macOS — mode 600 or more restrictive7475If no secrets are detected, proceed directly to Step 3 (Git Pipeline).7677### Step 2: Sensitive File Handling7879When secrets are detected, use **AskUserQuestion** to ask the user:8081```82This file appears to contain sensitive data. How should it be handled?83 1. Contains individual secrets (API keys, tokens) → Template with onepasswordRead84 2. Entire file is a secret (key, certificate) → Template with onepasswordDocument85 3. Just needs restricted permissions → Standard chezmoi add86 4. Not sensitive, false positive → Standard chezmoi add87```8889#### Option 1: Template with `onepasswordRead`9091For config files containing embedded secrets:92931. Read the file content942. Identify which values are secrets953. Generate a `.tmpl` version of the file. Replace each secret with a `{{ onepasswordRead "op://Vault/Item/Field" }}` placeholder. Keep all non-secret content exactly as-is.964. Present the template to the user, listing each placeholder with its detected secret value (masked: show first 4 and last 4 characters only)975. Ask the user to:98 - Store the secrets in 1Password (if not already there)99 - Provide the actual `op://` URI for each placeholder1006. Update the template with the real URIs1017. Determine the correct chezmoi source path:102 - Run `chezmoi source-path` to find the source directory103 - Convert the target path to chezmoi naming: replace leading `.` with `dot_`, use `private_` prefix for files with 600 permissions, add `.tmpl` suffix104 - Look at existing files in the source directory for naming convention reference1058. Write the template file to the source directory using Write1069. Proceed to Step 3107108#### Option 2: Template with `onepasswordDocument`109110For whole-file secrets (SSH private keys, certificates, PEM files):1111121. Tell the user: "This file should be stored as a 1Password document. Please upload it to 1Password and provide the document UUID."1132. Wait for the user to provide the UUID1143. Create the template content: `{{- onepasswordDocument "<UUID>" }}`1154. Determine the correct source path (same logic as Option 1, with `private_` or `private_readonly_` prefix as appropriate)1165. Write the template file to the source directory using Write1176. Proceed to Step 3118119#### Options 3 & 4: Standard add120121Run via Bash:122```bash123chezmoi add ~/<file>124```125126Chezmoi automatically detects file permissions and applies the `private_` prefix for 0600 files. Proceed to Step 3.127128### Step 3: Git Pipeline129130Run all git operations via Bash using chezmoi's native git integration:1311321. Stage: `chezmoi git -- add .`1332. Commit: `chezmoi git -- commit -m "<message>"`134 - Generate a descriptive commit message from the files being added (e.g., "Add fish shell config and fzf bindings" or "Update zed editor settings")135 - For multiple files, summarize what changed rather than listing every file1363. Confirm with user before push (use **AskUserQuestion**: "Push to remote? [yes/no]")1374. Push: `chezmoi git -- push`138139For multiple files added together, batch them into a single commit with a summary message.140141---142143## Subcommand: `status`144145### Execution Steps1461471. **Detect drift**: Run via Bash:148 ```bash149 chezmoi status150 ```151 Parse the output. Each line has a two-character status code and a file path (e.g., `MM .gitconfig`).1521532. **Handle empty results**: If no output, report "All managed files are in sync" and stop.1541553. **Group findings** by category based on the file path:156 - **Shell** — paths containing `fish`, `bash`, `zsh`, or shell config files (`.profile`, `.bashrc`, etc.)157 - **Git** — `.gitconfig`, `.gitignore_global`158 - **Config** — paths under `.config/`159 - **Sensitive** — paths under `.ssh/`, `.gnupg/`, or files that have `private_` prefix in the chezmoi source directory (check with `chezmoi source-path ~/<file>`)160 - **Other** — everything else1611624. **Display summary table** showing each group with file paths and sensitivity flags.1631645. **Offer actions** using **AskUserQuestion**:165 - **Add all** — run the shared Add Workflow for every drifted file166 - **Add selected** — present the file list and let the user pick which to add167 - **Diff** — ask which file to diff, then run `chezmoi diff ~/<file>` via Bash and display the output168 - **Skip** — do nothing, end the subcommand1691706. **Execute chosen action**: For each file being added, run the shared Add Workflow (sensitivity scan on the diff → handling → git pipeline). Batch all files into a single commit when adding multiple.171172---173174## Subcommand: `discover`175176### Execution Steps1771781. **Locate the skill directory**: The skill directory is the parent directory of this SKILL.md file. It contains `scripts/filter-unmanaged.sh`, `references/excludes.yaml`, and `references/excludes.local.yaml`.1791802. **Run the filter script** via Bash:181 ```bash182 <skill-dir>/scripts/filter-unmanaged.sh <skill-dir>/references/excludes.yaml <skill-dir>/references/excludes.local.yaml183 ```184 Capture the output — one unmanaged file path per line (relative to home directory).1851863. **Handle empty results**: If no output, report "No unmanaged config files found (after applying exclusion filters)" and stop.1871884. **Group findings** by top-level directory (e.g., `.config/`, `.cargo/`, `.ssh/`).1891905. **Present findings** grouped by directory with a count per group.1911926. **For each finding**, use **AskUserQuestion** to offer:193 - **Add** — triggers the shared Add Workflow (sensitivity scan on full file content → handling → git pipeline)194 - **Exclude** — append the file's path pattern to `references/excludes.local.yaml` using Edit (add to the `patterns:` list). If the file is in a directory with other excluded siblings, suggest excluding the parent directory pattern (e.g., `.some-app/*` instead of `.some-app/cache.json`).195 - **Skip** — move to the next finding1961977. **Batch adds**: Collect all files the user chose to add, then run the shared Add Workflow for the batch. Commit all together with a summary message.198199---200201## Subcommand: `doctor`202203### Execution Steps2042051. **Run chezmoi doctor** via Bash:206 ```bash207 chezmoi doctor208 ```209 Parse output for `ok`, `warning`, and `error` lines.2102112. **Run chezmoi verify** via Bash:212 ```bash213 chezmoi verify 2>&1; echo "EXIT:$?"214 ```215 Exit code 0 means all files match. Non-zero means drift exists. Capture stderr for the list of differing files.2162173. **Check 1Password connectivity** via Bash:218 ```bash219 op account list --format=json 2>/dev/null220 ```221 Parse for account names. If the command fails, report 1Password CLI is not authenticated.2222234. **Check source repo state** via Bash:224 ```bash225 chezmoi git -- status --porcelain226 ```227 Empty output means clean. Non-empty means uncommitted changes.2282295. **Check remote sync** via Bash:230 ```bash231 chezmoi git -- fetch --quiet 2>/dev/null232 DEFAULT_BRANCH=$(chezmoi git -- rev-parse --abbrev-ref origin/HEAD 2>/dev/null | sed 's|origin/||')233 AHEAD=$(chezmoi git -- rev-list --count "origin/${DEFAULT_BRANCH}..HEAD" 2>/dev/null)234 BEHIND=$(chezmoi git -- rev-list --count "HEAD..origin/${DEFAULT_BRANCH}" 2>/dev/null)235 echo "branch:${DEFAULT_BRANCH} ahead:${AHEAD} behind:${BEHIND}"236 ```2372386. **Display results** as a summary table with pass/warn/fail indicators for each check.2392407. **Show recommendations** based on findings:241 - If verify found drift → suggest `/chezmoi status`242 - If source repo has uncommitted changes → suggest `chezmoi git -- add . && chezmoi git -- commit`243 - If local is ahead of remote → suggest `chezmoi git -- push`244 - If local is behind remote → suggest `chezmoi update`245 - If 1Password is not authenticated → suggest `eval $(op signin)`246247---248249## Error Handling250251Handle these conditions gracefully:252- **chezmoi not installed**: Check with `command -v chezmoi` before any operation. Report and stop.253- **Not initialized**: If `chezmoi status` fails, suggest `chezmoi init`.254- **1Password CLI not installed**: If `op` is not found, skip 1Password checks in doctor and skip sensitivity Option 1/2 in the Add Workflow (fall back to standard add with a warning).255- **filter-unmanaged.sh not found**: If `scripts/filter-unmanaged.sh` can't be located, report the error and suggest reinstalling the skill.256- **Skill directory is read-only**: If Edit fails on `references/excludes.local.yaml`, copy it to `~/.config/chezmoi/excludes.local.yaml` and use that path for all future writes. Update the filter script invocation to pass the new path as the second argument.257- **yq not installed**: The filter script handles this internally (grep fallback). No action needed.258- **Git push fails**: Report the error. Common causes: no remote configured, auth failure. Don't retry.259- **Empty chezmoi source directory**: If `chezmoi source-path` fails, suggest `chezmoi init`.260261## Anti-Patterns262263- **Never read secrets into conversation output**: When showing detected secrets during sensitivity scan, always mask them (show first 4 and last 4 characters only, e.g., `ghp_****...x7Qm`)264- **Never run `chezmoi add --encrypt`**: The user uses 1Password templates, not file encryption265- **Never use raw `cd` + `git`**: Always use `chezmoi git --` for git operations266- **Never modify `references/excludes.yaml`**: Only `references/excludes.local.yaml` gets modified at runtime (user's personal exclusions). If the skill directory is read-only (e.g., installed as a plugin), copy `references/excludes.local.yaml` to `~/.config/chezmoi/excludes.local.yaml` on first write and use that path going forward.267- **Never auto-push**: Always confirm with the user before running `chezmoi git -- push`268- **Never create directories manually**: Let chezmoi handle directory creation in the source state269- **Never skip the sensitivity scan**: Even for files that don't trigger path-based heuristics, always scan content before adding