{ config, lib, pkgs, vars, ... }: let # Agent/skill bodies live in the cleo-infinite checkout; symlinked so they # follow upstream on every pull. ansibleFiles = "${config.home.homeDirectory}/Projects/cleo-infinite/scripts/local-dev-ansible/files"; linkAnsibleFile = name: config.lib.file.mkOutOfStoreSymlink "${ansibleFiles}/${name}"; in { imports = [ ./programs.vscode.nix ]; home.packages = with pkgs; [ mkcert nginx ]; programs.mise = { enable = true; enableZshIntegration = false; }; programs.zsh.shellAliases = { laws = "aws --endpoint=http://localhost:4566"; assume = "source $(brew --prefix)/bin/assume"; # Always runs the latest claude-code from the flake, no local install/update needed. claude = "nix run github:sadjow/claude-code-nix"; # Same pattern for process-compose. process-compose = "nix run github:F1bonacc1/process-compose --"; # Same pattern for treehouse. treehouse = "nix run github:kunchenguid/treehouse --"; }; programs.zsh.initContent = '' # Update tab title dynamically based on state update_starship_tab_title() { # 1. Respect Claude Code active sessions if [[ -n "$CLAUDE_CODE_ENTRYPOINT" || -n "$IN_CLAUDE_CODE" ]]; then return fi # 2. Extract git branch if available local branch branch=$(git branch --show-current 2>/dev/null) # 3. Output OSC escape sequence (Branch name or directory fallback) if [[ -n "$branch" ]]; then printf "\033]2;%s\007" "$branch" else printf "\033]2;%s\007" "''${PWD:t}" fi } # Hook into Starship's user function lifecycle starship_precmd_user_func="update_starship_tab_title" ''; home.sessionVariables = { CLAUDE_CONFIG_DIR = "${config.home.homeDirectory}/.config/claude"; # Points to the Homebrew location mentioned in your caveat JAVA_HOME = "/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home"; NPM_CONFIG_USERCONFIG = "${config.xdg.configHome}/npm/npmrc"; NPM_CONFIG_PREFIX = "${config.xdg.dataHome}/npm"; GRANTED_ALIAS_CONFIGURED = "true"; # Stub SDK root so tools that require ANDROID_HOME/platform-tools/adb work # even though adb is managed by Homebrew (android-platform-tools cask). ANDROID_HOME = "${config.xdg.dataHome}/android-sdk"; ANDROID_USER_HOME = "${config.xdg.dataHome}/android"; GRADLE_USER_HOME = "${config.xdg.dataHome}/gradle"; CP_HOME_DIR = "${config.xdg.dataHome}/cocoapods"; # Ruby comes from mise, so GEM_HOME is left alone (mise keeps per-version gem dirs). GEM_SPEC_CACHE = "${config.xdg.cacheHome}/gem/specs"; BUNDLE_USER_CONFIG = "${config.xdg.configHome}/bundle/config"; BUNDLE_USER_CACHE = "${config.xdg.cacheHome}/bundle"; BUNDLE_USER_PLUGIN = "${config.xdg.dataHome}/bundle"; TREEHOUSE_ROOT = "${config.xdg.dataHome}/treehouse"; # mkcert CA and leaf certs for local dev TLS (tasks/config/dev-certs.yml). CAROOT = "${config.xdg.dataHome}/mkcert"; DEV_CERTS_DIR = "${config.xdg.dataHome}/devcerts"; # Runtime lookup from Apple Keychain — only the command, not the token, is in the store. JIRA_API_TOKEN = ''$(security find-generic-password -a "$USER" -s jira-token -w 2>/dev/null)''; # Parity with the ANSIBLE MANAGED BLOCKs the playbook writes to ~/.zshrc. DOCKER_CLI_HINTS = "false"; GRANTED_ENABLE_AUTO_REASSUME = "true"; COPILOT_OTEL_ENABLED = "true"; COPILOT_OTEL_EXPORTER_TYPE = "file"; # Timestamped per session, like the ansible line (command substitution is intentional). COPILOT_OTEL_FILE_EXPORTER_PATH = ''$HOME/.copilot/otel/copilot-otel-$(date +%Y%m%d-%H%M%S).jsonl''; }; # Share one [sso-session] across the aws-sso-util profiles so `aws` and # Granted's `assume` stay in sync (tasks/aws.yml). ~/.aws/config stays # mutable because aws-sso-util rewrites it, so this converts it in place. home.activation.awsSsoSession = lib.hm.dag.entryAfter [ "writeBoundary" ] '' script="${ansibleFiles}/aws-sso-session.py" if [ -f "$script" ] && [ -f "$HOME/.aws/config" ]; then run ${pkgs.python3}/bin/python3 -I "$script" || [ $? -eq 3 ] fi ''; home.sessionPath = [ "${config.xdg.dataHome}/npm/bin" "${config.xdg.dataHome}/mise/shims" ]; # git credential.helper=osxkeychain (parity with tasks/git.yml). Darwin-only, so # it lives here rather than in the cross-platform home.shared.nix. programs.git.settings.credential = { helper = "osxkeychain"; # Moved from the stray ~/.gitconfig; the empty entry resets the helper list for these hosts. "https://github.com".helper = [ "" "!/opt/homebrew/bin/gh auth git-credential" ]; "https://gist.github.com".helper = [ "" "!/opt/homebrew/bin/gh auth git-credential" ]; }; xdg.configFile."npm/npmrc".text = '' prefix=${config.xdg.dataHome}/npm cache=${config.xdg.cacheHome}/npm ''; xdg.dataFile."android-sdk/platform-tools/adb".source = config.lib.file.mkOutOfStoreSymlink "/opt/homebrew/bin/adb"; xdg.dataFile."android-sdk/cmdline-tools/latest/bin/sdkmanager".source = config.lib.file.mkOutOfStoreSymlink "/opt/homebrew/bin/sdkmanager"; home.file.".ssh/authorized_keys".text = (builtins.concatStringsSep "\n" vars.sshPublicKeys) + "\n"; home.file.".copilot/settings.json".text = builtins.toJSON { footer = { showModelEffort = true; showBranch = true; showDirectory = true; showContextWindow = true; showQuota = true; showAgent = true; }; }; # Claude Code (tasks/config/claude-code.yml). The remaining settings keys # (additionalDirectories, statusLine, ...) come from home.shared.nix. my.claude.settings = { awaySummaryEnabled = false; promptSuggestionEnabled = false; # Generated by /auto-mode-setup; kept as JSON so it can be re-pasted and diffed. autoMode = builtins.fromJSON (builtins.readFile ./claude/auto-mode.json); }; xdg.configFile."claude/agents/Explore.md".source = linkAnsibleFile "claude-explore-agent.md"; xdg.configFile."claude/agents/orchestrator-sonnet.md".source = linkAnsibleFile "claude-orchestrator-sonnet-agent.md"; xdg.configFile."claude/agents/orchestrator-opus.md".source = linkAnsibleFile "claude-orchestrator-opus-agent.md"; xdg.configFile."claude/skills/orchestrate/SKILL.md".source = linkAnsibleFile "claude-orchestrate-skill.md"; xdg.configFile."claude/CLAUDE.md".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/nixos/claude/CLAUDE.md"; xdg.configFile."claude/skills/prod-db".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/nixos/claude/skills/prod-db"; xdg.configFile."claude/skills/redis".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/nixos/claude/skills/redis"; # opencode (tasks/config/opencode.yml) xdg.configFile."opencode/opencode.json".text = builtins.toJSON { "$schema" = "https://opencode.ai/config.json"; model = "github-copilot/gpt-5.4"; plugin = [ "@slkiser/opencode-quota" ]; autoupdate = false; }; xdg.configFile."opencode/tui.json".text = builtins.toJSON { "$schema" = "https://opencode.ai/tui.json"; plugin = [ "@slkiser/opencode-quota" ]; }; xdg.configFile."opencode/opencode-quota/quota-toast.json".text = builtins.toJSON { enabledProviders = "auto"; percentDisplayMode = "used"; enableToast = false; tuiCompactStatus.enabled = false; tuiSidebarPanel.enabled = true; maintainerAnnouncements = { enabled = false; home = false; }; }; xdg.configFile."treehouse/config.toml".text = '' [hooks] post_create = [ "if [ \"$(basename \"$PWD\")\" = \"wellthy.com\" ]; then (git diff HEAD..origin/cleo-native-local-patch | git apply --3way 2>/dev/null || true) && git status --porcelain | awk '{print $2}' >> \"$(git rev-parse --git-path info/exclude)\" && git status --porcelain | awk '{print $2}' | xargs -r git update-index --skip-worktree; fi", "./conditional-package-manage.sh" ] ''; }