summaryrefslogtreecommitdiffstatshomepage
path: root/nix/common.nix
blob: 143b161c646b22322bb429f664c487372dd64d0f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
{
  config,
  pkgs,
  lib,
  dotfilesRoot,
  ...
}:

# Shared Home-Manager package profile. Chezmoi owns dotfile deployment.

let
  configuredClaude = pkgs.symlinkJoin {
    name = "claude-configured-${pkgs.claude-release.version}";
    paths = [ pkgs.claude-release ];
    nativeBuildInputs = [ pkgs.makeWrapper ];
    postBuild = ''
      wrapProgram "$out/bin/claude" \
        --add-flags --mcp-config \
        --add-flags "${config.home.homeDirectory}/.claude/mcp-config.json"
    '';
  };
  configuredCopilot = pkgs.symlinkJoin {
    name = "copilot-configured-${pkgs.copilot-release.version}";
    paths = [ pkgs.copilot-release ];
    nativeBuildInputs = [ pkgs.makeWrapper ];
    postBuild = ''
      wrapProgram "$out/bin/copilot" \
        --set COPILOT_ALLOW_ALL true
    '';
  };
  configuredHermes = pkgs.symlinkJoin {
    name = "hermes-configured-${pkgs.hermes-release.version}";
    paths = [ pkgs.hermes-release ];
    nativeBuildInputs = [ pkgs.makeWrapper ];
    postBuild = ''
      wrapProgram "$out/bin/hermes" \
        --set HERMES_MANAGED_DIR "${config.home.homeDirectory}/.hermes/managed"
    '';
  };
  configuredOmp = pkgs.symlinkJoin {
    name = "omp-configured-${pkgs.omp-release.version}";
    paths = [ pkgs.omp-release ];
    nativeBuildInputs = [ pkgs.makeWrapper ];
    postBuild = ''
      wrapProgram "$out/bin/omp" \
        --set PI_CONFIG_FILES "${config.home.homeDirectory}/.omp/agent/policy.yml"
    '';
  };
