skills/AuditDotfiles/SKILL.md
Scan a dotfiles tree for secrets via gitleaks, aggregate findings by top-level directory and rule, then surgically filter flagged lines from shell-history files before importing into atuin. USE WHEN auditing dotfiles before pushing to a public repo, scanning rsynced dotfiles-private contents, importing legacy zsh or bash history into atuin, filtering credential leaks out of a shell history file, deciding which dotfile subdirectories can be made public.
npx skillsauth add n4m3z/forge-dev AuditDotfilesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scan a dotfiles tree (typically dotfiles/ and dotfiles-private/) for secrets with gitleaks, group findings by top-level directory and rule so the right disposition is obvious, and produce a clean version of any flagged shell-history file by stripping just the leaked lines (not the whole file).
The executable end-to-end pipeline lives at scripts/audit-dotfiles.sh in this skill directory. Run it for the actual audit; the body below documents intent and the reasoning behind each step. Related: SecretScan in forge-core covers commit-time prevention; this skill covers migration-time audit + history filtering.
Scan with gitleaks, redacted, into a JSON report:
gitleaks detect --no-banner --redact --no-git -s <path> \
--report-format json --report-path <out>.json
--redact masks matched secrets inside the report. --no-git scans the filesystem rather than git history — faster for one-shot audits.
Aggregate findings by top-level dir + rule. The breakdown maps directly to disposition decisions (much more useful than the raw count):
yq -p json -o tsv '.[] | [.File, .RuleID]' <out>.json \
| awk -F'\t' -v prefix="<scan-root>/" '
{ sub("^" prefix, "", $1)
split($1, parts, "/")
print parts[1] "\t" $2
}' \
| sort | uniq -c
Expected shape: gnupg/ shows private-key (the GPG export), kube/ shows jwt (bearer tokens), and the shell-history directory typically shows accumulated generic-api-key, github-pat, stripe-access-token matches from curl -H Authorization: and export STRIPE_KEY=... invocations.
Filter the history file when gitleaks flags a shell history (.zsh_history, .zhistory, .bash_history, .history). One yq + awk pass groups per-file findings and builds a sed delete-list (Nd;Md;...), then runs sed once per history file:
yq -p json -o tsv '.[] | [.File, .StartLine]' <out>.json \
| awk -F'\t' '
$1 ~ /(history|zhistory)$/ {
key = $1 "\t" $2
if (!seen[key]++) exprs[$1] = (exprs[$1] ? exprs[$1]";" : "") $2 "d"
}
END { for (f in exprs) print f "\t" exprs[f] }' \
| while IFS=$'\t' read -r hist expr; do
sed -e "${expr}" "${hist}" > "${hist}.clean"
done
Then import the clean file into atuin:
atuin import <shell> <history-file>.clean
Do not pre-split && chains before import. atuin's fulltext search matches substrings against the whole command, so typing cmd2 recalls cmd1 && cmd2 && cmd3. Splitting strips the meaningful preceding context (the cd dir that prefixed the action).
Decide per-dir disposition from the aggregated table:
| Finding shape | Action |
| ---------------------------------------------- | -------------------------------------------------------------------------------------- |
| gnupg/ private-key matches | Keep .asc exports as offline archive; do not bulk-import into the new GPG keychain |
| kube/ jwt matches | Rotate bearer tokens at next cluster touch; keep current file private until rotated |
| shell-history matches (multiple rule types) | Filter and import per step 3; preserve the rest of the file |
| password-store/ (no gitleaks matches) | Encrypted; filenames are the secret names — do not enumerate carelessly |
| *.gitconfig_local token matches | Diff against current git config --global; merge live config, drop stale tokens |
| ssh/hosts, ssh/vendor/* (no token matches) | Contain hostnames + IPs; treat as fully sensitive even when gitleaks finds nothing |
Rotate any live credentials that gitleaks flagged before the cleaned dotfiles tree migrates into a less-private context (chezmoi source, public push, agent training corpus).
--redact, the file path and line number can identify the secret in context. Report aggregated counts + categories only.--verify during this audit. Verification mode contacts the issuing service with the candidate token, which leaks the existence of the leak even when the token is benign. gitleaks is offline-only by design.&& chains carry semantic context that splitting destroys.curl command containing both an API key and a stripe token). The awk pass in step 3 deduplicates via the seen[] table.--no-git skips git history. When the rsynced tree contains a .git/ directory and historical secrets matter, drop --no-git and accept the slower scan.tools
Server-rendered web dashboards and apps in Rust using axum + htmx + Askama + rust-embed. USE WHEN building a web dashboard, adding a web UI to a CLI tool, server-rendered HTML, htmx partials, Askama templates, axum routes, embedded static assets, localhost webserver.
tools
Architecture for security and health-check programs: standalone-runnable checks, severity ladder with UNKNOWN, key:value output contract, orchestrator dispatch, exit-code semantics. Language-agnostic; reference implementations in Bash, applies to Python and other languages. USE WHEN writing health checks or audit tools, designing check-script contracts, adding checks to tools like check-mac, reviewing health-check architecture, or porting a check tool between languages.
development
Test-driven development practices — Red-Green-Refactor cycle, test categories, coverage strategy, property-based testing. USE WHEN writing tests, designing testable APIs, or reviewing test coverage.
development
Four-phase debugging methodology — root cause before fixes. USE WHEN encountering any bug, test failure, unexpected behavior, or build failure.