aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/darwin/default.nix36
-rw-r--r--modules/darwin/homebrew.nix182
-rw-r--r--modules/darwin/macos.nix140
-rw-r--r--modules/home/claude.nix87
-rw-r--r--modules/home/default.nix52
-rw-r--r--modules/home/ghostty.nix14
-rw-r--r--modules/home/git.nix55
-rw-r--r--modules/home/lazygit.nix20
-rw-r--r--modules/home/starship.nix7
-rw-r--r--modules/home/tmux.nix33
-rw-r--r--modules/home/twm.nix38
-rw-r--r--modules/home/zsh.nix186
-rw-r--r--modules/nixos/default.nix59
13 files changed, 909 insertions, 0 deletions
diff --git a/modules/darwin/default.nix b/modules/darwin/default.nix
new file mode 100644
index 0000000..5b487ef
--- /dev/null
+++ b/modules/darwin/default.nix
@@ -0,0 +1,36 @@
+{ pkgs, ... }:
+
+{
+ imports = [
+ ./homebrew.nix
+ ./macos.nix
+ ];
+
+ # Nix is managed by Determinate installer
+ nix.enable = false;
+
+ # Allow unfree packages
+ nixpkgs.config.allowUnfree = true;
+
+ # System state version – do not change after first installation
+ system.stateVersion = 5;
+
+ # Primary user for system defaults and homebrew
+ system.primaryUser = "cobraz";
+
+ # Enable Touch ID for sudo
+ security.pam.services.sudo_local.touchIdAuth = true;
+
+ # Remap Caps Lock to Escape
+ system.keyboard = {
+ enableKeyMapping = true;
+ remapCapsLockToEscape = true;
+ };
+
+ # Core system packages available to all users
+ environment.systemPackages = with pkgs; [
+ curl
+ git
+ wget
+ ];
+}
diff --git a/modules/darwin/homebrew.nix b/modules/darwin/homebrew.nix
new file mode 100644
index 0000000..ada2229
--- /dev/null
+++ b/modules/darwin/homebrew.nix
@@ -0,0 +1,182 @@
+{ ... }:
+
+{
+ homebrew = {
+ enable = true;
+
+ onActivation = {
+ autoUpdate = true;
+ upgrade = true;
+ cleanup = "zap";
+ };
+
+ taps = [
+ "oven-sh/bun"
+ "pulumi/tap"
+ "go-task/tap"
+ "tursodatabase/tap"
+ "simenandre/tools"
+ "anomalyco/tap"
+ "siderolabs/tap"
+ ];
+
+ brews = [
+ # Editors and terminal multiplexers
+ "neovim"
+ "tmux"
+
+ # Shells and shell utilities
+ "zsh"
+ "bash"
+ "bash-completion"
+ "ghq"
+ "moreutils"
+
+ # GNU tools
+ "findutils"
+ "gnu-sed"
+ "grep"
+
+ # Networking tools
+ "openssh"
+ "nmap"
+ "wget"
+ "cloudflared"
+
+ # Compression libraries and tools
+ "lz4"
+ "zlib"
+ "zstd"
+ "libffi"
+ "p7zip"
+ "pigz"
+
+ # Programming languages and runtimes
+ "golangci-lint"
+ "rust"
+ "zig"
+ "typst"
+
+ # JavaScript runtimes and package managers
+ "bun"
+ "pnpm"
+ "volta"
+ "deno"
+
+ # Version control
+ "git"
+ "git-lfs"
+ "git-delta"
+ "lazygit"
+
+ # Search tools
+ "ripgrep"
+ "fzf"
+
+ # JSON/YAML processing tools
+ "jq"
+ "yq"
+
+ # Protobuf and gRPC
+ "protobuf"
+ "protoc-gen-go"
+ "protoc-gen-go-grpc"
+
+ # Cloud and IaC tools
+ "pulumi"
+ "pulumictl"
+ "azure-cli"
+ "kubectl"
+ "helm"
+ "cilium-cli"
+ "doctl"
+ "flyctl"
+ "talosctl"
+ "tektoncd-cli"
+ "runpodctl"
+ # Container tools
+ "docker"
+ "docker-buildx"
+ "docker-compose"
+ "docker-completion"
+ "colima"
+ "podman"
+ "podman-compose"
+
+ # Task runners and automation
+ "go-task"
+ "act"
+
+ # Databases and database tools
+ "turso"
+ "libpq"
+ "sqlc"
+
+ # Terminal tools
+ "pv"
+ "rename"
+ "tree"
+ "tldr"
+ "starship"
+ "gh"
+ "htop"
+
+ # HTTP and web servers
+ "caddy"
+ "goreleaser"
+
+ # Document and media tools
+ "pandoc"
+ "ffmpeg"
+ "tesseract"
+ "ghostscript"
+ "graphviz"
+
+ # Python tools
+ "pipx"
+
+ # Specific toolsets
+ "rclone"
+
+ # Mac App Store CLI
+ "mas"
+ ];
+
+ casks = [
+ "1password"
+ "1password-cli"
+ "arc"
+ "basictex"
+ "claude"
+ "discord"
+ "elgato-camera-hub"
+ "figma"
+ "font-jetbrains-mono"
+ "font-symbols-only-nerd-font"
+ "gcloud-cli"
+ "ghostty"
+ "google-chrome"
+ "google-chrome@canary"
+ "google-drive"
+ "libreoffice"
+ "loom"
+ "microsoft-teams"
+ "obsidian"
+ "postico"
+ "proton-mail"
+ "protonvpn"
+ "raycast"
+ "signal"
+ "slack"
+ "sonos"
+ "spotify"
+ "visual-studio-code"
+ "zoom"
+ ];
+
+ masApps = {
+ "Tailscale" = 1475387142;
+ "Jojo" = 1659864300;
+ };
+ };
+}
diff --git a/modules/darwin/macos.nix b/modules/darwin/macos.nix
new file mode 100644
index 0000000..0dd120a
--- /dev/null
+++ b/modules/darwin/macos.nix
@@ -0,0 +1,140 @@
+{ ... }:
+
+{
+ system.defaults = {
+ NSGlobalDomain = {
+ # Sidebar icon size: medium
+ NSTableViewDefaultSizeMode = 2;
+ # Always show scrollbars
+ AppleShowScrollBars = "Always";
+ # Expand save panel by default
+ NSNavPanelExpandedStateForSaveMode = true;
+ NSNavPanelExpandedStateForSaveMode2 = true;
+ # Expand print panel by default
+ PMPrintingExpandedStateForPrint = true;
+ PMPrintingExpandedStateForPrint2 = true;
+ # Save to disk (not to iCloud) by default
+ NSDocumentSaveNewDocumentsToCloud = false;
+ # Disable auto-correct
+ NSAutomaticSpellingCorrectionEnabled = false;
+ # Font smoothing
+ AppleFontSmoothing = 1;
+ };
+
+ dock = {
+ # Minimize windows into their application's icon
+ minimize-to-application = true;
+ # Show indicator lights for open applications in the Dock
+ show-process-indicators = true;
+ # Wipe all default app icons from the Dock
+ persistent-apps = [ ];
+ # Speed up Mission Control animations
+ expose-animation-duration = 0.1;
+ # Remove the auto-hiding Dock delay
+ autohide-delay = 0.0;
+ # Remove the animation when hiding/showing the Dock
+ autohide-time-modifier = 0.0;
+ # Automatically hide and show the Dock
+ autohide = true;
+ # Make Dock icons of hidden applications translucent
+ showhidden = true;
+ # Don't show recent applications in Dock
+ show-recents = false;
+ };
+
+ finder = {
+ # Show status bar
+ ShowStatusBar = true;
+ # Show path bar
+ ShowPathbar = true;
+ # Search the current folder by default
+ FXDefaultSearchScope = "SCcf";
+ # Disable warning when changing a file extension
+ FXEnableExtensionChangeWarning = false;
+ };
+
+ screencapture = {
+ # Save screenshots to ~/Screenshots
+ location = "~/Screenshots";
+ # Save screenshots as JPG
+ type = "jpg";
+ };
+
+ trackpad = {
+ # Enable tap to click for this user and the login screen
+ Clicking = true;
+ };
+
+
+ ActivityMonitor = {
+ # Show the main window when launching Activity Monitor
+ OpenMainWindow = true;
+ # Visualize CPU usage in the Activity Monitor Dock icon
+ IconType = 5;
+ # Show all processes in Activity Monitor
+ ShowCategory = 100;
+ # Sort Activity Monitor results by CPU usage
+ SortColumn = "CPUUsage";
+ SortDirection = 0;
+ };
+
+ LaunchServices = {
+ # Disable the "Are you sure you want to open this application?" dialog
+ LSQuarantine = false;
+ };
+
+ CustomUserPreferences = {
+ "com.apple.print.PrintingPrefs" = {
+ # Automatically quit printer app once the print jobs complete
+ "Quit When Finished" = true;
+ };
+ "com.apple.desktopservices" = {
+ # Avoid creating .DS_Store files on network or USB volumes
+ DSDontWriteNetworkStores = true;
+ DSDontWriteUSBStores = true;
+ };
+ "com.apple.Safari" = {
+ # Privacy: don't send search queries to Apple
+ UniversalSearchEnabled = false;
+ SuppressSearchSuggestions = true;
+ };
+ "com.apple.TimeMachine" = {
+ # Prevent Time Machine from prompting to use new hard drives
+ DoNotOfferNewDisksForBackup = true;
+ };
+ "com.apple.TextEdit" = {
+ PlainTextEncoding = 4;
+ PlainTextEncodingForWrite = 4;
+ };
+ "com.apple.SoftwareUpdate" = {
+ AutomaticCheckEnabled = true;
+ ScheduleFrequency = 1;
+ AutomaticDownload = 1;
+ CriticalUpdateInstall = 1;
+ };
+ "com.apple.commerce" = {
+ AutoUpdate = true;
+ AutoUpdateRestartRequired = true;
+ };
+ "com.apple.helpviewer" = {
+ DevMode = true;
+ };
+ "com.apple.BluetoothAudioAgent" = {
+ "Apple Bitpool Min (editable)" = 40;
+ };
+ "com.google.Chrome" = {
+ # Disable the all too sensitive backswipe on trackpads and Magic Mouse
+ AppleEnableSwipeNavigateWithScrolls = false;
+ AppleEnableMouseSwipeNavigateWithScrolls = false;
+ # Expand the print dialog by default
+ PMPrintingExpandedStateForPrint2 = true;
+ };
+ "com.apple.messageshelper.MessageController" = {
+ SOInputLineSettings = {
+ automaticQuoteSubstitutionEnabled = false;
+ continuousSpellCheckingEnabled = false;
+ };
+ };
+ };
+ };
+}
diff --git a/modules/home/claude.nix b/modules/home/claude.nix
new file mode 100644
index 0000000..5895f03
--- /dev/null
+++ b/modules/home/claude.nix
@@ -0,0 +1,87 @@
+{ ... }:
+
+{
+ home.file.".claude/CLAUDE.md".text = ''
+ # Code Style
+
+ - ONLY use comments to explain weird code or non-obvious behaviour, never to explain simple code.
+
+ # Git & PRs
+
+ - ALWAYS write short and concise git commit messages and pull request messages.
+ - NEVER include a Co-Authored-By: line in commits.
+ - NEVER include "Generated with [Claude Code]" in PRs.
+
+ # Docker & Runtimes
+
+ - ALWAYS use the latest LTS version for base Docker images, Node.js engine requirements and similar.
+ - ALWAYS strive to use base Docker images with common ancestors in multi-stage builds: a stage based on `debian:trixie-slim` strongly suggests using `node:24-trixie-slim`.
+
+ # Nix
+
+ - If a command is not available and there is a `flake.nix` in the project, use `nix develop` or `nix run` to run commands from the flake's dev shell instead of installing tools manually.
+ '';
+
+ home.file.".claude/skills/push/SKILL.md".text = ''
+ ---
+ name: push
+ description: Create a commit and push changes. Optionally creates a branch and opens a PR with an intelligently chosen reviewer.
+ disable-model-invocation: true
+ ---
+
+ # /push — Commit & Push Workflow
+
+ You are executing the /push skill. Follow these steps precisely.
+
+ ## Step 1: Analyze Changes
+
+ Run `git status` and `git diff` (staged + unstaged) to understand all current changes.
+ If there are no changes to commit, inform the user and stop.
+
+ ## Step 2: Stage Files
+
+ Stage the relevant files. Prefer `git add <specific files>` over `git add -A`.
+ Never stage files that look like secrets (.env, credentials, tokens).
+
+ ## Step 3: Ask — Branch or Main?
+
+ Use the **AskUserQuestion** tool to ask the user how to push. Example:
+
+ - question: "How should this be pushed?"
+ - header: "Push target"
+ - options:
+ - label: "Current branch", description: "Push directly to the current branch"
+ - label: "New branch + PR", description: "Create a new branch and open a pull request"
+
+ ### If "Current branch":
+
+ - Run `git push` (with `-u` if needed).
+ - Done.
+
+ ### If "New branch + PR":
+
+ 1. Create a descriptive branch name based on the changes (e.g., `feat/add-logging`, `fix/null-check-user`).
+ 2. Check out the new branch and push with `-u`.
+ 3. **Choose a reviewer:**
+ - Run `git log --format='%an' -50 | sort | uniq -c | sort -rn | head -10` to find active contributors.
+ - Cross-reference with files changed: run `git log --format='%an' -- <changed-files> | sort | uniq -c | sort -rn | head -5` to find who has context on the modified code.
+ - Pick the most relevant reviewer (not the current user — check with `git config user.name`).
+ - Build a list of up to 4 candidate reviewers and use **AskUserQuestion** to let the user pick. Example:
+ - question: "Who should review this PR?"
+ - header: "Reviewer"
+ - options: one per candidate, with description showing their commit count / relevance to changed files.
+ 4. Create the PR using `gh pr create`:
+ - Short title (under 70 chars).
+ - Body with a `## Summary` section (2-3 bullet points) and a `## Test plan` section.
+ - Add the confirmed reviewer with `--reviewer`.
+ 5. Return the PR URL to the user.
+
+ ## Step 4: Create Commit
+
+ - Analyze the staged diff.
+ - Write a concise commit message using **conventional commit format** (e.g., `feat:`, `fix:`, `refactor:`, `chore:`, `docs:`, `test:`, `ci:`). Include a scope when it adds clarity (e.g., `feat(auth):`, `fix(api):`).
+ - Output ONLY the raw commit message — no markdown, no code blocks, no backticks, no explanations.
+ - **Do NOT add a Co-Authored-By line. Never include any co-author trailer.**
+ - Create the commit using a HEREDOC for the message.
+ '';
+}
diff --git a/modules/home/default.nix b/modules/home/default.nix
new file mode 100644
index 0000000..70b1174
--- /dev/null
+++ b/modules/home/default.nix
@@ -0,0 +1,52 @@
+{ pkgs, lib, config, dina, ... }:
+
+{
+ imports = [
+ ./claude.nix
+ ./git.nix
+ ./ghostty.nix
+ ./starship.nix
+ ./tmux.nix
+ ./lazygit.nix
+ ./twm.nix
+ ./zsh.nix
+ ];
+
+ home = {
+ homeDirectory = lib.mkForce (
+ if pkgs.stdenv.isDarwin
+ then "/Users/${config.home.username}"
+ else "/home/${config.home.username}"
+ );
+ # Do not change this after the first installation
+ stateVersion = "24.11";
+ };
+
+ home.packages = with pkgs; [
+ claude-code
+ dina.packages.${pkgs.stdenv.hostPlatform.system}.default
+ delta
+ fzf
+ gh
+ ghq
+ git-get
+ htop
+ jq
+ kubectl
+ lazygit
+ nodejs_22
+ ripgrep
+ starship
+ talosctl
+ tmux
+ tree
+ twm
+ yq
+ opencode
+ ];
+
+ programs.go.enable = true;
+
+ # Let home-manager manage itself
+ programs.home-manager.enable = true;
+}
diff --git a/modules/home/ghostty.nix b/modules/home/ghostty.nix
new file mode 100644
index 0000000..1d42944
--- /dev/null
+++ b/modules/home/ghostty.nix
@@ -0,0 +1,14 @@
+{ ... }:
+
+{
+ home.file.".config/ghostty/config".text = ''
+ theme = tokyonight-storm
+
+ window-padding-x = 12
+ window-padding-y = 12
+ window-decoration = true
+ window-theme = "dark"
+ window-inherit-working-directory = true
+ window-inherit-font-size = true
+ '';
+}
diff --git a/modules/home/git.nix b/modules/home/git.nix
new file mode 100644
index 0000000..1915304
--- /dev/null
+++ b/modules/home/git.nix
@@ -0,0 +1,55 @@
+{ pkgs, ... }:
+
+{
+ programs.git = {
+ enable = true;
+
+ signing = if pkgs.stdenv.isDarwin then {
+ key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMCyUn5BfmAnd/6xi0X1mYF/+jPOmCS0bpNUePF55gCY";
+ signByDefault = true;
+ } else {
+ signByDefault = false;
+ };
+
+ settings = {
+ user = {
+ name = "Simen A. W. Olsen";
+ email = "hello@simenandre.no";
+ };
+ apply.whitespace = "fix";
+ core = {
+ trustctime = false;
+ editor = "nvim";
+ excludesfile = "~/.gitignore_global";
+ };
+ color.ui = "auto";
+ "color \"branch\"" = {
+ current = "yellow reverse";
+ local = "yellow";
+ remote = "green";
+ };
+ "color \"diff\"" = {
+ meta = "yellow bold";
+ frag = "magenta bold";
+ old = "red";
+ new = "green";
+ };
+ "color \"status\"" = {
+ added = "yellow";
+ changed = "green";
+ untracked = "cyan";
+ };
+ diff.renames = "copies";
+ help.autocorrect = 1;
+ merge.log = true;
+ push.autoSetupRemote = true;
+ github.user = "simenandre";
+ pull.rebase = true;
+ } // (if pkgs.stdenv.isDarwin then {
+ gpg.format = "ssh";
+ "gpg \"ssh\"".program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign";
+ "url \"ssh://git@github.com/\"".insteadOf = "https://github.com/";
+ commit.gpgsign = true;
+ } else {});
+ };
+}
diff --git a/modules/home/lazygit.nix b/modules/home/lazygit.nix
new file mode 100644
index 0000000..da8996a
--- /dev/null
+++ b/modules/home/lazygit.nix
@@ -0,0 +1,20 @@
+{ ... }:
+
+{
+ home.file.".config/lazygit/config.yml".text = ''
+ git:
+ pagers:
+ - colorArg: always
+ pager: delta --dark --paging=never
+ customCommands:
+ - key: "C"
+ context: "files"
+ description: "Generate commit message with Claude Code"
+ command: >
+ git diff --staged | $HOME/.local/bin/claude -p 'Generate a concise git commit message for these staged changes.
+ Output ONLY the raw commit message with no markdown, no code blocks, no backticks, no explanations.
+ Use conventional commit format.' --model haiku --output-format text > /tmp/commit_msg &&
+ GIT_EDITOR=nvim git commit -e -F /tmp/commit_msg
+ output: terminal
+ '';
+}
diff --git a/modules/home/starship.nix b/modules/home/starship.nix
new file mode 100644
index 0000000..4f3cb61
--- /dev/null
+++ b/modules/home/starship.nix
@@ -0,0 +1,7 @@
+{ ... }:
+
+{
+ programs.starship = {
+ enable = true;
+ };
+}
diff --git a/modules/home/tmux.nix b/modules/home/tmux.nix
new file mode 100644
index 0000000..f084f35
--- /dev/null
+++ b/modules/home/tmux.nix
@@ -0,0 +1,33 @@
+{ ... }:
+
+{
+ home.file.".tmux.conf".text = ''
+ unbind r
+ bind r source-file ~/.tmux.conf \; display-message "Config reloaded"
+
+ set -g default-terminal "tmux-256color"
+ set -ag terminal-overrides ",xterm-256color:RGB"
+
+ set -g default-command "$SHELL -l"
+
+ set -g prefix C-s
+
+ set -g mouse on
+
+ set -g base-index 1
+ set-window-option -g mode-keys vi
+
+ bind g new-window -c "#{pane_current_path}" -n "lazygit" lazygit
+
+ bind f run-shell "tmux neww twm"
+ bind F run-shell "tmux neww twm -l"
+ bind s run-shell "tmux neww twm -e" # i rebind the original `s` to `S` so I can still use it
+ bind e run-shell "tmux switch -t $TWM_DEFAULT" # i set TWM_DEFAULT in my shellrc, just a session that is always available as a scratch area
+
+ set -g @plugin 'tmux-plugins/tpm'
+ set -g @plugin 'christoomey/vim-tmux-navigator'
+ set -g @plugin 'tmux-plugins/tmux-sensible'
+
+ run '~/.tmux/plugins/tpm/tpm'
+ '';
+}
diff --git a/modules/home/twm.nix b/modules/home/twm.nix
new file mode 100644
index 0000000..f5609d7
--- /dev/null
+++ b/modules/home/twm.nix
@@ -0,0 +1,38 @@
+{ config, ... }:
+
+{
+ home.file.".config/twm/twm.yaml".text = ''
+ search_paths:
+ - ${config.home.homeDirectory}/ghq
+ - ${config.home.homeDirectory}/repositories
+ - ~/.config/nvim/
+
+ exclude_path_components:
+ - .git
+ - .direnv
+ - node_modules
+ - venv
+ - target
+
+ workspace_definitions:
+ - name: catchall
+ default_layout: catchall-dev
+
+ - name: go
+ has_all_files:
+ - go.mod
+ - go.sum
+
+ - name: docker-compose
+ has_any_file:
+ - docker-compose.yml
+ - docker-compose.yaml
+ has_all_files:
+ - Dockerfile
+ - .git
+
+ - name: pulumi
+ has_all_files:
+ - Pulumi.yaml
+ '';
+}
diff --git a/modules/home/zsh.nix b/modules/home/zsh.nix
new file mode 100644
index 0000000..678e70d
--- /dev/null
+++ b/modules/home/zsh.nix
@@ -0,0 +1,186 @@
+{ pkgs, ... }:
+
+let
+ zsh-autocomplete = pkgs.fetchFromGitHub {
+ owner = "marlonrichert";
+ repo = "zsh-autocomplete";
+ rev = "main";
+ hash = "sha256-KYey9sidz8VzgfM9YVaCsVXpKKdCjSHk3tuadQz9Re0=";
+ };
+ zsh-autosuggestions = pkgs.fetchFromGitHub {
+ owner = "zsh-users";
+ repo = "zsh-autosuggestions";
+ rev = "master";
+ hash = "sha256-KmkXgK1J6iAyb1FtF/gOa0adUnh1pgFsgQOUnNngBaE=";
+ };
+ zsh-vi-mode = pkgs.fetchFromGitHub {
+ owner = "jeffreytse";
+ repo = "zsh-vi-mode";
+ rev = "master";
+ hash = "sha256-WdZHCVxVVOs4HyG5f56vAA17UWYOvb9Yf6v7M1RIdU4=";
+ };
+in
+{
+ home.file = {
+ ".zsh_repos/marlonrichert/zsh-autocomplete".source = zsh-autocomplete;
+ ".zsh_repos/zsh-users/zsh-autosuggestions".source = zsh-autosuggestions;
+ ".zsh_repos/jeffreytse/zsh-vi-mode".source = zsh-vi-mode;
+ ".zshenv".text = ''
+ ##!/bin/zsh
+ #
+ # .zshenv - Zsh environment file, loaded always.
+ #
+
+ # Set ZDOTDIR if you want to re-home Zsh.
+ export XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config}
+ export XDG_DATA_HOME=''${XDG_DATA_HOME:-$HOME/.local/share}
+ export XDG_CACHE_HOME=''${XDG_CACHE_HOME:-$HOME/.cache}
+ export ZDOTDIR=''${ZDOTDIR:-$XDG_CONFIG_HOME/zsh}
+
+ # Ensure path arrays do not contain duplicates.
+ typeset -gU path fpath
+
+ # Set the list of directories that zsh searches for commands.
+ path=(
+ $HOME/{,s}bin(N)
+ $HOME/.local/{,s}bin(N)
+ /opt/{homebrew,local}/{,s}bin(N)
+ /usr/local/{,s}bin(N)
+ $path
+ )
+ '' + (
+ if pkgs.stdenv.isDarwin then ''
+ eval "$(/opt/homebrew/bin/brew shellenv)"
+ '' else ""
+ ) + ''
+ [ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env"
+ '';
+
+ ".hushlogin".text = "";
+
+ ".config/zsh/.zshrc".text = ''
+ #!/bin/bash
+ # ^----- get shellcheck hints based on bash
+ # https://github.com/koalaman/shellcheck/issues/809
+ # shellcheck disable=SC1090 # sourced filenames with variables
+
+ # Remove stale compiled zsh files that shadow nix-managed configs
+ rm -f ~/.config/zsh/.zshrc.zwc ~/.config/zsh/{aliases,path,extras}.zwc
+
+ eval "$(starship init zsh)"
+
+ source ~/.zsh_repos/marlonrichert/zsh-autocomplete/zsh-autocomplete.plugin.zsh
+ source ~/.zsh_repos/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh
+ # zsh-vi-mode clobbers all keybindings on init, so anything
+ # that needs to survive (fzf, autosuggest) goes in zvm_after_init.
+ function zvm_after_init() {
+ eval "$(fzf --zsh)"
+ bindkey '^I^I' autosuggest-accept
+ }
+
+ source ~/.zsh_repos/jeffreytse/zsh-vi-mode/zsh-vi-mode.plugin.zsh
+
+ for file in ~/.config/zsh/{aliases,path,extras}; do
+ if [[ -r "$file" ]] && [[ -f "$file" ]]; then
+ # shellcheck source=/dev/null
+ source "$file"
+ fi
+ done
+
+ command -v twm &>/dev/null && eval "$(twm --print-zsh-completion)"
+ '';
+
+ ".config/zsh/aliases".text = ''
+ # Git aliases
+ alias git-unpushed='git log --branches --not --remotes --no-walk --decorate --oneline'
+ alias gb='git checkout -b'
+ alias gfr='git fetch upstream && git rebase upstream/main'
+ alias git-undo='git reset --soft HEAD~1'
+ alias git-clean-branches="git for-each-ref --format '%(refname:short)' refs/heads | grep -v 'master\|main' | xargs git branch -D"
+ alias :q='exit'
+ alias ls='ls --color'
+ '' + (if pkgs.stdenv.isDarwin then ''
+ alias tailscale='/Applications/Tailscale.app/Contents/MacOS/Tailscale'
+ '' else "") + ''
+
+ # Generic / Random / Utility
+ alias rg="rg --hidden --glob '!.git' --glob '!node_modules'"
+ alias r='cd $REPOS/github.com/'
+ alias c='clear'
+ '' + (if pkgs.stdenv.isDarwin then ''
+ alias afk='pmset displaysleepnow'
+ '' else "") + ''
+ alias dl="cd ~/Downloads"
+ alias l='ls -l'
+ alias todo='nt'
+ alias tasks="open 'obsidian://open?file=-%20Oppgaver'"
+ alias todos="mt ~/ghq/github.com/simenandre/notes/"
+ alias today="open 'obsidian://advanced-uri?daily=true'"
+ alias tre="tree -I '.git|node_modules'"
+ alias timeliste="open 'https://tripletex.no/execute/updateHourlist?contextId=11208568'"
+ alias t="open 'https://tripletex.no/execute/updateHourlist?contextId=11208568'"
+ alias fork-cleaner="fork-cleaner --token \$(cat ~/.secrets/github-token 2>/dev/null)"
+ alias vi="nvim"
+ alias vim="nvim"
+ alias v="nvim"
+
+ alias dump-issues="getBjerkIssues > ~/Downloads/issues-$(date -I).json"
+ '';
+
+ ".config/zsh/path".text = ''
+ #!/bin/bash
+
+ # Add directories to the PATH and prevent to add the same directory multiple times upon shell reload.
+ add_to_path() {
+ if [[ -d "$1" ]] && [[ ":$PATH:" != *":$1:"* ]]; then
+ export PATH="$1:$PATH"
+ fi
+ }
+
+ add_to_path "$HOME/.volta/bin"
+ add_to_path "/Library/Frameworks/Firebird.framework/Resources/bin"
+ add_to_path "/opt/homebrew/opt/libpq/bin"
+ '';
+
+ ".config/zsh/extras".text = ''
+ if [[ "$(uname)" == "Darwin" ]]; then
+ export DOCKER_HOST="unix://$HOME/.colima/default/docker.sock"
+ fi
+
+ NOTES_DIR=$HOME/ghq/github.com/simenandre/notes/
+ function note() {
+ if [ $1 ]; then
+ FILE=$NOTES_DIR/$(date +%Y-%m-%d).md
+
+ if [ $2 ]; then
+ FILE=$2
+ fi
+
+ echo $1 >> $FILE
+ fi
+ }
+
+ function nt() {
+ if [ $1 ]; then
+ CONTENT="- [ ] $1"
+ note $CONTENT $2
+ fi
+ }
+
+ function getBjerkIssues() {
+ gh issue list --repo bjerkio/internal \
+ --search "-label:backlog -label:kind/system" \
+ --json title,body,url,createdAt,assignees,number,labels,comments \
+ --jq 'map({url, createdAt, title, labels: [.labels[].name], assignees: [.assignees[].login], body, number: "#\(.number)", comments: .comments | map({ body: .body, createdAt: .createdAt })})'
+ }
+
+ function g() {
+ export GOOGLE_CLOUD_PROJECT="sokkel-sandbox"
+ export GOOGLE_CLOUD_LOCATION="europe-north1"
+ export GOOGLE_GENAI_USE_VERTEXAI=true
+
+ gemini
+ }
+ '';
+ };
+}
diff --git a/modules/nixos/default.nix b/modules/nixos/default.nix
new file mode 100644
index 0000000..0d51dc4
--- /dev/null
+++ b/modules/nixos/default.nix
@@ -0,0 +1,59 @@
+{ pkgs, lib, ... }:
+
+{
+ imports = [ ];
+
+ # Enable experimental Nix features
+ nix.settings.experimental-features = "nix-command flakes";
+
+ # Allow unfree packages
+ nixpkgs.config.allowUnfree = true;
+
+ # Bootloader
+ boot.loader.systemd-boot.enable = true;
+ boot.loader.efi.canTouchEfiVariables = true;
+
+ # Networking
+ networking.hostName = "nixbox";
+ networking.networkmanager.enable = true;
+
+ # Timezone / locale
+ time.timeZone = "Europe/Oslo";
+ i18n.defaultLocale = "en_US.UTF-8";
+
+ # User account
+ users.users.simenandre = {
+ isNormalUser = true;
+ extraGroups = [ "wheel" "networkmanager" "docker" ];
+ shell = pkgs.zsh;
+ openssh.authorizedKeys.keys = [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDDXfT23Ne7Rl/rdiqrLf4vWfQqgUCo26+NcL41PVSag"
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJwHftCdnV/pscQ4qCK/biIBsKk/G0A3hLVI1o62CJF4 so@bjerk.io"
+ ];
+ };
+ programs.zsh.enable = true;
+
+
+ # Core system packages
+ environment.systemPackages = with pkgs; [
+ curl
+ gcc
+ git
+ wget
+ neovim
+ ghostty.terminfo
+ ];
+
+ # 1Password (for SSH signing)
+ programs._1password.enable = true;
+ programs._1password-gui.enable = true;
+
+ # Docker
+ virtualisation.docker.enable = true;
+
+ # SSH
+ services.openssh.enable = true;
+
+ # System state version – do not change after first installation
+ system.stateVersion = "24.11";
+}