Kieran's opinionated (and probably slightly dumb) nix config
1#!/usr/bin/env bash 2 3set -euo pipefail 4 5# Configuration 6COPILOT_TOKEN=$(bash ~/.config/crush/copilot.sh) 7ANTHROPIC_TOKEN=$(bunx anthropic-api-key) 8 9# Colors for output 10RED='\033[0;31m' 11GREEN='\033[0;32m' 12YELLOW='\033[1;33m' 13BLUE='\033[0;34m' 14NC='\033[0m' # No Color 15 16echo -e "${GREEN}Fetching latest models from APIs...${NC}" 17 18# Fetch Copilot models 19echo " → Fetching Copilot models..." 20COPILOT_MODELS=$(curl -s https://api.githubcopilot.com/models \ 21 -H "Authorization: Bearer $COPILOT_TOKEN" \ 22 -H "Editor-Version: CRUSH/1.0" \ 23 -H "Editor-Plugin-Version: CRUSH/1.0" \ 24 -H "Copilot-Integration-Id: vscode-chat") 25 26# Fetch Anthropic models 27echo " → Fetching Anthropic models..." 28ANTHROPIC_MODELS=$(curl -s https://api.anthropic.com/v1/models \ 29 -H "Authorization: Bearer $ANTHROPIC_TOKEN" \ 30 -H "anthropic-version: 2023-06-01" \ 31 -H "anthropic-beta: oauth-2025-04-20") 32 33# Extract model-picker enabled Copilot models 34echo -e "\n${GREEN}Copilot models (model_picker_enabled):${NC}" 35echo "$COPILOT_MODELS" | jq -r '.data[] | select(.model_picker_enabled == true) | " ✓ \(.id) - \(.name)"' 36 37# Extract all Anthropic models 38echo -e "\n${GREEN}Anthropic models:${NC}" 39echo "$ANTHROPIC_MODELS" | jq -r '.data[] | " ✓ \(.id) - \(.display_name)"' 40 41# Generate Copilot models Nix config 42generate_copilot_models() { 43 echo "$COPILOT_MODELS" | jq -r '.data[] | select(.model_picker_enabled == true) | 44 (if .capabilities.supports.tool_calls then true else false end) as $can_reason | 45 (if .capabilities.supports.vision then true else false end) as $supports_attachments | 46 " {\n" + 47 " id = \"\(.id)\";\n" + 48 " name = \"Copilot: \(.name)\";\n" + 49 " cost_per_1m_in = 0;\n" + 50 " cost_per_1m_out = 0;\n" + 51 " cost_per_1m_in_cached = 0;\n" + 52 " cost_per_1m_out_cached = 0;\n" + 53 " context_window = \(.capabilities.limits.max_context_window_tokens);\n" + 54 " default_max_tokens = \(.capabilities.limits.max_output_tokens);\n" + 55 " can_reason = \($can_reason);\n" + 56 " has_reasoning_efforts = false;\n" + 57 " supports_attachments = \($supports_attachments);\n" + 58 " }"' 59} 60 61# Generate Anthropic models Nix config with pricing 62generate_anthropic_models() { 63 echo "$ANTHROPIC_MODELS" | jq -r '.data[] | 64 # Determine pricing based on model family 65 (if (.id | contains("opus-4")) then 66 {in: 15.0, out: 75.0, in_cached: 1.5, out_cached: 75.0} 67 elif (.id | contains("sonnet-4")) then 68 {in: 3.0, out: 15.0, in_cached: 0.225, out_cached: 15.0} 69 elif (.id | contains("3-7-sonnet")) then 70 {in: 2.5, out: 12.0, in_cached: 0.187, out_cached: 12.0} 71 elif (.id | contains("3-5-sonnet")) then 72 {in: 3.0, out: 15.0, in_cached: 0.225, out_cached: 15.0} 73 elif (.id | contains("3-5-haiku")) then 74 {in: 0.8, out: 4.0, in_cached: 0.06, out_cached: 4.0} 75 elif (.id | contains("3-haiku")) then 76 {in: 0.25, out: 1.25, in_cached: 0.03, out_cached: 1.25} 77 elif (.id | contains("3-opus")) then 78 {in: 15.0, out: 75.0, in_cached: 1.5, out_cached: 75.0} 79 else 80 {in: 3.0, out: 15.0, in_cached: 0.225, out_cached: 15.0} 81 end) as $pricing | 82 83 # Determine context window and max tokens 84 (if (.id | test("claude-sonnet-4-5")) 85 then {context: 200000, max_tokens: 64000} 86 elif (.id | test("claude-sonnet-4-")) 87 then {context: 200000, max_tokens: 64000} 88 elif (.id | test("claude-3-7-sonnet")) 89 then {context: 200000, max_tokens: 64000} 90 elif (.id | test("claude-opus-4-1")) 91 then {context: 200000, max_tokens: 32000} 92 elif (.id | test("claude-opus-4-")) 93 then {context: 200000, max_tokens: 32000} 94 elif (.id | test("claude-3-5-haiku")) 95 then {context: 200000, max_tokens: 8192} 96 elif (.id | test("claude-3-haiku")) 97 then {context: 200000, max_tokens: 4096} 98 else 99 {context: 200000, max_tokens: 8192} 100 end) as $limits | 101 102 # Determine has_reasoning_efforts 103 (if (.id | contains("opus-4")) then true else false end) as $has_reasoning | 104 105 " {\n" + 106 " id = \"\(.id)\";\n" + 107 " name = \"\(.display_name)\";\n" + 108 " cost_per_1m_in = \($pricing.in);\n" + 109 " cost_per_1m_out = \($pricing.out);\n" + 110 " cost_per_1m_in_cached = \($pricing.in_cached);\n" + 111 " cost_per_1m_out_cached = \($pricing.out_cached);\n" + 112 " context_window = \($limits.context);\n" + 113 " default_max_tokens = \($limits.max_tokens);\n" + 114 " can_reason = true;\n" + 115 " has_reasoning_efforts = \($has_reasoning);\n" + 116 " supports_attachments = true;\n" + 117 " }"' 118} 119 120# Generate formatted model arrays 121COPILOT_MODELS_NIX=$(generate_copilot_models) 122ANTHROPIC_MODELS_NIX=$(generate_anthropic_models) 123 124# Output results 125echo -e "\n${BLUE}═══════════════════════════════════════════════════════════${NC}" 126echo -e "${YELLOW}COPILOT MODELS${NC}" 127echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" 128echo -e "\nPaste this into your copilot.models array:\n" 129echo " models = [" 130echo "$COPILOT_MODELS_NIX" 131echo " ];" 132 133echo -e "\n${BLUE}═══════════════════════════════════════════════════════════${NC}" 134echo -e "${YELLOW}ANTHROPIC MODELS${NC}" 135echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" 136echo -e "\nPaste this into your claude-pro.models array:\n" 137echo " models = [" 138echo "$ANTHROPIC_MODELS_NIX" 139echo " ];" 140 141echo -e "\n${BLUE}═══════════════════════════════════════════════════════════${NC}" 142echo -e "\n${GREEN}Summary:${NC}" 143echo " • Copilot models: $(echo "$COPILOT_MODELS" | jq '[.data[] | select(.model_picker_enabled == true)] | length')" 144echo " • Anthropic models: $(echo "$ANTHROPIC_MODELS" | jq '.data | length')"