feat: major dotfiles refactor with documentation and multi-platform improvements

- Add comprehensive CLAUDE.md with chezmoi best practices and AI assistant guidelines
- Restructure TODO.md with prioritized tasks and detailed organization
- Add PowerShell profile template for Windows platform support
- Split git configs into identity-specific templates (ryan/xevion)
- Convert nushell env.nu and gitconfig to templates for cross-platform rendering
- Refactor chezmoi hooks to TypeScript (.init_pre.ts, .update_pre.ts)
- Update .chezmoiignore with better platform-specific file handling
- Remove deprecated shellchecker.sh and tasks.json from VS Code config
- Add mise integration to shell configs (bash, zsh, nushell, PowerShell)
- Clean up commonrc.sh.tmpl WSL-specific code
- Disable Deno in VS Code settings, enable simple dialog mode
- Add nushell extension recommendation to VS Code

This commit establishes better cross-platform support (Windows/WSL/Linux),
improves documentation for future maintenance, and standardizes configuration
management patterns across the repository.
This commit is contained in:
Ryan Walters
2025-10-26 17:01:45 -05:00
parent 8b718db155
commit 02b9236ecf
21 changed files with 951 additions and 361 deletions

View File

@@ -0,0 +1,13 @@
# Ryan Walters identity override
# Use this for work/professional repositories
#
# To use in a repository, run:
# git config --local include.path ~/.config/git/config-ryan
[user]
name = Ryan Walters
email = ryan@walters.to
signingkey = 39538EC79ACF2597
{{ if eq .chezmoi.os "windows" }}
{{ else }}
{{ end }}

View File

@@ -0,0 +1,13 @@
# Xevion identity override
# Use this for personal/open-source repositories
#
# To use in a repository, run:
# git config --local include.path ~/.config/git/config-xevion
[user]
name = Xevion
email = xevion@xevion.dev
signingkey = C217005CF3C00672
{{ if eq .chezmoi.os "windows" }}
{{ else }}
{{ end }}

View File

@@ -896,4 +896,20 @@ $env.config = {
event: { edit: selectall }
}
]
}
# mise - polyglot tool version manager
# Activates mise if installed (provides tools like vault, node, python, etc.)
# This adds mise-managed tool paths to $env.PATH dynamically based on the current directory
if (which mise | is-not-empty) {
$env.MISE_SHELL = "nu"
# Create the mise activation directory if it doesn't exist
let mise_cache = $"($nu.data-dir)/mise"
mkdir $mise_cache
# Run mise hook to setup environment for current directory
$env.PATH = ($env.PATH | split row (char esep))
mise hook-env -s nu | save --force $"($mise_cache)/hook-env.nu"
source $"($mise_cache)/hook-env.nu"
}

View File

