{ inputs, config, ... }: let hmModules = config.flake.modules.homeManager; nixosModules = config.flake.modules.nixos; in { flake.modules.nixos.ai = { pkgs, ... }: let ai = import ./ai/_assets.nix { inherit pkgs; }; codexConfig = (pkgs.formats.toml { }).generate "codex-config.toml" { model = "gpt-5.6-sol"; model_reasoning_effort = "high"; plan_mode_reasoning_effort = "high"; service_tier = "default"; approvals_reviewer = "auto_review"; features.hooks = true; notify = [ "${pkgs.llm-agents.agent-deck}/bin/agent-deck" "codex-notify" ]; tui = { status_line = [ "model-with-reasoning" "current-dir" "context-remaining" "five-hour-limit" "weekly-limit" "git-branch" ]; status_line_use_colors = true; }; features.plugins = true; hooks = ai.codexHooks; mcp_servers = ai.servers // { context7 = ai.servers.context7 // { http_headers_helper = "${ai.context7Headers}"; }; nixos = ai.servers.nixos // { startup_timeout_sec = 60.0; tools.nix.approval_mode = "approve"; }; }; }; in { imports = [ nixosModules.podman ]; home-manager.sharedModules = [ hmModules.ai ]; environment.etc."codex/config.toml".source = codexConfig; nixpkgs.overlays = [ inputs.llm-agents.overlays.shared-nixpkgs ]; nix.settings = { extra-substituters = [ "https://cache.numtide.com" ]; extra-trusted-public-keys = [ "niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=" ]; }; services.ollama = { enable = true; package = pkgs.ollama-vulkan; loadModels = [ "nomic-embed-text" ]; }; }; flake.modules.homeManager.ai = { pkgs, lib, ... }: let ai = import ./ai/_assets.nix { inherit pkgs; }; in { services.podman = { volumes = { grounded-docs-config = { }; grounded-docs-data = { }; }; containers.grounded-docs = { image = "ghcr.io/arabold/docs-mcp-server:2.4.0"; network = "host"; volumes = [ "grounded-docs-config.volume:/config" "grounded-docs-data.volume:/data" ]; environment = { DOCS_MCP_APP_TELEMETRY_ENABLED = "false"; DOCS_MCP_EMBEDDING_MODEL = "openai:nomic-embed-text"; DOCS_MCP_EMBEDDINGS_VECTOR_DIMENSION = "768"; OPENAI_API_BASE = "http://127.0.0.1:11434/v1"; OPENAI_API_KEY = "ollama"; }; exec = "--protocol http --host 127.0.0.1 --port 6280"; }; }; home.packages = [ # Node-based Codex plugin hooks and skill scripts need node/npm outside project dev shells. pkgs.nodejs pkgs.python3 pkgs.llm-agents.codex pkgs.llm-agents.chatgpt pkgs.llm-agents.pi pkgs.llm-agents.claude-code pkgs.llm-agents.agent-deck pkgs.llm-agents.herdr pkgs.tmux pkgs.ctx7 ]; xdg.configFile."herdr/config.toml".text = '' onboarding = false [ui] agent_panel_sort = "spaces" prompt_new_tab_name = false [ui.toast] delivery = "off" [keys] # Super belongs to Niri; Ctrl navigation belongs to Neovim. focus_pane_left = ["prefix+h", "ctrl+alt+h"] focus_pane_down = ["prefix+j", "ctrl+alt+j"] focus_pane_up = ["prefix+k", "ctrl+alt+k"] focus_pane_right = ["prefix+l", "ctrl+alt+l"] next_workspace = "ctrl+alt+u" previous_workspace = "ctrl+alt+i" navigate_workspace_down = "u" navigate_workspace_up = "i" workspace_picker = ["prefix+w", "ctrl+alt+o"] goto = ["prefix+g", "ctrl+alt+g"] previous_agent = "ctrl+alt+p" next_agent = "ctrl+alt+n" previous_tab = ["prefix+p", "ctrl+alt+["] next_tab = ["prefix+n", "ctrl+alt+]"] new_tab = ["prefix+c", "ctrl+alt+enter"] split_vertical = ["prefix+v", "ctrl+alt+v"] split_horizontal = ["prefix+minus", "ctrl+alt+minus"] zoom = ["prefix+z", "ctrl+alt+f"] detach = ["prefix+q", "ctrl+alt+q"] swap_pane_left = ["prefix+shift+h", "ctrl+alt+shift+h"] swap_pane_down = ["prefix+shift+j", "ctrl+alt+shift+j"] swap_pane_up = ["prefix+shift+k", "ctrl+alt+shift+k"] swap_pane_right = ["prefix+shift+l", "ctrl+alt+shift+l"] resize_pane_left = "ctrl+alt+shift+left" resize_pane_down = "ctrl+alt+shift+down" resize_pane_up = "ctrl+alt+shift+up" resize_pane_right = "ctrl+alt+shift+right" resize_mode = ["prefix+r", "ctrl+alt+r"] cycle_pane_next = ["prefix+tab", "ctrl+alt+tab"] cycle_pane_previous = ["prefix+shift+tab", "ctrl+alt+shift+tab"] last_pane = "ctrl+alt+backspace" close_pane = ["prefix+x", "ctrl+alt+x"] rename_pane = ["prefix+shift+p", "ctrl+alt+shift+p"] copy_mode = ["prefix+[", "ctrl+alt+y"] edit_scrollback = ["prefix+e", "ctrl+alt+e"] close_tab = ["prefix+shift+x", "ctrl+alt+shift+x"] rename_tab = ["prefix+shift+t", "ctrl+alt+shift+a"] move_tab_previous = "ctrl+alt+shift+[" move_tab_next = "ctrl+alt+shift+]" new_workspace = ["prefix+shift+n", "ctrl+alt+shift+enter"] rename_workspace = ["prefix+shift+w", "ctrl+alt+shift+w"] close_workspace = ["prefix+shift+d", "ctrl+alt+shift+d"] open_worktree = "ctrl+alt+w" new_worktree = ["prefix+shift+g", "ctrl+alt+shift+b"] toggle_sidebar = ["prefix+b", "ctrl+alt+b"] help = ["prefix+?", "ctrl+alt+slash"] settings = ["prefix+s", "ctrl+alt+s"] reload_config = ["prefix+shift+r", "ctrl+alt+shift+r"] [[keys.command]] key = "ctrl+alt+shift+g" type = "popup" command = "lazygit" description = "Lazygit" width = "90%" height = "90%" ''; # Codex owns its writable user config; Nix owns assets and system defaults. home.file = lib.mapAttrs' ( name: source: lib.nameValuePair ".agents/skills/${name}" { inherit source; } ) ai.skills // { ".codex/AGENTS.md".text = builtins.readFile ./ai/instructions/codex.md; }; }; }