Initial: Nix flake reproducing MacBook capabilities on x86_64 NixOS
- pi coding agent (from sscoble/pi-mono Forgejo mirror) - qmd vault search (via bunx @tobilu/qmd) - zsh + oh-my-zsh + developer tooling - home-manager config for sscoble user - Forgejo Action to check pi version drift daily
This commit is contained in:
commit
8155b9b64c
5 changed files with 461 additions and 0 deletions
51
.forgejo/workflows/check-pi-version.yml
Normal file
51
.forgejo/workflows/check-pi-version.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
name: Check pi version drift
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 6 * * *" # Daily at 6am
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-pi:
|
||||||
|
runs-on: nixos
|
||||||
|
steps:
|
||||||
|
- name: Checkout laptop-config
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Get latest pi-mono tag
|
||||||
|
id: tag
|
||||||
|
run: |
|
||||||
|
# Forgejo mirror of badlogic/pi-mono
|
||||||
|
LATEST=$(git ls-remote --tags https://os.scoble.me/forgejo/sscoble/pi-mono.git \
|
||||||
|
| grep -v '\^{}' \
|
||||||
|
| awk -F/ '{print $NF}' \
|
||||||
|
| grep -E '^v?[0-9]' \
|
||||||
|
| sort -V | tail -1)
|
||||||
|
echo "latest=$LATEST" >> $GITHUB_OUTPUT
|
||||||
|
echo "Latest pi-mono tag: $LATEST"
|
||||||
|
|
||||||
|
- name: Get current version in flake
|
||||||
|
id: current
|
||||||
|
run: |
|
||||||
|
CURRENT=$(grep -E 'version = "' flake.nix | head -1 | sed 's/.*version = "\([^"]*\)".*/\1/')
|
||||||
|
echo "current=$CURRENT" >> $GITHUB_OUTPUT
|
||||||
|
echo "Current flake version: $CURRENT"
|
||||||
|
|
||||||
|
- name: Open issue on drift
|
||||||
|
if: steps.tag.outputs.latest != '' && steps.tag.outputs.latest != steps.current.outputs.current
|
||||||
|
run: |
|
||||||
|
LATEST="${{ steps.tag.outputs.latest }}"
|
||||||
|
CURRENT="${{ steps.current.outputs.current }}"
|
||||||
|
echo "DRIFT: pi-mono $LATEST vs flake $CURRENT"
|
||||||
|
|
||||||
|
# Create issue via API if token is available
|
||||||
|
if [ -n "$FORGEJO_TOKEN" ]; then
|
||||||
|
curl -s -X POST "https://os.scoble.me/forgejo/api/v1/repos/sscoble/laptop-config/issues" \
|
||||||
|
-H "Authorization: token $FORGEJO_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"title\": \"pi: upstream $LATEST available (flake has $CURRENT)\",
|
||||||
|
\"body\": \"The pi-mono mirror has tag \`$LATEST\`. The flake currently targets \`$CURRENT\`.\\n\\nAction: update \`flake.nix\` version string and rebuild.\",
|
||||||
|
\"labels\": [\"improvement\"]
|
||||||
|
}"
|
||||||
|
fi
|
||||||
69
README.md
Normal file
69
README.md
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Scobleclaw Laptop Config
|
||||||
|
|
||||||
|
Nix flake that reproduces MacBook software capabilities on an x86\_64 NixOS laptop.
|
||||||
|
|
||||||
|
## What it installs
|
||||||
|
|
||||||
|
| Tool | Purpose | Source |
|
||||||
|
|------|---------|--------|
|
||||||
|
| **pi** | Coding agent (TUI) | `sscoble/pi-mono` (Forgejo mirror of `badlogic/pi-mono`) |
|
||||||
|
| **qmd** | Vault search | `@tobilu/qmd` (npm, wrapped via bunx) |
|
||||||
|
| **git** | Version control | nixpkgs |
|
||||||
|
| **nodejs** | JavaScript runtime | nixpkgs |
|
||||||
|
| **bun** | Fast JS runtime + bundler | nixpkgs |
|
||||||
|
| **zsh** | Shell (+ oh-my-zsh, autosuggestions, syntax highlighting) | nixpkgs |
|
||||||
|
| **python3 + uv** | Python tooling | nixpkgs |
|
||||||
|
| **docker** | Containers | nixpkgs |
|
||||||
|
| **tailscale** | Mesh VPN | nixpkgs |
|
||||||
|
| **ripgrep, fd, jq, bat, eza, delta** | Modern CLI tools | nixpkgs |
|
||||||
|
| **tmux** | Terminal multiplexer | nixpkgs |
|
||||||
|
| **nerd fonts** | JetBrains Mono, Fira Code | nixpkgs |
|
||||||
|
|
||||||
|
## First-time setup (new laptop)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install NixOS (minimal ISO)
|
||||||
|
# 2. Clone this repo
|
||||||
|
git clone https://os.scoble.me/forgejo/sscoble/laptop-config.git ~/laptop-config
|
||||||
|
|
||||||
|
# 3. First build (this will fail — copy the npmDepsHash from error output)
|
||||||
|
nix build ~/laptop-config#pi-coding-agent
|
||||||
|
|
||||||
|
# 4. Edit flake.nix: set npmDepsHash to the value from step 3
|
||||||
|
|
||||||
|
# 5. Rebuild system
|
||||||
|
sudo nixos-rebuild switch --flake ~/laptop-config#laptop
|
||||||
|
|
||||||
|
# 6. Restore personal configs
|
||||||
|
# Copy from backup:
|
||||||
|
# ~/.pi/agent/SYSTEM.md
|
||||||
|
# ~/.pi/agent/models.json
|
||||||
|
# ~/.pi/agent/mcp.json
|
||||||
|
# ~/.pi/agent/settings.json
|
||||||
|
# ~/.ssh/id_ed25519
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating pi
|
||||||
|
|
||||||
|
The pi-mono repo is mirrored from upstream and syncs every 24h.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Force a mirror sync (on n97-mini)
|
||||||
|
ssh n97-mini 'sudo -u forgejo forgejo admin repo sync --config /var/lib/forgejo/custom/conf/app.ini sscoble/pi-mono'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repo structure
|
||||||
|
|
||||||
|
```
|
||||||
|
laptop-config/
|
||||||
|
├── flake.nix # Main entry: overlay, nixosConfig, devShell
|
||||||
|
├── modules/
|
||||||
|
│ └── laptop.nix # NixOS system module (packages, shell, fonts, SSH)
|
||||||
|
├── home/
|
||||||
|
│ └── sscoble.nix # home-manager user config (git, tools, pi dirs)
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Forgejo Actions
|
||||||
|
|
||||||
|
An action keeps pi up to date — when upstream `badlogic/pi-mono` releases a new tag, the Forgejo mirror picks it up automatically. A scheduled workflow in this repo checks for tag drift daily.
|
||||||
106
flake.nix
Normal file
106
flake.nix
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
{
|
||||||
|
description = "Scobleclaw laptop — NixOS configuration reproducing MacBook software capabilities on x86_64";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
home-manager = {
|
||||||
|
url = "github:nix-community/home-manager";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
pi-mono = {
|
||||||
|
url = "git+https://os.scoble.me/forgejo/sscoble/pi-mono.git";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, home-manager, pi-mono, ... }@inputs: let
|
||||||
|
# System we're targeting (x86_64 NixOS laptop)
|
||||||
|
system = "x86_64-linux";
|
||||||
|
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [ self.overlays.default ];
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
# Overlay — add pi and qmd to nixpkgs
|
||||||
|
overlays.default = final: prev: {
|
||||||
|
# pi coding agent — built from Forgejo mirror of pi-mono
|
||||||
|
pi-coding-agent = final.buildNpmPackage {
|
||||||
|
pname = "pi-coding-agent";
|
||||||
|
version = "0.73.0";
|
||||||
|
src = pi-mono;
|
||||||
|
npmDepsHash = ""; # Set after first build: nix-build outputs the correct hash
|
||||||
|
npmWorkspace = "packages/coding-agent";
|
||||||
|
npmFlags = [ "--legacy-peer-deps" ];
|
||||||
|
dontNpmBuild = true;
|
||||||
|
preConfigure = ''
|
||||||
|
# Build dependencies first (monorepo workspace order)
|
||||||
|
cd packages/ai && npm run build && cd ../..
|
||||||
|
cd packages/agent && npm run build && cd ../..
|
||||||
|
cd packages/tui && npm run build && cd ../..
|
||||||
|
cd packages/coding-agent && npm run build && cd ../..
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out/lib/node_modules/@mariozechner/pi-coding-agent
|
||||||
|
cp -r packages/coding-agent/dist $out/lib/node_modules/@mariozechner/pi-coding-agent/
|
||||||
|
cp packages/coding-agent/package.json $out/lib/node_modules/@mariozechner/pi-coding-agent/
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cat > $out/bin/pi <<'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
exec ${final.nodejs}/bin/node $out/lib/node_modules/@mariozechner/pi-coding-agent/dist/cli.js "$@"
|
||||||
|
EOF
|
||||||
|
chmod +x $out/bin/pi
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# qmd — vault search tool, wraps npm package via bun
|
||||||
|
qmd = final.writeShellScriptBin "qmd" ''
|
||||||
|
exec ${final.bun}/bin/bunx --bun @tobilu/qmd "$@"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Pi config directory structure (without secrets)
|
||||||
|
pi-config-dir = final.runCommand "pi-config-dir" {} ''
|
||||||
|
mkdir -p $out
|
||||||
|
# Placeholder — real config should be restored from backup
|
||||||
|
echo "# Pi agent configuration" > $out/README.md
|
||||||
|
echo "# Defaults" > $out/settings.json
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# NixOS configuration for the laptop
|
||||||
|
nixosConfigurations.laptop = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
modules = [
|
||||||
|
home-manager.nixosModules.home-manager
|
||||||
|
./modules/laptop.nix
|
||||||
|
{
|
||||||
|
home-manager.useGlobalPkgs = true;
|
||||||
|
home-manager.useUserPackages = true;
|
||||||
|
home-manager.users.sscoble = import ./home/sscoble.nix;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Standalone dev shell for trying things out
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
pi-coding-agent
|
||||||
|
qmd
|
||||||
|
nodejs
|
||||||
|
bun
|
||||||
|
git
|
||||||
|
zsh
|
||||||
|
];
|
||||||
|
shellHook = ''
|
||||||
|
echo "🦞 Scobleclaw laptop dev shell"
|
||||||
|
echo " pi: $(which pi)"
|
||||||
|
echo " qmd: $(which qmd)"
|
||||||
|
echo " node: $(node --version)"
|
||||||
|
echo " bun: $(bun --version)"
|
||||||
|
echo " git: $(git --version)"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
122
home/sscoble.nix
Normal file
122
home/sscoble.nix
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
home.username = "sscoble";
|
||||||
|
home.homeDirectory = "/home/sscoble";
|
||||||
|
home.stateVersion = "25.11";
|
||||||
|
|
||||||
|
# Don't let home-manager manage these
|
||||||
|
manual.json.enable = false;
|
||||||
|
manual.manpages.enable = false;
|
||||||
|
|
||||||
|
# === Home Packages ===
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
# Python for scripting
|
||||||
|
python3
|
||||||
|
uv
|
||||||
|
|
||||||
|
# Go
|
||||||
|
go
|
||||||
|
|
||||||
|
# Container tools
|
||||||
|
docker
|
||||||
|
dive
|
||||||
|
|
||||||
|
# Network tools
|
||||||
|
nmap
|
||||||
|
tcpdump
|
||||||
|
speedtest-cli
|
||||||
|
tailscale
|
||||||
|
|
||||||
|
# Extra editors / viewers
|
||||||
|
bat # better cat
|
||||||
|
eza # better ls
|
||||||
|
delta # better git diff
|
||||||
|
tldr # simplified man pages
|
||||||
|
|
||||||
|
# File sync
|
||||||
|
rsync
|
||||||
|
rclone
|
||||||
|
];
|
||||||
|
|
||||||
|
# === Git Config ===
|
||||||
|
programs.git = {
|
||||||
|
enable = true;
|
||||||
|
userName = "Scott Scoble";
|
||||||
|
userEmail = "sscoble@scobleclaw.dev";
|
||||||
|
extraConfig = {
|
||||||
|
init.defaultBranch = "main";
|
||||||
|
pull.rebase = true;
|
||||||
|
push.autoSetupRemote = true;
|
||||||
|
core.editor = "vim";
|
||||||
|
};
|
||||||
|
aliases = {
|
||||||
|
lol = "log --graph --oneline --decorate --all";
|
||||||
|
co = "checkout";
|
||||||
|
br = "branch";
|
||||||
|
st = "status";
|
||||||
|
ci = "commit";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# === Pi Config Directory ===
|
||||||
|
# Creates ~/.pi/agent/ with template files — user should restore real configs
|
||||||
|
home.file.".pi/agent/README.md" = {
|
||||||
|
text = ''
|
||||||
|
# Pi Agent Configuration
|
||||||
|
Restore these files from backup:
|
||||||
|
- SYSTEM.md (core agent directive)
|
||||||
|
- models.json (provider + model configuration)
|
||||||
|
- mcp.json (MCP server connections)
|
||||||
|
- settings.json (pi preferences)
|
||||||
|
- auth.json (API keys — keep secure!)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# === Vault directory placeholder ===
|
||||||
|
home.file."vault/.gitkeep" = { text = ""; };
|
||||||
|
|
||||||
|
# === Shell Direnv ===
|
||||||
|
programs.direnv = {
|
||||||
|
enable = true;
|
||||||
|
nix-direnv.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# === SSH config (symlink to system config) ===
|
||||||
|
home.file.".ssh/config".text = ''
|
||||||
|
Host n97-mini
|
||||||
|
HostName 192.168.168.41
|
||||||
|
User sscoble
|
||||||
|
ForwardAgent yes
|
||||||
|
|
||||||
|
Host n97-mini-ext
|
||||||
|
HostName os.scoble.me
|
||||||
|
Port 22
|
||||||
|
User sscoble
|
||||||
|
ForwardAgent yes
|
||||||
|
|
||||||
|
Host forgejo
|
||||||
|
HostName os.scoble.me
|
||||||
|
User git
|
||||||
|
'';
|
||||||
|
|
||||||
|
# === Zsh extra config ===
|
||||||
|
programs.zsh = {
|
||||||
|
enable = true;
|
||||||
|
initExtra = ''
|
||||||
|
# Pi agent alias
|
||||||
|
alias pi="pi"
|
||||||
|
|
||||||
|
# QMD vault search
|
||||||
|
alias vq="qmd query"
|
||||||
|
|
||||||
|
# Nix shortcuts
|
||||||
|
alias nr="sudo nixos-rebuild switch --flake ~/laptop-config#laptop"
|
||||||
|
alias ngc="nix-collect-garbage -d"
|
||||||
|
'';
|
||||||
|
envExtra = ''
|
||||||
|
export EDITOR=vim
|
||||||
|
export VISUAL=vim
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
113
modules/laptop.nix
Normal file
113
modules/laptop.nix
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# === System Packages ===
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
# Core development tools
|
||||||
|
pi-coding-agent
|
||||||
|
qmd
|
||||||
|
nodejs
|
||||||
|
bun
|
||||||
|
git
|
||||||
|
zsh
|
||||||
|
curl
|
||||||
|
wget
|
||||||
|
|
||||||
|
# System utilities
|
||||||
|
htop
|
||||||
|
neofetch
|
||||||
|
ripgrep
|
||||||
|
fd
|
||||||
|
jq
|
||||||
|
tree
|
||||||
|
tmux
|
||||||
|
unzip
|
||||||
|
|
||||||
|
# SSH essentials
|
||||||
|
openssh
|
||||||
|
mosh
|
||||||
|
];
|
||||||
|
|
||||||
|
# === Shell ===
|
||||||
|
programs.zsh = {
|
||||||
|
enable = true;
|
||||||
|
autosuggestions.enable = true;
|
||||||
|
syntaxHighlighting.enable = true;
|
||||||
|
shellAliases = {
|
||||||
|
ll = "ls -la";
|
||||||
|
la = "ls -A";
|
||||||
|
l = "ls -CF";
|
||||||
|
nix-rebuild = "sudo nixos-rebuild switch --flake ~/laptop-config#laptop";
|
||||||
|
nix-clean = "sudo nix-collect-garbage -d && nix-collect-garbage -d";
|
||||||
|
vault-search = "qmd query";
|
||||||
|
pi-run = "pi";
|
||||||
|
};
|
||||||
|
ohMyZsh = {
|
||||||
|
enable = true;
|
||||||
|
plugins = [ "git" "node" "npm" "ssh-agent" "sudo" "history" ];
|
||||||
|
theme = "robbyrussell";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Default to zsh
|
||||||
|
users.defaultUserShell = pkgs.zsh;
|
||||||
|
|
||||||
|
# === SSH ===
|
||||||
|
programs.ssh = {
|
||||||
|
startAgent = true;
|
||||||
|
extraConfig = ''
|
||||||
|
Host n97-mini
|
||||||
|
HostName 192.168.168.41
|
||||||
|
User sscoble
|
||||||
|
ForwardAgent yes
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
|
||||||
|
Host n97-mini-ext
|
||||||
|
HostName os.scoble.me
|
||||||
|
Port 22
|
||||||
|
User sscoble
|
||||||
|
ForwardAgent yes
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
|
||||||
|
Host forgejo
|
||||||
|
HostName os.scoble.me
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/id_ed25519
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# === Fonts (for pi TUI) ===
|
||||||
|
fonts.packages = with pkgs; [
|
||||||
|
nerd-fonts.jetbrains-mono
|
||||||
|
nerd-fonts.fira-code
|
||||||
|
];
|
||||||
|
|
||||||
|
# === Nix Settings ===
|
||||||
|
nix = {
|
||||||
|
settings = {
|
||||||
|
experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
auto-optimise-store = true;
|
||||||
|
};
|
||||||
|
gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "weekly";
|
||||||
|
options = "--delete-older-than 30d";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# === User ===
|
||||||
|
users.users.sscoble = {
|
||||||
|
isNormalUser = true;
|
||||||
|
description = "Scott Scoble";
|
||||||
|
extraGroups = [ "networkmanager" "wheel" "docker" ];
|
||||||
|
shell = pkgs.zsh;
|
||||||
|
};
|
||||||
|
|
||||||
|
# === Locale ===
|
||||||
|
time.timeZone = "America/Los_Angeles";
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
# === Boot ===
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue