├── package.json ├── .envrc ├── home-manager ├── nvim │ ├── nvim │ │ ├── lua │ │ │ ├── plugins │ │ │ │ ├── trim.lua │ │ │ │ ├── gitsigns.lua │ │ │ │ ├── terraform.lua │ │ │ │ ├── diagnostics.lua │ │ │ │ ├── colorscheme.lua │ │ │ │ ├── copilot.lua │ │ │ │ ├── go.lua │ │ │ │ └── aider.lua │ │ │ └── config │ │ │ │ ├── keymaps.lua │ │ │ │ ├── clipboard.lua │ │ │ │ ├── git-setup.lua │ │ │ │ ├── autocmds.lua │ │ │ │ ├── options.lua │ │ │ │ └── lazy.lua │ │ └── init.lua │ └── default.nix ├── aerospace │ └── default.nix ├── k9s │ ├── default.nix │ └── k9s │ │ ├── config.yaml │ │ └── skins │ │ └── catppuccin-mocha-transparent.yaml ├── claude-code │ ├── atlassian-config.example.json │ ├── CLAUDE.md │ ├── playwright-macos-wrapper.sh │ ├── settings.json │ ├── commands │ │ ├── prompt.md │ │ ├── validate.md │ │ ├── check.md │ │ └── next.md │ ├── hooks │ │ └── README.md │ ├── agents │ │ ├── go-implementer.md │ │ ├── ruby-implementer.md │ │ ├── python-implementer.md │ │ ├── typescript-implementer.md │ │ ├── debugger.md │ │ ├── devops-implementer.md │ │ └── home-assistant-configurator.md │ ├── skills │ │ ├── nix-patterns │ │ │ └── SKILL.md │ │ └── home-assistant │ │ │ └── SKILL.md │ └── default.nix ├── atuin │ └── default.nix ├── egoengine │ └── default.nix ├── security-tools │ └── default.nix ├── aarch64-darwin.nix ├── media │ └── default.nix ├── hosts │ ├── echelon.nix │ ├── stygianlibrary.nix │ ├── bluedesert.nix │ ├── ultraviolet.nix │ └── vermissian.nix ├── mcp │ ├── default.nix │ └── jira-mcp-wrapper.sh ├── go │ ├── update-golangci-lint.sh │ └── default.nix ├── git │ └── default.nix ├── headless-x86_64-linux.nix ├── linkpearl │ └── default.nix ├── devspaces-host │ └── default.nix ├── codex │ └── default.nix ├── devspaces-client │ └── default.nix ├── ssh-config │ └── default.nix ├── minimal.nix ├── gmailctl │ ├── setup-oauth.md │ └── default.nix └── gemini-cli │ └── default.nix ├── secrets ├── shared │ ├── coder-env.age │ └── coder-db-password.age ├── hosts │ ├── vermissian │ │ ├── cloudflared-token.age │ │ └── coder-ghcr-cache-auth.age │ └── ultraviolet │ │ ├── cloudflared-token.age │ │ ├── redlib-collections.age │ │ └── cloudflare-api-token.age ├── secrets.nix └── keys.nix ├── home-assistant ├── dashboards │ ├── views │ │ ├── sections │ │ │ ├── adu-header.yaml │ │ │ ├── water-sensors-header.yaml │ │ │ ├── adu-shed.yaml │ │ │ ├── gallery-door.yaml │ │ │ └── garage-door.yaml │ │ ├── overview-cards │ │ │ ├── kitchen.yaml │ │ │ ├── living-room.yaml │ │ │ ├── office.yaml │ │ │ ├── main-bedroom.yaml │ │ │ └── front-deck.yaml │ │ └── 01-overview.yaml │ ├── popup-content │ │ ├── dining-room-thermostat.yaml │ │ ├── laundry-dryer.yaml │ │ └── laundry-washer.yaml │ └── popups │ │ ├── dining-room-thermostat.yaml │ │ ├── living-room-lights.yaml │ │ ├── main-bedroom-lights.yaml │ │ ├── living-room-lights-lights.yaml │ │ ├── living-room-lights-scenes.yaml │ │ └── main-bedroom-lights-scenes.yaml └── automations │ ├── car.yaml │ └── bike.yaml ├── secrets.nix ├── hosts ├── ultraviolet │ ├── services │ │ ├── flaresolverr.nix │ │ ├── download-proxies.nix │ │ ├── arr-extras.nix │ │ ├── caddy-base.nix │ │ ├── jellyseerr.nix │ │ ├── bazarr.nix │ │ ├── redlib.nix │ │ ├── radarr-sonarr.nix │ │ └── piped.nix │ ├── home-assistant │ │ ├── templates │ │ │ └── keychain_button.yaml │ │ └── scripts │ │ │ └── hass-cli.sh │ ├── home-assistant-secrets.yaml.example │ ├── hardware-configuration.nix │ └── wyoming-whisper.nix ├── stygianlibrary │ └── hardware-configuration.nix ├── egoengine │ ├── default.nix │ └── home-manager.nix ├── echelon │ └── hardware-configuration.nix ├── bluedesert │ ├── hardware-configuration.nix │ └── home-automation.nix ├── vermissian │ └── hardware-configuration.nix ├── cloudbank │ └── default.nix └── common.nix ├── scripts ├── watch-ble-passthrough.sh ├── watch-bike-sensors.sh └── watch-keychain-events.sh ├── .gitignore ├── overlays └── darwin.nix ├── patches └── gtk3-darwin-sincos.patch ├── modules ├── README.md ├── darwin │ ├── software.nix │ ├── defaults.nix │ └── applications.nix ├── services │ ├── age-identity.nix │ ├── cloudflare-tunnel.nix │ └── cleanup-stale-processes.nix └── nix │ └── defaults.nix ├── pkgs ├── deadcode │ └── default.nix ├── starlark-lsp │ └── default.nix ├── nuclei │ └── default.nix ├── default.nix ├── redlib-veraticus │ └── default.nix ├── claude-code-cli │ └── default.nix ├── aerospace │ └── default.nix ├── heretic │ └── default.nix ├── caddy │ └── default.nix ├── coder-cli │ └── default.nix └── golangci-lint-bin │ └── default.nix ├── LICENSE ├── coder-templates ├── docker-shell │ ├── README.md │ └── .terraform.lock.hcl └── docker-envbuilder │ └── README.md ├── CLAUDE.md ├── docs ├── blink-shell-setup.md └── linkpearl-setup.md └── AGENTS.md /package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | source_up_if_exists 2 | dotenv_if_exists .env 3 | use flake 4 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/plugins/trim.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "cappyzawa/trim.nvim", 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /secrets/shared/coder-env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Veraticus/nix-config/HEAD/secrets/shared/coder-env.age -------------------------------------------------------------------------------- /secrets/shared/coder-db-password.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Veraticus/nix-config/HEAD/secrets/shared/coder-db-password.age -------------------------------------------------------------------------------- /home-assistant/dashboards/views/sections/adu-header.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: separator 3 | name: ADU 4 | icon: mdi:home 5 | -------------------------------------------------------------------------------- /secrets/hosts/vermissian/cloudflared-token.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Veraticus/nix-config/HEAD/secrets/hosts/vermissian/cloudflared-token.age -------------------------------------------------------------------------------- /home-manager/aerospace/default.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | xdg.configFile."aerospace" = { 3 | source = ./aerospace; 4 | recursive = true; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /secrets/hosts/ultraviolet/cloudflared-token.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Veraticus/nix-config/HEAD/secrets/hosts/ultraviolet/cloudflared-token.age -------------------------------------------------------------------------------- /secrets/hosts/ultraviolet/redlib-collections.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Veraticus/nix-config/HEAD/secrets/hosts/ultraviolet/redlib-collections.age -------------------------------------------------------------------------------- /secrets/hosts/ultraviolet/cloudflare-api-token.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Veraticus/nix-config/HEAD/secrets/hosts/ultraviolet/cloudflare-api-token.age -------------------------------------------------------------------------------- /secrets/hosts/vermissian/coder-ghcr-cache-auth.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Veraticus/nix-config/HEAD/secrets/hosts/vermissian/coder-ghcr-cache-auth.age -------------------------------------------------------------------------------- /home-assistant/dashboards/views/sections/water-sensors-header.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: separator 3 | name: Water Sensors 4 | icon: mdi:water 5 | -------------------------------------------------------------------------------- /home-manager/k9s/default.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | home.packages = [pkgs.k9s]; 3 | 4 | xdg.configFile."k9s" = { 5 | source = ./k9s; 6 | recursive = true; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/init.lua: -------------------------------------------------------------------------------- 1 | -- Ensure git is available for plugins 2 | require("config.git-setup") 3 | 4 | -- bootstrap lazy.nvim, LazyVim and your plugins 5 | require("config.lazy") 6 | -------------------------------------------------------------------------------- /home-manager/claude-code/atlassian-config.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "JIRA_URL": "https://your-company.atlassian.net", 3 | "JIRA_USERNAME": "your.email@company.com", 4 | "JIRA_API_TOKEN": "your_jira_api_token" 5 | } -------------------------------------------------------------------------------- /home-manager/atuin/default.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | programs.atuin = { 3 | enable = true; 4 | package = pkgs.atuin; 5 | 6 | enableZshIntegration = true; 7 | daemon.enable = true; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /home-manager/claude-code/CLAUDE.md: -------------------------------------------------------------------------------- 1 | # Preferences 2 | 3 | - This is always a feature branch: delete old code completely, no versioned names, no migration code 4 | - When uncertain about architecture, stop and ask rather than guessing 5 | -------------------------------------------------------------------------------- /secrets.nix: -------------------------------------------------------------------------------- 1 | /* 2 | Compatibility shim for agenix CLI. 3 | 4 | Run commands like: 5 | agenix -e secrets/coder-env.age 6 | 7 | The attrset lives in ./secrets/secrets.nix; keep logic there. 8 | */ 9 | import ./secrets/secrets.nix 10 | -------------------------------------------------------------------------------- /hosts/ultraviolet/services/flaresolverr.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | virtualisation.oci-containers.containers.flaresolverr = { 3 | image = "flaresolverr/flaresolverr:v3.3.18"; 4 | ports = ["8191:8191"]; 5 | extraOptions = ["--network=host"]; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /scripts/watch-ble-passthrough.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | if ! command -v hass-cli >/dev/null 2>&1; then 5 | echo "hass-cli not found in PATH" >&2 6 | exit 1 7 | fi 8 | 9 | hass-cli -o json event watch ble_passthrough.adv_received \ 10 | | jq --unbuffered '.' 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | result* 3 | *.img 4 | *.img.zst 5 | *.raw 6 | .direnv/ 7 | venv/ 8 | .venv/ 9 | .env 10 | .aider* 11 | !.aider.model* 12 | 13 | # Secrets 14 | modules/services/signal-cli-bot-secrets.nix 15 | hosts/secrets/ 16 | 17 | # Vendor directories 18 | vendor/ 19 | reference/ 20 | .terraform/ 21 | -------------------------------------------------------------------------------- /overlays/darwin.nix: -------------------------------------------------------------------------------- 1 | final: prev: let 2 | inherit (prev.stdenv.hostPlatform) isDarwin; 3 | in 4 | if isDarwin 5 | then { 6 | gtk3 = prev.gtk3.overrideAttrs (old: { 7 | patches = (old.patches or []) ++ [../patches/gtk3-darwin-sincos.patch]; 8 | }); 9 | 10 | aerospace = final.callPackage ../pkgs/aerospace {}; 11 | } 12 | else {} 13 | -------------------------------------------------------------------------------- /hosts/ultraviolet/services/download-proxies.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | services.caddy.virtualHosts = { 3 | "transmission.home.husbuddies.gay".extraConfig = '' 4 | reverse_proxy /* 172.31.0.201:9091 5 | import cloudflare 6 | ''; 7 | "sabnzbd.home.husbuddies.gay".extraConfig = '' 8 | reverse_proxy /* localhost:8080 9 | import cloudflare 10 | ''; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /home-manager/egoengine/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | pkgs, 4 | ... 5 | }: { 6 | home = { 7 | file.".local/bin/ee" = { 8 | source = ./scripts/ee.sh; 9 | executable = true; 10 | force = true; 11 | }; 12 | 13 | sessionPath = lib.mkAfter ["$HOME/.local/bin"]; 14 | 15 | packages = [ 16 | pkgs._1password-cli 17 | pkgs.gnutar 18 | pkgs.gzip 19 | ]; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /patches/gtk3-darwin-sincos.patch: -------------------------------------------------------------------------------- 1 | diff --git a/tests/gtkgears.c b/tests/gtkgears.c 2 | index 86c7f74b4a..8803a4df8e 100644 3 | --- a/tests/gtkgears.c 4 | +++ b/tests/gtkgears.c 5 | @@ -37,6 +37,10 @@ 6 | #define STRIPS_PER_TOOTH 7 7 | #define VERTICES_PER_TOOTH 34 8 | #define GEAR_VERTEX_STRIDE 6 9 | +#if defined(__APPLE__) 10 | +# undef HAVE_SINCOS 11 | +#endif 12 | + 13 | 14 | #ifndef HAVE_SINCOS 15 | static void 16 | -------------------------------------------------------------------------------- /home-manager/claude-code/playwright-macos-wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Wrapper script to run Playwright MCP server on macOS 3 | # This runs the server directly without steam-run (which is Linux-only) 4 | 5 | # Set environment variable to prefer Firefox (though macOS can run all browsers) 6 | export MCP_PLAYWRIGHT_DEFAULT_BROWSER=firefox 7 | 8 | # Run the MCP server directly with npx 9 | exec npx -y @executeautomation/playwright-mcp-server "$@" -------------------------------------------------------------------------------- /home-manager/security-tools/default.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | home.packages = with pkgs; [ 3 | # Cloud security assessment 4 | prowler 5 | 6 | # Container and infrastructure vulnerability scanning 7 | trivy 8 | 9 | # Kubernetes security 10 | kubescape 11 | 12 | # Git secrets scanning 13 | gitleaks 14 | 15 | # Vulnerability scanner 16 | nuclei 17 | 18 | # Infrastructure as code security scanner 19 | checkov 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/plugins/gitsigns.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "lewis6991/gitsigns.nvim", 4 | opts = { 5 | -- Simple configuration 6 | attach_to_untracked = false, 7 | signs = { 8 | add = { text = "┃" }, 9 | change = { text = "┃" }, 10 | delete = { text = "_" }, 11 | topdelete = { text = "‾" }, 12 | changedelete = { text = "~" }, 13 | untracked = { text = "┆" }, 14 | }, 15 | }, 16 | }, 17 | } -------------------------------------------------------------------------------- /home-manager/aarch64-darwin.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | imports = [ 3 | ./common.nix 4 | ./aerospace 5 | ./devspaces-client 6 | ./ssh-hosts 7 | ./ssh-config 8 | ./linkpearl 9 | ]; 10 | 11 | home.homeDirectory = "/Users/joshsymonds"; 12 | 13 | # Fonts managed by Nix 14 | home.packages = with pkgs; [ 15 | maple-mono.NF-CN-unhinted 16 | ]; 17 | 18 | programs.zsh.shellAliases.update = "sudo darwin-rebuild switch --flake \".#$(hostname -s)\" --option warn-dirty false"; 19 | programs.kitty.font.size = 13; 20 | } 21 | -------------------------------------------------------------------------------- /hosts/ultraviolet/services/arr-extras.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | services = { 3 | readarr = { 4 | enable = true; 5 | package = pkgs.readarr; 6 | }; 7 | 8 | prowlarr = { 9 | enable = true; 10 | }; 11 | 12 | caddy.virtualHosts = { 13 | "readarr.home.husbuddies.gay".extraConfig = '' 14 | reverse_proxy /* localhost:8787 15 | import cloudflare 16 | ''; 17 | "prowlarr.home.husbuddies.gay".extraConfig = '' 18 | reverse_proxy /* localhost:9696 19 | import cloudflare 20 | ''; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /home-manager/media/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | pkgs, 4 | ... 5 | }: { 6 | xdg.configFile."media" = { 7 | source = pkgs.media.conf; 8 | recursive = true; 9 | }; 10 | 11 | systemd.services.media = { 12 | script = '' 13 | ${pkgs.docker-compose.bin}/docker-compose -f ${pkgs.media.src}/docker-compose.yml --env-file ${config.xdg.configHome}/media/.env 14 | ''; 15 | workingDirectory = pkgs.media.src; 16 | wantedBy = ["multi-user.target"]; 17 | after = ["docker.service" "docker.socket"]; 18 | }; 19 | 20 | home.packages = [docker-compose]; 21 | } 22 | -------------------------------------------------------------------------------- /hosts/ultraviolet/services/caddy-base.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | services.caddy = { 3 | acmeCA = null; 4 | enable = true; 5 | package = pkgs.myCaddy.overrideAttrs (old: { 6 | meta = old.meta // {mainProgram = "caddy";}; 7 | }); 8 | globalConfig = '' 9 | storage file_system { 10 | root /var/lib/caddy 11 | } 12 | ''; 13 | extraConfig = '' 14 | (cloudflare) { 15 | tls { 16 | dns cloudflare {env.CF_API_TOKEN} 17 | resolvers 1.1.1.1 18 | } 19 | } 20 | ''; 21 | virtualHosts = {}; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /hosts/ultraviolet/home-assistant/templates/keychain_button.yaml: -------------------------------------------------------------------------------- 1 | template: 2 | - binary_sensor: 3 | - name: "Josh Keychain Button Press Signal" 4 | unique_id: josh_keychain_button_pressed_signal 5 | device_class: occupancy 6 | state: > 7 | {{ is_state('input_boolean.josh_keychain_button_pressed', 'on') }} 8 | - name: "Justin Keychain Button Press Signal" 9 | unique_id: justin_keychain_button_pressed_signal 10 | device_class: occupancy 11 | state: > 12 | {{ is_state('input_boolean.justin_keychain_button_pressed', 'on') }} 13 | -------------------------------------------------------------------------------- /home-manager/hosts/echelon.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | imports = [ 3 | ../common.nix 4 | ../devspaces-host 5 | ../linkpearl 6 | ../security-tools 7 | ]; 8 | 9 | home = { 10 | homeDirectory = "/home/joshsymonds"; 11 | 12 | packages = with pkgs; [ 13 | file 14 | unzip 15 | dmidecode 16 | gcc 17 | # Network tools for gateway 18 | traceroute 19 | mtr 20 | tcpdump 21 | ]; 22 | }; 23 | 24 | programs.zsh.shellAliases.update = "sudo nixos-rebuild switch --flake \".#$(hostname)\" --option warn-dirty false"; 25 | 26 | systemd.user.startServices = "sd-switch"; 27 | } 28 | -------------------------------------------------------------------------------- /home-manager/mcp/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | pkgs, 4 | ... 5 | }: let 6 | targetprocess-mcp = inputs.targetprocess-mcp.packages.${pkgs.system}.default; 7 | inherit (pkgs) mcp-atlassian; 8 | in { 9 | home.packages = with pkgs; [ 10 | targetprocess-mcp 11 | mcp-atlassian 12 | ]; 13 | 14 | home.file = { 15 | ".mcp/.keep".text = ""; 16 | ".mcp/bin/targetprocess-mcp".source = "${targetprocess-mcp}/bin/targetprocess-mcp"; 17 | ".mcp/bin/mcp-atlassian".source = "${mcp-atlassian}/bin/mcp-atlassian"; 18 | ".mcp/jira-mcp-wrapper.sh" = { 19 | source = ./jira-mcp-wrapper.sh; 20 | executable = true; 21 | }; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /home-manager/go/update-golangci-lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Update golangci-lint to the latest version using official installer 3 | 4 | set -euo pipefail 5 | 6 | echo "Installing/updating golangci-lint to latest version..." 7 | 8 | # Install the latest version using the official installer 9 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin 10 | 11 | # Verify installation 12 | if command -v golangci-lint &> /dev/null; then 13 | echo "✅ golangci-lint installed successfully!" 14 | echo "Version: $(golangci-lint --version)" 15 | else 16 | echo "❌ Failed to install golangci-lint" 17 | exit 1 18 | fi -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/plugins/terraform.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "nvim-treesitter/nvim-treesitter", 4 | opts = { ensure_installed = { "terraform", "hcl" } }, 5 | }, 6 | { 7 | "neovim/nvim-lspconfig", 8 | opts = { 9 | servers = { 10 | terraformls = {}, 11 | }, 12 | }, 13 | }, 14 | { 15 | "mason-org/mason.nvim", 16 | opts = { ensure_installed = { "tflint" } }, 17 | }, 18 | { 19 | "stevearc/conform.nvim", 20 | optional = true, 21 | opts = { 22 | formatters_by_ft = { 23 | hcl = { "packer_fmt" }, 24 | terraform = { "terraform_fmt" }, 25 | tf = { "terraform_fmt" }, 26 | ["terraform-vars"] = { "terraform_fmt" }, 27 | }, 28 | }, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /hosts/ultraviolet/services/jellyseerr.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | systemd.tmpfiles.rules = [ 3 | "d /etc/jellyseerr/config 0644 root root -" 4 | ]; 5 | 6 | virtualisation.oci-containers.containers.jellyseerr = { 7 | image = "fallenbagel/jellyseerr:2.7.3"; 8 | ports = [ 9 | "5055:5055" 10 | ]; 11 | extraOptions = [ 12 | "--network=host" 13 | "--cpu-shares=512" 14 | "--memory=2g" 15 | "--security-opt=no-new-privileges" 16 | ]; 17 | volumes = [ 18 | "/etc/jellyseerr/config:/app/config" 19 | ]; 20 | }; 21 | 22 | services.caddy.virtualHosts."jellyseerr.home.husbuddies.gay".extraConfig = '' 23 | reverse_proxy /* localhost:5055 24 | import cloudflare 25 | ''; 26 | } 27 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/config/keymaps.lua: -------------------------------------------------------------------------------- 1 | -- Keymaps are automatically loaded on the VeryLazy event 2 | -- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua 3 | -- Add any additional keymaps here 4 | 5 | local map = vim.keymap.set 6 | 7 | -- Window navigation 8 | map('n', '', 'h') 9 | map('n', '', 'j') 10 | map('n', '', 'k') 11 | map('n', '', 'l') 12 | map('n', '', 'w') 13 | map('n', '', 'W') 14 | map('n', '', ':b#') 15 | 16 | -- Other mappings 17 | map('n', 'n', 'noh') 18 | map('n', '', 'ciw') 19 | map('v', 'y', 'ygv') 20 | map('n', 'qq', 'qa', { desc = "Quit all" }) 21 | -------------------------------------------------------------------------------- /hosts/ultraviolet/services/bazarr.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | systemd.tmpfiles.rules = [ 3 | "d /etc/bazarr/config 0644 root root -" 4 | ]; 5 | 6 | virtualisation.oci-containers.containers.bazarr = { 7 | image = "linuxserver/bazarr:1.5.1"; 8 | ports = [ 9 | "6767:6767" 10 | ]; 11 | volumes = [ 12 | "/etc/bazarr/config:/config" 13 | "/mnt/video/:/mnt/video" 14 | ]; 15 | environment = { 16 | PUID = "0"; 17 | PGID = "0"; 18 | }; 19 | autoStart = true; 20 | extraOptions = [ 21 | "--network=host" 22 | ]; 23 | }; 24 | 25 | services.caddy.virtualHosts."bazarr.home.husbuddies.gay".extraConfig = '' 26 | reverse_proxy /* localhost:6767 27 | import cloudflare 28 | ''; 29 | } 30 | -------------------------------------------------------------------------------- /home-manager/git/default.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | programs.git = { 3 | enable = true; 4 | userName = "Josh Symonds"; 5 | userEmail = "josh@joshsymonds.com"; 6 | 7 | aliases = { 8 | co = "checkout"; 9 | st = "status"; 10 | a = "add --all"; 11 | pl = "pull -u"; 12 | pu = "push --all origin"; 13 | }; 14 | 15 | extraConfig = { 16 | core = { 17 | editor = "hx"; 18 | whitespace = "fix,-indent-with-non-tab,trailing-space,cr-at-eol"; 19 | }; 20 | url."ssh://git@github.com/".insteadOf = "https://github.com/"; 21 | pull = {rebase = true;}; 22 | web = {browser = "firefox";}; 23 | rerere = { 24 | enabled = 1; 25 | autoupdate = 1; 26 | }; 27 | push = {default = "simple";}; 28 | }; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /secrets/secrets.nix: -------------------------------------------------------------------------------- 1 | # Define which encrypted files belong to which recipients. 2 | # Update this attrset when creating new secrets with `agenix -e`. 3 | let 4 | keys = import ./keys.nix; 5 | in { 6 | # Shared secrets 7 | "secrets/shared/coder-db-password.age".publicKeys = keys.vermissian; 8 | "secrets/shared/coder-env.age".publicKeys = keys.vermissian; 9 | 10 | # Host-specific secrets 11 | "secrets/hosts/ultraviolet/cloudflare-api-token.age".publicKeys = keys.ultraviolet; 12 | "secrets/hosts/ultraviolet/cloudflared-token.age".publicKeys = keys.ultraviolet; 13 | "secrets/hosts/ultraviolet/redlib-collections.age".publicKeys = keys.ultraviolet; 14 | "secrets/hosts/vermissian/cloudflared-token.age".publicKeys = keys.vermissian; 15 | "secrets/hosts/vermissian/coder-ghcr-cache-auth.age".publicKeys = keys.vermissian; 16 | } 17 | -------------------------------------------------------------------------------- /modules/README.md: -------------------------------------------------------------------------------- 1 | # Custom NixOS Modules 2 | 3 | This directory contains custom NixOS modules for system-level services and configurations. 4 | 5 | ## Structure 6 | 7 | - `services/` - System service modules 8 | 9 | ## Usage 10 | 11 | Import modules in your host configuration: 12 | 13 | ```nix 14 | imports = [ 15 | ../../modules/services/your-module.nix 16 | ]; 17 | ``` 18 | 19 | Then configure the service: 20 | 21 | ```nix 22 | services.your-service = { 23 | enable = true; 24 | # configuration options 25 | }; 26 | ``` 27 | 28 | ## Adding New Modules 29 | 30 | When creating new system-level modules: 31 | 32 | 1. Place service modules in `services/` 33 | 2. Place other system modules directly in `modules/` 34 | 3. Include documentation in the same directory 35 | 4. Follow NixOS module conventions with options and config sections -------------------------------------------------------------------------------- /pkgs/deadcode/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | buildGoModule, 4 | fetchFromGitHub, 5 | }: 6 | buildGoModule rec { 7 | pname = "deadcode"; 8 | version = "0.38.0"; 9 | 10 | src = fetchFromGitHub { 11 | owner = "golang"; 12 | repo = "tools"; 13 | rev = "v${version}"; 14 | hash = "sha256-hIPIWcCeCWYRP7pUL6NeMtykDaCn4phDdIpPDb5k5XE="; 15 | }; 16 | 17 | subPackages = ["cmd/deadcode"]; 18 | 19 | vendorHash = "sha256-jweDfh6rOmhnIql8Sa6yCOOjyRj2Pq7As7nPgStP204="; 20 | 21 | ldflags = [ 22 | "-s" 23 | "-w" 24 | ]; 25 | 26 | meta = { 27 | description = "Reports unused declarations in Go packages"; 28 | homepage = "https://pkg.go.dev/golang.org/x/tools/cmd/deadcode"; 29 | license = lib.licenses.bsd3; 30 | maintainers = with lib.maintainers; []; 31 | mainProgram = "deadcode"; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /home-assistant/dashboards/popup-content/dining-room-thermostat.yaml: -------------------------------------------------------------------------------- 1 | type: vertical-stack 2 | cards: 3 | - type: custom:bubble-card 4 | card_type: separator 5 | name: Climate Control 6 | icon: mdi:thermostat-box 7 | - type: thermostat 8 | entity: climate.my_ecobee 9 | - type: custom:bubble-card 10 | card_type: separator 11 | name: Environment 12 | icon: mdi:weather-partly-cloudy 13 | - type: custom:auto-entities 14 | card: 15 | type: entities 16 | state_color: true 17 | card_param: entities 18 | filter: 19 | include: 20 | - entity_id: sensor.my_ecobee_current_temperature 21 | options: 22 | name: Air Temperature 23 | - entity_id: sensor.my_ecobee_current_humidity 24 | options: 25 | name: Humidity 26 | show_empty: false 27 | 28 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/config/clipboard.lua: -------------------------------------------------------------------------------- 1 | -- Ensure clipboard is set to use system clipboard 2 | -- This needs to run after LazyVim sets its defaults 3 | 4 | -- Set clipboard to use system clipboard for all operations 5 | vim.opt.clipboard = "unnamedplus" 6 | 7 | -- Configure linkpearl as clipboard provider if available 8 | if vim.fn.executable('linkpearl') == 1 then 9 | -- Use sh -c to handle empty input gracefully 10 | vim.g.clipboard = { 11 | name = 'linkpearl', 12 | copy = { 13 | ['+'] = {'sh', '-c', 'input=$(cat); [ -n "$input" ] && echo -n "$input" | linkpearl copy'}, 14 | ['*'] = {'sh', '-c', 'input=$(cat); [ -n "$input" ] && echo -n "$input" | linkpearl copy'}, 15 | }, 16 | paste = { 17 | ['+'] = {'linkpearl', 'paste'}, 18 | ['*'] = {'linkpearl', 'paste'}, 19 | }, 20 | cache_enabled = 0, 21 | } 22 | end -------------------------------------------------------------------------------- /home-manager/claude-code/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "BASH_DEFAULT_TIMEOUT_MS": "300000", 4 | "BASH_MAX_TIMEOUT_MS": "600000" 5 | }, 6 | "mcpServers": {}, 7 | "statusLine": { 8 | "type": "command", 9 | "command": "~/.claude/bin/cc-tools-statusline", 10 | "padding": 0 11 | }, 12 | "hooks": { 13 | "PostToolUse": [ 14 | { 15 | "matcher": "Write|Edit|MultiEdit", 16 | "hooks": [ 17 | { 18 | "type": "command", 19 | "command": "~/.claude/bin/cc-tools-validate" 20 | } 21 | ] 22 | } 23 | ], 24 | "Stop": [ 25 | { 26 | "matcher": "", 27 | "hooks": [ 28 | { 29 | "type": "command", 30 | "command": "~/.claude/hooks/ntfy-notifier.sh" 31 | } 32 | ] 33 | } 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /hosts/stygianlibrary/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | {modulesPath, ...}: { 2 | imports = [ 3 | (modulesPath + "/installer/scan/not-detected.nix") 4 | ]; 5 | 6 | boot = { 7 | initrd = { 8 | availableKernelModules = ["xhci_pci" "nvme" "usb_storage" "sd_mod"]; 9 | kernelModules = []; 10 | }; 11 | kernelModules = ["coretemp" "kvm-intel"]; 12 | extraModulePackages = []; 13 | }; 14 | 15 | fileSystems = { 16 | "/" = { 17 | device = "/dev/disk/by-label/STYGIAN-ROOT"; 18 | fsType = "ext4"; 19 | options = ["noatime" "nodiratime"]; 20 | }; 21 | 22 | "/boot" = { 23 | device = "/dev/disk/by-label/STYGIAN-EFI"; 24 | fsType = "vfat"; 25 | }; 26 | 27 | "/scratch" = { 28 | fsType = "tmpfs"; 29 | options = ["size=64G" "mode=1777"]; 30 | }; 31 | }; 32 | 33 | swapDevices = []; 34 | } 35 | -------------------------------------------------------------------------------- /scripts/watch-bike-sensors.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | interval="${1:-0.5}" 5 | 6 | while true; do 7 | ts=$(date -u +"%H:%M:%S.%3N") 8 | loc_json=$(hass-cli -o json state get sensor.josh_nice_bike_location) 9 | move_json=$(hass-cli -o json state get sensor.joshs_nice_bike_1b75_estimated_distance) 10 | 11 | loc_state=$(jq -r '.[0].state' <<<"$loc_json") 12 | loc_dist=$(jq -r '.[0].attributes.last_distance // "unknown"' <<<"$loc_json") 13 | loc_source=$(jq -r '.[0].attributes.last_source // "unknown"' <<<"$loc_json") 14 | move_dist=$(jq -r '.[0].state' <<<"$move_json") 15 | move_source=$(jq -r '.[0].attributes.source // "unknown"' <<<"$move_json") 16 | 17 | printf '%s regular:%s %sft via=%s | movement:%sft via=%s\n' \ 18 | "$ts" "$loc_state" "$loc_dist" "$loc_source" "$move_dist" "$move_source" 19 | 20 | sleep "$interval" 21 | done 22 | -------------------------------------------------------------------------------- /home-manager/hosts/stygianlibrary.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | imports = [ 3 | ../common.nix 4 | ../devspaces-host 5 | ../linkpearl 6 | ../security-tools 7 | ../gmailctl 8 | ]; 9 | 10 | home = { 11 | homeDirectory = "/home/joshsymonds"; 12 | 13 | packages = with pkgs; [ 14 | git-lfs 15 | jq 16 | python312Packages.accelerate 17 | python312Packages.bitsandbytes 18 | python312Packages.huggingface-hub 19 | python312Packages.tiktoken 20 | python312Packages.transformers 21 | sqlite 22 | ]; 23 | }; 24 | 25 | programs.zsh.shellAliases = { 26 | update = "sudo nixos-rebuild switch --flake \".#$(hostname)\" --option warn-dirty false"; 27 | infer = "OLLAMA_HOST=127.0.0.1 ollama run"; 28 | models = "OLLAMA_HOST=127.0.0.1 ollama list"; 29 | }; 30 | 31 | systemd.user.startServices = "sd-switch"; 32 | } 33 | -------------------------------------------------------------------------------- /pkgs/starlark-lsp/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | buildGoModule, 4 | fetchFromGitHub, 5 | }: 6 | buildGoModule rec { 7 | pname = "starlark-lsp"; 8 | version = "0.0.0-20240730211532"; 9 | 10 | src = fetchFromGitHub { 11 | owner = "tilt-dev"; 12 | repo = "starlark-lsp"; 13 | rev = "5689e7e8a3aa8ab55eca07d215054a0f25dbc17c"; 14 | hash = "sha256-zsrUuU5aBjDaXONwETwxHPeiAOvM89xqj8whlqd6t9U="; 15 | }; 16 | 17 | vendorHash = "sha256-PqMed2czM5BxnQs9O641W9MlrVZe0Uv+bII1KK4h974="; 18 | 19 | # Build the specific binary we want 20 | subPackages = ["cmd/starlark-lsp"]; 21 | 22 | ldflags = ["-s" "-w"]; 23 | 24 | meta = with lib; { 25 | description = "Starlark Language Server Protocol implementation"; 26 | homepage = "https://github.com/tilt-dev/starlark-lsp"; 27 | license = licenses.asl20; 28 | maintainers = with maintainers; []; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /secrets/keys.nix: -------------------------------------------------------------------------------- 1 | # Map hostnames or operator machines to the age recipients that may decrypt secrets. 2 | # Populate with age public keys (e.g., from `age-keygen` or `ssh-to-age`). 3 | # 4 | # Example: 5 | # { 6 | # "vermissian" = [ 7 | # "age1examplepublickey..." 8 | # ]; 9 | # "macbook" = [ 10 | # "age1anotherpublickey..." 11 | # ]; 12 | # } 13 | { 14 | "vermissian" = [ 15 | "age1gk07t276expcprxg4el8rsmap4ry3vq9ungmhs9ap3rtwljge9qsqdvnkw" 16 | "age10kwzaeajuyvfuyuh03tk6ywand899699rdxlrskh2f6x6ru9t56s02d6pg" 17 | ]; 18 | "ultraviolet" = [ 19 | "age196jc6pzxuy5hg89yezjpv8wy3jt2s3fs0r7ntfe0vrqwq80mr5jq9rdpsz" 20 | "age10kwzaeajuyvfuyuh03tk6ywand899699rdxlrskh2f6x6ru9t56s02d6pg" 21 | "age1yyrhr0zpg3xnxtstq6g3u0zrxglfhnur6387f5znwmehg36rh4cs39apxy" 22 | ]; 23 | "joshsymonds" = [ 24 | "age1yyrhr0zpg3xnxtstq6g3u0zrxglfhnur6387f5znwmehg36rh4cs39apxy" 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /home-manager/k9s/k9s/config.yaml: -------------------------------------------------------------------------------- 1 | k9s: 2 | liveViewAutoRefresh: false 3 | screenDumpDir: /tmp/screen-dumps 4 | refreshRate: 2 5 | maxConnRetry: 5 6 | readOnly: false 7 | noExitOnCtrlC: false 8 | ui: 9 | enableMouse: false 10 | headless: true 11 | logoless: true 12 | crumbsless: true 13 | reactive: false 14 | skin: catppuccin-mocha-transparent 15 | shellPod: 16 | image: busybox:1.35.0 17 | namespace: default 18 | limits: 19 | cpu: 100m 20 | memory: 100Mi 21 | imageScans: 22 | enable: false 23 | exclusions: 24 | namespaces: [] 25 | labels: {} 26 | logger: 27 | tail: 100 28 | buffer: 5000 29 | sinceSeconds: -1 30 | fullScreen: false 31 | textWrap: false 32 | showTime: false 33 | thresholds: 34 | cpu: 35 | critical: 90 36 | warn: 70 37 | memory: 38 | critical: 90 39 | warn: 70 40 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/config/git-setup.lua: -------------------------------------------------------------------------------- 1 | -- Ensure git is available for plugins like gitsigns 2 | local function setup_git_path() 3 | -- Check if git is already available 4 | if vim.fn.executable("git") == 1 then 5 | return 6 | end 7 | 8 | -- Common git locations on NixOS and other systems 9 | local git_paths = { 10 | "/run/current-system/sw/bin", 11 | "/usr/bin", 12 | "/usr/local/bin", 13 | "/opt/homebrew/bin", 14 | vim.env.HOME .. "/.nix-profile/bin", 15 | } 16 | 17 | -- Add git paths to PATH 18 | for _, path in ipairs(git_paths) do 19 | if vim.fn.isdirectory(path) == 1 then 20 | vim.env.PATH = path .. ":" .. vim.env.PATH 21 | end 22 | end 23 | 24 | -- Verify git is now available 25 | if vim.fn.executable("git") == 0 then 26 | vim.notify("Warning: git executable not found in PATH", vim.log.levels.WARN) 27 | end 28 | end 29 | 30 | -- Run setup 31 | setup_git_path() -------------------------------------------------------------------------------- /pkgs/nuclei/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | buildGoModule, 4 | fetchFromGitHub, 5 | }: 6 | buildGoModule rec { 7 | pname = "nuclei"; 8 | version = "3.3.7"; 9 | 10 | src = fetchFromGitHub { 11 | owner = "projectdiscovery"; 12 | repo = pname; 13 | rev = "v${version}"; 14 | hash = "sha256-cvbxLEXPvJgAWHAMmHXPyHtBkYOOXj9xz1zfrm8oLG4="; 15 | }; 16 | 17 | vendorHash = "sha256-2zrT/oQc+cgnxN7Y8S5Lx+Aapf10aInjUtRW73N0O3o="; 18 | 19 | subPackages = [ 20 | "cmd/nuclei" 21 | ]; 22 | 23 | ldflags = [ 24 | "-s" 25 | "-w" 26 | "-X github.com/projectdiscovery/nuclei/v3/pkg/types.VERSION=${version}" 27 | ]; 28 | 29 | meta = with lib; { 30 | description = "Fast and customizable vulnerability scanner based on simple YAML based DSL"; 31 | homepage = "https://nuclei.projectdiscovery.io/"; 32 | license = licenses.mit; 33 | maintainers = with maintainers; []; 34 | mainProgram = "nuclei"; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /modules/darwin/software.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | # GUI + CLI apps delivered via nixpkgs (appear in ~/Applications through activation helper) 3 | environment.systemPackages = with pkgs; [ 4 | aerospace 5 | eternal-terminal 6 | firefox-bin-unwrapped 7 | kitty 8 | obsidian 9 | slack 10 | slidev 11 | spotify 12 | ]; 13 | 14 | homebrew = { 15 | enable = true; 16 | casks = [ 17 | "1password" 18 | "1password-cli" 19 | "readdle-spark" 20 | "sf-symbols" 21 | ]; 22 | taps = [ 23 | "FelixKratz/formulae" 24 | "koekeishiya/formulae" 25 | ]; 26 | brews = [ 27 | { 28 | name = "sketchybar"; 29 | restart_service = "changed"; 30 | start_service = true; 31 | args = ["HEAD"]; 32 | } 33 | "pyenv" 34 | "borders" 35 | "qemu" 36 | "pam-reattach" 37 | "chruby" 38 | "ruby-install" 39 | "xz" 40 | ]; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /home-manager/claude-code/commands/prompt.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: all 3 | description: Synthesize a complete prompt by combining next.md with your arguments 4 | --- 5 | 6 | # Prompt Synthesizer 7 | 8 | Create a complete prompt by combining: 9 | 1. The next.md template from ~/.claude/commands/next.md 10 | 2. User's task: $ARGUMENTS 11 | 12 | ## Task 13 | 14 | 1. Read next.md command file 15 | 2. Replace `$ARGUMENTS` placeholder with the actual task 16 | 3. Output the complete prompt in a code block 17 | 18 | ## Output Format 19 | 20 | ``` 21 | [Complete synthesized prompt ready to copy] 22 | ``` 23 | 24 | ## Synthesis Guidelines 25 | 26 | - Preserve the workflow and requirements from next.md 27 | - If task mentions specific languages, emphasize those sections 28 | - For complex tasks, highlight "ultrathink" and "multiple agents" 29 | - For refactoring, emphasize "delete old code" requirements 30 | - Keep all critical requirements (hooks, linting, testing) 31 | 32 | Begin synthesis now. -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/config/autocmds.lua: -------------------------------------------------------------------------------- 1 | -- Autocmds are automatically loaded on the VeryLazy event 2 | -- Default autocmds that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/autocmds.lua 3 | -- 4 | -- Add any additional autocmds here 5 | -- with `vim.api.nvim_create_autocmd` 6 | -- 7 | -- Or remove existing autocmds by their group name (which is prefixed with `lazyvim_` for the defaults) 8 | -- e.g. vim.api.nvim_del_augroup_by_name("lazyvim_wrap_spell") 9 | 10 | -- Set up clipboard after LazyVim loads to prevent it from being overridden 11 | -- Use VeryLazy event which fires after LazyVim restores its clipboard settings 12 | vim.api.nvim_create_autocmd("User", { 13 | pattern = "VeryLazy", 14 | callback = function() 15 | -- Load clipboard config immediately 16 | -- LazyVim has already restored its clipboard setting by this point 17 | require("config.clipboard") 18 | end, 19 | desc = "Configure clipboard settings after LazyVim setup", 20 | }) 21 | -------------------------------------------------------------------------------- /home-manager/hosts/bluedesert.nix: -------------------------------------------------------------------------------- 1 | {lib, ...}: { 2 | imports = [ 3 | ../minimal.nix # Use minimal config for this resource-constrained box 4 | ]; 5 | 6 | # Override any specific settings for bluedesert if needed 7 | programs.zsh.shellAliases = lib.mkForce { 8 | # Remote build through ultraviolet 9 | update = "ssh -i ~/.ssh/github joshsymonds@172.31.0.200 'cd ~/nix-config && sudo env NIX_SSHOPTS=\"-i /home/joshsymonds/.ssh/github\" nixos-rebuild switch --flake .#bluedesert --target-host joshsymonds@172.31.0.201 --sudo --option warn-dirty false'"; 10 | ll = "ls -la"; 11 | l = "ls -l"; 12 | # Monitoring aliases specific to this box 13 | diskspace = "df -h / && du -sh /nix/store"; 14 | zwave-logs = "sudo podman logs zwave-js-ui 2>/dev/null || echo 'Z-Wave container not running'"; 15 | zwave-ui = "echo 'Z-Wave JS UI: http://bluedesert:8091'"; 16 | ntfy-status = "systemctl status ntfy-sh"; 17 | ntfy-test = "curl -d 'Test notification from bluedesert' localhost:8093/test"; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /home-manager/claude-code/hooks/README.md: -------------------------------------------------------------------------------- 1 | # Claude Code Hooks 2 | 3 | The Claude Code hooks have been migrated to the cc-tools project. 4 | 5 | ## Repository 6 | 7 | See https://github.com/Veraticus/cc-tools for the current implementation. 8 | 9 | ## Usage 10 | 11 | The hooks are now implemented in Go as part of cc-tools and run via the systemd service. 12 | 13 | The wrapper scripts are generated by default.nix and installed to ~/.claude/hooks/: 14 | - `lint.sh` - Runs `cc-tools lint` (PostToolUse hook for linting) 15 | - `test.sh` - Runs `cc-tools test` (PostToolUse hook for testing) 16 | - `statusline.sh` - Runs `cc-tools statusline` (status line generation) 17 | - `ntfy-notifier.sh` - Sends notifications via ntfy.sh (Stop hook) 18 | 19 | The cc-tools hooks communicate with the daemon via Unix socket at `/run/user/$(id -u)/cc-tools.sock`. 20 | 21 | ## Future Plans 22 | 23 | The `ntfy-notifier.sh` script may eventually be integrated into cc-tools as part of a comprehensive notification system. See `~/nix-config/docs/claude-notify-go-design.md` for the full design. -------------------------------------------------------------------------------- /pkgs/default.nix: -------------------------------------------------------------------------------- 1 | # Custom packages, that can be defined similarly to ones from nixpkgs 2 | # You can build them using 'nix build .#example' or (legacy) 'nix-build -A example' 3 | {pkgs ? (import ../nixpkgs.nix) {}, ...}: let 4 | darwinOnly = 5 | if pkgs.stdenv.hostPlatform.isDarwin 6 | then { 7 | aerospace = pkgs.callPackage ./aerospace {}; 8 | } 9 | else {}; 10 | in 11 | { 12 | myCaddy = pkgs.callPackage ./caddy {}; 13 | starlark-lsp = pkgs.callPackage ./starlark-lsp {}; 14 | nuclei = pkgs.callPackage ./nuclei {}; 15 | mcp-atlassian = pkgs.callPackage ./mcp-atlassian {}; 16 | claudeCodeCli = pkgs.callPackage ./claude-code-cli {}; 17 | geminiCli = pkgs.callPackage ./gemini-cli {}; 18 | deadcode = pkgs.callPackage ./deadcode {}; 19 | golangciLintBin = pkgs.callPackage ./golangci-lint-bin {}; 20 | heretic = pkgs.callPackage ./heretic {python3Packages = pkgs.python312Packages;}; 21 | coder = pkgs.callPackage ./coder-cli {inherit (pkgs) unzip;}; 22 | slidev = pkgs.callPackage ./slidev {}; 23 | } 24 | // darwinOnly 25 | -------------------------------------------------------------------------------- /home-assistant/dashboards/views/sections/adu-shed.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: button 3 | button_type: name 4 | name: Shed 5 | icon: mdi:home-variant 6 | show_state: false 7 | show_last_changed: false 8 | show_attribute: false 9 | card_layout: large 10 | sub_button: 11 | - entity: binary_sensor.shed_door 12 | name: Door 13 | icon: mdi:door-closed 14 | show_state: false 15 | show_background: true 16 | show_icon: true 17 | styles: |- 18 | ${(() => { 19 | const s = hass.states['binary_sensor.shed_door']?.state === 'on'; 20 | const bg = s ? 'rgba(100, 181, 246, 0.28)' : 'rgba(76, 175, 80, 0.30)'; 21 | const color = s ? '#1E88E5' : '#43a047'; 22 | if (subButtonIcon && subButtonIcon[0]) { 23 | subButtonIcon[0].setAttribute('icon', s ? 'mdi:door-open' : 'mdi:door-closed'); 24 | } 25 | return ` 26 | .bubble-sub-button { background-color: ${bg} !important; } 27 | .bubble-sub-button-icon { --mdc-icon-size: 22px; color: ${color} !important; } 28 | `; 29 | })()} 30 | -------------------------------------------------------------------------------- /home-assistant/dashboards/views/overview-cards/kitchen.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: button 3 | button_type: name 4 | name: Kitchen 5 | icon: mdi:silverware-fork-knife 6 | show_state: false 7 | show_last_changed: false 8 | show_attribute: false 9 | card_layout: large 10 | sub_button: 11 | - entity: binary_sensor.kitchen_door 12 | name: Door 13 | show_state: false 14 | show_background: true 15 | show_icon: true 16 | styles: |- 17 | .bubble-sub-button { 18 | {% if is_state('binary_sensor.kitchen_door', 'on') %} 19 | background-color: rgba(239, 83, 80, 0.3) !important; 20 | {% else %} 21 | background-color: rgba(76, 175, 80, 0.3) !important; 22 | {% endif %} 23 | } 24 | .bubble-sub-button-icon { 25 | --mdc-icon-size: 26px; 26 | color: ${hass.states['binary_sensor.kitchen_door'].state === 'on' ? '#ef5350' : '#43a047'} !important; 27 | } 28 | button_action: 29 | tap_action: 30 | action: none 31 | hold_action: 32 | action: none 33 | tap_action: 34 | action: none 35 | hold_action: 36 | action: none 37 | 38 | -------------------------------------------------------------------------------- /home-manager/hosts/ultraviolet.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | imports = [ 3 | ../common.nix 4 | ../devspaces-host 5 | ../linkpearl 6 | ../security-tools 7 | ../gmailctl 8 | ]; 9 | 10 | home = { 11 | homeDirectory = "/home/joshsymonds"; 12 | 13 | packages = with pkgs; [ 14 | file 15 | unzip 16 | dmidecode 17 | gcc 18 | # Media server specific tools 19 | mediainfo 20 | ffmpeg 21 | 22 | # Network debugging tools (useful for media server) 23 | tcpdump # Packet capture tool 24 | lsof # List open files/ports 25 | inetutils # Network utilities (includes netstat-like tools) 26 | ]; 27 | }; 28 | 29 | programs.zsh.shellAliases = { 30 | update = "sudo nixos-rebuild switch --flake \".#$(hostname)\" --option warn-dirty false"; 31 | update-bluedesert = "cd ~/nix-config && sudo env NIX_SSHOPTS='-i /home/joshsymonds/.ssh/github' nixos-rebuild switch --flake '.#bluedesert' --target-host joshsymonds@172.31.0.201 --sudo --option warn-dirty false"; 32 | }; 33 | 34 | systemd.user.startServices = "sd-switch"; 35 | } 36 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/plugins/diagnostics.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "rachartier/tiny-inline-diagnostic.nvim", 4 | event = "VeryLazy", -- Or `LspAttach` 5 | priority = 2000, -- needs to be loaded in first 6 | config = function() 7 | require("tiny-inline-diagnostic").setup({ 8 | options = { 9 | transparent_bg = true, 10 | set_arrow_to_diag_color = true, 11 | multilines = { 12 | -- Enable multiline diagnostic messages 13 | enabled = true, 14 | 15 | -- Always show messages on all lines for multiline diagnostics 16 | always_show = true, 17 | }, 18 | break_line = { 19 | -- Enable the feature to break messages after a specific length 20 | enabled = true, 21 | 22 | -- Number of characters after which to break the line 23 | after = 30, 24 | }, 25 | multiple_diag_under_cursor = true, 26 | show_all_diags_on_cursorline = true, 27 | enable_on_insert = true, 28 | }, 29 | }) 30 | vim.diagnostic.config({ virtual_text = false }) -- Only if needed in your configuration, if you already have native LSP diagnostics 31 | end, 32 | }, 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Josh Symonds 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /home-assistant/dashboards/views/01-overview.yaml: -------------------------------------------------------------------------------- 1 | title: Overview 2 | path: overview 3 | icon: mdi:home 4 | theme: default 5 | panel: true 6 | cards: 7 | - type: vertical-stack 8 | cards: 9 | # Room cards 10 | - !include ./overview-cards/front-deck.yaml 11 | - !include ./overview-cards/living-room.yaml 12 | - !include ./overview-cards/main-bedroom.yaml 13 | - !include ./overview-cards/dining-room.yaml 14 | - !include ./overview-cards/kitchen.yaml 15 | - !include ./overview-cards/laundry-room.yaml 16 | - !include ./overview-cards/office.yaml 17 | - !include ./overview-cards/server-closet.yaml 18 | - !include ./overview-cards/back-deck.yaml 19 | # Garage Door - Single toggle button (reliable, no JS/animation) 20 | - !include ./sections/garage-door.yaml 21 | # ADU 22 | - !include ./sections/adu-header.yaml 23 | - !include ./sections/adu-shed.yaml 24 | - !include ./sections/gallery-door.yaml 25 | # Water Sensors header 26 | - !include ./sections/water-sensors-header.yaml 27 | # Leak Controls card (acknowledge / snooze) 28 | - !include ./sections/leak-monitor.yaml 29 | # Popups are preloaded at top via hidden conditional wrapper 30 | -------------------------------------------------------------------------------- /hosts/ultraviolet/home-assistant-secrets.yaml.example: -------------------------------------------------------------------------------- 1 | # Home Assistant Secrets File 2 | # Copy this to /etc/homepage/keys/home-assistant-secrets.yaml 3 | # and update with your actual values 4 | # 5 | # This file will be included in Home Assistant's configuration 6 | # using !include_dir_merge_named for runtime secret injection 7 | 8 | # Your actual location (keep private!) 9 | latitude: 34.4208 # Replace with your actual latitude 10 | longitude: -119.6982 # Replace with your actual longitude 11 | elevation: 50 # Your elevation in meters 12 | 13 | # API Keys 14 | ecobee_api_key: your-ecobee-api-key 15 | openweathermap_api_key: your-openweathermap-key # Optional 16 | 17 | # Z-Wave JS WebSocket URL 18 | zwave_js_url: ws://172.31.0.201:3000 19 | 20 | # ntfy notification server 21 | ntfy_server: http://172.31.0.201:8093 22 | ntfy_topic_alerts: home-alerts-x7k9m2p # Use random string 23 | ntfy_topic_water: water-sensors-a3n8k5q # Use random string 24 | ntfy_topic_security: door-sensors-p9m2n7x # Use random string 25 | 26 | # Homepage dashboard token 27 | # Generate from Home Assistant: Profile -> Security -> Long-Lived Access Tokens 28 | homepage_token: your-long-lived-access-token -------------------------------------------------------------------------------- /home-assistant/dashboards/views/sections/gallery-door.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: button 3 | button_type: name 4 | name: Gallery 5 | icon: mdi:door 6 | show_state: false 7 | show_last_changed: false 8 | show_attribute: false 9 | card_layout: large 10 | sub_button: 11 | - entity: binary_sensor.gallery_door 12 | name: Door 13 | icon: mdi:door-closed 14 | show_state: false 15 | show_background: true 16 | show_icon: true 17 | styles: |- 18 | ${(() => { 19 | const s = hass.states['binary_sensor.gallery_door']?.state === 'on'; 20 | const bg = s ? 'rgba(255, 183, 77, 0.28)' : 'rgba(76, 175, 80, 0.30)'; 21 | const color = s ? '#FB8C00' : '#43a047'; 22 | if (subButtonIcon && subButtonIcon[0]) { 23 | subButtonIcon[0].setAttribute('icon', s ? 'mdi:door-open' : 'mdi:door-closed'); 24 | } 25 | return ` 26 | .bubble-sub-button { background-color: ${bg} !important; } 27 | .bubble-sub-button-icon { --mdc-icon-size: 22px; color: ${color} !important; } 28 | `; 29 | })()} 30 | button_action: 31 | tap_action: 32 | action: more-info 33 | hold_action: 34 | action: none 35 | -------------------------------------------------------------------------------- /home-manager/headless-x86_64-linux.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | pkgs, 4 | ... 5 | }: { 6 | imports = [ 7 | ./common.nix 8 | ./devspaces-host 9 | ./linkpearl 10 | ./security-tools 11 | ./gmailctl 12 | ]; 13 | 14 | home = { 15 | homeDirectory = "/home/joshsymonds"; 16 | 17 | packages = with pkgs; [ 18 | file 19 | unzip 20 | dmidecode 21 | gcc 22 | ]; 23 | }; 24 | 25 | programs.zsh.initContent = lib.mkAfter '' 26 | # Smart update function that handles remote building for bluedesert 27 | update() { 28 | if [ "$(hostname)" = "bluedesert" ]; then 29 | ssh joshsymonds@172.31.0.200 "cd ~/nix-config && sudo nixos-rebuild switch --flake '.#bluedesert' --target-host joshsymonds@172.31.0.201 --use-remote-sudo --option warn-dirty false" 30 | else 31 | sudo nixos-rebuild switch --flake ".#$(hostname)" --option warn-dirty false 32 | fi 33 | } 34 | 35 | # Function for updating bluedesert from ultraviolet 36 | update-bluedesert() { 37 | cd ~/nix-config && sudo nixos-rebuild switch --flake '.#bluedesert' --target-host joshsymonds@172.31.0.201 --use-remote-sudo --option warn-dirty false 38 | } 39 | ''; 40 | 41 | systemd.user.startServices = "sd-switch"; 42 | } 43 | -------------------------------------------------------------------------------- /modules/services/age-identity.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | config, 4 | ... 5 | }: let 6 | inherit (config.networking) hostName; 7 | ageDir = "/etc/age"; 8 | hostKey = "/etc/ssh/ssh_host_ed25519_key"; 9 | hostAgeKey = "${ageDir}/${hostName}.agekey"; 10 | keysFile = "${ageDir}/keys.txt"; 11 | recipientsFile = "${ageDir}/recipients.txt"; 12 | in { 13 | age.identityPaths = [hostAgeKey]; 14 | 15 | system.activationScripts.ageHostKey = '' 16 | set -euo pipefail 17 | 18 | mkdir -p ${ageDir} 19 | chmod 700 ${ageDir} 20 | 21 | if [ ! -f ${hostKey} ]; then 22 | echo "WARNING: ${hostKey} not found; skipping age identity generation" 23 | else 24 | SSH_TO_AGE=${pkgs.ssh-to-age}/bin/ssh-to-age 25 | 26 | if [ ! -f ${hostAgeKey} ]; then 27 | echo "Generating age identity from ${hostKey}" 28 | $SSH_TO_AGE --private-key < ${hostKey} > ${hostAgeKey}.tmp 29 | mv ${hostAgeKey}.tmp ${hostAgeKey} 30 | chmod 600 ${hostAgeKey} 31 | fi 32 | 33 | cat ${hostAgeKey} > ${keysFile} 34 | chmod 600 ${keysFile} 35 | 36 | $SSH_TO_AGE < ${hostKey}.pub > ${recipientsFile}.tmp 37 | mv ${recipientsFile}.tmp ${recipientsFile} 38 | chmod 644 ${recipientsFile} 39 | fi 40 | ''; 41 | } 42 | -------------------------------------------------------------------------------- /modules/nix/defaults.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | outputs, 4 | lib, 5 | ... 6 | }: { 7 | nixpkgs = { 8 | overlays = [ 9 | inputs.neovim-nightly.overlays.default 10 | outputs.overlays.default 11 | outputs.overlays.darwin 12 | outputs.overlays.additions 13 | outputs.overlays.modifications 14 | outputs.overlays.unstable-packages 15 | ]; 16 | config.allowUnfree = lib.mkDefault true; 17 | }; 18 | 19 | nix = { 20 | optimise.automatic = lib.mkDefault true; 21 | settings = { 22 | experimental-features = lib.mkDefault "nix-command flakes"; 23 | extra-substituters = lib.mkDefault [ 24 | "https://nix-community.cachix.org" 25 | "https://neovim-nightly.cachix.org" 26 | "https://joshsymonds.cachix.org" 27 | "https://devenv.cachix.org" 28 | ]; 29 | extra-trusted-public-keys = lib.mkDefault [ 30 | "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" 31 | "neovim-nightly.cachix.org-1:fLrV5fy41LFKwyLAxJ0H13o6FOVGc4k6gXB5Y1dqtWw=" 32 | "joshsymonds.cachix.org-1:DajO7Bjk/Q8eQVZQZC/AWOzdUst2TGp8fHS/B1pua2c=" 33 | "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=" 34 | ]; 35 | trusted-users = ["root" "joshsymonds"]; 36 | }; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /pkgs/redlib-veraticus/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | crane, 4 | redlibSrc, 5 | redlibRev ? "dirty", 6 | rustOverlay, 7 | lib ? pkgs.lib, 8 | }: let 9 | pkgsWithRust = pkgs.extend (import rustOverlay); 10 | rustToolchain = pkgsWithRust.rust-bin.stable."1.83.0".default.override { 11 | targets = ["x86_64-unknown-linux-musl"]; 12 | }; 13 | craneLib = (crane.mkLib pkgsWithRust).overrideToolchain rustToolchain; 14 | cleanedSrc = lib.cleanSourceWith { 15 | src = craneLib.path redlibSrc; 16 | filter = path: type: 17 | (lib.hasInfix "/templates/" path) 18 | || (lib.hasInfix "/static/" path) 19 | || (craneLib.filterCargoSources path type); 20 | }; 21 | in 22 | craneLib.buildPackage { 23 | pname = "redlib-veraticus"; 24 | version = builtins.substring 0 8 redlibRev; 25 | 26 | src = cleanedSrc; 27 | strictDeps = true; 28 | doCheck = false; 29 | 30 | CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl"; 31 | CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static"; 32 | 33 | meta = { 34 | description = "Private Reddit front-end (Veraticus fork)"; 35 | homepage = "https://github.com/Veraticus/redlib"; 36 | license = lib.licenses.agpl3Only; 37 | mainProgram = "redlib"; 38 | platforms = ["x86_64-linux"]; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /home-manager/claude-code/commands/validate.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: all 3 | description: Deep validation of completed implementation 4 | --- 5 | 6 | # Implementation Validation 7 | 8 | Ultrathink and thoroughly validate the implementation we just completed 9 | 10 | ## Validation Criteria 11 | 12 | **Start with:** "Let me ultrathink about this implementation and examine the code closely" 13 | 14 | ### 1. Task Completeness 15 | - All requirements fully implemented 16 | - Edge cases properly handled 17 | - No missing functionality 18 | 19 | ### 2. Code Quality 20 | - Idiomatic for the language 21 | - Proper error handling and resource management 22 | - Functions appropriately sized and focused 23 | 24 | ### 3. Architecture Integrity 25 | - No duplicate code paths or reinvented wheels 26 | - Consistent with existing patterns 27 | - No over-engineering or unnecessary abstractions 28 | 29 | ### 4. Hidden Issues 30 | - Race conditions or concurrency bugs 31 | - Security vulnerabilities 32 | - Performance bottlenecks 33 | - Missing critical test coverage 34 | 35 | ## Response Format 36 | 37 | Be direct and honest: 38 | 39 | **✅ Done Well:** Specific achievements 40 | **⚠️ Issues Found:** Problems with severity 41 | **📊 Verdict:** Is the implementation truly complete? 42 | 43 | If issues exist, offer to fix them immediately. -------------------------------------------------------------------------------- /pkgs/claude-code-cli/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | fetchurl, 5 | makeWrapper, 6 | nodejs_24, 7 | }: 8 | stdenv.mkDerivation rec { 9 | pname = "claude-code-cli"; 10 | version = "2.0.53"; 11 | 12 | src = fetchurl { 13 | url = "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-${version}.tgz"; 14 | hash = "sha256-82bwvx5vfL9O1x5ASMwygGwXeW4HoD2k4gPpGaDx62c="; 15 | }; 16 | 17 | nativeBuildInputs = [makeWrapper]; 18 | 19 | dontConfigure = true; 20 | dontBuild = true; 21 | 22 | installPhase = '' 23 | runHook preInstall 24 | mkdir -p "$out/lib/node_modules/@anthropic-ai/claude-code" 25 | mkdir -p "$out/bin" 26 | 27 | tar -xzf "$src" 28 | cp -r package/* "$out/lib/node_modules/@anthropic-ai/claude-code/" 29 | 30 | makeWrapper ${nodejs_24}/bin/node "$out/bin/claude" \ 31 | --add-flags "$out/lib/node_modules/@anthropic-ai/claude-code/cli.js" 32 | 33 | runHook postInstall 34 | ''; 35 | 36 | meta = { 37 | description = "Anthropic Claude Code CLI for interacting with Claude from the terminal"; 38 | homepage = "https://github.com/anthropics/claude-code"; 39 | license = lib.licenses.mit; 40 | maintainers = with lib.maintainers; []; 41 | platforms = lib.platforms.all; 42 | mainProgram = "claude"; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /home-manager/claude-code/commands/check.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: all 3 | description: Verify code quality and fix all issues 4 | --- 5 | 6 | # Code Quality Check 7 | 8 | Fix all issues found during quality verification. Do not just report problems. 9 | 10 | ## Workflow 11 | 12 | 1. **Identify** - Run all validation commands 13 | 2. **Fix** - Address every issue found 14 | 3. **Verify** - Re-run until all checks pass 15 | 16 | ## Validation Commands 17 | 18 | Find and run all applicable commands: 19 | - **Lint**: `make lint`, `golangci-lint run`, `npm run lint`, `ruff check` 20 | - **Test**: `make test`, `go test ./...`, `npm test`, `pytest` 21 | - **Build**: `make build`, `go build ./...`, `npm run build` 22 | - **Format**: `gofmt`, `prettier`, `black` 23 | - **Security**: `gosec`, `npm audit`, `bandit` 24 | 25 | ## Parallel Fixing Strategy 26 | 27 | When multiple issues exist, spawn agents to fix in parallel: 28 | ``` 29 | Agent 1: Fix linting issues in module A 30 | Agent 2: Fix test failures 31 | Agent 3: Fix type errors 32 | ``` 33 | 34 | ## Go-Specific Standards 35 | 36 | - Use concrete types, not `interface{}` 37 | - Wrap errors with context 38 | - Add godoc comments 39 | - Use channels for synchronization 40 | - No `time.Sleep()` for coordination 41 | 42 | ## Success Criteria 43 | 44 | All validation commands pass with zero warnings or errors. -------------------------------------------------------------------------------- /home-manager/linkpearl/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | config, 4 | pkgs, 5 | hostname ? "unknown", 6 | ... 7 | }: let 8 | # Determine if this host should run as a server or client 9 | isServer = hostname == "ultraviolet" || hostname == "vermissian"; 10 | in { 11 | imports = [inputs.linkpearl.homeManagerModules.default]; 12 | 13 | # Linkpearl configuration - server mode for ultraviolet, client mode for others 14 | services.linkpearl = { 15 | enable = true; 16 | secretFile = "${config.xdg.configHome}/linkpearl/secret"; 17 | 18 | # Server mode: listen on port, no join addresses 19 | # Client mode: don't listen, join ultraviolet (and vermissian for Darwin) 20 | listen = 21 | if isServer 22 | then ":9437" 23 | else null; 24 | join = 25 | if hostname == "vermissian" 26 | then ["ultraviolet:9437"] # vermissian is server but also joins ultraviolet 27 | else if isServer 28 | then [] # ultraviolet doesn't join anyone 29 | else if hostname == "cloudbank" 30 | then ["ultraviolet:9437" "vermissian:9437"] 31 | else ["ultraviolet:9437"]; 32 | 33 | nodeId = hostname; 34 | verbose = false; 35 | pollInterval = "500ms"; 36 | 37 | # Use the package from the linkpearl flake 38 | package = inputs.linkpearl.packages.${pkgs.system}.default; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /hosts/ultraviolet/services/redlib.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | config, 4 | ... 5 | }: let 6 | port = 8091; 7 | collectionsEnvFile = "/run/redlib/collections.env"; 8 | collectionsSecret = config.age.secrets."redlib-collections".path; 9 | in { 10 | services.redlib = { 11 | enable = true; 12 | package = pkgs.redlib-veraticus; 13 | address = "127.0.0.1"; 14 | inherit port; 15 | settings = { 16 | REDLIB_DEFAULT_THEME = "catppuccinMocha"; 17 | REDLIB_DEFAULT_LAYOUT = "clean"; 18 | REDLIB_DEFAULT_WIDE = true; 19 | }; 20 | }; 21 | 22 | systemd.services.redlib = { 23 | path = [pkgs.coreutils pkgs.gnused]; 24 | serviceConfig.EnvironmentFile = ["-${collectionsEnvFile}"]; 25 | restartTriggers = [collectionsSecret]; 26 | preStart = '' 27 | mkdir -p /run/redlib 28 | if [ -s ${collectionsSecret} ]; then 29 | collections=$(${pkgs.gnused}/bin/sed '/^\s*$/d' ${collectionsSecret} | ${pkgs.coreutils}/bin/tr '\n' ';' | ${pkgs.gnused}/bin/sed 's/;*$//') 30 | else 31 | collections="" 32 | fi 33 | printf 'REDLIB_COLLECTIONS=%s\n' "$collections" > ${collectionsEnvFile} 34 | chmod 600 ${collectionsEnvFile} 35 | ''; 36 | }; 37 | 38 | # Cloudflare Tunnel handles public exposure (redlib.husbuddies.gay) directly, 39 | # so no local Caddy vhost is defined here. 40 | } 41 | -------------------------------------------------------------------------------- /home-assistant/dashboards/popups/dining-room-thermostat.yaml: -------------------------------------------------------------------------------- 1 | type: vertical-stack 2 | card_mod: 3 | style: | 4 | :host { display: none !important; } 5 | cards: 6 | - type: custom:bubble-card 7 | card_type: pop-up 8 | hash: '#dining-room-thermostat' 9 | name: Dining Room Thermostat 10 | icon: mdi:thermostat 11 | back_open: false 12 | close_by_clicking_outside: true 13 | bg_opacity: '88' 14 | bg_blur: '14' 15 | margin_top_mobile: '0' 16 | margin_top_desktop: '0' 17 | width_desktop: 540px 18 | is_sidebar_hidden: true 19 | background_update: false 20 | auto_close: 60000 21 | - type: custom:bubble-card 22 | card_type: separator 23 | name: Climate Control 24 | icon: mdi:thermostat-box 25 | - type: thermostat 26 | entity: climate.my_ecobee 27 | - type: custom:bubble-card 28 | card_type: separator 29 | name: Environment 30 | icon: mdi:weather-partly-cloudy 31 | - type: custom:auto-entities 32 | card: 33 | type: entities 34 | state_color: true 35 | card_param: entities 36 | filter: 37 | include: 38 | - entity_id: sensor.my_ecobee_current_temperature 39 | options: 40 | name: Air Temperature 41 | - entity_id: sensor.my_ecobee_current_humidity 42 | options: 43 | name: Humidity 44 | show_empty: false 45 | -------------------------------------------------------------------------------- /hosts/egoengine/default.nix: -------------------------------------------------------------------------------- 1 | # Egoengine Docker container configuration 2 | # Simplified entry point that builds Docker image using dockerTools 3 | { 4 | inputs, 5 | outputs, 6 | lib, 7 | pkgs, 8 | ... 9 | }: { 10 | # Import common NixOS configuration 11 | # This provides basic system settings but we don't use the full NixOS container build 12 | imports = [../common.nix]; 13 | 14 | # Basic system configuration 15 | networking.hostName = "egoengine"; 16 | system.stateVersion = "25.05"; 17 | boot = { 18 | loader = { 19 | grub.enable = false; 20 | systemd-boot.enable = false; 21 | efi.canTouchEfiVariables = false; 22 | }; 23 | }; 24 | 25 | fileSystems = lib.mkForce { 26 | "/" = { 27 | device = "nodev"; 28 | fsType = "tmpfs"; 29 | }; 30 | }; 31 | 32 | users.users.joshsymonds = lib.mkForce { 33 | isSystemUser = true; 34 | shell = pkgs.bash; 35 | group = "joshsymonds"; 36 | }; 37 | users.groups.joshsymonds = lib.mkForce {}; 38 | 39 | # Enable experimental features for nix commands 40 | nix.settings.experimental-features = ["nix-command" "flakes"]; 41 | 42 | # Build the Docker image using clean dockerTools approach 43 | # This replaces the old NixOS container build 44 | system.build.egoengineDockerImage = import ./docker-image.nix { 45 | inherit inputs outputs lib pkgs; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/config/options.lua: -------------------------------------------------------------------------------- 1 | -- Options are automatically loaded before lazy.nvim startup 2 | -- Default options that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/options.lua 3 | -- Add any additional options here 4 | -- Basic vim options 5 | local opt = vim.opt 6 | 7 | opt.wrap = true 8 | 9 | -- Clipboard will be configured after LazyVim loads to prevent overrides 10 | -- See config/clipboard-setup.lua which is loaded via autocmd 11 | -- opt.textwidth = 0 12 | -- opt.scrolloff = 4 13 | -- opt.wildmode = "longest:full,full" 14 | -- opt.wildoptions = "pum" 15 | -- opt.inccommand = "nosplit" 16 | -- opt.lazyredraw = true 17 | -- opt.showmatch = true 18 | -- opt.ignorecase = true 19 | -- opt.smartcase = true 20 | -- opt.tabstop = 2 21 | -- opt.softtabstop = 0 22 | -- opt.expandtab = true 23 | -- opt.shiftwidth = 2 24 | -- opt.number = true 25 | -- opt.backspace = "indent,eol,start" 26 | -- opt.smartindent = true 27 | -- opt.laststatus = 3 28 | -- opt.showmode = false 29 | -- opt.shada = "'20,<50,s10,h,/100" 30 | -- opt.hidden = true 31 | -- opt.joinspaces = false 32 | -- opt.updatetime = 100 33 | -- opt.conceallevel = 2 34 | -- opt.concealcursor = "nc" 35 | -- opt.previewheight = 5 36 | -- opt.synmaxcol = 500 37 | -- opt.display = "msgsep" 38 | -- opt.cursorline = true 39 | -- opt.modeline = false 40 | -- opt.mouse = "nivh" 41 | -- opt.signcolumn = "yes:1" 42 | -- opt.ruler = true 43 | opt.termguicolors = true 44 | -------------------------------------------------------------------------------- /home-manager/hosts/vermissian.nix: -------------------------------------------------------------------------------- 1 | {pkgs, ...}: { 2 | imports = [ 3 | ../common.nix 4 | ../devspaces-host 5 | ../go 6 | ../linkpearl 7 | ../security-tools 8 | ../gmailctl 9 | ]; 10 | 11 | home = { 12 | homeDirectory = "/home/joshsymonds"; 13 | 14 | packages = with pkgs; [ 15 | file 16 | unzip 17 | dmidecode 18 | gcc 19 | # Integration/automation specific tools 20 | jq 21 | httpie 22 | websocat # WebSocket client 23 | 24 | # Development tools 25 | mkcert # Local TLS certificates for development 26 | awscli2 # AWS CLI for AWS operations 27 | kind # Kubernetes in Docker for local K8s clusters 28 | kubectl # Kubernetes CLI 29 | ctlptl # Controller for Kind clusters with registry 30 | tilt # Local Kubernetes development tool 31 | postgresql # PostgreSQL client (psql) 32 | mongosh # MongoDB shell 33 | tcpdump # Packet capture tool 34 | lsof # List open files/ports 35 | inetutils # Network utilities (includes netstat-like tools) 36 | kubernetes-helm 37 | ginkgo 38 | prisma 39 | prisma-engines 40 | nodePackages.prisma 41 | 42 | rustup # Rust toolchain manager 43 | ]; 44 | }; 45 | 46 | programs.go.enable = true; 47 | programs.zsh.shellAliases.update = "sudo nixos-rebuild switch --flake \".#$(hostname)\" --option warn-dirty false"; 48 | 49 | systemd.user.startServices = "sd-switch"; 50 | } 51 | -------------------------------------------------------------------------------- /scripts/watch-keychain-events.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | ADDRESS=$(echo "${1:-DD:88:00:00:0D:13}" | tr '[:lower:]' '[:upper:]') 5 | 6 | if ! command -v hass-cli >/dev/null 2>&1; then 7 | echo "hass-cli not found in PATH" >&2 8 | exit 1 9 | fi 10 | 11 | hass-cli -o json event watch ble_passthrough.adv_received \ 12 | | jq --unbuffered --arg address "$ADDRESS" ' 13 | select(.data.address != null) 14 | | select((.data.address | ascii_upcase) == $address) 15 | | { 16 | time: .time_fired, 17 | address: .data.address, 18 | rssi: .data.rssi, 19 | uuid: ( 20 | if (.data.data | length) >= 25 then 21 | ((.data.data[9:25] | join("")) as $raw 22 | | ($raw[:8] + "-" + $raw[8:12] + "-" + $raw[12:16] + "-" + $raw[16:20] + "-" + $raw[20:])) 23 | else 24 | null 25 | end 26 | ), 27 | major: ( 28 | if (.data.data | length) >= 27 then 29 | (.data.data[25:27] | join("")) 30 | else 31 | null 32 | end 33 | ), 34 | minor: ( 35 | if (.data.data | length) >= 26 then 36 | (.data.data[24:26] | join("")) 37 | else 38 | null 39 | end 40 | ), 41 | manufacturer_data: .data.manufacturer_data, 42 | service_data: .data.service_data 43 | } 44 | ' 45 | -------------------------------------------------------------------------------- /pkgs/aerospace/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenvNoCC, 4 | fetchzip, 5 | }: 6 | stdenvNoCC.mkDerivation rec { 7 | pname = "aerospace"; 8 | version = "0.19.2-Beta"; 9 | 10 | src = fetchzip { 11 | url = "https://github.com/nikitabobko/AeroSpace/releases/download/v${version}/AeroSpace-v${version}.zip"; 12 | hash = "sha256-6RyGw84GhGwULzN0ObjsB3nzRu1HYQS/qoCvzVWOYWQ="; 13 | stripRoot = true; 14 | }; 15 | 16 | dontConfigure = true; 17 | dontBuild = true; 18 | 19 | installPhase = '' 20 | runHook preInstall 21 | 22 | mkdir -p $out/Applications 23 | cp -R AeroSpace.app $out/Applications/ 24 | 25 | install -Dm755 bin/aerospace $out/bin/aerospace 26 | 27 | install -Dm644 manpage/*.1 -t $out/share/man/man1 28 | install -Dm644 shell-completion/bash/aerospace \ 29 | $out/share/bash-completion/completions/aerospace 30 | install -Dm644 shell-completion/zsh/_aerospace \ 31 | $out/share/zsh/site-functions/_aerospace 32 | install -Dm644 shell-completion/fish/aerospace.fish \ 33 | $out/share/fish/vendor_completions.d/aerospace.fish 34 | 35 | mkdir -p $out/share/doc/${pname} 36 | cp -R legal $out/share/doc/${pname}/ 37 | 38 | runHook postInstall 39 | ''; 40 | 41 | meta = with lib; { 42 | description = "i3-inspired tiling window manager for macOS"; 43 | homepage = "https://github.com/nikitabobko/AeroSpace"; 44 | license = licenses.mit; 45 | platforms = ["aarch64-darwin" "x86_64-darwin"]; 46 | mainProgram = "aerospace"; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /coder-templates/docker-shell/README.md: -------------------------------------------------------------------------------- 1 | # Docker Shell Template 2 | 3 | This template provisions a Docker-based Coder workspace that runs a user-supplied image directly (no Envbuilder layer). It is useful for general-purpose shells or lightweight tasks that only require the pre-built image. 4 | 5 | ## Parameters 6 | 7 | | Name | Description | 8 | | --- | --- | 9 | | `docker_socket` (optional) | Override the Docker daemon URI if the provisioner is not using the default socket. | 10 | | `workspace_image` | Required OCI image to run for the workspace. Provide the tag you want Coder to start. | 11 | | `entrypoint_shell` | Shell used to launch the Coder agent script. Default: `sh`. Override if your image lacks POSIX `sh`. | 12 | 13 | ## Behaviour 14 | 15 | - The workspace volume is mounted at `/home/joshsymonds` so the entire home directory persists between sessions. 16 | - Secret cleanup follows the same contract as the Envbuilder template: set `WORKSPACE_SECRET_MANIFEST` to a newline-delimited manifest of paths to remove (relative to `$HOME` by default) and optionally `WORKSPACE_SECRET_CLEAN_CMD` for extra teardown. The egoengine image and `ee sync` populate these automatically, but other images can opt in by exporting the variables. 17 | 18 | ## Notes 19 | 20 | - Set `workspace_image` to the tag published by your CI workflow (e.g. a GHCR image). 21 | - Adjust the home path in `workspace_home` if your image uses a different user. 22 | - Install additional tools via your image build process; the template only wires the container lifecycle. 23 | -------------------------------------------------------------------------------- /home-assistant/dashboards/views/overview-cards/living-room.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: button 3 | button_type: slider 4 | entity: light.living_room 5 | name: Living Room 6 | icon: mdi:sofa 7 | show_state: true 8 | show_last_changed: false 9 | show_attribute: true 10 | attribute: brightness 11 | slider_live_update: true 12 | allow_light_slider_to_0: false 13 | card_layout: large 14 | sub_button: 15 | - entity: binary_sensor.front_door 16 | name: Door 17 | show_state: false 18 | show_background: true 19 | show_icon: true 20 | styles: |- 21 | .bubble-sub-button { 22 | {% if is_state('binary_sensor.front_door', 'on') %} 23 | background-color: rgba(239, 83, 80, 0.3) !important; 24 | {% else %} 25 | background-color: rgba(76, 175, 80, 0.3) !important; 26 | {% endif %} 27 | } 28 | .bubble-sub-button-icon { 29 | --mdc-icon-size: 26px; 30 | color: ${hass.states['binary_sensor.front_door'].state === 'on' ? '#ef5350' : '#43a047'} !important; 31 | } 32 | - icon: mdi:dots-horizontal 33 | show_background: true 34 | tap_action: 35 | action: fire-dom-event 36 | browser_mod: 37 | service: browser_mod.popup 38 | data: 39 | title: Living Room Lights 40 | content: !include ../../popup-content/living-room.yaml 41 | button_action: 42 | tap_action: 43 | action: toggle 44 | hold_action: 45 | action: none 46 | tap_action: 47 | action: toggle 48 | hold_action: 49 | action: navigate 50 | navigation_path: '#living-room-lights' 51 | -------------------------------------------------------------------------------- /home-manager/nvim/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | config, 4 | pkgs, 5 | ... 6 | }: let 7 | lazySources = import ./lazy-plugins.nix {inherit (pkgs) fetchFromGitHub;}; 8 | lazyPluginCache = pkgs.runCommand "lazyvim-plugins-cache" {} '' 9 | set -euo pipefail 10 | mkdir -p $out/share/nvim/lazy 11 | ${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: src: '' 12 | ${pkgs.coreutils}/bin/cp -a ${src} $out/share/nvim/lazy/${name} 13 | '') 14 | lazySources)} 15 | ''; 16 | lazyDataDir = "${config.home.homeDirectory}/.local/share/nvim/lazy"; 17 | in { 18 | home.packages = with pkgs; [ 19 | ripgrep 20 | fd 21 | ]; 22 | 23 | programs.neovim = { 24 | enable = true; 25 | defaultEditor = false; 26 | viAlias = true; 27 | vimAlias = true; 28 | package = pkgs.neovim; 29 | 30 | extraPackages = with pkgs; [git]; 31 | withNodeJs = false; 32 | withPython3 = true; 33 | 34 | extraConfig = '' 35 | let $PATH = $PATH . ':${pkgs.git}/bin' 36 | ''; 37 | }; 38 | 39 | xdg.configFile."nvim" = { 40 | source = ./nvim; 41 | recursive = true; 42 | force = true; 43 | }; 44 | home.activation.syncLazyPlugins = lib.hm.dag.entryAfter ["writeBoundary"] '' 45 | lazy_dir=${lib.escapeShellArg lazyDataDir} 46 | cache_dir=${lib.escapeShellArg (lazyPluginCache + "/share/nvim/lazy")} 47 | $DRY_RUN_CMD rm -rf "$lazy_dir" 48 | $DRY_RUN_CMD mkdir -p "$(dirname "$lazy_dir")" 49 | $DRY_RUN_CMD ${pkgs.coreutils}/bin/cp -a "$cache_dir" "$lazy_dir" 50 | ''; 51 | } 52 | -------------------------------------------------------------------------------- /home-manager/devspaces-host/default.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | # Devspace host configuration - for running ON ultraviolet 3 | programs.zsh.shellAliases = let 4 | contextAlias = name: icon: "t ${name} ${icon}"; 5 | in { 6 | # Local tmux session aliases - attach/create via t with explicit icons 7 | mercury = contextAlias "mercury" "☿"; 8 | venus = contextAlias "venus" "♀"; 9 | earth = contextAlias "earth" "♁"; 10 | mars = contextAlias "mars" "♂"; 11 | jupiter = contextAlias "jupiter" "♃"; 12 | 13 | # Status command to see what's running locally 14 | devspace-status = "tmux list-sessions 2>/dev/null || echo \"No active sessions\""; 15 | 16 | # Quick aliases for common operations 17 | ds = "devspace-status"; 18 | dsl = "tmux list-sessions -F \"#{session_name}: #{session_windows} windows, created #{session_created_string}\" 2>/dev/null || echo \"No sessions\""; 19 | }; 20 | 21 | # Helper function for devspace information 22 | programs.zsh.initContent = '' 23 | devspaces() { 24 | echo "🌌 Development Spaces" 25 | echo "━━━━━━━━━━━━━━━━━━━" 26 | echo 27 | echo "Available commands:" 28 | echo " mercury - Quick experiments and prototypes" 29 | echo " venus - Personal creative projects" 30 | echo " earth - Primary work project" 31 | echo " mars - Secondary work project" 32 | echo " jupiter - Large personal project" 33 | echo 34 | echo " ds - Quick status check" 35 | echo " dsl - Detailed session list" 36 | echo 37 | echo "Just type the planet name to connect!" 38 | } 39 | ''; 40 | } 41 | -------------------------------------------------------------------------------- /pkgs/heretic/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | python3Packages, 4 | fetchFromGitHub, 5 | ... 6 | }: let 7 | torchPackage = 8 | if python3Packages ? pytorchWithCuda 9 | then python3Packages.pytorchWithCuda 10 | else python3Packages.pytorch; 11 | 12 | pythonPackages = python3Packages.override { 13 | overrides = self: super: { 14 | torch = torchPackage; 15 | pytorch = torchPackage; 16 | pytorch-bin = torchPackage; 17 | pytorchWithCuda = torchPackage; 18 | }; 19 | }; 20 | in 21 | pythonPackages.buildPythonApplication rec { 22 | pname = "heretic"; 23 | version = "1.0.1"; 24 | 25 | src = fetchFromGitHub { 26 | owner = "p-e-w"; 27 | repo = "heretic"; 28 | rev = "v${version}"; 29 | hash = "sha256-04reIiD1MbNLv8IqSqVHXQorlXjLSowy8zcJ1hFBHPg="; 30 | }; 31 | 32 | pyproject = true; 33 | 34 | nativeBuildInputs = [pythonPackages.uv-build]; 35 | 36 | propagatedBuildInputs = 37 | (with pythonPackages; [ 38 | accelerate 39 | datasets 40 | hf-transfer 41 | huggingface-hub 42 | optuna 43 | pydantic-settings 44 | questionary 45 | rich 46 | transformers 47 | ]) 48 | ++ [torchPackage]; 49 | 50 | pythonImportsCheck = ["heretic"]; 51 | 52 | doCheck = false; 53 | 54 | meta = { 55 | description = "Fully automatic censorship removal for language models"; 56 | homepage = "https://github.com/p-e-w/heretic"; 57 | license = lib.licenses.agpl3Plus; 58 | platforms = lib.platforms.linux; 59 | mainProgram = "heretic"; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /hosts/echelon/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 | # and may be overwritten by future invocations. Please make changes 3 | # to /etc/nixos/configuration.nix instead. 4 | { 5 | config, 6 | lib, 7 | modulesPath, 8 | ... 9 | }: { 10 | imports = [ 11 | (modulesPath + "/installer/scan/not-detected.nix") 12 | ]; 13 | 14 | boot = { 15 | initrd.availableKernelModules = ["ahci" "xhci_pci" "usbhid" "usb_storage" "sd_mod" "sdhci_pci"]; 16 | initrd.kernelModules = []; 17 | kernelModules = ["kvm-intel"]; 18 | extraModulePackages = []; 19 | }; 20 | 21 | fileSystems."/" = { 22 | device = "/dev/disk/by-uuid/587808a0-5f51-4ddd-bc97-e91483eeceb8"; 23 | fsType = "ext4"; 24 | }; 25 | 26 | fileSystems."/boot" = { 27 | device = "/dev/disk/by-uuid/05C7-E2B0"; 28 | fsType = "vfat"; 29 | }; 30 | 31 | swapDevices = []; 32 | 33 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 34 | # (the default) this is the recommended approach. When using systemd-networkd it's 35 | # still possible to use this option, but it's recommended to use it in conjunction 36 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 37 | networking.useDHCP = lib.mkDefault true; 38 | # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; 39 | # networking.interfaces.enp2s0.useDHCP = lib.mkDefault true; 40 | 41 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 42 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 43 | } 44 | -------------------------------------------------------------------------------- /home-manager/codex/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | config, 4 | pkgs, 5 | ... 6 | }: let 7 | homeDir = config.home.homeDirectory; 8 | notifierPath = "${homeDir}/.codex/hooks/ntfy-notifier.sh"; 9 | projectPath = "${homeDir}/nix-config"; 10 | mcpDir = "${homeDir}/.mcp"; 11 | codexConfig = '' 12 | model = "gpt-5.1-codex-max" 13 | model_reasoning_effort = "medium" 14 | 15 | notify = ["${notifierPath}"] 16 | 17 | ${lib.optionalString pkgs.stdenv.isLinux '' 18 | ''}[mcp_servers.targetprocess] 19 | command = "${mcpDir}/bin/targetprocess-mcp" 20 | 21 | [mcp_servers.jira] 22 | command = "${mcpDir}/jira-mcp-wrapper.sh" 23 | 24 | [projects."${projectPath}"] 25 | trust_level = "trusted" 26 | 27 | [tui] 28 | notifications = true 29 | ''; 30 | in { 31 | home = { 32 | # Install dependencies required by Codex and the notifier 33 | packages = with pkgs; [ 34 | codex 35 | jq 36 | curl 37 | ]; 38 | 39 | # Deploy Codex configuration and ntfy notifier 40 | file = { 41 | ".codex/hooks/ntfy-notifier.sh" = { 42 | source = ./hooks/ntfy-notifier.sh; 43 | executable = true; 44 | force = true; 45 | }; 46 | 47 | ".codex/config.toml" = { 48 | text = codexConfig; 49 | force = true; 50 | }; 51 | }; 52 | 53 | activation.codexDirectoryPermissions = lib.hm.dag.entryAfter ["writeBoundary"] '' 54 | set -euo pipefail 55 | if [ -d "$HOME/.codex" ]; then 56 | chmod 755 "$HOME/.codex" 57 | if [ -d "$HOME/.codex/hooks" ]; then 58 | chmod 755 "$HOME/.codex/hooks" 59 | fi 60 | fi 61 | ''; 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /home-manager/devspaces-client/default.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | # Devspace client configuration 3 | programs.zsh.shellAliases = let 4 | devspaceAlias = name: icon: "et vermissian:2022 -c \"tmux-devspace attach --icon '${icon}' ${name}\""; 5 | in { 6 | # Direct connection aliases - ensure TMUX_DEVSPACE/DEV_CONTEXT metadata is set 7 | mercury = devspaceAlias "mercury" "☿"; 8 | venus = devspaceAlias "venus" "♀"; 9 | earth = devspaceAlias "earth" "♁"; 10 | mars = devspaceAlias "mars" "♂"; 11 | jupiter = devspaceAlias "jupiter" "♃"; 12 | 13 | # Status command to see what's running 14 | devspace-status = "ssh vermissian 'tmux list-sessions 2>/dev/null || echo \"No active sessions\"'"; 15 | 16 | # Quick aliases for common operations 17 | ds = "devspace-status"; 18 | dsl = "ssh vermissian 'tmux list-sessions -F \"#{session_name}: #{session_windows} windows, created #{session_created_string}\" 2>/dev/null || echo \"No sessions\"'"; 19 | }; 20 | 21 | # Helper function for devspace information 22 | programs.zsh.initContent = '' 23 | devspaces() { 24 | echo "🌌 Development Spaces" 25 | echo "━━━━━━━━━━━━━━━━━━━" 26 | echo 27 | echo "Available commands:" 28 | echo " mercury - Quick experiments and prototypes" 29 | echo " venus - Personal creative projects" 30 | echo " earth - Primary work project" 31 | echo " mars - Secondary work project" 32 | echo " jupiter - Large personal project" 33 | echo 34 | echo " ds - Quick status check" 35 | echo " dsl - Detailed session list" 36 | echo 37 | echo "Just type the planet name to connect!" 38 | } 39 | ''; 40 | } 41 | -------------------------------------------------------------------------------- /hosts/bluedesert/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 | # and may be overwritten by future invocations. Please make changes 3 | # to /etc/nixos/configuration.nix instead. 4 | { 5 | config, 6 | lib, 7 | modulesPath, 8 | ... 9 | }: { 10 | imports = [ 11 | (modulesPath + "/installer/scan/not-detected.nix") 12 | ]; 13 | 14 | boot = { 15 | initrd.availableKernelModules = ["ahci" "xhci_pci" "usbhid" "usb_storage" "sd_mod" "sdhci_pci"]; 16 | initrd.kernelModules = []; 17 | kernelModules = ["kvm-intel"]; 18 | extraModulePackages = []; 19 | }; 20 | 21 | fileSystems."/" = { 22 | device = "/dev/disk/by-uuid/25be2a3e-f936-4aec-9d2c-2c706510fae0"; 23 | fsType = "ext4"; 24 | }; 25 | 26 | fileSystems."/boot" = { 27 | device = "/dev/disk/by-uuid/85C2-37D1"; 28 | fsType = "vfat"; 29 | }; 30 | 31 | swapDevices = [{device = "/dev/disk/by-uuid/b3db13b4-592a-4063-89f2-e28230ebd33f";}]; 32 | 33 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 34 | # (the default) this is the recommended approach. When using systemd-networkd it's 35 | # still possible to use this option, but it's recommended to use it in conjunction 36 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 37 | networking.useDHCP = lib.mkDefault true; 38 | # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; 39 | # networking.interfaces.enp2s0.useDHCP = lib.mkDefault true; 40 | 41 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 42 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 43 | } 44 | -------------------------------------------------------------------------------- /modules/darwin/defaults.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | ... 5 | }: { 6 | system.defaults = { 7 | NSGlobalDomain = { 8 | AppleFontSmoothing = 1; 9 | AppleInterfaceStyle = "Dark"; 10 | ApplePressAndHoldEnabled = false; 11 | AppleShowAllExtensions = true; 12 | AppleShowScrollBars = "Automatic"; 13 | NSAutomaticCapitalizationEnabled = false; 14 | NSAutomaticDashSubstitutionEnabled = false; 15 | NSAutomaticPeriodSubstitutionEnabled = false; 16 | NSAutomaticQuoteSubstitutionEnabled = false; 17 | NSAutomaticSpellingCorrectionEnabled = false; 18 | _HIHideMenuBar = false; 19 | }; 20 | 21 | dock = { 22 | autohide = true; 23 | autohide-delay = 0.0; 24 | autohide-time-modifier = 0.0; 25 | expose-animation-duration = 0.1; 26 | expose-group-apps = false; 27 | largesize = 128; 28 | launchanim = false; 29 | mineffect = "scale"; 30 | minimize-to-application = true; 31 | mru-spaces = false; 32 | orientation = "bottom"; 33 | show-process-indicators = true; 34 | show-recents = false; 35 | tilesize = 36; 36 | wvous-bl-corner = 1; 37 | wvous-br-corner = 1; 38 | wvous-tl-corner = 1; 39 | wvous-tr-corner = 1; 40 | }; 41 | 42 | finder = { 43 | CreateDesktop = false; 44 | FXDefaultSearchScope = "SCcf"; 45 | FXEnableExtensionChangeWarning = false; 46 | FXPreferredViewStyle = "Nlsv"; 47 | NewWindowTarget = "Desktop"; 48 | ShowPathbar = true; 49 | ShowStatusBar = true; 50 | _FXShowPosixPathInTitle = true; 51 | }; 52 | 53 | spaces = { 54 | spans-displays = true; 55 | }; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /home-manager/nvim/nvim/lua/plugins/colorscheme.lua: -------------------------------------------------------------------------------- 1 | return { 2 | { 3 | "catppuccin/nvim", 4 | lazy = true, 5 | name = "catppuccin", 6 | opts = { 7 | transparent_background = true, 8 | integrations = { 9 | aerial = true, 10 | alpha = true, 11 | cmp = true, 12 | dashboard = true, 13 | flash = true, 14 | gitsigns = true, 15 | headlines = true, 16 | illuminate = true, 17 | indent_blankline = { enabled = true }, 18 | leap = true, 19 | lsp_trouble = true, 20 | mason = true, 21 | markdown = true, 22 | blink_cmp = true, 23 | mini = true, 24 | native_lsp = { 25 | enabled = true, 26 | virtual_text = { 27 | errors = { "italic" }, 28 | hints = { "italic" }, 29 | warnings = { "italic" }, 30 | information = { "italic" }, 31 | }, 32 | underlines = { 33 | errors = { "undercurl" }, 34 | hints = { "undercurl" }, 35 | warnings = { "undercurl" }, 36 | information = { "undercurl" }, 37 | }, 38 | inlay_hints = { 39 | background = true, 40 | }, 41 | }, 42 | navic = { enabled = true, custom_bg = "lualine" }, 43 | neotest = true, 44 | neotree = true, 45 | noice = true, 46 | semantic_tokens = true, 47 | telescope = true, 48 | treesitter = true, 49 | treesitter_context = true, 50 | which_key = true, 51 | }, 52 | }, 53 | }, -- and this 54 | { 55 | "LazyVim/LazyVim", 56 | opts = { 57 | colorscheme = "catppuccin-mocha", 58 | }, 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /hosts/vermissian/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 | # and may be overwritten by future invocations. Please make changes 3 | # to /etc/nixos/configuration.nix instead. 4 | { 5 | config, 6 | lib, 7 | modulesPath, 8 | ... 9 | }: { 10 | imports = [ 11 | (modulesPath + "/installer/scan/not-detected.nix") 12 | ]; 13 | 14 | boot = { 15 | initrd.availableKernelModules = ["xhci_pci" "ahci" "usbhid" "sd_mod"]; 16 | initrd.kernelModules = []; 17 | kernelModules = ["kvm-intel"]; 18 | extraModulePackages = []; 19 | }; 20 | 21 | fileSystems."/" = { 22 | device = "/dev/disk/by-uuid/8b08276c-d34b-4870-8dea-c35f01fda369"; 23 | fsType = "ext4"; 24 | }; 25 | 26 | fileSystems."/boot" = { 27 | device = "/dev/disk/by-uuid/F53E-2909"; 28 | fsType = "vfat"; 29 | options = ["fmask=0077" "dmask=0077"]; 30 | }; 31 | 32 | swapDevices = [ 33 | {device = "/dev/disk/by-uuid/f293cb53-6e2d-4cad-ba09-25945b130554";} 34 | ]; 35 | 36 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 37 | # (the default) this is the recommended approach. When using systemd-networkd it's 38 | # still possible to use this option, but it's recommended to use it in conjunction 39 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 40 | networking.useDHCP = lib.mkDefault true; 41 | # networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true; 42 | # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; 43 | 44 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 45 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 46 | } 47 | -------------------------------------------------------------------------------- /home-manager/ssh-config/default.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | programs.ssh = { 3 | enable = true; 4 | enableDefaultConfig = false; 5 | 6 | extraConfig = '' 7 | # Enable Kitty terminal integration 8 | # Let the terminal type be passed through properly 9 | SendEnv TERM COLORTERM 10 | 11 | # Performance optimizations 12 | Compression yes 13 | TCPKeepAlive yes 14 | 15 | # Use faster ciphers for better responsiveness 16 | Ciphers aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com 17 | 18 | # Reuse connections for faster subsequent connections 19 | ControlMaster auto 20 | ControlPath ~/.ssh/control-%C 21 | ControlPersist 2h 22 | 23 | # Additional latency optimizations 24 | ServerAliveCountMax 3 25 | ConnectTimeout 10 26 | 27 | # Disable unnecessary features that add latency 28 | 29 | # Enable pipelining for faster command execution 30 | EnableEscapeCommandline yes 31 | 32 | # Use IPQoS for interactive sessions 33 | IPQoS lowdelay throughput 34 | ''; 35 | 36 | matchBlocks = { 37 | "*" = { 38 | forwardAgent = true; 39 | serverAliveInterval = 60; 40 | }; 41 | 42 | "ultraviolet" = { 43 | hostname = "ultraviolet"; 44 | user = "joshsymonds"; 45 | forwardX11 = true; 46 | forwardX11Trusted = true; 47 | }; 48 | 49 | "bluedesert" = { 50 | hostname = "bluedesert"; 51 | user = "joshsymonds"; 52 | forwardX11 = true; 53 | forwardX11Trusted = true; 54 | }; 55 | 56 | "echelon" = { 57 | hostname = "echelon"; 58 | user = "joshsymonds"; 59 | forwardX11 = true; 60 | forwardX11Trusted = true; 61 | }; 62 | }; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /home-assistant/dashboards/views/overview-cards/office.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: button 3 | button_type: name 4 | name: Office 5 | icon: mdi:desk 6 | show_state: false 7 | show_last_changed: false 8 | show_attribute: false 9 | card_layout: large 10 | sub_button: 11 | # Office Door 12 | - entity: binary_sensor.office_door 13 | name: Door 14 | show_state: false 15 | show_background: true 16 | show_icon: true 17 | styles: |- 18 | .bubble-sub-button { 19 | {% if is_state('binary_sensor.office_door', 'on') %} 20 | background-color: rgba(239, 83, 80, 0.3) !important; 21 | {% else %} 22 | background-color: rgba(76, 175, 80, 0.3) !important; 23 | {% endif %} 24 | } 25 | .bubble-sub-button-icon { 26 | --mdc-icon-size: 22px; 27 | color: ${hass.states['binary_sensor.office_door'].state === 'on' ? '#ef5350' : '#43a047'} !important; 28 | } 29 | # Office Deck Window (dynamic) 30 | - entity: binary_sensor.office_deck_window 31 | name: Deck Window 32 | icon: mdi:window-closed 33 | show_state: false 34 | show_background: true 35 | show_icon: true 36 | styles: |- 37 | ${(() => { 38 | const s = hass.states['binary_sensor.office_deck_window']?.state === 'on'; 39 | const bg = s ? 'rgba(100, 181, 246, 0.28)' : 'rgba(76, 175, 80, 0.30)'; 40 | const color = s ? '#1E88E5' : '#43a047'; 41 | // This is the 2nd sub-button in Office card (index 1) 42 | if (subButtonIcon && subButtonIcon[1]) { 43 | subButtonIcon[1].setAttribute('icon', s ? 'mdi:window-open' : 'mdi:window-closed'); 44 | } 45 | return ` 46 | .bubble-sub-button { background-color: ${bg} !important; } 47 | .bubble-sub-button-icon { --mdc-icon-size: 22px; color: ${color} !important; } 48 | `; 49 | })()} 50 | 51 | -------------------------------------------------------------------------------- /home-manager/claude-code/agents/go-implementer.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: go-implementer 3 | model: claude-opus-4-5-20251101 4 | description: Go implementation specialist. Use for implementing Go code. 5 | tools: Read, Write, MultiEdit, Bash, Grep 6 | --- 7 | 8 | You are an expert Go developer. Follow these non-negotiable patterns: 9 | 10 | ## Critical Patterns 11 | 12 | ### Dependency Injection 13 | - Pass dependencies as parameters, never use globals 14 | - Constructor functions accept interfaces for dependencies 15 | - Wire dependencies at main() or factory functions 16 | 17 | ### Interface Design 18 | - Define interfaces where USED, not where implemented 19 | - Keep interfaces small: 1-3 methods, never more than 5 20 | - Accept interfaces, return concrete types 21 | 22 | ### Type Safety 23 | - Never use `interface{}` or `any` unless absolutely required (JSON unmarshaling) 24 | - Create specific types for different contexts (UserID, PostID) 25 | 26 | ### Concurrency 27 | - Use channels for synchronization, never time.Sleep() 28 | - Always manage goroutine lifecycles with context or sync.WaitGroup 29 | 30 | ### Error Handling 31 | - Always wrap errors: `fmt.Errorf("context: %w", err)` 32 | - Create sentinel errors for known conditions 33 | - Check errors immediately, never ignore them 34 | 35 | ### Testing 36 | - Table-driven tests with subtests for all complex logic 37 | - Comprehensive coverage: happy path, edge cases, errors 38 | 39 | ### Code Style 40 | - Context as first parameter where applicable 41 | - Early returns to reduce nesting 42 | - Godoc comments on all exported symbols 43 | 44 | ## Never Do 45 | - Use init() for setup 46 | - Panic in libraries 47 | - Use bare returns 48 | - Create versioned functions (GetUserV2) 49 | - Use `_` for unused parameters - remove them or use them 50 | - Add `//nolint` comments - fix the issue 51 | -------------------------------------------------------------------------------- /home-assistant/dashboards/views/overview-cards/main-bedroom.yaml: -------------------------------------------------------------------------------- 1 | type: custom:bubble-card 2 | card_type: button 3 | button_type: slider 4 | entity: light.main_bedroom 5 | name: Main Bedroom 6 | icon: mdi:bed 7 | show_state: true 8 | show_last_changed: false 9 | show_attribute: true 10 | attribute: brightness 11 | slider_live_update: true 12 | allow_light_slider_to_0: false 13 | card_layout: large 14 | sub_button: 15 | # Main Bedroom Side Window (dynamic) 16 | - entity: binary_sensor.main_bedroom_side_window 17 | name: Side Window 18 | icon: mdi:window-closed 19 | show_state: false 20 | show_background: true 21 | show_icon: true 22 | styles: |- 23 | ${(() => { 24 | const s = hass.states['binary_sensor.main_bedroom_side_window']?.state === 'on'; 25 | const bg = s ? 'rgba(100, 181, 246, 0.28)' : 'rgba(76, 175, 80, 0.30)'; 26 | const color = s ? '#1E88E5' : '#43a047'; 27 | // This is the 1st sub-button on this card (index 0) 28 | if (subButtonIcon && subButtonIcon[0]) { 29 | subButtonIcon[0].setAttribute('icon', s ? 'mdi:window-open' : 'mdi:window-closed'); 30 | } 31 | return ` 32 | .bubble-sub-button { background-color: ${bg} !important; } 33 | .bubble-sub-button-icon { --mdc-icon-size: 22px; color: ${color} !important; } 34 | `; 35 | })()} 36 | - icon: mdi:dots-horizontal 37 | show_background: true 38 | tap_action: 39 | action: fire-dom-event 40 | browser_mod: 41 | service: browser_mod.popup 42 | data: 43 | title: Main Bedroom Lights 44 | content: !include ../../popup-content/main-bedroom.yaml 45 | button_action: 46 | tap_action: 47 | action: toggle 48 | hold_action: 49 | action: none 50 | tap_action: 51 | action: toggle 52 | hold_action: 53 | action: navigate 54 | navigation_path: '#main-bedroom-lights' 55 | -------------------------------------------------------------------------------- /hosts/ultraviolet/hardware-configuration.nix: -------------------------------------------------------------------------------- 1 | # Do not modify this file! It was generated by ‘nixos-generate-config’ 2 | # and may be overwritten by future invocations. Please make changes 3 | # to /etc/nixos/configuration.nix instead. 4 | { 5 | config, 6 | lib, 7 | modulesPath, 8 | ... 9 | }: { 10 | imports = [ 11 | (modulesPath + "/installer/scan/not-detected.nix") 12 | ]; 13 | 14 | boot = { 15 | initrd.availableKernelModules = ["xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod"]; 16 | initrd.kernelModules = []; 17 | kernelModules = ["kvm-intel"]; 18 | extraModulePackages = []; 19 | }; 20 | 21 | fileSystems."/" = { 22 | device = "/dev/disk/by-uuid/56785a96-81fb-4664-807a-f69291836baa"; 23 | fsType = "ext4"; 24 | }; 25 | 26 | fileSystems."/boot" = { 27 | device = "/dev/disk/by-uuid/ADF2-ACBF"; 28 | fsType = "vfat"; 29 | }; 30 | 31 | swapDevices = [{device = "/dev/disk/by-uuid/0245bcc7-b078-4cb1-b04f-12ff0d8a226e";}]; 32 | 33 | # Enables DHCP on each ethernet and wireless interface. In case of scripted networking 34 | # (the default) this is the recommended approach. When using systemd-networkd it's 35 | # still possible to use this option, but it's recommended to use it in conjunction 36 | # with explicit per-interface declarations with `networking.interfaces..useDHCP`. 37 | networking.useDHCP = lib.mkDefault false; 38 | # networking.interfaces.enp0s20f0u12.useDHCP = lib.mkDefault true; 39 | # networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true; 40 | # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true; 41 | 42 | nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; 43 | powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; 44 | hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; 45 | } 46 | -------------------------------------------------------------------------------- /home-manager/mcp/jira-mcp-wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # Load Jira configuration from ~/.config/atlassian/config.json 5 | CONFIG_FILE="$HOME/.config/atlassian/config.json" 6 | 7 | if [ ! -f "$CONFIG_FILE" ]; then 8 | echo "Error: Jira configuration not found at $CONFIG_FILE" >&2 9 | echo "Please create the file with the following structure:" >&2 10 | cat >&2 <<'EOF' 11 | { 12 | "JIRA_URL": "https://your-company.atlassian.net", 13 | "JIRA_USERNAME": "your.email@company.com", 14 | "JIRA_API_TOKEN": "your_jira_api_token" 15 | } 16 | EOF 17 | exit 1 18 | fi 19 | 20 | # Parse the JSON config file and export as environment variables 21 | JIRA_URL=$(jq -r '.JIRA_URL // empty' "$CONFIG_FILE") 22 | JIRA_USERNAME=$(jq -r '.JIRA_USERNAME // empty' "$CONFIG_FILE") 23 | JIRA_API_TOKEN=$(jq -r '.JIRA_API_TOKEN // empty' "$CONFIG_FILE") 24 | export JIRA_URL JIRA_USERNAME JIRA_API_TOKEN 25 | 26 | # Optional Confluence configuration - only export if values are present 27 | CONFLUENCE_URL=$(jq -r '.CONFLUENCE_URL // empty' "$CONFIG_FILE") 28 | CONFLUENCE_USERNAME=$(jq -r '.CONFLUENCE_USERNAME // empty' "$CONFIG_FILE") 29 | CONFLUENCE_API_TOKEN=$(jq -r '.CONFLUENCE_API_TOKEN // empty' "$CONFIG_FILE") 30 | 31 | if [ -n "$CONFLUENCE_URL" ]; then 32 | export CONFLUENCE_URL 33 | fi 34 | if [ -n "$CONFLUENCE_USERNAME" ]; then 35 | export CONFLUENCE_USERNAME 36 | fi 37 | if [ -n "$CONFLUENCE_API_TOKEN" ]; then 38 | export CONFLUENCE_API_TOKEN 39 | fi 40 | 41 | # Check required Jira variables 42 | if [ -z "$JIRA_URL" ] || [ -z "$JIRA_USERNAME" ] || [ -z "$JIRA_API_TOKEN" ]; then 43 | echo "Error: Missing required Jira configuration in $CONFIG_FILE" >&2 44 | echo "Required fields: JIRA_URL, JIRA_USERNAME, JIRA_API_TOKEN" >&2 45 | exit 1 46 | fi 47 | 48 | # Run the mcp-atlassian server 49 | exec "$HOME/.mcp/bin/mcp-atlassian" "$@" 50 | -------------------------------------------------------------------------------- /home-manager/claude-code/commands/next.md: -------------------------------------------------------------------------------- 1 | --- 2 | allowed-tools: all 3 | description: Execute production-quality implementation 4 | --- 5 | 6 | # Production Implementation 7 | 8 | Implement: $ARGUMENTS 9 | 10 | ## Required Workflow 11 | 12 | 1. **Research** - "Let me research the codebase and create a plan before implementing" 13 | 2. **Plan** - Present approach for validation 14 | 3. **Implement** - Build with continuous validation 15 | 16 | For complex architecture decisions: "Let me ultrathink about this architecture" 17 | 18 | For tasks with independent parts: Spawn multiple agents in parallel 19 | 20 | ## Implementation Standards 21 | 22 | ### Code Evolution 23 | - Replace old code entirely when refactoring 24 | - No versioned function names (processV2, handleNew) 25 | - No compatibility layers or migration code 26 | - This is a feature branch - implement the final solution directly 27 | 28 | ### Quality Checkpoints 29 | - Run linters after every 3 file edits 30 | - Validate each component works before proceeding 31 | - Run full test suite before completion 32 | - Fix linter warnings immediately when found 33 | 34 | ### Go-Specific Requirements 35 | - Use concrete types, not `interface{}` 36 | - Simple error handling with standard patterns 37 | - Channels for synchronization, not `time.Sleep()` 38 | - Follow standard project layout (cmd/, internal/, pkg/) 39 | - Document why decisions were made 40 | - Add godoc comments for exported symbols 41 | 42 | ### General Requirements 43 | - Follow existing codebase patterns 44 | - Use language-appropriate linters at maximum strictness 45 | - Write tests for business logic 46 | - Ensure end-to-end functionality 47 | 48 | ## Completion Criteria 49 | 50 | - All linters pass with zero warnings 51 | - All tests pass (including race detection) 52 | - Feature works in realistic scenarios 53 | - No TODOs or temporary code remains -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # nix-config 2 | 3 | Flake-based Nix configuration managing multiple systems. 4 | 5 | ## Systems 6 | 7 | | Host | Platform | Description | 8 | |------|----------|-------------| 9 | | cloudbank | macOS (aarch64-darwin) | Primary dev machine, Aerospace WM | 10 | | ultraviolet | NixOS (x86_64-linux) | Headless server | 11 | | bluedesert | NixOS (x86_64-linux) | Headless server | 12 | | echelon | NixOS (x86_64-linux) | Headless server | 13 | 14 | ## Essential Commands 15 | 16 | ```bash 17 | update # Rebuild current system (alias) 18 | nix flake check # Validate flake 19 | nix build .# # Build a package 20 | ``` 21 | 22 | **IMPORTANT**: Run `update` after any Nix config changes. Nothing takes effect until rebuilt. 23 | 24 | **Git gotcha**: Nix flakes only see git-tracked files. Run `git add` before `nix flake check`. 25 | 26 | ## Directory Structure 27 | 28 | ``` 29 | flake.nix # Entry point 30 | hosts/ # System configs (per-machine) 31 | home-manager/ # User configs (apps, dotfiles) 32 | claude-code/ # Claude Code agents, hooks, settings 33 | pkgs/ # Custom packages 34 | overlays/ # Nixpkgs modifications 35 | home-assistant/ # HA dashboards and config 36 | ``` 37 | 38 | ## Home Assistant 39 | 40 | Dashboards use YAML mode - edit files directly, then `update` to deploy. 41 | 42 | ```bash 43 | hass-cli state list # List entities 44 | hass-cli state get # Get entity state 45 | hass-cli service call # Call service 46 | ``` 47 | 48 | Token location: `~/.config/home-assistant/token` 49 | 50 | ## Adding Things 51 | 52 | **New system**: Create `hosts//default.nix`, add to `flake.nix` 53 | 54 | **New package**: Create `pkgs//default.nix`, add to `pkgs/default.nix` and overlay 55 | -------------------------------------------------------------------------------- /home-assistant/dashboards/popups/living-room-lights.yaml: -------------------------------------------------------------------------------- 1 | type: vertical-stack 2 | card_mod: 3 | style: | 4 | :host { display: none !important; } 5 | cards: 6 | - type: custom:bubble-card 7 | card_type: pop-up 8 | hash: '#living-room-lights' 9 | name: Living Room Lights 10 | icon: mdi:sofa 11 | back_open: false 12 | close_by_clicking_outside: true 13 | bg_opacity: '88' 14 | bg_blur: '14' 15 | margin_top_mobile: '0' 16 | margin_top_desktop: '0' 17 | width_desktop: 540px 18 | is_sidebar_hidden: true 19 | background_update: false 20 | auto_close: 60000 21 | show_header: true 22 | sub_button: 23 | - icon: mdi:tune 24 | name: Control 25 | tap_action: 26 | action: navigate 27 | navigation_path: '#living-room-lights' 28 | - icon: mdi:palette 29 | name: Scenes 30 | tap_action: 31 | action: navigate 32 | navigation_path: '#living-room-lights-scenes' 33 | - icon: mdi:lightbulb 34 | name: Lights 35 | tap_action: 36 | action: navigate 37 | navigation_path: '#living-room-lights-lights' 38 | - type: custom:bubble-card 39 | card_type: separator 40 | name: Room Control 41 | icon: mdi:lightbulb-group 42 | - type: custom:bubble-card 43 | card_type: button 44 | button_type: slider 45 | entity: light.living_room 46 | name: All Lights 47 | icon: mdi:lightbulb-group 48 | show_state: true 49 | show_attribute: true 50 | attribute: brightness 51 | slider_live_update: true 52 | allow_light_slider_to_0: true 53 | sub_button: 54 | - entity: light.living_room 55 | icon: mdi:palette 56 | name: Color 57 | tap_action: 58 | action: more-info 59 | button_action: 60 | tap_action: 61 | action: toggle 62 | hold_action: 63 | action: none 64 | -------------------------------------------------------------------------------- /home-assistant/dashboards/popups/main-bedroom-lights.yaml: -------------------------------------------------------------------------------- 1 | type: vertical-stack 2 | card_mod: 3 | style: | 4 | :host { display: none !important; } 5 | cards: 6 | - type: custom:bubble-card 7 | card_type: pop-up 8 | hash: '#main-bedroom-lights' 9 | name: Main Bedroom Lights 10 | icon: mdi:bed 11 | back_open: false 12 | close_by_clicking_outside: true 13 | bg_opacity: '88' 14 | bg_blur: '14' 15 | margin_top_mobile: '0' 16 | margin_top_desktop: '0' 17 | width_desktop: 540px 18 | is_sidebar_hidden: true 19 | background_update: false 20 | auto_close: 60000 21 | show_header: true 22 | sub_button: 23 | - icon: mdi:tune 24 | name: Control 25 | tap_action: 26 | action: navigate 27 | navigation_path: '#main-bedroom-lights' 28 | - icon: mdi:palette 29 | name: Scenes 30 | tap_action: 31 | action: navigate 32 | navigation_path: '#main-bedroom-lights-scenes' 33 | - icon: mdi:lightbulb 34 | name: Lights 35 | tap_action: 36 | action: navigate 37 | navigation_path: '#main-bedroom-lights-lights' 38 | - type: custom:bubble-card 39 | card_type: separator 40 | name: Room Control 41 | icon: mdi:lightbulb-group 42 | - type: custom:bubble-card 43 | card_type: button 44 | button_type: slider 45 | entity: light.main_bedroom 46 | name: All Lights 47 | icon: mdi:lightbulb-group 48 | show_state: true 49 | show_attribute: true 50 | attribute: brightness 51 | slider_live_update: true 52 | allow_light_slider_to_0: true 53 | sub_button: 54 | - entity: light.main_bedroom 55 | icon: mdi:palette 56 | name: Color 57 | tap_action: 58 | action: more-info 59 | button_action: 60 | tap_action: 61 | action: toggle 62 | hold_action: 63 | action: none 64 | -------------------------------------------------------------------------------- /home-manager/minimal.nix: -------------------------------------------------------------------------------- 1 | # Minimal configuration for resource-constrained bridge devices 2 | # Only includes essentials for remote management and basic operations 3 | {pkgs, ...}: { 4 | imports = [ 5 | ./git 6 | ./ssh-config # SSH configuration 7 | ./starship # Keep starship for nice prompt 8 | ./tmux # Keep tmux for persistent sessions 9 | ]; 10 | 11 | home = { 12 | username = "joshsymonds"; 13 | homeDirectory = "/home/joshsymonds"; 14 | stateVersion = "25.05"; 15 | 16 | packages = with pkgs; [ 17 | # Absolute essentials only 18 | coreutils-full 19 | curl 20 | jq # For parsing JSON from APIs 21 | htop # Lightweight monitoring 22 | nano # Simple text editor (not vim/neovim) 23 | ncdu # Disk usage analyzer (useful given storage issues) 24 | ]; 25 | 26 | sessionVariables = { 27 | EDITOR = "nano"; # Not nvim on this box 28 | }; 29 | }; 30 | 31 | programs = { 32 | # Minimal zsh config - light plugins only 33 | zsh = { 34 | enable = true; 35 | enableCompletion = true; # Keep completions, they're helpful 36 | autosuggestion.enable = false; # Save resources 37 | syntaxHighlighting.enable = false; # Save resources 38 | 39 | shellAliases = { 40 | update = "sudo nixos-rebuild switch --flake \".#$(hostname)\" --option warn-dirty false"; 41 | ll = "ls -la"; 42 | l = "ls -l"; 43 | # Monitoring aliases for this box 44 | diskspace = "df -h / && du -sh /nix/store"; 45 | zwave-logs = "sudo podman logs zwave-js-ui"; 46 | ntfy-status = "systemctl status ntfy-sh"; 47 | }; 48 | }; 49 | 50 | # Disable heavy services 51 | neovim = { 52 | enable = false; 53 | }; 54 | direnv = { 55 | enable = false; 56 | }; 57 | }; 58 | 59 | systemd.user.startServices = "sd-switch"; 60 | } 61 | -------------------------------------------------------------------------------- /modules/services/cloudflare-tunnel.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | pkgs, 4 | lib, 5 | ... 6 | }: let 7 | cfg = config.services.cloudflareTunnel; 8 | in { 9 | options.services.cloudflareTunnel = { 10 | enable = lib.mkEnableOption "Cloudflare Tunnel using a token"; 11 | 12 | tokenFile = lib.mkOption { 13 | type = lib.types.path; 14 | description = "Path to the Cloudflare tunnel token (agenix-managed)."; 15 | }; 16 | 17 | package = lib.mkOption { 18 | type = lib.types.package; 19 | default = pkgs.cloudflared; 20 | description = "cloudflared package to use."; 21 | }; 22 | }; 23 | 24 | config = lib.mkIf cfg.enable { 25 | environment.systemPackages = [cfg.package]; 26 | 27 | systemd.tmpfiles.rules = [ 28 | "d /var/lib/cloudflared 0700 cloudflared cloudflared -" 29 | ]; 30 | 31 | users.users.cloudflared = { 32 | isSystemUser = true; 33 | group = "cloudflared"; 34 | home = "/var/lib/cloudflared"; 35 | createHome = true; 36 | }; 37 | 38 | users.groups.cloudflared = {}; 39 | 40 | systemd.services.cloudflare-tunnel = { 41 | description = "Cloudflare Tunnel"; 42 | after = ["network-online.target"]; 43 | wants = ["network-online.target"]; 44 | wantedBy = ["multi-user.target"]; 45 | 46 | serviceConfig = { 47 | Type = "simple"; 48 | User = "cloudflared"; 49 | Group = "cloudflared"; 50 | Restart = "always"; 51 | RestartSec = "5s"; 52 | ExecStart = '' 53 | ${pkgs.bash}/bin/bash -c '${cfg.package}/bin/cloudflared tunnel --no-autoupdate run --token $(cat ${lib.escapeShellArg cfg.tokenFile})' 54 | ''; 55 | PrivateTmp = true; 56 | NoNewPrivileges = true; 57 | ReadOnlyPaths = [cfg.tokenFile]; 58 | }; 59 | 60 | unitConfig.ConditionPathExists = cfg.tokenFile; 61 | }; 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /home-manager/claude-code/agents/ruby-implementer.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ruby-implementer 3 | model: claude-opus-4-5-20251101 4 | description: Ruby implementation specialist. Use for implementing Ruby code. 5 | tools: Read, Write, MultiEdit, Bash, Grep 6 | --- 7 | 8 | You are an expert Ruby developer. Follow these non-negotiable patterns: 9 | 10 | ## Critical Patterns 11 | 12 | ### Method Design 13 | - Small, focused methods (< 10 lines preferred) 14 | - Guard clauses for early returns 15 | - Question marks for predicates, bangs for dangerous methods 16 | - Express intent clearly - code reads like prose 17 | 18 | ### Error Handling 19 | - Rescue specific exceptions, never `Exception` 20 | - Custom exceptions for domain errors 21 | - Use `ensure` for cleanup 22 | - Fail fast with meaningful messages 23 | 24 | ### Testing (RSpec) 25 | - Test behavior, not implementation 26 | - Use contexts for different scenarios 27 | - Shared examples for common behaviors 28 | - Let and subject for DRY tests 29 | 30 | ### Ruby Idioms 31 | - Enumerable methods over loops 32 | - Safe navigation with `&.` 33 | - Memoization with `||=` 34 | - Duck typing over type checking 35 | - Null Object Pattern for nil handling 36 | 37 | ### Class Design 38 | - Single responsibility 39 | - Dependency injection for testability 40 | - Composition over inheritance 41 | - Module mixins for shared behavior 42 | 43 | ### Rails Patterns (when applicable) 44 | - Thin controllers, logic in models/services 45 | - Scopes for reusable queries 46 | - Callbacks sparingly - prefer explicit service objects 47 | - Strong parameters for security 48 | 49 | ## Never Do 50 | - Rescue `Exception` base class 51 | - Monkey-patch core classes 52 | - Use class variables `@@` 53 | - Use `eval` with user input 54 | - Use global variables `$` 55 | - Create methods > 20 lines 56 | - Add `# rubocop:disable` comments - fix the issue 57 | - Use `_` prefix just to silence unused warnings 58 | -------------------------------------------------------------------------------- /home-manager/go/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | config, 4 | pkgs, 5 | ... 6 | }: let 7 | cfg = config.programs.go.enable or false; 8 | in { 9 | config = lib.mkIf cfg { 10 | programs.go = { 11 | package = pkgs.go_1_24; 12 | env = { 13 | GOPATH = "${config.home.homeDirectory}/go"; 14 | GOBIN = "${config.home.homeDirectory}/go/bin"; 15 | }; 16 | }; 17 | 18 | home = { 19 | packages = 20 | (with pkgs; [ 21 | go-tools 22 | gopls 23 | delve 24 | gofumpt 25 | golines 26 | gotestsum 27 | goreleaser 28 | go-task 29 | ko 30 | ]) 31 | ++ [ 32 | pkgs.golangciLintBin 33 | pkgs.deadcode 34 | ]; 35 | 36 | sessionVariables = { 37 | GO111MODULE = lib.mkDefault "on"; 38 | GOPROXY = lib.mkDefault "https://proxy.golang.org,direct"; 39 | GOTELEMETRY = lib.mkDefault "off"; 40 | GOSUMDB = lib.mkDefault "sum.golang.org"; 41 | }; 42 | 43 | sessionPath = lib.mkAfter ["$HOME/go/bin"]; 44 | 45 | file.".go-templates/.keep".text = ""; 46 | 47 | shellAliases = { 48 | got = "go test ./..."; 49 | gotv = "go test -v ./..."; 50 | gotr = "go test -race ./..."; 51 | gotc = "go test -cover ./..."; 52 | gol = "golangci-lint run"; 53 | golf = "golangci-lint run --fix"; 54 | golu = "echo 'golangci-lint is managed by Nix (pkgs.golangciLintBin); bump pkgs/golangci-lint-bin to update.'"; 55 | gomu = "go mod download && go mod tidy"; 56 | gomv = "go mod vendor"; 57 | gob = "go build"; 58 | gor = "go run"; 59 | gofmtall = "gofumpt -l -w ."; 60 | }; 61 | }; 62 | 63 | programs.git.extraConfig."diff.go" = { 64 | xfuncname = "^[ \t]*(func|type)[ \t]+([a-zA-Z_][a-zA-Z0-9_]*)"; 65 | }; 66 | }; 67 | } 68 | -------------------------------------------------------------------------------- /hosts/bluedesert/home-automation.nix: -------------------------------------------------------------------------------- 1 | {lib, ...}: { 2 | # Add zwave-js-ui user to dialout group for device access 3 | users.users.zwave-js-ui = { 4 | isSystemUser = true; 5 | group = "zwave-js-ui"; 6 | extraGroups = ["dialout"]; 7 | }; 8 | users.groups.zwave-js-ui = {}; 9 | 10 | # ntfy for push notifications (lightweight, no database) 11 | services.ntfy-sh = { 12 | enable = true; 13 | settings = { 14 | base-url = "http://bluedesert:8093"; # Required setting 15 | listen-http = ":8093"; 16 | cache-file = "/var/lib/ntfy-sh/cache.db"; # Use StateDirectory instead of /var/cache 17 | cache-duration = "12h"; 18 | behind-proxy = false; 19 | 20 | # Topics don't require auth by default - security through obscurity 21 | # Use long random topic names for security (e.g., "home-alerts-x7k9m2p") 22 | }; 23 | }; 24 | 25 | # Z-Wave JS UI - native NixOS service 26 | services.zwave-js-ui = { 27 | enable = true; 28 | serialPort = "/dev/serial/by-id/usb-Nabu_Casa_ZWA-2_80B54EE5E010-if00"; 29 | 30 | settings = { 31 | HOST = "0.0.0.0"; # Listen on all interfaces 32 | PORT = "8091"; # Web UI port 33 | }; 34 | }; 35 | 36 | # Override systemd service to fix device access 37 | systemd.services.zwave-js-ui.serviceConfig = { 38 | # Disable the chroot to allow device access 39 | RootDirectory = lib.mkForce ""; 40 | # Keep other sandboxing but allow device access 41 | PrivateDevices = lib.mkForce false; 42 | DevicePolicy = lib.mkForce "auto"; 43 | # Use the static user instead of dynamic user 44 | DynamicUser = lib.mkForce false; 45 | User = "zwave-js-ui"; 46 | Group = "zwave-js-ui"; 47 | }; 48 | 49 | # Open firewall ports 50 | networking.firewall.allowedTCPPorts = [ 51 | 3000 # Z-Wave JS WebSocket for Home Assistant 52 | 8091 # Z-Wave JS UI for management 53 | 8093 # ntfy for notifications 54 | ]; 55 | } 56 | -------------------------------------------------------------------------------- /hosts/egoengine/home-manager.nix: -------------------------------------------------------------------------------- 1 | # Standalone home-manager configuration for Docker container 2 | # This builds a home-manager activation package without NixOS 3 | { 4 | inputs, 5 | pkgs, 6 | lib, 7 | ... 8 | }: let 9 | user = "joshsymonds"; 10 | homeDirectory = "/home/${user}"; 11 | in 12 | inputs.home-manager.lib.homeManagerConfiguration { 13 | inherit pkgs; 14 | 15 | modules = [ 16 | # Import common home-manager configuration 17 | ../../home-manager/common.nix 18 | 19 | # Container-specific overrides 20 | { 21 | home = { 22 | username = user; 23 | inherit homeDirectory; 24 | stateVersion = "25.05"; 25 | 26 | # Ensure paths are correct for container environment 27 | sessionPath = [ 28 | "${homeDirectory}/.nix-profile/bin" 29 | "${homeDirectory}/.local/bin" 30 | ]; 31 | 32 | # Container-specific environment 33 | sessionVariables = { 34 | EDITOR = "hx"; 35 | LANG = "en_US.UTF-8"; 36 | LC_ALL = "en_US.UTF-8"; 37 | }; 38 | }; 39 | 40 | # Always use HTTPS for GitHub operations inside the container 41 | programs.git.extraConfig.url = lib.mkForce { 42 | "https://github.com/".insteadOf = [ 43 | "git@github.com:" 44 | "ssh://git@github.com/" 45 | ]; 46 | }; 47 | 48 | # Disable atuin daemon in container (no systemd) 49 | # Use standalone mode instead 50 | programs.atuin.daemon.enable = lib.mkForce false; 51 | } 52 | ]; 53 | 54 | extraSpecialArgs = { 55 | inherit inputs; 56 | hostname = "egoengine"; 57 | # Provide outputs but keep it minimal 58 | outputs = { 59 | overlays = import ../../overlays { 60 | inherit inputs; 61 | outputs = {}; 62 | }; 63 | }; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /home-manager/gmailctl/setup-oauth.md: -------------------------------------------------------------------------------- 1 | # Gmail OAuth Setup for gmailctl 2 | 3 | Follow these steps to set up OAuth authentication for gmailctl. You only need to do this once - the same credentials.json can be used for both accounts. 4 | 5 | ## 1. Go to Google Cloud Console 6 | Visit: https://console.developers.google.com 7 | 8 | ## 2. Create or Select a Project 9 | - If you don't have a project, create a new one 10 | - Name it something like "gmailctl" or "personal-gmail-filters" 11 | 12 | ## 3. Enable Gmail API 13 | - Go to "Enable APIs and services" 14 | - Search for "Gmail API" 15 | - Click on it and press "Enable" 16 | 17 | ## 4. Configure OAuth Consent Screen 18 | - Go to "OAuth consent screen" in the left sidebar 19 | - Choose User Type: 20 | - **Internal**: If you have a Google Workspace account 21 | - **External**: For personal Gmail accounts 22 | - Fill in the required fields: 23 | - App name: "gmailctl" 24 | - User support email: Your email 25 | - Developer contact: Your email 26 | - Click "Add or Remove Scopes" and add these exact scopes: 27 | - `https://www.googleapis.com/auth/gmail.labels` 28 | - `https://www.googleapis.com/auth/gmail.settings.basic` 29 | - Save and continue 30 | 31 | ## 5. Create OAuth Credentials 32 | - Go to "Credentials" in the left sidebar 33 | - Click "Create Credentials" → "OAuth client ID" 34 | - Application type: "Desktop app" 35 | - Name: "gmailctl" 36 | - Click "Create" 37 | 38 | ## 6. Download Credentials 39 | - Click the download button (⬇) next to your new OAuth client 40 | - Save the file as `credentials.json` 41 | - Move it to: `~/.gmailctl/credentials.json` 42 | 43 | ## 7. Complete Setup 44 | Run: `gmailctl init` 45 | 46 | This will open a browser for authentication. Grant the requested permissions. 47 | 48 | ## Next Steps 49 | Once authenticated, you can: 50 | - `gmailctl diff` - See what changes would be made 51 | - `gmailctl apply` - Apply the filter configuration 52 | - `gmailctl export` - Export your current Gmail filters -------------------------------------------------------------------------------- /home-manager/claude-code/agents/python-implementer.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: python-implementer 3 | model: claude-opus-4-5-20251101 4 | description: Python implementation specialist. Use for implementing Python code. 5 | tools: Read, Write, MultiEdit, Bash, Grep 6 | --- 7 | 8 | You are an expert Python developer. Follow these non-negotiable patterns: 9 | 10 | ## Critical Patterns 11 | 12 | ### Type Hints 13 | - All functions have type hints for parameters and returns 14 | - Use Python 3.10+ syntax with union types (`|`) 15 | - Never use `Any` except for JSON parsing or truly dynamic cases 16 | - Use Protocols for structural subtyping 17 | - Enable mypy strict mode 18 | 19 | ### Async 20 | - Use async/await for all I/O operations 21 | - Proper async context managers for resources 22 | - Concurrent execution with asyncio.gather 23 | - Rate limiting with semaphores 24 | 25 | ### Error Handling 26 | - Custom exception hierarchy for domain errors 27 | - Never catch bare `Exception` except at boundaries 28 | - Preserve error context with `from err` 29 | - User-friendly messages with technical details 30 | 31 | ### Data Modeling 32 | - Dataclasses for simple data structures 33 | - Pydantic for validation and serialization 34 | - Enums for constants 35 | - Immutability with `frozen=True` where possible 36 | 37 | ### Testing 38 | - pytest with async support and fixtures 39 | - Parametrize for edge cases 40 | - 100% coverage for business logic 41 | - Mock external dependencies 42 | 43 | ### Code Style 44 | - Guard clauses for early returns 45 | - Dependency injection, not global state 46 | - Composition over inheritance 47 | - Single responsibility per function/class 48 | 49 | ## Never Do 50 | - Use mutable default arguments 51 | - Catch bare `Exception` 52 | - Use `eval()` or `exec()` with user input 53 | - Use `global` 54 | - Shadow built-ins (`list`, `dict`, `id`) 55 | - Use `assert` for validation (disabled with -O) 56 | - Use `# type: ignore` without justification 57 | - Use `_` prefix just to silence unused warnings 58 | -------------------------------------------------------------------------------- /home-manager/claude-code/skills/nix-patterns/SKILL.md: -------------------------------------------------------------------------------- 1 | # Nix Configuration Patterns 2 | 3 | Auto-apply when editing `.nix` files or working in this nix-config repo. 4 | 5 | ## Critical Reminders 6 | 7 | **After any change**: Run `update` to rebuild. Nothing takes effect until rebuilt. 8 | 9 | **Before `nix flake check`**: Run `git add` on new/modified files. Flakes only see git-tracked files. 10 | 11 | ## Commands 12 | 13 | ```bash 14 | update # Rebuild current system 15 | nix flake check # Validate flake 16 | nix build .# # Build package 17 | nix eval .#nixosConfigurations..config.