idk some stuff, mostly 25.11 and opencode stuff

This commit is contained in:
Anish Lakhwara
2026-02-01 23:39:21 -08:00
parent cd8bb0fe0f
commit 615ea7b026
33 changed files with 992 additions and 914 deletions
Generated
+98 -81
View File
@@ -1,5 +1,21 @@
{ {
"nodes": { "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": { "agenix": {
"inputs": { "inputs": {
"darwin": "darwin", "darwin": "darwin",
@@ -10,11 +26,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1754433428, "lastModified": 1762618334,
"narHash": "sha256-NA/FT2hVhKDftbHSwVnoRTFhes62+7dxZbxj5Gxvghs=", "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=",
"owner": "ryantm", "owner": "ryantm",
"repo": "agenix", "repo": "agenix",
"rev": "9edb1787864c4f59ae5074ad498b6272b3ec308d", "rev": "fcdea223397448d35d9b31f798479227e80183f6",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -84,11 +100,11 @@
"systems": "systems_5" "systems": "systems_5"
}, },
"locked": { "locked": {
"lastModified": 1767386128, "lastModified": 1769353768,
"narHash": "sha256-BJDu7dIMauO2nYRSL4aI8wDNtEm2KOb7lDKP3hxdrpo=", "narHash": "sha256-zI+7cbMI4wMIR57jMjDSEsVb3grapTnURDxxJPYFIW0=",
"owner": "numtide", "owner": "numtide",
"repo": "blueprint", "repo": "blueprint",
"rev": "0ed984d51a3031065925ab08812a5434f40b93d4", "rev": "c7da5c70ad1c9b60b6f5d4f674fbe205d48d8f6c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -100,16 +116,16 @@
"brew-src": { "brew-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1758543057, "lastModified": 1763638478,
"narHash": "sha256-lw3V2jOGYphUFHYQ5oARcb6urlbNpUCLJy1qhsGdUmc=", "narHash": "sha256-n/IMowE9S23ovmTkKX7KhxXC2Yq41EAVFR2FBIXPcT8=",
"owner": "Homebrew", "owner": "Homebrew",
"repo": "brew", "repo": "brew",
"rev": "5b236456eb93133c2bd0d60ef35ed63f1c0712f6", "rev": "fbfdbaba008189499958a7aeb1e2c36ab10c067d",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "Homebrew", "owner": "Homebrew",
"ref": "4.6.12", "ref": "5.0.3",
"repo": "brew", "repo": "brew",
"type": "github" "type": "github"
} }
@@ -143,16 +159,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1757432263, "lastModified": 1767634391,
"narHash": "sha256-qHn+/0+IOz5cG68BZUwL9BV3EO/e9eNKCjH3+N7wMdI=", "narHash": "sha256-owcSz2ICqTSvhBbhPP+1eWzi88e54rRZtfCNE5E/wwg=",
"owner": "LnL7", "owner": "LnL7",
"repo": "nix-darwin", "repo": "nix-darwin",
"rev": "1fef4404de4d1596aa5ab2bd68078370e1b9dcdb", "rev": "08585aacc3d6d6c280a02da195fdbd4b9cf083c2",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "LnL7", "owner": "LnL7",
"ref": "nix-darwin-25.05", "ref": "nix-darwin-25.11",
"repo": "nix-darwin", "repo": "nix-darwin",
"type": "github" "type": "github"
} }
@@ -166,11 +182,11 @@
"utils": "utils" "utils": "utils"
}, },
"locked": { "locked": {
"lastModified": 1756719547, "lastModified": 1766051518,
"narHash": "sha256-N9gBKUmjwRKPxAafXEk1EGadfk2qDZPBQp4vXWPHINQ=", "narHash": "sha256-znKOwPXQnt3o7lDb3hdf19oDo0BLP4MfBOYiWkEHoik=",
"owner": "serokell", "owner": "serokell",
"repo": "deploy-rs", "repo": "deploy-rs",
"rev": "125ae9e3ecf62fb2c0fd4f2d894eb971f1ecaed2", "rev": "d5eff7f948535b9c723d60cd8239f8f11ddc90fa",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -238,11 +254,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1758287904, "lastModified": 1768923567,
"narHash": "sha256-IGmaEf3Do8o5Cwp1kXBN1wQmZwQN3NLfq5t4nHtVtcU=", "narHash": "sha256-GVJ0jKsyXLuBzRMXCDY6D5J8wVdwP1DuQmmvYL/Vw/Q=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "67ff9807dd148e704baadbd4fd783b54282ca627", "rev": "00395d188e3594a1507f214a2f15d4ce5c07cb28",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -458,11 +474,11 @@
"systems": "systems_6" "systems": "systems_6"
}, },
"locked": { "locked": {
"lastModified": 1694529238, "lastModified": 1731533236,
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -503,11 +519,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1754078208, "lastModified": 1763982521,
"narHash": "sha256-YVoIFDCDpYuU3riaDEJ3xiGdPOtsx4sR5eTzHTytPV8=", "narHash": "sha256-ur4QIAHwgFc0vXiaxn5No/FuZicxBr2p0gmT54xZkUQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "gomod2nix", "repo": "gomod2nix",
"rev": "7f963246a71626c7fc70b431a315c4388a0c95cf", "rev": "02e63a239d6eabd595db56852535992c898eba72",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -540,11 +556,11 @@
}, },
"hardware": { "hardware": {
"locked": { "locked": {
"lastModified": 1758663926, "lastModified": 1768736227,
"narHash": "sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk=", "narHash": "sha256-qgGq7CfrYKc3IBYQ7qp0Z/ZXndQVC5Bj0N8HW9mS2rM=",
"owner": "nixos", "owner": "nixos",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "170ff93c860b2a9868ed1e1102d4e52cb3d934e1", "rev": "d447553bcbc6a178618d37e61648b19e744370df",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -581,16 +597,16 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1758463745, "lastModified": 1768949235,
"narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=", "narHash": "sha256-TtjKgXyg1lMfh374w5uxutd6Vx2P/hU81aEhTxrO2cg=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3", "rev": "75ed713570ca17427119e7e204ab3590cc3bf2a5",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "nix-community",
"ref": "release-25.05", "ref": "release-25.11",
"repo": "home-manager", "repo": "home-manager",
"type": "github" "type": "github"
} }
@@ -614,11 +630,11 @@
"homebrew-cask": { "homebrew-cask": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1759110411, "lastModified": 1769058882,
"narHash": "sha256-wsvLofMB/1bkjY6OQjjWU80+AbQiPzZSLZ3cjsYpOAs=", "narHash": "sha256-GOSEf+DtzP/ORw+wrP1o9qZaz2XEvUOBC2j0cEEl2MY=",
"owner": "homebrew", "owner": "homebrew",
"repo": "homebrew-cask", "repo": "homebrew-cask",
"rev": "80f95de379d69edb696dd29106b2d8b077c98896", "rev": "022be3ce1e808c0196c540471933238fc2bf0815",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -630,11 +646,11 @@
"homebrew-core": { "homebrew-core": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1759116320, "lastModified": 1769064658,
"narHash": "sha256-FGqC/WlIJnMkhV7l6XK6r5AqUYkwTHvHBCUjFe3DUUY=", "narHash": "sha256-xT3S9geUhs4jmyuuQhsyGFfv86baCXqLID8Bvtzewpo=",
"owner": "homebrew", "owner": "homebrew",
"repo": "homebrew-core", "repo": "homebrew-core",
"rev": "0ccf924357d3eca3268d6dafb224ad9dc4f2398a", "rev": "eaa460777befeb0b457587e04e05991ea90826be",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -717,11 +733,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762951919, "lastModified": 1768986040,
"narHash": "sha256-ma/xMEGf4J6n/RdZFdxXBJUQhP53HVEPQOC6Dp2TrkQ=", "narHash": "sha256-83npNk7w9yNJfSnpdZPNUjbhQwGVef3BWyBuIe6TJfk=",
"owner": "Jovian-Experiments", "owner": "Jovian-Experiments",
"repo": "Jovian-NixOS", "repo": "Jovian-NixOS",
"rev": "3d248f6e8f877218dd2573fef8925ac997889922", "rev": "d75e3c96c9f935a6ccdd4a91209950289b2dc2fc",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -737,11 +753,11 @@
"treefmt-nix": "treefmt-nix_2" "treefmt-nix": "treefmt-nix_2"
}, },
"locked": { "locked": {
"lastModified": 1768434130, "lastModified": 1769482725,
"narHash": "sha256-4rBBs7spDuimvUcL3egp2Zh94Lk8pf00VsjkOs59h7E=", "narHash": "sha256-Y4vH4PJIz4dt8SRJ5nm6yJCDzJRVEMdE5tP2TFIUfoQ=",
"owner": "numtide", "owner": "numtide",
"repo": "llm-agents.nix", "repo": "llm-agents.nix",
"rev": "d0ed3ef68a04b5bd127fecd405baf803eea29c29", "rev": "ae0667122f9a2336b23a4b4751997fe2f161fb37",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -846,11 +862,11 @@
"brew-src": "brew-src" "brew-src": "brew-src"
}, },
"locked": { "locked": {
"lastModified": 1758598228, "lastModified": 1764473698,
"narHash": "sha256-qr60maXGbZ4FX5tejPRI3nr0bnRTnZ3AbbbfO6/6jq4=", "narHash": "sha256-C91gPgv6udN5WuIZWNehp8qdLqlrzX6iF/YyboOj6XI=",
"owner": "zhaofengli-wip", "owner": "zhaofengli-wip",
"repo": "nix-homebrew", "repo": "nix-homebrew",
"rev": "f36e5db56e117f7df701ab152d0d2036ea85218c", "rev": "6a8ab60bfd66154feeaa1021fc3b32684814a62a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -919,11 +935,11 @@
}, },
"nixos-hardware": { "nixos-hardware": {
"locked": { "locked": {
"lastModified": 1758663926, "lastModified": 1768736227,
"narHash": "sha256-6CFdj7Xs616t1W4jLDH7IohAAvl5Dyib3qEv/Uqw1rk=", "narHash": "sha256-qgGq7CfrYKc3IBYQ7qp0Z/ZXndQVC5Bj0N8HW9mS2rM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "170ff93c860b2a9868ed1e1102d4e52cb3d934e1", "rev": "d447553bcbc6a178618d37e61648b19e744370df",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1017,11 +1033,11 @@
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1768032153, "lastModified": 1769421245,
"narHash": "sha256-6kD1MdY9fsE6FgSwdnx29hdH2UcBKs3/+JJleMShuJg=", "narHash": "sha256-m5QLKjpdhbDrhyrUbEm5Haq3lqE5Z6xh2tab5vTHUTo=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "3146c6aa9995e7351a398e17470e15305e6e18ff", "rev": "5b265bda51b42a2a85af0a543c3e57b778b01b7d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1049,27 +1065,27 @@
}, },
"nixpkgs_5": { "nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1758791193, "lastModified": 1768940263,
"narHash": "sha256-F8WmEwFoHsnix7rt290R0rFXNJiMbClMZyIC/e+HYf0=", "narHash": "sha256-sJERJIYTKPFXkoz/gBaBtRKke82h4DkX3BBSsKbfbvI=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "25e53aa156d47bad5082ff7618f5feb1f5e02d01", "rev": "3ceaaa8bc963ced4d830e06ea2d0863b6490ff03",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "nixos",
"ref": "nixos-25.05", "ref": "nixos-25.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs_6": { "nixpkgs_6": {
"locked": { "locked": {
"lastModified": 1758690382, "lastModified": 1768886240,
"narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=", "narHash": "sha256-C2TjvwYZ2VDxYWeqvvJ5XPPp6U7H66zeJlRaErJKoEM=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e643668fd71b949c53f8626614b21ff71a07379d", "rev": "80e4adbcf8992d3fd27ad4964fbb84907f9478b0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1085,11 +1101,11 @@
"nixpkgs": "nixpkgs_6" "nixpkgs": "nixpkgs_6"
}, },
"locked": { "locked": {
"lastModified": 1759117007, "lastModified": 1769056321,
"narHash": "sha256-DSnkPMhK2Eg0XLiyjEPVtIpdcmWmYsBMhVGCN1144SA=", "narHash": "sha256-BR4ACqZEfFivkJutgeZeG8Sip/LlvmQgBbkT9eKZeIQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "NUR", "repo": "NUR",
"rev": "fccc09dffa659dc1410eceb33285831ba12bc7f6", "rev": "6dd879dc2cb51262567d8b2b49b2d24f256fa343",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1105,11 +1121,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762711246, "lastModified": 1759014010,
"narHash": "sha256-coOLG/Bp118d1T3DBIZUcW+AdiKsHz9uh6ZuiR30GBM=", "narHash": "sha256-NMpUufnxiGDTs/4Nxj8t+n4wc4aSs02a4T5OORo0gBQ=",
"ref": "main", "ref": "main",
"rev": "54287446e1a8a1bc40ad1b12061f053181e6d264", "rev": "08c1dddf38c39d6f7c73a24f4d396f4c925185bf",
"revCount": 1614, "revCount": 1602,
"type": "git", "type": "git",
"url": "ssh://gitea@git.sealight.xyz/aynish/kitaab" "url": "ssh://gitea@git.sealight.xyz/aynish/kitaab"
}, },
@@ -1202,11 +1218,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1759113356, "lastModified": 1769050281,
"narHash": "sha256-xm4kEUcV2jk6u15aHazFP4YsMwhq+PczA+Ul/4FDKWI=", "narHash": "sha256-1H8DN4UZgEUqPUA5ecHOufLZMscJ4IlcGaEftaPtpBY=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "be3b8843a2be2411500f6c052876119485e957a2", "rev": "6deef0585c52d9e70f96b6121207e1496d4b0c49",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1336,6 +1352,7 @@
}, },
"tangled": { "tangled": {
"inputs": { "inputs": {
"actor-typeahead-src": "actor-typeahead-src",
"flake-compat": "flake-compat_4", "flake-compat": "flake-compat_4",
"gomod2nix": "gomod2nix", "gomod2nix": "gomod2nix",
"htmx-src": "htmx-src", "htmx-src": "htmx-src",
@@ -1350,11 +1367,11 @@
"sqlite-lib-src": "sqlite-lib-src" "sqlite-lib-src": "sqlite-lib-src"
}, },
"locked": { "locked": {
"lastModified": 1759559279, "lastModified": 1769064660,
"narHash": "sha256-gA0mh9Fx2uou2v75RMA6qUvWB3Z74Asc6pRjiojwaRo=", "narHash": "sha256-2ccXZ51txbX1jAhW52Z6QuSFAgFqo3Gr1bqxD4jXNw0=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "5ecd54b31547ac169a3b15d6034a67179f22aa33", "rev": "2b94ee226637e13c106d7782ca852bd608530b51",
"revCount": 1486, "revCount": 1865,
"type": "git", "type": "git",
"url": "https://tangled.org/@tangled.org/core" "url": "https://tangled.org/@tangled.org/core"
}, },
@@ -1431,11 +1448,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1768031762, "lastModified": 1769353635,
"narHash": "sha256-b2gJDJfi+TbA7Hu2sKip+1mWqya0GJaWrrXQjpbOVTU=", "narHash": "sha256-J0G1ACrUK29M0THPAsz429eZX07GmR9Bs/b0pB3N0dQ=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "0c445aa21b01fd1d4bb58927f7b268568af87b20", "rev": "f46bb205f239b415309f58166f8df6919fa88377",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -1446,11 +1463,11 @@
}, },
"unstable": { "unstable": {
"locked": { "locked": {
"lastModified": 1762844143, "lastModified": 1768886240,
"narHash": "sha256-SlybxLZ1/e4T2lb1czEtWVzDCVSTvk9WLwGhmxFmBxI=", "narHash": "sha256-C2TjvwYZ2VDxYWeqvvJ5XPPp6U7H66zeJlRaErJKoEM=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "9da7f1cf7f8a6e2a7cb3001b048546c92a8258b4", "rev": "80e4adbcf8992d3fd27ad4964fbb84907f9478b0",
"type": "github" "type": "github"
}, },
"original": { "original": {
+9 -7
View File
@@ -8,7 +8,7 @@
inputs = { inputs = {
# Nixpkgs # Nixpkgs
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
unstable.url = "github:nixos/nixpkgs/nixos-unstable"; unstable.url = "github:nixos/nixpkgs/nixos-unstable";
nixos-hardware.url = "github:NixOS/nixos-hardware/master"; nixos-hardware.url = "github:NixOS/nixos-hardware/master";
@@ -16,7 +16,7 @@
# ngipkgs-local.url = "path:/home/anish/usr/ngipkgs"; # ngipkgs-local.url = "path:/home/anish/usr/ngipkgs";
# Home manager # 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"; home-manager.inputs.nixpkgs.follows = "nixpkgs";
hardware.url = "github:nixos/nixos-hardware"; hardware.url = "github:nixos/nixos-hardware";
@@ -44,7 +44,7 @@
# Darwin # Darwin
darwin = { darwin = {
url = "github:LnL7/nix-darwin/nix-darwin-25.05"; url = "github:LnL7/nix-darwin/nix-darwin-25.11";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nix-homebrew = { nix-homebrew = {
@@ -160,13 +160,14 @@
"SunVox" "SunVox"
"renoise" "renoise"
"bitwig-studio-unwrapped" "bitwig-studio-unwrapped"
"via" # QMK keyboard configurator
]; ];
}; };
overlays = [ overlays = [
rust-overlay.overlays.default rust-overlay.overlays.default
tidalcycles.overlays.default tidalcycles.overlays.default
agenix.overlays.default agenix.overlays.default
nur.overlay nur.overlays.default
# nix-matrix-appservices.overlay # nixpkgs has these packages and newer ones at that # nix-matrix-appservices.overlay # nixpkgs has these packages and newer ones at that
unstableOverlay unstableOverlay
vimwikiOverlay vimwikiOverlay
@@ -217,7 +218,7 @@
rust-overlay.overlays.default rust-overlay.overlays.default
tidalcycles.overlays.default tidalcycles.overlays.default
agenix.overlays.default agenix.overlays.default
nur.overlay nur.overlays.default
unstableOverlay unstableOverlay
vimwikiOverlay vimwikiOverlay
self.overlays.additions self.overlays.additions
@@ -253,7 +254,7 @@
rust-overlay.overlays.default rust-overlay.overlays.default
tidalcycles.overlays.default tidalcycles.overlays.default
agenix.overlays.default agenix.overlays.default
nur.overlay nur.overlays.default
unstableOverlay unstableOverlay
vimwikiOverlay vimwikiOverlay
self.overlays.additions self.overlays.additions
@@ -325,7 +326,8 @@
agenix.nixosModules.age agenix.nixosModules.age
self.nixosModules.backup self.nixosModules.backup
self.nixosModules.wireguard self.nixosModules.wireguard
basant.nixosModule # TODO: basant needs pyproject update for 25.11 - re-enable after fixing
# basant.nixosModule
# self.nixosModules.microbin # self.nixosModules.microbin
disko.nixosModules.disko disko.nixosModules.disko
{ {
+3 -9
View File
@@ -2,15 +2,9 @@
{ {
programs.beets = { programs.beets = {
enable = true; enable = true;
package = pkgs.beets.override { # In 25.11, beets plugins are enabled via beetsPackages or the default package
pluginOverrides = { # The default beets package includes common plugins
fetchart.enable = true; package = pkgs.beets;
embedart.enable = true;
lastgenre.enable = true;
duplicates.enable = true;
missing.enable = true;
};
};
settings = { settings = {
directory = "/tank/media/music"; directory = "/tank/media/music";
library = "/home/anish/.local/share/beets/library.db"; library = "/home/anish/.local/share/beets/library.db";
+12 -4
View File
@@ -1,6 +1,13 @@
{ lib, pkgs, config, ... }:
{ {
home.packages = with pkgs; [ lib,
pkgs,
config,
...
}:
{
home.packages =
with pkgs;
[
binutils binutils
coreutils coreutils
dnsutils dnsutils
@@ -64,6 +71,7 @@
taskwarrior-tui taskwarrior-tui
# vimwiki-cli # vimwiki-cli
zk zk
radicle-node # rad CLI for interacting with Radicle repos
(pkgs.writeScriptBin "jq-repl" '' (pkgs.writeScriptBin "jq-repl" ''
#!/usr/bin/env bash #!/usr/bin/env bash
@@ -148,7 +156,8 @@
disconnect_keyboard disconnect_keyboard
'') '')
] ++ lib.optionals pkgs.stdenv.isLinux [ ]
++ lib.optionals pkgs.stdenv.isLinux [
# Linux-only packages # Linux-only packages
iputils iputils
strace strace
@@ -255,7 +264,6 @@
unzip = "aunpack"; unzip = "aunpack";
copy = if pkgs.stdenv.isDarwin then "pbcopy" else "xclip -selection clipboard"; copy = if pkgs.stdenv.isDarwin then "pbcopy" else "xclip -selection clipboard";
paste = if pkgs.stdenv.isDarwin then "pbpaste" else "xclip -selection clipboard -o"; paste = if pkgs.stdenv.isDarwin then "pbpaste" else "xclip -selection clipboard -o";
rm = "echo USE TRASH, FOOL: trash ";
trash = "trash-put"; trash = "trash-put";
make-secret = "< /dev/urandom \\tr -dc _A-Za-z0-9 | head -c \${1:-32};echo;"; make-secret = "< /dev/urandom \\tr -dc _A-Za-z0-9 | head -c \${1:-32};echo;";
+1 -2
View File
@@ -11,6 +11,5 @@ enable_audio_bell no
# Ctrl+V for paste # Ctrl+V handled by neovim for insert mode paste (allows visual block mode in normal mode)
map ctrl+v paste_from_clipboard
mouse_map middle release ungrabbed paste_from_clipboard mouse_map middle release ungrabbed paste_from_clipboard
+19 -12
View File
@@ -12,11 +12,14 @@
unstable.git-spice unstable.git-spice
]; ];
# Delta (git diff pager) - moved to top-level in home-manager 25.11
programs.delta = {
enable = true;
enableGitIntegration = true;
};
programs.git = { programs.git = {
enable = true; enable = true;
userName = "Anish Lakhwara";
userEmail = "anish+git@lakhwara.com";
delta.enable = true;
signing = { signing = {
signByDefault = if pkgs.stdenv.isLinux then false else true; signByDefault = if pkgs.stdenv.isLinux then false else true;
key = if pkgs.stdenv.isLinux then "B8492C8FB53397B7" else "7FC5DF072EF7B716"; key = if pkgs.stdenv.isLinux then "B8492C8FB53397B7" else "7FC5DF072EF7B716";
@@ -60,14 +63,20 @@
"node_modules/" "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; pull.rebase = false;
push.autoSetupRemote = true; push.autoSetupRemote = true;
init.defaultBranch = "main"; init.defaultBranch = "main";
"url \"git@github.com:\"" = { insteadOf = "https://github.com/"; }; "url \"git@github.com:\"" = {
insteadOf = "https://github.com/";
}; };
aliases = { alias = {
a = "add -p"; a = "add -p";
co = "checkout"; co = "checkout";
cob = "checkout -b"; cob = "checkout -b";
@@ -91,12 +100,9 @@
h1rd = "hard HEAD~1"; h1rd = "hard HEAD~1";
# logging # logging
lg = lg = "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit";
"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'";
plog = tlog = "log --stat --since='1 Day Ago' --graph --pretty=oneline --abbrev-commit --date=relative";
"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"; rank = "shortlog -sn --no-merges";
authors = "authors | sort | uniq -c | sort -n"; 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"; 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";
@@ -107,4 +113,5 @@
blm = "blame -w -C -C -C"; blm = "blame -w -C -C -C";
}; };
}; };
};
} }
+41 -11
View File
@@ -1,4 +1,9 @@
{ pkgs, lib, config, ... }: {
pkgs,
lib,
config,
...
}:
let let
customPlugins = { customPlugins = {
vim-zettel = pkgs.vimUtils.buildVimPlugin { vim-zettel = pkgs.vimUtils.buildVimPlugin {
@@ -116,7 +121,8 @@ let
}; };
}; };
my-python-packages = python-packages: with python-packages; [ my-python-packages =
python-packages: with python-packages; [
tasklib tasklib
pynvim pynvim
six six
@@ -146,7 +152,8 @@ in
''; '';
# Private mode plugin for concealing buffer content (out of store symlink for live editing) # 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 ]; #environment.systemPackages = with customPlugins; [ tidal ];
programs.neovim = { programs.neovim = {
@@ -1032,8 +1039,8 @@ in
vim.keymap.set({'n', 't'}, '<M-k>', '<CMD>NavigatorUp<CR>') vim.keymap.set({'n', 't'}, '<M-k>', '<CMD>NavigatorUp<CR>')
vim.keymap.set({'n', 't'}, '<M-l>', '<CMD>NavigatorRight<CR>') vim.keymap.set({'n', 't'}, '<M-l>', '<CMD>NavigatorRight<CR>')
-- Paste from system clipboard in insert mode (handles tmux/kitty better) -- Paste from system clipboard in insert mode (Ctrl+V in normal mode remains visual block)
-- vim.keymap.set('i', '<C-v>', '<C-r><C-p>+', {noremap = true, silent = true}) vim.keymap.set('i', '<C-v>', '<C-r><C-p>+', {noremap = true, silent = true})
-- Pane resizing with Alt+Shift+hjkl (to match tmux) -- Pane resizing with Alt+Shift+hjkl (to match tmux)
vim.keymap.set('n', '<M-S-h>', '<Cmd>vertical resize -2<CR>', {silent = true}) vim.keymap.set('n', '<M-S-h>', '<Cmd>vertical resize -2<CR>', {silent = true})
@@ -1130,7 +1137,9 @@ in
zk zk
]; ];
plugins = with pkgs.vimPlugins // customPlugins; [ plugins =
with pkgs.vimPlugins // customPlugins;
[
# ui # ui
lualine-nvim lualine-nvim
fzf-vim fzf-vim
@@ -1148,7 +1157,24 @@ in
telescope-nvim telescope-nvim
plenary-nvim plenary-nvim
nvim-navic 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.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 nvim-treesitter-context
my-fterm my-fterm
barbar-nvim barbar-nvim
@@ -1222,14 +1248,18 @@ in
# opencode integration # opencode integration
opencode-nvim opencode-nvim
# vim-processing # vim-processing
] ++ lib.optionals pkgs.stdenv.isLinux [ ]
++ lib.optionals pkgs.stdenv.isLinux [
# Linux-only plugins # Linux-only plugins
vim-tidal # requires SuperCollider which is Linux-only vim-tidal # requires SuperCollider which is Linux-only
]; ];
withPython3 = true; withPython3 = true;
extraPython3Packages = pkgs: with pkgs; [ tasklib six packaging ]; extraPython3Packages =
pkgs: with pkgs; [
tasklib
six
packaging
];
vimAlias = true; vimAlias = true;
}; };
} }
-73
View File
@@ -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.
@@ -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. 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 temperature: 0.2
tools: tools:
"*": false "*": false
+41
View File
@@ -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:<port>` (from any device on the VPN)
- **LAN**: `http://mossnet.lan:<port>` (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
+106
View File
@@ -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
+13 -11
View File
@@ -2,9 +2,14 @@
pkgs, pkgs,
lib, lib,
inputs, inputs,
osConfig ? { },
... ...
}: }:
let
# Check if we're running on box (for box-specific config)
isBox = (osConfig.networking.hostName or "") == "box";
in
let let
# Paths to agenix-decrypted secrets (same on Darwin and NixOS) # Paths to agenix-decrypted secrets (same on Darwin and NixOS)
githubToken = "/run/agenix/github-token"; githubToken = "/run/agenix/github-token";
@@ -29,10 +34,6 @@ in
autoupdate = false; autoupdate = false;
permission = { permission = {
external_directory = "allow"; external_directory = "allow";
# Restrict session-search skill to archivist only
skill = {
"session-search" = "deny";
};
}; };
provider = { provider = {
anthropic = { anthropic = {
@@ -60,12 +61,13 @@ in
#plugin = ["@plannotator/opencode@latest"]; #plugin = ["@plannotator/opencode@latest"];
}; };
"opencode/themes/ayu-mirage.json".source = ./themes/ayu-mirage.json; "opencode/themes".source = ./themes;
"opencode/agent/librarian.md".source = ./agent/librarian.md; "opencode/agents".source = ./agents;
"opencode/agent/adversary.md".source = ./agent/adversary.md; "opencode/commands".source = ./commands;
"opencode/agent/archivist.md".source = ./agent/archivist.md; "opencode/skills".source = ./skills;
"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; home.file = lib.mkIf isBox {
"usr/.opencode/agents.md".source = ./agents/box.md;
}; };
} }
@@ -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.
@@ -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"`
@@ -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<SessionInfo | null> {
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<SearchResult> {
const matches: MessageMatch[] = []
const sessionIDs = new Set<string>()
// 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<SessionInfo[]> {
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
},
})
+3 -1
View File
@@ -10,7 +10,7 @@
../profiles/monitoring ../profiles/monitoring
../profiles/nfs ../profiles/nfs
../profiles/gonic ../profiles/gonic
../profiles/headphones # ../profiles/headphones
../profiles/radicale ../profiles/radicale
# ../profiles/seafile # waiting for https://github.com/NixOS/nixpkgs/pull/249523 to be merged # ../profiles/seafile # waiting for https://github.com/NixOS/nixpkgs/pull/249523 to be merged
../profiles/syncthing ../profiles/syncthing
@@ -20,6 +20,7 @@
../profiles/finance ../profiles/finance
../profiles/sync/website ../profiles/sync/website
../profiles/sync/music ../profiles/sync/music
../profiles/sync/tv
# ../profiles/grasp # private repo - disabled # ../profiles/grasp # private repo - disabled
# ../profiles/archivebox # requires insecure django - fix in flake.nix permittedInsecurePackages # ../profiles/archivebox # requires insecure django - fix in flake.nix permittedInsecurePackages
../profiles/woodpecker-agent ../profiles/woodpecker-agent
@@ -31,6 +32,7 @@
../profiles/transmission ../profiles/transmission
../profiles/raven ../profiles/raven
../profiles/radicle-node ../profiles/radicle-node
../profiles/opencode-server
# ../profiles/postgres_upgrade_script # one-time use # ../profiles/postgres_upgrade_script # one-time use
]; ];
+1
View File
@@ -12,6 +12,7 @@
../profiles/mimetypes ../profiles/mimetypes
../profiles/syncthing ../profiles/syncthing
../profiles/mossnet-hosts ../profiles/mossnet-hosts
../profiles/opencode-server
# ../profiles/fly-wg # ../profiles/fly-wg
# ../profiles/kuberenetes # ../profiles/kuberenetes
# ../profiles/mount-mossnet # ../profiles/mount-mossnet
+6 -5
View File
@@ -11,14 +11,15 @@
../profiles/core ../profiles/core
../profiles/server ../profiles/server
# ../profiles/metrics # ../profiles/metrics
# ../profiles/gitea # Replaced by radicle ../profiles/gitea
../profiles/radicle-seed # ../profiles/radicle-seed
# ../profiles/woodpecker-server # ../profiles/woodpecker-server
../profiles/rss-bridge ../profiles/rss-bridge
# ../profiles/mount-mossnet # ../profiles/mount-mossnet
../profiles/freshrss # ../profiles/freshrss
../profiles/microbin # ../profiles/microbin
../profiles/site # TODO: re-enable after basant pyproject fix for 25.11
# ../profiles/site
# ../profiles/postgres_upgrade_script # ../profiles/postgres_upgrade_script
]; ];
+1
View File
@@ -5,6 +5,7 @@
package = pkgs.unstable.immich; package = pkgs.unstable.immich;
database = { database = {
enable = true; enable = true;
enableVectorChord = true; # 25.11: Use VectorChord instead of pgvecto-rs
}; };
host = "0.0.0.0"; host = "0.0.0.0";
port = 8567; port = 8567;
+38 -2
View File
@@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }: {
config,
lib,
pkgs,
...
}:
{ {
services.jackett = { services.jackett = {
enable = true; enable = true;
@@ -17,9 +22,9 @@
''; '';
}; };
}; };
services.lidarr = { services.lidarr = {
enable = true; enable = true;
user = "headphones";
group = "audio"; group = "audio";
}; };
services.nginx.virtualHosts."lidarr.mossnet.lan" = { 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/;
'';
};
};
} }
+3 -3
View File
@@ -6,13 +6,13 @@
}: }:
{ {
# Enable Hardware Acceleration for transcoding # 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 = { hardware.graphics = {
enable = true; enable = true;
extraPackages = with pkgs; [ extraPackages = with pkgs; [
intel-media-driver intel-media-driver
vaapiIntel intel-vaapi-driver # renamed from vaapiIntel in 25.11
vaapiVdpau libva-vdpau-driver # renamed from vaapiVdpau in 25.11
libvdpau-va-gl libvdpau-va-gl
intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in) intel-compute-runtime # OpenCL filter support (hardware tonemapping and subtitle burn-in)
]; ];
@@ -1,10 +1,10 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
environment.systemPackages = [ pkgs.matrix-synapse-tools.rust-synapse-compress-state ]; environment.systemPackages = [ pkgs.rust-synapse-compress-state ];
systemd.services.compress-matrix-state = { systemd.services.compress-matrix-state = {
serviceConfig.Type = "oneshot"; serviceConfig.Type = "oneshot";
path = [ path = [
pkgs.matrix-synapse-tools.rust-synapse-compress-state pkgs.rust-synapse-compress-state
]; ];
script = '' script = ''
synapse_auto_compressor -p "host=/run/postgresql port=5432 user=matrix-synapse dbname=matrix-synapse" -n 2000000 -c 10000 synapse_auto_compressor -p "host=/run/postgresql port=5432 user=matrix-synapse dbname=matrix-synapse" -n 2000000 -c 10000
+27 -13
View File
@@ -1,10 +1,16 @@
{ self, config, lib, pkgs, ... }: {
self,
config,
lib,
pkgs,
...
}:
{ {
imports = [ imports = [
./mautrix-telegram.nix ./mautrix-telegram.nix
./mautrix-services.nix # ./mautrix-discord.nix # Native NixOS 25.11 module (replaces nix-matrix-appservices)
# ./mautrix-discord.nix # ./mautrix-services.nix # Old nix-matrix-appservices - discord moved to native module
# ./mautrix-whatsapp.nix # ./mautrix-whatsapp.nix
# ./mautrix-slack.nix # ./mautrix-slack.nix
# ./mautrix-signal.nix # ./mautrix-signal.nix
@@ -29,19 +35,26 @@
{ {
port = 8448; port = 8448;
tls = false; tls = false;
resources = [{ resources = [
{
compress = true; compress = true;
names = [ "client" "federation" ]; names = [
}]; "client"
"federation"
];
}
];
} }
{ {
port = 9090; port = 9090;
type = "metrics"; type = "metrics";
bind_addresses = [ "0.0.0.0" ]; bind_addresses = [ "0.0.0.0" ];
resources = [{ resources = [
{
compress = false; compress = false;
names = [ ]; names = [ ];
}]; }
];
tls = false; tls = false;
} }
]; ];
@@ -52,10 +65,9 @@
# chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/discord-registration.yaml # chown matrix-synapse:matrix-synapse /var/lib/matrix-synapse/discord-registration.yaml
# "/var/lib/matrix-synapse/telegram-registration.yaml" # "/var/lib/matrix-synapse/telegram-registration.yaml"
"/var/lib/matrix-synapse/signal-registration.yaml" "/var/lib/matrix-synapse/signal-registration.yaml"
#"/var/lib/matrix-as-whatsapp/whatsapp-registration.yaml" # Discord now uses native module with registerToSynapse = true (auto-registers)
"/var/lib/matrix-as-discord/discord-registration.yaml" # "/var/lib/matrix-as-discord/discord-registration.yaml"
# "/var/lib/matrix-synapse/slack-registration.yaml" # "/var/lib/matrix-synapse/slack-registration.yaml"
# "/var/lib/matrix-synapse/discord-registration.yaml"
# "/var/lib/matrix-synapse/whatsapp-registration.yaml" # "/var/lib/matrix-synapse/whatsapp-registration.yaml"
]; ];
turn_uris = [ turn_uris = [
@@ -156,10 +168,12 @@
networking.firewall = networking.firewall =
let let
range = with config.services.coturn; [{ range = with config.services.coturn; [
{
from = min-port; from = min-port;
to = max-port; to = max-port;
}]; }
];
in in
{ {
enable = true; enable = true;
+34
View File
@@ -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";
};
};
};
};
}
@@ -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/";
};
};
};
}
+1 -1
View File
@@ -13,7 +13,7 @@ touch "$TRACKING_FILE"
# Get list of albums on remote server # Get list of albums on remote server
echo "$(date): Checking for new albums on seedbox..." >>"$LOG_FILE" 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 if [ -z "$REMOTE_ALBUMS" ]; then
echo "$(date): No albums found on remote server" >>"$LOG_FILE" echo "$(date): No albums found on remote server" >>"$LOG_FILE"
+23
View File
@@ -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" ];
};
}
+63
View File
@@ -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"
+1
View File
@@ -3,6 +3,7 @@
environment.systemPackages = [ pkgs.beets ]; environment.systemPackages = [ pkgs.beets ];
services.transmission = { services.transmission = {
enable = true; enable = true;
package = pkgs.transmission_4; # 25.11: transmission_3 removed, explicitly use v4
settings = { settings = {
rpc-enabled = true; rpc-enabled = true;
rpc-bind-address = "0.0.0.0"; rpc-bind-address = "0.0.0.0";
+4 -4
View File
@@ -24,7 +24,9 @@
}); });
wallabag = prev.wallabag.overrideAttrs (attrs: { wallabag = prev.wallabag.overrideAttrs (attrs: {
patches = builtins.filter (patch: builtins.baseNameOf patch != "wallabag-data.patch") attrs.patches ++ [ 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. # Out of the box, Wallabag wants to write to various subdirectories of the project directory.
# Lets replace references to such paths with designated systemd locations # Lets replace references to such paths with designated systemd locations
# so that the project source can remain immutable. # so that the project source can remain immutable.
@@ -36,9 +38,7 @@
knownVulnerabilities = [ ]; knownVulnerabilities = [ ];
}); });
mautrix-discord = prev.mautrix-discord.overrideAttrs (attrs: rec { # mautrix-discord overlay removed - now using native NixOS 25.11 module
license = "";
});
# Need to do server and agent too, maybe # Need to do server and agent too, maybe
# woodpecker-cli-next = # woodpecker-cli-next =
+1 -1
View File
@@ -9,7 +9,7 @@ pkgs.stdenv.mkDerivation rec {
}; };
buildInputs = with pkgs; [ buildInputs = with pkgs; [
lua lua
fennel luaPackages.fennel # renamed from fennel in 25.11
]; ];
configurePhase = '' configurePhase = ''