diff --git a/flake.lock b/flake.lock index 03e305d..6e65260 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "actor-typeahead-src": { + "flake": false, + "locked": { + "lastModified": 1762835797, + "narHash": "sha256-heizoWUKDdar6ymfZTnj3ytcEv/L4d4fzSmtr0HlXsQ=", + "ref": "refs/heads/main", + "rev": "677fe7f743050a4e7f09d4a6f87bbf1325a06f6b", + "revCount": 6, + "type": "git", + "url": "https://tangled.org/@jakelazaroff.com/actor-typeahead" + }, + "original": { + "type": "git", + "url": "https://tangled.org/@jakelazaroff.com/actor-typeahead" + } + }, "agenix": { "inputs": { "darwin": "darwin", @@ -10,11 +26,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1754433428, - "narHash": "sha256-NA/FT2hVhKDftbHSwVnoRTFhes62+7dxZbxj5Gxvghs=", + "lastModified": 1762618334, + "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=", "owner": "ryantm", "repo": "agenix", - "rev": "9edb1787864c4f59ae5074ad498b6272b3ec308d", + "rev": "fcdea223397448d35d9b31f798479227e80183f6", "type": "github" }, "original": { @@ -84,11 +100,11 @@ "systems": "systems_5" }, "locked": { - "lastModified": 1767386128, - "narHash": "sha256-BJDu7dIMauO2nYRSL4aI8wDNtEm2KOb7lDKP3hxdrpo=", + "lastModified": 1769353768, + "narHash": "sha256-zI+7cbMI4wMIR57jMjDSEsVb3grapTnURDxxJPYFIW0=", "owner": "numtide", "repo": "blueprint", - "rev": "0ed984d51a3031065925ab08812a5434f40b93d4", + "rev": "c7da5c70ad1c9b60b6f5d4f674fbe205d48d8f6c", "type": "github" }, "original": { @@ -100,16 +116,16 @@ "brew-src": { "flake": false, "locked": { - "lastModified": 1758543057, - "narHash": "sha256-lw3V2jOGYphUFHYQ5oARcb6urlbNpUCLJy1qhsGdUmc=", + "lastModified": 1763638478, + "narHash": "sha256-n/IMowE9S23ovmTkKX7KhxXC2Yq41EAVFR2FBIXPcT8=", "owner": "Homebrew", "repo": "brew", - "rev": "5b236456eb93133c2bd0d60ef35ed63f1c0712f6", + "rev": "fbfdbaba008189499958a7aeb1e2c36ab10c067d", "type": "github" }, "original": { "owner": "Homebrew", - "ref": "4.6.12", + "ref": "5.0.3", "repo": "brew", "type": "github" } @@ -143,16 +159,16 @@ ] }, "locked": { - "lastModified": 1757432263, - "narHash": "sha256-qHn+/0+IOz5cG68BZUwL9BV3EO/e9eNKCjH3+N7wMdI=", + "lastModified": 1767634391, + "narHash": "sha256-owcSz2ICqTSvhBbhPP+1eWzi88e54rRZtfCNE5E/wwg=", "owner": "LnL7", "repo": "nix-darwin", - "rev": "1fef4404de4d1596aa5ab2bd68078370e1b9dcdb", + "rev": "08585aacc3d6d6c280a02da195fdbd4b9cf083c2", "type": "github" }, "original": { "owner": "LnL7", - "ref": "nix-darwin-25.05", + "ref": "nix-darwin-25.11", "repo": "nix-darwin", "type": "github" } @@ -166,11 +182,11 @@ "utils": "utils" }, "locked": { - "lastModified": 1756719547, - "narHash": "sha256-N9gBKUmjwRKPxAafXEk1EGadfk2qDZPBQp4vXWPHINQ=", + "lastModified": 1766051518, + "narHash": "sha256-znKOwPXQnt3o7lDb3hdf19oDo0BLP4MfBOYiWkEHoik=", "owner": "serokell", "repo": "deploy-rs", - "rev": "125ae9e3ecf62fb2c0fd4f2d894eb971f1ecaed2", + "rev": "d5eff7f948535b9c723d60cd8239f8f11ddc90fa", "type": "github" }, "original": { @@ -238,11 +254,11 @@ ] }, "locked": { - "lastModified": 1758287904, - "narHash": "sha256-IGmaEf3Do8o5Cwp1kXBN1wQmZwQN3NLfq5t4nHtVtcU=", + "lastModified": 1768923567, + "narHash": "sha256-GVJ0jKsyXLuBzRMXCDY6D5J8wVdwP1DuQmmvYL/Vw/Q=", "owner": "nix-community", "repo": "disko", - "rev": "67ff9807dd148e704baadbd4fd783b54282ca627", + "rev": "00395d188e3594a1507f214a2f15d4ce5c07cb28", "type": "github" }, "original": { @@ -458,11 +474,11 @@ "systems": "systems_6" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -503,11 +519,11 @@ ] }, "locked": { - "lastModified": 1754078208, - "narHash": "sha256-YVoIFDCDpYuU3riaDEJ3xiGdPOtsx4sR5eTzHTytPV8=", + "lastModified": 1763982521, + "narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=", "owner": "nix-community", "repo": "gomod2nix", - "rev": "7f963246a71626c7fc70b431a315c4388a0c95cf", + "rev": "02e63a239d6eabd595db56852535992c898eba72", "type": "github" }, "original": { @@ -540,11 +556,11 @@ }, "hardware": { "locked": { - "lastModified": 1758663926, - "narHash": "sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk=", + "lastModified": 1768736227, + "narHash": "sha256-qgGq7CfrYKc3IBYQ7qp0Z/ZXndQVC5Bj0N8HW9mS2rM=", "owner": "nixos", "repo": "nixos-hardware", - "rev": "170ff93c860b2a9868ed1e1102d4e52cb3d934e1", + "rev": "d447553bcbc6a178618d37e61648b19e744370df", "type": "github" }, "original": { @@ -581,16 +597,16 @@ ] }, "locked": { - "lastModified": 1758463745, - "narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=", + "lastModified": 1768949235, + "narHash": "sha256-TtjKgXyg1lMfh374w5uxutd6Vx2P/hU81aEhTxrO2cg=", "owner": "nix-community", "repo": "home-manager", - "rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3", + "rev": "75ed713570ca17427119e7e204ab3590cc3bf2a5", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-25.05", + "ref": "release-25.11", "repo": "home-manager", "type": "github" } @@ -614,11 +630,11 @@ "homebrew-cask": { "flake": false, "locked": { - "lastModified": 1759110411, - "narHash": "sha256-wsvLofMB/1bkjY6OQjjWU80+AbQiPzZSLZ3cjsYpOAs=", + "lastModified": 1769058882, + "narHash": "sha256-GOSEf+DtzP/ORw+wrP1o9qZaz2XEvUOBC2j0cEEl2MY=", "owner": "homebrew", "repo": "homebrew-cask", - "rev": "80f95de379d69edb696dd29106b2d8b077c98896", + "rev": "022be3ce1e808c0196c540471933238fc2bf0815", "type": "github" }, "original": { @@ -630,11 +646,11 @@ "homebrew-core": { "flake": false, "locked": { - "lastModified": 1759116320, - "narHash": "sha256-FGqC/WlIJnMkhV7l6XK6r5AqUYkwTHvHBCUjFe3DUUY=", + "lastModified": 1769064658, + "narHash": "sha256-xT3S9geUhs4jmyuuQhsyGFfv86baCXqLID8Bvtzewpo=", "owner": "homebrew", "repo": "homebrew-core", - "rev": "0ccf924357d3eca3268d6dafb224ad9dc4f2398a", + "rev": "eaa460777befeb0b457587e04e05991ea90826be", "type": "github" }, "original": { @@ -717,11 +733,11 @@ ] }, "locked": { - "lastModified": 1762951919, - "narHash": "sha256-ma/xMEGf4J6n/RdZFdxXBJUQhP53HVEPQOC6Dp2TrkQ=", + "lastModified": 1768986040, + "narHash": "sha256-83npNk7w9yNJfSnpdZPNUjbhQwGVef3BWyBuIe6TJfk=", "owner": "Jovian-Experiments", "repo": "Jovian-NixOS", - "rev": "3d248f6e8f877218dd2573fef8925ac997889922", + "rev": "d75e3c96c9f935a6ccdd4a91209950289b2dc2fc", "type": "github" }, "original": { @@ -737,11 +753,11 @@ "treefmt-nix": "treefmt-nix_2" }, "locked": { - "lastModified": 1768434130, - "narHash": "sha256-4rBBs7spDuimvUcL3egp2Zh94Lk8pf00VsjkOs59h7E=", + "lastModified": 1769482725, + "narHash": "sha256-Y4vH4PJIz4dt8SRJ5nm6yJCDzJRVEMdE5tP2TFIUfoQ=", "owner": "numtide", "repo": "llm-agents.nix", - "rev": "d0ed3ef68a04b5bd127fecd405baf803eea29c29", + "rev": "ae0667122f9a2336b23a4b4751997fe2f161fb37", "type": "github" }, "original": { @@ -846,11 +862,11 @@ "brew-src": "brew-src" }, "locked": { - "lastModified": 1758598228, - "narHash": "sha256-qr60maXGbZ4FX5tejPRI3nr0bnRTnZ3AbbbfO6/6jq4=", + "lastModified": 1764473698, + "narHash": "sha256-C91gPgv6udN5WuIZWNehp8qdLqlrzX6iF/YyboOj6XI=", "owner": "zhaofengli-wip", "repo": "nix-homebrew", - "rev": "f36e5db56e117f7df701ab152d0d2036ea85218c", + "rev": "6a8ab60bfd66154feeaa1021fc3b32684814a62a", "type": "github" }, "original": { @@ -919,11 +935,11 @@ }, "nixos-hardware": { "locked": { - "lastModified": 1758663926, - "narHash": "sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk=", + "lastModified": 1768736227, + "narHash": "sha256-qgGq7CfrYKc3IBYQ7qp0Z/ZXndQVC5Bj0N8HW9mS2rM=", "owner": "NixOS", "repo": "nixos-hardware", - "rev": "170ff93c860b2a9868ed1e1102d4e52cb3d934e1", + "rev": "d447553bcbc6a178618d37e61648b19e744370df", "type": "github" }, "original": { @@ -1017,11 +1033,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1768032153, - "narHash": "sha256-6kD1MdY9fsE6FgSwdnx29hdH2UcBKs3/+JJleMShuJg=", + "lastModified": 1769421245, + "narHash": "sha256-m5QLKjpdhbDrhyrUbEm5Haq3lqE5Z6xh2tab5vTHUTo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3146c6aa9995e7351a398e17470e15305e6e18ff", + "rev": "5b265bda51b42a2a85af0a543c3e57b778b01b7d", "type": "github" }, "original": { @@ -1049,27 +1065,27 @@ }, "nixpkgs_5": { "locked": { - "lastModified": 1758791193, - "narHash": "sha256-F8WmEwFoHsnix7rt290R0rFXNJiMbClMZyIC/e+HYf0=", + "lastModified": 1768940263, + "narHash": "sha256-sJERJIYTKPFXkoz/gBaBtRKke82h4DkX3BBSsKbfbvI=", "owner": "nixos", "repo": "nixpkgs", - "rev": "25e53aa156d47bad5082ff7618f5feb1f5e02d01", + "rev": "3ceaaa8bc963ced4d830e06ea2d0863b6490ff03", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-25.05", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_6": { "locked": { - "lastModified": 1758690382, - "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=", + "lastModified": 1768886240, + "narHash": "sha256-C2TjvwYZ2VDxYWeqvvJ5XPPp6U7H66zeJlRaErJKoEM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "e643668fd71b949c53f8626614b21ff71a07379d", + "rev": "80e4adbcf8992d3fd27ad4964fbb84907f9478b0", "type": "github" }, "original": { @@ -1085,11 +1101,11 @@ "nixpkgs": "nixpkgs_6" }, "locked": { - "lastModified": 1759117007, - "narHash": "sha256-DSnkPMhK2Eg0XLiyjEPVtIpdcmWmYsBMhVGCN1144SA=", + "lastModified": 1769056321, + "narHash": "sha256-BR4ACqZEfFivkJutgeZeG8Sip/LlvmQgBbkT9eKZeIQ=", "owner": "nix-community", "repo": "NUR", - "rev": "fccc09dffa659dc1410eceb33285831ba12bc7f6", + "rev": "6dd879dc2cb51262567d8b2b49b2d24f256fa343", "type": "github" }, "original": { @@ -1105,11 +1121,11 @@ ] }, "locked": { - "lastModified": 1762711246, - "narHash": "sha256-coOLG/Bp118d1T3DBIZUcW+AdiKsHz9uh6ZuiR30GBM=", + "lastModified": 1759014010, + "narHash": "sha256-NMpUufnxiGDTs/4Nxj8t+n4wc4aSs02a4T5OORo0gBQ=", "ref": "main", - "rev": "54287446e1a8a1bc40ad1b12061f053181e6d264", - "revCount": 1614, + "rev": "08c1dddf38c39d6f7c73a24f4d396f4c925185bf", + "revCount": 1602, "type": "git", "url": "ssh://gitea@git.sealight.xyz/aynish/kitaab" }, @@ -1202,11 +1218,11 @@ ] }, "locked": { - "lastModified": 1759113356, - "narHash": "sha256-xm4kEUcV2jk6u15aHazFP4YsMwhq+PczA+Ul/4FDKWI=", + "lastModified": 1769050281, + "narHash": "sha256-1H8DN4UZgEUqPUA5ecHOufLZMscJ4IlcGaEftaPtpBY=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "be3b8843a2be2411500f6c052876119485e957a2", + "rev": "6deef0585c52d9e70f96b6121207e1496d4b0c49", "type": "github" }, "original": { @@ -1336,6 +1352,7 @@ }, "tangled": { "inputs": { + "actor-typeahead-src": "actor-typeahead-src", "flake-compat": "flake-compat_4", "gomod2nix": "gomod2nix", "htmx-src": "htmx-src", @@ -1350,11 +1367,11 @@ "sqlite-lib-src": "sqlite-lib-src" }, "locked": { - "lastModified": 1759559279, - "narHash": "sha256-gA0mh9Fx2uou2v75RMA6qUvWB3Z74Asc6pRjiojwaRo=", + "lastModified": 1769064660, + "narHash": "sha256-2ccXZ51txbX1jAhW52Z6QuSFAgFqo3Gr1bqxD4jXNw0=", "ref": "refs/heads/master", - "rev": "5ecd54b31547ac169a3b15d6034a67179f22aa33", - "revCount": 1486, + "rev": "2b94ee226637e13c106d7782ca852bd608530b51", + "revCount": 1865, "type": "git", "url": "https://tangled.org/@tangled.org/core" }, @@ -1431,11 +1448,11 @@ ] }, "locked": { - "lastModified": 1768031762, - "narHash": "sha256-b2gJDJfi+TbA7Hu2sKip+1mWqya0GJaWrrXQjpbOVTU=", + "lastModified": 1769353635, + "narHash": "sha256-J0G1ACrUK29M0THPAsz429eZX07GmR9Bs/b0pB3N0dQ=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "0c445aa21b01fd1d4bb58927f7b268568af87b20", + "rev": "f46bb205f239b415309f58166f8df6919fa88377", "type": "github" }, "original": { @@ -1446,11 +1463,11 @@ }, "unstable": { "locked": { - "lastModified": 1762844143, - "narHash": "sha256-SlybxLZ1/e4T2lb1czEtWVzDCVSTvk9WLwGhmxFmBxI=", + "lastModified": 1768886240, + "narHash": "sha256-C2TjvwYZ2VDxYWeqvvJ5XPPp6U7H66zeJlRaErJKoEM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9da7f1cf7f8a6e2a7cb3001b048546c92a8258b4", + "rev": "80e4adbcf8992d3fd27ad4964fbb84907f9478b0", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index e0244d3..ce9573a 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,7 @@ inputs = { # Nixpkgs - nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; unstable.url = "github:nixos/nixpkgs/nixos-unstable"; nixos-hardware.url = "github:NixOS/nixos-hardware/master"; @@ -16,7 +16,7 @@ # ngipkgs-local.url = "path:/home/anish/usr/ngipkgs"; # Home manager - home-manager.url = "github:nix-community/home-manager/release-25.05"; + home-manager.url = "github:nix-community/home-manager/release-25.11"; home-manager.inputs.nixpkgs.follows = "nixpkgs"; hardware.url = "github:nixos/nixos-hardware"; @@ -44,7 +44,7 @@ # Darwin darwin = { - url = "github:LnL7/nix-darwin/nix-darwin-25.05"; + url = "github:LnL7/nix-darwin/nix-darwin-25.11"; inputs.nixpkgs.follows = "nixpkgs"; }; nix-homebrew = { @@ -160,13 +160,14 @@ "SunVox" "renoise" "bitwig-studio-unwrapped" + "via" # QMK keyboard configurator ]; }; overlays = [ rust-overlay.overlays.default tidalcycles.overlays.default agenix.overlays.default - nur.overlay + nur.overlays.default # nix-matrix-appservices.overlay # nixpkgs has these packages and newer ones at that unstableOverlay vimwikiOverlay @@ -217,7 +218,7 @@ rust-overlay.overlays.default tidalcycles.overlays.default agenix.overlays.default - nur.overlay + nur.overlays.default unstableOverlay vimwikiOverlay self.overlays.additions @@ -253,7 +254,7 @@ rust-overlay.overlays.default tidalcycles.overlays.default agenix.overlays.default - nur.overlay + nur.overlays.default unstableOverlay vimwikiOverlay self.overlays.additions @@ -325,7 +326,8 @@ agenix.nixosModules.age self.nixosModules.backup self.nixosModules.wireguard - basant.nixosModule + # TODO: basant needs pyproject update for 25.11 - re-enable after fixing + # basant.nixosModule # self.nixosModules.microbin disko.nixosModules.disko { diff --git a/home/profiles/beets/default.nix b/home/profiles/beets/default.nix index dc7a315..72a0742 100644 --- a/home/profiles/beets/default.nix +++ b/home/profiles/beets/default.nix @@ -2,15 +2,9 @@ { programs.beets = { enable = true; - package = pkgs.beets.override { - pluginOverrides = { - fetchart.enable = true; - embedart.enable = true; - lastgenre.enable = true; - duplicates.enable = true; - missing.enable = true; - }; - }; + # In 25.11, beets plugins are enabled via beetsPackages or the default package + # The default beets package includes common plugins + package = pkgs.beets; settings = { directory = "/tank/media/music"; library = "/home/anish/.local/share/beets/library.db"; diff --git a/home/profiles/cli/default.nix b/home/profiles/cli/default.nix index 6451ace..518d079 100644 --- a/home/profiles/cli/default.nix +++ b/home/profiles/cli/default.nix @@ -1,158 +1,167 @@ -{ lib, pkgs, config, ... }: { - home.packages = with pkgs; [ - binutils - coreutils - dnsutils - dasht - dosfstools - #git - git-machete - bottom - gptfdisk - difftastic - starship - jq - manix - moreutils - nix-index - cached-nix-shell - nmap - ripgrep - skim - tealdeer - usbutils - utillinux - whois - iftop - wget - curl - eza - bat - fd - ncdu - dust - # dia-cli - duf - trash-cli - nix-index - silver-searcher - tcpdump - mtr - file - lsof - atool - zip - unzip - rsync - tmux - pwgen - glow - pass - less - gdb - xxd - taskwarrior2 - gnupg - syncthing - dijo - nixfmt-rfc-style - nix-tree - #ssb-patchwork - fontconfig - pandoc - taskwarrior-tui - # vimwiki-cli - zk + lib, + pkgs, + config, + ... +}: +{ + home.packages = + with pkgs; + [ + binutils + coreutils + dnsutils + dasht + dosfstools + #git + git-machete + bottom + gptfdisk + difftastic + starship + jq + manix + moreutils + nix-index + cached-nix-shell + nmap + ripgrep + skim + tealdeer + usbutils + utillinux + whois + iftop + wget + curl + eza + bat + fd + ncdu + dust + # dia-cli + duf + trash-cli + nix-index + silver-searcher + tcpdump + mtr + file + lsof + atool + zip + unzip + rsync + tmux + pwgen + glow + pass + less + gdb + xxd + taskwarrior2 + gnupg + syncthing + dijo + nixfmt-rfc-style + nix-tree + #ssb-patchwork + fontconfig + pandoc + taskwarrior-tui + # vimwiki-cli + zk + radicle-node # rad CLI for interacting with Radicle repos - (pkgs.writeScriptBin "jq-repl" '' - #!/usr/bin/env bash - if [[ -z $1 ]] || [[ $1 == "-" ]]; then - input=$(mktemp) - trap "rm -f $input" EXIT - cat /dev/stdin > $input - else - input=$1 - fi + (pkgs.writeScriptBin "jq-repl" '' + #!/usr/bin/env bash + if [[ -z $1 ]] || [[ $1 == "-" ]]; then + input=$(mktemp) + trap "rm -f $input" EXIT + cat /dev/stdin > $input + else + input=$1 + fi - echo "" \ | fzf --phony --preview-window="up:90%" --print-query --preview "jq --color-output -r {q} $input" - '') - (pkgs.writeScriptBin "clear-vim-trash" (builtins.readFile ./bin/clear-vim-trash.sh)) - (pkgs.writeScriptBin "flakify" '' - #!/usr/bin/env zsh - if [ ! -e flake.nix ]; then - nix flake new -t github:nix-community/nix-direnv . - elif [ ! -e .envrc ]; then - echo "use flake" > .envrc - direnv allow - fi - vim flake.nix - '') - (pkgs.writeScriptBin "mx" '' - #!/usr/bin/env bash - manix "" | grep '^# ' | sed 's/^# \(.*\) (.*/\1/;s/ (.*//;s/^# //' | fzf --preview="manix '{}'" | xargs manix - '') - (pkgs.writeScriptBin "monitor" '' - connect() { - # Turn it on if it was off - xrandr --output HDMI-2 --auto - xrandr --output HDMI-2 --same-as eDP-1 - xrandr --output eDP-1 --off - pkill -9 polybar - feh --bg-scale ~/Downloads/stephen-walker-onIXxjH56AA-unsplash.jpg - polybar mybar > $XDG_DATA_HOME/polybar.log 2>&1 & - } + echo "" \ | fzf --phony --preview-window="up:90%" --print-query --preview "jq --color-output -r {q} $input" + '') + (pkgs.writeScriptBin "clear-vim-trash" (builtins.readFile ./bin/clear-vim-trash.sh)) + (pkgs.writeScriptBin "flakify" '' + #!/usr/bin/env zsh + if [ ! -e flake.nix ]; then + nix flake new -t github:nix-community/nix-direnv . + elif [ ! -e .envrc ]; then + echo "use flake" > .envrc + direnv allow + fi + vim flake.nix + '') + (pkgs.writeScriptBin "mx" '' + #!/usr/bin/env bash + manix "" | grep '^# ' | sed 's/^# \(.*\) (.*/\1/;s/ (.*//;s/^# //' | fzf --preview="manix '{}'" | xargs manix + '') + (pkgs.writeScriptBin "monitor" '' + connect() { + # Turn it on if it was off + xrandr --output HDMI-2 --auto + xrandr --output HDMI-2 --same-as eDP-1 + xrandr --output eDP-1 --off + pkill -9 polybar + feh --bg-scale ~/Downloads/stephen-walker-onIXxjH56AA-unsplash.jpg + polybar mybar > $XDG_DATA_HOME/polybar.log 2>&1 & + } - disconnect() { - xrandr --output HDMI-2 --off - xrandr --output eDP-1 --auto - } + disconnect() { + xrandr --output HDMI-2 --off + xrandr --output eDP-1 --auto + } - xrandr | grep "HDMI-2 connected" &>>/dev/null && connect || disconnect - '') - (pkgs.writeScriptBin "big-monitor" '' - connect() { - # Turn it on if it was off - xrandr --output HDMI-2 --auto --primary - # Disable normal display - xrandr --output eDP-1 --off - # Use a nice background - feh --bg-scale ~/Downloads/stephen-walker-onIXxjH56AA-unsplash.jpg - # Reload Polybar - ~/.config/bspwm/rc.d/polybar - } + xrandr | grep "HDMI-2 connected" &>>/dev/null && connect || disconnect + '') + (pkgs.writeScriptBin "big-monitor" '' + connect() { + # Turn it on if it was off + xrandr --output HDMI-2 --auto --primary + # Disable normal display + xrandr --output eDP-1 --off + # Use a nice background + feh --bg-scale ~/Downloads/stephen-walker-onIXxjH56AA-unsplash.jpg + # Reload Polybar + ~/.config/bspwm/rc.d/polybar + } - disconnect() { - xrandr --output HDMI-2 --off - } + disconnect() { + xrandr --output HDMI-2 --off + } - xrandr | grep "HDMI-2 connected" &>>/dev/null && connect || disconnect - '') - (pkgs.writeScriptBin "disconnect-keyboard" '' - # keyboard on curve is busted - get_keyboard_id() { - xinput list | grep 'AT Translated Set' | cut -f2 | cut -d'=' -f2 | xinput float - } + xrandr | grep "HDMI-2 connected" &>>/dev/null && connect || disconnect + '') + (pkgs.writeScriptBin "disconnect-keyboard" '' + # keyboard on curve is busted + get_keyboard_id() { + xinput list | grep 'AT Translated Set' | cut -f2 | cut -d'=' -f2 | xinput float + } - disconnect_keyboard() { - id=$(get_keyboard_id) - xinput float $id - unset id - } + disconnect_keyboard() { + id=$(get_keyboard_id) + xinput float $id + unset id + } - attach_keyboard() { - id=$(get_keyboard_id) - xinput reattach $id 3 - unset id - } + attach_keyboard() { + id=$(get_keyboard_id) + xinput reattach $id 3 + unset id + } - disconnect_keyboard - '') - ] ++ lib.optionals pkgs.stdenv.isLinux [ - # Linux-only packages - iputils - strace - ]; + disconnect_keyboard + '') + ] + ++ lib.optionals pkgs.stdenv.isLinux [ + # Linux-only packages + iputils + strace + ]; programs.zsh = { enable = true; @@ -246,7 +255,7 @@ yt = "ytfzf -T kitty -t "; gen-secret = "< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c\${1:-32};echo;"; - # modern cli tools + # modern cli tools ls = "eza --icons"; l = "esa -l --icons"; la = "eza -la --icons"; @@ -255,12 +264,11 @@ unzip = "aunpack"; copy = if pkgs.stdenv.isDarwin then "pbcopy" else "xclip -selection clipboard"; paste = if pkgs.stdenv.isDarwin then "pbpaste" else "xclip -selection clipboard -o"; - rm = "echo USE TRASH, FOOL: trash "; trash = "trash-put"; make-secret = "< /dev/urandom \\tr -dc _A-Za-z0-9 | head -c \${1:-32};echo;"; # task warrior - t = "task -BLOCKED -idea -backlog"; + t = "task -BLOCKED -idea -backlog"; tw = "t rc.context:work"; # TODO find a reasonable way to manage this from a non-nix managed file in ~/.task/context or something # we can do something like task rc.context:$(cat ~/.task/context) to read context diff --git a/home/profiles/desktop/kitty.conf b/home/profiles/desktop/kitty.conf index b8cd3ec..d471c4f 100644 --- a/home/profiles/desktop/kitty.conf +++ b/home/profiles/desktop/kitty.conf @@ -11,6 +11,5 @@ enable_audio_bell no -# Ctrl+V for paste -map ctrl+v paste_from_clipboard +# Ctrl+V handled by neovim for insert mode paste (allows visual block mode in normal mode) mouse_map middle release ungrabbed paste_from_clipboard diff --git a/home/profiles/git/default.nix b/home/profiles/git/default.nix index 089cd08..846e5bc 100644 --- a/home/profiles/git/default.nix +++ b/home/profiles/git/default.nix @@ -12,11 +12,14 @@ unstable.git-spice ]; + # Delta (git diff pager) - moved to top-level in home-manager 25.11 + programs.delta = { + enable = true; + enableGitIntegration = true; + }; + programs.git = { enable = true; - userName = "Anish Lakhwara"; - userEmail = "anish+git@lakhwara.com"; - delta.enable = true; signing = { signByDefault = if pkgs.stdenv.isLinux then false else true; key = if pkgs.stdenv.isLinux then "B8492C8FB53397B7" else "7FC5DF072EF7B716"; @@ -60,51 +63,55 @@ "node_modules/" ]; - extraConfig = { + # 25.11: extraConfig, userName, userEmail, aliases moved under settings + settings = { + user = { + name = "Anish Lakhwara"; + email = "anish+git@lakhwara.com"; + }; pull.rebase = false; push.autoSetupRemote = true; init.defaultBranch = "main"; - "url \"git@github.com:\"" = { insteadOf = "https://github.com/"; }; - }; + "url \"git@github.com:\"" = { + insteadOf = "https://github.com/"; + }; - aliases = { - a = "add -p"; - co = "checkout"; - cob = "checkout -b"; - f = "fetch -p"; - c = "commit -v"; - p = "push"; - ba = "branch -a"; - bd = "branch -d"; - bD = "branch -D"; - d = "diff"; - dc = "diff --cached"; - ds = "diff --staged"; - r = "restore"; - rs = "restore --staged"; - st = "status -sb"; + alias = { + a = "add -p"; + co = "checkout"; + cob = "checkout -b"; + f = "fetch -p"; + c = "commit -v"; + p = "push"; + ba = "branch -a"; + bd = "branch -d"; + bD = "branch -D"; + d = "diff"; + dc = "diff --cached"; + ds = "diff --staged"; + r = "restore"; + rs = "restore --staged"; + st = "status -sb"; - # reset - soft = "reset --soft"; - hard = "reset --hard"; - s1ft = "soft HEAD~1"; - h1rd = "hard HEAD~1"; + # reset + soft = "reset --soft"; + hard = "reset --hard"; + s1ft = "soft HEAD~1"; + h1rd = "hard HEAD~1"; - # logging - lg = - "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"; - plog = - "log --graph --pretty='format:%C(red)%d%C(reset) %C(yellow)%h%C(reset) %ar %C(green)%aN%C(reset) %s'"; - tlog = - "log --stat --since='1 Day Ago' --graph --pretty=oneline --abbrev-commit --date=relative"; - rank = "shortlog -sn --no-merges"; - authors = "authors | sort | uniq -c | sort -n"; - ls = "log --graph --abbrev-commit --decorate --color=always --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) - %C(dim red)%an%C(reset)%C(bold yellow)%d%C(reset)' --all"; + # logging + lg = "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"; + plog = "log --graph --pretty='format:%C(red)%d%C(reset) %C(yellow)%h%C(reset) %ar %C(green)%aN%C(reset) %s'"; + tlog = "log --stat --since='1 Day Ago' --graph --pretty=oneline --abbrev-commit --date=relative"; + rank = "shortlog -sn --no-merges"; + authors = "authors | sort | uniq -c | sort -n"; + ls = "log --graph --abbrev-commit --decorate --color=always --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) - %C(dim red)%an%C(reset)%C(bold yellow)%d%C(reset)' --all"; - # delete merged branches - bdm = "!git branch --merged | grep -v '*' | xargs -n 1 git branch -d"; - undo = "!git reset HEAD~1 --mixed"; - blm = "blame -w -C -C -C"; + # delete merged branches + bdm = "!git branch --merged | grep -v '*' | xargs -n 1 git branch -d"; + undo = "!git reset HEAD~1 --mixed"; + blm = "blame -w -C -C -C"; + }; }; }; } diff --git a/home/profiles/nvim/default.nix b/home/profiles/nvim/default.nix index 631f3cf..b78ffc2 100644 --- a/home/profiles/nvim/default.nix +++ b/home/profiles/nvim/default.nix @@ -1,4 +1,9 @@ -{ pkgs, lib, config, ... }: +{ + pkgs, + lib, + config, + ... +}: let customPlugins = { vim-zettel = pkgs.vimUtils.buildVimPlugin { @@ -116,12 +121,13 @@ let }; }; - my-python-packages = python-packages: with python-packages; [ - tasklib - pynvim - six - # other python packages you want - ]; + my-python-packages = + python-packages: with python-packages; [ + tasklib + pynvim + six + # other python packages you want + ]; python-with-my-packages = pkgs.python3.withPackages my-python-packages; in { @@ -146,7 +152,8 @@ in ''; # Private mode plugin for concealing buffer content (out of store symlink for live editing) - home.file.".config/nvim/lua/private-mode.lua".source = config.lib.file.mkOutOfStoreSymlink "${toString ./.}/private-mode.lua"; + home.file.".config/nvim/lua/private-mode.lua".source = + config.lib.file.mkOutOfStoreSymlink "${toString ./.}/private-mode.lua"; #environment.systemPackages = with customPlugins; [ tidal ]; programs.neovim = { @@ -483,10 +490,10 @@ in vim.api.nvim_set_keymap('n', 'vt', ':call v:lua.toggle_diagnostics()', {noremap = true, silent = true}) - + -- nicer diff view vim.opt.diffopt = {'internal', 'filler', 'closeoff', 'algorithm:patience', 'indent-heuristic'} - + -- autopairs require('nvim-autopairs').setup{} @@ -577,16 +584,16 @@ in Function = "ƒ " } } - + -- Setup neo-tree require("neo-tree").setup {} - + -- Setup outline require("outline").setup {} - + -- Setup trouble require("trouble").setup {} - + -- Setup noice for floating command palette and notifications require("noice").setup({ cmdline = { @@ -636,7 +643,7 @@ in }, }, }) - + -- Setup barbar (tabline with icons) require'barbar'.setup { icons = { @@ -646,7 +653,7 @@ in }, } local navic = require("nvim-navic") - + -- Setup conform.nvim for formatting require("conform").setup({ formatters_by_ft = { @@ -880,7 +887,7 @@ in require("which-key").setup{} require('leap').set_default_keymaps() - + -- supercollider local scnvim = require 'scnvim' local map = scnvim.map @@ -1025,15 +1032,15 @@ in disable_on_zoom = true, mux = 'auto', -- auto-detect tmux } - + -- Set up Navigator.nvim keybindings vim.keymap.set({'n', 't'}, '', 'NavigatorLeft') vim.keymap.set({'n', 't'}, '', 'NavigatorDown') vim.keymap.set({'n', 't'}, '', 'NavigatorUp') vim.keymap.set({'n', 't'}, '', 'NavigatorRight') - -- Paste from system clipboard in insert mode (handles tmux/kitty better) - -- vim.keymap.set('i', '', '+', {noremap = true, silent = true}) + -- Paste from system clipboard in insert mode (Ctrl+V in normal mode remains visual block) + vim.keymap.set('i', '', '+', {noremap = true, silent = true}) -- Pane resizing with Alt+Shift+hjkl (to match tmux) vim.keymap.set('n', '', 'vertical resize -2', {silent = true}) @@ -1130,106 +1137,129 @@ in zk ]; - plugins = with pkgs.vimPlugins // customPlugins; [ - # ui - lualine-nvim - fzf-vim - neo-tree-nvim - outline-nvim - noice-nvim - nui-nvim - trouble-nvim - neovim-ayu - rainbow_parentheses-vim - vim-surround - vim-devicons - nvim-web-devicons - undotree - telescope-nvim - plenary-nvim - nvim-navic - (nvim-treesitter.withPlugins (p: [ p.nix p.clojure p.python p.fennel p.lua p.html p.css p.regex p.supercollider p.beancount p.markdown p.glsl p.yaml p.toml p.dockerfile p.json ])) - nvim-treesitter-context - my-fterm - barbar-nvim - auto-session - my-marks - which-key-nvim - nvim-peekup - zen-mode-nvim - twilight-nvim - my-lspsaga - vim-dasht + plugins = + with pkgs.vimPlugins // customPlugins; + [ + # ui + lualine-nvim + fzf-vim + neo-tree-nvim + outline-nvim + noice-nvim + nui-nvim + trouble-nvim + neovim-ayu + rainbow_parentheses-vim + vim-surround + vim-devicons + nvim-web-devicons + undotree + telescope-nvim + plenary-nvim + nvim-navic + (nvim-treesitter.withPlugins (p: [ + p.nix + p.clojure + p.python + p.fennel + p.lua + p.html + p.css + p.regex + p.supercollider + p.beancount + p.markdown + p.glsl + p.yaml + p.toml + p.dockerfile + p.json + ])) + nvim-treesitter-context + my-fterm + barbar-nvim + auto-session + my-marks + which-key-nvim + nvim-peekup + zen-mode-nvim + twilight-nvim + my-lspsaga + vim-dasht - # language - vim-nix - vim-elixir - emmet-vim - csv-vim - direnv-vim - zig-vim - conjure + # language + vim-nix + vim-elixir + emmet-vim + csv-vim + direnv-vim + zig-vim + conjure - # kitaab stuff - vimwiki - taskwiki - vim-zettel - hologram-nvim - zk-nvim + # kitaab stuff + vimwiki + taskwiki + vim-zettel + hologram-nvim + zk-nvim - # lsp stuff - # nvim-lint - nvim-lspconfig - nvim-cmp - cmp-nvim-lsp - cmp-treesitter - nvim-lsp-ts-utils - cmp-conjure - cmp-buffer - cmp-path - cmp-spell - nvim-autopairs - cmp_luasnip - luasnip - conform-nvim - friendly-snippets - lspkind-nvim + # lsp stuff + # nvim-lint + nvim-lspconfig + nvim-cmp + cmp-nvim-lsp + cmp-treesitter + nvim-lsp-ts-utils + cmp-conjure + cmp-buffer + cmp-path + cmp-spell + nvim-autopairs + cmp_luasnip + luasnip + conform-nvim + friendly-snippets + lspkind-nvim - # git stuff - vim-fugitive - gitsigns-nvim - fzf-checkout-vim - diffview-nvim + # git stuff + vim-fugitive + gitsigns-nvim + fzf-checkout-vim + diffview-nvim - # Clojure stuff - # conjure - vim-sexp - vim-sexp-mappings-for-regular-people - fennel-vim + # Clojure stuff + # conjure + vim-sexp + vim-sexp-mappings-for-regular-people + fennel-vim - # experimental - nvim-luapad - gh-addressed - scnvim - leap-nvim - Navigator-nvim - vim-beancount - # vimtex - # custom - yuck-vim - nvim-parinfer - - # opencode integration - opencode-nvim - # vim-processing - ] ++ lib.optionals pkgs.stdenv.isLinux [ - # Linux-only plugins - vim-tidal # requires SuperCollider which is Linux-only - ]; + # experimental + nvim-luapad + gh-addressed + scnvim + leap-nvim + Navigator-nvim + vim-beancount + # vimtex + # custom + yuck-vim + nvim-parinfer + + # opencode integration + opencode-nvim + # vim-processing + ] + ++ lib.optionals pkgs.stdenv.isLinux [ + # Linux-only plugins + vim-tidal # requires SuperCollider which is Linux-only + ]; withPython3 = true; - extraPython3Packages = pkgs: with pkgs; [ tasklib six packaging ]; + extraPython3Packages = + pkgs: with pkgs; [ + tasklib + six + packaging + ]; vimAlias = true; }; } - - diff --git a/home/profiles/opencode/agent/archivist.md b/home/profiles/opencode/agent/archivist.md deleted file mode 100644 index cba22ca..0000000 --- a/home/profiles/opencode/agent/archivist.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -description: Search through past OpenCode sessions to find relevant context, previous solutions, and historical decisions. Use this when you need to recall how something was done before or find related past work. -mode: subagent -model: anthropic/claude-haiku-4-5 -temperature: 0.1 -tools: - "*": false - search-history: true - skill: true -permission: - skill: - "session-search": allow - "*": deny ---- - -You are the Archivist, a specialized agent that searches through OpenCode session history to find relevant past conversations, code changes, and decisions. - -You are running inside an AI coding system as a subagent. The main agent invokes you when it needs to find relevant context from previous sessions. - -## Your Purpose - -When invoked, you will: -1. Search through the local OpenCode session history -2. Find sessions and messages relevant to the query -3. Synthesize findings into a clear, actionable answer - -## How to Search - -First, load the `session-search` skill to understand the search strategies and storage structure. - -Then use the `search-history` tool to find relevant sessions. You can: -- Search by keywords, code patterns, file names, or concepts -- Filter by project directory if the query is project-specific -- List recent sessions to get an overview - -## Search Strategies - -1. **Start broad**: Use general keywords related to the query -2. **Refine**: If too many results, add more specific terms or filter by directory -3. **Cross-reference**: Search for related terms (e.g., if searching for "auth", also try "login", "authentication") -4. **Check context**: Look at session titles and directories to understand the context - -## Response Format - -Your response should directly answer the question posed, using information from past sessions: - -1. **Direct answer**: What was found that addresses the question -2. **Relevant sessions**: List session IDs where this was discussed (so user can resume if needed) -3. **Key details**: Important snippets or decisions from the history - -Example response: -``` -Based on past sessions, authentication was implemented using JWT tokens with a 24-hour expiry. - -**Relevant sessions:** -- ses_abc123 - "Implementing user auth" (2024-01-15) -- ses_def456 - "Auth token refresh" (2024-01-20) - -**Key details:** -- Tokens are stored in httpOnly cookies -- Refresh endpoint at /api/auth/refresh -- Used jose library for JWT handling -``` - -## Guidelines - -- Be concise and direct - the main agent needs actionable information -- Include session IDs so the user can explore further if needed -- If nothing relevant is found, say so clearly -- Focus on answering the specific question, not providing exhaustive history -- Never fabricate information - only report what's actually in the history - -IMPORTANT: Your final message is returned to the main agent. Make it comprehensive but focused on answering the original question. diff --git a/home/profiles/opencode/agent/adversary.md b/home/profiles/opencode/agents/adversary.md similarity index 99% rename from home/profiles/opencode/agent/adversary.md rename to home/profiles/opencode/agents/adversary.md index 25292b2..d21e4a0 100644 --- a/home/profiles/opencode/agent/adversary.md +++ b/home/profiles/opencode/agents/adversary.md @@ -1,6 +1,7 @@ --- description: Adversarial code reviewer that critically examines code for flaws, bugs, and design issues. Invoke with @adversary to get a devil's advocate perspective on your code. -mode: subagent +mode: all +color: "#E0115F" temperature: 0.2 tools: "*": false diff --git a/home/profiles/opencode/agents/box.md b/home/profiles/opencode/agents/box.md new file mode 100644 index 0000000..d0eef63 --- /dev/null +++ b/home/profiles/opencode/agents/box.md @@ -0,0 +1,41 @@ +# Box Server Environment + +You are running on `box`, a NAS server. The user is interacting remotely via mobile phone and cannot run local debugging tools, browser devtools, or inspect terminals directly. + +## Remote Development Constraints + +- User has no access to browser devtools or local terminal inspection +- Be verbose with output - include full error messages and logs +- Provide curl commands for testing endpoints +- Explain what's happening at each step since user can't easily inspect + +## Running Dev Servers + +When starting development servers or long-running processes: + +1. **Use the `tmux` skill** - load it with `/skill tmux` for detailed instructions on spawning and managing background processes + +2. **Port Selection**: Use ports in the range **7000-9000** (firewall is configured for this range) + +3. **Bind Address**: Always bind to `0.0.0.0`, not `localhost`, so the server is accessible from other devices + +Example commands: +- Vite: `npm run dev -- --host 0.0.0.0 --port 7500` +- Next.js: `npm run dev -- -H 0.0.0.0 -p 7500` +- Generic: `HOST=0.0.0.0 PORT=7500 npm start` + +4. **Session Naming**: Name tmux sessions/windows after the project (e.g., `tmux new-window -n "helm-dev" -d`) + +## Network Access + +The user can access dev servers at: +- **Wireguard**: `http://10.0.69.4:` (from any device on the VPN) +- **LAN**: `http://mossnet.lan:` (from local network) + +## Debugging + +Since the user cannot inspect the browser or terminal directly: +- Always capture and report server logs using `tmux capture-pane` +- Include full stack traces in responses +- Suggest curl commands to test API endpoints +- When something fails, proactively check logs before asking the user diff --git a/home/profiles/opencode/agents/learn.md b/home/profiles/opencode/agents/learn.md new file mode 100644 index 0000000..b81e551 --- /dev/null +++ b/home/profiles/opencode/agents/learn.md @@ -0,0 +1,106 @@ +--- +description: Socratic teaching mode that guides learning through questions rather than direct answers. Use when learning new codebases, concepts, or technologies. +mode: primary +color: "#22C55E" +temperature: 0.3 +tools: + "*": false + read: true + glob: true + grep: true + webfetch: true + task: true + skill: true +--- + +You are a Socratic programming tutor. Your purpose is to guide the user's understanding through thoughtful questions and exploration, not to provide direct answers or write code for them. + +You are running inside an AI coding system as a primary agent mode. Users switch to you when they want to learn rather than just get things done. + +## Core Principles + +1. **Guide, don't answer**: Ask "How would you approach this?" instead of solving +2. **Socratic questioning**: Use "What do you notice?", "What happens if...?", "What evidence supports that?" +3. **Emphasize fundamentals**: Highlight underlying principles, not just syntax or implementation details +4. **Scaffold learning**: Provide progressively specific hints when the user is stuck, not solutions + +## Teaching Strategies + +### Starting a Topic +- Ask what the user already knows or expects +- Identify their mental model before exploring code +- Frame the exploration as a joint investigation + +### During Exploration +- Point to relevant code/docs but ask the user to interpret what they see +- Use "What do you notice about...?" questions +- Ask the user to predict behavior before revealing it +- Connect new concepts to things they already understand + +### When They're Stuck +- Offer a small hint, not the answer +- Break the problem into smaller questions +- Ask what specific part is confusing +- Suggest a simpler related example to reason through first + +### Building Understanding +- Ask the user to explain their understanding back to you +- Have them trace through code mentally or on paper +- Encourage them to form hypotheses and test them +- Celebrate correct reasoning; gently redirect misconceptions + +## Response Patterns + +**Good**: "What do you think this function is responsible for, based on its name and parameters?" + +**Good**: "You mentioned JWT tokens. Where would you expect to find token validation logic? Let's check your hypothesis." + +**Good**: "That's a solid observation about the middleware. What implications does that have for how errors are handled?" + +**Avoid**: "Here's how authentication works: [explanation]" + +**Avoid**: "The answer is X because Y." + +**Avoid**: Writing or suggesting code solutions directly. + +## When Asked to "Just Tell Me" + +Acknowledge their frustration, but gently redirect: + +> I understand you want a quick answer, but working through this will help it stick. Let me give you a more specific hint: [focused question or smaller piece of the puzzle] + +If they insist after multiple attempts, you may provide a partial explanation while still prompting them to fill in gaps. + +## Tools Usage + +You have read-only access to explore code and fetch documentation: + +- **read/glob/grep**: Explore the local codebase to find relevant examples +- **webfetch**: Fetch documentation, tutorials, or reference material +- **task**: Delegate research to specialized agents (librarian, explore) when needed +- **skill**: Load skills for specialized knowledge + +Use these to gather context and point the user toward relevant material - but always frame findings as prompts for their analysis, not answers. + +## Providing Structure (When Asked) + +You may create: +- **Learning paths**: Ordered list of concepts to study +- **Study guides**: Key questions to answer about a topic +- **Practice exercises**: Problems for the user to solve (without solutions) +- **Concept maps**: How ideas relate to each other + +## Communication + +Use Markdown for formatting. When including code blocks, always specify the language. + +Be warm and encouraging, but not patronizing. You're a patient mentor who believes the user can figure things out with the right guidance. + +Keep responses focused. A few good questions are better than a wall of text. + +## Constraints + +- You cannot edit files or run commands that modify state +- You must not provide direct code solutions +- If the user needs code written, suggest they switch to build mode +- Your role is to develop understanding, not to complete tasks for them diff --git a/home/profiles/opencode/agent/librarian.md b/home/profiles/opencode/agents/librarian.md similarity index 100% rename from home/profiles/opencode/agent/librarian.md rename to home/profiles/opencode/agents/librarian.md diff --git a/home/profiles/opencode/command/cleanup.md b/home/profiles/opencode/commands/cleanup.md similarity index 100% rename from home/profiles/opencode/command/cleanup.md rename to home/profiles/opencode/commands/cleanup.md diff --git a/home/profiles/opencode/default.nix b/home/profiles/opencode/default.nix index b757a29..2e5c9fa 100644 --- a/home/profiles/opencode/default.nix +++ b/home/profiles/opencode/default.nix @@ -2,9 +2,14 @@ pkgs, lib, inputs, + osConfig ? { }, ... }: +let + # Check if we're running on box (for box-specific config) + isBox = (osConfig.networking.hostName or "") == "box"; +in let # Paths to agenix-decrypted secrets (same on Darwin and NixOS) githubToken = "/run/agenix/github-token"; @@ -29,10 +34,6 @@ in autoupdate = false; permission = { external_directory = "allow"; - # Restrict session-search skill to archivist only - skill = { - "session-search" = "deny"; - }; }; provider = { anthropic = { @@ -60,12 +61,13 @@ in #plugin = ["@plannotator/opencode@latest"]; }; - "opencode/themes/ayu-mirage.json".source = ./themes/ayu-mirage.json; - "opencode/agent/librarian.md".source = ./agent/librarian.md; - "opencode/agent/adversary.md".source = ./agent/adversary.md; - "opencode/agent/archivist.md".source = ./agent/archivist.md; - "opencode/command/cleanup.md".source = ./command/cleanup.md; - "opencode/tool/search-history.ts".source = ./tool/search-history.ts; - "opencode/skill/session-search/SKILL.md".source = ./skill/session-search/SKILL.md; + "opencode/themes".source = ./themes; + "opencode/agents".source = ./agents; + "opencode/commands".source = ./commands; + "opencode/skills".source = ./skills; + }; + + home.file = lib.mkIf isBox { + "usr/.opencode/agents.md".source = ./agents/box.md; }; } diff --git a/home/profiles/opencode/skill/session-search/SKILL.md b/home/profiles/opencode/skill/session-search/SKILL.md deleted file mode 100644 index ae034dc..0000000 --- a/home/profiles/opencode/skill/session-search/SKILL.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -name: session-search -description: Advanced strategies for searching OpenCode session history. Restricted to the archivist agent. ---- - -# Session Search Skill - -This skill provides advanced strategies for searching through OpenCode's session history storage. - -## Storage Structure - -OpenCode stores data in `~/.local/share/opencode/storage/`: - -``` -storage/ -├── session/ # Session metadata by project -│ └── {projectHash}/ -│ └── ses_*.json # Session info (title, directory, timestamps) -├── message/ # Messages organized by session -│ └── ses_*/ -│ └── msg_*.json # Message metadata (role, agent, model) -├── part/ # Actual message content -│ └── msg_*/ -│ └── prt_*.json # Content parts (text, tool calls) -└── project/ # Project metadata - └── {hash}.json # Worktree path, timestamps -``` - -## Search Tool Usage - -The `search-history` tool accepts: -- `query`: Text pattern to search for (searches message content) -- `directory`: Optional filter by project path (partial match) -- `limit`: Max results (default 30) - -### Examples - -```typescript -// Find all sessions mentioning "authentication" -search-history({ query: "authentication" }) - -// Find sessions in a specific project -search-history({ query: "database", directory: "myproject" }) - -// List recent sessions (empty query) -search-history({ query: "", limit: 20 }) -``` - -## Search Strategies - -### 1. Keyword Expansion -Don't just search for the exact term. Try synonyms and related concepts: -- "auth" → also try "login", "authentication", "jwt", "token" -- "database" → also try "postgres", "sqlite", "db", "migration" -- "api" → also try "endpoint", "route", "handler" - -### 2. Code Pattern Search -Search for code-specific patterns: -- Function names: `handleAuth`, `validateToken` -- File paths: `src/auth`, `lib/database` -- Import statements: `import.*prisma` -- Error messages: specific error text - -### 3. Tool Usage Search -Find when specific tools were used: -- Edit operations: search for file paths that were edited -- Bash commands: search for command names -- Specific operations: "git push", "npm install" - -### 4. Directory Filtering -Use the `directory` parameter to scope searches: -- Filter by project name: `directory: "myapp"` -- Filter by path segment: `directory: "usr/projects"` - -### 5. Iterative Refinement -1. Start with broad search -2. If too many results, add specificity -3. If no results, broaden or try alternative terms -4. Cross-reference multiple searches - -## Understanding Results - -### Session Info -- `id`: Unique session identifier (can be used to reference) -- `title`: Auto-generated session title -- `directory`: Project worktree path -- `updated`: Last activity timestamp - -### Content Matches -- `sessionID`: Which session contains this match -- `snippet`: Context around the match (±100 chars) -- `role`: user/assistant/tool - -## Tips - -1. **Recent vs Relevant**: The tool returns recent sessions first. Older but more relevant sessions may be further in results. - -2. **Title Search**: Session titles are auto-generated from the conversation and can be good search targets. - -3. **Multiple Searches**: Don't hesitate to run multiple searches with different terms to build a complete picture. - -4. **Context Matters**: The snippet provides limited context. Session titles and directories help understand the broader context. - -5. **No Results**: If no results found, the pattern may be too specific. Try shorter or more general terms. diff --git a/home/profiles/opencode/skills/tmux/SKILL.md b/home/profiles/opencode/skills/tmux/SKILL.md new file mode 100644 index 0000000..d0441a5 --- /dev/null +++ b/home/profiles/opencode/skills/tmux/SKILL.md @@ -0,0 +1,97 @@ +--- +name: tmux +description: Instructions for using tmux to spawn multiple processes, inspect them, and capture their output. Useful for running servers or long-running tasks in the background. +allowed-tools: + - Bash +--- + +# Tmux Skill + +This skill empowers you to manage multiple concurrent processes (like servers, watchers, or long builds) using `tmux` directly from the `Bash` tool. + +Since you are likely already running inside a tmux session, you can spawn new windows or panes to handle these tasks without blocking your main communication channel. + +## 1. Verify Environment & Check Status + +First, verify you are running inside tmux: + +```bash +echo $TMUX +``` + +If this returns empty, you are not running inside tmux and these commands will not work as expected. + +Once verified, check your current windows: + +```bash +tmux list-windows +``` + +## 2. Spawn a Background Process + +To run a command (e.g., a dev server) in a way that persists and can be inspected: + +1. **Create a new detached window** with a specific name. This keeps it isolated and easy to reference. + + ```bash + tmux new-window -n "server-log" -d + ``` + + _(Replace "server-log" with a relevant name for your task)_ + +2. **Send the command** to that window. + ```bash + tmux send-keys -t "server-log" "npm start" C-m + ``` + _(`C-m` simulates the Enter key)_ + +## 3. Inspect Output (Read Logs) + +You can read the output of that pane at any time without switching your context. + +**Get the current visible screen:** + +```bash +tmux capture-pane -p -t "server-log" +``` + +**Get the entire history (scrollback):** + +```bash +tmux capture-pane -p -S - -t "server-log" +``` + +_Use this if the output might have scrolled off the screen._ + +## 4. Interact with the Process + +If you need to stop or restart the process: + +**Send Ctrl+C (Interrupt):** + +```bash +tmux send-keys -t "server-log" C-c +``` + +**Kill the window (Clean up):** + +```bash +tmux kill-window -t "server-log" +``` + +## 5. Advanced: Chaining Commands + +You can chain multiple tmux commands in a single invocation using `';'` (note the quotes to avoid interpretation by the shell). This is faster and cleaner than running multiple `tmux` commands. + +Example: Create window and start process in one go: + +```bash +tmux new-window -n "server-log" -d ';' send-keys -t "server-log" "npm start" C-m +``` + +## Summary of Pattern + +1. `tmux new-window -n "ID" -d` +2. `tmux send-keys -t "ID" "CMD" C-m` +3. `tmux capture-pane -p -t "ID"` + diff --git a/home/profiles/opencode/tool/search-history.ts b/home/profiles/opencode/tool/search-history.ts deleted file mode 100644 index 4c287ef..0000000 --- a/home/profiles/opencode/tool/search-history.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { tool } from "@opencode-ai/plugin" -import { $ } from "bun" -import { readdir, readFile } from "fs/promises" -import { join } from "path" -import { homedir } from "os" - -const STORAGE_PATH = join(homedir(), ".local/share/opencode/storage") - -interface SessionInfo { - id: string - title: string - directory: string - projectID: string - created: number - updated: number -} - -interface MessageMatch { - sessionID: string - messageID: string - snippet: string - role: string - timestamp?: number -} - -interface SearchResult { - sessions: SessionInfo[] - matches: MessageMatch[] - totalMatches: number -} - -async function getSessionInfo(sessionID: string): Promise { - try { - // Sessions are stored in directories named by project hash - const sessionDirs = await readdir(join(STORAGE_PATH, "session")) - for (const dir of sessionDirs) { - const sessionPath = join(STORAGE_PATH, "session", dir) - const files = await readdir(sessionPath).catch(() => []) - for (const file of files) { - if (file.startsWith(sessionID) || file.includes(sessionID)) { - const content = await readFile(join(sessionPath, file), "utf-8") - const data = JSON.parse(content) - return { - id: data.id, - title: data.title || "Untitled", - directory: data.directory || "", - projectID: data.projectID || "", - created: data.time?.created || 0, - updated: data.time?.updated || 0, - } - } - } - } - } catch { - // Fall back to searching message directories - } - return null -} - -async function searchWithRipgrep( - pattern: string, - directory?: string, - limit: number = 50 -): Promise { - const matches: MessageMatch[] = [] - const sessionIDs = new Set() - - // Search through part storage (contains actual message content) - const partPath = join(STORAGE_PATH, "part") - - try { - // Use ripgrep to search JSON files, extracting context around matches - const rgResult = await $`rg -i -l ${pattern} ${partPath} --type json 2>/dev/null || true`.text() - const matchingFiles = rgResult.trim().split("\n").filter(Boolean) - - for (const file of matchingFiles.slice(0, limit * 2)) { - try { - const content = await readFile(file, "utf-8") - const data = JSON.parse(content) - - // Filter by directory if specified - if (directory) { - const sessionInfo = await getSessionInfo(data.sessionID) - if (sessionInfo && !sessionInfo.directory.includes(directory)) { - continue - } - } - - // Extract snippet around the match - const text = data.text || data.content || JSON.stringify(data) - const lowerText = text.toLowerCase() - const lowerPattern = pattern.toLowerCase() - const matchIndex = lowerText.indexOf(lowerPattern) - - if (matchIndex !== -1) { - const start = Math.max(0, matchIndex - 100) - const end = Math.min(text.length, matchIndex + pattern.length + 100) - const snippet = (start > 0 ? "..." : "") + - text.slice(start, end) + - (end < text.length ? "..." : "") - - matches.push({ - sessionID: data.sessionID, - messageID: data.messageID, - snippet: snippet.replace(/\n/g, " ").trim(), - role: data.role || "unknown", - timestamp: data.time?.created, - }) - - sessionIDs.add(data.sessionID) - - if (matches.length >= limit) break - } - } catch { - // Skip files that can't be parsed - } - } - } catch (e) { - // ripgrep not found or error - } - - // Also search message metadata for titles - const messagePath = join(STORAGE_PATH, "message") - try { - const rgResult = await $`rg -i -l ${pattern} ${messagePath} --type json 2>/dev/null || true`.text() - const matchingFiles = rgResult.trim().split("\n").filter(Boolean) - - for (const file of matchingFiles.slice(0, 20)) { - try { - const content = await readFile(file, "utf-8") - const data = JSON.parse(content) - if (data.sessionID) { - sessionIDs.add(data.sessionID) - } - } catch { - // Skip - } - } - } catch { - // Ignore errors - } - - // Get session info for all matched sessions - const sessions: SessionInfo[] = [] - for (const sessionID of sessionIDs) { - const info = await getSessionInfo(sessionID) - if (info) { - // Apply directory filter for sessions too - if (!directory || info.directory.includes(directory)) { - sessions.push(info) - } - } - } - - // Sort sessions by most recent - sessions.sort((a, b) => b.updated - a.updated) - - return { - sessions: sessions.slice(0, 20), - matches: matches.slice(0, limit), - totalMatches: matches.length, - } -} - -async function listRecentSessions( - directory?: string, - limit: number = 20 -): Promise { - const sessions: SessionInfo[] = [] - - try { - const sessionDirs = await readdir(join(STORAGE_PATH, "session")) - - for (const dir of sessionDirs) { - const sessionPath = join(STORAGE_PATH, "session", dir) - const files = await readdir(sessionPath).catch(() => []) - - for (const file of files) { - if (!file.endsWith(".json")) continue - try { - const content = await readFile(join(sessionPath, file), "utf-8") - const data = JSON.parse(content) - - if (directory && !data.directory?.includes(directory)) { - continue - } - - sessions.push({ - id: data.id, - title: data.title || "Untitled", - directory: data.directory || "", - projectID: data.projectID || "", - created: data.time?.created || 0, - updated: data.time?.updated || 0, - }) - } catch { - // Skip invalid files - } - } - } - } catch { - // Storage doesn't exist - } - - sessions.sort((a, b) => b.updated - a.updated) - return sessions.slice(0, limit) -} - -export default tool({ - description: - "Search through OpenCode session history to find past conversations, code changes, and decisions. Use this to find relevant context from previous sessions.", - args: { - query: tool.schema - .string() - .describe( - "Search pattern to find in session history. Searches message content, titles, and tool outputs." - ), - directory: tool.schema - .string() - .optional() - .describe( - "Optional: Filter results to sessions from a specific project directory path (partial match)" - ), - limit: tool.schema - .number() - .optional() - .describe("Maximum number of matches to return (default: 30)"), - }, - async execute(args) { - const limit = args.limit || 30 - - if (!args.query || args.query.trim() === "") { - // List recent sessions if no query - const sessions = await listRecentSessions(args.directory, limit) - return JSON.stringify( - { - type: "recent_sessions", - sessions, - message: `Found ${sessions.length} recent sessions${args.directory ? ` in ${args.directory}` : ""}`, - }, - null, - 2 - ) - } - - const results = await searchWithRipgrep(args.query, args.directory, limit) - - // Format output for the agent - let output = `## Search Results for "${args.query}"\n\n` - - if (results.sessions.length > 0) { - output += `### Relevant Sessions (${results.sessions.length})\n\n` - for (const session of results.sessions) { - const date = new Date(session.updated).toLocaleDateString() - output += `- **${session.title}** (${session.id})\n` - output += ` - Directory: \`${session.directory}\`\n` - output += ` - Last updated: ${date}\n\n` - } - } - - if (results.matches.length > 0) { - output += `### Content Matches (${results.totalMatches})\n\n` - for (const match of results.matches.slice(0, 15)) { - output += `**Session:** ${match.sessionID}\n` - output += `> ${match.snippet}\n\n` - } - } - - if (results.sessions.length === 0 && results.matches.length === 0) { - output += `No matches found for "${args.query}"${args.directory ? ` in ${args.directory}` : ""}\n` - } - - return output - }, -}) diff --git a/hosts/box/default.nix b/hosts/box/default.nix index e5120bf..fa1def4 100644 --- a/hosts/box/default.nix +++ b/hosts/box/default.nix @@ -10,7 +10,7 @@ ../profiles/monitoring ../profiles/nfs ../profiles/gonic - ../profiles/headphones + # ../profiles/headphones ../profiles/radicale # ../profiles/seafile # waiting for https://github.com/NixOS/nixpkgs/pull/249523 to be merged ../profiles/syncthing @@ -20,6 +20,7 @@ ../profiles/finance ../profiles/sync/website ../profiles/sync/music + ../profiles/sync/tv # ../profiles/grasp # private repo - disabled # ../profiles/archivebox # requires insecure django - fix in flake.nix permittedInsecurePackages ../profiles/woodpecker-agent @@ -31,6 +32,7 @@ ../profiles/transmission ../profiles/raven ../profiles/radicle-node + ../profiles/opencode-server # ../profiles/postgres_upgrade_script # one-time use ]; diff --git a/hosts/curve/default.nix b/hosts/curve/default.nix index 4522579..3f6559e 100644 --- a/hosts/curve/default.nix +++ b/hosts/curve/default.nix @@ -12,6 +12,7 @@ ../profiles/mimetypes ../profiles/syncthing ../profiles/mossnet-hosts + ../profiles/opencode-server # ../profiles/fly-wg # ../profiles/kuberenetes # ../profiles/mount-mossnet diff --git a/hosts/helix/default.nix b/hosts/helix/default.nix index 18f933f..e77bc35 100644 --- a/hosts/helix/default.nix +++ b/hosts/helix/default.nix @@ -11,14 +11,15 @@ ../profiles/core ../profiles/server # ../profiles/metrics - # ../profiles/gitea # Replaced by radicle - ../profiles/radicle-seed + ../profiles/gitea + # ../profiles/radicle-seed # ../profiles/woodpecker-server ../profiles/rss-bridge # ../profiles/mount-mossnet - ../profiles/freshrss - ../profiles/microbin - ../profiles/site + # ../profiles/freshrss + # ../profiles/microbin + # TODO: re-enable after basant pyproject fix for 25.11 + # ../profiles/site # ../profiles/postgres_upgrade_script ]; diff --git a/hosts/profiles/immich/default.nix b/hosts/profiles/immich/default.nix index 546c669..6c18dfd 100644 --- a/hosts/profiles/immich/default.nix +++ b/hosts/profiles/immich/default.nix @@ -5,6 +5,7 @@ package = pkgs.unstable.immich; database = { enable = true; + enableVectorChord = true; # 25.11: Use VectorChord instead of pgvecto-rs }; host = "0.0.0.0"; port = 8567; diff --git a/hosts/profiles/jacket/default.nix b/hosts/profiles/jacket/default.nix index b362a29..2ee3962 100644 --- a/hosts/profiles/jacket/default.nix +++ b/hosts/profiles/jacket/default.nix @@ -1,4 +1,9 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: { services.jackett = { enable = true; @@ -17,9 +22,9 @@ ''; }; }; + services.lidarr = { enable = true; - user = "headphones"; group = "audio"; }; services.nginx.virtualHosts."lidarr.mossnet.lan" = { @@ -32,4 +37,35 @@ ''; }; }; + + services.sonarr = { + enable = true; + group = "video"; + }; + services.nginx.virtualHosts."sonarr.mossnet.lan" = { + enableACME = false; + forceSSL = false; + + locations."/" = { + extraConfig = '' + proxy_pass http://127.0.0.1:8989/; + ''; + }; + }; + + services.jellyseerr = { + enable = true; + # group = "video"; + }; + services.nginx.virtualHosts."seerr.mossnet.lan" = { + enableACME = false; + forceSSL = false; + + locations."/" = { + extraConfig = '' + proxy_pass http://127.0.0.1:5055/; + ''; + }; + }; + } diff --git a/hosts/profiles/jellyfin/default.nix b/hosts/profiles/jellyfin/default.nix index dc85ca6..d4cba3a 100644 --- a/hosts/profiles/jellyfin/default.nix +++ b/hosts/profiles/jellyfin/default.nix @@ -6,13 +6,13 @@ }: { # Enable Hardware Acceleration for transcoding - # Note: vaapiIntel override with enableHybridCodec should be in flake.nix overlay if needed + # Note: intel-vaapi-driver override with enableHybridCodec should be in flake.nix overlay if needed hardware.graphics = { enable = true; extraPackages = with pkgs; [ intel-media-driver - vaapiIntel - vaapiVdpau + intel-vaapi-driver # renamed from vaapiIntel in 25.11 + libva-vdpau-driver # renamed from vaapiVdpau in 25.11 libvdpau-va-gl intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in) ]; @@ -37,6 +37,6 @@ enableACME = false; locations."/".proxyPass = "http://localhost:8096/"; }; - }; + }; }; } diff --git a/hosts/profiles/matrix/compress-state-service.nix b/hosts/profiles/matrix/compress-state-service.nix index 30b9006..e03811b 100644 --- a/hosts/profiles/matrix/compress-state-service.nix +++ b/hosts/profiles/matrix/compress-state-service.nix @@ -1,10 +1,10 @@ { pkgs, ... }: { - environment.systemPackages = [ pkgs.matrix-synapse-tools.rust-synapse-compress-state ]; + environment.systemPackages = [ pkgs.rust-synapse-compress-state ]; systemd.services.compress-matrix-state = { serviceConfig.Type = "oneshot"; path = [ - pkgs.matrix-synapse-tools.rust-synapse-compress-state + pkgs.rust-synapse-compress-state ]; script = '' synapse_auto_compressor -p "host=/run/postgresql port=5432 user=matrix-synapse dbname=matrix-synapse" -n 2000000 -c 10000 diff --git a/hosts/profiles/matrix/default.nix b/hosts/profiles/matrix/default.nix index d23273a..503540d 100644 --- a/hosts/profiles/matrix/default.nix +++ b/hosts/profiles/matrix/default.nix @@ -1,10 +1,16 @@ -{ self, config, lib, pkgs, ... }: +{ + self, + config, + lib, + pkgs, + ... +}: { imports = [ ./mautrix-telegram.nix - ./mautrix-services.nix - # ./mautrix-discord.nix + # ./mautrix-discord.nix # Native NixOS 25.11 module (replaces nix-matrix-appservices) + # ./mautrix-services.nix # Old nix-matrix-appservices - discord moved to native module # ./mautrix-whatsapp.nix # ./mautrix-slack.nix # ./mautrix-signal.nix @@ -18,30 +24,37 @@ age.secrets.synapse-config.owner = "matrix-synapse"; systemd.services.matrix-synapse.serviceConfig.TimeoutStartSec = "10min"; - + services.matrix-synapse = { enable = true; settings = { max_upload_size = "100M"; server_name = "sealight.xyz"; - federation_sender_instances = []; + federation_sender_instances = [ ]; listeners = [ { port = 8448; tls = false; - resources = [{ - compress = true; - names = [ "client" "federation" ]; - }]; + resources = [ + { + compress = true; + names = [ + "client" + "federation" + ]; + } + ]; } { port = 9090; type = "metrics"; bind_addresses = [ "0.0.0.0" ]; - resources = [{ - compress = false; - names = [ ]; - }]; + resources = [ + { + compress = false; + names = [ ]; + } + ]; tls = false; } ]; @@ -52,10 +65,9 @@ # chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/discord-registration.yaml # "/var/lib/matrix-synapse/telegram-registration.yaml" "/var/lib/matrix-synapse/signal-registration.yaml" - #"/var/lib/matrix-as-whatsapp/whatsapp-registration.yaml" - "/var/lib/matrix-as-discord/discord-registration.yaml" + # Discord now uses native module with registerToSynapse = true (auto-registers) + # "/var/lib/matrix-as-discord/discord-registration.yaml" # "/var/lib/matrix-synapse/slack-registration.yaml" - # "/var/lib/matrix-synapse/discord-registration.yaml" # "/var/lib/matrix-synapse/whatsapp-registration.yaml" ]; turn_uris = [ @@ -67,7 +79,7 @@ # '' # max_upload_size: "50M" # use_presence: false - # registration_shared_secret: "hD9HQGTTDxp0mQsQ5JDsfudWMDiubmZENOgPchIvfBvUlPxlvQSvjoO4wn2L1seU"; + # registration_shared_secret: "hD9HQGTTDxp0mQsQ5JDsfudWMDiubmZENOgPchIvfBvUlPxlvQSvjoO4wn2L1seU"; # enable_registration_without_verification: true # ''; enable_metrics = true; @@ -156,10 +168,12 @@ networking.firewall = let - range = with config.services.coturn; [{ - from = min-port; - to = max-port; - }]; + range = with config.services.coturn; [ + { + from = min-port; + to = max-port; + } + ]; in { enable = true; diff --git a/hosts/profiles/matrix/mautrix-discord.nix b/hosts/profiles/matrix/mautrix-discord.nix new file mode 100644 index 0000000..1e372d5 --- /dev/null +++ b/hosts/profiles/matrix/mautrix-discord.nix @@ -0,0 +1,34 @@ +{ + self, + config, + lib, + pkgs, + ... +}: +{ + # Native NixOS 25.11 mautrix-discord module + # Replaces the nix-matrix-appservices discord configuration + services.mautrix-discord = { + enable = true; + registerToSynapse = true; + settings = { + homeserver = { + address = "https://sealight.xyz"; + domain = "sealight.xyz"; + }; + appservice = { + id = "discord"; + bot_username = "discordbridge"; + address = "http://localhost:29188"; + port = 29188; + # Uses SQLite by default, can switch to PostgreSQL: + # database = "postgresql:///mautrix-discord?host=/run/postgresql"; + }; + bridge = { + permissions = { + "@aynish:sealight.xyz" = "admin"; + }; + }; + }; + }; +} diff --git a/hosts/profiles/opencode-server/default.nix b/hosts/profiles/opencode-server/default.nix new file mode 100644 index 0000000..53825a6 --- /dev/null +++ b/hosts/profiles/opencode-server/default.nix @@ -0,0 +1,50 @@ +{ + self, + config, + pkgs, + inputs, + ... +}: + +let + opencode = inputs.llm-agents.packages.${pkgs.system}.opencode; +in +{ + systemd.services.opencode-server = { + description = "OpenCode HTTP Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + # Read the API key from the agenix secret file and export it + script = '' + export ANTHROPIC_API_KEY="$(cat /run/agenix/anthropicToken)" + exec ${opencode}/bin/opencode serve --port 4096 --hostname 0.0.0.0 + ''; + + serviceConfig = { + Type = "simple"; + WorkingDirectory = "/home/anish/usr"; + User = "anish"; + Restart = "on-failure"; + RestartSec = "10"; + + # Hardening + NoNewPrivileges = true; + PrivateTmp = true; + }; + }; + + # Open firewall port for LAN access + networking.firewall.allowedTCPPorts = [ 4096 ]; + + services.nginx = { + enable = true; + virtualHosts = { + "opencode.mossnet.lan" = { + forceSSL = false; + enableACME = false; + locations."/".proxyPass = "http://localhost:4096/"; + }; + }; + }; +} diff --git a/hosts/profiles/sync/music/get-music.sh b/hosts/profiles/sync/music/get-music.sh index ed1751e..8f511ab 100755 --- a/hosts/profiles/sync/music/get-music.sh +++ b/hosts/profiles/sync/music/get-music.sh @@ -13,7 +13,7 @@ touch "$TRACKING_FILE" # Get list of albums on remote server echo "$(date): Checking for new albums on seedbox..." >>"$LOG_FILE" -REMOTE_ALBUMS=$(rsync --dry-run --list-only "$REMOTE_HOST:$REMOTE_PATH" | grep '^d' | awk '{$1=$2=$3=$4=""; sub(/^ +/, ""); print}' | grep -v '^\.') || true +REMOTE_ALBUMS=$(rsync --dry-run --list-only "$REMOTE_HOST:$REMOTE_PATH" | grep '^d' | awk '{$1=$2=$3=$4=""; sub(/^ +/, ""); print}' | grep -v '^\.' | grep -v '^tv-sonarr$') || true if [ -z "$REMOTE_ALBUMS" ]; then echo "$(date): No albums found on remote server" >>"$LOG_FILE" diff --git a/hosts/profiles/sync/tv/default.nix b/hosts/profiles/sync/tv/default.nix new file mode 100644 index 0000000..d6f0b52 --- /dev/null +++ b/hosts/profiles/sync/tv/default.nix @@ -0,0 +1,23 @@ +{ pkgs, lib, ... }: + +{ + systemd.services.get-tv-sync = { + serviceConfig.Type = "oneshot"; + path = [ + pkgs.coreutils + pkgs.openssh + pkgs.gawk + pkgs.rsync + pkgs.curl + ]; + script = builtins.readFile ./get-tv.sh; + serviceConfig = { + User = "anish"; + }; + }; + systemd.timers.get-tv-sync = { + wantedBy = [ "timers.target" ]; + partOf = [ "get-tv-sync.service" ]; + timerConfig.OnCalendar = [ "hourly" ]; + }; +} diff --git a/hosts/profiles/sync/tv/get-tv.sh b/hosts/profiles/sync/tv/get-tv.sh new file mode 100644 index 0000000..6253168 --- /dev/null +++ b/hosts/profiles/sync/tv/get-tv.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +set -euo pipefail + +REMOTE_HOST="aynish@talos.feralhosting.com" +REMOTE_PATH="private/transmission/data/tv-sonarr/" +LOCAL_PATH="/tank/media/tv" +TRACKING_FILE="/tank/media/tv/.downloaded_shows" +LOG_FILE="/tank/media/tv/download-log" + +# Create local directory and tracking file if they don't exist +mkdir -p "$LOCAL_PATH" +touch "$TRACKING_FILE" + +# Get list of shows on remote server +echo "$(date): Checking for new TV shows on seedbox..." >>"$LOG_FILE" +REMOTE_SHOWS=$(rsync --dry-run --list-only "$REMOTE_HOST:$REMOTE_PATH" | grep '^d' | awk '{$1=$2=$3=$4=""; sub(/^ +/, ""); print}' | grep -v '^\.') || true + +if [ -z "$REMOTE_SHOWS" ]; then + echo "$(date): No shows found on remote server" >>"$LOG_FILE" + exit 0 +fi + +# Check each show against tracking file +NEW_SHOWS="" +while IFS= read -r show; do + if [ -n "$show" ] && ! grep -qF "$show" "$TRACKING_FILE"; then + NEW_SHOWS="$NEW_SHOWS$show\n" + echo "$(date): Found new show: $show" >>"$LOG_FILE" + fi +done <<<"$REMOTE_SHOWS" + +if [ -z "$NEW_SHOWS" ]; then + echo "$(date): No new shows to download" >>"$LOG_FILE" + exit 0 +fi + +# Download new shows only +echo "$(date): Starting download of new shows..." >>"$LOG_FILE" +while IFS= read -r show; do + if [ -n "$show" ]; then + echo "$(date): Downloading $show" >>"$LOG_FILE" + # Set umask to allow group read/write access for Jellyfin + umask 002 + if rsync -r --log-file="$LOG_FILE" "$REMOTE_HOST:$REMOTE_PATH$show/" "$LOCAL_PATH/$show/"; then + echo "$show" >>"$TRACKING_FILE" + echo "$(date): Successfully downloaded $show" >>"$LOG_FILE" + else + echo "$(date): Failed to download $show" >>"$LOG_FILE" + fi + fi +done <<<"$(echo -e "$NEW_SHOWS")" + +# Trigger Jellyfin library scan +echo "$(date): Triggering Jellyfin library refresh..." >>"$LOG_FILE" +if curl -s -X POST "http://localhost:8096/Library/Refresh" \ + -H "X-Emby-Token: aef1b1e0cd5445dc97b755ef8c6224e5"; then + echo "$(date): Jellyfin library refresh triggered" >>"$LOG_FILE" +else + echo "$(date): Failed to trigger Jellyfin library refresh" >>"$LOG_FILE" +fi + +echo "$(date): TV sync completed" >>"$LOG_FILE" diff --git a/hosts/profiles/transmission/default.nix b/hosts/profiles/transmission/default.nix index e574f67..413d954 100644 --- a/hosts/profiles/transmission/default.nix +++ b/hosts/profiles/transmission/default.nix @@ -3,6 +3,7 @@ environment.systemPackages = [ pkgs.beets ]; services.transmission = { enable = true; + package = pkgs.transmission_4; # 25.11: transmission_3 removed, explicitly use v4 settings = { rpc-enabled = true; rpc-bind-address = "0.0.0.0"; diff --git a/overlays/default.nix b/overlays/default.nix index 397ccb0..5ba9c33 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -19,29 +19,29 @@ # patching whitespace with sed is insane headphones = prev.headphones.overrideAttrs (prevAttrs: rec { patchPhase = '' - sed -i '1395s/^\([[:space:]]*\).*/\1"cat": "3000",/' headphones/searcher.py + sed -i '1395s/^\([[:space:]]*\).*/\1"cat": "3000",/' headphones/searcher.py ''; }); wallabag = prev.wallabag.overrideAttrs (attrs: { - patches = builtins.filter (patch: builtins.baseNameOf patch != "wallabag-data.patch") attrs.patches ++ [ - # Out of the box, Wallabag wants to write to various subdirectories of the project directory. - # Let’s replace references to such paths with designated systemd locations - # so that the project source can remain immutable. - ../pkgs/wallabag-data.patch - ]; + patches = + builtins.filter (patch: builtins.baseNameOf patch != "wallabag-data.patch") attrs.patches + ++ [ + # Out of the box, Wallabag wants to write to various subdirectories of the project directory. + # Let’s replace references to such paths with designated systemd locations + # so that the project source can remain immutable. + ../pkgs/wallabag-data.patch + ]; }); olm = prev.olm.overrideAttrs (attrs: { - knownVulnerabilities = []; + knownVulnerabilities = [ ]; }); - mautrix-discord = prev.mautrix-discord.overrideAttrs (attrs: rec { - license = ""; - }); + # mautrix-discord overlay removed - now using native NixOS 25.11 module # Need to do server and agent too, maybe - # woodpecker-cli-next = + # woodpecker-cli-next = # let # common = prev.callPackage "${inputs.unstable}/pkgs/development/tools/continuous-integration/woodpecker/common.nix" { }; # in diff --git a/pkgs/fennel-ls.nix b/pkgs/fennel-ls.nix index 3c8ba78..188c435 100644 --- a/pkgs/fennel-ls.nix +++ b/pkgs/fennel-ls.nix @@ -9,7 +9,7 @@ pkgs.stdenv.mkDerivation rec { }; buildInputs = with pkgs; [ lua - fennel + luaPackages.fennel # renamed from fennel in 25.11 ]; configurePhase = ''