advent of code 2025 in ts and nix

feat: add aoc cli

dunkirk.sh f743cc4e b1b2c7cf

verified
+4
.gitignore
···
# Finder (MacOS) folder config
.DS_Store
+
+
# nix
+
.direnv
+
.envrc
+14 -2
README.md
···
# Advent of Code 2025 - Kieran's way
+
![made with vhs](https://vhs.charm.sh/vhs-4Iru9nKzVVROVumbtUEW20.gif)
+
This contains my solutions to advent of code 2025 in mostly `ts` or `nix` depending on how lazy I'm feeling that day.
+
## Running Solutions
+
+
Either you can run the days manually or you can use direnv and the `aoc` tui I made!
+
```bash
-
bun run index.ts
-
nix-instantiate --eval --strict solution.nix
+
echo "use flake" >> .envrc && direnv allow
+
```
+
+
the rather boring manual way:
+
+
```bash
+
cd ts/01 && bun run index.ts
+
nix-instantiate --eval --strict nix/01/solution.nix
```
The main repo is [the tangled repo](https://tangled.org/dunkirk.sh/aoc-2025) and the github is just a mirror.
+15
demo.tape
···
+
Output demo.gif
+
+
Set FontSize 14
+
Set Width 600
+
Set Height 500
+
Set Theme "Catppuccin Mocha"
+
+
Type "aoc"
+
Enter
+
Sleep 3s
+
+
# Choose "Run all solutions"
+
Down
+
Enter
+
Sleep 5s
+61
flake.lock
···
+
{
+
"nodes": {
+
"flake-utils": {
+
"inputs": {
+
"systems": "systems"
+
},
+
"locked": {
+
"lastModified": 1731533236,
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+
"owner": "numtide",
+
"repo": "flake-utils",
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+
"type": "github"
+
},
+
"original": {
+
"owner": "numtide",
+
"repo": "flake-utils",
+
"type": "github"
+
}
+
},
+
"nixpkgs": {
+
"locked": {
+
"lastModified": 1764517877,
+
"narHash": "sha256-pp3uT4hHijIC8JUK5MEqeAWmParJrgBVzHLNfJDZxg4=",
+
"owner": "NixOS",
+
"repo": "nixpkgs",
+
"rev": "2d293cbfa5a793b4c50d17c05ef9e385b90edf6c",
+
"type": "github"
+
},
+
"original": {
+
"owner": "NixOS",
+
"ref": "nixos-unstable",
+
"repo": "nixpkgs",
+
"type": "github"
+
}
+
},
+
"root": {
+
"inputs": {
+
"flake-utils": "flake-utils",
+
"nixpkgs": "nixpkgs"
+
}
+
},
+
"systems": {
+
"locked": {
+
"lastModified": 1681028828,
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+
"owner": "nix-systems",
+
"repo": "default",
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+
"type": "github"
+
},
+
"original": {
+
"owner": "nix-systems",
+
"repo": "default",
+
"type": "github"
+
}
+
}
+
},
+
"root": "root",
+
"version": 7
+
}
+184
flake.nix
···
+
{
+
description = "Advent of Code 2025 Solutions";
+
+
inputs = {
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
flake-utils.url = "github:numtide/flake-utils";
+
};
+
+
outputs = { self, nixpkgs, flake-utils }:
+
flake-utils.lib.eachDefaultSystem (system:
+
let
+
pkgs = nixpkgs.legacyPackages.${system};
+
gum = pkgs.gum;
+
+
# Get list of available days
+
getDays = ''
+
days=()
+
for dir in ts/* nix/*; do
+
if [ -d "$dir" ]; then
+
day=$(basename "$dir")
+
if [[ ! " ''${days[@]} " =~ " ''${day} " ]]; then
+
days+=("$day")
+
fi
+
fi
+
done
+
echo "''${days[@]}" | tr ' ' '\n' | sort
+
'';
+
+
# Run a specific day's solution
+
runDay = day: lang: ''
+
if [ "$lang" = "ts" ] && [ -f "ts/${day}/index.ts" ]; then
+
${gum}/bin/gum style --border rounded --border-foreground 212 --padding "0 1" --margin "1 0" "Day ${day} (TypeScript)"
+
(cd ts/${day} && ${pkgs.bun}/bin/bun run index.ts)
+
elif [ "$lang" = "nix" ] && [ -f "nix/${day}/solution.nix" ]; then
+
${gum}/bin/gum style --border rounded --border-foreground 212 --padding "0 1" --margin "1 0" "Day ${day} (Nix)"
+
result=$(${pkgs.nix}/bin/nix-instantiate --eval --strict --json nix/${day}/solution.nix)
+
part1=$(echo "$result" | ${pkgs.jq}/bin/jq -r '.part1')
+
part2=$(echo "$result" | ${pkgs.jq}/bin/jq -r '.part2')
+
echo "part 1: $part1"
+
echo "part 2: $part2"
+
fi
+
'';
+
+
# Interactive runner with gum
+
runner = pkgs.writeShellScriptBin "aoc" ''
+
set -e
+
+
${gum}/bin/gum style \
+
--border double \
+
--border-foreground 212 \
+
--padding "1 2" \
+
--margin "1 0" \
+
"$(${gum}/bin/gum style --foreground 212 '๐ŸŽ„ Advent of Code 2025 ๐ŸŽ„')"
+
+
# Choose action
+
action=$(${gum}/bin/gum choose "Run specific day" "Run all solutions" "Exit")
+
+
case "$action" in
+
"Run specific day")
+
# Get available days
+
days=$(${getDays})
+
+
if [ -z "$days" ]; then
+
${gum}/bin/gum style --foreground 196 "No solutions found!"
+
exit 1
+
fi
+
+
day=$(echo "$days" | ${gum}/bin/gum choose)
+
+
# Check what languages are available for this day
+
langs=()
+
[ -f "ts/$day/index.ts" ] && langs+=("TypeScript")
+
[ -f "nix/$day/solution.nix" ] && langs+=("Nix")
+
langs+=("Both")
+
+
lang=$(printf '%s\n' "''${langs[@]}" | ${gum}/bin/gum choose)
+
+
echo ""
+
case "$lang" in
+
"TypeScript")
+
if [ -f "ts/$day/index.ts" ]; then
+
${gum}/bin/gum style --border rounded --border-foreground 212 --padding "0 1" "Day $day"
+
echo ""
+
${gum}/bin/gum style --foreground 212 "TypeScript:"
+
(cd ts/$day && ${pkgs.bun}/bin/bun run index.ts)
+
fi
+
;;
+
"Nix")
+
if [ -f "nix/$day/solution.nix" ]; then
+
${gum}/bin/gum style --border rounded --border-foreground 212 --padding "0 1" "Day $day"
+
echo ""
+
${gum}/bin/gum style --foreground 212 "Nix:"
+
result=$(${pkgs.nix}/bin/nix-instantiate --eval --strict --json nix/$day/solution.nix)
+
echo "$result" | ${pkgs.jq}/bin/jq -r 'to_entries | .[] | "\(.key): \(.value)"'
+
fi
+
;;
+
"Both")
+
${gum}/bin/gum style --border rounded --border-foreground 212 --padding "0 1" "Day $day"
+
echo ""
+
if [ -f "ts/$day/index.ts" ]; then
+
${gum}/bin/gum style --foreground 212 "TypeScript:"
+
(cd ts/$day && ${pkgs.bun}/bin/bun run index.ts)
+
echo ""
+
fi
+
if [ -f "nix/$day/solution.nix" ]; then
+
${gum}/bin/gum style --foreground 212 "Nix:"
+
result=$(${pkgs.nix}/bin/nix-instantiate --eval --strict --json nix/$day/solution.nix)
+
echo "$result" | ${pkgs.jq}/bin/jq -r 'to_entries | .[] | "\(.key): \(.value)"'
+
fi
+
;;
+
esac
+
;;
+
+
"Run all solutions")
+
# Collect all unique days
+
all_days=()
+
for dir in ts/* nix/*; do
+
if [ -d "$dir" ]; then
+
day=$(basename "$dir")
+
if [[ ! " ''${all_days[@]} " =~ " ''${day} " ]]; then
+
all_days+=("$day")
+
fi
+
fi
+
done
+
+
# Sort and run each day
+
for daynum in $(printf '%s\n' "''${all_days[@]}" | sort); do
+
${gum}/bin/gum style --border rounded --border-foreground 212 --padding "0 1" "Day $daynum"
+
echo ""
+
+
if [ -f "ts/$daynum/index.ts" ]; then
+
${gum}/bin/gum style --foreground 212 "TypeScript:"
+
(cd ts/$daynum && ${pkgs.bun}/bin/bun run index.ts)
+
echo ""
+
fi
+
+
if [ -f "nix/$daynum/solution.nix" ]; then
+
${gum}/bin/gum style --foreground 212 "Nix:"
+
result=$(${pkgs.nix}/bin/nix-instantiate --eval --strict --json nix/$daynum/solution.nix)
+
echo "$result" | ${pkgs.jq}/bin/jq -r 'to_entries | .[] | "\(.key): \(.value)"'
+
echo ""
+
fi
+
done
+
;;
+
+
"Exit")
+
${gum}/bin/gum style --foreground 212 "Happy coding! ๐ŸŽ…"
+
exit 0
+
;;
+
esac
+
'';
+
+
in {
+
# Development shell with all tools
+
devShells.default = pkgs.mkShell {
+
packages = with pkgs; [
+
bun
+
jq
+
gum
+
runner
+
];
+
+
shellHook = ''
+
${gum}/bin/gum style --foreground 212 "๐ŸŽ„ Advent of Code 2025 ๐ŸŽ„"
+
echo ""
+
${gum}/bin/gum style "Run 'aoc' to start the interactive runner"
+
'';
+
};
+
+
# Packages
+
packages = {
+
default = runner;
+
};
+
+
# Apps
+
apps = {
+
default = {
+
type = "app";
+
program = "${runner}/bin/aoc";
+
};
+
};
+
}
+
);
+
}