@@ -1,101 +0,0 @@
# Nushell Environment Config File
#
# version = "0.98.0"
def create_left_prompt [] {
let dir = match (do --ignore-shell-errors { $env.PWD | path relative-to $nu.home-path }) {
null => $env.PWD
'' => '~'
$relative_pwd => ([~ $relative_pwd] | path join)
}
let path_color = (if (is-admin) { ansi red_bold } else { ansi green_bold })
let separator_color = (if (is-admin) { ansi light_red_bold } else { ansi light_green_bold })
let path_segment = $"($path_color)($dir)(ansi reset)"
$path_segment | str replace --all (char path_sep) $"($separator_color)(char path_sep)($path_color)"
}
def create_right_prompt [] {
# create a right prompt in magenta with green separators and am/pm underlined
let time_segment = ([
(ansi reset)
(ansi magenta)
(date now | format date '%x %X') # try to respect user's locale
] | str join | str replace --regex --all "([/:])" $"(ansi green)${1}(ansi magenta)" |
str replace --regex --all "([AP]M)" $"(ansi magenta_underline)${1}")
let last_exit_code = if ($env.LAST_EXIT_CODE != 0) {([
(ansi rb)
($env.LAST_EXIT_CODE)
] | str join)
} else { "" }
([$last_exit_code, (char space), $time_segment] | str join)
}
# Use nushell functions to define your right and left prompt
$env.PROMPT_COMMAND = {|| create_left_prompt }
# FIXME: This default is not implemented in rust code as of 2023-09-08.
$env.PROMPT_COMMAND_RIGHT = {|| create_right_prompt }
# The prompt indicators are environmental variables that represent
# the state of the prompt
$env.PROMPT_INDICATOR = {|| "> " }
$env.PROMPT_INDICATOR_VI_INSERT = {|| ": " }
$env.PROMPT_INDICATOR_VI_NORMAL = {|| "> " }
$env.PROMPT_MULTILINE_INDICATOR = {|| "::: " }
# If you want previously entered commands to have a different prompt from the usual one,
# you can uncomment one or more of the following lines.
# This can be useful if you have a 2-line prompt and it's taking up a lot of space
# because every command entered takes up 2 lines instead of 1. You can then uncomment
# the line below so that previously entered commands show with a single `🚀`.
# $env.TRANSIENT_PROMPT_COMMAND = {|| "🚀 " }
# $env.TRANSIENT_PROMPT_INDICATOR = {|| "" }
# $env.TRANSIENT_PROMPT_INDICATOR_VI_INSERT = {|| "" }
# $env.TRANSIENT_PROMPT_INDICATOR_VI_NORMAL = {|| "" }
# $env.TRANSIENT_PROMPT_MULTILINE_INDICATOR = {|| "" }
# $env.TRANSIENT_PROMPT_COMMAND_RIGHT = {|| "" }
# Specifies how environment variables are:
# - converted from a string to a value on Nushell startup (from_string)
# - converted from a value back to a string when running external commands (to_string)
# Note: The conversions happen *after* config.nu is loaded
$env.ENV_CONVERSIONS = {
"PATH": {
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
}
"Path": {
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
}
}
# Directories to search for scripts when calling source or use
# The default for this is $nu.default-config-dir/scripts
$env.NU_LIB_DIRS = [
($nu.default-config-dir | path join 'scripts') # add <nushell-config-dir>/scripts
($nu.data-dir | path join 'completions') # default home for nushell completions
]
# Directories to search for plugin binaries when calling register
# The default for this is $nu.default-config-dir/plugins
$env.NU_PLUGIN_DIRS = [
($nu.default-config-dir | path join 'plugins') # add <nushell-config-dir>/plugins
]
# To add entries to PATH (on Windows you might use Path), you can use the following pattern:
# $env.PATH = ($env.PATH | split row (char esep) | prepend '/some/path')
# An alternate way to add entries to $env.PATH is to use the custom command `path add`
# which is built into the nushell stdlib:
# use std "path add"
# $env.PATH = ($env.PATH | split row (char esep))
# path add /some/path
# path add ($env.CARGO_HOME | path join "bin")
# path add ($env.HOME | path join ".local" "bin")
# $env.PATH = ($env.PATH | uniq)
# To load from a custom file you can use:
# source ($nu.default-config-dir | path join 'custom.nu')

View File