in
{
  home.stateVersion = "25.05";

  # ── Packages ────────────────────────────────────────────────────────────────
  home.packages = with pkgs; [
    # Editor + multiplexer
    neovim
    zellij
    tree-sitter

    # Search / move
    ripgrep
    fd
    fzf
    sd
    choose
    zoxide
    just
    chezmoi

    # Viewers
    bat
    lsd
    glow

    # Git stack
    (git.override { sendEmailSupport = true; })
    gh
    delta
    mergiraf
    git-absorb
    difftastic

    # JSON / YAML
    jq
    yq-go

    # System
    dash
    # Prefer Rust uutils for the unprefixed replacements that pass repo-local
    # usage checks. Keep GNU tar on the system PATH for now; uutils-tar still
    # rejects common GNU tar invocations like `tar -czf`.
    uutils-coreutils-noprefix
    uutils-diffutils
    uutils-findutils
    uutils-procps
    uutils-sed
    htop
    fastfetch
    hyperfine
    duf
    gdu
    procs
    yazi

    # Net
    curl
    curlie
    wget
    bind.dnsutils
    nmap
    rsync
    openssh
    mosh

    # Debug / trace / profile
    gdb
    lldb # also brings lldb-dap (used by dap.lua via type="lldb")
    strace
    samply
    t-rec
    valgrind

    # Build orchestrators
    cmake
    ninja
    ccache
    sccache

    # Source-only docs/analysis (no compiler driver)
    doxygen

    # Docs
    less
    tldr
    man-db
    man-pages
    pandoc

    # Secrets — `pass-otp` is wired as an extension so `pass otp ...`
    # works against the same store.
    gnupg
    pinentry-curses
    (pass.withExtensions (exts: [ exts.pass-otp ]))

    # C/C++ source tooling
    clang-tools
    (runCommand "run-clang-tidy" { } ''
      mkdir -p $out/bin
      for cand in ${llvmPackages.clang-unwrapped}/bin/run-clang-tidy \
                  ${llvmPackages.clang-unwrapped.python}/bin/run-clang-tidy \
                  ${llvmPackages.clang-unwrapped.python}/share/clang/run-clang-tidy.py; do
        if [ -f "$cand" ]; then
          install -m755 "$cand" $out/bin/run-clang-tidy
          exit 0
        fi
      done
      echo "run-clang-tidy not found in clang-unwrapped outputs" >&2
      exit 1
    '')

    # CI runner
    act

    # ── Rootless podman ─────────────────────────────────────────────────────
    podman
    crun # OCI runtime (lighter than runc; default for rootless)
    conmon # container monitor process
    netavark # default network stack on podman 4+
    aardvark-dns # DNS for netavark networks
    slirp4netns # rootless user-mode networking
    passt # pasta backend (slirp4netns successor; podman picks it up)
    podman-compose
    # `docker` shell shim → podman.
    (writeShellScriptBin "docker" ''exec ${podman}/bin/podman "$@"'')

    # Editor/AI agent runtimes
    nodejs_24 # copilot-language-server requires Node 24 (see ai.lua)
    uv # for project tooling that asks for `uv`/`uvx`; brings no python
    python3Packages.ipython # interactive REPL; pulls its own python, only `ipython` lands on PATH

    # AI tools
    configuredClaude # Anthropic latest release pinned in nix/releases.json
    codex-release # OpenAI stable release pinned in nix/releases.json
    configuredCopilot # GitHub stable release pinned in nix/releases.json
    configuredHermes # Hermes Agent stable release pinned in nix/releases.json
    configuredOmp # Oh My Pi stable release pinned in nix/releases.json
    opencode-release # OpenCode stable release pinned in nix/releases.json
    ori-release # OpenRouter Ori stable release pinned in nix/releases.json
    tuicr # interactive git-change reviewer; flake input, see nix/flake.nix. Skill: dot_claude/skills/tuicr/
    aibox # Bubblewrap sandbox for AI coding agent sessions; flake input, see nix/flake.nix

    # ── LSPs / formatters / linters / DAPs ─────────────────────────────────
    # LSPs
    actionlint
    autotools-language-server
    basedpyright
    bash-language-server
    dockerfile-language-server
    just-lsp
    lua-language-server
    neocmakelsp
    ruff
    rust-analyzer
    systemd-language-server
    taplo
    typescript-language-server
    vscode-langservers-extracted # cssls + html + jsonls + eslint
    yaml-language-server

    # Formatters
    mdformat
    prettier
    shfmt
    stylua

    # Linters
    codespell
    hadolint
    markdownlint-cli
    selene
    shellcheck
    shellharden
    stylelint
    typos
    yamllint

    # Zsh and plugins
    zsh
    zsh-completions
    zsh-syntax-highlighting
    zsh-autosuggestions
    zsh-history-substring-search
  ];

  # AI agent policy
  # Hermes keeps MCP and provider settings in one mutable file. Merge only the
  # shared server so authentication and provider choices stay untracked.
  home.activation.configureHermesMcp = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
    config_file="$HOME/.hermes/config.yaml"
    mkdir -p "$(dirname "$config_file")"
    if [ ! -e "$config_file" ]; then
      printf '{}\n' >"$config_file"
    fi

    if current_url="$(${pkgs.yq-go}/bin/yq -r '.mcp_servers.openaiDeveloperDocs.url // ""' "$config_file" 2>/dev/null)" \
      && current_enabled="$(${pkgs.yq-go}/bin/yq -r '.mcp_servers.openaiDeveloperDocs.enabled // false' "$config_file" 2>/dev/null)"; then
      if [ "$current_url" != "https://developers.openai.com/mcp" ] || [ "$current_enabled" != true ]; then
        ${pkgs.yq-go}/bin/yq -i '
          .mcp_servers.openaiDeveloperDocs = {
            "url": "https://developers.openai.com/mcp",
            "enabled": true
          }
        ' "$config_file"
      fi
    else
      echo "warning: cannot update invalid Hermes config: $config_file" >&2
    fi
  '';

  # direnv + nix-direnv
  programs.direnv = {
    enable = true;
    nix-direnv.enable = true;
    enableZshIntegration = false; # zshrc already calls `eval "$(direnv hook zsh)"`
  };

  # ── XDG base dirs ──────────────────────────────────────────────────────────
  xdg.enable = true;

  # ── Enable HM-managed activation messages ──────────────────────────────────
  programs.home-manager.enable = true;

  # Silence "X news items" banner on every `home-manager switch`.
  news.display = "silent";
}