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