@@ -0,0 +1,242 @@
# {{ template "banner.tmpl" .}}
# Nushell Environment Config File
# Cross-platform configuration for Windows and Linux
# version = "0.98.0"
def create_left_prompt [] {
let dir = match (try { $env.PWD | path relative-to $nu.home-path } catch { null }) {
null => $env.PWD
'' => '~'
$relative_pwd => ([~ $relative_pwd] | path join)
}
let path_color = (if (is-admin) { ansi red_bold } else { ansi green_bold })
let separator_color = (if (is-admin) { ansi light_red_bold } else { ansi light_green_bold })
let path_segment = $"($path_color)($dir)(ansi reset)"
$path_segment | str replace --all (char path_sep) $"($separator_color)(char path_sep)($path_color)"
}
def create_right_prompt [] {
let time_segment = ([
(ansi reset)
(ansi magenta)
(date now | format date '%x %X')
] | str join | str replace --regex --all "([/:])" $"(ansi green)${1}(ansi magenta)" |
str replace --regex --all "([AP]M)" $"(ansi magenta_underline)${1}")
let last_exit_code = if ($env.LAST_EXIT_CODE != 0) {([
(ansi rb)
($env.LAST_EXIT_CODE)
] | str join)
} else { "" }
([$last_exit_code, (char space), $time_segment] | str join)
}
$env.PROMPT_COMMAND = {|| create_left_prompt }
$env.PROMPT_COMMAND_RIGHT = {|| create_right_prompt }
$env.PROMPT_INDICATOR = {|| "> " }
$env.PROMPT_INDICATOR_VI_INSERT = {|| ": " }
$env.PROMPT_INDICATOR_VI_NORMAL = {|| "> " }
$env.PROMPT_MULTILINE_INDICATOR = {|| "::: " }
# Environment variable conversions
$env.ENV_CONVERSIONS = {
"PATH": {
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
}
"Path": {
from_string: { |s| $s | split row (char esep) | path expand --no-symlink }
to_string: { |v| $v | path expand --no-symlink | str join (char esep) }
}
}
# Library and plugin directories
$env.NU_LIB_DIRS = [
($nu.default-config-dir | path join 'scripts')
($nu.data-dir | path join 'completions')
]
$env.NU_PLUGIN_DIRS = [
($nu.default-config-dir | path join 'plugins')
]
# Editor configuration
$env.EDITOR = "micro"
$env.MICRO_TRUECOLOR = 1
# OpenAI API Key
$env.OPENAI_API_KEY = "{{ dopplerProjectJson.OPENAI_CHATGPT_CLI }}"
# Initialize PATH as a list for easier manipulation
$env.PATH = ($env.PATH | split row (char esep))
# Platform-specific PATH additions
{{ if eq .chezmoi.os "windows" }}
# Windows PATH additions
# User local bin
$env.PATH = ($env.PATH | prepend ($env.USERPROFILE | path join ".local" "bin"))
# Go
let go_user = ($env.USERPROFILE | path join "go" "bin")
let go_system = "C:\\Program Files\\Go\\bin"
if ($go_user | path exists) { $env.PATH = ($env.PATH | prepend $go_user) }
if ($go_system | path exists) { $env.PATH = ($env.PATH | prepend $go_system) }
# Rust/Cargo
let cargo_bin = ($env.USERPROFILE | path join ".cargo" "bin")
if ($cargo_bin | path exists) { $env.PATH = ($env.PATH | prepend $cargo_bin) }
# Deno
let deno_bin = ($env.USERPROFILE | path join ".deno" "bin")
if ($deno_bin | path exists) {
$env.DENO_INSTALL = ($env.USERPROFILE | path join ".deno")
$env.PATH = ($env.PATH | prepend $deno_bin)
}
# Node/pnpm
let pnpm_home = ($env.LOCALAPPDATA | path join "pnpm")
if ($pnpm_home | path exists) {
$env.PNPM_HOME = $pnpm_home
$env.PATH = ($env.PATH | prepend $pnpm_home)
}
# Bun
let bun_bin = ($env.USERPROFILE | path join ".bun" "bin")
if ($bun_bin | path exists) {
$env.BUN_INSTALL = ($env.USERPROFILE | path join ".bun")
$env.PATH = ($env.PATH | prepend $bun_bin)
}
# Python/pyenv
let pyenv_root = ($env.USERPROFILE | path join ".pyenv" "pyenv-win")
if ($pyenv_root | path exists) {
$env.PYENV_ROOT = $pyenv_root
$env.PATH = ($env.PATH | prepend ($pyenv_root | path join "bin"))
$env.PATH = ($env.PATH | prepend ($pyenv_root | path join "shims"))
}
# .NET
let dotnet_root = ($env.USERPROFILE | path join ".dotnet")
if ($dotnet_root | path exists) {
$env.DOTNET_ROOT = $dotnet_root
$env.PATH = ($env.PATH | prepend $dotnet_root)
$env.PATH = ($env.PATH | prepend ($dotnet_root | path join "tools"))
}
# Bob (Neovim version manager)
let bob_bin = ($env.LOCALAPPDATA | path join "bob" "nvim-bin")
if ($bob_bin | path exists) { $env.PATH = ($env.PATH | prepend $bob_bin) }
# Spicetify
let spicetify = ($env.USERPROFILE | path join ".spicetify")
if ($spicetify | path exists) { $env.PATH = ($env.PATH | prepend $spicetify) }
# Pulumi
let pulumi_bin = ($env.USERPROFILE | path join ".pulumi" "bin")
if ($pulumi_bin | path exists) { $env.PATH = ($env.PATH | prepend $pulumi_bin) }
# Scoop
let scoop_shims = ($env.USERPROFILE | path join "scoop" "shims")
if ($scoop_shims | path exists) { $env.PATH = ($env.PATH | prepend $scoop_shims) }
{{ else }}
# Linux/macOS PATH additions
# User local bin
$env.PATH = ($env.PATH | prepend ($env.HOME | path join ".local" "bin"))
# System bins
$env.PATH = ($env.PATH | prepend "/usr/local/bin")
$env.PATH = ($env.PATH | prepend ($env.HOME | path join "bin"))
# Go
let go_bin = "/usr/local/go/bin"
let go_user = ($env.HOME | path join "go" "bin")
if ($go_bin | path exists) { $env.PATH = ($env.PATH | prepend $go_bin) }
if ($go_user | path exists) { $env.PATH = ($env.PATH | prepend $go_user) }
# Rust/Cargo
let cargo_bin = ($env.HOME | path join ".cargo" "bin")
if ($cargo_bin | path exists) { $env.PATH = ($env.PATH | prepend $cargo_bin) }
# Deno
let deno_bin = ($env.HOME | path join ".deno" "bin")
if ($deno_bin | path exists) { $env.PATH = ($env.PATH | prepend $deno_bin) }
# Bob (Neovim version manager)
let bob_bin = ($env.HOME | path join ".local" "share" "bob" "nvim-bin")
if ($bob_bin | path exists) { $env.PATH = ($env.PATH | prepend $bob_bin) }
# Homebrew (Linux)
let brew_bin = "/home/linuxbrew/.linuxbrew/bin"
if ($brew_bin | path exists) {
$env.PATH = ($env.PATH | prepend $brew_bin)
$env.PATH = ($env.PATH | prepend "/home/linuxbrew/.linuxbrew/sbin")
}
# pnpm
let pnpm_home = ($env.HOME | path join ".local" "share" "pnpm")
if ($pnpm_home | path exists) {
$env.PNPM_HOME = $pnpm_home
$env.PATH = ($env.PATH | prepend $pnpm_home)
}
# Bun
let bun_bin = ($env.HOME | path join ".bun" "bin")
if ($bun_bin | path exists) {
$env.BUN_INSTALL = ($env.HOME | path join ".bun")
$env.PATH = ($env.PATH | prepend $bun_bin)
}
# pyenv
let pyenv_root = ($env.HOME | path join ".pyenv")
if ($pyenv_root | path exists) {
$env.PYENV_ROOT = $pyenv_root
$env.PATH = ($env.PATH | prepend ($pyenv_root | path join "bin"))
}
# jenv (Java)
let jenv_bin = ($env.HOME | path join ".jenv" "bin")
if ($jenv_bin | path exists) { $env.PATH = ($env.PATH | prepend $jenv_bin) }
# .NET
let dotnet_root = ($env.HOME | path join ".dotnet")
if ($dotnet_root | path exists) {
$env.DOTNET_ROOT = $dotnet_root
$env.PATH = ($env.PATH | prepend $dotnet_root)
$env.PATH = ($env.PATH | prepend ($dotnet_root | path join "tools"))
}
# Spicetify
let spicetify = ($env.HOME | path join ".spicetify")
if ($spicetify | path exists) { $env.PATH = ($env.PATH | prepend $spicetify) }
# Pulumi
let pulumi_bin = ($env.HOME | path join ".pulumi" "bin")
if ($pulumi_bin | path exists) { $env.PATH = ($env.PATH | prepend $pulumi_bin) }
# Perl (if installed)
let perl_bin = ($env.HOME | path join "perl5" "bin")
if ($perl_bin | path exists) {
$env.PATH = ($env.PATH | prepend $perl_bin)
$env.PERL5LIB = ($env.HOME | path join "perl5" "lib" "perl5")
$env.PERL_LOCAL_LIB_ROOT = ($env.HOME | path join "perl5")
}
# hishtory
let hishtory_bin = ($env.HOME | path join ".hishtory")
if ($hishtory_bin | path exists) { $env.PATH = ($env.PATH | prepend $hishtory_bin) }
{{ end }}
# Remove duplicates from PATH
$env.PATH = ($env.PATH | uniq)
# WSL-specific settings
{{ if .wsl }}
# Ensure CLI apps open URLs in Windows browser
$env.BROWSER = 'powershell.exe /c start'
{{ end }}