Files
lux/modules/capabilities/ai.nix
T

207 lines
6.7 KiB
Nix

{ 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;
marketplaces.nix-managed = {
source_type = "local";
source = "${ai.marketplace}";
};
plugins."ponytail@nix-managed".enabled = 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.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;
# Codex's installed-plugin scanner skips symlinked version directories.
"${ai.codexCache}" = {
source = ai.sources.ponytail;
recursive = true;
};
};
};
}