Files
fleet-dotfiles-template/AGENTS.md
T

11 KiB

AGENTS.md — agent-led setup instructions

For Claude Code, Codex, Aider, or any other coding agent dropped into this repo. Follow this file top-to-bottom. It walks the user through replacing every placeholder with real values. Treat each numbered section as one Q&A round: ask the listed questions, wait for answers, fill in the files, then move on.

Background reading you may need mid-flow

If you hit a chezmoi-specific question you can't answer from this file alone, consult the live docs:

  • chezmoi homepage — overview, install, quick start.
  • chezmoi command reference — every subcommand (init, add, apply, diff, update, re-add, edit-config, cd, source-path, execute-template).
  • chezmoi templates — the {{ .chezmoi.homeDir }} and {{ .chezmoi.hostname }} syntax used throughout this template.
  • chezmoi encryption — how chezmoi add --encrypt works with age.
  • age homepage — keypair format, age-keygen, age and age -d commands.
  • Apple launchd guide — if a plist fails to load, this is the reference for launchctl load/unload/list.

If the user asks about cross-fleet time estimates, the companion repo is task-durations (separate; install instructions in this template's README).

Operating rules for this setup session

  1. One question at a time. Don't fire all questions in a giant wall of text. Ask one, wait for the answer, ask the next.
  2. Confirm before destructive actions. Generating a new SSH keypair, generating a new age keypair, force-pushing — always confirm. Show what you're about to do; let the user say yes/no.
  3. Never paste secrets back to the user in plaintext. When the user gives you an API token, treat it like radioactive material: write it to the right file, then forget it. Don't echo it in a status message, don't put it in a commit, don't show it in a cat of the file.
  4. Fail loud on missing prerequisites. If chezmoi, age, or duckdb isn't installed, stop and tell the user the brew command. Don't try to proceed.
  5. Verify before claiming. When a step is "done", run a check that proves it: a chezmoi diff, a file existence test, a token round-trip. Tell the user what you verified.
  6. Don't commit secrets. Verify with git status before any commit that no .env, no unencrypted secret, no SSH private key is staged. If chezmoi's autoCommit fires, double-check the diff.

Section 0 — Prerequisites check

Run silently, only surface findings to the user:

command -v chezmoi >/dev/null && chezmoi --version
command -v age >/dev/null && age --version
command -v age-keygen >/dev/null
command -v duckdb >/dev/null && duckdb --version
command -v git >/dev/null && git --version

If any are missing, tell the user the install command (brew install chezmoi age duckdb) and stop until they confirm.

Section 1 — Identify the user

Ask:

Q1: What name and email should I use for git commits on this fleet? (e.g., Alice Smith <alice@example.com>)

Take their answer and write to dot_gitconfig (replacing the examples/gitconfig.example placeholders). Don't render the file yet — just stage the chezmoi source.

Section 2 — Generate or reuse the age keypair

Ask:

Q2: Do you already have an age keypair you want to use for this fleet? (yes / no)

If yes: where is the private key file? (default: ~/.config/chezmoi/key.txt) If no: I'll generate a new one. Confirm before I create the file.

If generating new:

mkdir -p ~/.config/chezmoi
age-keygen -o ~/.config/chezmoi/key.txt
chmod 600 ~/.config/chezmoi/key.txt

Then extract the public key (it's printed to stderr by age-keygen, and on the first commented line of the file). Show ONLY the public key to the user.

Edit .chezmoi.toml.tmpl: replace REPLACE_ME_WITH_YOUR_AGE_PUBLIC_KEY with the public key.

Verify: chezmoi execute-template '{{ (index .age "recipient") }}' should print the new public key.

Section 3 — Identify this machine and the fleet

Ask:

Q3: What's the hostname of this machine? (run hostname -s if you need a default)

Q4: What's the username on this machine? (run whoami for the default)

Q5: How many other machines will be in this fleet, and what are their hostnames + usernames?

Generate this machine's identity SSH key if missing:

[ -f ~/.ssh/id_ed25519 ] || ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""

Read the public key from ~/.ssh/id_ed25519.pub and capture it.

Build .chezmoidata/fleet.yaml from .chezmoidata/fleet.yaml.example, replacing the placeholder entries with the machines the user named. For now, leave the OTHER machines' pubkeys as placeholders — they'll get filled in when those machines run their own onboarding (Section 9 covers this).

Section 4 — SSH config for the fleet

Use examples/ssh-config.tmpl.example as a starting point. Render it for this fleet by:

  • Replacing laptop1 / laptop2 / desktop placeholders with the real hostnames from Q5.
  • Replacing <USERNAME_FOR_*> with the real usernames from Q5.

Save to private_dot_ssh/config.tmpl in the chezmoi source.

Verify: chezmoi execute-template < private_dot_ssh/config.tmpl renders cleanly with no {{ literals leaking through.

Section 5 — Decide on a forge

Ask:

Q6: Where will this fleet's git remote live? (gitea, github, forgejo, gitlab, self-hosted bare repo) If on a service that requires auth, how do you authenticate? (SSH key in agent, HTTPS PAT in keychain, gh CLI, etc.)

Verify the user can git push to that forge from this machine. If they're on a private gitea (like gitea.tojo.team), confirm they have a PAT in their environment or a credential helper configured.

Section 6 — Secrets

Ask:

Q7: Which API tokens do you want to manage via the fleet's encrypted secrets file? The default list (in examples/secrets.env.example) covers Cloudflare, Porkbun, Tailscale, HuggingFace, OpenAI/Anthropic/Gemini, and the forge token. You can add or remove freely.

For each token the user wants:

  • Ask for it ONE AT A TIME.
  • Append it to a temp file at ~/.config/fleet-dotfiles/secrets.env (create the dir + chmod 600 the file FIRST).
  • Don't echo the value.

When all tokens are entered:

chmod 600 ~/.config/fleet-dotfiles/secrets.env
chezmoi add --encrypt ~/.config/fleet-dotfiles/secrets.env

Verify: ls $(chezmoi source-path)/dot_config/private_fleet-dotfiles/ should now show encrypted_private_secrets.env.age (the encrypted source file). The original ~/.config/fleet-dotfiles/secrets.env stays unencrypted on disk for runtime use; never commit it directly.

Sanity check: git status in the chezmoi source dir must NOT show any unencrypted secret file staged.

Section 7 — CLAUDE.md

Ask:

Q8: I've copied a generic CLAUDE.md skeleton from examples/CLAUDE.md.example to dot_claude/CLAUDE.md. Do you want me to add machine-specific or project-specific sections on top? (e.g., your servers, your common commands, your workflow rules)

If yes, ask what sections, write them.

If no, leave the skeleton — they can add later.

Verify: chezmoi diff dot_claude/CLAUDE.md shows the file ready to apply.

Section 8 — First apply

Show the user chezmoi diff --no-pager summary (file count + paths, NOT contents — the diff might leak secret-shaped strings). Confirm:

Q9: Ready to materialize all of this to the live filesystem? (yes / no)

On yes:

chezmoi apply

Then verify the launchd jobs registered:

launchctl list | grep -E "chezmoi|taskdurations"

You should see three labels:

  • com.chezmoi.claude-watcher
  • com.chezmoi.claude-puller
  • com.taskdurations.pull-fleet

If any are missing, tell the user to run chezmoi apply --force ~/Library/LaunchAgents/ and reload manually:

for plist in ~/Library/LaunchAgents/com.{chezmoi,taskdurations}.*.plist; do
  launchctl unload "$plist" 2>/dev/null
  launchctl load "$plist"
done

Section 9 — Push to the forge

Initial commit + push:

cd $(chezmoi source-path)
git remote -v   # confirm it points at the user's forge
git status      # confirm no unencrypted secrets are staged
git add -A
git commit -m "Initial fleet dotfiles for $(hostname -s)"
git push -u origin main

Final verify: Open the forge URL in a browser. Confirm examples/, dot_local/bin/..., private_Library/LaunchAgents/... are all present. Confirm dot_config/private_fleet-dotfiles/encrypted_private_secrets.env.age is the encrypted form (.age extension), not a plaintext .env.

Section 10 — Onboarding additional machines

Tell the user:

Each additional fleet machine repeats Sections 0, 2 (reuse the SAME age private key — copy it via secure channel; do NOT push it to git), 3 (generate that machine's identity SSH key), and then runs chezmoi init https://<forge>/<user>/<repo>.git and chezmoi apply.

The age public key in .chezmoi.toml.tmpl doesn't need changing — it's the same recipient for the whole fleet.

After the new machine applies, edit .chezmoidata/fleet.yaml to add its real pubkey. The watcher commits the change; within 7 min, every other fleet machine has it in authorized_keys.

Walk them through the first additional machine if they're available.

Done — what to leave the user with

  • A running fleet sync on this machine.
  • A ~/.local/bin/chezmoi-auto-sync.sh that the watcher will call on every change to a managed path.
  • A ~/.config/fleet-dotfiles/secrets.env (cleartext on disk, encrypted in source) sourced by .zshrc on shell start.
  • The full Claude Code agentic team available at ~/.claude/agents/agentic-team/, plus /lite and /lite-sub slash commands.
  • A pointer to the task-durations repo (separate) if they want fleet-wide time estimates.
  • A note that any subsequent machine they want to add: see docs/add-machine.md.

What to NEVER do during this setup

  • Never echo a secret back to the user in plaintext (not in chat, not in a log, not in a commit message).
  • Never run git push --force without explicit user confirmation.
  • Never delete the user's existing ~/.ssh/id_ed25519 if they already have one — work with what's there.
  • Never commit ~/.config/chezmoi/key.txt (it's the age private key — losing it locks them out of every encrypted file in the repo).
  • Never overwrite ~/.zshrc without showing a diff first; users often have hand-tuned aliases there.
  • Never assume the user wants every example file — confirm each section before applying.