Compare commits

..

1 Commits

Author SHA1 Message Date
rust
41bd72fc40 chore: add Gas Town directories to gitignore
Added .runtime/, .claude/, and .logs/ to gitignore for Gas Town polecat workspace.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:20:24 -08:00
34 changed files with 132 additions and 1104 deletions

View File

@@ -6,7 +6,7 @@
# Issue prefix for this repository (used by bd init)
# If not set, bd init will auto-detect from directory name
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
issue-prefix: "x"
# issue-prefix: ""
# Use no-db mode: load from JSONL, no SQLite, write back after each command
# When true, bd will use .beads/issues.jsonl as the source of truth
@@ -59,6 +59,4 @@ sync-branch: "beads-sync"
# - linear.url
# - linear.api-key
# - github.org
# - github.repo
routing.mode: "explicit"
# - github.repo

View File

@@ -18,91 +18,3 @@ jobs:
run: nix flake check
env:
NIX_CONFIG: "access-tokens = git.johnogle.info=${{ secrets.GITEA_ACCESS_TOKEN }}"
build-and-cache:
runs-on: ubuntu-latest
needs: check
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v6
- uses: https://git.johnogle.info/johno/gitea-actions/nix-setup@v1
- name: Setup SSH for cache
run: |
mkdir -p ~/.ssh
echo "${{ secrets.CACHE_SSH_KEY }}" > ~/.ssh/cache_key
chmod 600 ~/.ssh/cache_key
ssh-keyscan -H ${{ secrets.CACHE_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
- name: Setup signing key
run: |
echo "${{ secrets.NIX_SIGNING_KEY }}" > /tmp/signing-key
chmod 600 /tmp/signing-key
- name: Build, sign, and cache all packages
run: |
PACKAGES=(
custom-claude-code
custom-app-launcher-server
custom-mcrcon-rbw
custom-tea-rbw
custom-rclone-torbox-setup
custom-beads
custom-gastown
custom-perles
qt-pinned-jellyfin-media-player
qt-pinned-stremio
nix-deck-kernel
)
FAILED=()
SKIPPED=()
for pkg in "${PACKAGES[@]}"; do
echo "::group::Building $pkg"
# Check if package is already cached by evaluating its store path and checking the remote
OUT_PATH=$(nix eval ".#$pkg.outPath" --raw 2>/dev/null)
if [ -n "$OUT_PATH" ] && ssh -i ~/.ssh/cache_key ${{ secrets.CACHE_USER }}@${{ secrets.CACHE_HOST }} \
"nix path-info '$OUT_PATH' >/dev/null 2>&1"; then
echo "⏭ $pkg already cached ($OUT_PATH), skipping"
SKIPPED+=("$pkg")
echo "::endgroup::"
continue
fi
# --cores 2 limits parallel jobs to reduce RAM pressure on john-endesktop
if BUILD_OUTPUT=$(nix build ".#$pkg" --no-link --print-out-paths --cores 2 2>&1); then
OUT_PATH=$(echo "$BUILD_OUTPUT" | grep '^/nix/store/' | tail -1)
echo "$BUILD_OUTPUT"
echo "Store path: $OUT_PATH"
# Sign the closure
nix store sign --key-file /tmp/signing-key -r "$OUT_PATH"
# Push to cache
nix copy --to "ssh-ng://${{ secrets.CACHE_USER }}@${{ secrets.CACHE_HOST }}?ssh-key=$HOME/.ssh/cache_key" "$OUT_PATH"
# Create GC root to prevent garbage collection
OUT_HASH=$(basename "$OUT_PATH" | cut -d'-' -f1)
ssh -i ~/.ssh/cache_key ${{ secrets.CACHE_USER }}@${{ secrets.CACHE_HOST }} \
"mkdir -p /nix/var/nix/gcroots/ci-cache && ln -sfn $OUT_PATH /nix/var/nix/gcroots/ci-cache/${OUT_HASH}"
echo "✓ $pkg cached successfully"
else
echo "✗ $pkg failed to build"
FAILED+=("$pkg")
fi
echo "::endgroup::"
done
if [ ${#SKIPPED[@]} -gt 0 ]; then
echo "Skipped (already cached): ${SKIPPED[*]}"
fi
if [ ${#FAILED[@]} -gt 0 ]; then
echo "::error::Failed packages: ${FAILED[*]}"
exit 1
fi
env:
NIX_CONFIG: "access-tokens = git.johnogle.info=${{ secrets.GITEA_ACCESS_TOKEN }}"

143
flake.lock generated
View File

@@ -2,16 +2,17 @@
"nodes": {
"beads": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1773070962,
"narHash": "sha256-kHZXx+kygpVholOBsuQocCtksHo5ZWYskP64qK2Kjh0=",
"lastModified": 1769020852,
"narHash": "sha256-MR6evuoa8w6mjYTesTAa3bsRH+c3IB7EOEDTCjsiAp8=",
"owner": "steveyegge",
"repo": "beads",
"rev": "9604d30b7c746f9f04f6dea5f82996f71bb66073",
"rev": "cb46db603d34c0190605eecb8724a6c581119f09",
"type": "github"
},
"original": {
@@ -23,11 +24,11 @@
"doomemacs": {
"flake": false,
"locked": {
"lastModified": 1772615218,
"narHash": "sha256-z+3c0AGkrMf1xZ+pq57aVp4Zo4KsqFMIjEVzSZinghc=",
"lastModified": 1767773143,
"narHash": "sha256-QL/t9v2kFNxBDyNJb/s411o3mxujan+QX5IZglTdpTk=",
"owner": "doomemacs",
"repo": "doomemacs",
"rev": "d23bbe87721c61f4d5a605f2914b32780bb89949",
"rev": "3e15fb36d7f94f0a218bda977be4d3f5da983a71",
"type": "github"
},
"original": {
@@ -46,11 +47,11 @@
]
},
"locked": {
"lastModified": 1772706147,
"narHash": "sha256-C0UMYQg3KBU6+L8TLfQ/s60O6/Tiu/JpN8C/WiIH9DU=",
"lastModified": 1768011937,
"narHash": "sha256-SnU2XTo34vwVaijs+4VwcXTNwMWO4nwzzs08N39UagA=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "54af2ae96631311dc4d2686a07e4f472fb36f516",
"rev": "79abf71d9897cf3b5189f7175cda1b1102abc65c",
"type": "github"
},
"original": {
@@ -78,27 +79,19 @@
}
},
"gastown": {
"inputs": {
"beads": [
"beads"
],
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs-unstable"
]
},
"flake": false,
"locked": {
"lastModified": 1773088127,
"narHash": "sha256-gJFayiBYrF0Q99AOQH29uq0Mli8KRfwReYeAh5H5evY=",
"owner": "steveyegge",
"repo": "gastown",
"rev": "8da798be0663af74be7960844b90038e51769203",
"type": "github"
"lastModified": 1769031452,
"narHash": "sha256-tTvtLvTr38okqbpNnr5exfurI6VkVKNLcnM+A6O7DGY=",
"ref": "refs/heads/main",
"rev": "93e22595cd59802a24253b100dcfae98a6849428",
"revCount": 2938,
"type": "git",
"url": "ssh://git@git.johnogle.info:2222/johno/gastown.git"
},
"original": {
"owner": "steveyegge",
"repo": "gastown",
"type": "github"
"type": "git",
"url": "ssh://git@git.johnogle.info:2222/johno/gastown.git"
}
},
"google-cookie-retrieval": {
@@ -108,11 +101,11 @@
]
},
"locked": {
"lastModified": 1768846578,
"narHash": "sha256-82f/+e8HAwmBukiLlr7I3HYvM/2GCd5SOc+BC+qzsOQ=",
"lastModified": 1761423376,
"narHash": "sha256-pMy3cnUFfue4vz/y0jx71BfcPGxZf+hk/DtnzWvfU0c=",
"ref": "refs/heads/main",
"rev": "c11ff9d3c67372a843a0fa6bf23132e986bd6955",
"revCount": 14,
"rev": "a1f695665771841a988afc965526cbf99160cd77",
"revCount": 11,
"type": "git",
"url": "https://git.johnogle.info/johno/google-cookie-retrieval.git"
},
@@ -128,11 +121,11 @@
]
},
"locked": {
"lastModified": 1772633058,
"narHash": "sha256-SO7JapRy2HPhgmqiLbfnW1kMx5rakPMKZ9z3wtRLQjI=",
"lastModified": 1767514898,
"narHash": "sha256-ONYqnKrPzfKEEPChoJ9qPcfvBqW9ZgieDKD7UezWPg4=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "080657a04188aca25f8a6c70a0fb2ea7e37f1865",
"rev": "7a06e8a2f844e128d3b210a000a62716b6040b7f",
"type": "github"
},
"original": {
@@ -149,11 +142,11 @@
]
},
"locked": {
"lastModified": 1772633327,
"narHash": "sha256-jl+DJB2DUx7EbWLRng+6HNWW/1/VQOnf0NsQB4PlA7I=",
"lastModified": 1767556355,
"narHash": "sha256-RDTUBDQBi9D4eD9iJQWtUDN/13MDLX+KmE+TwwNUp2s=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "5a75730e6f21ee624cbf86f4915c6e7489c74acc",
"rev": "f894bc4ffde179d178d8deb374fcf9855d1a82b7",
"type": "github"
},
"original": {
@@ -171,11 +164,11 @@
]
},
"locked": {
"lastModified": 1772517207,
"narHash": "sha256-qxHfxqbigqBTn//U4leIS5he22Wp1GS0+zmwGV7Pozs=",
"lastModified": 1767082077,
"narHash": "sha256-2tL1mRb9uFJThUNfuDm/ehrnPvImL/QDtCxfn71IEz4=",
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"rev": "7ca1501c2d80900b5967baea4d42581f84b388dd",
"rev": "efd4b22e6fdc6d7fb4e186ae333a4b74e03da440",
"type": "github"
},
"original": {
@@ -191,11 +184,11 @@
]
},
"locked": {
"lastModified": 1772129556,
"narHash": "sha256-Utk0zd8STPsUJPyjabhzPc5BpPodLTXrwkpXBHYnpeg=",
"lastModified": 1765066094,
"narHash": "sha256-0YSU35gfRFJzx/lTGgOt6ubP8K6LeW0vaywzNNqxkl4=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "ebec37af18215214173c98cf6356d0aca24a2585",
"rev": "688427b1aab9afb478ca07989dc754fa543e03d5",
"type": "github"
},
"original": {
@@ -213,11 +206,11 @@
"systems": "systems_2"
},
"locked": {
"lastModified": 1772716420,
"narHash": "sha256-T3UEKNTGqBl44AQ0+0OIpiWMXeQ8+4QW/akSc4yeL2A=",
"lastModified": 1768034604,
"narHash": "sha256-62pIZMvGHhYJmMiiBsxHqZt/dFyENPcFHlJq5NJF3Sw=",
"owner": "marienz",
"repo": "nix-doom-emacs-unstraightened",
"rev": "be3ecb81a85be302e27d46ff32a1e251e444327d",
"rev": "9b3b8044fe4ccdcbb2d6f733d7dbe4d5feea18bc",
"type": "github"
},
"original": {
@@ -250,27 +243,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1772598333,
"narHash": "sha256-YaHht/C35INEX3DeJQNWjNaTcPjYmBwwjFJ2jdtr+5U=",
"lastModified": 1767480499,
"narHash": "sha256-8IQQUorUGiSmFaPnLSo2+T+rjHtiNWc+OAzeHck7N48=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "fabb8c9deee281e50b1065002c9828f2cf7b2239",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-qt": {
"locked": {
"lastModified": 1772598333,
"narHash": "sha256-YaHht/C35INEX3DeJQNWjNaTcPjYmBwwjFJ2jdtr+5U=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "fabb8c9deee281e50b1065002c9828f2cf7b2239",
"rev": "30a3c519afcf3f99e2c6df3b359aec5692054d92",
"type": "github"
},
"original": {
@@ -282,11 +259,11 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1772624091,
"narHash": "sha256-QKyJ0QGWBn6r0invrMAK8dmJoBYWoOWy7lN+UHzW1jc=",
"lastModified": 1767379071,
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "80bdc1e5ce51f56b19791b52b2901187931f5353",
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
"type": "github"
},
"original": {
@@ -296,22 +273,6 @@
"type": "github"
}
},
"perles": {
"flake": false,
"locked": {
"lastModified": 1772661365,
"narHash": "sha256-kMlvIpfGMBkN5D1W0O1fcqUiH3dyobcH0GRRLCX7GGo=",
"owner": "zjrosen",
"repo": "perles",
"rev": "326ff1938dfe073daab7939762ce2f44c2ee74a1",
"type": "github"
},
"original": {
"owner": "zjrosen",
"repo": "perles",
"type": "github"
}
},
"plasma-manager": {
"inputs": {
"home-manager": [
@@ -322,11 +283,11 @@
]
},
"locked": {
"lastModified": 1772361940,
"narHash": "sha256-B1Cz+ydL1iaOnGlwOFld/C8lBECPtzhiy/pP93/CuyY=",
"lastModified": 1763909441,
"narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=",
"owner": "nix-community",
"repo": "plasma-manager",
"rev": "a4b33606111c9c5dcd10009042bb710307174f51",
"rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4",
"type": "github"
},
"original": {
@@ -345,11 +306,11 @@
]
},
"locked": {
"lastModified": 1772361940,
"narHash": "sha256-B1Cz+ydL1iaOnGlwOFld/C8lBECPtzhiy/pP93/CuyY=",
"lastModified": 1763909441,
"narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=",
"owner": "nix-community",
"repo": "plasma-manager",
"rev": "a4b33606111c9c5dcd10009042bb710307174f51",
"rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4",
"type": "github"
},
"original": {
@@ -369,9 +330,7 @@
"nix-darwin": "nix-darwin",
"nix-doom-emacs-unstraightened": "nix-doom-emacs-unstraightened",
"nixpkgs": "nixpkgs",
"nixpkgs-qt": "nixpkgs-qt",
"nixpkgs-unstable": "nixpkgs-unstable",
"perles": "perles",
"plasma-manager": "plasma-manager",
"plasma-manager-unstable": "plasma-manager-unstable"
}

View File

@@ -4,9 +4,6 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
# Separate nixpkgs for qt5webengine-dependent packages (jellyfin-media-player, etc.)
# Updates on separate Renovate schedule to avoid massive qt rebuilds
nixpkgs-qt.url = "github:nixos/nixpkgs/nixos-25.11";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-25.11";
@@ -51,13 +48,7 @@
};
gastown = {
url = "github:steveyegge/gastown";
inputs.nixpkgs.follows = "nixpkgs-unstable";
inputs.beads.follows = "beads";
};
perles = {
url = "github:zjrosen/perles";
url = "git+ssh://git@git.johnogle.info:2222/johno/gastown.git";
flake = false; # No flake.nix upstream yet
};
@@ -78,14 +69,6 @@
config.allowUnfree = true;
overlays = unstableOverlays;
};
# Separate nixpkgs for qt5webengine-heavy packages to avoid rebuild churn
qt-pinned = import inputs.nixpkgs-qt {
system = prev.stdenv.hostPlatform.system;
config = {
allowUnfree = true;
permittedInsecurePackages = [ "qtwebengine-5.15.19" ];
};
};
custom = prev.callPackage ./packages {};
# Compatibility: bitwarden renamed to bitwarden-desktop in unstable
bitwarden-desktop = prev.bitwarden-desktop or prev.bitwarden;
@@ -113,19 +96,6 @@
(ufinal: uprev: {
claude-code = uprev.callPackage ./packages/claude-code {};
})
# Pin dolt to v1.82.4 (gastown requires >= 1.82.4)
(ufinal: uprev: {
dolt = uprev.dolt.overrideAttrs (old: rec {
version = "1.82.4";
src = uprev.fetchFromGitHub {
owner = "dolthub";
repo = "dolt";
tag = "v${version}";
hash = "sha256-mavL3y+Kv25hzFlDFXk7W/jeKVKlCBjlc67GkL3Jcwk=";
};
vendorHash = "sha256-K1KzsqptZxO5OraWKIXeqKuVSzb6E/Mjy3c5PQ7Rs9k=";
});
})
];
nixosModules = [
@@ -196,16 +166,7 @@
modules = nixosModules ++ [
./machines/zix790prors/configuration.nix
{
home-manager.users.johno = {
imports = [ ./home/home-desktop.nix ];
home.roles.i3_sway.extraSwayConfig = {
output = {
"DP-1" = {
mode = "3440x1440@164.900Hz";
};
};
};
};
home-manager.users.johno = import ./home/home-desktop.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
@@ -249,7 +210,7 @@
};
# Darwin/macOS configurations
darwinConfigurations."BLKFV4YF49KT7" = inputs.nix-darwin.lib.darwinSystem rec {
darwinConfigurations."blkfv4yf49kt7" = inputs.nix-darwin.lib.darwinSystem rec {
system = "aarch64-darwin";
modules = darwinModules ++ [
./machines/johno-macbookpro/configuration.nix
@@ -260,53 +221,6 @@
];
};
# Packages for CI caching (custom packages, flake inputs, and qt-pinned)
packages = nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" ] (system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
overlays = [ (mkBaseOverlay {}) ];
};
pkgsQt = import inputs.nixpkgs-qt {
inherit system;
config = {
allowUnfree = true;
permittedInsecurePackages = [ "qtwebengine-5.15.19" ];
};
};
# Version strings for flake input packages
beadsRev = builtins.substring 0 8 (inputs.beads.rev or "unknown");
gastownRev = builtins.substring 0 8 (inputs.gastown.rev or "unknown");
in {
"custom-claude-code" = pkgs.custom.claude-code;
"custom-app-launcher-server" = pkgs.custom.app-launcher-server;
"custom-mcrcon-rbw" = pkgs.custom.mcrcon-rbw;
"custom-tea-rbw" = pkgs.custom.tea-rbw;
"custom-rclone-torbox-setup" = pkgs.custom.rclone-torbox-setup;
"qt-pinned-jellyfin-media-player" = pkgsQt.jellyfin-media-player;
"qt-pinned-stremio" = pkgsQt.stremio;
# Flake input packages (beads, gastown) - these get version from input rev
"custom-beads" = pkgs.callPackage ./packages/beads {
inherit (pkgs.unstable) buildGoModule;
src = inputs.beads;
version = "0.52.0-${beadsRev}";
};
"custom-gastown" = pkgs.callPackage ./packages/gastown {
src = inputs.gastown;
version = "unstable-${gastownRev}";
};
"custom-perles" = pkgs.callPackage ./packages/perles {
inherit (pkgs.unstable) buildGoModule;
src = inputs.perles;
version = "unstable-${builtins.substring 0 8 (inputs.perles.rev or "unknown")}";
};
} // (if system == "x86_64-linux" then {
# nix-deck kernel from Jovian-NixOS (Steam Deck) - expensive to build
"nix-deck-kernel" = self.nixosConfigurations.nix-deck.config.boot.kernelPackages.kernel;
} else {})
);
# Flake apps
apps = nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ] (system:
let

View File

@@ -107,7 +107,7 @@
aerospace = {
enable = true;
leader = "cmd";
ctrlShortcuts.enable = false;
ctrlShortcuts.enable = true;
sketchybar.enable = true;
# Optional: Add per-machine userSettings overrides
# userSettings = {

View File

@@ -12,7 +12,6 @@
home.roles = {
base.enable = true;
plasma-manager-kodi.enable = true;
kdeconnect.enable = true;
};
home.packages = with pkgs; [

View File

@@ -632,9 +632,7 @@ in
text = ''
#!/bin/bash
# Monitor /System/Volumes/Data which contains user data on APFS
# The root / is a read-only snapshot with minimal usage
DISK_USAGE=$(df -H /System/Volumes/Data | grep -v Filesystem | awk '{print $5}')
DISK_USAGE=$(df -H / | grep -v Filesystem | awk '{print $5}')
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$DISK_USAGE"
'';

View File

@@ -22,7 +22,6 @@ in
shellcheck
tmux
tree
watch
];
# Automatic garbage collection for user profile (home-manager generations).

View File

@@ -5,25 +5,27 @@ with lib;
let
cfg = config.home.roles.development;
# Build beads and gastown from flake inputs using shared package definitions
beadsRev = builtins.substring 0 8 (globalInputs.beads.rev or "unknown");
beadsPackage = pkgs.callPackage ../../../packages/beads {
src = globalInputs.beads;
version = "0.52.0-${beadsRev}";
};
# FIXME: Temporary override for upstream beads vendorHash mismatch
# Remove after upstream fix: https://github.com/steveyegge/beads/issues/XXX
beadsPackage = globalInputs.beads.packages.${system}.default.overrideAttrs (old: {
vendorHash = "sha256-YU+bRLVlWtHzJ1QPzcKJ70f+ynp8lMoIeFlm+29BNPE=";
});
gastownRev = builtins.substring 0 8 (globalInputs.gastown.rev or "unknown");
gastownPackage = pkgs.callPackage ../../../packages/gastown {
# Gastown - multi-agent workspace manager (no upstream flake.nix yet)
# Source is tracked via flake input for renovate updates
gastownPackage = pkgs.buildGoModule {
pname = "gastown";
version = "unstable-${builtins.substring 0 8 globalInputs.gastown.rev or "unknown"}";
src = globalInputs.gastown;
version = "unstable-${gastownRev}";
};
# Build perles from flake input using shared package definition
perlesRev = builtins.substring 0 8 (globalInputs.perles.rev or "unknown");
perlesPackage = pkgs.callPackage ../../../packages/perles {
inherit (pkgs.unstable) buildGoModule;
src = globalInputs.perles;
version = "unstable-${perlesRev}";
vendorHash = "sha256-ripY9vrYgVW8bngAyMLh0LkU/Xx1UUaLgmAA7/EmWQU=";
subPackages = [ "cmd/gt" ];
doCheck = false;
meta = with lib; {
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
homepage = "https://github.com/steveyegge/gastown";
license = licenses.mit;
mainProgram = "gt";
};
};
# Fetch the claude-plugins repository (for humanlayer commands/agents)
@@ -36,12 +38,6 @@ let
ref = "main";
};
# Claude Code statusline: shows model, cwd, git branch, and context usage %
claudeCodeStatusLineConfig = pkgs.writeText "claude-statusline.json" (builtins.toJSON {
type = "command";
command = ''input=$(cat); model=$(echo "$input" | jq -r '.model.display_name'); cwd=$(echo "$input" | jq -r '.workspace.current_dir'); if git -C "$cwd" rev-parse --git-dir > /dev/null 2>&1; then branch=$(git -C "$cwd" --no-optional-locks rev-parse --abbrev-ref HEAD 2>/dev/null || echo ""); if [ -n "$branch" ]; then git_info=" on $branch"; else git_info=""; fi; else git_info=""; fi; usage=$(echo "$input" | jq '.context_window.current_usage'); if [ "$usage" != "null" ]; then current=$(echo "$usage" | jq '.input_tokens + .cache_creation_input_tokens + .cache_read_input_tokens'); size=$(echo "$input" | jq '.context_window.context_window_size'); pct=$((current * 100 / size)); context_info=" | ''${pct}% context"; else context_info=""; fi; printf "%s in %s%s%s" "$model" "$cwd" "$git_info" "$context_info"'';
});
in
{
options.home.roles.development = {
@@ -66,16 +62,13 @@ in
home.packages = [
beadsPackage
gastownPackage
perlesPackage
pkgs.unstable.claude-code
pkgs.unstable.claude-code-router
pkgs.unstable.codex
pkgs.unstable.dolt
pkgs.sqlite
# Custom packages
pkgs.custom.tea-rbw
pkgs.custom.pi-coding-agent
];
# Install Claude Code humanlayer command and agent plugins
@@ -84,9 +77,6 @@ in
rm -f ~/.claude/commands/humanlayer:* 2>/dev/null || true
rm -f ~/.claude/agents/humanlayer:* 2>/dev/null || true
# Remove explicitly blocked commands that may have been installed previously
rm -f ~/.claude/commands/humanlayer:create_handoff.md 2>/dev/null || true
# Create directories if they don't exist
mkdir -p ~/.claude/commands
mkdir -p ~/.claude/agents
@@ -95,21 +85,13 @@ in
for file in ${claudePluginsRepo}/humanlayer/commands/*.md; do
if [ -f "$file" ]; then
filename=$(basename "$file" .md)
# Skip blocked commands
case "$filename" in
create_handoff) continue ;;
esac
dest="$HOME/.claude/commands/humanlayer:''${filename}.md"
rm -f "$dest" 2>/dev/null || true
# Copy file and conditionally remove the "model:" line from frontmatter
${if cfg.allowArbitraryClaudeCodeModelSelection
then "cp \"$file\" \"$dest\""
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
}
chmod u+w "$dest" 2>/dev/null || true
fi
done
@@ -118,14 +100,12 @@ in
if [ -f "$file" ]; then
filename=$(basename "$file" .md)
dest="$HOME/.claude/agents/humanlayer:''${filename}.md"
rm -f "$dest" 2>/dev/null || true
# Copy file and conditionally remove the "model:" line from frontmatter
${if cfg.allowArbitraryClaudeCodeModelSelection
then "cp \"$file\" \"$dest\""
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
}
chmod u+w "$dest" 2>/dev/null || true
fi
done
@@ -140,7 +120,6 @@ in
sleep 0.5
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to commands"
fi
chmod u+w "$dest" 2>/dev/null || true
fi
done
@@ -155,17 +134,13 @@ in
sleep 0.5
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to skills"
fi
chmod u+w "$dest" 2>/dev/null || true
fi
done
# Copy micro-skills (compact reusable knowledge referenced by formulas)
for file in ${./skills/micro}/*.md; do
if [ -f "$file" ]; then
dest="$HOME/.claude/commands/skills/$(basename "$file")"
rm -f "$dest" 2>/dev/null || true
cp "$file" "$dest"
chmod u+w "$dest" 2>/dev/null || true
cp "$file" "$HOME/.claude/commands/skills/$(basename "$file")"
fi
done
@@ -173,10 +148,7 @@ in
mkdir -p ~/.beads/formulas
for file in ${./formulas}/*.formula.toml; do
if [ -f "$file" ]; then
dest="$HOME/.beads/formulas/$(basename "$file")"
rm -f "$dest" 2>/dev/null || true
cp "$file" "$dest"
chmod u+w "$dest" 2>/dev/null || true
cp "$file" "$HOME/.beads/formulas/$(basename "$file")"
fi
done
@@ -193,18 +165,6 @@ in
$DRY_RUN_CMD echo "Claude Code beads integration configured (hooks installed)"
'';
# Configure Claude Code statusline (merge into existing settings.json)
home.activation.claudeCodeStatusLine = lib.hm.dag.entryAfter ["writeBoundary" "claudeCodeBeadsSetup"] ''
SETTINGS="$HOME/.claude/settings.json"
mkdir -p "$HOME/.claude"
if [ -f "$SETTINGS" ]; then
${pkgs.jq}/bin/jq --slurpfile sl ${claudeCodeStatusLineConfig} '.statusLine = $sl[0]' "$SETTINGS" > "''${SETTINGS}.tmp" && mv "''${SETTINGS}.tmp" "$SETTINGS"
else
${pkgs.jq}/bin/jq -n --slurpfile sl ${claudeCodeStatusLineConfig} '{statusLine: $sl[0]}' > "$SETTINGS"
fi
$DRY_RUN_CMD echo "Claude Code statusline configured"
'';
# Beads timer gate checker (Linux only - uses systemd)
# Runs every 5 minutes to auto-resolve expired timer gates across all beads projects
# This enables self-scheduling molecules (watchers, patrols, etc.)

View File

@@ -8,8 +8,8 @@ let
doomEmacs = pkgs.fetchFromGitHub {
owner = "doomemacs";
repo = "doomemacs";
rev = "d23bbe87721c61f4d5a605f2914b32780bb89949";
sha256 = "sha256-z+3c0AGkrMf1xZ+pq57aVp4Zo4KsqFMIjEVzSZinghc=";
rev = "38d94da67dc84897a4318714dcc48494c016d8c4";
sha256 = "sha256-Uc6qONH3jjUVDgW+pPBCGC7mh88ZY05u1y37fQrsxq0=";
};
# Shared emacs packages

View File

@@ -53,22 +53,6 @@
;; change `org-directory'. It must be set before org loads!
(setq org-directory "~/org/")
(after! org
;; Skip recurring events past their CALDAV_UNTIL date
;; org-caldav ignores UNTIL from RRULE, so we store it as a property
;; and filter here in the agenda
(defun my/skip-if-past-until ()
"Return non-nil if entry has CALDAV_UNTIL and current date is past it."
(let ((until-str (org-entry-get nil "CALDAV_UNTIL")))
(when (and until-str
(string-match "^\\([0-9]\\{4\\}\\)\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\)" until-str))
(let* ((until-year (string-to-number (match-string 1 until-str)))
(until-month (string-to-number (match-string 2 until-str)))
(until-day (string-to-number (match-string 3 until-str)))
(until-time (encode-time 0 0 0 until-day until-month until-year))
(today (current-time)))
(when (time-less-p until-time today)
(org-end-of-subtree t))))))
(setq org-agenda-span 'week
org-agenda-start-with-log-mode t
my-agenda-dirs '("projects" "roam")
@@ -77,7 +61,6 @@
"\.org$"))
my-agenda-dirs))
org-log-done 'time
org-agenda-skip-function-global #'my/skip-if-past-until
org-agenda-custom-commands '(("n" "Agenda"
((agenda "")
(tags-todo "-someday-recurring")))
@@ -100,135 +83,25 @@
"d" #'org-agenda-day-view
"w" #'org-agenda-week-view))
;; org-caldav: Sync Org entries with Nextcloud CalDAV
;; Setup requirements:
;; 1. Create Nextcloud app password: Settings -> Security -> Devices & sessions
;; 2. Store in rbw: rbw add nextcloud-caldav (put app password as the secret)
;; 3. Run: doom sync
;; 4. Test: M-x my/org-caldav-sync-with-rbw (or SPC o a s)
;;
;; Note: Conflict resolution is "Org always wins" - treat Org as source of truth
;; for entries that originated in Org.
;; (use-package! org-caldav
;; :defer t
;; :config
;; (setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno"
;; org-caldav-calendar-id "personal"
;; org-icalendar-timezone "America/Los_Angeles"
;; org-caldav-inbox "~/org/calendar.org"
;; org-caldav-files nil
;; org-caldav-sync-direction 'cal->org))
;; Define sync wrapper before use-package (so keybinding works)
(defun my/org-caldav-sync-with-rbw ()
"Run org-caldav-sync with credentials from rbw embedded in URL."
(interactive)
(require 'org)
(require 'org-caldav)
(let* ((password (my/get-rbw-password "nextcloud-caldav"))
;; Embed credentials in URL (url-encode password in case of special chars)
(encoded-pass (url-hexify-string password)))
(setq org-caldav-url
(format "https://johno:%s@nextcloud.johnogle.info/remote.php/dav/calendars/johno"
encoded-pass))
(org-caldav-sync)))
(use-package! org-caldav
:after org
:commands (org-caldav-sync my/org-caldav-sync-with-rbw)
:init
(map! :leader
(:prefix ("o" . "open")
(:prefix ("a" . "agenda/calendar")
:desc "Sync CalDAV" "s" #'my/org-caldav-sync-with-rbw)))
:config
;; Nextcloud CalDAV base URL (credentials added dynamically by sync wrapper)
(setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno")
;; Timezone for iCalendar export
(setq org-icalendar-timezone "America/Los_Angeles")
;; Sync state storage (in org directory for multi-machine sync)
(setq org-caldav-save-directory (expand-file-name ".org-caldav/" org-directory))
;; Backup file for entries before modification
(setq org-caldav-backup-file (expand-file-name ".org-caldav/backup.org" org-directory))
;; Limit past events to 30 days (avoids uploading years of scheduled tasks)
(setq org-caldav-days-in-past 30)
;; Sync behavior: bidirectional by default
(setq org-caldav-sync-direction 'twoway)
;; What changes from calendar sync back to Org (conservative: title and timestamp only)
(setq org-caldav-sync-changes-to-org 'title-and-timestamp)
;; Deletion handling: never auto-delete to prevent accidental mass deletion
(setq org-caldav-delete-calendar-entries 'never)
(setq org-caldav-delete-org-entries 'never)
;; Enable TODO/VTODO sync
(setq org-icalendar-include-todo 'all)
(setq org-caldav-sync-todo t)
;; Map VTODO percent-complete to org-todo-keywords
;; Format: (PERCENT "KEYWORD") - percent thresholds map to states
(setq org-caldav-todo-percent-states
'((0 "TODO")
(25 "WAIT")
(50 "IN-PROGRESS")
(100 "DONE")
(100 "KILL")))
;; Allow export with broken links (mu4e links can't be resolved during export)
(setq org-export-with-broken-links 'mark)
;; Calendar-specific configuration
(setq org-caldav-calendars
'(;; Personal calendar: two-way sync with family-shared Nextcloud calendar
(:calendar-id "personal"
:inbox "~/org/personal-calendar.org"
:files ("~/org/personal-calendar.org"))
;; Tasks calendar: one-way sync (org → calendar only)
;; SCHEDULED/DEADLINE items from todo.org push to private Tasks calendar.
;; No inbox = no download from calendar (effectively one-way).
;; Note: Create 'tasks' calendar in Nextcloud first, keep it private.
(:calendar-id "tasks"
:files ("~/org/todo.org"))))
;; Handle UNTIL in recurring events
;; org-caldav ignores UNTIL from RRULE - events repeat forever.
;; This advice extracts UNTIL and stores it as a property for agenda filtering.
(defun my/org-caldav-add-until-property (orig-fun eventdata-alist)
"Advice to store CALDAV_UNTIL property for recurring events."
(let ((result (funcall orig-fun eventdata-alist)))
(let* ((rrule-props (alist-get 'rrule-props eventdata-alist))
(until-str (cadr (assoc 'UNTIL rrule-props)))
(summary (alist-get 'summary eventdata-alist)))
;; Debug: log what we're seeing
(message "CALDAV-DEBUG: %s | rrule-props: %S | until: %s"
(or summary "?") rrule-props until-str)
(when until-str
(save-excursion
(org-back-to-heading t)
(org-entry-put nil "CALDAV_UNTIL" until-str))))
result))
(advice-add 'org-caldav-insert-org-event-or-todo
:around #'my/org-caldav-add-until-property)
)
(defun my/get-rbw-password (alias &optional no-error)
"Return the password for ALIAS via rbw, unlocking the vault only if needed.
If NO-ERROR is non-nil, return nil instead of signaling an error when
rbw is unavailable or the entry is not found."
(if (not (executable-find "rbw"))
(if no-error
nil
(user-error "rbw: not installed or not in PATH"))
(let* ((cmd (format "rbw get %s 2>/dev/null" (shell-quote-argument alias)))
(output (string-trim (shell-command-to-string cmd))))
(if (string-empty-p output)
(if no-error
nil
(user-error "rbw: no entry found for '%s' - run: rbw add %s" alias alias))
output))))
(defun my/get-rbw-password (alias)
"Return the password for ALIAS via rbw, unlocking the vault only if needed."
(let* ((cmd (format "rbw get %s 2>&1" alias))
(output (shell-command-to-string cmd)))
(string-trim output)))
(after! gptel
:config
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el" t)
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el")
gptel-default-mode 'org-mode
gptel-use-tools t
gptel-confirm-tool-calls 'always
@@ -274,18 +147,6 @@ rbw is unavailable or the entry is not found."
(error (format "Error listing directory %s: %s" dirpath (error-message-string err)))))
:args (list '(:name "dirpath" :type "string" :description "Directory path to list"))))
(use-package! pi-coding-agent
:commands (pi-coding-agent pi-coding-agent-toggle)
:init
(defalias 'pi 'pi-coding-agent)
(map! :leader
(:prefix ("o" . "open")
:desc "Pi Coding Agent" "p" #'pi-coding-agent))
:config
;; Tree-sitter grammars are managed by Nix (treesit-grammars.with-all-grammars),
;; so suppress the auto-install prompt
(setq pi-coding-agent-essential-grammar-action 'warn))
(use-package! claude-code-ide
:commands (claude-code-ide-menu claude-code-ide-open-here)
:init

View File

@@ -49,10 +49,7 @@
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
;; (unpin! t)
(package! org-caldav)
;; Pin org-msg - upstream doom pin references a force-pushed commit
(package! org-msg :pin "aa608b399586fb771ad37045a837f8286a0b6124")
;; (package! org-caldav)
;; Note: Packages with custom recipes must be pinned for nix-doom-emacs-unstraightened
;; to build deterministically. Update pins when upgrading packages.
@@ -72,8 +69,3 @@
:recipe (:type git :repo "https://codeberg.org/ctietze/beads.el.git"
:files ("lisp/*.el"))
:pin "f40a6461d3c0fa0969311bbb6a1e30d1bba86c88")
(package! pi-coding-agent
:recipe (:host github :repo "dnouri/pi-coding-agent"
:files ("*.el"))
:pin "8d8158b0a6150ce13d91e561a1223790670acaa7")

View File

@@ -2,7 +2,7 @@
# The currentIndex is incremented by `nix run .#rotate-wallpaper`
# and gets committed as part of `nix run .#upgrade`
{
currentIndex = 2; # Index into wallpapers list
currentIndex = 1; # Index into wallpapers list
wallpapers = [
{

View File

@@ -54,7 +54,6 @@ with lib;
4000 # nfs callback
4001 # nlockmgr
4002 # mountd
5000 # harmonia binary cache
20048 # mountd
];
allowedUDPPorts = [
@@ -91,8 +90,6 @@ with lib;
htop
tmux
zfs
rclone
custom.rclone-torbox-setup # Helper script to set up TorBox credentials via rbw
];
# Enable SSH
@@ -129,36 +126,6 @@ with lib;
roles.virtualisation.enable = true;
# TorBox WebDAV mount for rdt-client and Jellyfin
roles.rclone-mount = {
enable = true;
mounts.torbox = {
webdavUrl = "https://webdav.torbox.app";
username = "john@ogle.fyi"; # TorBox account email
mountPoint = "/media/media/torbox-rclone";
environmentFile = "/etc/rclone/torbox.env";
vfsCacheMode = "full"; # Best for streaming media
dirCacheTime = "5m";
extraArgs = [
"--buffer-size=64M"
"--vfs-read-chunk-size=32M"
"--vfs-read-chunk-size-limit=off"
];
# Wait for ZFS media pool to be mounted before starting
requiresMountsFor = [ "/media" ];
};
};
# Harmonia binary cache server
# Replaces the broken k8s deployment with native NixOS service
services.harmonia = {
enable = true;
signKeyPaths = [ "/etc/harmonia/signing-key.private" ];
settings = {
bind = "[::]:5000";
};
};
# Time zone
time.timeZone = "America/Los_Angeles"; # Adjust as needed

View File

@@ -23,12 +23,12 @@
printing.enable = true;
remote-build.builders = [
{
hostName = "zix790prors.oglehome";
hostName = "zix790prors";
maxJobs = 16;
speedFactor = 3;
}
{
hostName = "john-endesktop.oglehome";
hostName = "john-endesktop";
maxJobs = 1;
speedFactor = 1;
}
@@ -38,24 +38,7 @@
enable = true;
extraGroups = [ "video" ];
};
virtualisation = {
enable = true;
waydroid = true;
};
wireguard = {
enable = true;
autostart = true;
interfaceName = "ogleNet";
address = [ "192.168.4.2/32" ];
privateKeyFile = "/etc/wireguard/oglehome-private-key";
dns = [ "192.168.4.1" ];
peers = [{
publicKey = "AWkmtaz0poyyKJGnRcabO5ecd6ESh1lKu+XRb3ObxBc=";
endpoint = "pi.johnogle.info:6666";
allowedIPs = [ "0.0.0.0/0" ];
persistentKeepalive = 25;
}];
};
virtualisation.enable = true;
};
# Bootloader.
@@ -75,6 +58,13 @@
# Enable networking
networking.networkmanager.enable = true;
# WireGuard setup
networking.wg-quick.interfaces = {
ogleNet = {
configFile = "/root/Oglehome-VPN-johno-nixbook.conf";
};
};
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [

View File

@@ -19,18 +19,11 @@
desktopSession = "plasma";
};
};
remote-build.builders = [
{
hostName = "zix790prors.oglehome";
maxJobs = 16;
speedFactor = 4;
}
{
hostName = "john-endesktop.oglehome";
maxJobs = 1;
speedFactor = 2;
}
];
remote-build.builders = [{
hostName = "zix790prors";
maxJobs = 16;
speedFactor = 4; # Prefer remote heavily on Steam Deck
}];
users = {
enable = true;
extraGroups = [ "video" ];

View File

@@ -1,26 +0,0 @@
# Beads package - issue tracker for AI-supervised coding workflows
# Takes src as argument so it can be called from both overlay and flake packages
{ lib
, buildGoModule
, git
, pkg-config
, icu
, src
, version ? "unknown"
}:
buildGoModule {
pname = "beads";
inherit version src;
subPackages = [ "cmd/bd" ];
doCheck = false;
vendorHash = "sha256-XAhe4yuLzP9vQ3IFhWAO5fN/3OOfokcRxfeGKaRYEws=";
nativeBuildInputs = [ git pkg-config ];
buildInputs = [ icu ];
meta = with lib; {
description = "beads (bd) - An issue tracker designed for AI-supervised coding workflows";
homepage = "https://github.com/steveyegge/beads";
license = licenses.mit;
mainProgram = "bd";
};
}

View File

@@ -6,24 +6,24 @@
}:
let
version = "2.1.75";
version = "2.1.12";
srcs = {
aarch64-darwin = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-arm64/claude";
sha256 = "8c541a5e924eda2070eaf1702a48047af671c4dff6a11a5e762076614a082675";
sha256 = "40be59519a84bd35eb1111aa46f72aa6b3443866d3f6336252a198fdcaefbbe5";
};
x86_64-darwin = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-x64/claude";
sha256 = "82c90b91a0a18f60191f817b9b42304d8b17dbed75795b715c41f4fdfe4c782d";
sha256 = "0eee4b46c91749480bf856f88e49b15a3e944faa9d346679c5f0c0d7fa6f2f54";
};
x86_64-linux = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-x64/claude";
sha256 = "328b0a429c05a04f911157d886be5123cf1824a19ba8ca1f9d594c004eac32c9";
sha256 = "3fe979215489dc1b31463fadf95ed2d2d5473a9969447bb7a46431f4578847d4";
};
aarch64-linux = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-arm64/claude";
sha256 = "ec8f4f7f7bb50611dae70c109a76ee1da6a3ab45511c65f117df215848ecc905";
sha256 = "e214b1d3b5afd4cd2de9177359001d41a3eb98cb1e3665fe97edc592f5aa132f";
};
};

View File

@@ -4,6 +4,4 @@
app-launcher-server = pkgs.callPackage ./app-launcher-server {};
claude-code = pkgs.callPackage ./claude-code {};
mcrcon-rbw = pkgs.callPackage ./mcrcon-rbw {};
rclone-torbox-setup = pkgs.callPackage ./rclone-torbox-setup {};
pi-coding-agent = pkgs.callPackage ./pi-coding-agent {};
}

View File

@@ -1,31 +0,0 @@
# Gastown package - multi-agent workspace manager
# Takes src as argument so it can be called from both overlay and flake packages
{ lib
, buildGoModule
, src
, version ? "unknown"
}:
buildGoModule {
pname = "gastown";
inherit version src;
vendorHash = "sha256-8SdvSASP+bJjMooqEQvkCzG+J6CbsK+HCQulrPnJZ1Y=";
subPackages = [ "cmd/gt" ];
doCheck = false;
# Must match ldflags from gastown Makefile - BuiltProperly=1 is required
# or gt will error with "This binary was built with 'go build' directly"
ldflags = [
"-X github.com/steveyegge/gastown/internal/cmd.Version=${version}"
"-X github.com/steveyegge/gastown/internal/cmd.Commit=${version}"
"-X github.com/steveyegge/gastown/internal/cmd.BuildTime=nix-build"
"-X github.com/steveyegge/gastown/internal/cmd.BuiltProperly=1"
];
meta = with lib; {
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
homepage = "https://github.com/steveyegge/gastown";
license = licenses.mit;
mainProgram = "gt";
};
}

View File

@@ -1,25 +0,0 @@
# Perles - Terminal UI for beads issue tracking
# Takes src as argument so it can be called from both overlay and flake packages
{ lib
, buildGoModule
, src
, version ? "unknown"
}:
buildGoModule {
pname = "perles";
inherit version src;
doCheck = false;
vendorHash = "sha256-A5LE9Cor/DRcJtVpiScSoqDYhJIKyaq0cbK+OGmr4XU=";
ldflags = [
"-X main.version=${version}"
];
meta = with lib; {
description = "Perles - Terminal UI for beads issue tracking";
homepage = "https://github.com/zjrosen/perles";
license = licenses.mit;
mainProgram = "perles";
};
}

View File

@@ -1,79 +0,0 @@
{ lib
, stdenv
, fetchurl
, patchelf
, glibc
, makeWrapper
}:
let
version = "0.55.4";
srcs = {
aarch64-darwin = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-darwin-arm64.tar.gz";
sha256 = "0vsav9frvnzskk6p6j60i7klrs3m8lphhyi4c39mv2mvhpm8fkl5";
};
x86_64-darwin = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-darwin-x64.tar.gz";
sha256 = "1377rvhsiiww1bbpgv2v46fjm7iz2smmh8g2yhm28kbsq3gwvvr0";
};
x86_64-linux = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-linux-x64.tar.gz";
sha256 = "1wnfwnkfq5ffz6wyqyhciv4lz06bpxims0hv0dlhz0f9vliyc1md";
};
aarch64-linux = {
url = "https://github.com/badlogic/pi-mono/releases/download/v${version}/pi-linux-arm64.tar.gz";
sha256 = "00fp37hgjl40kc59jfpv189i7np53ymm037hvds6k9y2sz818wjy";
};
};
src = srcs.${stdenv.hostPlatform.system} or (throw "Unsupported system: ${stdenv.hostPlatform.system}");
in stdenv.mkDerivation {
pname = "pi-coding-agent";
inherit version;
src = fetchurl {
inherit (src) url sha256;
};
sourceRoot = "pi";
# Bun standalone binaries have JS code appended after the ELF sections
dontStrip = true;
dontPatchELF = true;
nativeBuildInputs = [ makeWrapper ]
++ lib.optionals stdenv.isLinux [ patchelf ];
installPhase = ''
runHook preInstall
# Install the full pi directory structure (binary + supporting files)
mkdir -p $out/lib/pi-coding-agent
cp -r . $out/lib/pi-coding-agent/
# Create bin wrapper that runs the binary from its lib directory
# (pi expects supporting files like themes and wasm relative to itself)
mkdir -p $out/bin
makeWrapper $out/lib/pi-coding-agent/pi $out/bin/pi
runHook postInstall
'';
# Manually patch the interpreter for bun standalone binaries on Linux
postFixup = lib.optionalString stdenv.isLinux ''
interpreter="${glibc}/lib/${if stdenv.hostPlatform.system == "aarch64-linux" then "ld-linux-aarch64.so.1" else "ld-linux-x86-64.so.2"}"
patchelf --set-interpreter "$interpreter" $out/lib/pi-coding-agent/pi
'';
meta = with lib; {
description = "Minimal terminal coding agent with extensible tools and session management";
homepage = "https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent";
license = licenses.mit;
maintainers = [ ];
platforms = [ "aarch64-darwin" "x86_64-darwin" "x86_64-linux" "aarch64-linux" ];
mainProgram = "pi";
};
}

View File

@@ -1,98 +0,0 @@
{ pkgs, ... }:
pkgs.writeShellScriptBin "rclone-torbox-setup" ''
set -euo pipefail
# Default values
RBW_ENTRY="''${1:-torbox}"
ENV_FILE="''${2:-/etc/rclone/torbox.env}"
usage() {
echo "Usage: rclone-torbox-setup [rbw-entry] [env-file]"
echo ""
echo "Sets up rclone credentials for TorBox WebDAV mount."
echo "Retrieves password from rbw (Bitwarden), obscures it for rclone,"
echo "and writes it to the environment file for the systemd service."
echo ""
echo "Arguments:"
echo " rbw-entry Name of the Bitwarden entry containing the password (default: torbox)"
echo " env-file Path to write the environment file (default: /etc/rclone/torbox.env)"
echo ""
echo "The Bitwarden entry should contain your TorBox password as the password field."
echo ""
echo "Example:"
echo " rclone-torbox-setup torbox-password /etc/rclone/torbox.env"
exit 1
}
if [[ "''${1:-}" == "-h" ]] || [[ "''${1:-}" == "--help" ]]; then
usage
fi
echo "rclone TorBox credential setup"
echo "=============================="
echo ""
# Check if rbw is available
if ! command -v rbw &> /dev/null; then
echo "Error: rbw is not available. Please ensure rbw is installed and configured."
exit 1
fi
# Check if rclone is available
if ! command -v rclone &> /dev/null; then
echo "Error: rclone is not available. Please ensure rclone is installed."
exit 1
fi
echo "Retrieving password from rbw entry: $RBW_ENTRY"
# Retrieve password from Bitwarden
if ! TORBOX_PASS=$(rbw get "$RBW_ENTRY" 2>/dev/null); then
echo ""
echo "Error: Failed to retrieve password from rbw entry '$RBW_ENTRY'"
echo ""
echo "Please ensure:"
echo " 1. The entry '$RBW_ENTRY' exists in Bitwarden"
echo " 2. rbw is unlocked: rbw unlock"
echo " 3. rbw is synced: rbw sync"
echo ""
echo "To create the entry in Bitwarden:"
echo " - Name: $RBW_ENTRY"
echo " - Password: Your TorBox password"
exit 1
fi
echo "Password retrieved successfully"
# Obscure the password for rclone
echo "Obscuring password for rclone..."
if ! OBSCURED_PASS=$(echo -n "$TORBOX_PASS" | rclone obscure -); then
echo "Error: Failed to obscure password with rclone"
exit 1
fi
# Create the directory if needed (requires sudo)
ENV_DIR=$(dirname "$ENV_FILE")
if [[ ! -d "$ENV_DIR" ]]; then
echo "Creating directory $ENV_DIR (requires sudo)..."
sudo mkdir -p "$ENV_DIR"
fi
# Write the environment file
echo "Writing environment file to $ENV_FILE (requires sudo)..."
echo "RCLONE_WEBDAV_PASS=$OBSCURED_PASS" | sudo tee "$ENV_FILE" > /dev/null
sudo chmod 600 "$ENV_FILE"
echo ""
echo "Setup complete!"
echo ""
echo "The environment file has been created at: $ENV_FILE"
echo "The rclone-mount-torbox systemd service will use this file."
echo ""
echo "To activate the mount after NixOS rebuild:"
echo " sudo systemctl start rclone-mount-torbox"
echo ""
echo "To check status:"
echo " sudo systemctl status rclone-mount-torbox"
''

View File

@@ -13,7 +13,7 @@
"lockFileMaintenance": {
"enabled": true,
"schedule": [
"after 2pm and before 4pm on Saturday"
"before 5am on monday"
]
},
"dependencyDashboard": true,
@@ -37,9 +37,6 @@
"/^nixpkgs$/",
"/^home-manager$/",
"/^nix-darwin$/"
],
"schedule": [
"after 2pm and before 4pm on Saturday"
]
},
{
@@ -51,21 +48,6 @@
"matchPackageNames": [
"/nixpkgs-unstable/",
"/home-manager-unstable/"
],
"schedule": [
"after 2pm and before 4pm on Saturday"
]
},
{
"description": "nixpkgs-qt updates on Saturday (staggered from main ecosystem)",
"matchManagers": [
"nix"
],
"matchPackageNames": [
"/nixpkgs-qt/"
],
"schedule": [
"after 4pm and before 6pm on Saturday"
]
},
{
@@ -87,7 +69,7 @@
"/gastown/"
],
"schedule": [
"before 6am"
"before 6am every day"
],
"automerge": false
},
@@ -100,7 +82,7 @@
"/beads/"
],
"schedule": [
"before 6am"
"before 6am every day"
],
"automerge": false
}

View File

@@ -23,13 +23,7 @@
max-jobs = "auto";
trusted-users = [ "johno" ];
substituters = [
"http://john-endesktop.oglehome:5000"
];
trusted-public-keys = [
"harmonia.john-endesktop:1iGr4xZrsR7WtXOlPCgFF3LcODYBpu+B3TS54MyBn4M="
];
fallback = true;
connect-timeout = 5;
};
gc = {

View File

@@ -14,12 +14,10 @@ with lib;
./nfs-mounts
./nvidia
./printing
./rclone-mount
./remote-build
./spotifyd
./users
./virtualisation
./wireguard
];
config = {

View File

@@ -17,10 +17,9 @@ in
services.gnome.gnome-keyring.enable = true;
programs.kdeconnect.enable = true;
# XDG Desktop Portal for default application handling
# XDG Desktop Portal for default application handling in non-KDE environments
xdg.portal = {
enable = true;
wlr.enable = cfg.wayland; # xdg-desktop-portal-wlr for Sway screen sharing
extraPortals = with pkgs; [
kdePackages.xdg-desktop-portal-kde # For KDE application integration
xdg-desktop-portal-gtk # Fallback for GTK applications

View File

@@ -11,8 +11,9 @@ in
enable = true;
wrapperFeatures.gtk = true;
};
programs.light.enable = true;
environment.systemPackages = with pkgs; [
brightnessctl
grim
slurp
wl-clipboard

View File

@@ -47,23 +47,23 @@ in
if cfg.jellyfinScaleFactor != null
then pkgs.symlinkJoin {
name = "jellyfin-media-player-scaled";
paths = [ pkgs.qt-pinned.jellyfin-media-player ];
paths = [ pkgs.jellyfin-media-player ];
nativeBuildInputs = [ pkgs.makeWrapper ];
postBuild = ''
mkdir -p $out/bin
rm -f $out/bin/jellyfin-desktop
makeWrapper ${pkgs.qt-pinned.jellyfin-media-player}/bin/jellyfin-desktop $out/bin/jellyfin-desktop \
makeWrapper ${pkgs.jellyfin-media-player}/bin/jellyfin-desktop $out/bin/jellyfin-desktop \
--add-flags "--tv --scale-factor ${toString cfg.jellyfinScaleFactor}"
# Update .desktop file to include scale factor and TV mode arguments
mkdir -p $out/share/applications
rm -f $out/share/applications/org.jellyfin.JellyfinDesktop.desktop
substitute ${pkgs.qt-pinned.jellyfin-media-player}/share/applications/org.jellyfin.JellyfinDesktop.desktop \
substitute ${pkgs.jellyfin-media-player}/share/applications/org.jellyfin.JellyfinDesktop.desktop \
$out/share/applications/org.jellyfin.JellyfinDesktop.desktop \
--replace-fail "Exec=jellyfin-desktop" "Exec=jellyfin-desktop --tv --scale-factor ${toString cfg.jellyfinScaleFactor}"
'';
}
else pkgs.qt-pinned.jellyfin-media-player;
else pkgs.jellyfin-media-player;
in mkIf cfg.enable
{
users.extraUsers.kodi = {
@@ -77,15 +77,14 @@ in
};
environment.systemPackages = with pkgs; [
firefox
jellyfinMediaPlayerPkg
kodiPkg
qt-pinned.stremio
wget
firefox
] ++ optional cfg.appLauncherServer.enable pkgs.custom.app-launcher-server;
nixpkgs.config.permittedInsecurePackages = lib.warn
"Allowing insecure package qtwebengine-5.15.19 as a jellyfin-media-player/stremio dependency. These are pinned to nixpkgs-qt to avoid rebuilds - update that input separately when you have time."
"Allowing insecure package qtwebengine-5.15.19 as a jellyfin-media-player dependency. Remove this once jellyfin is updated to use qt6"
[
"qtwebengine-5.15.19"
];

View File

@@ -1,149 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.roles.rclone-mount;
# Generate systemd service for a single mount
mkMountService = name: mountCfg: {
description = "rclone mount for ${name}";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
# Wait for parent mount points (e.g., ZFS pools) to be available
unitConfig = mkIf (mountCfg.requiresMountsFor != []) {
RequiresMountsFor = mountCfg.requiresMountsFor;
};
serviceConfig = {
Type = "notify";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mountCfg.mountPoint}";
ExecStart = concatStringsSep " " ([
"${pkgs.rclone}/bin/rclone mount"
":webdav:${mountCfg.remotePath}"
"${mountCfg.mountPoint}"
"--webdav-url=${mountCfg.webdavUrl}"
"--webdav-vendor=${mountCfg.webdavVendor}"
"--webdav-user=${mountCfg.username}"
"--allow-other"
"--vfs-cache-mode=${mountCfg.vfsCacheMode}"
"--dir-cache-time=${mountCfg.dirCacheTime}"
"--poll-interval=${mountCfg.pollInterval}"
"--log-level=${mountCfg.logLevel}"
] ++ mountCfg.extraArgs);
ExecStop = "${pkgs.fuse}/bin/fusermount -uz ${mountCfg.mountPoint}";
Restart = "on-failure";
RestartSec = "10s";
EnvironmentFile = mountCfg.environmentFile;
};
};
in
{
options.roles.rclone-mount = {
enable = mkEnableOption "Enable rclone WebDAV mounts";
mounts = mkOption {
type = types.attrsOf (types.submodule {
options = {
webdavUrl = mkOption {
type = types.str;
description = "WebDAV server URL (e.g., https://webdav.torbox.app)";
};
webdavVendor = mkOption {
type = types.enum [ "other" "nextcloud" "owncloud" "sharepoint" "sharepoint-ntlm" "fastmail" ];
default = "other";
description = "WebDAV server vendor for optimizations";
};
username = mkOption {
type = types.str;
description = "WebDAV username (often email address)";
};
environmentFile = mkOption {
type = types.path;
description = ''
Path to environment file containing RCLONE_WEBDAV_PASS.
The password should be obscured using: rclone obscure <password>
File format: RCLONE_WEBDAV_PASS=<obscured_password>
'';
};
mountPoint = mkOption {
type = types.str;
description = "Local mount point path";
};
remotePath = mkOption {
type = types.str;
default = "/";
description = "Remote path on WebDAV server to mount";
};
vfsCacheMode = mkOption {
type = types.enum [ "off" "minimal" "writes" "full" ];
default = "full";
description = ''
VFS cache mode. For streaming media, 'full' is recommended.
- off: No caching (direct reads/writes)
- minimal: Cache open files only
- writes: Cache writes and open files
- full: Full caching of all files
'';
};
dirCacheTime = mkOption {
type = types.str;
default = "5m";
description = "Time to cache directory entries";
};
pollInterval = mkOption {
type = types.str;
default = "1m";
description = "Poll interval for remote changes";
};
logLevel = mkOption {
type = types.enum [ "DEBUG" "INFO" "NOTICE" "ERROR" ];
default = "INFO";
description = "rclone log level";
};
extraArgs = mkOption {
type = types.listOf types.str;
default = [];
description = "Extra arguments to pass to rclone mount";
};
requiresMountsFor = mkOption {
type = types.listOf types.str;
default = [];
description = ''
List of mount points that must be available before this service starts.
Use this when the mount point's parent is on a ZFS pool or other filesystem
that may not be mounted at boot time.
Example: [ "/media" ] to wait for the media ZFS pool to mount.
'';
};
};
});
default = {};
description = "Attribute set of rclone WebDAV mounts to configure";
};
};
config = mkIf cfg.enable {
# Ensure FUSE is available
environment.systemPackages = [ pkgs.rclone pkgs.fuse ];
programs.fuse.userAllowOther = true;
# Create systemd services for each mount
systemd.services = mapAttrs' (name: mountCfg:
nameValuePair "rclone-mount-${name}" (mkMountService name mountCfg)
) cfg.mounts;
};
}

View File

@@ -35,12 +35,12 @@
# a) Configure builders in configuration.nix:
# roles.remote-build.builders = [
# {
# hostName = "zix790prors.oglehome";
# hostName = "zix790prors";
# maxJobs = 16; # Number of parallel build jobs
# speedFactor = 3; # Higher = prefer this builder
# }
# {
# hostName = "john-endesktop.oglehome";
# hostName = "john-endesktop";
# maxJobs = 1; # Conservative for busy machines
# speedFactor = 1;
# }

View File

@@ -13,11 +13,6 @@ in
default = [ "johno" ];
description = "List of users to add to the docker group";
};
waydroid = mkOption {
type = types.bool;
default = false;
description = "Enable waydroid support";
};
};
config = mkIf cfg.enable
@@ -26,6 +21,5 @@ in
programs.virt-manager.enable = true;
virtualisation.docker.enable = true;
users.extraGroups.docker.members = cfg.dockerUsers;
virtualisation.waydroid.enable = cfg.waydroid;
};
}

View File

@@ -1,71 +0,0 @@
{ config, lib, ... }:
with lib;
let
cfg = config.roles.wireguard;
in
{
options.roles.wireguard = {
enable = mkEnableOption "Enable WireGuard VPN";
interfaceName = mkOption {
type = types.str;
default = "wg0";
description = "Name of the WireGuard interface";
};
address = mkOption {
type = types.listOf types.str;
description = "Address(es) for the WireGuard interface";
};
privateKeyFile = mkOption {
type = types.path;
description = "Path to a root-owned file containing the WireGuard private key";
};
dns = mkOption {
type = types.listOf types.str;
default = [];
description = "DNS servers to use when the tunnel is active";
};
peers = mkOption {
type = types.listOf (types.submodule {
options = {
publicKey = mkOption {
type = types.str;
description = "Public key of the peer";
};
endpoint = mkOption {
type = types.str;
description = "Endpoint address of the peer (host:port)";
};
allowedIPs = mkOption {
type = types.listOf types.str;
description = "List of allowed IP ranges for this peer";
};
persistentKeepalive = mkOption {
type = types.int;
default = 25;
description = "Persistent keepalive interval in seconds";
};
};
});
description = "WireGuard peers";
};
autostart = mkOption {
type = types.bool;
default = false;
description = "Whether to start the VPN automatically on boot";
};
};
config = mkIf cfg.enable {
networking.wg-quick.interfaces.${cfg.interfaceName} = {
inherit (cfg) address dns autostart peers;
privateKeyFile = cfg.privateKeyFile;
};
systemd.services."wg-quick-${cfg.interfaceName}" = {
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
};
};
}

0
scripts/rotate-wallpaper.sh Executable file → Normal file
View File