Claude
Installs the Claude Code CLI and configures settings.
Demo

What it does
- Auto-installs and updates Claude Code CLI to the latest version from npm
- Configures
~/.claude/settings.jsonwith merged settings - Optionally configures
~/.claude.json - Deploys a global
~/.claude/CLAUDE.md - Materializes the selected Agent Skills (
claude_code_skills, from the agents role's synced sources) into~/.claude/skills - Installs shell aliases (one per model)
- Sets up per-shell multi-account isolation (
claude_code_accounts, selected withclaude-account) - Deploys a zero-dependency status line script (jq + git only)
Variables
| Variable | Default | Description |
|---|---|---|
claude_code_settings | effortLevel: max, custom spinner verbs | Settings merged into claude_code_settings_path |
claude_code_config | {} | Settings merged into claude_code_config_path |
claude_code_keybindings | Chat ctrl+enter submit, shift+enter newline | Keybindings written to claude_code_keybindings_path |
claude_aliases | fable, opus, sonnet, haiku | Shell aliases rendered to ~/.profile.d/aliases/claude.sh |
claude_code_accounts | [] | Named accounts for per-shell account selection; each gets ~/.claude/accounts/<name> as its CLAUDE_CONFIG_DIR with the role-managed config deployed into it |
claude_code_settings_path | ~/.claude/settings.json | Destination for the merged settings; write-through if it is a symlink |
claude_code_config_path | ~/.claude.json | Destination for the merged config; write-through if it is a symlink |
claude_code_keybindings_path | ~/.claude/keybindings.json | Destination for the keybindings; write-through if it is a symlink |
claude_code_claudemd_path | ~/.claude/CLAUDE.md | Destination for the global CLAUDE.md; write-through if it is a symlink |
claude_code_skills | | The skills materialized into the skills path: git source URL mapped to all or a list of skill names; defaults to the whole machine inventory the agents role syncs |
claude_code_skills_path | ~/.claude/skills | Where the selected skills are copied, Claude Code's personal skills discovery path |
claude_code_statusline_path | ~/.claude/statusline.sh | Destination for the status line script; also the statusLine.command default |
claude_code_statusline_deploy | true | Deploy the bundled script; set false to manage your own |
claude_code_statusline_refresh_interval | 5 | Seconds between timer-based refreshes (statusLine.refreshInterval), on top of the event triggers (new message, /compact, permission/vim-mode change), so idle git changes (e.g. from background agents in a worktree) still show. Set null/empty for event-driven only, or another positive whole number (min 1) |
claude_code_statusline_separator | | | What the sections are joined with; every section carries an icon (the folder glyph and mono variants need a Nerd Font, the nerdfonts role) |
claude_code_statusline_sections | [location, git, session, usage] | The sections shown and their order; session is the model/cost/context of this Claude Code session, usage shows the 5-hour/7-day subscription windows, time left on the 5-hour and reset moment on the 7-day (Pro/Max, appears after the first response) |
claude_code_statusline_color | true | ANSI colour: dim decorations, cost/context by threshold, branch by cleanliness, green/red +/- line counts |
claude_code_statusline_progress_style | both | Percentage indicators (context, usage) as a gapless █▓░░ mini progress bar (cells fill through ░ ▒ ▓ █) with the percent (█▓░░ 42%), or bar / percent (62%) alone |
claude_code_statusline_progress_bar_width | 4 | Progress bar width in cells |
claude_code_statusline_location_style | full | full (~/projects/dotfiles) or short (fish-style ~/p/dotfiles) |
claude_code_statusline_compact_reserve | 33000 | Tokens Claude Code reserves below the window before a forced auto-compact (summary + safety buffer); the compact point is context_window_size - reserve, so it tracks the model (~83% of a 200k window, ~97% of a 1M one) instead of a flat percent. Honors CLAUDE_AUTOCOMPACT_PCT_OVERRIDE / CLAUDE_CODE_AUTO_COMPACT_WINDOW; 0 hides the marker |
claude_code_statusline_compact_lead | 20 | Context-window percent before the compact point at which the marker starts, counting the headroom down (♻️ 20% … ♻️ 0% at the point); 0 shows only at the point |
claude_code_statusline_thresholds | cost 2/10, context 70/90, usage 70/90 | Where the session cost ($), context usage (%), and subscription windows (%) turn yellow (*_warn) and red (*_high) |
claude_code_statusline_icon_style | emoji | Icon style: emoji (🦊 🌱) or mono (Nerd Font: ). Every icon value may be one string (both styles) or an {emoji, mono} dict following this selector |
claude_code_statusline_icons | dir , git 🌱/, worktree 🪴/, model ✳/, usage ⏳/, compact ♻️/, compact-active ✨/ | Section icons and the model fallback; in a linked worktree the worktree icon replaces git in front of the branch (the 🌱 sprout, potted 🪴) |
claude_code_statusline_model_icons | fable 🦊/, opus 🎼/, sonnet 🖋/, haiku 🌸/ | Icon before the model name to identify it at a glance; first key contained in the model name wins, unmatched models get the model fallback icon |
Config (defaults)
This is the default application config the role ships for Claude Code, merged into ~/.claude/settings.json. With hash_behaviour = merge, your claude_code_settings overrides deep-merge onto these defaults, so you change only the keys you set. The full set of keys is documented in the Claude Code settings reference:
| Setting | Value | Effect |
|---|---|---|
effortLevel | max | Run at the highest reasoning effort |
permissions.defaultMode | auto | Auto-approve allowed tools without prompting |
permissions.allow | low-risk command allowlist | Pre-approves search/read tools (git, rg, cat, …), read-only package, forge, container, cluster, and cloud queries (apt list, gh pr view, docker ps, kubectl get, aws … list-*, …), read-only terraform/tofu subcommands, and the collection's own CLIs (worktree, git-statuses, dotpro versions) |
permissions.ask | destructive arg forms, infra mutations, expensive subagents | Prompt-first carve-outs from the allows: force pushes, find/fd exec and delete modes, yq -i, cluster/cloud/vault mutations, dotpro apply, and subagent spawns explicitly picking fable or opus |
permissions.deny | secrets + infra mutations | Blocks reads and writes of credential files (SSH/GPG keys, cloud creds, tokens, vaults, shell history) and terraform/tofu apply/destroy |
attribution.commit / .pr | "" | Drops the Claude co-author trailer from commits and PRs |
feedbackSurveyRate | 0 | Turns off the session feedback survey |
env | CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY, DISABLE_TELEMETRY, DISABLE_ERROR_REPORTING → "1" | Disables the survey, telemetry, and error reporting; autoupdates stay on |
spinnerVerbs | replace → [Processing] | Replaces the default spinner verbs |
statusLine | command → statusline.sh | Renders location, session (model, cost), git (branch, changes) in the status line |
hooks.Notification | ~/scripts/notify on the attention events | Pings when a session needs you — idle_prompt (waiting for your input), permission_prompt (approval), agent_needs_input (a background agent) — through the bash role's notifier (bell + OSC 9, a Windows toast on WSL, and an OSC 9;4 ring on the pinging tab in Windows Terminal), so a tab you've switched away from flags itself. The ring is coloured by reason — yellow for a blocking approval, green for a session merely waiting. Not on every turn end, and guarded so a host without the script no-ops |
hooks.UserPromptSubmit / hooks.PostToolUse | ~/scripts/notify --clear | Clears the Windows Terminal tab ring once you're back — on your next prompt (the only signal for a green idle ring) and on the next tool running (which catches an approval, granted in the TUI without a prompt submit). The prompt submit also carries --capture, recording which WT window hosts the session (typing here proves it's foreground) so a later toast is clickable back to that window and tab. No-op off WT; guarded like the Notification hooks |
hooks.WorktreeCreate | ~/scripts/worktree hook (60s) | Creates Claude's worktrees under <repo>/.worktrees/ (the git role's helper, shared with the worktree command) instead of .claude/worktrees/, copying the repo's wt.copy files and running its wt.setup command |
hooks.WorktreeRemove | ~/scripts/worktree hook | Removes the agent worktree and deletes its branch when merged (unmerged work keeps its branch) |
Rendered, the defaults produce this ~/.claude/settings.json:
{
"attribution": {
"commit": "",
"pr": ""
},
"effortLevel": "max",
"feedbackSurveyRate": 0,
"env": {
"CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY": "1",
"DISABLE_TELEMETRY": "1",
"DISABLE_ERROR_REPORTING": "1"
},
"spinnerVerbs": {
"mode": "replace",
"verbs": ["Processing"]
},
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh",
"padding": 0,
"refreshInterval": 5
},
"hooks": {
"Notification": [
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "[ -x ~/scripts/notify ] && ~/scripts/notify \"Claude Code\" \"Waiting for your input\" ready || true"
}
]
},
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "[ -x ~/scripts/notify ] && ~/scripts/notify \"Claude Code\" \"Needs your approval\" warn || true"
}
]
},
{
"matcher": "agent_needs_input",
"hooks": [
{
"type": "command",
"command": "[ -x ~/scripts/notify ] && ~/scripts/notify \"Claude Code\" \"Background agent needs input\" ready || true"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "[ -x ~/scripts/notify ] && ~/scripts/notify --clear --capture || true"
}
]
}
],
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "[ -x ~/scripts/notify ] && ~/scripts/notify --clear || true"
}
]
}
],
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "~/scripts/worktree hook",
"timeout": 60
}
]
}
],
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "~/scripts/worktree hook"
}
]
}
]
},
"permissions": {
"defaultMode": "auto",
"allow": [
"Bash(git *)",
"Bash(ls *)",
"Bash(find *)",
"Bash(grep *)",
"Bash(rg *)",
"Bash(fd *)",
"Bash(ast-grep *)",
"Bash(tree *)",
"Bash(shellcheck *)",
"Bash(shfmt -d *)",
"Bash(difft *)",
"Bash(cat *)",
"Bash(head *)",
"Bash(tail *)",
"Bash(wc *)",
"Bash(jq *)",
"Bash(yq *)",
"Bash(which *)",
"Bash(echo *)",
"Bash(date *)",
"Bash(stat *)",
"Bash(diff *)",
"Bash(uname*)",
"Bash(df *)",
"Bash(du *)",
"Bash(ps *)",
"Bash(dig *)",
"Bash(scc*)",
"Bash(worktree *)",
"Bash(git-statuses*)",
"Bash(dotpro versions*)",
"Bash(dotpro logs*)",
"Bash(ansible-lint *)",
"Bash(ansible-doc *)",
"Bash(ansible-playbook --check *)",
"Bash(ansible-playbook * --check*)",
"Bash(ansible-playbook --syntax-check *)",
"Bash(ansible-playbook * --syntax-check*)",
"Bash(ansible-playbook --list-tasks *)",
"Bash(ansible-playbook * --list-tasks*)",
"Bash(apt list*)",
"Bash(apt show *)",
"Bash(apt-cache *)",
"Bash(dpkg -l*)",
"Bash(dpkg -L *)",
"Bash(dpkg -s *)",
"Bash(snap list*)",
"Bash(brew list*)",
"Bash(brew info *)",
"Bash(brew outdated*)",
"Bash(pip3 list*)",
"Bash(pip3 show *)",
"Bash(uv tree*)",
"Bash(uv pip list*)",
"Bash(npm ls*)",
"Bash(npm view *)",
"Bash(npm outdated*)",
"Bash(go version*)",
"Bash(go env*)",
"Bash(go list *)",
"Bash(cargo tree*)",
"Bash(cargo metadata*)",
"Bash(gh pr list*)",
"Bash(gh pr view*)",
"Bash(gh pr diff*)",
"Bash(gh pr checks*)",
"Bash(gh issue list*)",
"Bash(gh issue view*)",
"Bash(gh run list*)",
"Bash(gh run view*)",
"Bash(gh release list*)",
"Bash(gh release view*)",
"Bash(gh repo view*)",
"Bash(gh search *)",
"Bash(glab mr list*)",
"Bash(glab mr view*)",
"Bash(glab mr diff*)",
"Bash(glab issue list*)",
"Bash(glab issue view*)",
"Bash(glab ci list*)",
"Bash(glab ci view*)",
"Bash(docker ps*)",
"Bash(docker images*)",
"Bash(docker logs *)",
"Bash(docker inspect *)",
"Bash(docker version*)",
"Bash(docker compose ps*)",
"Bash(docker compose logs*)",
"Bash(docker compose config*)",
"Bash(colima status*)",
"Bash(colima list*)",
"Bash(kubectl get *)",
"Bash(kubectl describe *)",
"Bash(kubectl logs *)",
"Bash(kubectl explain *)",
"Bash(kubectl diff *)",
"Bash(kubectl version*)",
"Bash(kubectl config get-contexts*)",
"Bash(kubectl config current-context*)",
"Bash(helm list*)",
"Bash(helm status *)",
"Bash(helm history *)",
"Bash(helm get *)",
"Bash(helm show *)",
"Bash(helm template *)",
"Bash(helm diff *)",
"Bash(helm version*)",
"Bash(aws sts get-caller-identity*)",
"Bash(aws * describe-*)",
"Bash(aws * list-*)",
"Bash(aws s3 ls*)",
"Bash(gcloud * list*)",
"Bash(gcloud * describe*)",
"Bash(gcloud config list*)",
"Bash(doctl * list*)",
"Bash(doctl * get*)",
"Bash(doctl account get*)",
"Bash(terraform plan*)",
"Bash(terraform validate*)",
"Bash(terraform fmt*)",
"Bash(terraform show*)",
"Bash(terraform output*)",
"Bash(terraform state list*)",
"Bash(terraform state show*)",
"Bash(terraform version*)",
"Bash(tofu plan*)",
"Bash(tofu validate*)",
"Bash(tofu fmt*)",
"Bash(tofu show*)",
"Bash(tofu output*)",
"Bash(tofu state list*)",
"Bash(tofu state show*)",
"Bash(tofu version*)"
],
"ask": [
"Bash(git push --force*)",
"Bash(git push -f*)",
"Bash(git push * --force*)",
"Bash(git push * -f *)",
"Bash(git push * -f)",
"Bash(find * -delete*)",
"Bash(find * -exec*)",
"Bash(find * -ok*)",
"Bash(fd -x *)",
"Bash(fd -X *)",
"Bash(fd * -x *)",
"Bash(fd * -X *)",
"Bash(fd --exec*)",
"Bash(fd * --exec*)",
"Bash(yq -i*)",
"Bash(yq --in-place*)",
"Bash(yq * -i *)",
"Bash(yq * --in-place*)",
"Bash(kubectl delete *)",
"Bash(kubectl drain *)",
"Bash(helm uninstall *)",
"Bash(helm rollback *)",
"Bash(docker system prune*)",
"Bash(docker volume prune*)",
"Bash(aws * delete-*)",
"Bash(gcloud * delete *)",
"Bash(doctl * delete *)",
"Bash(bw *)",
"Bash(dotpro apply*)",
"Agent(model:fable)",
"Agent(model:opus)"
],
"deny": [
"Bash(terraform apply*)",
"Bash(terraform destroy*)",
"Bash(tofu apply*)",
"Bash(tofu destroy*)",
"Read(~/.ssh/*)",
"Edit(~/.ssh/*)",
"Write(~/.ssh/*)",
"Bash(* ~/.ssh/*)",
"Bash(* $HOME/.ssh/*)",
"Bash(* /home/*/.ssh/*)",
"Bash(* /Users/*/.ssh/*)",
"Read(~/.gnupg/*)",
"Edit(~/.gnupg/*)",
"Write(~/.gnupg/*)",
"Bash(* ~/.gnupg/*)",
"Bash(* $HOME/.gnupg/*)",
"Bash(* /home/*/.gnupg/*)",
"Bash(* /Users/*/.gnupg/*)",
"Read(~/.aws/credentials)",
"Edit(~/.aws/credentials)",
"Write(~/.aws/credentials)",
"Bash(* ~/.aws/credentials)",
"Bash(* $HOME/.aws/credentials)",
"Bash(* /home/*/.aws/credentials)",
"Bash(* /Users/*/.aws/credentials)",
"Read(~/.config/gcloud/*)",
"Edit(~/.config/gcloud/*)",
"Write(~/.config/gcloud/*)",
"Bash(* ~/.config/gcloud/*)",
"Read(~/.azure/*)",
"Edit(~/.azure/*)",
"Write(~/.azure/*)",
"Bash(* ~/.azure/*)",
"Read(~/.config/doctl/*)",
"Edit(~/.config/doctl/*)",
"Write(~/.config/doctl/*)",
"Bash(* ~/.config/doctl/*)",
"Read(~/.docker/config.json)",
"Edit(~/.docker/config.json)",
"Write(~/.docker/config.json)",
"Bash(* ~/.docker/config.json)",
"Read(~/.kube/config)",
"Edit(~/.kube/config)",
"Write(~/.kube/config)",
"Bash(* ~/.kube/config)",
"Read(~/.netrc)",
"Edit(~/.netrc)",
"Write(~/.netrc)",
"Bash(* ~/.netrc)",
"Read(~/.npmrc)",
"Edit(~/.npmrc)",
"Write(~/.npmrc)",
"Bash(* ~/.npmrc)",
"Read(~/.pypirc)",
"Edit(~/.pypirc)",
"Write(~/.pypirc)",
"Bash(* ~/.pypirc)",
"Read(~/.git-credentials)",
"Edit(~/.git-credentials)",
"Write(~/.git-credentials)",
"Bash(* ~/.git-credentials*)",
"Read(~/.config/gh/hosts.yml)",
"Edit(~/.config/gh/hosts.yml)",
"Write(~/.config/gh/hosts.yml)",
"Bash(* ~/.config/gh/hosts.yml)",
"Read(~/.terraform.d/credentials.tfrc.json)",
"Edit(~/.terraform.d/credentials.tfrc.json)",
"Write(~/.terraform.d/credentials.tfrc.json)",
"Bash(* ~/.terraform.d/credentials.tfrc.json)",
"Read(~/.vault-token)",
"Edit(~/.vault-token)",
"Write(~/.vault-token)",
"Bash(* ~/.vault-token)",
"Read(~/.password-store/*)",
"Edit(~/.password-store/*)",
"Write(~/.password-store/*)",
"Bash(* ~/.password-store/*)",
"Read(~/.local/share/keyrings/*)",
"Edit(~/.local/share/keyrings/*)",
"Write(~/.local/share/keyrings/*)",
"Bash(* ~/.local/share/keyrings/*)",
"Read(~/.config/Bitwarden CLI/*)",
"Edit(~/.config/Bitwarden CLI/*)",
"Write(~/.config/Bitwarden CLI/*)",
"Bash(* ~/.config/Bitwarden*)",
"Read(~/.bash_history)",
"Bash(* ~/.bash_history)",
"Read(~/.zsh_history)",
"Bash(* ~/.zsh_history)"
]
}
}claude_code_keybindings ships the Chat bindings ctrl+enter (submit) and shift+enter (newline). claude_code_config is empty by default, so ~/.claude.json is left untouched unless you set keys.
Examples
claude_code_settings and claude_code_config deep-merge onto the shipped defaults (with hash_behaviour = merge), so set only the keys you want to change:
claude_code_settings:
effortLevel: high # default: max
permissions:
allow: # merged onto the default allow list
- "Bash(make *)"Skills
Agent Skills teach the agent a workflow, a convention, or a runbook; Claude Code discovers personal skills in ~/.claude/skills/<name>/. The agents role owns the machine's skill inventory (agents_skills, git sources synced into a local cache), and this role copies the selection into claude_code_skills_path on every apply, so a skill edit in its source repository lands with the next apply.
claude_code_skills defaults to the whole inventory, so listing a source in agents_skills is enough to get its skills into Claude Code. Override it to narrow what this tool receives; every source named here must also be in agents_skills, which is what the cache syncs:
agents_skills: # the machine inventory (agents role)
git@github.com:example/skills.git: all
https://github.com/anthropics/skills:
- skill-creator
claude_code_skills: # default: the whole agents_skills inventory
git@github.com:example/skills.git:
- blog-postThe role tracks what it deploys in a .dotfilespro manifest next to the skills, so a skill dropped from the selection is removed on the next apply while skills you placed in the directory by hand are left alone. A skill named in a selection but missing from its synced source fails the apply naming it. When two sources provide a skill with the same name, the later source in claude_code_skills wins. Project skills (<repo>/.claude/skills/) and plugin skills are not touched; this manages only the personal scope.
Accounts
Claude Code keeps its login in a single config dir (~/.claude), so two Anthropic accounts on the same machine, say a personal one and a company one, cannot coexist there: a login in one shell logs every other shell over. Claude Code has no account switcher of its own, but it honors CLAUDE_CONFIG_DIR, which relocates the whole config dir including the credential. The role builds per-shell account selection on that, the same per-shell context isolation the googlecloud, kubernetes, digitalocean, and gitlab roles apply:
claude_code_accounts: # default: [] (single account, nothing extra deployed)
- personal
- workEach listed name gets ~/.claude/accounts/<name> as its config dir, and every apply deploys the role-managed config into it: the merged settings.json, the global CLAUDE.md, the keybindings, and a symlink to the managed skills directory, so a fresh account starts with the same setup as ~/.claude. The status line and hook commands in the settings point at shared absolute paths and need no copies. Everything else stays per account: the credential (.credentials.json), .claude.json, and session state such as history and projects.
Log in once per account, then select per shell:
claude-authenticate work # select ~/.claude/accounts/work and run the interactive login
claude-account work # this shell now runs Claude Code as the work account
claude-account personal # while another shell holds the personal one
claude-account # no argument: show the current account and login statusWith no account selected Claude Code keeps using ~/.claude, so an existing login keeps working as the implicit default account; the account dirs only apply where you export one.
Aliases
Rendered to ~/.profile.d/aliases/claude.sh. Each model alias passes a --model value that resolves to the latest model in that tier (see claude --model); override claude_aliases to add or change them.
| Alias | Command | Description |
|---|---|---|
fable | claude --model fable | Session on the latest Fable model |
opus | claude --model opus | Session on the latest Opus model |
sonnet | claude --model sonnet | Session on the latest Sonnet model |
haiku | claude --model haiku | Session on the latest Haiku model |
And the account functions, with completion on the account names:
| Function | Description |
|---|---|
claude-account <name> | Select the account for this shell (exports CLAUDE_CONFIG_DIR); no argument shows the current account and login status |
claude-authenticate <name> | Select the account and run the interactive claude auth login |
Status line
The role deploys a self-contained ~/.claude/statusline.sh that Claude Code runs on each refresh, piping it session context as JSON on stdin. The default script depends only on jq and git (no starship) and renders the configured sections (location, git, session, usage):
~/projects/dotfiles | 🌱 main ↑2 +45/-12 ?3 | 🦊 Fable 5 $0.42 24m █▓░░ 42% 1.2M | ⏳ 5h █░░░ 31% 2h15m → 7d ██▓░ 68% Tue 09:00Reading left to right:
- location the working directory,
full(~/projects/dotfiles) or fish-styleshort(~/p/dotfiles). - git the branch (colored by cleanliness), ahead/behind arrows,
+/-line counts, and an untracked count. Inside a linked worktree the branch icon becomes the worktree marker (🌱 → 🪴, or→in mono), the same swap the starship role's prompt makes. - session a per-model icon so the model is identifiable at a glance (🦊 Fable, 🎼 Opus, 🖋 Sonnet, 🌸 Haiku, with an ✳ fallback for anything unmatched), then the model name, session cost (colored by threshold), duration, a context-window bar, and total session tokens. As the context nears a forced auto-compact (
context_window_size - claude_code_statusline_compact_reservetokens), a compact marker counting the headroom down (♻️ 5%) appears so you can compact or hand off first. The render right after a compaction completes shows a just-compacted marker (♻️ ✨) for that one refresh. - usage both subscription windows (Pro/Max), the 5-hour with the time left before it resets and the 7-day with its reset moment.
Every icon also has a Nerd Font mono variant, selected with claude_code_statusline_icon_style: mono, for emoji-free terminals; the icons and model marks are configurable through claude_code_statusline_icons and claude_code_statusline_model_icons. The status line blog post walks through why each signal is on the line and in that order.
The percentage indicators (context, usage windows) fill each cell through ░ ▒ ▓ █, and the thresholds turn the bar yellow, then red:
0% ░░░░ 15% ▒░░░ 42% █▓░░ 70% ██▓░ 100% ████Preview it
The line renders from whatever JSON arrives on stdin, so after re-applying a claude_code_statusline_* change you can preview the result without opening a session:
printf '%s' '{
"model": {"display_name": "Fable 5"},
"workspace": {"current_dir": "'"$PWD"'"},
"cost": {"total_cost_usd": 1.23, "total_duration_ms": 5040000},
"context_window": {"used_percentage": 42},
"rate_limits": {
"five_hour": {"used_percentage": 31, "resets_at": '$(($(date +%s) + 8100))'},
"seven_day": {"used_percentage": 68, "resets_at": '$(($(date +%s) + 250000))'}
}
}' | ~/.claude/statusline.shDrop a field to see the degradation: without rate_limits the usage section disappears, and without valid JSON the line falls back to the bare path.
Bring your own
You own the format, so there are a few ways to change it:
Tweak the default by copying statusline.sh into your own dotfiles repo, editing it, then telling the role to stand aside and use yours instead:
claude_code_statusline_deploy: false # stop deploying the bundled script
claude_code_statusline_path: ~/dotfiles/claude/statusline.shBecause the settings write is a deep merge, statusLine.command already points at claude_code_statusline_path, so relocating the script is enough. To swap in an entirely different command (for example, render it with starship, which the role never installs or requires), override the command directly:
claude_code_statusline_deploy: false
claude_code_settings:
statusLine:
command: >-
STARSHIP_CONFIG=~/.claude/starship.toml starship prompt --cwd "$(jq -r .workspace.current_dir)"To turn the status line off, set claude_code_statusline_deploy: false and leave statusLine out of your claude_code_settings (the deep merge cannot remove a key, so clear it in your own settings file if the default has already been written).
Your script receives the same JSON Claude Code documents for the status line: .model.display_name, .workspace.current_dir, the .cost.* fields, .context_window.used_percentage, and (Pro/Max) the .rate_limits.* windows. Only the first line of stdout is used, and ANSI color is supported.
CLAUDE.md
The shipped ~/.claude/CLAUDE.md carries no instructions of its own; it imports the shared ~/AGENTS.md deployed by the agents role, so Claude Code and every other agent read the same guidelines:
# CLAUDE.md
Global instructions for Claude Code across all sessions.
@~/AGENTS.md