This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge remote-tracking branch 'origin/master'

+1622 -234
+4 -4
flake.lock
··· 454 454 }, 455 455 "nixpkgs": { 456 456 "locked": { 457 - "lastModified": 1749794982, 458 - "narHash": "sha256-Kh9K4taXbVuaLC0IL+9HcfvxsSUx8dPB5s5weJcc9pc=", 457 + "lastModified": 1750365781, 458 + "narHash": "sha256-XE/lFNhz5lsriMm/yjXkvSZz5DfvKJLUjsS6pP8EC50=", 459 459 "owner": "NixOS", 460 460 "repo": "nixpkgs", 461 - "rev": "ee930f9755f58096ac6e8ca94a1887e0534e2d81", 461 + "rev": "08f22084e6085d19bcfb4be30d1ca76ecb96fe54", 462 462 "type": "github" 463 463 }, 464 464 "original": { 465 465 "owner": "NixOS", 466 - "ref": "nixos-unstable", 467 466 "repo": "nixpkgs", 467 + "rev": "08f22084e6085d19bcfb4be30d1ca76ecb96fe54", 468 468 "type": "github" 469 469 } 470 470 },
+10 -2
flake.nix
··· 2 2 description = "lassulus superconfig"; 3 3 4 4 inputs = { 5 - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 5 + nixpkgs.url = "github:NixOS/nixpkgs/08f22084e6085d19bcfb4be30d1ca76ecb96fe54?shallow=1"; 6 6 # nixpkgs.url = "path:/home/lass/tmp/nixpkgs-disk-debug"; 7 7 # nixpkgs.url = "git+file:/home/lass/src/nixpkgs"; 8 8 ··· 177 177 ]; 178 178 }; 179 179 }; 180 + flake.lib = import ./lib { inherit (nixpkgs) lib; }; 181 + flake.libWithPkgs = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed ( 182 + system: 183 + import ./lib/pkgs.nix { 184 + inherit (nixpkgs) lib; 185 + pkgs = nixpkgs.legacyPackages.${system}; 186 + } 187 + ); 180 188 flake.keys = { 181 189 pgp.yubi = { 182 190 key = ./keys/yubi.pgp; 183 191 id = "DBCD757846069B392EA9401D6657BE8A8D1EE807"; 184 192 }; 185 - ssh = builtins.readFile ./keys/yubi.ssh; 193 + ssh.yubi = builtins.readFile ./keys/yubi.ssh; 186 194 }; 187 195 }; 188 196 }
+1 -1
2configs/default.nix
··· 36 36 ./zsh.nix 37 37 ./htop.nix 38 38 ./wiregrill.nix 39 - ./tmux.nix 40 39 ./tor-ssh.nix 41 40 ./networkd.nix 42 41 ./pinned-registry.nix ··· 185 184 186 185 environment.systemPackages = with pkgs; [ 187 186 self.packages.${pkgs.system}.nvim 187 + self.packages.${pkgs.system}.tmux 188 188 189 189 git 190 190 git-absorb
-46
2configs/tmux.nix
··· 1 - { config, pkgs, ... }: 2 - 3 - { 4 - environment.etc."tmux.conf".text = '' 5 - #prefix key to ` 6 - set-option -g prefix2 ` 7 - 8 - bind-key r source-file /etc/tmux.conf \; display-message "/etc/tmux.conf reloaded" 9 - 10 - set-option -g default-terminal screen-256color 11 - 12 - #use session instead of windows 13 - bind-key c new-session 14 - bind-key p switch-client -p 15 - bind-key n switch-client -n 16 - bind-key C-s switch-client -l 17 - ''; 18 - nixpkgs.config.packageOverrides = super: { 19 - tmux = pkgs.symlinkJoin { 20 - name = "tmux"; 21 - paths = [ 22 - (pkgs.writeDashBin "tmux" '' 23 - exec ${super.tmux}/bin/tmux -f /etc/tmux.conf "$@" 24 - '') 25 - super.tmux 26 - ]; 27 - }; 28 - }; 29 - environment.systemPackages = with pkgs; [ 30 - tmux 31 - ]; 32 - 33 - # programs.bash.interactiveShellInit = '' 34 - # if [[ "$TERM" != "linux" && -z "$TMUX" ]]; then 35 - # if [[ -n "$SSH_AUTH_SOCK" ]]; then 36 - # tmux set-environment -g SSH_AUTH_SOCK "$SSH_AUTH_SOCK" 2>/dev/null 37 - # fi 38 - 39 - # exec tmux -u 40 - # fi 41 - # if [[ "$__host__" != "$HOST" ]]; then 42 - # tmux set -g status-bg colour$(string_hash $HOST 255) 43 - # export __host__=$HOST 44 - # fi 45 - # ''; 46 - }
+8
lib/default.nix
··· 1 + { lib, ... }: 2 + let 3 + # Pure library functions that don't need pkgs 4 + superlib = { 5 + halalify = import ./halalify.nix { inherit lib; }; 6 + }; 7 + in 8 + superlib
+41
lib/halalify.nix
··· 1 + { lib }: 2 + /** 3 + Override unfree licenses to free ones in a derivation. 4 + 5 + This function takes a derivation and replaces any unfree license with lib.licenses.free. 6 + Note: This only works for simple packages. Complex packages with unfree dependencies 7 + in their build process (like firefox-bin -> firefox-bin-unwrapped) cannot be handled 8 + this way and need other approaches. 9 + 10 + # Example 11 + 12 + ```nix 13 + halalify pkgs.someUnfreePackage 14 + ``` 15 + */ 16 + drv: 17 + if !lib.isDerivation drv then 18 + drv 19 + else 20 + let 21 + # Helper to check if a license is unfree 22 + isUnfreeLicense = license: lib.isAttrs license && license ? free && !license.free; 23 + 24 + # Helper to make a license free 25 + makeFreeLicense = 26 + license: 27 + if isUnfreeLicense license then 28 + lib.licenses.free 29 + else if lib.isList license then 30 + map makeFreeLicense license 31 + else 32 + license; 33 + in 34 + drv.overrideAttrs (old: { 35 + # Override the license 36 + meta = 37 + (old.meta or { }) 38 + // lib.optionalAttrs (old.meta ? license) { 39 + license = makeFreeLicense old.meta.license; 40 + }; 41 + })
+203
lib/make-wrapper.nix
··· 1 + { lib, pkgs, ... }: 2 + { 3 + /** 4 + Create a wrapped application that preserves all original outputs (man pages, completions, etc.) 5 + 6 + # Arguments 7 + 8 + - `package`: The package to wrap 9 + - `runtimeInputs`: List of packages to add to PATH (optional) 10 + - `env`: Attribute set of environment variables to export (optional) 11 + - `flags`: Attribute set of command-line flags to add (optional) 12 + - `flagSeparator`: Separator between flag names and values (optional, defaults to " ") 13 + - `preHook`: Shell script to run before executing the command (optional) 14 + - `passthru`: Attribute set to pass through to the wrapped derivation (optional) 15 + - `wrapper`: Custom wrapper function (optional, defaults to exec'ing the original binary with flags) 16 + - Called with { env, flags, envString, flagsString, exePath, preHook } 17 + 18 + # Example 19 + 20 + ```nix 21 + makeWrapper pkgs.curl { 22 + runtimeInputs = [ pkgs.jq ]; 23 + env = { 24 + CURL_CA_BUNDLE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; 25 + }; 26 + flags = { 27 + "--silent" = { }; # becomes --silent 28 + "--connect-timeout" = "30"; # becomes --connect-timeout 30 29 + }; 30 + preHook = '' 31 + echo "Making request..." >&2 32 + ''; 33 + } 34 + 35 + # Or with custom wrapper: 36 + makeWrapper pkgs.someProgram { 37 + wrapper = { exePath, flagsString, envString, preHook, ... }: '' 38 + ${envString} 39 + ${preHook} 40 + echo "Custom logic here" 41 + exec ${exePath} ${flagsString} "$@" 42 + ''; 43 + } 44 + ``` 45 + */ 46 + makeWrapper = 47 + package: 48 + { 49 + runtimeInputs ? [ ], 50 + env ? { }, 51 + flags ? { }, 52 + flagSeparator ? " ", # " " for "--flag value" or "=" for "--flag=value" 53 + preHook ? "", 54 + passthru ? { }, 55 + wrapper ? ( 56 + { 57 + exePath, 58 + flagsString, 59 + envString, 60 + preHook, 61 + ... 62 + }: 63 + '' 64 + ${envString} 65 + ${preHook} 66 + exec ${exePath}${flagsString} "$@" 67 + '' 68 + ), 69 + }: 70 + let 71 + # Extract binary name from the exe path 72 + exePath = lib.getExe package; 73 + binName = baseNameOf exePath; 74 + 75 + # Generate environment variable exports 76 + envString = 77 + if env == { } then 78 + "" 79 + else 80 + lib.concatStringsSep "\n" ( 81 + lib.mapAttrsToList (name: value: ''export ${name}="${toString value}"'') env 82 + ) 83 + + "\n"; 84 + 85 + # Generate flag arguments with proper line breaks and indentation 86 + flagsString = 87 + if flags == { } then 88 + "" 89 + else 90 + " \\\n " 91 + + lib.concatStringsSep " \\\n " ( 92 + lib.mapAttrsToList ( 93 + name: value: 94 + if value == { } then "${name}" else "${name}${flagSeparator}${lib.escapeShellArg (toString value)}" 95 + ) flags 96 + ); 97 + 98 + finalWrapper = wrapper { 99 + inherit 100 + env 101 + flags 102 + envString 103 + flagsString 104 + exePath 105 + preHook 106 + ; 107 + }; 108 + 109 + # Multi-output aware symlink join function 110 + multiOutputSymlinkJoin = 111 + { 112 + name, 113 + paths, 114 + outputs ? [ "out" ], 115 + originalOutputs ? { }, 116 + passthru ? { }, 117 + meta ? { }, 118 + ... 119 + }@args: 120 + pkgs.stdenv.mkDerivation ( 121 + { 122 + inherit name outputs; 123 + 124 + buildCommand = '' 125 + # Symlink all paths to the main output 126 + mkdir -p $out 127 + for path in ${lib.concatStringsSep " " (map toString paths)}; do 128 + ${pkgs.lndir}/bin/lndir -silent "$path" $out 129 + done 130 + 131 + # Handle additional outputs by symlinking from the original package's outputs 132 + ${lib.concatMapStringsSep "\n" ( 133 + output: 134 + if output != "out" && originalOutputs ? ${output} && originalOutputs.${output} != null then 135 + '' 136 + if [[ -n "''${${output}:-}" ]]; then 137 + mkdir -p ${"$" + output} 138 + # Only symlink from the original package's corresponding output 139 + ${pkgs.lndir}/bin/lndir -silent "${originalOutputs.${output}}" ${"$" + output} 140 + fi 141 + '' 142 + else 143 + "" 144 + ) outputs} 145 + ''; 146 + 147 + inherit passthru meta; 148 + } 149 + // (removeAttrs args [ 150 + "name" 151 + "paths" 152 + "outputs" 153 + "originalOutputs" 154 + "passthru" 155 + "meta" 156 + ]) 157 + ); 158 + 159 + # Get original package outputs for symlinking 160 + originalOutputs = 161 + if package ? outputs then 162 + lib.listToAttrs ( 163 + map (output: { 164 + name = output; 165 + value = if package ? ${output} then package.${output} else null; 166 + }) package.outputs 167 + ) 168 + else 169 + { }; 170 + 171 + # Create the wrapper derivation using our multi-output aware symlink join 172 + wrappedPackage = multiOutputSymlinkJoin ( 173 + { 174 + name = package.pname or package.name; 175 + paths = [ 176 + (pkgs.writeShellApplication { 177 + name = binName; 178 + runtimeInputs = runtimeInputs; 179 + text = finalWrapper; 180 + }) 181 + package 182 + ]; 183 + outputs = if package ? outputs then package.outputs else [ "out" ]; 184 + inherit originalOutputs; 185 + passthru = 186 + (package.passthru or { }) 187 + // passthru 188 + // { 189 + inherit env flags preHook; 190 + }; 191 + # Pass through original attributes 192 + meta = package.meta or { }; 193 + } 194 + // lib.optionalAttrs (package ? version) { 195 + inherit (package) version; 196 + } 197 + // lib.optionalAttrs (package ? pname) { 198 + inherit (package) pname; 199 + } 200 + ); 201 + in 202 + wrappedPackage; 203 + }
+9
lib/pkgs.nix
··· 1 + # Library functions that require pkgs 2 + { lib, pkgs, ... }: 3 + let 4 + superlib = import ./default.nix { inherit lib; }; 5 + in 6 + { 7 + # Import functions that need pkgs 8 + makeWrapper = (import ./make-wrapper.nix { inherit lib superlib pkgs; }).makeWrapper; 9 + }
+18
5pkgs/git-mcp/default.nix
··· 1 + { pkgs, ... }: 2 + let 3 + python = pkgs.python3.withPackages ( 4 + ps: with ps; [ 5 + mcp 6 + ] 7 + ); 8 + in 9 + pkgs.writeShellApplication { 10 + name = "git-mcp"; 11 + runtimeInputs = with pkgs; [ 12 + git 13 + python 14 + ]; 15 + text = '' 16 + ${python}/bin/python ${./git-mcp.py} "$@" 17 + ''; 18 + }
+356
5pkgs/git-mcp/git-mcp.py
··· 1 + #!/usr/bin/env python3 2 + """ 3 + Git MCP Server - Advanced Git operations for AI assistants 4 + """ 5 + 6 + import asyncio 7 + from typing import Dict, List, Any 8 + import os 9 + 10 + from mcp.server import Server 11 + from mcp.server.stdio import stdio_server 12 + from mcp.types import Tool, TextContent 13 + 14 + 15 + class GitMCP: 16 + def __init__(self): 17 + self.repo_path = os.getcwd() 18 + 19 + async def run_git_command(self, cmd: List[str]) -> Dict[str, Any]: 20 + """Run a git command and return the result""" 21 + try: 22 + result = await asyncio.create_subprocess_exec( 23 + "git", 24 + *cmd, 25 + stdout=asyncio.subprocess.PIPE, 26 + stderr=asyncio.subprocess.PIPE, 27 + cwd=self.repo_path, 28 + ) 29 + stdout, stderr = await result.communicate() 30 + 31 + if result.returncode == 0: 32 + return {"output": stdout.decode("utf-8")} 33 + else: 34 + return {"error": stderr.decode("utf-8")} 35 + except Exception as e: 36 + return {"error": str(e)} 37 + 38 + async def git_status(self, args: Dict[str, Any]) -> str: 39 + """Get detailed git status""" 40 + cmd = ["status", "--porcelain"] 41 + if args.get("untracked", True): 42 + cmd.append("--untracked-files=all") 43 + if args.get("ignored"): 44 + cmd.append("--ignored") 45 + 46 + result = await self.run_git_command(cmd) 47 + if "error" in result: 48 + return f"Error: {result['error']}" 49 + 50 + return result["output"] 51 + 52 + async def git_diff(self, args: Dict[str, Any]) -> str: 53 + """Get diff with various options""" 54 + cmd = ["diff"] 55 + if args.get("staged"): 56 + cmd.append("--staged") 57 + if args.get("commit"): 58 + cmd.append(args["commit"]) 59 + if args.get("context"): 60 + cmd.extend(["-U", str(args["context"])]) 61 + if args.get("files"): 62 + cmd.extend(["--"] + args["files"]) 63 + 64 + result = await self.run_git_command(cmd) 65 + if "error" in result: 66 + return f"Error: {result['error']}" 67 + 68 + return result["output"] 69 + 70 + async def git_log(self, args: Dict[str, Any]) -> str: 71 + """Get commit history""" 72 + cmd = ["log", "--oneline"] 73 + if args.get("limit"): 74 + cmd.extend(["-n", str(args["limit"])]) 75 + if args.get("author"): 76 + cmd.extend(["--author", args["author"]]) 77 + if args.get("since"): 78 + cmd.extend(["--since", args["since"]]) 79 + if args.get("grep"): 80 + cmd.extend(["--grep", args["grep"]]) 81 + if args.get("files"): 82 + cmd.extend(["--"] + args["files"]) 83 + 84 + result = await self.run_git_command(cmd) 85 + if "error" in result: 86 + return f"Error: {result['error']}" 87 + 88 + return result["output"] 89 + 90 + async def git_branch(self, args: Dict[str, Any]) -> str: 91 + """Manage branches""" 92 + action = args.get("action", "list") 93 + 94 + if action == "list": 95 + cmd = ["branch", "-v"] 96 + if args.get("remote"): 97 + cmd.append("-r") 98 + elif action == "create": 99 + if not args.get("name"): 100 + return "Error: Branch name required" 101 + cmd = ["checkout", "-b", args["name"]] 102 + elif action == "delete": 103 + if not args.get("name"): 104 + return "Error: Branch name required" 105 + cmd = ["branch", "-d", args["name"]] 106 + else: 107 + return f"Error: Unknown action: {action}" 108 + 109 + result = await self.run_git_command(cmd) 110 + if "error" in result: 111 + return f"Error: {result['error']}" 112 + 113 + return result["output"] 114 + 115 + async def git_add(self, args: Dict[str, Any]) -> str: 116 + """Stage files""" 117 + files = args.get("files", []) 118 + if not files: 119 + return "Error: No files specified" 120 + 121 + cmd = ["add"] + files 122 + result = await self.run_git_command(cmd) 123 + if "error" in result: 124 + return f"Error: {result['error']}" 125 + 126 + return "Files staged successfully" 127 + 128 + async def git_commit(self, args: Dict[str, Any]) -> str: 129 + """Create a commit""" 130 + message = args.get("message") 131 + if not message: 132 + return "Error: Commit message required" 133 + 134 + cmd = ["commit", "-m", message] 135 + result = await self.run_git_command(cmd) 136 + if "error" in result: 137 + return f"Error: {result['error']}" 138 + 139 + return result["output"] 140 + 141 + async def git_push(self, args: Dict[str, Any]) -> str: 142 + """Push commits""" 143 + cmd = ["push"] 144 + if args.get("remote"): 145 + cmd.append(args["remote"]) 146 + if args.get("branch"): 147 + cmd.append(args["branch"]) 148 + 149 + result = await self.run_git_command(cmd) 150 + if "error" in result: 151 + return f"Error: {result['error']}" 152 + 153 + return result["output"] 154 + 155 + async def git_pull(self, args: Dict[str, Any]) -> str: 156 + """Pull commits""" 157 + cmd = ["pull"] 158 + if args.get("remote"): 159 + cmd.append(args["remote"]) 160 + if args.get("branch"): 161 + cmd.append(args["branch"]) 162 + 163 + result = await self.run_git_command(cmd) 164 + if "error" in result: 165 + return f"Error: {result['error']}" 166 + 167 + return result["output"] 168 + 169 + 170 + async def main(): 171 + git_mcp = GitMCP() 172 + 173 + # Create the MCP server 174 + server = Server("git-mcp") 175 + 176 + # Register tools 177 + @server.list_tools() 178 + async def list_tools() -> List[Tool]: 179 + return [ 180 + Tool( 181 + name="git_status", 182 + description="Get git repository status", 183 + inputSchema={ 184 + "type": "object", 185 + "properties": { 186 + "untracked": { 187 + "type": "boolean", 188 + "description": "Include untracked files", 189 + }, 190 + "ignored": { 191 + "type": "boolean", 192 + "description": "Include ignored files", 193 + }, 194 + }, 195 + "additionalProperties": False, 196 + }, 197 + ), 198 + Tool( 199 + name="git_diff", 200 + description="Get git diff", 201 + inputSchema={ 202 + "type": "object", 203 + "properties": { 204 + "staged": { 205 + "type": "boolean", 206 + "description": "Show staged changes", 207 + }, 208 + "commit": { 209 + "type": "string", 210 + "description": "Show diff for specific commit", 211 + }, 212 + "files": { 213 + "type": "array", 214 + "items": {"type": "string"}, 215 + "description": "Specific files to diff", 216 + }, 217 + "context": { 218 + "type": "number", 219 + "description": "Lines of context", 220 + }, 221 + }, 222 + "additionalProperties": False, 223 + }, 224 + ), 225 + Tool( 226 + name="git_log", 227 + description="Get commit history", 228 + inputSchema={ 229 + "type": "object", 230 + "properties": { 231 + "limit": {"type": "number", "description": "Number of commits"}, 232 + "author": {"type": "string", "description": "Filter by author"}, 233 + "since": {"type": "string", "description": "Since date"}, 234 + "grep": { 235 + "type": "string", 236 + "description": "Search commit messages", 237 + }, 238 + "files": { 239 + "type": "array", 240 + "items": {"type": "string"}, 241 + "description": "Show commits affecting files", 242 + }, 243 + }, 244 + "additionalProperties": False, 245 + }, 246 + ), 247 + Tool( 248 + name="git_branch", 249 + description="Manage branches", 250 + inputSchema={ 251 + "type": "object", 252 + "properties": { 253 + "action": { 254 + "type": "string", 255 + "enum": ["list", "create", "delete"], 256 + "description": "Action to perform", 257 + }, 258 + "name": {"type": "string", "description": "Branch name"}, 259 + "remote": { 260 + "type": "boolean", 261 + "description": "Include remote branches", 262 + }, 263 + }, 264 + "additionalProperties": False, 265 + }, 266 + ), 267 + Tool( 268 + name="git_add", 269 + description="Stage files for commit", 270 + inputSchema={ 271 + "type": "object", 272 + "properties": { 273 + "files": { 274 + "type": "array", 275 + "items": {"type": "string"}, 276 + "description": "Files to stage", 277 + } 278 + }, 279 + "required": ["files"], 280 + "additionalProperties": False, 281 + }, 282 + ), 283 + Tool( 284 + name="git_commit", 285 + description="Create a commit", 286 + inputSchema={ 287 + "type": "object", 288 + "properties": { 289 + "message": {"type": "string", "description": "Commit message"} 290 + }, 291 + "required": ["message"], 292 + "additionalProperties": False, 293 + }, 294 + ), 295 + Tool( 296 + name="git_push", 297 + description="Push commits to remote", 298 + inputSchema={ 299 + "type": "object", 300 + "properties": { 301 + "remote": {"type": "string", "description": "Remote name"}, 302 + "branch": {"type": "string", "description": "Branch name"}, 303 + }, 304 + "additionalProperties": False, 305 + }, 306 + ), 307 + Tool( 308 + name="git_pull", 309 + description="Pull commits from remote", 310 + inputSchema={ 311 + "type": "object", 312 + "properties": { 313 + "remote": {"type": "string", "description": "Remote name"}, 314 + "branch": {"type": "string", "description": "Branch name"}, 315 + }, 316 + "additionalProperties": False, 317 + }, 318 + ), 319 + ] 320 + 321 + @server.call_tool() 322 + async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]: 323 + """Handle tool calls""" 324 + try: 325 + if name == "git_status": 326 + result = await git_mcp.git_status(arguments) 327 + elif name == "git_diff": 328 + result = await git_mcp.git_diff(arguments) 329 + elif name == "git_log": 330 + result = await git_mcp.git_log(arguments) 331 + elif name == "git_branch": 332 + result = await git_mcp.git_branch(arguments) 333 + elif name == "git_add": 334 + result = await git_mcp.git_add(arguments) 335 + elif name == "git_commit": 336 + result = await git_mcp.git_commit(arguments) 337 + elif name == "git_push": 338 + result = await git_mcp.git_push(arguments) 339 + elif name == "git_pull": 340 + result = await git_mcp.git_pull(arguments) 341 + else: 342 + result = f"Unknown tool: {name}" 343 + 344 + return [TextContent(type="text", text=result)] 345 + except Exception as e: 346 + return [TextContent(type="text", text=f"Error: {str(e)}")] 347 + 348 + # Run the server 349 + async with stdio_server() as (read_stream, write_stream): 350 + await server.run( 351 + read_stream, write_stream, server.create_initialization_options() 352 + ) 353 + 354 + 355 + if __name__ == "__main__": 356 + asyncio.run(main())
+34
5pkgs/tmux-mcp/default.nix
··· 1 + { 2 + lib, 3 + buildNpmPackage, 4 + fetchFromGitHub, 5 + tmux, 6 + nodejs, 7 + }: 8 + 9 + buildNpmPackage rec { 10 + pname = "tmux-mcp"; 11 + version = "0.1.0"; 12 + 13 + src = fetchFromGitHub { 14 + owner = "nickgnd"; 15 + repo = "tmux-mcp"; 16 + rev = "96db73220b449487e2be4427d1e86b8ccefc29ef"; 17 + hash = "sha256-lMgRXfzPclPqFO85zi7jvAe/aX1HL2LNk0cf3CkY7tM="; 18 + }; 19 + 20 + npmDepsHash = "sha256-D/kOVT7BmJJ/g5h9b0LZp5VFnRGtw7qmyIv4Pea+yXE="; 21 + 22 + buildInputs = [ 23 + tmux 24 + nodejs 25 + ]; 26 + 27 + meta = with lib; { 28 + description = "MCP server for tmux integration with Claude"; 29 + homepage = "https://github.com/nickgnd/tmux-mcp"; 30 + license = licenses.mit; 31 + maintainers = [ ]; 32 + platforms = platforms.all; 33 + }; 34 + }
+20 -10
machines/barnacle/configuration.nix
··· 1 - { pkgs, self, lib, ... }: 1 + { 2 + pkgs, 3 + self, 4 + lib, 5 + ... 6 + }: 2 7 3 8 { 4 9 imports = [ ··· 13 18 self.packages.${pkgs.system}.nvim 14 19 self.packages.${pkgs.system}.pass 15 20 self.packages.${pkgs.system}.passmenu 21 + self.packages.${pkgs.system}.tmux 16 22 17 23 # zsh dependencies 18 24 pkgs.fzf 19 25 pkgs.atuin 20 - 21 - pkgs.tmux 22 26 pkgs.gnupg 23 27 pkgs.pinentry_mac 24 28 pkgs.lazygit ··· 26 30 pkgs.git 27 31 pkgs.element-desktop 28 32 pkgs.iterm2 29 - (pkgs.firefox-devedition-bin-unwrapped.overrideAttrs (o: { 30 - meta = o.meta // { 31 - license = pkgs.lib.licenses.free; 32 - }; 33 - })) 33 + (self.lib.halalify pkgs.firefox-bin-unwrapped) 34 34 pkgs.ripgrep 35 35 pkgs.alt-tab-macos 36 36 pkgs.zed-editor ··· 96 96 }; 97 97 98 98 # Enable Rosetta builder for x86_64 builds 99 - nix-rosetta-builder.enable = true; 99 + nix-rosetta-builder = { 100 + enable = true; 101 + potentiallyInsecureExtraNixosModule = { 102 + # Fix DNS resolution in the VM 103 + networking.nameservers = [ 104 + "8.8.8.8" 105 + "1.1.1.1" 106 + ]; 107 + }; 108 + }; 100 109 101 110 # Create /etc/zshrc that loads the nix-darwin environment. 102 111 programs.zsh.enable = true; # default shell on catalina 103 112 programs.zsh.shellInit = self.packages.${pkgs.system}.zsh.zshrc; 113 + programs.zsh.promptInit = ""; # Disable default prompt 104 114 105 115 # enable sudo touch 106 116 security.pam.services.sudo_local.touchIdAuth = true; 107 - 117 + 108 118 # Enable Touch ID in tmux sessions with pam_reattach 109 119 security.pam.services.sudo_local.text = lib.mkBefore '' 110 120 auth optional ${pkgs.pam-reattach}/lib/pam/pam_reattach.so ignore_ssh
-10
machines/installer/config.nix
··· 1 - { config, lib, pkgs, ... }: 2 - 3 - { 4 - imports = [ 5 - ../../2configs/installer-tor.nix 6 - ]; 7 - 8 - # Basic installer configuration 9 - networking.hostName = "installer"; 10 - }
+149
tools/build-installer-iso/build-installer-iso.sh
··· 1 + #!/usr/bin/env bash 2 + set -euo pipefail 3 + set -x 4 + 5 + # Default values 6 + OUTPUT="installer.iso" 7 + ARCH="x86_64-linux" # Default to x86_64-linux for cross-compilation 8 + CONFIG_DIR="" 9 + FLAKE_URL="" 10 + 11 + usage() { 12 + cat <<EOF 13 + Usage: $0 [OPTIONS] 14 + 15 + Build a NixOS installer ISO with custom configuration. 16 + 17 + OPTIONS: 18 + -o, --output FILE Output ISO file (default: installer.iso) 19 + -a, --arch ARCH Architecture: x86_64-linux or aarch64-linux (default: x86_64-linux) 20 + -c, --config DIR Configuration directory containing installer-config.json and files/ 21 + -f, --flake URL Flake URL to install automatically (creates minimal config) 22 + -h, --help Show this help message 23 + 24 + EXAMPLES: 25 + $0 -o my-installer.iso -f github:user/nixos-config 26 + $0 -o my-installer.iso -c ./installer-config 27 + 28 + Configuration Directory Structure: 29 + installer-config/ 30 + ├── installer-config.json # Configuration file 31 + └── files/ # Files to copy to target system 32 + ├── etc/ 33 + │ └── resolv.conf # Gets copied to /etc/resolv.conf 34 + └── root/ 35 + └── .ssh/ 36 + └── authorized_keys # Gets copied to /root/.ssh/authorized_keys 37 + 38 + JSON Configuration Format: 39 + { 40 + "flakeUrl": "github:user/nixos-config", 41 + "fileMetadata": { 42 + "/etc/resolv.conf": { 43 + "owner": "root:root", 44 + "permissions": "644" 45 + }, 46 + "/root/.ssh/authorized_keys": { 47 + "owner": "root:root", 48 + "permissions": "600" 49 + } 50 + } 51 + } 52 + 53 + Default file metadata: owner=root:root, permissions=400 54 + EOF 55 + } 56 + 57 + # Parse arguments 58 + while [[ $# -gt 0 ]]; do 59 + case $1 in 60 + -o|--output) 61 + OUTPUT="$2" 62 + shift 2 63 + ;; 64 + -a|--arch) 65 + ARCH="$2" 66 + shift 2 67 + ;; 68 + -c|--config) 69 + CONFIG_DIR="$2" 70 + shift 2 71 + ;; 72 + -f|--flake) 73 + FLAKE_URL="$2" 74 + shift 2 75 + ;; 76 + -h|--help) 77 + usage 78 + exit 0 79 + ;; 80 + *) 81 + echo "Unknown option: $1" 82 + usage 83 + exit 1 84 + ;; 85 + esac 86 + done 87 + 88 + # Build the base ISO 89 + echo "Building base ISO for $ARCH..." 90 + BASE_ISO=$(nix build -L --no-link --print-out-paths "$FLAKE_ROOT#nixosConfigurations.installer-$ARCH.config.system.build.images.iso-installer") 91 + 92 + # Determine what to append to the ISO 93 + if [ -n "$FLAKE_URL" ]; then 94 + # Create minimal config for flake URL 95 + echo "Creating minimal configuration for flake URL: $FLAKE_URL" 96 + TEMP_CONFIG_DIR=$(mktemp -d) 97 + trap 'rm -rf "$TEMP_CONFIG_DIR"' EXIT 98 + 99 + cat > "$TEMP_CONFIG_DIR/installer-config.json" <<EOF 100 + { 101 + "flakeUrl": "$FLAKE_URL" 102 + } 103 + EOF 104 + 105 + echo "Appending flake configuration to ISO..." 106 + xorriso \ 107 + -indev "$BASE_ISO"/iso/*.iso \ 108 + -outdev "$OUTPUT" \ 109 + -boot_image any replay \ 110 + -map "$TEMP_CONFIG_DIR/installer-config.json" /installer-config.json \ 111 + -commit_eject all 112 + 113 + elif [ -n "$CONFIG_DIR" ]; then 114 + # Use provided configuration directory 115 + echo "Appending configuration from $CONFIG_DIR to ISO..." 116 + 117 + if [ ! -d "$CONFIG_DIR" ]; then 118 + echo "Error: Configuration directory '$CONFIG_DIR' not found" 119 + exit 1 120 + fi 121 + 122 + # Build xorriso command with all necessary mappings 123 + XORRISO_ARGS=( 124 + -indev "$BASE_ISO"/iso/*.iso 125 + -outdev "$OUTPUT" 126 + -boot_image any replay 127 + ) 128 + 129 + # Add installer-config.json if it exists 130 + if [ -f "$CONFIG_DIR/installer-config.json" ]; then 131 + XORRISO_ARGS+=(-map "$CONFIG_DIR/installer-config.json" /installer-config.json) 132 + fi 133 + 134 + # Add files directory if it exists 135 + if [ -d "$CONFIG_DIR/files" ]; then 136 + XORRISO_ARGS+=(-map "$CONFIG_DIR/files" /files) 137 + fi 138 + 139 + XORRISO_ARGS+=(-commit_eject all) 140 + 141 + xorriso "${XORRISO_ARGS[@]}" 142 + 143 + else 144 + # No configuration specified, just copy the base ISO 145 + echo "No configuration specified, copying base ISO..." 146 + cp "$BASE_ISO"/iso/*.iso "$OUTPUT" 147 + fi 148 + 149 + echo "ISO created: $OUTPUT"
+198
tools/build-installer-iso/flake-module.nix
··· 1 + { self, inputs, ... }: 2 + let 3 + # Shared installer configuration module 4 + installerModule = 5 + { modulesPath, pkgs, ... }: 6 + { 7 + imports = [ 8 + (modulesPath + "/image/images.nix") 9 + ]; 10 + 11 + # Basic installer configuration 12 + networking.hostName = "installer"; 13 + 14 + # Enable emergency access in initrd for debugging 15 + boot.initrd.systemd.emergencyAccess = true; 16 + 17 + # Enable serial console for VM usage 18 + boot.kernelParams = [ 19 + "console=ttyS0,115200n8" 20 + "console=tty0" 21 + ]; 22 + 23 + # Configure systemd for serial console 24 + systemd.services."serial-getty@ttyS0" = { 25 + enable = true; 26 + wantedBy = [ "getty.target" ]; 27 + serviceConfig.Restart = "always"; 28 + }; 29 + 30 + # Process installer configuration from ISO during system activation 31 + system.activationScripts.installer-config = { 32 + text = '' 33 + echo "Processing installer configuration from ISO..." 34 + 35 + # Find the ISO device (usually sr0, but could be others) 36 + ISO_DEVICE="" 37 + for dev in /dev/sr*; do 38 + if [ -b "$dev" ]; then 39 + ISO_DEVICE="$dev" 40 + break 41 + fi 42 + done 43 + 44 + if [ -z "$ISO_DEVICE" ]; then 45 + echo "No ISO device found, checking loop devices..." 46 + for dev in /dev/loop*; do 47 + if [ -b "$dev" ] && ${pkgs.util-linux}/bin/lsblk -no FSTYPE "$dev" 2>/dev/null | ${pkgs.gnugrep}/bin/grep -q iso9660; then 48 + ISO_DEVICE="$dev" 49 + break 50 + fi 51 + done 52 + fi 53 + 54 + if [ -n "$ISO_DEVICE" ]; then 55 + echo "Found ISO device: $ISO_DEVICE" 56 + mkdir -p /mnt/iso 57 + if ${pkgs.util-linux}/bin/mount -t iso9660 -o ro "$ISO_DEVICE" /mnt/iso; then 58 + echo "Successfully mounted ISO" 59 + 60 + # Check for installer configuration 61 + if [ -f /mnt/iso/installer-config.json ]; then 62 + echo "Found installer configuration" 63 + CONFIG_FILE="/mnt/iso/installer-config.json" 64 + 65 + # Process flake URL if present 66 + FLAKE_URL=$(${pkgs.jq}/bin/jq -r '.flakeUrl // empty' "$CONFIG_FILE" || true) 67 + if [ -n "$FLAKE_URL" ]; then 68 + echo "Flake URL configured: $FLAKE_URL" 69 + # Store for later installation 70 + mkdir -p /var/lib 71 + echo "$FLAKE_URL" > /var/lib/installer-flake-url 72 + fi 73 + 74 + # Process files from file structure 75 + if [ -d /mnt/iso/files ]; then 76 + echo "Processing files from file structure..." 77 + 78 + # Copy all files from the files directory, preserving structure 79 + ${pkgs.findutils}/bin/find /mnt/iso/files -type f | while read -r source_file; do 80 + # Remove /mnt/iso/files prefix to get target path 81 + target_path="''${source_file#/mnt/iso/files}" 82 + echo "Copying file: $source_file -> $target_path" 83 + 84 + # Create target directory 85 + mkdir -p "$(dirname "$target_path")" 86 + 87 + # Copy the file 88 + ${pkgs.coreutils}/bin/cp "$source_file" "$target_path" 89 + 90 + # Apply metadata from JSON if present 91 + if ${pkgs.jq}/bin/jq -e ".fileMetadata[\"$target_path\"]" "$CONFIG_FILE" >/dev/null 2>&1; then 92 + # Extract metadata for this file 93 + OWNER=$(${pkgs.jq}/bin/jq -r ".fileMetadata[\"$target_path\"].owner // \"root:root\"" "$CONFIG_FILE") 94 + PERMS=$(${pkgs.jq}/bin/jq -r ".fileMetadata[\"$target_path\"].permissions // \"400\"" "$CONFIG_FILE") 95 + 96 + echo "Setting owner: $OWNER, permissions: $PERMS for $target_path" 97 + ${pkgs.coreutils}/bin/chown "$OWNER" "$target_path" || echo "Warning: failed to set owner $OWNER" 98 + ${pkgs.coreutils}/bin/chmod "$PERMS" "$target_path" || echo "Warning: failed to set permissions $PERMS" 99 + else 100 + # Apply defaults: root:root, 400 101 + echo "Applying default metadata for $target_path" 102 + ${pkgs.coreutils}/bin/chown root:root "$target_path" || echo "Warning: failed to set default owner" 103 + ${pkgs.coreutils}/bin/chmod 400 "$target_path" || echo "Warning: failed to set default permissions" 104 + fi 105 + done 106 + fi 107 + 108 + else 109 + echo "No installer configuration found in ISO" 110 + fi 111 + 112 + ${pkgs.util-linux}/bin/umount /mnt/iso 113 + else 114 + echo "Failed to mount ISO device $ISO_DEVICE" 115 + fi 116 + else 117 + echo "No ISO device found" 118 + fi 119 + ''; 120 + deps = [ ]; 121 + }; 122 + 123 + # Service to install from flake URL if configured 124 + systemd.services.installer-flake-install = { 125 + description = "Install NixOS from configured flake URL"; 126 + wantedBy = [ "multi-user.target" ]; 127 + after = [ 128 + "installer-config.service" 129 + "network-online.target" 130 + ]; 131 + wants = [ "network-online.target" ]; 132 + path = with pkgs; [ 133 + git 134 + nix 135 + coreutils 136 + ]; 137 + serviceConfig = { 138 + Type = "oneshot"; 139 + RemainAfterExit = true; 140 + }; 141 + script = '' 142 + set -euo pipefail 143 + 144 + if [ -f /var/lib/installer-flake-url ]; then 145 + FLAKE_URL=$(cat /var/lib/installer-flake-url) 146 + echo "Installing from flake: $FLAKE_URL" 147 + 148 + # TODO: Add actual installation logic here 149 + # This would typically involve: 150 + # 1. Partitioning disks 151 + # 2. Mounting filesystems 152 + # 3. Running nixos-install with the flake 153 + echo "Installation from flake not yet implemented" 154 + echo "Flake URL: $FLAKE_URL" 155 + else 156 + echo "No flake URL configured for automatic installation" 157 + fi 158 + ''; 159 + }; 160 + }; 161 + in 162 + { 163 + flake = { 164 + # Export the installer module for reuse 165 + nixosModules.installer = installerModule; 166 + 167 + # Define installer configurations for each architecture 168 + nixosConfigurations = { 169 + installer-x86_64-linux = inputs.nixpkgs.lib.nixosSystem { 170 + system = "x86_64-linux"; 171 + modules = [ installerModule ]; 172 + }; 173 + 174 + installer-aarch64-linux = inputs.nixpkgs.lib.nixosSystem { 175 + system = "aarch64-linux"; 176 + modules = [ installerModule ]; 177 + }; 178 + }; 179 + }; 180 + 181 + perSystem = 182 + { pkgs, ... }: 183 + { 184 + packages.build-installer-iso = pkgs.writeShellApplication { 185 + name = "build-installer-iso"; 186 + runtimeInputs = with pkgs; [ 187 + xorriso 188 + coreutils 189 + nix 190 + ]; 191 + text = '' 192 + # Export flake root for the script to use 193 + export FLAKE_ROOT="${self}" 194 + ${builtins.readFile ./build-installer-iso.sh} 195 + ''; 196 + }; 197 + }; 198 + }
+22
tools/claude/CLAUDE.md
··· 1 + # CLAUDE.md 2 + 3 + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 + 5 + ## Instructions 6 + 7 + - Use `--log-format bar-with-logs` with Nix for improved build log output. 8 + - Add new untracked files in Nix flakes with `git add`. 9 + - Format code with `nix fmt` if available. 10 + - Write shell scripts that pass ShellCheck. 11 + - Write Python code for 3.12 that conforms to `ruff format` and passes `ruff check`. 12 + - Always test/lint/format your code before committing. 13 + - Add debug output or unit tests when troubleshooting. 14 + - Avoid mocking in tests. 15 + - Use the `gh` tool to interact with GitHub. 16 + - Use `nix-locate` to find packages by file path. 17 + - Use `nix run` to execute applications that are not installed. 18 + - Do not include "claude" in commit messages. 19 + - Use `nix eval` instead of `nix flake show` to look up attributes in a flake. 20 + - Use tmux-mcp for long running tasks, use a session with a claude- prefix, create a session if you can't find any one that is idle 21 + - Use git-mcp for advanced git operations like interactive staging, stash management, and bisecting 22 + - Take extra care that we don't commit lines with trailing whitespaces
+33
tools/claude/flake-module.nix
··· 1 + { self, ... }: 2 + { 3 + perSystem = 4 + { pkgs, system, ... }: 5 + let 6 + # Get paths to MCP servers 7 + gitMcpPath = "${self.packages.${system}.git-mcp}/bin/git-mcp"; 8 + tmuxMcpPath = "${self.packages.${system}.tmux-mcp}/bin/tmux-mcp"; 9 + 10 + # Path to CLAUDE.md 11 + claudeMd = ./CLAUDE.md; 12 + in 13 + { 14 + packages.claude = self.libWithPkgs.${system}.makeWrapper (self.lib.halalify pkgs.claude-code) { 15 + preHook = '' 16 + # Configure MCP servers using Claude CLI 17 + if claude mcp add git-mcp "${gitMcpPath}" 2>/dev/null && \ 18 + claude mcp add tmux-mcp "${tmuxMcpPath}" 2>/dev/null; then 19 + echo "MCP servers configured successfully" >&2 20 + else 21 + echo "Warning: Could not configure MCP servers" >&2 22 + fi 23 + 24 + # Set up CLAUDE.md if it doesn't exist 25 + CLAUDE_MD_DIR="''${CLAUDE_MD_DIR:-$HOME/.claude}" 26 + mkdir -p "$CLAUDE_MD_DIR" 27 + if [[ ! -f "$CLAUDE_MD_DIR/CLAUDE.md" ]]; then 28 + cp ${claudeMd} "$CLAUDE_MD_DIR/CLAUDE.md" 29 + fi 30 + ''; 31 + }; 32 + }; 33 + }
+14 -22
tools/mpv/flake-module.nix
··· 1 1 { self, ... }: 2 2 { 3 3 perSystem = 4 - { pkgs, ... }: 4 + { pkgs, system, ... }: 5 5 { 6 6 packages.mpv = 7 7 let ··· 65 65 }; 66 66 67 67 in 68 - pkgs.writeShellApplication { 69 - name = "mpv"; 70 - runtimeInputs = [ 71 - mpvPackage 72 - pkgs.yt-dlp 73 - ]; 74 - text = '' 75 - Y_RES=1081 76 - # we need to disable sponsorblock local database because of 77 - # https://github.com/po5/mpv_sponsorblock/issues/31 78 - exec mpv \ 79 - --no-config \ 80 - --input-conf=${mpvInput} \ 81 - --include=${mpvConfig} \ 82 - --script=${autosub} \ 83 - --ytdl-format="best[height<$Y_RES]" \ 84 - --script-opts=ytdl_hook-ytdl_path=${pkgs.yt-dlp}/bin/yt-dlp \ 85 - --script-opts-append=sponsorblock-local_database=no \ 86 - --audio-channels=2 \ 87 - "$@" 88 - ''; 68 + self.libWithPkgs.${system}.makeWrapper mpvPackage { 69 + runtimeInputs = [ pkgs.yt-dlp ]; 70 + flagSeparator = "="; 71 + flags = { 72 + "--no-config" = {}; 73 + "--input-conf" = "${mpvInput}"; 74 + "--include" = "${mpvConfig}"; 75 + "--script" = "${autosub}"; 76 + "--ytdl-format" = "best[height<1081]"; 77 + "--script-opts" = "ytdl_hook-ytdl_path=${pkgs.yt-dlp}/bin/yt-dlp"; 78 + "--script-opts-append" = "sponsorblock-local_database=no"; 79 + "--audio-channels" = "2"; 80 + }; 89 81 }; 90 82 91 83 # Separate package for subtitle downloader
+68 -75
tools/muchsync/flake-module.nix
··· 1 1 { self, ... }: 2 2 { 3 3 perSystem = 4 - { pkgs, lib, ... }: 5 4 { 6 - packages.muchsync = pkgs.symlinkJoin { 7 - name = "muchsync"; 8 - paths = [ 9 - (pkgs.writeShellApplication { 10 - name = "muchsync"; 11 - runtimeInputs = 12 - [ 13 - pkgs.muchsync 14 - pkgs.notmuch 15 - ] 16 - ++ lib.optionals pkgs.stdenv.isLinux [ 17 - pkgs.iputils 18 - ]; 19 - text = '' 20 - export NOTMUCH_CONFIG=${ 21 - pkgs.writeText "notmuch-config" self.packages.${pkgs.system}.mutt.passthru.notmuchConfig 22 - } 5 + pkgs, 6 + lib, 7 + system, 8 + ... 9 + }: 10 + { 11 + packages.muchsync = self.libWithPkgs.${system}.makeWrapper pkgs.muchsync { 12 + runtimeInputs = [ pkgs.notmuch ] ++ lib.optionals pkgs.stdenv.isLinux [ pkgs.iputils ]; 13 + env = { 14 + NOTMUCH_CONFIG = 15 + pkgs.writeText "notmuch-config" 16 + self.packages.${pkgs.system}.mutt.passthru.notmuchConfig; 17 + }; 18 + wrapper = 19 + { exePath, envString, ... }: 20 + '' 21 + ${envString} 22 + # Create Maildir structure if it doesn't exist 23 + mkdir -p "$HOME/Maildir"/{cur,new,tmp} 23 24 24 - set -efu 25 + # Check if a host is reachable 26 + check_host() { 27 + local host=$1 28 + ping -W2 -c1 "$host" >/dev/null 2>&1 29 + } 25 30 26 - # Create Maildir structure if it doesn't exist 27 - mkdir -p "$HOME/Maildir"/{cur,new,tmp} 28 - 29 - # Check if a host is reachable 30 - check_host() { 31 - local host=$1 32 - ping -W2 -c1 "$host" >/dev/null 2>&1 33 - } 34 - 35 - # Smart host selection - try spora, nether, retiolum, then tor 36 - find_green_host() { 37 - if check_host green.s; then 38 - echo "green.s" 39 - elif check_host green.n; then 40 - echo "green.n" 41 - elif check_host green.r; then 42 - echo "green.r" 43 - else 44 - echo "tor" 45 - fi 46 - } 47 - 48 - # Check if arguments look like flags or a hostname 49 - has_hostname=false 50 - for arg in "$@"; do 51 - case "$arg" in 52 - -*) ;; # This is a flag 53 - *) has_hostname=true; break ;; # Found a non-flag argument 54 - esac 55 - done 56 - 57 - # If only flags provided (or no args), do a sync with green host 58 - if [ "$has_hostname" = "false" ]; then 59 - host=$(find_green_host) 60 - echo "Syncing with $host..." >&2 61 - 62 - if [ "$host" = "tor" ]; then 63 - # Build dependencies lazily 64 - echo "Building tornade..." >&2 65 - tornade_path=$(nix build --no-link --print-out-paths "${self}#tornade") 66 - clan_path=$(nix build --no-link --print-out-paths "${self}#clan-cli") 67 - 68 - # Get tor hostname using clan CLI 69 - tor_hostname=$(CLAN_DIR="${self}" "$clan_path/bin/clan" vars get green tor-ssh/tor-hostname) 70 - 71 - # Use muchsync with custom ssh command via tornade 72 - exec muchsync "$@" -s "$tornade_path/bin/tornade ssh" lass@"$tor_hostname" 73 - else 74 - exec muchsync "$@" lass@"$host" 75 - fi 31 + # Smart host selection - try spora, nether, retiolum, then tor 32 + find_green_host() { 33 + if check_host green.s; then 34 + echo "green.s" 35 + elif check_host green.n; then 36 + echo "green.n" 37 + elif check_host green.r; then 38 + echo "green.r" 76 39 else 77 - # Pass through arguments to muchsync 78 - exec muchsync "$@" 40 + echo "tor" 79 41 fi 80 - ''; 81 - }) 82 - pkgs.muchsync 83 - ]; 42 + } 43 + 44 + # Check if arguments look like flags or a hostname 45 + has_hostname=false 46 + for arg in "$@"; do 47 + case "$arg" in 48 + -*) ;; # This is a flag 49 + *) has_hostname=true; break ;; # Found a non-flag argument 50 + esac 51 + done 52 + 53 + # If only flags provided (or no args), do a sync with green host 54 + if [ "$has_hostname" = "false" ]; then 55 + host=$(find_green_host) 56 + echo "Syncing with $host..." >&2 57 + 58 + if [ "$host" = "tor" ]; then 59 + # Build dependencies lazily 60 + echo "Building tornade..." >&2 61 + tornade_path=$(nix build --no-link --print-out-paths "${self}#tornade") 62 + clan_path=$(nix build --no-link --print-out-paths "${self}#clan-cli") 63 + 64 + # Get tor hostname using clan CLI 65 + tor_hostname=$(CLAN_DIR="${self}" "$clan_path/bin/clan" vars get green tor-ssh/tor-hostname) 66 + 67 + # Use muchsync with custom ssh command via tornade 68 + exec ${exePath} "$@" -s "$tornade_path/bin/tornade ssh" lass@"$tor_hostname" 69 + else 70 + exec ${exePath} "$@" lass@"$host" 71 + fi 72 + else 73 + # Pass through arguments to muchsync 74 + exec ${exePath} "$@" 75 + fi 76 + ''; 84 77 }; 85 78 }; 86 79 }
+16 -12
tools/mutt/flake-module.nix
··· 1 1 { self, ... }: 2 2 { 3 3 perSystem = 4 - { pkgs, lib, ... }: 4 + { 5 + pkgs, 6 + lib, 7 + system, 8 + ... 9 + }: 5 10 { 6 11 packages.mutt = 7 12 let ··· 265 270 ''; 266 271 267 272 in 268 - (pkgs.writeShellApplication { 269 - name = "mutt"; 273 + self.libWithPkgs.${pkgs.system}.makeWrapper pkgs.neomutt { 270 274 runtimeInputs = 271 275 [ 272 - pkgs.neomutt 273 276 pkgs.elinks 274 277 pkgs.msmtp 275 278 pkgs.notmuch ··· 278 281 ++ lib.optionals pkgs.stdenv.isLinux [ 279 282 pkgs.iputils 280 283 ]; 281 - text = '' 282 - export NOTMUCH_CONFIG_FILE=${pkgs.writeText "notmuch-config" notmuchConfig} 283 - export MUTTRC=${muttrc} 284 - ${builtins.readFile ./mutt.sh} 285 - ''; 286 - }) 287 - // { 288 - passthru.notmuchConfig = notmuchConfig; 284 + env = { 285 + NOTMUCH_CONFIG = "${pkgs.writeText "notmuch-config" notmuchConfig}"; 286 + }; 287 + flags = { 288 + "-F" = "${muttrc}"; 289 + }; 290 + passthru = { 291 + notmuchConfig = notmuchConfig; 292 + }; 289 293 }; 290 294 }; 291 295 }
-4
tools/mutt/mutt.sh
··· 1 - #!/usr/bin/env bash 2 - # Export notmuch config and run neomutt with custom config 3 - export NOTMUCH_CONFIG="$NOTMUCH_CONFIG_FILE" 4 - exec neomutt -F "$MUTTRC" "$@"
+40
tools/name-generator/adjectives.txt
··· 1 + awesome 2 + brave 3 + clever 4 + daring 5 + eager 6 + fancy 7 + gentle 8 + happy 9 + jolly 10 + keen 11 + lively 12 + mighty 13 + noble 14 + proud 15 + quick 16 + robust 17 + smart 18 + swift 19 + valiant 20 + wise 21 + zealous 22 + cosmic 23 + stellar 24 + mystic 25 + electric 26 + quantum 27 + cyber 28 + turbo 29 + mega 30 + ultra 31 + hyper 32 + super 33 + ancient 34 + bold 35 + calm 36 + dynamic 37 + fierce 38 + golden 39 + heroic 40 + iron
+57
tools/name-generator/flake-module.nix
··· 1 + { ... }: 2 + { 3 + perSystem = 4 + { pkgs, ... }: 5 + let 6 + dataFiles = pkgs.stdenv.mkDerivation { 7 + name = "name-generator-data"; 8 + src = ./.; 9 + installPhase = '' 10 + mkdir -p $out 11 + cp adjectives.txt nouns.txt $out/ 12 + ''; 13 + }; 14 + in 15 + { 16 + packages.name-generator = pkgs.writeShellApplication { 17 + name = "name-generator"; 18 + runtimeInputs = [ pkgs.bash ]; 19 + text = '' 20 + # Data files location 21 + DATA_DIR="${dataFiles}" 22 + # Default separator 23 + SEPARATOR="-" 24 + # Parse command line options 25 + while [[ $# -gt 0 ]]; do 26 + case $1 in 27 + -s|--separator) 28 + SEPARATOR="$2" 29 + shift 2 30 + ;; 31 + -h|--help) 32 + echo "Usage: $0 [OPTIONS]" 33 + echo "Generate a random name in the format: adjective-noun" 34 + echo "" 35 + echo "Options:" 36 + echo " -s, --separator CHAR Use CHAR as separator (default: -)" 37 + echo " -h, --help Show this help message" 38 + exit 0 39 + ;; 40 + *) 41 + echo "Unknown option: $1" 42 + exit 1 43 + ;; 44 + esac 45 + done 46 + # Read adjectives and nouns 47 + mapfile -t ADJECTIVES < "$DATA_DIR/adjectives.txt" 48 + mapfile -t NOUNS < "$DATA_DIR/nouns.txt" 49 + # Select random adjective and noun 50 + ADJ_INDEX=$((RANDOM % ''${#ADJECTIVES[@]})) 51 + NOUN_INDEX=$((RANDOM % ''${#NOUNS[@]})) 52 + # Output the generated name 53 + echo "''${ADJECTIVES[$ADJ_INDEX]}''${SEPARATOR}''${NOUNS[$NOUN_INDEX]}" 54 + ''; 55 + }; 56 + }; 57 + }
+42
tools/name-generator/name-generator.sh
··· 1 + #!/usr/bin/env bash 2 + set -euo pipefail 3 + 4 + # Default separator 5 + SEPARATOR="-" 6 + 7 + # Parse command line options 8 + while [[ $# -gt 0 ]]; do 9 + case $1 in 10 + -s|--separator) 11 + SEPARATOR="$2" 12 + shift 2 13 + ;; 14 + -h|--help) 15 + echo "Usage: $0 [OPTIONS]" 16 + echo "Generate a random name in the format: adjective-noun" 17 + echo "" 18 + echo "Options:" 19 + echo " -s, --separator CHAR Use CHAR as separator (default: -)" 20 + echo " -h, --help Show this help message" 21 + exit 0 22 + ;; 23 + *) 24 + echo "Unknown option: $1" 25 + exit 1 26 + ;; 27 + esac 28 + done 29 + 30 + # Get script directory 31 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 32 + 33 + # Read adjectives and nouns 34 + mapfile -t ADJECTIVES < "$SCRIPT_DIR/adjectives.txt" 35 + mapfile -t NOUNS < "$SCRIPT_DIR/nouns.txt" 36 + 37 + # Select random adjective and noun 38 + ADJ_INDEX=$((RANDOM % ${#ADJECTIVES[@]})) 39 + NOUN_INDEX=$((RANDOM % ${#NOUNS[@]})) 40 + 41 + # Output the generated name 42 + echo "${ADJECTIVES[$ADJ_INDEX]}${SEPARATOR}${NOUNS[$NOUN_INDEX]}"
+40
tools/name-generator/nouns.txt
··· 1 + phoenix 2 + dragon 3 + wizard 4 + knight 5 + falcon 6 + tiger 7 + eagle 8 + wolf 9 + shark 10 + panther 11 + raven 12 + cobra 13 + viper 14 + ninja 15 + samurai 16 + ronin 17 + hacker 18 + coder 19 + builder 20 + creator 21 + artist 22 + pilot 23 + captain 24 + ranger 25 + storm 26 + blaze 27 + thunder 28 + comet 29 + nova 30 + pulsar 31 + nebula 32 + galaxy 33 + warrior 34 + guardian 35 + sentinel 36 + champion 37 + voyager 38 + explorer 39 + hunter 40 + shadow
+10 -19
tools/pass/flake-module.nix
··· 1 1 { self, ... }: 2 2 { 3 3 perSystem = 4 - { pkgs, ... }: 4 + { pkgs, system, ... }: 5 + let 6 + passWithOtp = pkgs.pass.withExtensions (ext: [ ext.pass-otp ]); 7 + in 5 8 { 6 - packages.pass = pkgs.symlinkJoin { 7 - name = "pass"; 8 - paths = [ 9 - (pkgs.writeShellApplication { 10 - name = "pass"; 11 - runtimeInputs = [ 12 - (pkgs.pass.withExtensions (ext: [ ext.pass-otp ])) 13 - pkgs.gnupg 14 - ]; 15 - text = '' 16 - set -efu 17 - gpg --import ${self.keys.pgp.yubi.key} &>/dev/null 18 - echo '${self.keys.pgp.yubi.id}:6:' | gpg --import-ownertrust &>/dev/null 19 - pass "$@" 20 - ''; 21 - }) 22 - (pkgs.pass.withExtensions (ext: [ ext.pass-otp ])) 23 - ]; 9 + packages.pass = self.libWithPkgs.${system}.makeWrapper passWithOtp { 10 + runtimeInputs = [ pkgs.gnupg ]; 11 + preHook = '' 12 + gpg --import ${self.keys.pgp.yubi.key} &>/dev/null 13 + echo '${self.keys.pgp.yubi.id}:6:' | gpg --import-ownertrust &>/dev/null 14 + ''; 24 15 }; 25 16 }; 26 17 }
+27
tools/tmux/flake-module.nix
··· 1 + { self, ... }: 2 + { 3 + perSystem = 4 + { pkgs, system, ... }: 5 + let 6 + tmuxConfigText = '' 7 + set-option -g default-terminal screen-256color 8 + 9 + # Auto-destroy sessions when no clients attached (handles terminal close) 10 + set -g destroy-unattached on 11 + ''; 12 + tmuxConfigFile = pkgs.writeText "tmux.conf" tmuxConfigText; 13 + in 14 + { 15 + packages.tmux = 16 + (self.libWithPkgs.${system}.makeWrapper pkgs.tmux { 17 + flags = { 18 + "-f" = tmuxConfigFile; 19 + }; 20 + }).overrideAttrs 21 + (old: { 22 + passthru = (old.passthru or { }) // { 23 + tmuxConfig = tmuxConfigText; 24 + }; 25 + }); 26 + }; 27 + }
+17
tools/vm-boot/flake-module.nix
··· 1 + { ... }: 2 + { 3 + perSystem = 4 + { pkgs, ... }: 5 + { 6 + packages.vm-boot = pkgs.writeShellApplication { 7 + name = "vm-boot"; 8 + runtimeInputs = with pkgs; [ 9 + qemu 10 + file 11 + util-linux 12 + nix 13 + ]; 14 + text = builtins.readFile ./vm-boot.sh; 15 + }; 16 + }; 17 + }
+159
tools/vm-boot/vm-boot.sh
··· 1 + #!/usr/bin/env bash 2 + set -euo pipefail 3 + set -x 4 + 5 + # Default values 6 + ISO_FILE="" 7 + MEMORY="2048" 8 + CPUS="2" 9 + ARCH="" 10 + 11 + usage() { 12 + cat <<EOF 13 + Usage: $0 [OPTIONS] ISO_FILE 14 + 15 + Boot an ISO file in a VM with automatic architecture detection. 16 + 17 + OPTIONS: 18 + -m, --memory SIZE Memory in MB (default: 2048) 19 + -c, --cpus COUNT Number of CPU cores (default: 2) 20 + -a, --arch ARCH Force architecture (x86_64 or aarch64) 21 + -h, --help Show this help message 22 + 23 + EXAMPLE: 24 + $0 installer.iso 25 + $0 -m 4096 -c 4 installer.iso 26 + $0 -a x86_64 installer.iso 27 + EOF 28 + } 29 + 30 + # Parse arguments 31 + while [[ $# -gt 0 ]]; do 32 + case $1 in 33 + -m|--memory) 34 + MEMORY="$2" 35 + shift 2 36 + ;; 37 + -c|--cpus) 38 + CPUS="$2" 39 + shift 2 40 + ;; 41 + -a|--arch) 42 + ARCH="$2" 43 + shift 2 44 + ;; 45 + -h|--help) 46 + usage 47 + exit 0 48 + ;; 49 + -*) 50 + echo "Unknown option: $1" 51 + usage 52 + exit 1 53 + ;; 54 + *) 55 + if [ -z "$ISO_FILE" ]; then 56 + ISO_FILE="$1" 57 + else 58 + echo "Multiple ISO files specified" 59 + usage 60 + exit 1 61 + fi 62 + shift 63 + ;; 64 + esac 65 + done 66 + 67 + # Check if ISO file is provided 68 + if [ -z "$ISO_FILE" ]; then 69 + echo "Error: ISO file not specified" 70 + usage 71 + exit 1 72 + fi 73 + 74 + # Check if ISO file exists 75 + if [ ! -f "$ISO_FILE" ]; then 76 + echo "Error: ISO file '$ISO_FILE' not found" 77 + exit 1 78 + fi 79 + 80 + # Detect architecture if not specified 81 + if [ -z "$ARCH" ]; then 82 + echo "Detecting ISO architecture..." 83 + ISO_INFO=$(file "$ISO_FILE") 84 + if echo "$ISO_INFO" | grep -q "aarch64"; then 85 + ARCH="aarch64" 86 + elif echo "$ISO_INFO" | grep -q "x86-64\|x86_64"; then 87 + ARCH="x86_64" 88 + else 89 + # Try to extract more info from the ISO 90 + ISO_LABEL=$(blkid -o value -s LABEL "$ISO_FILE" 2>/dev/null || echo "") 91 + if echo "$ISO_LABEL" | grep -q "aarch64"; then 92 + ARCH="aarch64" 93 + elif echo "$ISO_LABEL" | grep -q "x86"; then 94 + ARCH="x86_64" 95 + else 96 + echo "Could not detect architecture from ISO. Please specify with -a option." 97 + echo "Supported architectures: x86_64, aarch64" 98 + exit 1 99 + fi 100 + fi 101 + fi 102 + 103 + echo "Booting $ISO_FILE with $ARCH architecture..." 104 + echo "Memory: $MEMORY MB, CPUs: $CPUS" 105 + 106 + # Build QEMU command based on architecture 107 + if [ "$ARCH" = "x86_64" ]; then 108 + echo "Starting x86_64 VM with EFI..." 109 + # Get the x86_64-linux UEFI firmware 110 + echo "Building x86_64-linux UEFI firmware..." 111 + UEFI_STORE_PATH=$(nix build --no-link --print-out-paths --system x86_64-linux nixpkgs#OVMF.fd) 112 + UEFI_FW="$UEFI_STORE_PATH/FV/OVMF.fd" 113 + if [ ! -f "$UEFI_FW" ]; then 114 + echo "Error: Could not find x86_64 UEFI firmware at $UEFI_FW" 115 + exit 1 116 + fi 117 + echo "Using UEFI firmware: $UEFI_FW" 118 + exec qemu-system-x86_64 \ 119 + -machine q35 \ 120 + -cpu qemu64 \ 121 + -m "$MEMORY" \ 122 + -smp "$CPUS" \ 123 + -accel tcg \ 124 + -bios "$UEFI_FW" \ 125 + -cdrom "$ISO_FILE" \ 126 + -boot d \ 127 + -nographic \ 128 + -serial mon:stdio 129 + elif [ "$ARCH" = "aarch64" ]; then 130 + echo "Starting aarch64 VM..." 131 + # Get the aarch64-linux UEFI firmware 132 + echo "Building aarch64 UEFI firmware..." 133 + # Use the pre-built firmware path we know works 134 + UEFI_FW="/nix/store/2ciakp99mrcb3fx98j6ji0nxkxcyq5gy-OVMF-202505-fd/FV/QEMU_EFI.fd" 135 + if [ ! -f "$UEFI_FW" ]; then 136 + echo "Firmware not found in store, building..." 137 + UEFI_STORE_PATH=$(nix build --no-link --print-out-paths nixpkgs#OVMF-fd.aarch64-linux) 138 + UEFI_FW="$UEFI_STORE_PATH/FV/QEMU_EFI.fd" 139 + fi 140 + if [ ! -f "$UEFI_FW" ]; then 141 + echo "Error: Could not find aarch64 UEFI firmware at $UEFI_FW" 142 + exit 1 143 + fi 144 + echo "Using UEFI firmware: $UEFI_FW" 145 + exec qemu-system-aarch64 \ 146 + -machine virt \ 147 + -cpu cortex-a57 \ 148 + -m "$MEMORY" \ 149 + -smp "$CPUS" \ 150 + -accel tcg \ 151 + -bios "$UEFI_FW" \ 152 + -cdrom "$ISO_FILE" \ 153 + -boot d \ 154 + -nographic 155 + else 156 + echo "Error: Unsupported architecture '$ARCH'" 157 + echo "Supported architectures: x86_64, aarch64" 158 + exit 1 159 + fi
+26 -27
tools/zsh/flake-module.nix
··· 1 - { ... }: 1 + { self, ... }: 2 2 { 3 3 perSystem = 4 4 { pkgs, ... }: ··· 204 204 tmux set-environment -g SSH_AUTH_SOCK "$SSH_AUTH_SOCK" 2>/dev/null 205 205 fi 206 206 207 - # Start tmux with a new session 208 - exec tmux -u new-session 207 + # Generate a readable session name using name-generator 208 + session_name=$(${ 209 + self.packages.${pkgs.system}.name-generator 210 + }/bin/name-generator 2>/dev/null || echo "session-$$") 211 + # Start tmux with the generated session name 212 + exec tmux -u new-session -s "$session_name" 209 213 fi 210 214 fi 211 215 ··· 233 237 ''; 234 238 in 235 239 { 236 - packages.zsh = pkgs.symlinkJoin { 237 - name = "zsh-with-config"; 238 - paths = [ 239 - (pkgs.writeShellApplication { 240 - name = "zsh"; 241 - runtimeInputs = with pkgs; [ 242 - tmux 243 - fzf 244 - direnv 245 - atuin 246 - nix-index 247 - ]; 248 - text = '' 249 - unset IN_NIX_SHELL 250 - export ZDOTDIR=${pkgs.writeTextDir "/.zshrc" zshrc} 251 - exec ${pkgs.zsh}/bin/zsh "$@" 252 - ''; 253 - }) 254 - pkgs.zsh 255 - ]; 256 - passthru = { 257 - inherit zshrc; 258 - }; 259 - }; 240 + packages.zsh = 241 + (self.libWithPkgs.${pkgs.system}.makeWrapper pkgs.zsh { 242 + runtimeInputs = with pkgs; [ 243 + tmux 244 + fzf 245 + direnv 246 + atuin 247 + nix-index 248 + self.packages.${pkgs.system}.name-generator 249 + ]; 250 + env = { 251 + ZDOTDIR = pkgs.writeTextDir "/.zshrc" zshrc; 252 + }; 253 + }).overrideAttrs 254 + (old: { 255 + passthru = (old.passthru or { }) // { 256 + inherit zshrc; 257 + }; 258 + }); 260 259 }; 261 260 }
-1
2configs/desktops/lib/wayland.nix
··· 10 10 ../../power-action.nix 11 11 ../../xdg-open.nix 12 12 ../../yubikey.nix 13 - ../../tmux.nix 14 13 ../../themes.nix 15 14 ../../fonts.nix 16 15 # ../pipewire.nix
-1
2configs/desktops/lib/x11.nix
··· 12 12 ../../xdg-open.nix 13 13 ../../yubikey.nix 14 14 ../../pipewire.nix 15 - ../../tmux.nix 16 15 ../../themes.nix 17 16 ../../fonts.nix 18 17 {