{ config, pkgs, lib, inputs, ... }: let cfg = config.dot.llm; piPath = "${config.dot.dotfilesPath}/modules/pi/agent"; opencodePath = "${config.dot.dotfilesPath}/modules/opencode"; in { options.dot.llm = { enable = lib.mkEnableOption "LLM tools"; claude-code.enable = lib.mkOption { type = lib.types.bool; default = false; description = "Enable claude-code"; }; gemini-cli.enable = lib.mkOption { type = lib.types.bool; default = false; description = "Enable gemini-cli"; }; bubblewrap.enable = lib.mkOption { type = lib.types.bool; default = pkgs.stdenv.isLinux; description = "Enable bubblewrap (Linux only)"; }; opencode = { enable = lib.mkOption { type = lib.types.bool; default = false; description = "Enable opencode"; }; enableConfig = lib.mkOption { type = lib.types.bool; default = false; description = "Enable managed OpenCode configuration"; }; workMode = lib.mkEnableOption "work-specific opencode configuration"; }; pi = { enable = lib.mkOption { type = lib.types.bool; default = false; description = "Enable pi"; }; enableConfig = lib.mkEnableOption "managed pi coding agent configuration" // { default = true; }; workMode = lib.mkOption { type = lib.types.bool; default = false; description = "Apply work machine (WSL NixOS) specific pi patches"; }; }; }; config = lib.mkMerge [ (lib.mkIf cfg.enable { home.packages = (lib.optional cfg.claude-code.enable pkgs.unstable.claude-code) ++ (lib.optional cfg.opencode.enable pkgs.unstable.opencode) ++ (lib.optional cfg.gemini-cli.enable pkgs.unstable.gemini-cli) ++ (lib.optional cfg.pi.enable ( let pi = inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.pi; in if cfg.pi.workMode then # Work machine runs WSL NixOS, which needs the checks disabled and the # Bun binary's ELF interpreter patched to the correct dynamic linker. pi.overrideAttrs (oldAttrs: { doInstallCheck = false; # Run patchelf after the package is installed/fixed up by nix postFixup = (oldAttrs.postFixup or "") + '' # 1. Target the actual Bun binary inside libexec, not the shell wrapper in bin/ TARGET_BIN="$out/libexec/pi/pi" if [ -f "$TARGET_BIN" ]; then # 2. Make it writable in the build store so patchelf can edit it chmod +w "$TARGET_BIN" # 3. Running patchelf to set the interpreter forces it to rebuild the ELF headers patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$TARGET_BIN" fi ''; }) else pi )) ++ (lib.optional (cfg.bubblewrap.enable && pkgs.stdenv.isLinux) pkgs.unstable.bubblewrap); }) # pi coding agent configuration (lib.mkIf cfg.pi.enableConfig { home.file = { ".pi/agent/settings.json".source = config.lib.file.mkOutOfStoreSymlink "${piPath}/${if cfg.pi.workMode then "settings.work" else "settings.home"}.json"; ".pi/agent/extensions".source = config.lib.file.mkOutOfStoreSymlink "${piPath}/extensions-common"; } // lib.optionalAttrs cfg.pi.workMode { ".pi/agent/extensions-work".source = config.lib.file.mkOutOfStoreSymlink "${piPath}/extensions-work"; ".pi/agent/models.json".source = config.lib.file.mkOutOfStoreSymlink "${piPath}/models.work.json"; }; }) # OpenCode configuration (lib.mkIf cfg.opencode.enableConfig { xdg.configFile."opencode/opencode.json".source = config.lib.file.mkOutOfStoreSymlink "${opencodePath}/opencode.${if cfg.opencode.workMode then "work" else "home"}.json"; }) ]; }