diff --git a/flake.lock b/flake.lock index 232408f..ba7a45b 100644 --- a/flake.lock +++ b/flake.lock @@ -1,33 +1,12 @@ { "nodes": { - "beads": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1765103675, - "narHash": "sha256-PCsU2GwrPYFAE2KTCdk60E6ZTPRtgNdbPF+bzf8qi3Q=", - "owner": "steveyegge", - "repo": "beads", - "rev": "08d8353619777dfdd4119477ca029689597cec60", - "type": "github" - }, - "original": { - "owner": "steveyegge", - "repo": "beads", - "type": "github" - } - }, "blueprint": { "inputs": { "nixpkgs": [ - "nix-ai-tools", + "llm-agents", "nixpkgs" ], - "systems": "systems_3" + "systems": "systems_2" }, "locked": { "lastModified": 1763308703, @@ -152,28 +131,10 @@ "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "ghostty": { "inputs": { "flake-compat": "flake-compat", - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils", "nixpkgs": [ "nixpkgs" ], @@ -257,7 +218,7 @@ "type": "github" } }, - "nix-ai-tools": { + "llm-agents": { "inputs": { "blueprint": "blueprint", "nixpkgs": [ @@ -266,16 +227,16 @@ "treefmt-nix": "treefmt-nix" }, "locked": { - "lastModified": 1764643925, - "narHash": "sha256-GLvOrqcAURAbLapyTLk9CHf30YXyllwMbouErByho0I=", + "lastModified": 1766028137, + "narHash": "sha256-eM4l4J4G43WiHsixxnbQWGKG+9Jc1tmTK8I2ojJrSyc=", "owner": "numtide", - "repo": "nix-ai-tools", - "rev": "56de5ea7034968434a3a56f027ea6bacc19a4978", + "repo": "llm-agents.nix", + "rev": "16aaf36953b4bf65783dc05cecaa521262f3705b", "type": "github" }, "original": { "owner": "numtide", - "repo": "nix-ai-tools", + "repo": "llm-agents.nix", "type": "github" } }, @@ -364,12 +325,11 @@ }, "root": { "inputs": { - "beads": "beads", "chaotic": "chaotic", "darwin": "darwin", "ghostty": "ghostty", "home-manager": "home-manager_2", - "nix-ai-tools": "nix-ai-tools", + "llm-agents": "llm-agents", "nixpkgs": "nixpkgs_3", "sops-nix": "sops-nix", "try": "try" @@ -446,34 +406,19 @@ "type": "github" } }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "treefmt-nix": { "inputs": { "nixpkgs": [ - "nix-ai-tools", + "llm-agents", "nixpkgs" ] }, "locked": { - "lastModified": 1762938485, - "narHash": "sha256-AlEObg0syDl+Spi4LsZIBrjw+snSVU4T8MOeuZJUJjM=", + "lastModified": 1766000401, + "narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "5b4ee75aeefd1e2d5a1cc43cf6ba65eba75e83e4", + "rev": "42d96e75aa56a3f70cab7e7dc4a32868db28e8fd", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 696851f..f8bbf5f 100644 --- a/flake.nix +++ b/flake.nix @@ -12,8 +12,8 @@ ghostty.url = "github:ghostty-org/ghostty"; ghostty.inputs.nixpkgs.follows = "nixpkgs"; - nix-ai-tools.url = "github:numtide/nix-ai-tools"; - nix-ai-tools.inputs.nixpkgs.follows = "nixpkgs"; + llm-agents.url = "github:numtide/llm-agents.nix"; + llm-agents.inputs.nixpkgs.follows = "nixpkgs"; sops-nix.url = "github:Mic92/sops-nix"; sops-nix.inputs.nixpkgs.follows = "nixpkgs"; @@ -22,13 +22,10 @@ try.url = "github:tobi/try"; try.inputs.nixpkgs.follows = "nixpkgs"; - - beads.url = "github:steveyegge/beads"; - beads.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = inputs@{ self, nixpkgs, darwin, home-manager, ghostty, nix-ai-tools - , sops-nix, chaotic, try, beads, ... }: + outputs = inputs@{ self, nixpkgs, darwin, home-manager, ghostty, llm-agents + , sops-nix, chaotic, try, ... }: let inherit (nixpkgs.lib) genAttrs; lib = nixpkgs.lib; @@ -58,7 +55,7 @@ , homeFile ? ./modules/darwin/home/default.nix, }: let pkgs = mkPkgs system; - customPkgs = import ./packages { inherit pkgs lib beads; }; + customPkgs = import ./packages { inherit pkgs lib llm-agents; }; in darwin.lib.darwinSystem { inherit pkgs; system = pkgs.stdenv.hostPlatform.system; @@ -83,7 +80,7 @@ , homeFile ? ./modules/nixos/home/default.nix, }: let pkgs = mkPkgs system; - customPkgs = import ./packages { inherit pkgs lib beads; }; + customPkgs = import ./packages { inherit pkgs lib llm-agents; }; in lib.nixosSystem { inherit pkgs; system = pkgs.stdenv.hostPlatform.system; @@ -130,7 +127,7 @@ pkgs.python3; # Custom packages (osgrep, beads, etc.) - customPkgs = import ./packages { inherit pkgs lib beads; }; + customPkgs = import ./packages { inherit pkgs lib llm-agents; }; # Common arguments passed to all devshell imports shellArgs = { inherit pkgs lib python customPkgs; }; @@ -155,13 +152,13 @@ packages = forEachSystem (system: let pkgs = mkPkgs system; - customPkgs = import ./packages { inherit pkgs lib beads; }; + customPkgs = import ./packages { inherit pkgs lib llm-agents; }; in customPkgs // { inherit (pkgs) git; }); checks = forEachSystem (system: let pkgs = mkPkgs system; - customPkgs = import ./packages { inherit pkgs lib beads; }; + customPkgs = import ./packages { inherit pkgs lib llm-agents; }; in { inherit (customPkgs) osgrep opencode beads; }); }; } diff --git a/modules/darwin/system.nix b/modules/darwin/system.nix index 8a92b20..4ea3722 100644 --- a/modules/darwin/system.nix +++ b/modules/darwin/system.nix @@ -10,6 +10,10 @@ settings = { experimental-features = [ "nix-command" "flakes" ]; warn-dirty = false; + # Numtide binary cache for llm-agents.nix packages + extra-substituters = [ "https://cache.numtide.com" ]; + extra-trusted-public-keys = + [ "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ber8L+2z9FqRZJ+KBKGE4NNsT0=" ]; }; optimise.automatic = true; gc = { diff --git a/modules/home/base.nix b/modules/home/base.nix index 0a43565..5261b4c 100644 --- a/modules/home/base.nix +++ b/modules/home/base.nix @@ -23,11 +23,11 @@ programs.git.enable = true; - home.packages = with pkgs; [ docker docker-compose customPkgs.opencode ]; + home.packages = with pkgs; [ docker docker-compose ]; rsydn.aiTools = { enable = lib.mkDefault true; - claude.zai.enable = lib.mkDefault true; + zai.enable = lib.mkDefault true; }; rsydn.try = { diff --git a/modules/home/devtools/ai-tools.nix b/modules/home/devtools/ai-tools.nix index 4a8cb0d..232de2a 100644 --- a/modules/home/devtools/ai-tools.nix +++ b/modules/home/devtools/ai-tools.nix @@ -1,165 +1,81 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, inputs, ... }: let - inherit (lib) - mkEnableOption mkOption types mkIf concatMap unique warn escapeShellArg; + inherit (lib) mkEnableOption mkOption types mkIf escapeShellArg; cfg = config.rsydn.aiTools; + system = pkgs.stdenv.hostPlatform.system; + llmPkgs = inputs.llm-agents.packages.${system}; - getPkgsPackage = name: lib.attrByPath [ name ] null pkgs; + # Z.AI Gateway wrapper for Claude Code + zaiWrapperPackages = let zaiCfg = cfg.zai; + in if zaiCfg.enable then + let + claudeExe = lib.getExe llmPkgs.claude-code; + commandName = zaiCfg.commandName; + baseUrl = zaiCfg.baseUrl; + model = zaiCfg.model; + tokenEnvVar = zaiCfg.tokenEnvVar; + in [ + (pkgs.writeShellApplication { + name = commandName; + text = '' + if [ -z "''${${tokenEnvVar}:-}" ]; then + echo "${commandName}: environment variable ${tokenEnvVar} is not set" >&2 + exit 1 + fi - codexDefault = getPkgsPackage "codex"; + export ANTHROPIC_BASE_URL=${escapeShellArg baseUrl} + export ANTHROPIC_AUTH_TOKEN="''${${tokenEnvVar}}" + export ANTHROPIC_MODEL=${escapeShellArg model} - crushDefault = getPkgsPackage "crush"; - - claudeDefault = getPkgsPackage "claude-code"; - - toolOption = { name, description, defaultPackage, extraOptions ? { } - , extraDefaults ? { } }: - mkOption { - type = types.submodule { - options = { - enable = mkOption { - type = types.bool; - default = true; - description = "Enable the ${name} CLI."; - }; - package = mkOption { - type = types.nullOr types.package; - default = defaultPackage; - description = - "Package providing ${name}. Set to null to skip installation."; - }; - } // extraOptions; - }; - default = { - enable = true; - package = defaultPackage; - } // extraDefaults; - description = description; - }; - - gatherToolPackages = tools: - concatMap (tool: - let - name = tool.name; - cfgTool = tool.cfg; - in if cfgTool.enable then - if cfgTool.package != null then - [ cfgTool.package ] - else - warn "rsydn.aiTools.${name}: package is null, skipping install" [ ] - else - [ ]) tools; - - toolPackages = gatherToolPackages [ - { - name = "codex"; - cfg = cfg.codex; - } - { - name = "crush"; - cfg = cfg.crush; - } - { - name = "claude"; - cfg = cfg.claude; - } - ]; - - zaiWrapperPackages = let - claudeCfg = cfg.claude; - zaiCfg = claudeCfg.zai or { enable = false; }; - in if claudeCfg.enable && zaiCfg.enable then - if claudeCfg.package != null then - let - claudeExe = lib.getExe claudeCfg.package; - commandName = zaiCfg.commandName; - baseUrl = zaiCfg.baseUrl; - model = zaiCfg.model; - tokenEnvVar = zaiCfg.tokenEnvVar; - in [ - (pkgs.writeShellApplication { - name = commandName; - text = '' - if [ -z "''${${tokenEnvVar}:-}" ]; then - echo "${commandName}: environment variable ${tokenEnvVar} is not set" >&2 - exit 1 - fi - - export ANTHROPIC_BASE_URL=${escapeShellArg baseUrl} - export ANTHROPIC_AUTH_TOKEN="''${${tokenEnvVar}}" - export ANTHROPIC_MODEL=${escapeShellArg model} - - exec ${claudeExe} "$@" - ''; - }) - ] - else - warn "rsydn.aiTools.claude: Z.AI wrapper requested but package is null" - [ ] + exec ${claudeExe} "$@" + ''; + }) + ] else [ ]; in { options.rsydn.aiTools = { - enable = mkEnableOption "AI-oriented command line tooling"; + enable = mkEnableOption "AI CLI tools from llm-agents.nix"; - codex = toolOption { - name = "Codex"; - description = "OpenAI Codex CLI packaged in nixpkgs."; - defaultPackage = codexDefault; - }; - - crush = toolOption { - name = "Crush"; - description = "Charmbracelet Crush CLI packaged in nixpkgs."; - defaultPackage = crushDefault; - }; - - claude = toolOption { - name = "Claude Code"; - description = "Anthropic Claude Code CLI packaged in nixpkgs."; - defaultPackage = claudeDefault; - extraOptions = { - zai = mkOption { - type = types.submodule { - options = { - enable = mkEnableOption - "Expose the Z.AI Gateway wrapper command for Claude."; - commandName = mkOption { - type = types.str; - default = "glm"; - description = - "Name of the wrapper command that launches Claude via Z.AI."; - }; - baseUrl = mkOption { - type = types.str; - default = "https://api.z.ai/api/anthropic"; - description = "Gateway base URL used for the Anthropic client."; - }; - model = mkOption { - type = types.str; - default = "glm-4.6"; - description = "Model identifier passed via ANTHROPIC_MODEL."; - }; - tokenEnvVar = mkOption { - type = types.str; - default = "ZAI_API_KEY"; - description = - "Environment variable that stores the Z.AI API token."; - }; - }; + zai = mkOption { + type = types.submodule { + options = { + enable = mkEnableOption + "Expose the Z.AI Gateway wrapper command for Claude."; + commandName = mkOption { + type = types.str; + default = "glm"; + description = + "Name of the wrapper command that launches Claude via Z.AI."; }; - default = { - enable = false; - commandName = "glm"; - baseUrl = "https://api.z.ai/api/anthropic"; - model = "glm-4.6"; - tokenEnvVar = "ZAI_API_KEY"; + baseUrl = mkOption { + type = types.str; + default = "https://api.z.ai/api/anthropic"; + description = "Gateway base URL used for the Anthropic client."; + }; + model = mkOption { + type = types.str; + default = "glm-4.6"; + description = "Model identifier passed via ANTHROPIC_MODEL."; + }; + tokenEnvVar = mkOption { + type = types.str; + default = "ZAI_API_KEY"; + description = + "Environment variable that stores the Z.AI API token."; }; - description = - "Configuration for the Claude wrapper that targets the Z.AI gateway."; }; }; + default = { + enable = false; + commandName = "glm"; + baseUrl = "https://api.z.ai/api/anthropic"; + model = "glm-4.6"; + tokenEnvVar = "ZAI_API_KEY"; + }; + description = + "Configuration for the Claude wrapper that targets the Z.AI gateway."; }; extraPackages = mkOption { @@ -171,7 +87,13 @@ in { }; config = mkIf cfg.enable { - home.packages = - unique (toolPackages ++ zaiWrapperPackages ++ cfg.extraPackages); + home.packages = [ + llmPkgs.claude-code + llmPkgs.codex + llmPkgs.opencode + llmPkgs.crush + llmPkgs.ccstatusline + llmPkgs.ccusage + ] ++ zaiWrapperPackages ++ cfg.extraPackages; }; } diff --git a/packages/default.nix b/packages/default.nix index b4f0bcd..274ed47 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -1,8 +1,11 @@ -{ pkgs, lib, beads }: +{ pkgs, lib, llm-agents }: -{ +let + system = pkgs.stdenv.hostPlatform.system; + llmPkgs = llm-agents.packages.${system}; +in { osgrep = pkgs.callPackage ./osgrep.nix { }; - opencode = pkgs.callPackage ./opencode.nix { }; - # Re-export beads from flake input for consistent access - beads = beads.packages.${pkgs.stdenv.hostPlatform.system}.default; + opencode = llmPkgs.opencode; + # Source beads from llm-agents.nix + beads = llmPkgs.beads; } diff --git a/packages/opencode.nix b/packages/opencode.nix deleted file mode 100644 index 69504c3..0000000 --- a/packages/opencode.nix +++ /dev/null @@ -1,59 +0,0 @@ -{ lib, stdenv, fetchzip, autoPatchelfHook }: -let - version = "1.0.137"; - - platformInfo = { - x86_64-darwin = { - name = "darwin-x64"; - ext = "zip"; - hash = "sha256-ZgzvpW8SAN0HP3Qh+aT1S+WrEoXkTZiL8/8z1w9p9Y0="; - }; - aarch64-darwin = { - name = "darwin-arm64"; - ext = "zip"; - hash = "sha256-w5hs5arVroLtjr9pRAHYXQL+4L5gw1bJVL4DhgvrpLs="; - }; - x86_64-linux = { - name = "linux-x64"; - ext = "tar.gz"; - hash = "sha256-N8IR3xq/dGF5jPq9/UW8Mu/PnAUb7r+Ox6nge6wKw1M="; - }; - aarch64-linux = { - name = "linux-arm64"; - ext = "tar.gz"; - hash = "sha256-LwWbTAVnfEmCjUBNf58bgFFVVvDIQVJkLsGtYX+/EfU="; - }; - }; - - platform = platformInfo.${stdenv.hostPlatform.system} or (throw - "Unsupported system: ${stdenv.hostPlatform.system}"); -in stdenv.mkDerivation { - pname = "opencode"; - inherit version; - - src = fetchzip { - url = - "https://github.com/sst/opencode/releases/download/v${version}/opencode-${platform.name}.${platform.ext}"; - hash = platform.hash; - stripRoot = false; - }; - - nativeBuildInputs = lib.optionals stdenv.isLinux [ autoPatchelfHook ]; - - installPhase = '' - runHook preInstall - mkdir -p $out/bin - cp opencode $out/bin/opencode - chmod +x $out/bin/opencode - runHook postInstall - ''; - - meta = { - description = "AI coding assistant CLI from SST"; - homepage = "https://opencode.ai"; - license = lib.licenses.mit; - mainProgram = "opencode"; - platforms = - [ "x86_64-darwin" "aarch64-darwin" "x86_64-linux" "aarch64-linux" ]; - }; -}