From 731e4fed798ce4f582d8c1709d9176892f106a36 Mon Sep 17 00:00:00 2001 From: servius Date: Thu, 16 Apr 2026 01:09:31 +0530 Subject: [PATCH] feat: Disable items in tsuba --- AGENTS.md | 6 + home/programs/1password-cli.nix | 9 +- home/programs/aichat.nix | 345 ++++++++++++++++---------------- home/programs/ast-grep.nix | 7 +- home/programs/binwalk.nix | 7 +- home/programs/calendar.nix | 33 +-- home/programs/claude-code.nix | 4 +- home/programs/codex.nix | 7 +- home/programs/fnm.nix | 9 +- home/programs/gh.nix | 2 +- home/programs/himalaya.nix | 27 ++- home/programs/jujutsu.nix | 7 +- home/programs/neomutt.nix | 125 ++++++------ home/programs/nixify.nix | 4 +- home/programs/nushell.nix | 44 ++-- home/programs/omnix.nix | 9 +- home/programs/pkg-config.nix | 7 +- home/programs/television.nix | 4 +- home/programs/uv.nix | 4 +- modules/aichat.nix | 6 +- scripts/list-host-config.py | 169 ++++++++++++++++ 21 files changed, 531 insertions(+), 304 deletions(-) create mode 100755 scripts/list-host-config.py diff --git a/AGENTS.md b/AGENTS.md index 90bd88b..d7ad8b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,6 +40,12 @@ alejandra fmt . # Format all files alejandra fmt .nix # Format single file ``` +### Checking Enabled Programs/Services Per Device + +```bash +python3 ./scripts/list-host-config.py . # Table of system + HM programs & services per host +``` + ## Directory Structure - `flake.nix` - Main entry point, device definitions diff --git a/home/programs/1password-cli.nix b/home/programs/1password-cli.nix index a689a2d..8f11d1e 100644 --- a/home/programs/1password-cli.nix +++ b/home/programs/1password-cli.nix @@ -1,5 +1,10 @@ -{pkgs, ...}: { - home.packages = [ +{ + pkgs, + lib, + device, + ... +}: { + home.packages = lib.optionals (!device.isServer) [ pkgs._1password-cli ]; } diff --git a/home/programs/aichat.nix b/home/programs/aichat.nix index 4b4f940..faa398e 100644 --- a/home/programs/aichat.nix +++ b/home/programs/aichat.nix @@ -1,189 +1,194 @@ { pkgs, config, + lib, + device, ... }: { - sops = { - secrets."llama/api_key" = {}; - secrets."openai/api_key" = {}; - secrets."openrouter/api_key" = {}; - secrets."gemini/api_key" = {}; - }; imports = [ ../../modules/aichat.nix ]; disabledModules = ["programs/aichat.nix"]; - programs.aichat = { - enable = true; - enableFishIntegration = true; - enableNushellIntegration = false; - settings = { - save_session = true; - model = "chutes:zai-org/GLM-5-Turbo"; - rag_embedding_model = "ryu:RobinBially/nomic-embed-text-8k"; - clients = [ - { - type = "openai-compatible"; - name = "ryu"; - api_base = "https://ollama.darksailor.dev/v1"; - api_key_cmd = "cat ${config.sops.secrets."llama/api_key".path}"; - models = [ - { - name = "gpt-oss:20b"; - type = "chat"; - } - # { - # name = "gpt-oss:20b-instruct"; - # type = "chat"; - # real_name = "gpt-oss:20b"; - # patch = { - # body = { - # think = "low"; - # }; - # }; - # } - { - name = "qwen3:30b-a3b"; - type = "chat"; - } - { - name = "RobinBially/nomic-embed-text-8k"; - type = "embedding"; - default_chunk_size = 1000; - max_tokens_per_chunk = 8192; - max_batch_size = 100; - } - # { - # name = "deepseek-r1:14b"; - # type = "chat"; - # } - # { - # name = "qwen3:8b"; - # type = "chat"; - # } - ]; - } - { - type = "openai-compatible"; - name = "chutes"; - api_base = "https://llm.chutes.ai/v1"; - api_key_cmd = "op item get chutes.ai --fields token --reveal"; - models = [ - { - name = "zai-org/GLM-5-Turbo"; - type = "chat"; - } - ]; - } - { - type = "gemini"; - name = "gemini"; - api_base = "https://generativelanguage.googleapis.com/v1beta"; - api_key_cmd = "cat ${config.sops.secrets."gemini/api_key".path}"; - # api_key_file = "${config.sops.secrets."gemini/api_key".path}"; - models = [ - { - name = "gemini-2.5-pro"; - type = "chat"; - } - ]; - } - { - type = "openai-compatible"; - name = "openrouter"; - api_base = "https://openrouter.ai/api/v1"; - api_key_cmd = "cat ${config.sops.secrets."openrouter/api_key".path}"; - models = [ - { - name = "deepseek/deepseek-r1:free"; - type = "chat"; - } - ]; - } - { - type = "openai-compatible"; - name = "kuro"; - api_base = "http://kuro:1234/v1"; - models = [ - { - name = "openai/gpt-oss-20b"; - type = "chat"; - } - ]; - } - { - type = "openai-compatible"; - name = "shiro"; - api_base = "http://shiro:1234/v1"; - models = [ - { - name = "openai/gpt-oss-20b"; - type = "chat"; - } - ]; - } - { - type = "openai"; - name = "openai"; - api_base = "https://api.openai.com/v1"; - api_key_cmd = "cat ${config.sops.secrets."openai/api_key".path}"; - models = [ - { - name = "gpt-3.5-turbo"; - } - { - name = "gpt-5-nano"; - } - { - name = "o4-mini-high"; - } - ]; - } - { - type = "openai-compatible"; - name = "copilot"; - api_key = "xxx"; # exchange your `ghu_` token at https://api.github.com/copilot_internal/v2/token with Bearer token - api_base = "https://api.githubcopilot.com"; - patch = { - # Patch api - chat_completions = { - # Api type, possible values: chat_completions, embeddings, and rerank - ".*" = { - # The regex to match model names, e.g. '.*' 'gpt-4o' 'gpt-4o|gpt-4-.*' - headers = { - # Patch request headers - "Copilot-Integration-Id" = "vscode-chat"; - "Editor-Version:" = "aichat/0.1.0"; # optional + + config = lib.mkIf (!device.isServer) { + sops = { + secrets."llama/api_key" = {}; + secrets."openai/api_key" = {}; + secrets."openrouter/api_key" = {}; + secrets."gemini/api_key" = {}; + }; + programs.aichat = { + enable = true; + enableFishIntegration = true; + enableNushellIntegration = false; + settings = { + save_session = true; + model = "chutes:zai-org/GLM-5-Turbo"; + rag_embedding_model = "ryu:RobinBially/nomic-embed-text-8k"; + clients = [ + { + type = "openai-compatible"; + name = "ryu"; + api_base = "https://ollama.darksailor.dev/v1"; + api_key_cmd = "cat ${config.sops.secrets."llama/api_key".path}"; + models = [ + { + name = "gpt-oss:20b"; + type = "chat"; + } + # { + # name = "gpt-oss:20b-instruct"; + # type = "chat"; + # real_name = "gpt-oss:20b"; + # patch = { + # body = { + # think = "low"; + # }; + # }; + # } + { + name = "qwen3:30b-a3b"; + type = "chat"; + } + { + name = "RobinBially/nomic-embed-text-8k"; + type = "embedding"; + default_chunk_size = 1000; + max_tokens_per_chunk = 8192; + max_batch_size = 100; + } + # { + # name = "deepseek-r1:14b"; + # type = "chat"; + # } + # { + # name = "qwen3:8b"; + # type = "chat"; + # } + ]; + } + { + type = "openai-compatible"; + name = "chutes"; + api_base = "https://llm.chutes.ai/v1"; + api_key_cmd = "op item get chutes.ai --fields token --reveal"; + models = [ + { + name = "zai-org/GLM-5-Turbo"; + type = "chat"; + } + ]; + } + { + type = "gemini"; + name = "gemini"; + api_base = "https://generativelanguage.googleapis.com/v1beta"; + api_key_cmd = "cat ${config.sops.secrets."gemini/api_key".path}"; + # api_key_file = "${config.sops.secrets."gemini/api_key".path}"; + models = [ + { + name = "gemini-2.5-pro"; + type = "chat"; + } + ]; + } + { + type = "openai-compatible"; + name = "openrouter"; + api_base = "https://openrouter.ai/api/v1"; + api_key_cmd = "cat ${config.sops.secrets."openrouter/api_key".path}"; + models = [ + { + name = "deepseek/deepseek-r1:free"; + type = "chat"; + } + ]; + } + { + type = "openai-compatible"; + name = "kuro"; + api_base = "http://kuro:1234/v1"; + models = [ + { + name = "openai/gpt-oss-20b"; + type = "chat"; + } + ]; + } + { + type = "openai-compatible"; + name = "shiro"; + api_base = "http://shiro:1234/v1"; + models = [ + { + name = "openai/gpt-oss-20b"; + type = "chat"; + } + ]; + } + { + type = "openai"; + name = "openai"; + api_base = "https://api.openai.com/v1"; + api_key_cmd = "cat ${config.sops.secrets."openai/api_key".path}"; + models = [ + { + name = "gpt-3.5-turbo"; + } + { + name = "gpt-5-nano"; + } + { + name = "o4-mini-high"; + } + ]; + } + { + type = "openai-compatible"; + name = "copilot"; + api_key = "xxx"; # exchange your `ghu_` token at https://api.github.com/copilot_internal/v2/token with Bearer token + api_base = "https://api.githubcopilot.com"; + patch = { + # Patch api + chat_completions = { + # Api type, possible values: chat_completions, embeddings, and rerank + ".*" = { + # The regex to match model names, e.g. '.*' 'gpt-4o' 'gpt-4o|gpt-4-.*' + headers = { + # Patch request headers + "Copilot-Integration-Id" = "vscode-chat"; + "Editor-Version:" = "aichat/0.1.0"; # optional + }; }; }; }; - }; - } - ]; - document_loaders = { - git = + } + ]; + document_loaders = { + git = + /* + sh + */ + ''sh -c "yek $1 --json | jq '[.[] | { path: .filename, contents: .content }]'"''; + }; + }; + roles = { + "git-commit" = /* - sh + md */ - ''sh -c "yek $1 --json | jq '[.[] | { path: .filename, contents: .content }]'"''; + '' + --- + model: chutes:zai-org/GLM-5-Turbo + --- + Your task is to generate a concise and informative commit message based on the provided diff. Use the conventional commit format, which includes a type (feat, fix, chore, docs, style, refactor, perf, test) and an optional scope. The message should be in the imperative mood and should not exceed 72 characters in the subject line. Do not under any circumstance include any additional text or explanations, just add the commit message. + ''; }; + extraPackages = with pkgs; [ + jq + yek + ]; }; - roles = { - "git-commit" = - /* - md - */ - '' - --- - model: chutes:zai-org/GLM-5-Turbo - --- - Your task is to generate a concise and informative commit message based on the provided diff. Use the conventional commit format, which includes a type (feat, fix, chore, docs, style, refactor, perf, test) and an optional scope. The message should be in the imperative mood and should not exceed 72 characters in the subject line. Do not under any circumstance include any additional text or explanations, just add the commit message. - ''; - }; - extraPackages = with pkgs; [ - jq - yek - ]; }; } diff --git a/home/programs/ast-grep.nix b/home/programs/ast-grep.nix index 006ac26..2781fd8 100644 --- a/home/programs/ast-grep.nix +++ b/home/programs/ast-grep.nix @@ -1 +1,6 @@ -{pkgs, ...}: {home.packages = [pkgs.ast-grep];} +{ + pkgs, + lib, + device, + ... +}: {home.packages = lib.optionals (!device.isServer) [pkgs.ast-grep];} diff --git a/home/programs/binwalk.nix b/home/programs/binwalk.nix index a09d3a8..9756866 100644 --- a/home/programs/binwalk.nix +++ b/home/programs/binwalk.nix @@ -1 +1,6 @@ -{pkgs, ...}: {home.packages = [pkgs.binwalk];} +{ + pkgs, + lib, + device, + ... +}: {home.packages = lib.optionals (!device.isServer) [pkgs.binwalk];} diff --git a/home/programs/calendar.nix b/home/programs/calendar.nix index bd854a1..52b0576 100644 --- a/home/programs/calendar.nix +++ b/home/programs/calendar.nix @@ -1,17 +1,24 @@ -{pkgs, ...}: { - programs.khal = { - enable = true; - settings = { - default = { - # default_calendar = "fastmail"; - }; - view = { - agenda_event_format = "{calendar-color}{cancelled}{start-end-time-style} {title}{repeat-symbol}{reset}"; +{ + pkgs, + lib, + device, + ... +}: { + config = lib.mkIf (!device.isServer) { + programs.khal = { + enable = true; + settings = { + default = { + # default_calendar = "fastmail"; + }; + view = { + agenda_event_format = "{calendar-color}{cancelled}{start-end-time-style} {title}{repeat-symbol}{reset}"; + }; }; }; + programs.qcal.enable = true; + programs.vdirsyncer.enable = true; + services.vdirsyncer.enable = true; + accounts.calendar.accounts.fastmail.qcal.enable = true; }; - programs.qcal.enable = true; - programs.vdirsyncer.enable = true; - services.vdirsyncer.enable = true; - accounts.calendar.accounts.fastmail.qcal.enable = true; } diff --git a/home/programs/claude-code.nix b/home/programs/claude-code.nix index f2b1127..4a2518e 100644 --- a/home/programs/claude-code.nix +++ b/home/programs/claude-code.nix @@ -1,3 +1,3 @@ -{...}: { - programs.claude-code.enable = true; +{device, ...}: { + programs.claude-code.enable = !device.isServer; } diff --git a/home/programs/codex.nix b/home/programs/codex.nix index b6f0614..5b19b23 100644 --- a/home/programs/codex.nix +++ b/home/programs/codex.nix @@ -1 +1,6 @@ -{pkgs, ...}: {home.packages = [pkgs.codex];} +{ + pkgs, + lib, + device, + ... +}: {home.packages = lib.optionals (!device.isServer) [pkgs.codex];} diff --git a/home/programs/fnm.nix b/home/programs/fnm.nix index 50556d6..5bf0277 100644 --- a/home/programs/fnm.nix +++ b/home/programs/fnm.nix @@ -1,3 +1,8 @@ -{pkgs, ...}: { - home.packages = [pkgs.fnm]; +{ + pkgs, + lib, + device, + ... +}: { + home.packages = lib.optionals (!device.isServer) [pkgs.fnm]; } diff --git a/home/programs/gh.nix b/home/programs/gh.nix index fe90b60..5f49f58 100644 --- a/home/programs/gh.nix +++ b/home/programs/gh.nix @@ -4,7 +4,7 @@ device, ... }: { - programs = { + programs = lib.mkIf (!device.isServer) { gh.enable = true; gh-dash.enable = true; }; diff --git a/home/programs/himalaya.nix b/home/programs/himalaya.nix index bb0a1c0..e3eae64 100644 --- a/home/programs/himalaya.nix +++ b/home/programs/himalaya.nix @@ -1,13 +1,20 @@ -{config, ...}: { - programs.himalaya = { - enable = true; - }; - accounts.email.accounts.fastmail.himalaya = { - enable = true; - settings = { - downloads-dir = "${config.home.homeDirectory}/Mail/fastmail"; - backend.type = "maildir"; - backend.root-dir = "${config.home.homeDirectory}/Mail/fastmail"; +{ + config, + lib, + device, + ... +}: { + config = lib.mkIf (!device.isServer) { + programs.himalaya = { + enable = true; + }; + accounts.email.accounts.fastmail.himalaya = { + enable = true; + settings = { + downloads-dir = "${config.home.homeDirectory}/Mail/fastmail"; + backend.type = "maildir"; + backend.root-dir = "${config.home.homeDirectory}/Mail/fastmail"; + }; }; }; } diff --git a/home/programs/jujutsu.nix b/home/programs/jujutsu.nix index 35e2b6a..47c434f 100644 --- a/home/programs/jujutsu.nix +++ b/home/programs/jujutsu.nix @@ -1 +1,6 @@ -{pkgs, ...}: {home.packages = [pkgs.jujutsu];} +{ + pkgs, + lib, + device, + ... +}: {home.packages = lib.optionals (!device.isServer) [pkgs.jujutsu];} diff --git a/home/programs/neomutt.nix b/home/programs/neomutt.nix index f1359d3..2301364 100644 --- a/home/programs/neomutt.nix +++ b/home/programs/neomutt.nix @@ -1,77 +1,64 @@ -{pkgs, ...}: { - home.packages = [pkgs.w3m]; - programs.notmuch = { - enable = true; - new.tags = ["new" "unread"]; - hooks = { - preNew = '' - ${pkgs.notmuch}/bin/notmuch tag +servius -- folder:fastmail/Inbox/Servius - ${pkgs.notmuch}/bin/notmuch tag +hardware -- folder:fastmail/Inbox/Hardware - ${pkgs.notmuch}/bin/notmuch tag +uber -- folder:fastmail/Inbox/Uber - ${pkgs.notmuch}/bin/notmuch tag +spam -- folder:fastmail/Spam - ${pkgs.notmuch}/bin/notmuch tag +spam -- folder:'gmail/[Gmail]/Spam' - ${pkgs.notmuch}/bin/notmuch tag +sent -- folder:fastmail/Sent - ${pkgs.notmuch}/bin/notmuch tag +sent -- folder:'gmail/[Gmail]/Sent Mail' - ${pkgs.notmuch}/bin/notmuch tag +archive -- folder:fastmail/Archive - ${pkgs.notmuch}/bin/notmuch tag +archive -- folder:'gmail/[Gmail]/All Mail' - ''; +{ + pkgs, + lib, + device, + ... +}: { + config = lib.mkIf (!device.isServer) { + home.packages = [pkgs.w3m]; + programs.notmuch = { + enable = true; + new.tags = ["new" "unread"]; + hooks = { + preNew = '' + ${pkgs.notmuch}/bin/notmuch tag +servius -- folder:fastmail/Inbox/Servius + ${pkgs.notmuch}/bin/notmuch tag +hardware -- folder:fastmail/Inbox/Hardware + ${pkgs.notmuch}/bin/notmuch tag +uber -- folder:fastmail/Inbox/Uber + ${pkgs.notmuch}/bin/notmuch tag +spam -- folder:fastmail/Spam + ${pkgs.notmuch}/bin/notmuch tag +spam -- folder:'gmail/[Gmail]/Spam' + ${pkgs.notmuch}/bin/notmuch tag +sent -- folder:fastmail/Sent + ${pkgs.notmuch}/bin/notmuch tag +sent -- folder:'gmail/[Gmail]/Sent Mail' + ${pkgs.notmuch}/bin/notmuch tag +archive -- folder:fastmail/Archive + ${pkgs.notmuch}/bin/notmuch tag +archive -- folder:'gmail/[Gmail]/All Mail' + ''; + }; }; - }; - accounts.email.accounts = { - fastmail = { - notmuch = { - enable = true; + accounts.email.accounts = { + fastmail = { + notmuch = { + enable = true; + }; + imapnotify = { + enable = true; + boxes = ["Inbox" "Inbox/Servius" "Inbox/Hardware" "Inbox/Uber"]; + onNotify = "${pkgs.writeShellScript "mbsync-notify" '' + ${pkgs.isync}/bin/mbsync $1 + ${pkgs.notmuch}/bin/notmuch new + ${pkgs.libnotify}/bin/notify-send "New Mail" "New email in $1" + ''} %s"; + }; }; - imapnotify = { - enable = true; - boxes = ["Inbox" "Inbox/Servius" "Inbox/Hardware" "Inbox/Uber"]; - onNotify = "${pkgs.writeShellScript "mbsync-notify" '' - ${pkgs.isync}/bin/mbsync $1 - ${pkgs.notmuch}/bin/notmuch new - ${pkgs.libnotify}/bin/notify-send "New Mail" "New email in $1" - ''} %s"; + gmail = { + notmuch = { + enable = true; + }; + imapnotify = { + enable = true; + boxes = ["Inbox"]; + onNotify = "${pkgs.writeShellScript "mbsync-notify" '' + ${pkgs.isync}/bin/mbsync $1 + ${pkgs.notmuch}/bin/notmuch new + ${pkgs.libnotify}/bin/notify-send "New Mail" "New email in $1" + ''} %s"; + }; }; }; - gmail = { - notmuch = { - enable = true; - }; - imapnotify = { - enable = true; - boxes = ["Inbox"]; - onNotify = "${pkgs.writeShellScript "mbsync-notify" '' - ${pkgs.isync}/bin/mbsync $1 - ${pkgs.notmuch}/bin/notmuch new - ${pkgs.libnotify}/bin/notify-send "New Mail" "New email in $1" - ''} %s"; - }; + services.imapnotify = { + enable = true; + path = [pkgs.coreutils pkgs.isync pkgs.libnotify pkgs.notmuch]; }; + programs.mbsync.enable = true; + services.mbsync.enable = pkgs.stdenv.isLinux; }; - services.imapnotify = { - enable = true; - path = [pkgs.coreutils pkgs.isync pkgs.libnotify pkgs.notmuch]; - }; - programs.mbsync.enable = true; - services.mbsync.enable = pkgs.stdenv.isLinux; - - # launchd.agents.mbsync = { - # enable = true; - # config = { - # # A label for the service - # Label = "dev.darksailor.atuin-daemon"; - # # The command to run - # ProgramArguments = [ - # "${pkgs.atuin}/bin/atuin" - # "daemon" - # ]; - # # Run the service when you log in - # RunAtLoad = true; - # # Keep the process alive, or restart if it dies - # KeepAlive = true; - # # Log files - # StandardOutPath = "${device.home}/Library/Logs/atuin-daemon.log"; - # StandardErrorPath = "${device.home}/Library/Logs/atuin-daemon.error.log"; - # }; - # }; } diff --git a/home/programs/nixify.nix b/home/programs/nixify.nix index 873a051..96943c8 100644 --- a/home/programs/nixify.nix +++ b/home/programs/nixify.nix @@ -1,9 +1,11 @@ { pkgs, + lib, + device, inputs, ... }: { - home.packages = [ + home.packages = lib.optionals (!device.isServer) [ inputs.nixify.packages.${pkgs.system}.default ]; } diff --git a/home/programs/nushell.nix b/home/programs/nushell.nix index eca1c10..c273f58 100644 --- a/home/programs/nushell.nix +++ b/home/programs/nushell.nix @@ -4,27 +4,29 @@ device, ... }: { - programs.nushell = { - enable = true; - shellAliases = { - cd = "z"; - yy = "yazi"; - cat = "bat"; + config = lib.mkIf (!device.isServer) { + programs.nushell = { + enable = true; + shellAliases = { + cd = "z"; + yy = "yazi"; + cat = "bat"; + }; + plugins = with pkgs.nushellPlugins; [ + formats + polars + # highlight + ]; + extraConfig = '' + ${pkgs.pfetch-rs}/bin/pfetch + ''; + package = pkgs.nushell; + configFile.text = '' + $env.config = { + show_banner: false, + } + ''; }; - plugins = with pkgs.nushellPlugins; [ - formats - polars - # highlight - ]; - extraConfig = '' - ${pkgs.pfetch-rs}/bin/pfetch - ''; - package = pkgs.nushell; - configFile.text = '' - $env.config = { - show_banner: false, - } - ''; + home.shell.enableNushellIntegration = true; }; - home.shell.enableNushellIntegration = true; } diff --git a/home/programs/omnix.nix b/home/programs/omnix.nix index a92c3fb..de7ff7b 100644 --- a/home/programs/omnix.nix +++ b/home/programs/omnix.nix @@ -1,3 +1,8 @@ -{pkgs, ...}: { - home.packages = [pkgs.omnix]; +{ + pkgs, + lib, + device, + ... +}: { + home.packages = lib.optionals (!device.isServer) [pkgs.omnix]; } diff --git a/home/programs/pkg-config.nix b/home/programs/pkg-config.nix index 0fa3173..f9dc865 100644 --- a/home/programs/pkg-config.nix +++ b/home/programs/pkg-config.nix @@ -1 +1,6 @@ -{pkgs, ...}: {home.packages = [pkgs.pkg-config];} +{ + pkgs, + lib, + device, + ... +}: {home.packages = lib.optionals (!device.isServer) [pkgs.pkg-config];} diff --git a/home/programs/television.nix b/home/programs/television.nix index 114d570..092eb3e 100644 --- a/home/programs/television.nix +++ b/home/programs/television.nix @@ -1,3 +1,3 @@ -{...}: { - programs.television.enable = true; +{device, ...}: { + programs.television.enable = !device.isServer; } diff --git a/home/programs/uv.nix b/home/programs/uv.nix index be20565..e02a73c 100644 --- a/home/programs/uv.nix +++ b/home/programs/uv.nix @@ -1,3 +1,3 @@ -{...}: { - programs.uv.enable = true; +{device, ...}: { + programs.uv.enable = !device.isServer; } diff --git a/modules/aichat.nix b/modules/aichat.nix index 5638556..ef1227d 100644 --- a/modules/aichat.nix +++ b/modules/aichat.nix @@ -79,6 +79,7 @@ in { }; roles = lib.mkOption { type = lib.types.attrsOf lib.types.str; + default = {}; description = "Roles for the AI chat clients"; }; extraPackages = mkOption { @@ -114,11 +115,12 @@ in { programs.zsh.initExtra = mkIf cfg.enableZshIntegration zshIntegration; programs.nushell.extraConfig = mkIf cfg.enableNushellIntegration nuIntegration; - xdg.configFile = + xdg.configFile = mkIf cfg.enable ( { "aichat/config.yaml".source = yamlFormat.generate "config.yaml" cfg.settings; } - // (mapAttrs' (name: role: nameValuePair "aichat/roles/${name}.md" {text = role;}) cfg.roles); + // (mapAttrs' (name: role: nameValuePair "aichat/roles/${name}.md" {text = role;}) cfg.roles) + ); }; } diff --git a/scripts/list-host-config.py b/scripts/list-host-config.py new file mode 100755 index 0000000..778f0fd --- /dev/null +++ b/scripts/list-host-config.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +"""List enabled programs and services per host across NixOS, nix-darwin, and home-manager.""" + +import json +import subprocess +import sys +from collections import defaultdict + +FLAKE = sys.argv[1] if len(sys.argv) > 1 else "." + +NIXOS_HOSTS = ["ryu", "tako", "tsuba"] +DARWIN_HOSTS = ["kuro", "shiro"] + +# Nix expression to find enabled options using options.*.isDefined (robust for NixOS) +NIX_SYSTEM_ENABLED_EXPR = """sys: let + opts = sys.options.{section}; + cfg = sys.config.{section}; + names = builtins.attrNames opts; + check = n: let + r = builtins.tryEval ( + builtins.isAttrs opts.${{n}} + && builtins.hasAttr "enable" opts.${{n}} + && opts.${{n}}.enable.isDefined or false + && cfg.${{n}}.enable + ); + in r.success && r.value; +in builtins.filter check names""" + +# Simpler expression for darwin (fewer options, no removed-option aborts) +NIX_DARWIN_ENABLED_EXPR = """svcs: let + names = builtins.attrNames svcs; + results = map (n: let r = builtins.tryEval (svcs.${{n}}.enable or false); in {{ name = n; val = r.value or null; }}) names; +in map (r: r.name) (builtins.filter (r: r.val == true) results)""" + +# Home-manager expression (not passed through .format(), so use single braces) +NIX_HM_ENABLED_EXPR = r"""items: let + names = builtins.attrNames items; + results = map (n: let r = builtins.tryEval (items.${n}.enable or false); in { name = n; val = r.value or null; }) names; +in map (r: r.name) (builtins.filter (r: r.val == true) results)""" + + +def nix_eval(expr: str) -> list[str]: + """Run nix eval and return parsed JSON list.""" + result = subprocess.run( + ["nix", "eval", "--json", expr], + capture_output=True, + text=True, + ) + if result.returncode != 0: + return [] + try: + return sorted(json.loads(result.stdout)) + except json.JSONDecodeError: + return [] + + +def nix_eval_apply(attr: str, apply_expr: str) -> list[str]: + """Run nix eval with --apply and return parsed JSON list.""" + result = subprocess.run( + ["nix", "eval", "--json", attr, "--apply", apply_expr], + capture_output=True, + text=True, + ) + if result.returncode != 0: + return [] + try: + return sorted(json.loads(result.stdout)) + except json.JSONDecodeError: + return [] + + +def get_hm_user(config_attr: str) -> str | None: + """Get the home-manager username for a config.""" + result = subprocess.run( + [ + "nix", + "eval", + "--raw", + f"{FLAKE}#{config_attr}.config.home-manager.users", + "--apply", + "x: builtins.head (builtins.attrNames x)", + ], + capture_output=True, + text=True, + ) + if result.returncode != 0: + return None + return result.stdout.strip() + + +def get_nixos_system(host: str, section: str) -> list[str]: + """Get enabled system-level programs/services for a NixOS host.""" + expr = NIX_SYSTEM_ENABLED_EXPR.format(section=section) + return nix_eval_apply(f"{FLAKE}#nixosConfigurations.{host}", expr) + + +def get_darwin_system(host: str, section: str) -> list[str]: + """Get enabled system-level programs/services for a darwin host.""" + expr = NIX_DARWIN_ENABLED_EXPR.format(section=section) + return nix_eval_apply( + f"{FLAKE}#darwinConfigurations.{host}.config.{section}", expr + ) + + +def get_hm(config_type: str, host: str, section: str) -> list[str]: + """Get enabled home-manager programs/services for a host.""" + config_attr = f"{config_type}.{host}" + user = get_hm_user(config_attr) + if not user: + return [] + return nix_eval_apply( + f"{FLAKE}#{config_attr}.config.home-manager.users.{user}.{section}", + NIX_HM_ENABLED_EXPR, + ) + + +def print_table(title: str, data: dict[str, dict[str, list[str]]], hosts: list[str]): + """Print a markdown table of enabled items per host.""" + all_items = sorted({item for host_data in data.values() for item in host_data}) + if not all_items: + return + + print(f"\n## {title}\n") + header = f"| {'Name':<32} |" + sep = f"|{'-' * 34}|" + for host in hosts: + header += f" {host:^5} |" + sep += f"{'-' * 7}|" + print(header) + print(sep) + + for item in all_items: + row = f"| {item:<32} |" + for host in hosts: + marker = "Y" if item in data[host] else "-" + row += f" {marker:<4} |" + print(row) + + +def main(): + all_hosts = NIXOS_HOSTS + DARWIN_HOSTS + + sys_programs: dict[str, list[str]] = {} + sys_services: dict[str, list[str]] = {} + hm_programs: dict[str, list[str]] = {} + hm_services: dict[str, list[str]] = {} + + for host in NIXOS_HOSTS: + print(f"Evaluating {host}...", file=sys.stderr) + sys_programs[host] = get_nixos_system(host, "programs") + sys_services[host] = get_nixos_system(host, "services") + hm_programs[host] = get_hm("nixosConfigurations", host, "programs") + hm_services[host] = get_hm("nixosConfigurations", host, "services") + + for host in DARWIN_HOSTS: + print(f"Evaluating {host}...", file=sys.stderr) + sys_programs[host] = get_darwin_system(host, "programs") + sys_services[host] = get_darwin_system(host, "services") + hm_programs[host] = get_hm("darwinConfigurations", host, "programs") + hm_services[host] = get_hm("darwinConfigurations", host, "services") + + print_table("System Programs", sys_programs, all_hosts) + print_table("System Services", sys_services, all_hosts) + print_table("Home Manager Programs", hm_programs, all_hosts) + print_table("Home Manager Services", hm_services, all_hosts) + + +if __name__ == "__main__": + main() -- 2.51.2