Skip to content

Latest commit

 

History

History
231 lines (170 loc) · 8.39 KB

File metadata and controls

231 lines (170 loc) · 8.39 KB

Dotfiles Repository

Cross-platform dotfiles managed by dotter.

Directory Guidance

  • AGENTS.md: repo-level architecture, package organization, template conventions, variable strategy
  • .dotter/AGENTS.md: Dotter internals, pre_deploy / post_deploy scripts, post-deploy patching, and other implementation exceptions

If you touch .dotter/, read .dotter/AGENTS.md first.

Agent Config Paths (XDG)

Read this section before modifying any agent configs. Do not assume default upstream paths like ~/.codex or ~/.claude.

This repo manages configs for multiple AI coding agents. Their paths follow XDG spec (~/.config/<name>) instead of defaults (~/.<name>):

Agent Env Var Path
Codex CODEX_HOME ~/.config/codex
Claude CLAUDE_CONFIG_DIR ~/.config/claude
Pi PI_CODING_AGENT_DIR ~/.config/pi
Copilot COPILOT_HOME ~/.config/copilot
Gemini GEMINI_CLI_HOME ~/.config/gemini
Qoder QODERCLI_HOME ~/.config/qoder

When reading or writing agent configs, use these paths.

Package Organization

This repo uses Dotter packages as a layered grouping system rather than a flat list of unrelated folders.

  • Base packages hold concrete config payloads for one tool or concern
  • Higher-level packages compose those base packages into reusable environments
  • Current agent-related grouping is intentionally nested:
    • agent: shared entry package for agent-facing configs and common glue
    • agent-tunnel: agent-adjacent remote/service integrations
    • agent-runtime: local runtime support for agent workflows

When changing package names, dependencies, or file mappings, preserve this layered relationship unless the change is explicitly a package-architecture refactor.

Dotter Templates

Blackhole Mapping

When a package should remain in the default package groups but must not overwrite the real live config on a specific machine, remap its deploy target to a blackhole directory such as ~/.cache/dotter-blackhole/<package> in a machine-specific include like .dotter/wsl.toml.

  • Use this when the package is still part of shared package composition, but the actual live path is intentionally managed locally and should not be overwritten by Dotter.
  • Prefer this over removing the package from global groups when you still want dependency structure, variables, and repo defaults to stay intact.
  • Keep the blackhole target obviously non-live and disposable, typically under ~/.cache/dotter-blackhole/.
  • Document the real live config ownership nearby so future changes do not accidentally assume Dotter still controls the production path.

Template Basics

Built-in Variables

{{dotter.linux}}
# true on Linux
{{dotter.windows}}
# true on Windows
{{dotter.macos}}
# true on macOS
{{dotter.hostname}}
# machine hostname

Built-in Helpers

{{env_var "VAR_NAME"}}
# read environment variable
{{command_output "cmd"}}
# execute command, return stdout
{{command_success "cmd"}}
# true if command exits 0
{{trim "  hello  "}}
# trim whitespace -> "hello"
{{to_lower_case "HELLO"}}
# lowercase -> "hello"
{{to_upper_case "hello"}}
# uppercase -> "HELLO"
{{replace "old" "o" "0"}}
# string replace -> "0ld"

WSL Detection

Use command_success to detect WSL:

{{#if (command_success "uname -r | grep -qi wsl")}}
  # WSL-specific config
{{/if}}

{{#if (and dotter.linux (not (command_success "uname -r | grep -qi wsl")))}}
  # Pure Linux (not WSL)
{{/if}}

Conditional Logic

{{#if (and cond1 cond2)}}...{{/if}}
{{#if (or cond1 cond2)}}...{{/if}}
{{#if (not cond)}}...{{/if}}

Variable Contract

Dotter variables in this repo fall into two categories: global.toml / local.toml override variables, and shared secrets injected inline with rbw get inside templates.

Override Order

# .dotter/global.toml
[package_name.variables]
scalar_value = ""
nested_value = { key_a = "", key_b = "" }
# .dotter/local.toml
[variables]
scalar_value = "local"
nested_value = { key_b = "overridden" }
  • [package.variables] in global.toml defines package-level placeholders and schema.
  • local.toml does not use [package.variables]; it only uses top-level [variables].
  • Dotter first merges variables from the selected packages, then recursively overrides them with top-level [variables] from local.toml by variable name.
  • Scalar values with the same name are replaced; tables with the same name are recursively merged by key.
  • If there is no sensible default, prefer an empty placeholder in global.toml to reduce template branching. This is a schema choice, not a recommended default value.

Source Rules

  • Per-machine / not synced across machines: define a placeholder in global.toml, then override it from top-level [variables] in .dotter/local.toml.
  • Shareable secrets: keep using inline rbw get in templates or scripts instead of storing them in local.toml.
  • Detailed constraints for .dotter/pre_deploy.sh and .dotter/post_deploy.sh live in .dotter/AGENTS.md.

Approved Variable Schema

Variable Shape Source Notes
slock_api_key string global + local May be empty; the service should still render
slock_wss_proxy string global + local May be empty; render no proxy env when unset
git_repo_identities table global + local Keyed by identity name; values include repo_dir, name, and email
skm_local_packages array of tables global + local Each item includes repo, with optional skills
mihomo_direct_suffixes array of strings global + local May be empty; render no extra rules when unset

Git Repo Identities

Add repo-scoped Git identities in .dotter/local.toml under variables.git_repo_identities:

[variables.git_repo_identities.company_a]
repo_dir = "~/Documents/company-a.repos/"
name = "your-work-name"
email = "your-work-email@company-a.example"

[variables.git_repo_identities.company_b]
repo_dir = "~/Documents/company-b.repos/"
name = "your-other-work-name"
email = "your-other-work-email@company-b.example"

Each entry generates git/generated/<key>.conf, then Dotter links it to ~/.config/git/generated/<key>.conf.

Template Patterns

Secret Injection with rbw

Inject secrets from Bitwarden using rbw get:

{ "api_key": "{{replace (command_output "rbw get my-api-key") "\n" ""}}" }

The replace ... '\n' '' removes trailing newline from rbw output.

Workflow:

  1. rbw add my-api-key - store secret in Bitwarden
  2. Use {{replace (command_output 'rbw get my-api-key') '\n' ''}} in template
  3. dotter deploy --force - deploy with secrets injected

Optional Variables with #each

Always wrap #each with #if to handle undefined variables gracefully.

Dotter's #each fails when the variable is undefined, breaking template rendering. Wrap it with #if to skip the block when the variable is missing:

# {{#if my_items}}
# {{#each my_items}}
- name: "{{name}}"
# {{/each}}
# {{/if}}

This pattern ensures templates render correctly even when my_items is not defined in local.toml.

YAML Template Tips

When a YAML file is both a Dotter template and pre-commit formatted with yamlfmt, keep these rules:

# {{#each skm_local_packages}}
- repo: "{{repo}}"
  # {{#if skills}}
  skills:
    # {{#each skills}}
    - "{{this}}"
    # {{/each}}
# {{/if}}
# {{/each}}
  • Put control blocks like #each and #if in YAML comments so YAML formatters and editors can still parse the file.
  • Quote inline Handlebars values in YAML scalars, for example repo: "{{repo}}" and - "{{this}}". Unquoted forms may be rewritten into invalid { { repo } } style text by formatters.
  • Dotter treats any file containing {{ as a template; .tmpl is not a required or special suffix.
  • After deploy, generated YAML files may contain empty # lines left by commented control blocks. It is safe to delete lines matching ^[[:space:]]*#[[:space:]]*$ in both the rendered target and the Dotter cache copy so future deploys stay in sync.

Commands

dotter deploy           # deploy dotfiles
dotter deploy --force   # overwrite existing files
dotter deploy --dry-run # preview changes