Compare commits

8 Commits

Author SHA1 Message Date
559ef8c17d remove ff sponsored 2025-04-19 21:12:13 -04:00
036f162264 fix sops 2025-04-19 21:08:21 -04:00
00070837c2 fix bindings 2025-04-19 20:37:58 -04:00
e84f6e7cb4 again 2025-04-19 20:35:52 -04:00
914f8e8d30 more 2025-04-19 20:35:22 -04:00
fb1b69153b thinkpad conf 2025-04-19 20:09:49 -04:00
cf82afea4b utility 2025-04-10 20:01:05 -04:00
4d04f2600f snowfall migration 2025-04-10 17:40:56 -04:00
179 changed files with 1548 additions and 17004 deletions

View File

@@ -1,127 +0,0 @@
---
name: update-package-hashes
description: Update a package in packages/ to a new version and refresh its hashes (src, vendorHash, npmDepsHash, cargoHash, etc.) WITHOUT compiling the package. Use when the user asks to bump, update, or upgrade a specific package under packages/. If version is provided, proceed directly. If not, look up the latest version and ask the user before proceeding.
---
# Update Package Hashes (Without Building)
If the user provides a **package name** and **target version/rev/tag**, proceed directly.
If the user provides only a **package name** (no version), look up the latest version and **ask the user** if they want to proceed before updating.
## Hard Rules — Read First
1. **Never run `nix build .#<pkg>`** or `.#packages.<system>.<pkg>`. That compiles the package. Only realise **FOD sub-attributes** (`.src`, `.goModules`, `.npmDeps`, `.cargoDeps`) — those are pure downloads, not builds.
2. **Never** use `nix-prefetch-git`, `nix-prefetch-url`, `nix hash path`, `git clone` + manual hashing, `builtins.fetchGit`, or any other ad-hoc method to compute hashes. They produce hashes in formats that don't match what `fetchgit`/`fetchFromGitHub`/etc. expect, and you will waste time chasing mismatches.
3. There are exactly **two** correct ways to get a hash, both listed below. If neither fits, stop and ask the user — don't improvise.
## The Only Two Methods
### Method A — `nurl` via helper script (preferred for `src` on any git forge)
Use the co-located helper script. It wraps `nurl` and works for any git URL.
```bash
./update-package-hashes.sh hash <git-url> <rev-or-tag>
```
Copy the `hash = "sha256-..."` line from the output into the package's `src` block.
### Method B — FOD mismatch trick (for everything else)
For `vendorHash`, `npmDepsHash`, `cargoHash`, `cargoLock.outputHashes.<crate>`, or any `src` using a custom fetcher (`leaveDotGit`, `postFetch`, `fetchSubmodules`, etc. — applies to `llama-cpp` and `llama-swap`), realise the **specific FOD sub-attribute** and read the `got:` line from the error.
```bash
nix build .#<name>.src --no-link 2>&1 | tee /tmp/hash.log # for src
nix build .#<name>.goModules --no-link 2>&1 | tee /tmp/hash.log # for vendorHash
nix build .#<name>.npmDeps --no-link 2>&1 | tee /tmp/hash.log # for npmDepsHash
nix build .#<name>.cargoDeps --no-link 2>&1 | tee /tmp/hash.log # for cargoHash
grep -E '^[[:space:]]*got:' /tmp/hash.log | tail -1 | awk '{print $2}'
```
Setting the hash to `lib.fakeHash` (preferred when `lib` is in scope), `sha256-AAAA...` (44 A's), or leaving the old one in place all work — the build will fail at the FOD with `got: sha256-...` which is the correct value.
**Note:** `.src`, `.goModules`, etc. are sub-attributes of the derivation. They download but do not compile. `nix build .#<name>` (without the `.src` suffix) compiles — never do that.
### Example — Package With `src`, `npmDepsHash`, and `vendorHash`
Use the same pattern for packages that combine a custom `src` fetcher, Go dependencies, and a nested npm UI derivation. `llama-swap` is a concrete example because it uses `leaveDotGit` + `postFetch`, `vendorHash`, and a UI package exposed under `passthru.ui`.
1. Set stale hashes to `lib.fakeHash` where possible:
```nix
src.hash = lib.fakeHash;
vendorHash = lib.fakeHash;
passthru.npmDepsHash = lib.fakeHash;
```
2. Resolve the custom `src` hash first; downstream FODs depend on it:
```bash
nix build .#llama-swap.src --no-link 2>&1 | tee /tmp/llama-swap-src.log
grep -E '^[[:space:]]*got:' /tmp/llama-swap-src.log | tail -1 | awk '{print $2}'
```
Put the `got:` value into `src.hash` before resolving dependency hashes.
3. Resolve the UI npm dependency hash from the nested UI derivation:
```bash
nix build .#llama-swap.passthru.ui.npmDeps --no-link 2>&1 | tee /tmp/llama-swap-npm.log
grep -E '^[[:space:]]*got:' /tmp/llama-swap-npm.log | tail -1 | awk '{print $2}'
```
Put the `got:` value into `passthru.npmDepsHash`.
4. Resolve the Go `vendorHash`:
```bash
nix build .#llama-swap.goModules --no-link 2>&1 | tee /tmp/llama-swap-go.log
grep -E '^[[:space:]]*got:' /tmp/llama-swap-go.log | tail -1 | awk '{print $2}'
```
Put the `got:` value into `vendorHash`.
For other packages, adapt the flake attribute path to where the FOD is exposed (for example, `.#<name>.npmDeps`, `.#<name>.passthru.ui.npmDeps`, or `.#<name>.goModules`).
5. Re-run the FOD sub-attribute builds to confirm they realise successfully:
```bash
nix build .#llama-swap.src --no-link
nix build .#llama-swap.passthru.ui.npmDeps --no-link
nix build .#llama-swap.goModules --no-link
```
If a dependency FOD fails before a hash mismatch (for example, `go.mod requires go >= ...`), fix the package inputs minimally (for example, switch to the matching `buildGo126Module`) and re-run the same FOD sub-attribute. Do not build `.#llama-swap`.
## Lookup Latest Version
When the user asks to update a package but doesn't specify a version:
1. Read `packages/<name>/default.nix` to find the git URL and current version/tag.
2. Determine the tag pattern from the `tag` field (e.g. `"b${version}"` → `'b*'`, `"v${version}"` → `'v*'`).
3. Run the helper script:
```bash
./update-package-hashes.sh releases <git-url> '<pattern>'
```
Shows main HEAD + 5 newest matching tags with commit hashes.
4. **Ask the user** before proceeding (`Current: b8815 → Latest: b8914 — proceed?`).
## Flow
1. **If no version was provided**, look up the latest version (see section above) and ask the user to confirm.
2. Edit `packages/<name>/default.nix` — bump `version` / `rev` / `tag`. Check for sibling `.nix` files (e.g. `ui.nix`) that may also need bumping.
3. Get the new `src` hash with **Method A** (`nurl`). If the package uses a custom fetcher, use **Method B** on `.src` instead.
4. For each dependency hash (`vendorHash` / `npmDepsHash` / `cargoHash` / etc.), use **Method B** on the matching sub-attribute.
5. **Opaque `outputHash` FODs** (e.g. opencode's `node_modules` which runs `bun install`) — do NOT attempt locally. Leave as-is and flag for CI in the summary.
6. Show `git diff -- packages/<name>/` and list any hashes left for CI.
## Don't Touch What Didn't Change
Skip pinned sub-dependencies whose inputs didn't change — e.g. `slack-cli`'s `python-snappy` / `zstd-python` PyPI tarballs are pinned by the upstream `dfindexeddb`, not by the slack-cli rev.
## Optional Shortcut
`nix-update --flake <name> --version <v>` sometimes handles everything for simple packages. Try it first; fall back to the methods above if it fails.

View File

@@ -1,206 +0,0 @@
#!/usr/bin/env bash
#
# update-package-hashes.sh — Helper for the update-package-hashes skill.
# Co-located in .agents/skills/update-package-hashes/
#
# Usage:
# ./update-package-hashes.sh releases <git-url> [pattern]
# ./update-package-hashes.sh hash <git-url> <rev-or-tag>
# ./update-package-hashes.sh hash <git-url> <rev-or-tag> <fetcher>
# ./update-package-hashes.sh all <git-url> <rev-or-tag> [fetcher]
#
# Examples:
# ./update-package-hashes.sh releases https://github.com/ggml-org/llama.cpp
# ./update-package-hashes.sh releases https://github.com/ggml-org/llama.cpp b*
# ./update-package-hashes.sh hash https://github.com/owner/repo v1.2.3
# ./update-package-hashes.sh hash https://github.com/owner/repo abc1234 fetchFromGitLab
# ./update-package-hashes.sh all https://github.com/owner/repo v1.2.3
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
$0 releases <git-url> [tag-pattern]
Show main branch HEAD + 5 most recent matching releases/tags.
$0 hash <git-url> <rev-or-tag> [fetcher]
Fetch source and print the nix fetcher expression with hash (via nurl).
$0 all <git-url> <rev-or-tag> [fetcher]
Same as hash, but also shows main HEAD + recent releases for context.
Options:
tag-pattern Glob pattern for filtering tags (e.g. 'v*', 'b*').
If omitted, all tags are shown.
fetcher Override the auto-detected fetcher:
fetchFromGitHub (default)
fetchFromGitLab
fetchFromGitLab (e.g. gitea.va.reichard.io)
fetchFromSourceHut
fetchgit (fallback for non-standard forges)
Requires: nix (for nurl), git.
EOF
exit 1
}
# ── Helpers ──────────────────────────────────────────────────────────────────
detect_fetcher() {
local url="$1"
local host
host=$(echo "$url" | sed -E 's|^https?://||' | cut -d/ -f1)
case "$host" in
*github.com)
echo "fetchFromGitHub"
;;
*gitlab.com)
echo "fetchFromGitLab"
;;
*gitea*|*gitlab.reichard*|*gitlab.va.reichard*)
echo "fetchFromGitLab"
;;
*sourcehut*)
echo "fetchFromSourceHut"
;;
*)
echo "fetchgit"
;;
esac
}
normalise_url() {
echo "${1%.git}"
}
# ── Commands ─────────────────────────────────────────────────────────────────
# Show main/master branch HEAD.
show_main_head() {
local url="$1"
local clean_url
clean_url=$(normalise_url "$url")
for branch in main master; do
local line
line=$(git ls-remote "$url" "refs/heads/$branch" 2>/dev/null | head -1)
if [[ -n "$line" ]]; then
local full_rev="${line%% *}"
echo " main: $full_rev"
echo
return
fi
done
# Fallback: try to find any branch
echo " (no main/master branch found)"
echo
}
# List recent tags/releases for a repo.
cmd_releases() {
local url="$1"
local pattern="${2:-}"
local clean_url
clean_url=$(normalise_url "$url")
echo "Recent tags for: $clean_url"
echo "────────────────────────────────────────────────────────"
echo
# Always show main HEAD first
show_main_head "$url"
# Get tags sorted by date (newest first), limited to 5
local tags
if [[ -n "$pattern" ]]; then
tags=$(git ls-remote --tags "$url" "$pattern" 2>&1 \
| grep -v '\^{}' \
| sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \
| sort -V \
| tail -5 || true)
else
tags=$(git ls-remote --tags "$url" 2>&1 \
| grep -v '\^{}' \
| sed -E 's/^[a-f0-9]+[[:space:]]+refs\/tags\/([^\/]+)[[:space:]]*$/\1/' \
| sort -V \
| tail -5 || true)
fi
if [[ -z "$tags" ]]; then
echo " (no tags found)"
return
fi
printf " %-30s %s\n" "TAG" "COMMIT"
echo " $(printf '%.0s-' {1..72})"
echo "$tags" | tac | while IFS= read -r tag_name; do
local full_rev
full_rev=$(git ls-remote "$url" "refs/tags/$tag_name" 2>/dev/null | head -1 | cut -f1)
if [[ -n "$full_rev" ]]; then
printf " %-30s %s\n" "$tag_name" "$full_rev"
else
printf " %-30s %s\n" "$tag_name" "(not found)"
fi
done
}
# Get the nix fetcher expression with hash for a specific rev/tag.
cmd_hash() {
local url="$1"
local rev="$2"
local fetcher="${3:-}"
if [[ -z "$fetcher" ]]; then
fetcher=$(detect_fetcher "$url")
fi
local clean_url
clean_url=$(normalise_url "$url")
echo "Fetching hash for: $clean_url @ $rev"
echo "Fetcher: $fetcher"
echo "────────────────────────────────────────────────────────"
echo
nix run nixpkgs#nurl -- "$url" "$rev"
}
# Get hash + recent releases for context.
cmd_all() {
local url="$1"
local rev="$2"
local fetcher="${3:-}"
cmd_releases "$url"
echo
cmd_hash "$url" "$rev" "$fetcher"
}
# ── Main ─────────────────────────────────────────────────────────────────────
if [[ $# -lt 2 ]]; then
usage
fi
case "$1" in
releases)
[[ $# -lt 2 ]] && { echo "Error: missing <git-url>" >&2; usage; }
cmd_releases "$2" "${3:-}"
;;
hash)
[[ $# -lt 3 ]] && { echo "Error: missing <git-url> <rev-or-tag>" >&2; usage; }
cmd_hash "$2" "$3" "${4:-}"
;;
all)
[[ $# -lt 3 ]] && { echo "Error: missing <git-url> <rev-or-tag>" >&2; usage; }
cmd_all "$2" "$3" "${4:-}"
;;
*)
echo "Unknown command: $1" >&2
usage
;;
esac

View File

@@ -1,71 +0,0 @@
---
name: update-vllm-3090-configs
description: Update only the qwen3.6-27b vLLM 3090 llama-swap configs from club-3090 refs; compare diffs, present a plan, and require approval before editing.
---
# Update vLLM 3090 Configs
## Scope
Use only for Qwen3.6 27B vLLM 3090 configs in `modules/nixos/services/llama-swap/`.
Do not use this skill for other models, other Qwen sizes, non-vLLM configs, or package bumps.
Local files:
- `modules/nixos/services/llama-swap/config.nix`
- `modules/nixos/services/llama-swap/setup-qwen36-vllm.sh`
Local config keys:
- `vllm-qwen3.6-27b-tools-text`
- `vllm-qwen3.6-27b-long-text`
- `vllm-qwen3.6-27b-long-vision`
## Hash Tracking
Each config entry stores an upstream commit hash comment:
`# Upstream: club-3090 <hash> (<date>) - <compose-file>`
When comparing, first extract stored hashes. If a config's hash matches
upstream HEAD, skip it (report "already synced"). Only full-diff configs
whose hash differs. Update the hash comment when edits are applied.
## Upstream References
Compare against `club-3090` master:
- `models/qwen3.6-27b/vllm/compose/single/tools-text.yml`
- `models/qwen3.6-27b/vllm/compose/single/long-text.yml`
- `models/qwen3.6-27b/vllm/compose/single/long-vision.yml`
- `scripts/setup.sh` for the current `GENESIS_PIN="${GENESIS_PIN:-...}"`
Use raw URLs or a temp clone under `_scratch/club-3090`. Prefer a temp clone when checking broad changes:
```bash
mkdir -p _scratch
git clone https://github.com/noonghunna/club-3090 _scratch/club-3090 2>/dev/null || git -C _scratch/club-3090 pull --ff-only
```
## Required Workflow
1. Fetch/update upstream refs under `_scratch/club-3090` or fetch the raw files.
2. Extract stored upstream hashes from `# Upstream: club-3090 ...` comments in config.nix. Skip any config whose hash matches upstream HEAD (report "already synced").
3. Compare upstream compose files to the remaining local llama-swap entries. Translate docker-compose semantics into the existing `docker run`/llama-swap format.
4. Compare upstream `scripts/setup.sh` Genesis pin to local `GENESIS_PIN` in `setup-qwen36-vllm.sh`.
5. Check upstream compose volumes/entrypoint for sidecar patches. If patches are added, removed, renamed, or invoked differently, update both:
- runtime mounts and `python3 /patches/...` calls in `config.nix`
- download/install logic and summary in `setup-qwen36-vllm.sh`
6. Ignore these diffs unless the user explicitly asks otherwise:
- `shm_size` / shm-related compose settings
- local timing patch `patch_timings_07351e088.py` and its mount/invocation
- model served-name differences caused by llama-swap `${MODEL_ID}`
- `HUGGING_FACE_HUB_TOKEN`; keep local CUDA device/env choices
- upstream relative paths vs local `/mnt/ssd/vLLM/...` paths
- docker-compose format vs local llama-swap/Nix format
7. Before editing, present:
- upstream files/commit checked
- meaningful diffs found
- ignored diffs
- exact planned local changes
Then wait for explicit user approval.
8. After approval, edit minimally and update the `# Upstream: club-3090 ...` hash comments. Validate:
- `bash -n modules/nixos/services/llama-swap/setup-qwen36-vllm.sh`
- `nix-instantiate --parse modules/nixos/services/llama-swap/config.nix`
9. Summarize changed files and any remaining upstream differences.

1
.envrc
View File

@@ -1 +0,0 @@
use flake

2
.gitignore vendored
View File

@@ -1,6 +1,4 @@
.codexis
.DS_Store
_scratch
result
._*
.direnv

View File

@@ -1,50 +1,15 @@
keys:
# Global Admin
# Admin - Age Native
- &admin_reichard age1sac93wpnjcv62s7583jv6a4yspndh6k0r25g3qx3k7gq748uvafst6nz4w
# User SSH Derived
- &user_lin-va-desktop age15hdlen5dgjvdfgg2j0uzvchs5vs3xuptkhsw9xeuatcuk6uwrvcsz7hcsg
# lin-va-mbp-personal@evanreichard - SSH Derived
- &user_lin-va-mbp-personal age17ayje4uv2mhwehhp9jr3u9l0ds07396kt7ef40sufx89vm7cgfjq6d5d4y
- &user_lin-va-mbp-work-vm age1mar507c9mxmwalg486chs5kfh0mya38rv5w64ypfwnwlawewrpnswerpg8
- &user_lin-va-terminal age1w6avj7gd4f5frk90lsyh4e2k5am6z92hzlr0vpgrm767muyj59qsnuah62
- &user_lin-va-thinkpad age1avlhszrryt4gf4ya536jhzm7qwt9xfttm8x4sns6h9w2tahzqp8sspz9y5
- &user_mac-va-mbp-personal age1dccte7xtwswgef089nd80dutp96xnezx5lrqnneh9cusegsnda8sj3dj6c
- &user_mac-va-mbp-work age1ped3hpugq06908ex8kgama33qckqe03rmac5pa6th87vks5d249qhshvqu
# System SSH Derived
- &system_lin-va-desktop age1mxjrvjxkn69kfn2np3wpd73g44fuhsgykw7l5ss9rx30em5jfp2scnrq32
- &system_lin-va-thinkpad age13gymlygyac9z2slecl53jp8spq7e8n4zkan86n0gmnm3nrj4muxqa5ullm
- &system_lin-va-terminal age1rfgh40p6wmaam4ttwgv9xfukgxjruskhfvm0q3ud6lvxtlqc2y8snhwjft
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *admin_reichard
- path_regex: secrets/common/systems.yaml
key_groups:
- age:
- *admin_reichard
- *system_lin-va-desktop
- *system_lin-va-thinkpad
- *system_lin-va-terminal
- path_regex: secrets/common/llama-swap.yaml
key_groups:
- age:
- *admin_reichard
- *system_lin-va-desktop
- *user_lin-va-mbp-personal
- *user_lin-va-mbp-work-vm
- *user_lin-va-terminal
- *user_lin-va-thinkpad
- *user_mac-va-mbp-personal
- *user_mac-va-mbp-work
- path_regex: secrets/common/evanreichard.yaml
- path_regex: secrets/lin-va-mbp-personal/evanreichard/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *admin_reichard
- *user_lin-va-mbp-personal
- *user_lin-va-mbp-work-vm
- *user_lin-va-terminal
- *user_lin-va-thinkpad
- *user_mac-va-mbp-personal
- *user_mac-va-mbp-work

119
AGENTS.md
View File

@@ -1,119 +0,0 @@
# NixOS Configuration — Agent Guide
This is a multi-host NixOS/nix-darwin configuration managed with [Snowfall Lib](https://github.com/snowfallorg/lib). It declaratively configures NixOS (Linux), nix-darwin (macOS), and Home Manager across many machines from a single flake.
## Snowfall Lib Conventions
Snowfall Lib auto-discovers everything by directory convention — there is no manual wiring. The namespace is `reichard`, so all custom options live under `reichard.*` (e.g. `reichard.services.tailscale`, `reichard.programs.terminal.nvim`). Modules use `lib.reichard.enabled` / `lib.reichard.disabled` helpers from `lib/module/default.nix`.
**Important:** Files must be tracked by git (`git add`) for the flake to see them.
## Layout
### `flake.nix`
Entrypoint. Defines inputs (nixpkgs, home-manager, sops-nix, disko, apple-silicon, snowfall-lib, etc.) and calls `snowfall-lib.mkFlake`. All system and home modules are auto-discovered from the directory structure below.
### `systems/`
System-level NixOS and nix-darwin configurations, organized by architecture:
- `systems/aarch64-linux/` — ARM Linux hosts (e.g. Asahi MacBook, Oracle Cloud nodes, headscale)
- `systems/x86_64-linux/` — x86 Linux hosts (desktop, thinkpad, utility servers, Kubernetes nodes)
- `systems/aarch64-darwin/` — macOS hosts (work and personal MacBooks)
- `systems/aarch64-raw-efi/` — Raw EFI image builds (terminal image via nixos-generators)
- `systems/x86_64-vmware/` — VMware images (RKE2 node)
Each host is a directory with a `default.nix` that composes modules via the `reichard.*` option namespace (e.g. `reichard.services.tailscale = enabled;`). Some hosts include `hardware-configuration.nix` or firmware directories.
### `homes/`
Home Manager configurations, organized by `<arch>/<user>@<host>/default.nix`. Each home config enables per-host programs and services (e.g. Neovim, Firefox, Hyprland, git, tmux) via the `reichard.*` namespace, same as system modules.
### `modules/`
Reusable NixOS, Home Manager, and Darwin modules. This is where most of the configuration logic lives.
- **`modules/nixos/`** — NixOS system modules:
- `common/` — Packages applied to all NixOS systems
- `home/` — Home Manager integration (useGlobalPkgs, useUserPackages)
- `nix/` — Nix daemon settings, registries, binary caches, distributed builds
- `user/` — User account creation
- `security/sops/` — sops-nix secret decryption (age-based)
- `system/` — Boot, networking, NetworkManager, disko disk partitioning
- `services/` — Headscale, Tailscale, llama-swap (LLM model server), OpenSSH, RKE2, printing, Avahi, cloud-init, Sunshine, etc.
- `hardware/` — OpenGL, Asahi (Apple Silicon), battery/UPower
- `programs/graphical/wms/hyprland/` — System-level Hyprland WM setup
- `display-managers/sddm/` — SDDM display manager
- `virtualisation/` — Podman, libvirtd
- **`modules/home/`** — Home Manager modules:
- `common/` — Packages applied to all home configs (sqlite, jq, ripgrep, ncdu, jnv)
- `user/` — Home Manager user identity
- `security/sops/` — Per-user secret decryption
- `services/` — Fusuma (touchpad gestures), swww (wallpaper), SSH agent, poweralertd
- `programs/terminal/` — CLI tools: bash, tmux, btop, git, k9s, zk, aws, Neovim, opencode, claude-code, pi
- `programs/graphical/` — GUI apps: Firefox (with extensions overlay), Ghostty, Hyprland (home-level), Remmina, GIMP, Wireshark, Ghidra, Strawberry
- **`modules/darwin/`** — nix-darwin modules: user, OpenSSH, sops
### `modules/home/programs/terminal/nvim/` — Neovim Configuration
This is the full Neovim setup, frequently modified. The `default.nix` declares:
- All plugins (via nixpkgs vimPlugins + custom `buildVimPlugin` for codecompanion.nvim, none-ls-extras, llama.vim)
- LSP servers and formatters as `extraPackages`
- A generated `nix-vars.lua` that injects Nix store paths for LSP binaries
The actual Neovim Lua configuration lives in `config/lua/`:
- `init.lua` — Main loader
- `base.lua` — Core Vim settings and keymaps
- `lsp-config.lua` — LSP server setup (uses paths from `nix-vars.lua`)
- `cmp-config.lua` — nvim-cmp completion
- `llm-config.lua` — LLM integration (codecompanion, llama.vim)
- `snacks-config.lua` — Snacks.nvim dashboard/picker
- `dap-config.lua` — Debug Adapter Protocol (Go, etc.)
- `diagnostics-config.lua`, `ts-config.lua`, `lualine-config.lua`, `noice-config.lua`, `git-config.lua`, `which-key-config.lua`, etc.
When editing the Neovim config, note that **plugin declarations** happen in `default.nix` (Nix), while **plugin configuration** happens in the Lua files under `config/lua/`. LSP binary paths are bridged via the auto-generated `nix-vars.lua`.
### `packages/`
Custom package derivations, auto-discovered by Snowfall Lib and available as `pkgs.reichard.<name>`:
- `codexis/` — Go: code indexer using tree-sitter (built from gitea via `fetchgit`)
- `llama-cpp/` — LLaMA C++ inference engine
- `llama-swap/` — Go: LLM model swap proxy (with UI sub-derivation)
- `opencode/` — Go: terminal coding tool
- `pi-coding-agent/` — Node.js: coding agent CLI
- `qwen-code/` — Qwen code assistant
- `stable-diffusion-cpp/` — Stable Diffusion C++ inference
### `overlays/`
Nixpkgs overlays. Currently just `firefox-addons/` which imports the rycee Firefox addons repository.
### `secrets/`
sops-encrypted secrets (age keys). Managed via `.sops.yaml` which defines per-host and per-user key groups:
- `keys.yaml` — Master key definitions
- `common/evanreichard.yaml` — User-level secrets (shared across personal machines)
- `common/systems.yaml` — System-level secrets (e.g. builder SSH keys)
### `shells/`
Dev shell definitions. `shells/default/` provides the default `nix develop` environment for working on this repo.
### `lib/`
Shared library helpers. `lib/module/default.nix` exports `mkOpt`, `mkBoolOpt`, `enabled`, and `disabled` — used throughout all modules for consistent option declarations.
## Common Tasks
- **Adding a new system:** Create `systems/<arch>/<hostname>/default.nix` and optionally `homes/<arch>/<user>@<hostname>/default.nix`. Compose existing modules via `reichard.*` options.
- **Adding a new module:** Create `modules/{nixos,home,darwin}/<category>/<name>/default.nix` with an `enable` option under the `reichard` namespace. It will be auto-discovered.
- **Adding a new package:** Create `packages/<name>/default.nix`. Run `git add` so the flake sees it. Reference it in modules as `pkgs.reichard.<name>`.
- **Editing Neovim plugins:** Modify `modules/home/programs/terminal/nvim/default.nix` (plugin list, extraPackages, nix-vars).
- **Editing Neovim config (Lua):** Modify files under `modules/home/programs/terminal/nvim/config/lua/`.
- **Managing secrets:** Edit `.sops.yaml` for key groups, use `sops` CLI to encrypt/decrypt files in `secrets/`.
- **Building/testing:** `nix build .#packages.<arch>.<name>` for packages, `nix build .#nixosConfigurations.<host>.config.system.build.toplevel` for full system builds.
- **Bumping a package version / refreshing hashes:** Use the `update-package-hashes` skill at `.pi/skills/update-package-hashes/`.

110
README.md
View File

@@ -2,101 +2,65 @@
This repository contains the configuration for multiple machines, as well as my home / IDE config (home-manager).
```bash
# Install NixOS
./bootstrap.sh install --name lin-va-nix-builder
# Remote Image Build (NixOS Builder)
./bootstrap.sh image --name lin-va-rke2 --remote
# Home Manager Install
home-manager switch --flake .#evanreichard@mac-va-mbp-personal
# Update Flake
nix flake update
```
## Manual
### NixOS
```bash
# Install NixOS
sudo nixos-rebuild switch --flake .#lin-va-mbp-personal
# Install NixOS (Remote)
nix run github:nix-community/nixos-anywhere -- --flake .#lin-cloud-kube1 --target-host \<USER\>@\<IP\>
# Build Image
nix build .#vmwareConfigurations.lin-va-rke2
```
## Nix Darwin
### NixOS Generators
```bash
# Install Nix Without Determinate
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
# Switch Nix Darwin
sudo nix run nix-darwin#darwin-rebuild -- switch --flake .#mac-va-mbp-personal
sudo darwin-rebuild switch --flake .#mac-va-mbp-personal
nix build .#vmwareConfigurations.rke2-node
```
## Clean Garbage
NOTE: This will remove previous generations
### Home Manager
```bash
sudo nix-collect-garbage --delete-old
nix-collect-garbage --delete-old
home-manager switch --flake .#evanreichard@MBP-Personal
```
## Home Manager
### NixOS Hosts
#### Copy Config
```bash
# Update System Channels
sudo nix-channel --add https://nixos.org/channels/nixpkgs-25.11-darwin nixpkgs
sudo nix-channel --update
# Update Home Manager
nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz home-manager
nix-channel --update
# Link Repo
ln -s /Users/evanreichard/Development/git/personal/nix/home-manager ~/.config/home-manager
# Build Home Manager
home-manager switch
rsync -av --exclude='.git' . root@HOST:/etc/nixos
```
### OS Update
`/etc/bashrc` may get overridden. To properly load Nix, prepend the following:
#### Partition Drives
```bash
# Nix
if [ -e '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh' ]; then
. '/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh'
fi
# End Nix
# Validate Disk
ls -l /dev/disk/by-id
# Partition Disk
# WARNING: This will destroy all data on the disk(s)
sudo nix \
--experimental-features "nix-command flakes" \
run github:nix-community/disko -- \
--mode disko \
--flake /etc/nixos#HOST_CONFIG
```
#### SOPS
1. Convert your SSH key to an age key
2. Get age public key
3. Update `.sops.yaml` with rules
4. Edit file
#### Install NixOS
```bash
# Convert SSH to Age
mkdir -p ~/.config/sops/age
ssh-to-age -private-key -i $HOME/.ssh/id_ed25519 -o ~/.config/sops/age/keys.txt
# Install
sudo nixos-install --flake /etc/nixos#HOST_CONFIG
# Get Public Key
age-keygen -y ~/.config/sops/age/keys.txt
ssh-to-age -private-key -i ~/.ssh/id_ed25519 | age-keygen -y
SOPS_AGE_KEY_FILE=<ADMIN_KEY> sops -d --extract '["lin-va-desktop"]["host"]' ./secrets/keys.yaml | ssh-to-age -private-key | age-keygen -y
# Edit File
# NOTE: You can specify key with - `SOPS_AGE_KEY_FILE=~/.config/sops/age/other.txt`
sops secrets/lin-va-thinkpad/evanreichard/default.yaml
# Reboot
sudo reboot
```
#### Copy Config Back to Host
```bash
rsync -av --exclude='.git' . root@HOST:/etc/nixos
```
#### Rebuild NixOS
```bash
sudo nixos-rebuild switch
```

View File

@@ -31,13 +31,13 @@ function cmd_image() {
# Validate Config Exists
if ! nix eval --json --impure \
".#vmwareConfigurations" \
".#qcowConfigurations" \
--apply "s: builtins.hasAttr \"$name\" s" 2>/dev/null | grep -q "true"; then
echo "Error: NixOS Generator Config '$name' not found"
exit 1
fi
build_args=(".#vmwareConfigurations.$name")
build_args=(".#qcowConfigurations.$name")
if [ "$remote" = true ]; then
build_args+=("-j0")
fi
@@ -51,9 +51,8 @@ function cmd_image() {
}
function cmd_install() {
local usage="Usage: $0 install --name <system-name> [--remote <user@remote-host>]"
local usage="Usage: $0 install --name <system-name>"
local name=""
local remote=""
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -61,10 +60,6 @@ function cmd_install() {
name="$2"
shift 2
;;
--remote)
remote="$2"
shift 2
;;
*)
echo "$usage"
exit 1
@@ -92,18 +87,6 @@ function cmd_install() {
exit 1
fi
# Remote or Local
if [ -n "$remote" ]; then
cmd_install_remote "$name" "$remote"
else
cmd_install_local "$name" "$disk_id"
fi
}
function cmd_install_local(){
local name="$1"
local disk_id="$2"
# Validate Disk Exists
if [ ! -e "$disk_id" ]; then
echo "Error: Disk $disk_id not found on system"
@@ -150,27 +133,6 @@ function cmd_install_local(){
sudo reboot
}
function cmd_install_remote(){
local name="$1"
local remote="$2"
# Prompt Install
read -p "This will completely wipe and install NixOS on $remote with configuration $name. Continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Operation Cancelled"
exit 1
fi
# Install NixOS
echo "Installing $name to remote host: $remote"
if ! nix run github:nix-community/nixos-anywhere -- --flake ".#$name" --target-host "$remote"; then
echo "Error: Remote NixOS installation failed"
exit 1
fi
echo "Successfully installed $name to remote host: $remote"
}
case "$1" in
image)
shift

373
flake.lock generated
View File

@@ -5,111 +5,34 @@
"flake-compat": "flake-compat",
"nixpkgs": [
"nixpkgs"
]
],
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1778234684,
"narHash": "sha256-usIHfvSt7aXvMvRGtcbsue3rA13Z+9TW/7I3WBzLqFY=",
"owner": "nix-community",
"lastModified": 1738646032,
"narHash": "sha256-57BdBE9anNpIpf48EiTVLGxg4mOQ04XjHCEP0gLTsFA=",
"owner": "tpwrules",
"repo": "nixos-apple-silicon",
"rev": "3d7fe422ef6162154830209b9e50bf69e150cff7",
"rev": "e77031211944723a38bebc043e48847c36e43668",
"type": "github"
},
"original": {
"owner": "nix-community",
"owner": "tpwrules",
"ref": "releasep2-2024-12-25",
"repo": "nixos-apple-silicon",
"type": "github"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1772129556,
"narHash": "sha256-Utk0zd8STPsUJPyjabhzPc5BpPodLTXrwkpXBHYnpeg=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "ebec37af18215214173c98cf6356d0aca24a2585",
"type": "github"
},
"original": {
"owner": "nix-darwin",
"ref": "nix-darwin-25.11",
"repo": "nix-darwin",
"type": "github"
}
},
"determinate": {
"inputs": {
"determinate-nixd-aarch64-darwin": "determinate-nixd-aarch64-darwin",
"determinate-nixd-aarch64-linux": "determinate-nixd-aarch64-linux",
"determinate-nixd-x86_64-linux": "determinate-nixd-x86_64-linux",
"nix": "nix",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1778179392,
"narHash": "sha256-W6zorvjBYbzMNvqKIqCdpDF4rq3gj50Xximl56YM9/I=",
"owner": "determinatesystems",
"repo": "determinate",
"rev": "efd54faa68be8cd777b5c28cab11e638998a0853",
"type": "github"
},
"original": {
"owner": "determinatesystems",
"repo": "determinate",
"type": "github"
}
},
"determinate-nixd-aarch64-darwin": {
"flake": false,
"locked": {
"narHash": "sha256-z4mCqKI3Qd6weuHrlfzGccJG0giym/VJhKv20ijRSs0=",
"type": "file",
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/macOS"
},
"original": {
"type": "file",
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/macOS"
}
},
"determinate-nixd-aarch64-linux": {
"flake": false,
"locked": {
"narHash": "sha256-yW+VNepSRytzfanSssPMJPvwioCcmlZYaBX8++UFkAk=",
"type": "file",
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/aarch64-linux"
},
"original": {
"type": "file",
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/aarch64-linux"
}
},
"determinate-nixd-x86_64-linux": {
"flake": false,
"locked": {
"narHash": "sha256-+L102C3Hhkd1GlXmRm2eLTLsZKBxEvooiQZFqQRlBf0=",
"type": "file",
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/x86_64-linux"
},
"original": {
"type": "file",
"url": "https://install.determinate.systems/determinate-nixd/tag/v3.20.0/x86_64-linux"
}
},
"disko": {
"inputs": {
"nixpkgs": "nixpkgs_3"
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1777713215,
"narHash": "sha256-8GzXDOXckDWwST8TY5DbwYFjdvQLlP7K9CLSVx6iTTo=",
"lastModified": 1743598667,
"narHash": "sha256-ViE7NoFWytYO2uJONTAX35eGsvTYXNHjWALeHAg8OQY=",
"owner": "nix-community",
"repo": "disko",
"rev": "63b4e7e6cf75307c1d26ac3762b886b5b0247267",
"rev": "329d3d7e8bc63dd30c39e14e6076db590a6eabe6",
"type": "github"
},
"original": {
@@ -120,17 +43,18 @@
},
"firefox-addons": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"dir": "pkgs/firefox-addons",
"lastModified": 1778385775,
"narHash": "sha256-n0MUvWA2SML/qBB4hpShQ7i+i961MX4oPtaQfYo0+uU=",
"lastModified": 1743861198,
"narHash": "sha256-PzbPHoSI5U1juWd01Spf3ST7ylR9mQ84v5p7NksBplY=",
"owner": "rycee",
"repo": "nur-expressions",
"rev": "268324916742a48cd03b94fd63f2822d6b66d519",
"rev": "7408ed5bbc9009741094f4dd4cc1abec79e79e7e",
"type": "gitlab"
},
"original": {
@@ -142,11 +66,11 @@
},
"flake-compat": {
"locked": {
"lastModified": 1761640442,
"narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=",
"lastModified": 1688025799,
"narHash": "sha256-ktpB4dRtnksm9F5WawoIkEneh1nrEvuxb5lJFt1iOyw=",
"owner": "nix-community",
"repo": "flake-compat",
"rev": "4a56054d8ffc173222d09dad23adf4ba946c8884",
"rev": "8bf105319d44f6b9f0d764efa4fdef9f1cc9ba1c",
"type": "github"
},
"original": {
@@ -156,22 +80,6 @@
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_3": {
"flake": false,
"locked": {
"lastModified": 1650374568,
@@ -187,28 +95,41 @@
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"determinate",
"nix",
"nixpkgs"
]
},
"flake-utils": {
"locked": {
"lastModified": 1748821116,
"narHash": "sha256-F82+gS044J1APL0n4hH50GYdPRv/5JWm34oCJYmVKdE=",
"rev": "49f0870db23e8c1ca0b5259734a02cd9e1e371a1",
"revCount": 377,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/hercules-ci/flake-parts/0.1.377%2Brev-49f0870db23e8c1ca0b5259734a02cd9e1e371a1/01972f28-554a-73f8-91f4-d488cc502f08/source.tar.gz"
"lastModified": 1629284811,
"narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c5d161cc0af116a2e17f54316f0bf43f0819785c",
"type": "github"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/hercules-ci/flake-parts/0.1"
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils": {
"flake-utils-plus": {
"inputs": {
"flake-utils": "flake-utils_2"
},
"locked": {
"lastModified": 1715533576,
"narHash": "sha256-fT4ppWeCJ0uR300EH3i7kmgRZnAVxrH+XtK09jQWihk=",
"owner": "gytis-ivaskevicius",
"repo": "flake-utils-plus",
"rev": "3542fe9126dc492e53ddd252bb0260fe035f2c0f",
"type": "github"
},
"original": {
"owner": "gytis-ivaskevicius",
"repo": "flake-utils-plus",
"rev": "3542fe9126dc492e53ddd252bb0260fe035f2c0f",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems"
},
@@ -226,51 +147,6 @@
"type": "github"
}
},
"flake-utils-plus": {
"inputs": {
"flake-utils": "flake-utils"
},
"locked": {
"lastModified": 1715533576,
"narHash": "sha256-fT4ppWeCJ0uR300EH3i7kmgRZnAVxrH+XtK09jQWihk=",
"owner": "gytis-ivaskevicius",
"repo": "flake-utils-plus",
"rev": "3542fe9126dc492e53ddd252bb0260fe035f2c0f",
"type": "github"
},
"original": {
"owner": "gytis-ivaskevicius",
"repo": "flake-utils-plus",
"rev": "3542fe9126dc492e53ddd252bb0260fe035f2c0f",
"type": "github"
}
},
"git-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat_2",
"gitignore": [
"determinate",
"nix"
],
"nixpkgs": [
"determinate",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1747372754,
"narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
"rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
"revCount": 1026,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/cachix/git-hooks.nix/0.1.1026%2Brev-80479b6ec16fefd9c1db3ea13aeb038c60530f46/0196d79a-1b35-7b8e-a021-c894fb62163d/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/cachix/git-hooks.nix/0.1.941"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@@ -278,41 +154,20 @@
]
},
"locked": {
"lastModified": 1778401693,
"narHash": "sha256-OVHdCqXXUF5UdGkH+FF2ZL06OLZjj2kvP2dIUmzVWoo=",
"lastModified": 1743808813,
"narHash": "sha256-2lDQBOmlz9ggPxcS7/GvcVdzXMIiT+PpMao6FbLJSr0=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "389b83002efc26f1145e89a6a8e6edc5a6435948",
"rev": "a9f8b3db211b4609ddd83683f9db89796c7f6ac6",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.11",
"ref": "release-24.11",
"repo": "home-manager",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-parts": "flake-parts",
"git-hooks-nix": "git-hooks-nix",
"nixpkgs": "nixpkgs",
"nixpkgs-23-11": "nixpkgs-23-11",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1778177425,
"narHash": "sha256-oyHvP5HDRe59opmjTrq2ED9lh+R9FrHyaCGPPNfBqWM=",
"rev": "f0ccb960d3ad5bff28acd9cabf8bdef885b5d52f",
"revCount": 25858,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nix-src/3.20.0/019e03bc-3f83-7833-aba3-b691ef4956c7/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/DeterminateSystems/nix-src/%2A"
}
},
"nixlib": {
"locked": {
"lastModified": 1736643958,
@@ -336,11 +191,11 @@
]
},
"locked": {
"lastModified": 1769813415,
"narHash": "sha256-nnVmNNKBi1YiBNPhKclNYDORoHkuKipoz7EtVnXO50A=",
"lastModified": 1742568034,
"narHash": "sha256-QaMEhcnscfF2MqB7flZr+sLJMMYZPnvqO4NYf9B4G38=",
"owner": "nix-community",
"repo": "nixos-generators",
"rev": "8946737ff703382fda7623b9fab071d037e897d5",
"rev": "42ee229088490e3777ed7d1162cb9e9d8c3dbb11",
"type": "github"
},
"original": {
@@ -351,57 +206,27 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1773222311,
"narHash": "sha256-BHoB/XpbqoZkVYZCfXJXfkR+GXFqwb/4zbWnOr2cRcU=",
"rev": "0590cd39f728e129122770c029970378a79d076a",
"revCount": 909248,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2511.909248%2Brev-0590cd39f728e129122770c029970378a79d076a/019ce32b-8ace-7339-b129-cceaa8dd10c6/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/NixOS/nixpkgs/0.2511"
}
},
"nixpkgs-23-11": {
"locked": {
"lastModified": 1717159533,
"narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=",
"lastModified": 1743259260,
"narHash": "sha256-ArWLUgRm1tKHiqlhnymyVqi5kLNCK5ghvm06mfCl4QY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
"rev": "eb0e0f21f15c559d2ac7633dc81d079d1caf5f5f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1777954456,
"narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
"lastModified": 1744098102,
"narHash": "sha256-tzCdyIJj9AjysC3OuKA+tMD/kDEDAF9mICPDU7ix0JA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
"rev": "c8cd81426f45942bb2906d5ed2fe21d2f19d95b7",
"type": "github"
},
"original": {
@@ -413,46 +238,16 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1777826146,
"narHash": "sha256-wQ/iN5Zp5VIa3ebBibijPnLyKhor+xEbDy4d0goa9Zs=",
"rev": "73c703c22422b8951895a960959dbbaca7296492",
"revCount": 991389,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/nixpkgs-weekly/0.1.991389%2Brev-73c703c22422b8951895a960959dbbaca7296492/019df6c8-934b-7d40-b402-027bb5def30f/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/DeterminateSystems/nixpkgs-weekly/0.1"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1773628058,
"narHash": "sha256-hpXH0z3K9xv0fHaje136KY872VT2T5uwxtezlAskQgY=",
"lastModified": 1743813633,
"narHash": "sha256-BgkBz4NpV6Kg8XF7cmHDHRVGZYnKbvG0Y4p+jElwxaM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f8573b9c935cfaa162dd62cc9e75ae2db86f85df",
"rev": "7819a0d29d1dd2bc331bec4b327f0776359b1fa6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1778003029,
"narHash": "sha256-q/nkKLDtHIyLjZpKhWk3cSK5IYsFqtMd6UtXF3ddjgA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0c88e1f2bdb93d5999019e99cb0e61e1fe2af4c5",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
@@ -460,32 +255,46 @@
"root": {
"inputs": {
"apple-silicon": "apple-silicon",
"darwin": "darwin",
"determinate": "determinate",
"disko": "disko",
"firefox-addons": "firefox-addons",
"home-manager": "home-manager",
"nixos-generators": "nixos-generators",
"nixpkgs": "nixpkgs_4",
"nixpkgs": "nixpkgs_2",
"nixpkgs-unstable": "nixpkgs-unstable",
"snowfall-lib": "snowfall-lib",
"sops-nix": "sops-nix"
}
},
"rust-overlay": {
"flake": false,
"locked": {
"lastModified": 1686795910,
"narHash": "sha256-jDa40qRZ0GRQtP9EMZdf+uCbvzuLnJglTUI2JoHfWDc=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "5c2b97c0a9bc5217fc3dfb1555aae0fb756d99f9",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"snowfall-lib": {
"inputs": {
"flake-compat": "flake-compat_3",
"flake-compat": "flake-compat_2",
"flake-utils-plus": "flake-utils-plus",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1765361626,
"narHash": "sha256-kX0Dp/kYSRbQ+yd9e3lmmUWdNbipufvKfL2IzbrSpnY=",
"lastModified": 1736130495,
"narHash": "sha256-4i9nAJEZFv7vZMmrE0YG55I3Ggrtfo5/T07JEpEZ/RM=",
"owner": "snowfallorg",
"repo": "lib",
"rev": "c566ad8b7352c30ec3763435de7c8f1c46ebb357",
"rev": "02d941739f98a09e81f3d2d9b3ab08918958beac",
"type": "github"
},
"original": {
@@ -501,11 +310,11 @@
]
},
"locked": {
"lastModified": 1777944972,
"narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=",
"lastModified": 1743910657,
"narHash": "sha256-zr2jmWeWyhCD8WmO2aWov2g0WPPuZfcJDKzMJZYGq3Y=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "c591bf665727040c6cc5cb409079acb22dcce33c",
"rev": "523f58a4faff6c67f5f685bed33a7721e984c304",
"type": "github"
},
"original": {

View File

@@ -2,20 +2,19 @@
description = "NixOS Hosts";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
disko.url = "github:nix-community/disko";
determinate.url = "github:determinatesystems/determinate";
snowfall-lib = {
url = "github:snowfallorg/lib";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
url = "github:nix-community/home-manager/release-24.11";
inputs.nixpkgs.follows = "nixpkgs";
};
apple-silicon = {
url = "github:nix-community/nixos-apple-silicon";
url = "github:tpwrules/nixos-apple-silicon/releasep2-2024-12-25";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-generators = {
@@ -30,14 +29,9 @@
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs:
outputs = inputs:
inputs.snowfall-lib.mkFlake {
inherit inputs;
src = ./.;
@@ -57,27 +51,14 @@
];
};
outputs-builder = channels: {
devShells = {
default = import ./shells/default/default.nix { pkgs = channels.nixpkgs; };
};
};
homes.modules = with inputs; [
sops-nix.homeManagerModules.sops
./modules/home/common
];
systems.modules = {
nixos = with inputs; [
disko.nixosModules.disko
sops-nix.nixosModules.sops
./modules/nixos/common
];
darwin = with inputs; [
determinate.darwinModules.default
home-manager.darwinModules.home-manager
sops-nix.darwinModules.sops
];
};
};

View File

@@ -1,13 +1,9 @@
{ lib
, config
, namespace
, ...
}:
{ lib, config, namespace, ... }:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
home.stateVersion = "24.11";
reichard = {
user = {
@@ -15,6 +11,15 @@ in
inherit (config.snowfallorg.user) name;
};
services = {
# TODO
# sops = {
# enable = true;
# defaultSopsFile = lib.snowfall.fs.get-file "secrets/mac-va-mbp-personal/evanreichard/default.yaml";
# sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ];
# };
};
programs = {
graphical = {
ghostty = enabled;
@@ -27,12 +32,26 @@ in
git = enabled;
k9s = enabled;
nvim = enabled;
opencode = enabled;
};
};
security = {
sops = enabled;
};
};
# Global Packages
# programs.jq = enabled;
# programs.pandoc = enabled;
# home.packages = with pkgs; [
# android-tools
# imagemagick
# mosh
# python311
# texliveSmall # Pandoc PDF Dep
# google-cloud-sdk
# tldr
# ];
# SQLite Configuration
home.file.".sqliterc".text = ''
.headers on
.mode column
'';
}

View File

@@ -1,14 +1,9 @@
{ pkgs
, lib
, config
, namespace
, ...
}:
{ pkgs, lib, config, namespace, ... }:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
home.stateVersion = "24.11";
reichard = {
user = {
@@ -16,44 +11,47 @@ in
inherit (config.snowfallorg.user) name;
};
services = {
# TODO
# sops = {
# enable = true;
# defaultSopsFile = lib.snowfall.fs.get-file "secrets/mac-va-mbp-work/evanreichard/default.yaml";
# sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ];
# };
};
programs = {
graphical = {
ghostty = enabled;
};
terminal = {
bash = {
enable = true;
customProfile = builtins.readFile ./vm-init.sh;
customFastFetchLogo = ./prophet.txt;
};
aws = enabled;
btop = enabled;
claude-code = enabled;
direnv = enabled;
git = enabled;
k9s = enabled;
nvim = enabled;
pi = enabled;
zk = enabled;
aws = enabled;
};
};
services = {
sketchybar = enabled;
};
security = {
sops = enabled;
};
};
# Global Packages
programs.jq = enabled;
programs.pandoc = enabled;
home.packages = with pkgs; [
keycastr
reichard.slack-cli
_1password-cli
android-tools
imagemagick
mosh
python311
texliveSmall # Pandoc PDF Dep
google-cloud-sdk
tldr
];
# SQLite Configuration
home.file.".sqliterc".text = ''
.headers on
.mode column
'';
}

View File

@@ -1,19 +0,0 @@
                :+++++++=.        
                 =++++++++:       
                  -++++++++:      
                   -++++++++-     
                    :++++++++-    
          .-=======. :++++++++=   
         :********+   .++++++++=  
        -********=     .=+++++++=.
       -********-        =++++++++
      =********-          =++++++:
     =********:            -++++. 
    +********.              -+=.  
  .+*******+.                     
 .+*******+. :%#%%%%%%%%%%#%-     
:********=    +%%%%%%%%%%%%*      
********=      =%%%%%%%%%%+       
.+*****-        -%%%%%%%%=        
  +***-           ......          
   =*:                            

View File

@@ -1,39 +0,0 @@
echo "
██████╗██████╗ ██████╗ ██╗ ██╗██████╗ ███████╗████████╗██████╗ ██╗██╗ ██╗███████╗
██╔════╝██╔══██╗██╔═══██╗██║ ██║██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██║██║ ██╔╝██╔════╝
██║ ██████╔╝██║ ██║██║ █╗ ██║██║ ██║███████╗ ██║ ██████╔╝██║█████╔╝ █████╗
██║ ██╔══██╗██║ ██║██║███╗██║██║ ██║╚════██║ ██║ ██╔══██╗██║██╔═██╗ ██╔══╝
╚██████╗██║ ██║╚██████╔╝╚███╔███╔╝██████╔╝███████║ ██║ ██║ ██║██║██║ ██╗███████╗
╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝
EVASION SYSTEM - Because IT can't handle Nix 
"
# Start VM
utmctl=/Applications/UTM.app/Contents/MacOS/utmctl
vm="lin-va-mbp-work-vm"
if ! "$utmctl" status "$vm" | grep -q "started"; then
echo " [*] CrowdStrike Evasion VM Starting..."
"$utmctl" start "$vm"
fi
# Wait for VM & Start Tunnel
if ! pgrep -f "ssh -N -D 1080 adios-cs" > /dev/null; then
echo " [*] VM Starting..."
until nc -z -w 2 192.168.64.3 22 &> /dev/null; do
sleep 2
done
echo " [✓] VM Started"
echo " [*] VM SOCKS Proxy Starting..."
ssh -N -D 1080 adios-cs &> /dev/null &
disown
echo " [✓] VM SOCKS Proxy Started"
else
echo " [✓] VM SOCKS Proxy Already Running"
fi
echo -e " [*] Connecting..."
# Connect to VM
mosh --ssh="ssh -q" adios-cs -- tmux new-session -A -s main

View File

@@ -1,15 +1,9 @@
{ pkgs
, lib
, config
, namespace
, osConfig
, ...
}:
{ pkgs, lib, config, namespace, osConfig, ... }:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
home.stateVersion = "24.11";
reichard = {
user = {
@@ -21,22 +15,23 @@ in
ssh-agent = enabled;
fusuma = enabled;
swww = enabled;
};
security = {
sops = enabled;
sops = {
enable = true;
defaultSopsFile = lib.snowfall.fs.get-file "secrets/default.yaml";
sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ];
};
};
programs = {
graphical = {
wms.hyprland = {
enable = true;
mainMod = "ALT";
monitors = [ ",highres,auto,2" ]; # Alternatively - 1.68
monitors = [
",highres,auto,2" # Optional - 1.68
];
};
ghostty = enabled;
ghidra = enabled;
gimp = enabled;
browsers.firefox = {
enable = true;
gpuAcceleration = true;
@@ -50,15 +45,13 @@ in
git = enabled;
k9s = enabled;
nvim = enabled;
opencode = enabled;
pi = enabled;
};
};
};
home.packages = with pkgs; [
orca-slicer
];
# home.packages = with pkgs; [
# catppuccin-gtk
# ];
dconf = {
settings = {
@@ -66,10 +59,15 @@ in
color-scheme = "prefer-dark";
cursor-theme = "catppuccin-macchiato-mauve-cursors";
cursor-size = 24;
# enable-hot-corners = false;
# font-name = osConfig.${namespace}.system.fonts.default;
# gtk-theme = cfg.theme.name;
# icon-theme = cfg.icon.name;
};
};
};
home.pointerCursor = {
gtk.enable = true;
name = "catppuccin-macchiato-mauve-cursors";
@@ -80,7 +78,26 @@ in
# Kubernetes Secrets
sops.secrets = lib.mkIf osConfig.${namespace}.security.sops.enable {
rke2_kubeconfig = {
path = "${config.home.homeDirectory}/.kube/lin-va-kube";
path = "${config.home.homeDirectory}/.kube/rke2";
};
};
# Global Packages
# programs.jq = enabled;
# programs.pandoc = enabled;
# home.packages = with pkgs; [
# android-tools
# imagemagick
# mosh
# python311
# texliveSmall # Pandoc PDF Dep
# google-cloud-sdk
# tldr
# ];
# SQLite Configuration
home.file.".sqliterc".text = ''
.headers on
.mode column
'';
}

View File

@@ -1,44 +0,0 @@
{ lib
, config
, namespace
, ...
}:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
reichard = {
user = {
enable = true;
inherit (config.snowfallorg.user) name;
};
services = {
ssh-agent = enabled;
};
security = {
pass-keyring = enabled;
sops = enabled;
};
programs = {
terminal = {
bash = {
enable = true;
customFastFetchLogo = ./prophet.txt;
};
btop = enabled;
claude-code = enabled;
direnv = enabled;
git = enabled;
k9s = enabled;
nvim = enabled;
pi = enabled;
tmux = enabled;
};
};
};
}

View File

@@ -1,19 +0,0 @@
                :+++++++=.        
                 =++++++++:       
                  -++++++++:      
                   -++++++++-     
                    :++++++++-    
          .-=======. :++++++++=   
         :********+   .++++++++=  
        -********=     .=+++++++=.
       -********-        =++++++++
      =********-          =++++++:
     =********:            -++++. 
    +********.              -+=.  
  .+*******+.                     
 .+*******+. :%#%%%%%%%%%%#%-     
:********=    +%%%%%%%%%%%%*      
********=      =%%%%%%%%%%+       
.+*****-        -%%%%%%%%=        
  +***-           ......          
   =*:                            

View File

@@ -1,29 +0,0 @@
{ lib
, config
, namespace
, ...
}:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
reichard = {
user = {
enable = true;
inherit (config.snowfallorg.user) name;
};
programs = {
terminal = {
btop = enabled;
direnv = enabled;
nvim = enabled;
tmux = enabled;
};
};
};
programs.jq = enabled;
}

View File

@@ -1,32 +0,0 @@
{ lib
, config
, namespace
, ...
}:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
reichard = {
user = {
enable = true;
inherit (config.snowfallorg.user) name;
};
services = {
ssh-agent = enabled;
};
programs = {
terminal = {
bash = enabled;
btop = enabled;
direnv = enabled;
tmux = enabled;
git = enabled;
};
};
};
}

View File

@@ -1,49 +0,0 @@
{ lib
, config
, namespace
, osConfig
, ...
}:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
reichard = {
user = {
enable = true;
inherit (config.snowfallorg.user) name;
};
services = {
ssh-agent = enabled;
};
security = {
sops = enabled;
};
programs = {
terminal = {
bash = enabled;
btop = enabled;
conduit = enabled;
direnv = enabled;
git = enabled;
k9s = enabled;
nvim = enabled;
opencode = enabled;
pi = enabled;
tmux = enabled;
};
};
};
# Kubernetes Secrets
sops.secrets = lib.mkIf osConfig.${namespace}.security.sops.enable {
rke2_kubeconfig = {
path = "${config.home.homeDirectory}/.kube/lin-va-kube";
};
};
}

View File

@@ -1,15 +1,9 @@
{ pkgs
, lib
, config
, namespace
, osConfig
, ...
}:
{ pkgs, lib, config, namespace, osConfig, ... }:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.11";
home.stateVersion = "24.11";
reichard = {
user = {
@@ -21,25 +15,18 @@ in
ssh-agent = enabled;
fusuma = enabled;
swww = enabled;
poweralertd = enabled;
};
security = {
sops = enabled;
sops = {
enable = true;
defaultSopsFile = lib.snowfall.fs.get-file "secrets/default.yaml";
sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ];
};
};
programs = {
graphical = {
wms.hyprland = {
enable = true;
menuMod = "ALT";
};
wms.hyprland = enabled;
ghostty = enabled;
strawberry = enabled;
gimp = enabled;
wireshark = enabled;
ghidra = enabled;
remmina = enabled;
browsers.firefox = {
enable = true;
gpuAcceleration = true;
@@ -53,21 +40,29 @@ in
git = enabled;
k9s = enabled;
nvim = enabled;
pi = enabled;
};
};
};
# home.packages = with pkgs; [
# catppuccin-gtk
# ];
dconf = {
settings = {
"org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
cursor-theme = "catppuccin-macchiato-mauve-cursors";
cursor-size = 24;
# enable-hot-corners = false;
# font-name = osConfig.${namespace}.system.fonts.default;
# gtk-theme = cfg.theme.name;
# icon-theme = cfg.icon.name;
};
};
};
home.pointerCursor = {
gtk.enable = true;
name = "catppuccin-macchiato-mauve-cursors";
@@ -78,7 +73,13 @@ in
# Kubernetes Secrets
sops.secrets = lib.mkIf osConfig.${namespace}.security.sops.enable {
rke2_kubeconfig = {
path = "${config.home.homeDirectory}/.kube/lin-va-kube";
path = "${config.home.homeDirectory}/.kube/rke2";
};
};
# SQLite Configuration
home.file.".sqliterc".text = ''
.headers on
.mode column
'';
}

View File

@@ -1,15 +1,9 @@
{ pkgs
, lib
, config
, namespace
, osConfig
, ...
}:
{ pkgs, lib, config, namespace, osConfig, ... }:
let
inherit (lib.${namespace}) enabled;
in
{
home.stateVersion = "25.05";
home.stateVersion = "24.11";
reichard = {
user = {
@@ -21,10 +15,11 @@ in
ssh-agent = enabled;
fusuma = enabled;
swww = enabled;
};
security = {
sops = enabled;
sops = {
enable = true;
defaultSopsFile = lib.snowfall.fs.get-file "secrets/default.yaml";
sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ];
};
};
programs = {
@@ -49,16 +44,25 @@ in
};
};
# home.packages = with pkgs; [
# catppuccin-gtk
# ];
dconf = {
settings = {
"org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
cursor-theme = "catppuccin-macchiato-mauve-cursors";
cursor-size = 24;
# enable-hot-corners = false;
# font-name = osConfig.${namespace}.system.fonts.default;
# gtk-theme = cfg.theme.name;
# icon-theme = cfg.icon.name;
};
};
};
home.pointerCursor = {
gtk.enable = true;
name = "catppuccin-macchiato-mauve-cursors";
@@ -69,7 +73,26 @@ in
# Kubernetes Secrets
sops.secrets = lib.mkIf osConfig.${namespace}.security.sops.enable {
rke2_kubeconfig = {
path = "${config.home.homeDirectory}/.kube/lin-va-kube";
path = "${config.home.homeDirectory}/.kube/rke2";
};
};
# Global Packages
# programs.jq = enabled;
# programs.pandoc = enabled;
# home.packages = with pkgs; [
# android-tools
# imagemagick
# mosh
# python311
# texliveSmall # Pandoc PDF Dep
# google-cloud-sdk
# tldr
# ];
# SQLite Configuration
home.file.".sqliterc".text = ''
.headers on
.mode column
'';
}

View File

@@ -1,9 +0,0 @@
{
config = {
nix.enable = false;
home-manager = {
useGlobalPkgs = true;
useUserPackages = true;
};
};
}

View File

@@ -1,35 +0,0 @@
{ config
, lib
, namespace
, ...
}:
let
inherit (lib) mkIf mkEnableOption types;
inherit (lib.${namespace}) mkOpt;
getFile = lib.snowfall.fs.get-file;
user = config.users.users.${config.${namespace}.user.name};
cfg = config.${namespace}.security.sops;
in
{
options.${namespace}.security.sops = with types; {
enable = mkEnableOption "Enable sops";
defaultSopsFile = mkOpt str "secrets/systems/${config.system.name}.yaml" "Default sops file.";
sshKeyPaths = mkOpt (listOf path) [ ] "Additional SSH key paths to use.";
};
config = mkIf cfg.enable {
sops = {
defaultSopsFile = getFile cfg.defaultSopsFile;
age = {
keyFile = "${user.home}/.config/sops/age/keys.txt";
sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ] ++ cfg.sshKeyPaths;
};
};
sops.secrets.builder_ssh_key = {
sopsFile = getFile "secrets/common/systems.yaml";
};
};
}

View File

@@ -1,20 +0,0 @@
{ config, namespace, lib, ... }:
let
inherit (lib.${namespace}) mkOpt;
cfg = config.${namespace}.security.sops;
in
{
options.${namespace}.services.openssh = with lib.types; {
enable = lib.mkEnableOption "OpenSSH support";
authorizedKeys = mkOpt (listOf str) [ ] "The public keys to apply.";
extraConfig = mkOpt str "" "Extra configuration to apply.";
port = mkOpt port 2222 "The port to listen on (in addition to 22).";
};
config = lib.mkIf cfg.enable {
services.openssh = {
enable = true;
};
};
}

View File

@@ -1,23 +0,0 @@
{ config, lib, namespace, pkgs, ... }:
let
inherit (lib) types mkIf;
inherit (lib.${namespace}) mkOpt;
cfg = config.${namespace}.user;
in
{
options.${namespace}.user = with types; {
name = mkOpt str "evanreichard" "The name to use for the user account.";
email = mkOpt str "evan@reichard.io" "The email of the user.";
fullName = mkOpt str "Evan Reichard" "The full name of the user.";
uid = mkOpt (types.nullOr types.int) 501 "The uid for the user account.";
};
config = {
users.users.${cfg.name} = {
uid = mkIf (cfg.uid != null) cfg.uid;
shell = pkgs.bashInteractive;
home = "/Users/${cfg.name}";
};
};
}

View File

@@ -1,12 +0,0 @@
{ pkgs, ... }:
{
home.packages = with pkgs; [
sqlite-interactive
jnv
jq
ncdu
ripgrep
reichard.codexis
];
}

View File

@@ -38,12 +38,6 @@ in
ExtensionRecommendations = false;
SkipOnboarding = true;
};
GenerativeAI = {
Chatbot = false;
LinkPreviews = false;
TabGroups = false;
Locked = false;
};
ExtensionSettings = {
# Block All
# "*".installation_mode = "blocked";
@@ -59,7 +53,7 @@ in
settings = mkOpt attrs { } "Settings to apply to the profile.";
extensions.packages = mkOpt (with lib.types; listOf package)
extensions = mkOpt (with lib.types; listOf package)
(with pkgs.firefox-addons; [
bitwarden
darkreader

View File

@@ -0,0 +1,93 @@
{
"modules": [
{
"type": "separator",
"string": "",
"length": 35,
},
{
"type": "title",
"format": "Hardware Information",
},
{
"type": "cpu",
"key": " ",
},
{
"type": "memory",
"key": " ",
},
{
"type": "display",
"key": "󰍹 ",
},
{
"type": "separator",
},
{
"type": "title",
"format": "Software Information",
},
{
"type": "os",
"key": " ",
},
{
"type": "kernel",
"key": " ",
},
{
"type": "terminal",
"key": " ",
},
{
"type": "packages",
"key": "󰏖 ",
},
{
"type": "terminalfont",
"key": " ",
},
{
"type": "separator",
},
{
"type": "title",
"format": "Network Information",
},
{
"type": "publicip",
"key": " ",
},
{
"type": "localip",
"key": " ",
},
{
"type": "separator",
},
{
"type": "custom",
"format": " {#white} {#red} {#green} {#yellow} {#blue} {#magenta} {#cyan} {#white}\n",
},
],
"display": {
"separator": "  ",
"key": {
"width": 7,
},
"color": {
"keys": "yellow",
"title": "blue",
},
},
"settings": {
"kernelFormat": "minimal",
"memoryUnit": "gib",
"temperatureUnit": "celsius",
"publicIpTimeout": 2000,
"publicIpHost": "http://ident.me",
"diskUnit": "gib",
"showDisks": ["/"],
},
}

View File

@@ -10,14 +10,62 @@ in
};
config = mkIf cfg.enable {
# Enable Bash
${namespace}.programs.terminal.bash.enable = true;
programs.bash = {
enable = true;
shellAliases = {
grep = "grep --color";
ssh = "TERM=xterm-256color ssh";
flush_dns = "sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder";
};
profileExtra = ''
SHELL="$BASH"
PATH=~/.bin:$PATH
bind "set show-mode-in-prompt on"
# Pending Darwin @ https://github.com/NixOS/nixpkgs/pull/369788
home.packages = with pkgs; optionals isLinux [
set -o vi || true
VISUAL=vim
EDITOR="$VISUAL"
fastfetch
eval "$(thefuck --alias)"
'';
};
programs.powerline-go = {
enable = true;
settings = {
git-mode = "compact";
theme = "gruvbox";
};
modules = [
"host"
"cwd"
"git"
"docker"
"venv"
];
};
programs.readline = {
enable = true;
extraConfig = ''
# Approximate VIM Dracula Colors
set vi-ins-mode-string \1\e[01;38;5;23;48;5;231m\2 I \1\e[38;5;231;48;5;238m\2\1\e[0m\2
set vi-cmd-mode-string \1\e[01;38;5;22;48;5;148m\2 C \1\e[38;5;148;48;5;238m\2\1\e[0m\2
'';
};
home.packages = with pkgs; [
thefuck
fastfetch
bashInteractive
(nerdfonts.override { fonts = [ "Meslo" ]; })
] ++ optionals isLinux [
# Pending Darwin @ https://github.com/NixOS/nixpkgs/pull/369788
ghostty
];
home.file.".config/fastfetch/config.jsonc".text = builtins.readFile ./config/fastfetch.jsonc;
home.file.".config/ghostty/config".text =
let
bashPath = "${pkgs.bashInteractive}/bin/bash";

View File

@@ -1,24 +0,0 @@
{ pkgs
, lib
, config
, namespace
, ...
}:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.programs.graphical.gimp;
in
{
options.${namespace}.programs.graphical.gimp = {
enable = mkEnableOption "GIMP";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
darktable
gimp-with-plugins
gthumb
];
};
}

View File

@@ -1,17 +0,0 @@
{ pkgs, lib, config, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.programs.graphical.remmina;
in
{
options.${namespace}.programs.graphical.remmina = {
enable = mkEnableOption "Remmina";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
remmina
];
};
}

View File

@@ -1,17 +0,0 @@
{ pkgs, lib, config, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.programs.graphical.strawberry;
in
{
options.${namespace}.programs.graphical.strawberry = {
enable = mkEnableOption "Enable Strawberry";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
strawberry
libgpod
];
};
}

View File

@@ -1,17 +0,0 @@
{ pkgs, lib, config, namespace, ... }:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.programs.graphical.wireshark;
in
{
options.${namespace}.programs.graphical.wireshark = {
enable = mkEnableOption "Wireshark";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
wireshark
];
};
}

View File

@@ -44,6 +44,7 @@ decoration {
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
#https://wiki.hyprland.org/Configuring/Animations/
animations {
enabled = yes, please :)
@@ -126,7 +127,11 @@ input {
}
# https://wiki.hyprland.org/Configuring/Variables/#gestures
gesture = 4, horizontal, workspace, invert
gestures {
workspace_swipe = true
workspace_swipe_fingers = 4
workspace_swipe_invert = true
}
# Thinkpad Trackpoint
device {

View File

@@ -1,10 +1,4 @@
{
lib,
pkgs,
config,
namespace,
...
}:
{ lib, pkgs, config, namespace, ... }:
let
inherit (lib) types mkIf;
inherit (lib.${namespace}) mkOpt enabled;
@@ -14,9 +8,10 @@ in
{
options.${namespace}.programs.graphical.wms.hyprland = {
enable = lib.mkEnableOption "Hyprland";
mainMod = mkOpt types.str "SUPER" "main modifier key";
menuMod = mkOpt types.str "SUPER" "menu modifier key (i.e. menuMod + space)";
monitors = mkOpt (with types; listOf str) [ ", preferred, auto, 1" ] "monitor configuration";
mainMod = mkOpt types.str "SUPER" "Hyprland main modifier key";
monitors = mkOpt (with types; listOf str) [
", preferred, auto, 1"
] "Hyprland monitor configuration";
};
config = mkIf cfg.enable {
@@ -27,26 +22,25 @@ in
extraConfig = builtins.readFile ./config/hyprland.conf;
settings = {
"$mainMod" = cfg.mainMod;
"$menuMod" = cfg.menuMod;
"$terminal" = "ghostty";
"$menu" = "wofi --show drun";
monitor = cfg.monitors;
bind = [
# Menu Mod Bindings (macOS Transition - Spotlight & Screenshots)
"$menuMod, SPACE, exec, $menu"
"$menuMod SHIFT, 1, exec, hyprshot -m output"
"$menuMod SHIFT, 2, exec, hyprshot -m window"
"$menuMod SHIFT, 3, exec, hyprshot -m region"
"$menuMod, Q, killactive"
# Super Bindings (macOS Transition)
"ALT_SHIFT, 1, exec, hyprshot -m output"
"ALT_SHIFT, 2, exec, hyprshot -m window"
"ALT_SHIFT, 3, exec, hyprshot -m region"
# Primary Bindings
"$mainMod, SPACE, exec, $menu"
"$mainMod, RETURN, exec, $terminal"
"$mainMod, Q, killactive"
"$mainMod, M, exit"
"$mainMod, V, togglefloating"
"$mainMod, P, pin"
"$mainMod, J, togglesplit"
"$mainMod, P, pseudo" # dwindle
"$mainMod, J, togglesplit" # dwindle
"$mainMod, S, togglespecialworkspace, magic"
"$mainMod SHIFT, S, movetoworkspace, special:magic"
@@ -93,12 +87,12 @@ in
",XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
",XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
",XF86MonBrightnessUp, exec, brightnessctl s 4%+"
",XF86MonBrightnessDown, exec, brightnessctl s 5%-"
",XF86MonBrightnessUp, exec, brightnessctl s 10%+"
",XF86MonBrightnessDown, exec, brightnessctl s 10%-"
# macOS Keyboard Brightness
"$menuMod, XF86MonBrightnessUp, exec, brightnessctl -d kbd_backlight s 10%+"
"$menuMod, XF86MonBrightnessDown, exec, brightnessctl -d kbd_backlight s 10%-"
"ALT, XF86MonBrightnessUp, exec, brightnessctl -d kbd_backlight s 10%+"
"ALT, XF86MonBrightnessDown, exec, brightnessctl -d kbd_backlight s 10%-"
];
bindl = [
# Player Controls
@@ -113,161 +107,153 @@ in
programs.waybar = {
enable = true;
style = builtins.readFile ./config/waybar-style.css;
settings = [
{
layer = "top";
position = "top";
mod = "dock";
exclusive = true;
passtrough = false;
gtk-layer-shell = true;
height = 0;
modules-left = [
"hyprland/workspaces"
"hyprland/window"
settings = [{
layer = "top";
position = "top";
mod = "dock";
exclusive = true;
passtrough = false;
gtk-layer-shell = true;
height = 0;
modules-left = [
"hyprland/workspaces"
"hyprland/window"
];
# modules-center = [ "hyprland/window" ];
modules-right = [
"tray"
"cpu"
"memory"
"pulseaudio"
"network"
"backlight"
"battery"
"clock"
];
"hyprland/window" = { format = "{}"; };
"wlr/workspaces" = {
on-scroll-up = "hyprctl dispatch workspace e+1";
on-scroll-down = "hyprctl dispatch workspace e-1";
all-outputs = true;
on-click = "activate";
};
battery = {
states = {
warning = 30;
critical = 15;
};
format = "{icon}";
format-charging = "󰂄";
format-plugged = "󰂄";
format-alt = "{icon}";
format-icons = [
"󰂃"
"󰁺"
"󰁻"
"󰁼"
"󰁽"
"󰁾"
"󰁾"
"󰁿"
"󰂀"
"󰂁"
"󰂂"
"󰁹"
];
# modules-center = [ "hyprland/window" ];
modules-right = [
"tray"
"cpu"
"memory"
"pulseaudio"
"network"
"backlight"
"battery"
"clock"
};
cpu = {
interval = 10;
format = " {}%";
max-length = 10;
on-click = "";
};
memory = {
interval = 30;
format = " {}%";
format-alt = " {used:0.1f}G";
max-length = 10;
};
backlight = {
format = "{icon}";
format-icons = [
"󰋙"
"󰫃"
"󰫄"
"󰫅"
"󰫆"
"󰫇"
"󰫈"
];
"hyprland/window" = {
format = "{}";
};
"wlr/workspaces" = {
on-scroll-up = "hyprctl dispatch workspace e+1";
on-scroll-down = "hyprctl dispatch workspace e-1";
all-outputs = true;
on-click = "activate";
};
battery = {
states = {
warning = 30;
critical = 15;
};
format = "{icon}";
format-charging = "󰂄";
format-plugged = "󰂄";
format-alt = "{icon}";
format-icons = [
"󰂃"
"󰁺"
"󰁻"
"󰁼"
"󰁽"
"󰁾"
"󰁾"
"󰁿"
"󰂀"
"󰂁"
"󰂂"
"󰁹"
];
};
cpu = {
interval = 10;
format = " {}%";
max-length = 10;
on-click = "";
};
memory = {
interval = 30;
format = " {}%";
format-alt = " {used:0.1f}G";
max-length = 10;
};
on-scroll-up = "brightnessctl s 1%-";
on-scroll-down = "brightnessctl s +1%";
};
tray = {
icon-size = 13;
tooltip = false;
spacing = 10;
};
network = {
interval = 1;
format-wifi = "󰖩";
format-ethernet = "󰈀";
format-linked = "󰈁";
format-disconnected = "";
on-click-right = "${pkgs.networkmanagerapplet}/bin/nm-connection-editor";
# tooltip-format = ''
# <big>Network Details</big>
# <tt><small>Interface: {ifname}</small></tt>
# <tt><small>IP: {ipaddr}/{cidr}</small></tt>
# <tt><small>Gateway: {gwaddr}</small></tt>
# <tt><small>󰜷 {bandwidthUpBytes}\n󰜮 {bandwidthDownBytes}</small></tt>'';
tooltip-format = ''
<big>Network Details</big>
<small>
Interface: {ifname}
SSID: {essid}
IP Address: {ipaddr}/{cidr}
Gateway: {gwaddr}
backlight = {
format = "{icon}";
format-icons = [
"󰋙"
"󰫃"
"󰫄"
"󰫅"
"󰫆"
"󰫇"
"󰫈"
];
on-scroll-up = "brightnessctl s 1%-";
on-scroll-down = "brightnessctl s +1%";
};
tray = {
icon-size = 13;
tooltip = false;
spacing = 10;
};
network = {
interval = 1;
format-wifi = "󰖩";
format-ethernet = "󰈀";
format-linked = "󰈁";
format-disconnected = "";
on-click-right = "${pkgs.networkmanagerapplet}/bin/nm-connection-editor";
# tooltip-format = ''
# <big>Network Details</big>
# <tt><small>Interface: {ifname}</small></tt>
# <tt><small>IP: {ipaddr}/{cidr}</small></tt>
# <tt><small>Gateway: {gwaddr}</small></tt>
# <tt><small>󰜷 {bandwidthUpBytes}\n󰜮 {bandwidthDownBytes}</small></tt>'';
tooltip-format = ''
<big>Network Details</big>
<small>
Interface: {ifname}
SSID: {essid}
IP Address: {ipaddr}/{cidr}
Gateway: {gwaddr}
󰜷 {bandwidthUpBytes} / 󰜮 {bandwidthDownBytes}
</small>'';
󰜷 {bandwidthUpBytes} / 󰜮 {bandwidthDownBytes}
</small>'';
};
clock = {
format = " {:%Y-%m-%d %H:%M:%S}";
interval = 1;
tooltip-format = ''
<big>{:%Y %B}</big>
<tt><small>{calendar}</small></tt>'';
};
pulseaudio = {
format = "{icon} {volume}%";
tooltip = false;
format-muted = " Muted";
on-click = "pamixer -t";
on-scroll-up = "pamixer -i 5";
on-scroll-down = "pamixer -d 5";
scroll-step = 5;
format-icons = {
headphone = "";
hands-free = "";
headset = "";
phone = "";
portable = "";
car = "";
default = [ "" "" "" ];
};
clock = {
format = " {:%Y-%m-%d %H:%M:%S}";
interval = 1;
tooltip-format = ''
<big>{:%Y %B}</big>
<tt><small>{calendar}</small></tt>'';
};
pulseaudio = {
format = "{icon} {volume}%";
tooltip = false;
format-muted = " Muted";
on-click = "pamixer -t";
on-scroll-up = "pamixer -i 5";
on-scroll-down = "pamixer -d 5";
scroll-step = 5;
format-icons = {
headphone = "";
hands-free = "";
headset = "";
phone = "";
portable = "";
car = "";
default = [
""
""
""
];
};
};
"pulseaudio#microphone" = {
format = "{format_source}";
tooltip = false;
format-source = " {volume}%";
format-source-muted = " Muted";
on-click = "pamixer --default-source -t";
on-scroll-up = "pamixer --default-source -i 5";
on-scroll-down = "pamixer --default-source -d 5";
scroll-step = 5;
};
}
];
};
"pulseaudio#microphone" = {
format = "{format_source}";
tooltip = false;
format-source = " {volume}%";
format-source-muted = " Muted";
on-click = "pamixer --default-source -t";
on-scroll-up = "pamixer --default-source -i 5";
on-scroll-down = "pamixer --default-source -d 5";
scroll-step = 5;
};
}];
};
home.packages = with pkgs; [

View File

@@ -10,9 +10,8 @@ in
config = mkIf cfg.enable {
home.packages = with pkgs; [
aws-sso-util
awscli2
cw
awscli2
ssm-session-manager-plugin
];
};

View File

@@ -1,3 +0,0 @@
_scratch
.direnv
.envrc

View File

@@ -1,2 +0,0 @@
.headers on
.mode column

View File

@@ -1,99 +0,0 @@
{ cfg }:
builtins.toJSON (
{
modules = [
{
type = "separator";
string = "";
length = 35;
}
{
type = "title";
format = "Hardware Information";
}
{
type = "cpu";
key = " ";
}
{
type = "memory";
key = " ";
}
{
type = "display";
key = "󰍹 ";
}
{ type = "separator"; }
{
type = "title";
format = "Software Information";
}
{
type = "os";
key = " ";
}
{
type = "kernel";
key = " ";
}
{
type = "terminal";
key = " ";
}
{
type = "packages";
key = "󰏖 ";
}
{
type = "terminalfont";
key = " ";
}
{ type = "separator"; }
{
type = "title";
format = "Network Information";
}
{
type = "publicip";
key = " ";
}
{
type = "localip";
key = " ";
}
{ type = "separator"; }
{
type = "custom";
format = " {#white} {#red} {#green} {#yellow} {#blue} {#magenta} {#cyan} {#white}\n";
}
];
display = {
separator = " ";
key.width = 7;
color = {
keys = "yellow";
title = "blue";
};
};
settings = {
kernelFormat = "minimal";
memoryUnit = "gib";
temperatureUnit = "celsius";
publicIpTimeout = 2000;
publicIpHost = "http://ident.me";
diskUnit = "gib";
showDisks = [ "/" ];
};
}
// (
if cfg.customFastFetchLogo != null then
{
logo = {
source = cfg.customFastFetchLogo;
type = "file";
};
}
else
{ }
)
)

View File

@@ -1,75 +0,0 @@
#!/usr/bin/env bash
MODEL="vllm-qwen3.6-27b-long-text"
SYSTEM_PROMPT="You are a shell command expert. Given a natural language query, generate a single shell command that accomplishes the task."
# Colors
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
RESET='\033[0m'
hey-intern() {
local query="$*"
# Help
if [ -z "$query" ]; then
echo "Usage: hey-intern \"your query here\"" >&2
return 1
fi
# Execute LLM Request
response=$(curl -s -X POST "https://llm-api.va.reichard.io/v1/chat/completions" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg model "$MODEL" \
--arg system "$SYSTEM_PROMPT" \
--arg user "$query" \
'{
model: $model,
temperature: 0.2,
messages: [
{role: "system", content: $system},
{role: "user", content: $user}
],
tools: [{
type: "function",
function: {
name: "generate_shell_command",
description: "Generate a shell command to answer a query",
parameters: {
type: "object",
properties: {
command: {type: "string", description: "The shell command to execute"}
},
required: ["command"]
}
}
}]
}')" | jq -r '.choices[0].message.tool_calls[0].function.arguments // empty')
# Extract Command
local command=$(echo "$response" | jq -r '.command // empty')
if [ -n "$command" ]; then
echo -e "\n ${CYAN}${command}${RESET}\n"
read -p "$(echo -e "${YELLOW}Would you like to run this command? [y/N]${RESET} ")" -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo -e "${GREEN}Running...${RESET}\n"
history -s "$command"
eval "$command"
fi
else
echo "Failed to generate a valid command from the response." >&2
echo "Raw response: $response" >&2
return 1
fi
}
# Export Script
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
hey-intern "$@"
fi

View File

@@ -1,93 +0,0 @@
{ pkgs
, lib
, config
, namespace
, ...
}:
let
inherit (lib.${namespace}) mkOpt;
inherit (lib) mkEnableOption mkIf optionalAttrs;
inherit (pkgs.stdenv) isLinux isDarwin;
cfg = config.${namespace}.programs.terminal.bash;
in
{
options.${namespace}.programs.terminal.bash = with lib.types; {
enable = mkEnableOption "bash";
customProfile = mkOpt str "" "custom profile";
customFastFetchLogo = mkOpt (nullOr path) null "custom fast fetch logo path";
};
config = mkIf cfg.enable {
programs.bash = {
enable = true;
shellAliases = {
grep = "grep --color";
ssh = "TERM=xterm-256color ssh";
}
// optionalAttrs isLinux {
sync-watch = "watch -d grep -e Dirty: -e Writeback: /proc/meminfo";
}
// optionalAttrs isDarwin {
mosh = "mosh --ssh=\"/usr/bin/ssh\"";
};
profileExtra = ''
export COLORTERM=truecolor
SHELL="$BASH"
PATH=~/.local/bin:$PATH
bind "set show-mode-in-prompt on"
set -o vi || true
source <(fzf --bash)
VISUAL=vim
EDITOR="$VISUAL"
if [ -z "$CLAUDE_CODE_ENTRYPOINT" ]; then
fastfetch
fi
[[ -f ~/.bash_custom ]] && . ~/.bash_custom
source ${./config/hey-intern.sh}
''
+ cfg.customProfile;
};
programs.powerline-go = {
enable = true;
settings = {
git-mode = "compact";
theme = "gruvbox";
};
modules = [
"host"
"cwd"
"git"
"docker"
"venv"
];
};
programs.readline = {
enable = true;
extraConfig = ''
# Approximate VIM Dracula Colors
set vi-ins-mode-string \1\e[01;38;5;23;48;5;231m\2 I \1\e[38;5;231;48;5;238m\2\1\e[0m\2
set vi-cmd-mode-string \1\e[01;38;5;22;48;5;148m\2 C \1\e[38;5;148;48;5;238m\2\1\e[0m\2
'';
};
programs.fzf.enable = true;
home.packages = with pkgs; [
bashInteractive
fastfetch
mosh
nerd-fonts.meslo-lg
];
home.file.".config/fastfetch/config.jsonc".text = import ./config/fastfetch.nix { inherit cfg; };
home.file.".sqliterc".text = builtins.readFile ./config/.sqliterc;
};
}

View File

@@ -1,9 +1,4 @@
{ lib
, pkgs
, config
, namespace
, ...
}:
{ lib, config, namespace, ... }:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.btop;
@@ -14,12 +9,10 @@ in
};
config = mkIf cfg.enable {
programs.btop = {
enable = true;
package = pkgs.btop-cuda;
};
programs.btop.enable = true;
home.file.".config/btop/btop.conf".text = builtins.readFile ./config/btop.conf;
home.file.".config/btop/btop.conf".text =
builtins.readFile ./config/btop.conf;
home.file.".config/btop/themes/catppuccin_mocha.theme".text =
builtins.readFile ./config/catppuccin_mocha.theme;
};

View File

@@ -1,89 +0,0 @@
{ lib
, pkgs
, config
, namespace
, ...
}:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.claude-code;
in
{
options.${namespace}.programs.terminal.claude-code = {
enable = lib.mkEnableOption "enable claude-code";
};
config = mkIf cfg.enable {
programs.claude-code = {
enable = true;
mcpServers = {
gopls = {
type = "stdio";
command = "gopls";
args = [ "mcp" ];
};
};
};
programs.bash = lib.mkIf config.programs.bash.enable {
shellAliases = {
claude-custom = "default_claude_custom";
};
initExtra =
let
baseUrl = "https://llm-api.va.reichard.io";
authToken = "placeholder";
in
''
default_claude_custom() {
local model_id=""
while [[ $# -gt 0 ]]; do
case $1 in
-m|--model)
model_id="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -z "$model_id" ]; then
echo "Error: Model ID is required. Usage: claude --model <model-id>"
return 1
fi
ANTHROPIC_BASE_URL="${baseUrl}" \
ANTHROPIC_AUTH_TOKEN="${authToken}" \
ANTHROPIC_MODEL="$model_id" \
ANTHROPIC_SMALL_FAST_MODEL="$model_id" \
${lib.getExe pkgs.claude-code}
}
# Completion Function
_complete_claude_custom() {
local cur=''${COMP_WORDS[COMP_CWORD]}
local prev=''${COMP_WORDS[COMP_CWORD-1]}
if [[ "$prev" == "-m" || "$prev" == "--model" ]]; then
local models=( $(${pkgs.curl}/bin/curl -s -H "Authorization: Bearer ${authToken}" "${baseUrl}/v1/models" | ${pkgs.jq}/bin/jq -r '
.data[] |
select(
(try (.meta.llamaswap.type[] | contains("coding")) catch false) or
(.name | startswith("synthetic:"))
) |
.id
' 2>/dev/null) )
COMPREPLY=( $(compgen -W "''${models[*]}" -- "$cur") )
fi
}
# Register Completion
complete -F _complete_claude_custom claude-custom
'';
};
};
}

View File

@@ -1,38 +0,0 @@
{ lib
, pkgs
, config
, namespace
, ...
}:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.conduit;
in
{
options.${namespace}.programs.terminal.conduit = {
enable = lib.mkEnableOption "conduit";
};
config = mkIf cfg.enable {
# Add Conduit
home.packages = with pkgs; [
reichard.conduit
];
# Define Conduit Configuration
sops = {
secrets.conduit_apikey = {
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
};
templates."conduit.json" = {
path = "${config.xdg.configHome}/conduit/config.json";
content = builtins.toJSON {
server = "https://conduit.va.reichard.io";
api_key = "${config.sops.placeholder.conduit_apikey}";
log_level = "info";
log_format = "text";
};
};
};
};
}

View File

@@ -1,4 +1,3 @@
_scratch
.direnv
.envrc
.agents

View File

@@ -1,9 +1,4 @@
{ pkgs
, lib
, config
, namespace
, ...
}:
{ pkgs, lib, config, namespace, ... }:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.git;
@@ -16,13 +11,22 @@ in
config = mkIf cfg.enable {
programs.git = {
enable = true;
lfs.enable = true;
settings = {
aliases = {
lg = "log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all -n 15";
};
userName = "Evan Reichard";
aliases = {
lg = "log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all -n 15";
};
includes = [
{
path = "~/.config/git/work";
condition = "gitdir:~/Development/git/work/";
}
{
path = "~/.config/git/personal";
condition = "gitdir:~/Development/git/personal/";
}
];
extraConfig = {
user = {
name = "Evan Reichard";
email = "evan@reichard.io";
};
core = {
@@ -37,7 +41,6 @@ in
prune = true;
pruneTags = true;
all = true;
forceUpdateTags = true;
};
help = {
autocorrect = true;
@@ -70,16 +73,6 @@ in
autoSetupRemote = true;
};
};
includes = [
{
path = "~/.config/git/work";
condition = "gitdir:~/Development/git/work/";
}
{
path = "~/.config/git/personal";
condition = "gitdir:~/Development/git/personal/";
}
];
};
programs.gh = {
@@ -89,7 +82,10 @@ in
};
};
home.packages = with pkgs; [ gh ];
home.packages = with pkgs; [
gitAndTools.gh
pre-commit
];
# Copy Configuration
xdg.configFile = {

View File

@@ -1,36 +0,0 @@
{ lib
, pkgs
, config
, namespace
, ...
}:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.glimpse;
in
{
options.${namespace}.programs.terminal.glimpse = {
enable = lib.mkEnableOption "glimpse";
};
config = mkIf cfg.enable {
# Add Glimpse
home.packages = with pkgs; [
reichard.glimpse
];
# Define Glimpse Configuration
sops = {
secrets.kagi_token = {
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
};
templates."glimpse.json" = {
path = "${config.home.homeDirectory}/.config/glimpse/config.json";
content = builtins.toJSON {
search.provider = "kagi";
providers.kagi.token = "${config.sops.placeholder.kagi_token}";
};
};
};
};
}

View File

@@ -0,0 +1,8 @@
require('aerial').setup({
on_attach = function(bufnr)
vim.keymap.set('n', '{', '<cmd>AerialPrev<CR>', {buffer = bufnr})
vim.keymap.set('n', '}', '<cmd>AerialNext<CR>', {buffer = bufnr})
end
})
vim.keymap.set('n', '<leader>a', '<cmd>AerialToggle!<CR>')

View File

@@ -5,9 +5,6 @@
-- vim.cmd('colorscheme melange')
vim.cmd("colorscheme catppuccin-mocha")
-- Set User Shell
vim.o.shell = require("nix-vars").bash
-- Set Leader
vim.keymap.set("n", "<Space>", "<Nop>", { silent = true })
vim.g.mapleader = " "
@@ -23,7 +20,7 @@ vim.g.loaded_netrwPlugin = 1
vim.opt.termguicolors = true
-- Synchronize with system clipboard
vim.opt.clipboard:append("unnamedplus")
vim.opt.clipboard = "unnamed"
-- Always show the signcolumn
vim.opt.signcolumn = "yes"
@@ -38,3 +35,35 @@ vim.opt.shiftwidth = 2
vim.opt.foldmethod = "indent"
vim.opt.foldnestmax = 10
vim.opt.foldlevel = 2
-- Diagnostics Mappings
local diagnostics_active = true
local toggle_diagnostics = function()
diagnostics_active = not diagnostics_active
if diagnostics_active then
vim.diagnostic.enable()
else
vim.diagnostic.disable()
end
end
local diagnostics_loclist_active = false
local toggle_diagnostics_loclist = function()
diagnostics_loclist_active = not diagnostics_loclist_active
if diagnostics_loclist_active then
vim.diagnostic.setloclist()
else
vim.cmd("lclose")
end
end
local opts = { noremap = true, silent = true }
vim.keymap.set("n", "<leader>qt", toggle_diagnostics, opts)
vim.keymap.set("n", "<leader>qN", function()
vim.diagnostic.goto_prev({ float = false })
end, opts)
vim.keymap.set("n", "<leader>qn", function()
vim.diagnostic.goto_next({ float = false })
end, opts)
vim.keymap.set("n", "<leader>qq", toggle_diagnostics_loclist, opts)
vim.keymap.set("n", "<leader>qe", vim.diagnostic.open_float, opts)

View File

@@ -3,67 +3,67 @@ require("luasnip.loaders.from_vscode").lazy_load()
-- Check Tab Completion
local has_words_before = function()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and
vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col,
col)
:match("%s") == nil
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and
vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col,
col)
:match("%s") == nil
end
cmp.setup({
snippet = {
expand = function(args) require 'luasnip'.lsp_expand(args.body) end
},
snippet = {
expand = function(args) require'luasnip'.lsp_expand(args.body) end
},
mapping = cmp.mapping.preset.insert({
mapping = cmp.mapping.preset.insert({
-- Tab Completion
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end, { "i", "s" }),
-- Tab Completion
["<Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end, {"i", "s"}),
-- Reverse Tab Completion
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end, { "i", "s" }),
-- Reverse Tab Completion
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end, {"i", "s"}),
-- Misc Mappings
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm({ select = true })
-- Misc Mappings
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm({select = true})
}),
}),
-- Default Sources
sources = cmp.config.sources({
{ name = 'nvim_lsp' }, { name = 'luasnip' }, { name = 'path' },
{ name = 'buffer' }
})
-- Default Sources
sources = cmp.config.sources({
{name = 'nvim_lsp'}, {name = 'luasnip'}, {name = 'path'},
{name = 'buffer'}
})
})
-- Completion - `/` and `?`
cmp.setup.cmdline({ '/', '?' }, {
mapping = cmp.mapping.preset.cmdline(),
sources = { { name = 'buffer' } }
cmp.setup.cmdline({'/', '?'}, {
mapping = cmp.mapping.preset.cmdline(),
sources = {{name = 'buffer'}}
})
-- Completion = `:`
cmp.setup.cmdline(':', {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({ { name = 'path' }, { name = 'cmdline' } })
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({{name = 'path'}, {name = 'cmdline'}})
})
-- Autopairs

View File

@@ -3,48 +3,48 @@ local dapui = require("dapui")
local dapgo = require("dap-go")
dapui.setup({
controls = {
element = "repl",
enabled = true,
icons = {
disconnect = "",
pause = "",
play = "",
run_last = "",
step_back = "",
step_into = "",
step_out = "",
step_over = "",
terminate = ""
}
},
element_mappings = {},
expand_lines = false,
floating = { border = "single", mappings = { close = { "q", "<Esc>" } } },
force_buffers = true,
icons = { collapsed = "", current_frame = "", expanded = "" },
layouts = {
{
elements = { { id = "repl", size = 0.5 }, { id = "scopes", size = 0.5 } },
position = "bottom",
size = 10
}, {
elements = {
{ id = "breakpoints", size = 0.5 }, { id = "stacks", size = 0.5 }
},
position = "left",
size = 40
}
},
mappings = {
edit = "e",
expand = { "<CR>", "<2-LeftMouse>" },
open = "o",
remove = "d",
repl = "r",
toggle = "t"
},
render = { indent = 1, max_value_lines = 100 }
controls = {
element = "repl",
enabled = true,
icons = {
disconnect = "",
pause = "",
play = "",
run_last = "",
step_back = "",
step_into = "",
step_out = "",
step_over = "",
terminate = ""
}
},
element_mappings = {},
expand_lines = false,
floating = {border = "single", mappings = {close = {"q", "<Esc>"}}},
force_buffers = true,
icons = {collapsed = "", current_frame = "", expanded = ""},
layouts = {
{
elements = {{id = "repl", size = 0.5}, {id = "scopes", size = 0.5}},
position = "bottom",
size = 10
}, {
elements = {
{id = "breakpoints", size = 0.5}, {id = "stacks", size = 0.5}
},
position = "left",
size = 40
}
},
mappings = {
edit = "e",
expand = {"<CR>", "<2-LeftMouse>"},
open = "o",
remove = "d",
repl = "r",
toggle = "t"
},
render = {indent = 1, max_value_lines = 100}
})
dapgo.setup()
@@ -54,17 +54,17 @@ dap.listeners.before.launch.dapui_config = function() dapui.open() end
-- Continue Hotkey ("c")
vim.api.nvim_create_autocmd("FileType", {
pattern = "dap-repl",
callback = function()
vim.api.nvim_buf_set_keymap(0, 'n', 'c',
"<cmd>lua require'dap'.continue()<CR>",
{ noremap = true, silent = true })
end
pattern = "dap-repl",
callback = function()
vim.api.nvim_buf_set_keymap(0, 'n', 'c',
"<cmd>lua require'dap'.continue()<CR>",
{noremap = true, silent = true})
end
})
-- Create KeyMaps
local opts = { noremap = true, silent = true }
vim.keymap.set('n', '<leader>db', dap.toggle_breakpoint, vim.tbl_extend("force", { desc = "Toggle Breakpoint" }, opts))
vim.keymap.set('n', '<leader>dc', dap.continue, vim.tbl_extend("force", { desc = "Continue" }, opts))
vim.keymap.set('n', '<leader>dt', dapgo.debug_test, vim.tbl_extend("force", { desc = "Run Test" }, opts))
vim.keymap.set('n', '<leader>du', dapui.toggle, vim.tbl_extend("force", { desc = "Toggle UI" }, opts))
-- Leader Keys
local opts = {noremap = true, silent = true}
vim.keymap.set('n', '<leader>db', dap.toggle_breakpoint, opts)
vim.keymap.set('n', '<leader>du', dapui.toggle, opts)
vim.keymap.set('n', '<leader>dc', dap.continue, opts)
vim.keymap.set('n', '<leader>dt', dapgo.debug_test, opts)

View File

@@ -1,35 +0,0 @@
-- Diagnostics Mappings
local diagnostics_active = true
local toggle_diagnostics = function()
diagnostics_active = not diagnostics_active
if diagnostics_active then
vim.diagnostic.enable()
else
vim.diagnostic.disable()
end
end
local diagnostics_loclist_active = false
local toggle_diagnostics_loclist = function()
diagnostics_loclist_active = not diagnostics_loclist_active
if diagnostics_loclist_active then
vim.diagnostic.setloclist()
else
vim.cmd("lclose")
end
end
-- Create KeyMaps
local opts = { noremap = true, silent = true }
vim.keymap.set("n", "<leader>qN", function()
vim.diagnostic.goto_prev({ float = false })
end, vim.tbl_extend("force", { desc = "Previous Diagnostic" }, opts))
vim.keymap.set("n", "<leader>qe", vim.diagnostic.open_float,
vim.tbl_extend("force", { desc = "Open Diagnostics" }, opts))
vim.keymap.set("n", "<leader>qt", toggle_diagnostics,
vim.tbl_extend("force", { desc = "Toggle Inline Diagnostics" }, opts))
vim.keymap.set("n", "<leader>qn", function()
vim.diagnostic.goto_next({ float = false })
end, vim.tbl_extend("force", { desc = "Next Diagnostic" }, opts))
vim.keymap.set("n", "<leader>qq", toggle_diagnostics_loclist,
vim.tbl_extend("force", { desc = "Toggle Diagnostic List" }, opts))

View File

@@ -0,0 +1,6 @@
vim.keymap.set('n', '<leader>go', '<cmd>DiffviewOpen<CR>')
vim.keymap.set('n', '<leader>gO', '<cmd>DiffviewOpen origin/main...HEAD<CR>')
vim.keymap.set('n', '<leader>gh', '<cmd>DiffviewFileHistory<CR>')
vim.keymap.set('n', '<leader>gH',
'<cmd>DiffviewFileHistory --range=origin..HEAD<CR>')
vim.keymap.set('n', '<leader>gc', '<cmd>DiffviewClose<CR>')

View File

@@ -1,126 +0,0 @@
require("gitsigns").setup({
current_line_blame = true,
current_line_blame_opts = { delay = 0 },
on_attach = function(bufnr)
local gitsigns = require("gitsigns")
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
map("n", "<leader>gb", gitsigns.toggle_current_line_blame, { desc = "Git Blame Line" })
map("n", "<leader>gB", function()
gitsigns.blame_line({ full = true })
end, { desc = "Git Blame Full" })
end,
})
local function git_cmd(dir, cmd)
return vim.fn.system("git -C " .. vim.fn.escape(dir, " ") .. " " .. cmd)
end
local function git_cmd_list(dir, cmd)
return vim.fn.systemlist("git -C " .. vim.fn.escape(dir, " ") .. " " .. cmd)
end
local function yank(message)
vim.fn.setreg("+", message)
vim.notify("Copied:\n\t" .. message, vim.log.levels.INFO)
end
local function get_git_info()
local abs_path = vim.fn.expand("%:p")
local file_dir = vim.fn.fnamemodify(abs_path, ":h")
local git_root = git_cmd_list(file_dir, "rev-parse --show-toplevel")[1]
if vim.v.shell_error ~= 0 then
vim.notify("Failed to get git info", vim.log.levels.ERROR)
return
end
local git_repo = git_cmd(file_dir, "remote get-url origin"):match("([^/:]+/[^/.]+)%.?[^/]*$"):gsub("\n", "")
local git_branch = git_cmd(file_dir, "rev-parse --abbrev-ref HEAD"):gsub("\n", "")
return {
abs_path = abs_path,
file_dir = file_dir,
file = vim.fn.fnamemodify(abs_path, ":s?" .. git_root .. "/??"),
branch = git_branch,
repo = git_repo,
}
end
local function get_blame_info(file_dir, abs_path, line_num)
local blame_output = git_cmd_list(
file_dir,
"blame -L " .. line_num .. "," .. line_num .. " --porcelain " .. vim.fn.escape(abs_path, " ")
)
if vim.v.shell_error ~= 0 then
vim.notify("Failed to get git blame", vim.log.levels.ERROR)
return
end
local commit_hash = blame_output[1]:match("^(%S+)")
local original_line = blame_output[1]:match("^%S+ (%d+)")
local commit_file = nil
for _, line in ipairs(blame_output) do
local f = line:match("^filename (.+)$")
if f then
commit_file = f
break
end
end
if not commit_hash or not commit_file then
vim.notify("Failed to extract commit info", vim.log.levels.ERROR)
return
end
return {
hash = commit_hash,
line = tonumber(original_line),
file = commit_file,
file_hash = vim.fn.system("echo -n " .. vim.fn.shellescape(commit_file) .. " | sha256sum | cut -d' ' -f1"):gsub("\n",
""),
}
end
local function copy_git_link()
local git_info = get_git_info()
if not git_info then return end
local start_line = vim.fn.line("v")
local end_line = vim.fn.line(".")
yank(string.format(
"https://github.com/%s/blob/%s/%s#L%d-L%d",
git_info.repo, git_info.branch, git_info.file, start_line, end_line
))
end
local function copy_commit_url()
local git_info = get_git_info()
if not git_info then return end
local blame = get_blame_info(git_info.file_dir, git_info.abs_path, vim.fn.line("."))
if not blame then return end
yank(string.format(
"https://github.com/%s/commit/%s#diff-%sR%d",
git_info.repo, blame.hash, blame.file_hash, blame.line
))
end
-- Keymaps
vim.keymap.set("v", "<Leader>gy", copy_git_link, { desc = "Copy GitHub Link" })
vim.keymap.set("n", "<Leader>gC", copy_commit_url, { desc = "Copy Commit URL" })
vim.keymap.set('n', '<leader>go', '<cmd>DiffviewOpen<CR>', { desc = "Open Diff - Current" })
vim.keymap.set('n', '<leader>gO', '<cmd>DiffviewOpen origin/main...HEAD<CR>', { desc = "Open Diff - Main" })
vim.keymap.set('n', '<leader>gh', '<cmd>DiffviewFileHistory<CR>', { desc = "Diff History" })
vim.keymap.set('n', '<leader>gH', '<cmd>DiffviewFileHistory --range=origin..HEAD<CR>', { desc = "Diff History - Main" })
vim.keymap.set('n', '<leader>gc', '<cmd>DiffviewClose<CR>', { desc = "Close Diff" })

View File

@@ -0,0 +1,41 @@
function get_git_info()
local abs_path = vim.fn.expand("%:p")
local git_root = vim.fn.systemlist(
"git -C " .. vim.fn.escape(vim.fn.fnamemodify(abs_path, ":h"), " ") .. " rev-parse --show-toplevel"
)[1]
if vim.v.shell_error ~= 0 then
return
end
local git_repo = vim.fn.system("git remote get-url origin"):match("([^/:]+/[^/.]+)%.?[^/]*$"):gsub("\n", "")
local git_branch = vim.fn.system("git rev-parse --abbrev-ref HEAD"):gsub("\n", "")
return {
file = vim.fn.fnamemodify(abs_path, ":s?" .. git_root .. "/??"),
branch = git_branch,
repo = git_repo,
}
end
vim.keymap.set("v", "<Leader>gy", function()
local git_info = get_git_info()
if git_info == nil then
vim.notify("Failed to get git info", vim.log.levels.ERROR)
return
end
local start_line = vim.fn.line("v")
local end_line = vim.fn.line(".")
local message = string.format(
"https://github.com/%s/blob/%s/%s#L%d-L%d",
git_info.repo,
git_info.branch,
git_info.file,
start_line,
end_line
)
vim.fn.setreg("+", message)
vim.notify("Copied:\n\t" .. message, vim.log.levels.INFO)
end, { noremap = true, silent = true, desc = "Copy GitHub Link" })

View File

@@ -0,0 +1,16 @@
require('gitsigns').setup {
on_attach = function(bufnr)
local gitsigns = require('gitsigns')
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
map('n', '<leader>gb', gitsigns.toggle_current_line_blame)
map('n', '<leader>gB', function()
gitsigns.blame_line {full = true}
end)
end
}

View File

@@ -1,19 +1,23 @@
require("base")
require("aerial-config")
require("autopairs-config")
require("cmp-config")
require("comment-config")
require("dap-config")
require("diagnostics-config")
require("git-config")
require("diffview-config")
require("git-ref")
require("git-signs")
require("llm")
require("leap-config")
require("llm-config")
require("lsp-config")
require("lsp-lines-config")
require("lualine-config")
require("neotree-config")
require("noice-config")
require("numb-config")
require("octo-config")
require("snacks-config")
require("silicon-config")
require("telescope-config")
require("toggleterm-config")
require("ts-config")
require("weird-chars")
require("which-key-config")
require("weird-chars")

View File

@@ -1,87 +0,0 @@
local llm_endpoint = "https://llm-api.va.reichard.io"
local llm_assistant_model = "vllm-qwen3.6-27b-tools-text "
local llm_infill_model = "qwen3.5-4b-thinking"
local current_fim = "llama"
-- Copilot Configuration
vim.g.copilot_no_tab_map = true
vim.g.copilot_filetypes = { ["*"] = true }
-- LLama LLM FIM
vim.g.llama_config = {
endpoint = llm_endpoint .. "/infill",
model = llm_infill_model,
n_predict = 2048,
ring_n_chunks = 32,
enable_at_startup = (current_fim == "llama"), -- enable based on default
}
-- Toggle function for manual switching
local function switch_llm_fim_provider(switch_to)
if switch_to == "llama" then
vim.cmd("Copilot disable")
vim.cmd("LlamaEnable")
current_fim = "llama"
vim.notify("Llama FIM enabled", vim.log.levels.INFO)
else
vim.cmd("Copilot enable")
vim.cmd("LlamaDisable")
current_fim = "copilot"
vim.notify("Copilot FIM enabled", vim.log.levels.INFO)
end
end
-- Configure Code Companion
require("plugins.codecompanion.fidget-spinner"):init()
local codecompanion = require("codecompanion")
codecompanion.setup({
display = {
chat = {
show_token_count = true,
window = {
layout = "float",
width = 0.6,
}
}
},
adapters = {
http = {
opts = { show_defaults = false, },
copilot = "copilot",
llamaswap = function()
return require("codecompanion.adapters").extend("openai_compatible", {
formatted_name = "LlamaSwap",
name = "llamaswap",
schema = { model = { default = llm_assistant_model } },
env = { url = llm_endpoint },
})
end,
},
},
strategies = {
chat = { adapter = "llamaswap" },
inline = { adapter = "llamaswap" },
cmd = { adapter = "llamaswap" },
},
chat = { display = "telescope" },
memory = { opts = { chat = { enabled = true } } },
})
-- Create KeyMaps for Code Companion
vim.keymap.set("n", "<leader>aa", codecompanion.actions, { desc = "Actions" })
vim.keymap.set("n", "<leader>af", function()
if current_fim == "llama" then
switch_llm_fim_provider("copilot")
else
switch_llm_fim_provider("llama")
end
end, { desc = "Toggle FIM (Llama / Copilot)" })
vim.keymap.set("n", "<leader>ao", function() require("snacks.terminal").toggle("opencode") end,
{ desc = "Toggle OpenCode" })
vim.keymap.set("v", "<leader>ai", ":CodeCompanion<cr>", { desc = "Inline Prompt" })
vim.keymap.set({ "n", "v" }, "<leader>an", codecompanion.chat, { desc = "New Chat" })
vim.keymap.set({ "n", "t" }, "<leader>at", codecompanion.toggle, { desc = "Toggle Chat" })
vim.keymap.set('i', '<C-J>', 'copilot#Accept("\\<CR>")', {
expr = true,
replace_keycodes = false
})

View File

@@ -0,0 +1,20 @@
-- Configure LLama LLM
vim.g.llama_config = {
endpoint = "http://10.0.50.120:8080/infill",
api_key = "",
n_prefix = 256,
n_suffix = 64,
n_predict = 256,
t_max_prompt_ms = 500,
t_max_predict_ms = 500,
show_info = 2,
auto_fim = true,
max_line_suffix = 8,
max_cache_keys = 256,
ring_n_chunks = 8,
ring_chunk_size = 32,
ring_scope = 512,
ring_update_ms = 1000,
}
-- require("gen").setup({ model = "codegemma" })

View File

@@ -8,35 +8,22 @@ vim.api.nvim_create_autocmd("FileType", {
end,
})
require('render-markdown').setup({
completions = { lsp = { enabled = true } },
file_types = { 'markdown', 'codecompanion' },
html = {
-- CodeCompanion Markdown Tweaks
tag = {
file = { icon = '󰨸 ', highlight = 'Normal' },
buf = { icon = '󰂥 ', highlight = 'Normal' },
},
vim.filetype.add({
extension = {
templ = "templ",
},
})
------------------------------------------------------
---------------------- LSP Lines ---------------------
------------------------------------------------------
require("lsp_lines").setup()
vim.diagnostic.config({
virtual_text = false,
virtual_lines = true,
})
------------------------------------------------------
-------------------- Built-in LSP --------------------
------------------------------------------------------
local nix_vars = require("nix-vars")
local nvim_lsp = require("lspconfig")
local augroup = vim.api.nvim_create_augroup("LspFormatting", { clear = false })
local on_attach = function(client, bufnr)
if client:supports_method("textDocument/formatting") then
local bufopts = { noremap = true, silent = true, buffer = bufnr }
if client.supports_method("textDocument/formatting") then
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,
@@ -47,25 +34,17 @@ local on_attach = function(client, bufnr)
})
end
-- Create KeyMaps
local bufopts = { noremap = true, silent = true, buffer = bufnr }
vim.keymap.set("n", "K", vim.lsp.buf.hover, vim.tbl_extend("force", { desc = 'Hover documentation' }, bufopts))
vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help,
vim.tbl_extend("force", { desc = "Show signature help" }, bufopts))
vim.keymap.set("n", "<leader>lD", vim.lsp.buf.declaration,
vim.tbl_extend("force", { desc = "Go to declaration" }, bufopts))
vim.keymap.set("n", "<leader>ld", vim.lsp.buf.definition,
vim.tbl_extend("force", { desc = "Go to definition" }, bufopts))
vim.keymap.set("n", "<leader>li", vim.lsp.buf.implementation,
vim.tbl_extend("force", { desc = "Go to implementation" }, bufopts))
vim.keymap.set("n", "<leader>ln", vim.lsp.buf.rename, vim.tbl_extend("force", { desc = "Rename symbol" }, bufopts))
vim.keymap.set("n", "<leader>lr", vim.lsp.buf.references,
vim.tbl_extend("force", { desc = "Show references" }, bufopts))
vim.keymap.set("n", "<leader>lt", vim.lsp.buf.type_definition,
vim.tbl_extend("force", { desc = "Go to type definition" }, bufopts))
vim.keymap.set("n", "K", vim.lsp.buf.hover, bufopts)
vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help, bufopts)
vim.keymap.set("n", "<leader>lD", vim.lsp.buf.declaration, bufopts)
vim.keymap.set("n", "<leader>ld", vim.lsp.buf.definition, bufopts)
vim.keymap.set("n", "<leader>li", vim.lsp.buf.implementation, bufopts)
vim.keymap.set("n", "<leader>ln", vim.lsp.buf.rename, bufopts)
vim.keymap.set("n", "<leader>lr", vim.lsp.buf.references, bufopts)
vim.keymap.set("n", "<leader>lt", vim.lsp.buf.type_definition, bufopts)
vim.keymap.set("n", "<leader>lf", function()
vim.lsp.buf.format({ async = true, timeout_ms = 2000 })
end, vim.tbl_extend("force", { desc = "Format file" }, bufopts))
end, bufopts)
end
local on_attach_no_formatting = function(client, bufnr)
@@ -92,146 +71,81 @@ local organize_go_imports = function()
end
end
local default_config = {
flags = { debounce_text_changes = 150 },
capabilities = require("cmp_nvim_lsp").default_capabilities(),
on_attach = on_attach,
}
local setup_lsp = function(name, config, opts)
opts = opts or {}
local final_config = vim.tbl_deep_extend("force", default_config, config or {})
vim.lsp.config(name, final_config)
-- Manual Mode - Register a buffer-local command instead of auto-enabling.
-- The command name is derived from opts.cmd_name or the LSP name.
if opts.manual then
local cmd_name = opts.cmd_name or name:gsub("^%l", string.upper):gsub("_%l", function(s)
return s:sub(2):upper()
end)
local fts = final_config.filetypes or {}
vim.api.nvim_create_autocmd("FileType", {
pattern = fts,
callback = function(ev)
vim.api.nvim_buf_create_user_command(ev.buf, cmd_name, function()
vim.lsp.enable(name)
end, { desc = "Enable " .. name .. " LSP" })
end,
})
return
end
vim.lsp.enable(name)
end
-- Define LSP Flags & Capabilities
local lsp_flags = { debounce_text_changes = 150 }
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- Python LSP Configuration
setup_lsp("pyright", {
filetypes = { "starlark", "python" },
nvim_lsp.pyright.setup({
on_attach = on_attach,
flags = lsp_flags,
capabilities = capabilities,
})
-- HTML LSP Configuration
setup_lsp("html", {
nvim_lsp.html.setup({
on_attach = on_attach_no_formatting,
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.vscls .. "/bin/vscode-html-language-server", "--stdio" },
filetypes = { "htm", "html" },
})
-- JSON LSP Configuration
setup_lsp("jsonls", {
nvim_lsp.jsonls.setup({
on_attach = on_attach_no_formatting,
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.vscls .. "/bin/vscode-html-language-server", "--stdio" },
filetypes = { "json", "jsonc", "jsonl" },
})
-- CSS LSP Configuration
setup_lsp("cssls", {
nvim_lsp.cssls.setup({
on_attach = on_attach_no_formatting,
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.vscls .. "/bin/vscode-html-language-server", "--stdio" },
filetypes = { "css" },
})
-- Typescript / Javascript LSP Configuration
setup_lsp("ts_ls", {
nvim_lsp.ts_ls.setup({
on_attach = on_attach_no_formatting,
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.tsls, "--stdio" },
filetypes = { "typescript", "typescriptreact", "javascript" },
})
-- Oxlint - Prefer a project-local oxlint from node_modules so the LSP
-- version matches the project's pinned dependency; fall back to PATH.
setup_lsp("oxlint", {
-- Svelte LSP Configuration
nvim_lsp.svelte.setup({
on_attach = on_attach_no_formatting,
root_markers = { ".oxlintrc.json", "oxlint.config.json", "package.json", ".git" },
cmd = function(dispatchers, config)
local bin = "oxlint"
if config.root_dir then
local local_bin = config.root_dir .. "/node_modules/.bin/oxlint"
if vim.fn.executable(local_bin) == 1 then
bin = local_bin
end
end
return vim.lsp.rpc.start({ bin, "--lsp" }, dispatchers, {
cwd = config.root_dir,
})
end,
})
-- C LSP Configuration
setup_lsp("clangd", {
cmd = { nix_vars.clangd },
filetypes = { "c", "cpp", "objc", "objcpp", "cuda" },
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.sveltels, "--stdio" },
})
-- Lua LSP Configuration
setup_lsp("lua_ls", {
nvim_lsp.lua_ls.setup({
on_attach = on_attach_no_formatting,
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.luals },
filetypes = { "lua" },
})
-- Unison LSP Configuration
setup_lsp("unison")
-- SQL Configuration
-- NOTE: root_markers resolves root_dir but Neovim does NOT use root_dir as
-- the process cwd (cmd_cwd defaults to Neovim's cwd). We use cmd as a
-- function so we can read root_dir from the resolved config and build an
-- absolute path to .sqls.yml.
setup_lsp("sqls", {
on_attach = on_attach_no_formatting,
root_markers = { ".sqls.yml", ".git" },
cmd = function(dispatchers, config)
local cmd = { nix_vars.sqls }
if config.root_dir then
local config_file = config.root_dir .. "/.sqls.yml"
if vim.fn.filereadable(config_file) == 1 then
cmd = { nix_vars.sqls, "-config", config_file }
end
end
return vim.lsp.rpc.start(cmd, dispatchers, {
cwd = config.root_dir,
})
end,
-- Templ LSP Configuration
nvim_lsp.templ.setup({
on_attach = on_attach,
flags = lsp_flags,
capabilities = capabilities,
})
-- Nix LSP Configuration
setup_lsp("nil_ls", {
filetypes = { "nix" },
nvim_lsp.nil_ls.setup({
on_attach = on_attach,
flags = lsp_flags,
capabilities = capabilities,
})
-- Omnisharp LSP Configuration
-- NOTE: https://github.com/NixOS/nixpkgs/issues/479348
-- setup_lsp("omnisharp", {
-- enable_roslyn_analyzers = true,
-- enable_import_completion = true,
-- organize_imports_on_format = true,
-- enable_decompilation_support = true,
-- filetypes = { "cs", "vb", "csproj", "sln", "slnx", "props", "csx", "targets", "tproj", "slngen", "fproj" },
-- cmd = { nix_vars.omnisharp, "--languageserver", "--hostPID", tostring(vim.fn.getpid()) },
-- })
-- Go LSP Configuration
setup_lsp("gopls", {
nvim_lsp.gopls.setup({
on_attach = function(client, bufnr)
on_attach(client, bufnr)
vim.api.nvim_create_autocmd("BufWritePre", {
@@ -240,8 +154,9 @@ setup_lsp("gopls", {
callback = organize_go_imports,
})
end,
filetypes = { "go" },
cmd = { "gopls", "-remote=auto" },
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.gopls },
settings = {
gopls = {
buildFlags = { "-tags=e2e" },
@@ -250,10 +165,11 @@ setup_lsp("gopls", {
})
-- Go LSP Linting
setup_lsp("golangci_lint_ls", {
nvim_lsp.golangci_lint_ls.setup({
on_attach = on_attach_no_formatting,
flags = lsp_flags,
capabilities = capabilities,
cmd = { nix_vars.golintls },
filetypes = { "go" },
init_options = {
command = {
"golangci-lint",
@@ -264,35 +180,51 @@ setup_lsp("golangci_lint_ls", {
"--issues-exit-code=1",
},
},
}, { manual = true, cmd_name = "GolangciLint" })
})
------------------------------------------------------
--------------------- None-LS LSP --------------------
--------------------- Null-LS LSP --------------------
------------------------------------------------------
local none_ls = require("null-ls")
local null_ls = require("null-ls")
none_ls.setup({
local eslintFiles = {
".eslintrc",
".eslintrc.js",
".eslintrc.cjs",
".eslintrc.yaml",
".eslintrc.yml",
".eslintrc.json",
"eslint.config.js",
"eslint.config.mjs",
"eslint.config.cjs",
"eslint.config.ts",
"eslint.config.mts",
"eslint.config.cts",
}
has_eslint_in_parents = function(fname)
root_file = nvim_lsp.util.insert_package_json(eslintFiles, "eslintConfig", fname)
return nvim_lsp.util.root_pattern(unpack(root_file))(fname)
end
null_ls.setup({
sources = {
-- Formatting
none_ls.builtins.formatting.prettier,
none_ls.builtins.formatting.prettier.with({ filetypes = { "template" } }),
none_ls.builtins.formatting.nixpkgs_fmt, -- TODO: nixd native LSP?
none_ls.builtins.formatting.sql_formatter.with({
extra_args = {
"--config",
'{"tabWidth":4,"keywordCase":"upper","language":"sql"}',
}
-- Prettier Formatting
null_ls.builtins.formatting.prettier,
null_ls.builtins.formatting.prettier.with({ filetypes = { "template" } }),
require("none-ls.diagnostics.eslint_d").with({
condition = function(utils)
return has_eslint_in_parents(vim.fn.getcwd())
end,
}),
require("none-ls.formatting.autopep8").with({
filetypes = { "starlark", "python" },
extra_args = { "--max-line-length", "100" },
}),
-- Completion
none_ls.builtins.completion.spell,
null_ls.builtins.completion.spell,
null_ls.builtins.formatting.nixpkgs_fmt,
null_ls.builtins.formatting.stylua,
null_ls.builtins.diagnostics.sqlfluff,
null_ls.builtins.formatting.sqlfluff,
},
on_attach = function(client, bufnr)
if client:supports_method("textDocument/formatting") then
if client.supports_method("textDocument/formatting") then
vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
vim.api.nvim_create_autocmd("BufWritePre", {
group = augroup,

View File

@@ -0,0 +1,2 @@
require("lsp_lines").setup()
vim.diagnostic.config({virtual_text = false})

View File

@@ -3,28 +3,23 @@ local cached_pr_status = ""
-- Read process output
local function read_output(err, data)
if err then
return
end
if not data then
return
end
cached_pr_status = data
if err then return end
if not data then return end
cached_pr_status = data
end
-- Spawn process
local function execute_command()
local stdout = vim.loop.new_pipe(false)
local stdout = vim.loop.new_pipe(false)
local spawn_opts = {
detached = true,
stdio = { nil, stdout, nil },
args = { "-c", "gh pr checks | awk -F'\t' '{ print $2 }'" },
}
local spawn_opts = {
detached = true,
stdio = {nil, stdout, nil},
args = {"-c", "gh pr checks | awk -F'\t' '{ print $2 }'"}
}
vim.loop.spawn("bash", spawn_opts, function()
stdout:read_start(read_output)
end)
vim.loop.spawn("bash", spawn_opts,
function() stdout:read_start(read_output) end)
end
-- Spawn & schedule process
@@ -32,26 +27,23 @@ execute_command()
vim.fn.timer_start(300000, execute_command)
-- Return status from cache
local function pr_status()
--   
--    
--
-- PENDING COLOR - #d29922
-- PASS COLOR - #3fb950
-- FAIL COLOR - #f85149
return cached_pr_status
:gsub("\n", "")
:gsub("fail", " ")
:gsub("pass", "")
:gsub("pending", "")
:gsub("skipping", "")
:sub(1, -2)
function pr_status()
--   
--    
--
-- PENDING COLOR - #d29922
-- PASS COLOR - #3fb950
-- FAIL COLOR - #f85149
return cached_pr_status:gsub("\n", ""):gsub("fail", ""):gsub("pass",
"")
:gsub("pending", ""):gsub("skipping", " "):sub(1, -2)
end
require("lualine").setup({
options = { theme = "catppuccin" },
sections = {
lualine_c = { { pr_status } },
-- lualine_z = { require("opencode").statusline }
},
require('lualine').setup({
options = {
theme = "gruvbox_dark"
-- theme = "nord"
-- theme = "OceanicNext",
},
sections = {lualine_c = {{pr_status}}}
})

View File

@@ -0,0 +1,2 @@
require("neo-tree").setup({ window = { mappings = { ["<space>"] = "none" } } })
vim.keymap.set("n", "<leader>t", ":Neotree toggle<CR>", { silent = true })

View File

@@ -1,14 +1,26 @@
-- Noice Doc Scrolling
vim.keymap.set("n", "<c-f>", function()
if not require("noice.lsp").scroll(4) then return "<c-f>" end
end, {silent = true, expr = true})
vim.keymap.set("n", "<c-b>", function()
if not require("noice.lsp").scroll(-4) then return "<c-b>" end
end, {silent = true, expr = true})
-- Noice Setup
require("noice").setup({
-- Ignore (Snacks Priority)
routes = {
{
filter = { event = "ui", kind = "input", },
opts = { skip = true },
},
{
filter = { event = "ui", kind = "select", },
opts = { skip = true },
},
},
lsp = {
override = {
["vim.lsp.util.convert_input_to_markdown_lines"] = true,
["vim.lsp.util.stylize_markdown"] = true,
["cmp.entry.get_documentation"] = false
},
signature = {enabled = false}
},
presets = {
command_palette = true, -- position the cmdline and popupmenu together
long_message_to_split = true, -- long messages will be sent to a split
inc_rename = false, -- enables an input dialog for inc-rename.nvim
lsp_doc_border = false -- add a border to hover docs and signature help
}
})

View File

@@ -1,13 +0,0 @@
require("octo").setup()
-- Create KeyMaps
vim.keymap.set("n", "<leader>rs", "<cmd>Octo review start<cr>")
vim.keymap.set("n", "<leader>rd", "<cmd>Octo review discard<cr>")
vim.keymap.set("n", "<leader>rr", "<cmd>Octo review resume<cr>")
vim.keymap.set("n", "<leader>re", "<cmd>Octo review submit<cr>")
vim.keymap.set("n", "<leader>rca", "<cmd>Octo review comments<cr>")
vim.keymap.set("n", "<leader>rcs", "<cmd>Octo comment suggest<cr>")
vim.keymap.set("n", "<leader>rcc", "<cmd>Octo comment add<cr>")
vim.keymap.set("n", "<leader>rcr", "<cmd>Octo comment reply<cr>")
vim.keymap.set("n", "<leader>pd", "<cmd>Octo pr diff<cr>")
vim.keymap.set("n", "<leader>pc", "<cmd>Octo pr changes<cr>")

View File

@@ -1,71 +0,0 @@
local progress = require("fidget.progress")
local M = {}
function M:init()
local group = vim.api.nvim_create_augroup("CodeCompanionFidgetHooks", {})
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "CodeCompanionRequestStarted",
group = group,
callback = function(request)
local handle = M:create_progress_handle(request)
M:store_progress_handle(request.data.id, handle)
end,
})
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "CodeCompanionRequestFinished",
group = group,
callback = function(request)
local handle = M:pop_progress_handle(request.data.id)
if handle then
M:report_exit_status(handle, request)
handle:finish()
end
end,
})
end
M.handles = {}
function M:store_progress_handle(id, handle)
M.handles[id] = handle
end
function M:pop_progress_handle(id)
local handle = M.handles[id]
M.handles[id] = nil
return handle
end
function M:create_progress_handle(request)
return progress.handle.create({
title = " Requesting assistance (" .. request.data.adapter.formatted_name .. ")",
message = "In progress...",
lsp_client = {
name = M:llm_role_title(request.data.adapter),
},
})
end
function M:llm_role_title(adapter)
local parts = {}
table.insert(parts, adapter.formatted_name)
if adapter.model and adapter.model ~= "" then
table.insert(parts, "(" .. adapter.model .. ")")
end
return table.concat(parts, " ")
end
function M:report_exit_status(handle, request)
if request.data.status == "success" then
handle.message = "Completed"
elseif request.data.status == "error" then
handle.message = " Error"
else
handle.message = "󰜺 Cancelled"
end
end
return M

View File

@@ -0,0 +1,10 @@
local silicon = require('silicon')
silicon.setup({})
vim.keymap.set('v', '<Leader>ss', function() silicon.visualise_api({}) end)
vim.keymap.set('v', '<Leader>sb',
function() silicon.visualise_api({show_buf = true}) end)
vim.keymap.set('n', '<Leader>sv',
function() silicon.visualise_api({visible = true}) end)
vim.keymap.set('n', '<Leader>sb',
function() silicon.visualise_api({show_buf = true}) end)

View File

@@ -1,23 +0,0 @@
-- Snacks Setup
local snacks = require("snacks")
snacks.setup({
input = { enabled = true, style = "popup" },
select = { enabled = true, style = "popup" },
})
-- Create KeyMaps
vim.keymap.set("n", "<leader>fb", snacks.picker.buffers, { desc = "Buffers" })
vim.keymap.set("n", "<leader>fd", snacks.picker.diagnostics, { desc = "Diagnostics" })
vim.keymap.set("n", "<leader>ff", snacks.picker.files, { desc = "Find Files" })
vim.keymap.set("n", "<leader>fg", snacks.picker.grep, { desc = "Grep Files" })
vim.keymap.set("n", "<leader>fh", snacks.picker.help, { desc = "Help Tags" })
vim.keymap.set("n", "<leader>fj", snacks.picker.jumps, { desc = "Jump List" })
vim.keymap.set("n", "<leader>fl", function() snacks.terminal("lazygit") end, { desc = "LazyGit" })
vim.keymap.set("n", "<leader>fp", snacks.picker.gh_pr, { desc = "GitHub Pull Requests" })
vim.keymap.set("n", "<leader>fs", snacks.picker.lsp_symbols, { desc = "Symbols" })
vim.keymap.set("n", "<leader>fu", snacks.picker.undo, { desc = "Undo History" })
vim.keymap.set("n", "gI", snacks.picker.lsp_implementations, { desc = "LSP Implementations" })
vim.keymap.set("n", "gi", snacks.picker.lsp_incoming_calls, { desc = "LSP Incoming Calls" })
vim.keymap.set("n", "go", snacks.picker.lsp_outgoing_calls, { desc = "LSP Outgoing Calls" })
vim.keymap.set("n", "gr", snacks.picker.lsp_references, { desc = "LSP References" })
vim.keymap.set("n", [[<c-\>]], function() snacks.terminal() end, { desc = "Toggle Terminal" })

View File

@@ -0,0 +1,20 @@
require('telescope').setup {
extensions = {
fzf = {
fuzzy = true,
override_generic_sorter = true,
override_file_sorter = true,
case_mode = "smart_case"
}
}
}
require('telescope').load_extension('fzf')
require("telescope").load_extension("ui-select")
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, {})
vim.keymap.set('n', '<leader>fg', builtin.live_grep, {})
vim.keymap.set('n', '<leader>fb', builtin.buffers, {})
vim.keymap.set('n', '<leader>fh', builtin.help_tags, {})
vim.keymap.set('n', '<leader>fj', builtin.jumplist, {})

View File

@@ -1,20 +1,20 @@
-- require("toggleterm").setup({open_mapping = [[<c-\>]]})
--
-- -- Get PR status on terminal load
-- -- require("toggleterm").setup({
-- -- open_mapping = [[<c-\>]],
-- -- on_create = function(term)
-- -- vim.cmd("startinsert")
-- -- term:send("gh pr checks")
-- -- end
-- -- })
--
-- -- Duplicate C-w & Esc Behavior
-- function _G.set_terminal_keymaps()
-- local opts = {buffer = 0}
-- vim.opt.signcolumn = "no"
-- vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], opts)
-- vim.keymap.set('t', '<C-w>', [[<C-\><C-n><C-w>]], opts)
-- end
--
-- vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()')
require("toggleterm").setup({open_mapping = [[<c-\>]]})
-- Get PR status on terminal load
-- require("toggleterm").setup({
-- open_mapping = [[<c-\>]],
-- on_create = function(term)
-- vim.cmd("startinsert")
-- term:send("gh pr checks")
-- end
-- })
-- Duplicate C-w & Esc Behavior
function _G.set_terminal_keymaps()
local opts = {buffer = 0}
vim.opt.signcolumn = "no"
vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], opts)
vim.keymap.set('t', '<C-w>', [[<C-\><C-n><C-w>]], opts)
end
vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()')

View File

@@ -1,4 +1,3 @@
require("nvim-treesitter.configs").setup({
highlight = { enable = true, additional_vim_regex_highlighting = false },
})
vim.treesitter.language.register("markdown", "octo")
require'nvim-treesitter.configs'.setup {
highlight = {enable = true, additional_vim_regex_highlighting = false}
}

View File

@@ -1,9 +1,47 @@
local wk = require("which-key")
wk.setup({})
wk.add({
{ "<leader>a", group = "LLM" }, -- llm-config.lua
{ "<leader>f", group = "Find" }, -- snacks-config.lua
{ "<leader>l", group = "LSP" }, -- lsp-config.lua
{ "<leader>g", group = "Git" }, -- git-config.lua
{ "<leader>q", group = "Diagnostics" }, -- diagnostics-config.lua
{ "<leader>d", group = "Debug" }, -- dap-config.lua
{ "<C-k>", desc = "Signature Help" },
{ "<leader>a", desc = "Aerial" },
{ "<leader>d", group = "Debug" },
{ "<leader>db", desc = "Toggle Breakpoint" },
{ "<leader>dc", desc = "Continue" },
{ "<leader>dt", desc = "Run Test" },
{ "<leader>du", desc = "Toggle UI" },
{ "<leader>f", group = "Find - Telescope" },
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Find Buffer" },
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find File" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live Grep" },
{ "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help Tags" },
{ "<leader>fj", "<cmd>Telescope jumplist<cr>", desc = "Jump List" },
{ "<leader>g", group = "DiffView" },
{ "<leader>gB", desc = "Git Blame Full" },
{ "<leader>gH", "<cmd>DiffviewFileHistory --range=origin..HEAD<cr>", desc = "Diff History - Main" },
{ "<leader>gO", "<cmd>DiffviewOpen origin/main...HEAD<cr>", desc = "Open Diff - Main" },
{ "<leader>gb", desc = "Git Blame Line" },
{ "<leader>gc", "<cmd>DiffviewClose<cr>", desc = "Close Diff" },
{ "<leader>gh", "<cmd>DiffviewFileHistory<cr>", desc = "Diff History" },
{ "<leader>go", "<cmd>DiffviewOpen<cr>", desc = "Open Diff - Current" },
{ "<leader>l", group = "LSP" },
{ "<leader>lD", desc = "Declaration" },
{ "<leader>ld", desc = "Definition" },
{ "<leader>lf", desc = "Format" },
{ "<leader>li", desc = "Implementation" },
{ "<leader>ln", desc = "Rename" },
{ "<leader>lr", desc = "References" },
{ "<leader>lt", desc = "Type Definition" },
{ "<leader>q", group = "Diagnostics" },
{ "<leader>qN", desc = "Previous Diagnostic" },
{ "<leader>qe", desc = "Open Diagnostic Float" },
{ "<leader>qn", desc = "Next Diagnostic" },
{ "<leader>qq", desc = "Toggle Diagnostic List" },
{ "<leader>qt", desc = "Toggle Inline Diagnostics" },
{ "<leader>sv", desc = "Visual Screenshot" },
{ "<leader>t", desc = "NeoTree" },
{ "K", desc = "Definition Hover" },
{ "<leader>ss", desc = "Selected Screenshot", mode = "v" },
{ "<leader>s", group = "Screenshot", mode = { "n", "v" } },
{ "<leader>sb", desc = "Buffer Screenshot", mode = { "n", "v" } },
})

View File

@@ -1,9 +1,4 @@
{ pkgs
, lib
, config
, namespace
, ...
}:
{ pkgs, lib, config, namespace, ... }:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.nvim;
@@ -39,29 +34,31 @@ in
# -------------------
# ----- Helpers -----
# -------------------
aerial-nvim # Code Outline
comment-nvim # Code Comments
copilot-vim # GitHub Copilot
diffview-nvim # Diff View
fidget-nvim # Notification Helper
gitsigns-nvim # Git Blame
leap-nvim # Quick Movement
markdown-preview-nvim # Markdown Preview
neo-tree-nvim # File Explorer
none-ls-nvim # Formatters
numb-nvim # Peek / Jump to Lines
nvim-autopairs # Automatically Close Pairs (),[],{}
octo-nvim # Git Octo
render-markdown-nvim # Markdown Renderer
snacks-nvim # Snacks
telescope-fzf-native-nvim # Faster Telescope
telescope-nvim # Fuzzy Finder
telescope-ui-select-nvim # UI
toggleterm-nvim # Terminal Helper
vim-nix # Nix Helpers
which-key-nvim # Shortcut Helper
# ------------------
# --- Theme / UI ---
# ------------------
catppuccin-nvim # Theme
lualine-nvim # Bottom Line
noice-nvim # UI Tweaks
# nord-nvim # Theme
# melange-nvim # Theme
catppuccin-nvim # Theme
nvim-notify # Noice Dependency
nvim-web-devicons # Dev Icons
@@ -78,61 +75,87 @@ in
nvim-dap-go
nvim-dap-ui
# --------------------
# -- CODE COMPANION --
# --------------------
(pkgs.vimUtils.buildVimPlugin {
pname = "codecompanion.nvim";
version = "2025-12-20";
src = pkgs.fetchFromGitHub {
owner = "olimorris";
repo = "codecompanion.nvim";
rev = "a226ca071ebc1d8b5ae1f70800fa9cf4a06a2101";
sha256 = "sha256-F1nI7q98SPpDjlwPvGy/qFuHvlT1FrbQPcjWrBwLaHU=";
};
doCheck = false;
meta.homepage = "https://github.com/olimorris/codecompanion.nvim/";
meta.hydraPlatforms = [ ];
})
# --------------------
# -- NONE-LS EXTRAS --
# --------------------
(pkgs.vimUtils.buildVimPlugin {
pname = "none-ls-extras.nvim";
version = "2025-10-28";
src = pkgs.fetchFromGitHub {
owner = "nvimtools";
repo = "none-ls-extras.nvim";
rev = "402c6b5c29f0ab57fac924b863709f37f55dc298";
sha256 = "sha256-4s/xQNWNA4dgb5gZR4Xqn6zDDWrSJNtmHOmmjmYnN/8=";
};
doCheck = false;
meta.homepage = "https://github.com/nvimtools/none-ls-extras.nvim/";
})
(
pkgs.vimUtils.buildVimPlugin {
pname = "none-ls-extras.nvim";
version = "2024-06-11";
src = pkgs.fetchFromGitHub {
owner = "nvimtools";
repo = "none-ls-extras.nvim";
rev = "336e84b9e43c0effb735b08798ffac382920053b";
sha256 = "sha256-UtU4oWSRTKdEoMz3w8Pk95sROuo3LEwxSDAm169wxwk=";
};
meta.homepage = "https://github.com/nvimtools/none-ls-extras.nvim/";
}
)
# -------------------
# ----- Silicon -----
# -------------------
(
pkgs.vimUtils.buildVimPlugin {
pname = "silicon.lua";
version = "2022-12-03";
src = pkgs.fetchFromGitHub {
owner = "mhanberg";
repo = "silicon.lua";
rev = "5ca462bee0a39b058786bc7fbeb5d16ea49f3a23";
sha256 = "0vlp645d5mmii513v72jca931miyrhkvhwb9bfzhix1199zx7vi2";
};
meta.homepage = "https://github.com/mhanberg/silicon.lua/";
}
)
# -------------------
# ------- LLM -------
# -------------------
(
pkgs.vimUtils.buildVimPlugin {
pname = "llm.nvim";
version = "2024-05-25";
src = pkgs.fetchFromGitHub {
owner = "David-Kunz";
repo = "gen.nvim";
rev = "bd19cf584b5b82123de977b44105e855e61e5f39";
sha256 = "sha256-0AEB6im8Jz5foYzmL6KEGSAYo48g1bkFpjlCSWT6JeE=";
};
meta.homepage = "https://github.com/David-Kunz/gen.nvim/";
}
)
# -------------------
# ---- LLAMA.VIM ----
# -------------------
(pkgs.vimUtils.buildVimPlugin {
pname = "llama.vim";
version = "2025-10-28";
src = pkgs.fetchFromGitHub {
owner = "ggml-org";
repo = "llama.vim";
rev = "ade8966eff57dcbe4a359dd26fb1ea97378ea03c";
sha256 = "sha256-uPqOZLWKVMimhc9eG7yM5OmhJy3mTRgKsiqKhstWs4Y=";
};
meta.homepage = "https://github.com/ggml-org/llama.vim/";
})
(
pkgs.vimUtils.buildVimPlugin {
pname = "llama.vim";
version = "2025-01-23";
src = pkgs.fetchFromGitHub {
owner = "ggml-org";
repo = "llama.vim";
rev = "143fe910b8d47a054ed464c38d8b7c17d5354468";
sha256 = "sha256-PW0HKzhSxcZiWzpDOuy98rl/X0o2nE7tMjZjwwh0qLE=";
};
meta.homepage = "https://github.com/ggml-org/llama.vim/";
}
)
];
extraPackages = with pkgs; [
# LSP
eslint_d
# Telescope Dependencies
fd
ripgrep
tree-sitter
# LSP Dependencies
go
golangci-lint
golangci-lint-langserver
gopls
lua-language-server
nil
nodePackages.eslint
@@ -141,20 +164,17 @@ in
nodePackages.typescript-language-server
nodePackages.vscode-langservers-extracted
pyright
python312Packages.autopep8
eslint_d
# Formatters
luaformatter
nixpkgs-fmt
nodePackages.prettier
sqlfluff
stylua
sql-formatter
# Tools
ripgrep
lazygit
git
gh
# Silicon
silicon
];
extraConfig = ":luafile ~/.config/nvim/lua/init.lua";
@@ -168,19 +188,14 @@ in
};
# Generate Nix Vars
# NOTE: https://github.com/NixOS/nixpkgs/issues/479348
# omnisharp = "${pkgs.omnisharp-roslyn}/bin/OmniSharp",
"nvim/lua/nix-vars.lua".text = ''
local nix_vars = {
bash = "${pkgs.bashInteractive}/bin/bash",
clangd = "${pkgs.clang-tools}/bin/clangd",
golintls = "${pkgs.golangci-lint-langserver}/bin/golangci-lint-langserver",
gopls = "${pkgs.gopls}/bin/gopls",
luals = "${pkgs.lua-language-server}/bin/lua-language-server",
sveltels = "${pkgs.nodePackages.svelte-language-server}/bin/svelteserver",
tsls = "${pkgs.nodePackages.typescript-language-server}/bin/typescript-language-server",
golintls = "${pkgs.golangci-lint-langserver}/bin/golangci-lint-langserver",
vscls = "${pkgs.nodePackages.vscode-langservers-extracted}",
sqls = "${pkgs.sqls}/bin/sqls",
}
return nix_vars
'';

View File

@@ -1,65 +0,0 @@
---
description: Creates and configures new OpenCode agents based on requirements
mode: subagent
temperature: 0.3
permission:
write: allow
---
You help users create custom OpenCode agents. When asked to create an agent:
1. **Understand the need**: Ask clarifying questions about:
- What tasks should this agent handle?
- Should it be primary or subagent?
- What tools does it need access to?
- Any special permissions or restrictions?
- Should it use a specific model?
2. **Generate the config**: Create a markdown file in the appropriate location:
- Global: `~/.config/opencode/agent/`
- Project: `.opencode/agent/`
3. **Available config options**:
- `description` (required): Brief description of agent purpose
- `mode`: "primary", "subagent", or "all" (defaults to "all")
- `temperature`: 0.0-1.0 (lower = focused, higher = creative)
- `maxSteps`: Limit agentic iterations
- `disable`: Set to true to disable agent
- `tools`: Control tool access (write, edit, bash, etc.)
- `permission`: Set to "ask", "allow", or "deny" for edit/bash/webfetch
- Additional provider-specific options pass through to the model
4. **Tools configuration**:
- Set individual tools: `write: true`, `bash: false`
- Use wildcards: `mymcp_*: false`
- Inherits from global config, agent config overrides
5. **Permissions** (for edit, bash, webfetch):
- `ask`: Prompt before running
- `allow`: Run without approval
- `deny`: Disable completely
- Can set per-command for bash: `"git push": "ask"`
6. **Keep it simple**: Start minimal, users can extend later.
7. **Explain usage**: Tell them how to invoke with `@agent-name`.
Example structure:
```markdown
---
description: [one-line purpose]
mode: subagent
model: anthropic/claude-sonnet-4-20250514
temperature: 0.2
tools:
write: false
bash: false
permission:
edit: deny
---
[Clear instructions for the agent's behavior]
```
Be conversational. Ask questions before generating.

View File

@@ -1,51 +0,0 @@
---
description: Implements code from plans and review feedback
mode: subagent
temperature: 0.3
permission:
"*": deny
bash: allow
context7_*: allow
edit: allow
glob: allow
grep: allow
list: allow
lsp: allow
read: allow
todoread: allow
todowrite: allow
---
You implement code. You're the only agent that modifies files.
**Input:**
- Plan file path from @planner
- Optional: Review feedback from @reviewer
**Workflow:**
1. Read the plan file
2. Read the specific files/lines mentioned in context maps
3. Read incrementally if needed (imports, function definitions, etc.)
4. Implement changes and accompanying tests
5. Commit:
```bash
git add -A
git commit -m "type: description"
```
Types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`
**Testing Requirements:**
- All tests must pass before committing
- Never ignore or skip failing tests
- If tests fail: either fix the code or fix the test
- Run tests after implementation to verify
**Rules:**
- Trust the plan - don't re-analyze or re-plan
- Start with context map locations, expand only as needed
- Fix all critical/regular findings, use judgment on nits
- Stop reading once you understand the change

View File

@@ -1,37 +0,0 @@
---
description: Orchestrates development by delegating to subagents
mode: primary
temperature: 0.2
maxSteps: 50
permission:
"*": deny
task:
"*": deny
planner: allow
developer: allow
reviewer: allow
---
You orchestrate development by delegating to subagents. Never code yourself.
**Subagents:**
- **@planner** - Creates implementation plans in `./plans/`
- **@developer** - Implements from plan files
- **@reviewer** - Reviews implementations
**Workflow:**
1. **Plan**: Call @planner with requirements
2. **Review Plan**: Show user the plan path, ask for approval
3. **Develop**: Call @developer with plan file path
4. **Review Code**: Call @reviewer with implementation
5. **Iterate**: If NEEDS_WORK, call @developer with plan + feedback
6. **Done**: When APPROVED or APPROVED_WITH_NITS
**Rules:**
- Always pass plan file path to @developer (not plan content)
- Include review feedback on iterations
- Nits are optional - ask user if they want them fixed
- Keep user informed of current step

View File

@@ -1,100 +0,0 @@
---
description: Explores codebase and breaks features into ordered implementation tasks. Writes plans to ./plans/
mode: subagent
temperature: 0.3
permission:
"*": deny
context7_*: allow
edit: allow
glob: allow
grep: allow
list: allow
lsp: allow
read: allow
---
# Code Task Planner Agent
You are a code analysis agent that breaks down feature requests into implementable, independent tasks.
## Your Task
1. **Analyze the codebase** using available tools (grep, lsp, read, etc.)
2. **Identify dependencies** between components
3. **Create ordered tasks** where each task can be implemented independently
4. **Generate context maps** showing exact files and line numbers that need changes
5. **Write the plan** to `./plans/<PLAN_NAME>.md`
## Task Requirements
- **Independent**: Each task should be implementable without future tasks
- **Hierarchical**: Dependencies must come before dependents
- **Specific**: Include exact file paths and line numbers
- **Contextual**: Explain WHY each file matters (1-2 lines max)
## Output Format
Write to `./plans/<PLAN_NAME>.md` with this structure:
```markdown
# Plan: <PLAN_NAME>
## Feature Overview
<feature summary>
## Implementation Tasks
### Task 1: <Descriptive Title>
**Context Map:**
- `<file_path>:<line_number>` - <why it's relevant or what changes>
- `<file_path>:<line_number>` - <why it's relevant or what changes>
---
### Task 2: <Descriptive Title>
**Context Map:**
- `<file_path>:<line_number>` - <why it's relevant or what changes>
---
```
## Analysis Strategy
1. **Start with interfaces/contracts** - these are foundational
2. **Then implementations** - concrete types that satisfy interfaces
3. **Then handlers/controllers** - code that uses the implementations
4. **Finally integrations** - wiring everything together
## Context Map Guidelines
- Use exact line numbers from actual code analysis
- Be specific: "Add AddChat method" not "modify file"
- Include both new additions AND modifications to existing code
- If a file doesn't exist yet, use line 0 and note "new file"
## Example
```markdown
### Task 1: Add Store Interface Methods
**Context Map:**
- `./internal/store/interface.go:15` - Add Conversation struct definition
- `./internal/store/interface.go:28` - Add AddConversation method to Store interface
- `./internal/store/interface.go:32` - Add AddMessage method to Store interface
```
Remember: The context map is what developers see FIRST, so make it count!
## Completion
After writing the plan file, respond with:
**Plan created:** `<PLAN_NAME>`
**Path:** `./plans/<PLAN_NAME>.md`
**Tasks:** <number of tasks>

View File

@@ -1,54 +0,0 @@
---
description: Reviews implementations and provides structured feedback
mode: subagent
temperature: 0.2
permission:
"*": deny
bash:
"*": deny
"git diff *": allow
"git log *": allow
"git show *": allow
"git show": allow
"git status *": allow
"git status": allow
glob: allow
grep: allow
list: allow
lsp: allow
read: allow
---
You review code implementations.
**Process:**
1. Check `git status` - if uncommitted changes, stop and tell @developer to commit
2. Review latest commit with `git show`
3. Read full files only if needed for context
4. Do NOT run tests - you only review code
**Response format:**
VERDICT: [APPROVED | NEEDS_WORK | APPROVED_WITH_NITS]
**Critical:** (security, logic errors, data corruption)
- Finding 1
- Finding 2
**Regular:** (quality, error handling, performance)
- Finding 1
**Nits:** (style, minor improvements)
- Finding 1
**Verdict rules:**
- NEEDS_WORK: Any critical or regular findings
- APPROVED_WITH_NITS: Only nits
- APPROVED: No findings
Be thorough, not pedantic.

View File

@@ -1,42 +0,0 @@
---
description: Create a conventional commit from git changes
agent: build
---
Run git commands to ensure all changes are staged, then generate a conventional commit message based on the diff and create the commit.
Steps:
1. Add all files (including untracked) using `git add -A`
2. Show the current diff with `git diff --cached` and `git diff --staged`
3. Analyze the changes and create a conventional commit message following the format:
- Type (feat, fix, docs, style, refactor, test, chore, etc.)
- Scope (optional, e.g., "runtime", "functions")
- Subject (brief description, imperative mood)
- Body (detailed explanation, optional)
4. Execute `git commit -m "message"` with the generated message
5. Show the commit result with `git log -1 --stat`
Conventional commit format:
```
<type>(<scope>): <subject>
<body>
<footer>
```
Common types:
- feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (formatting, semicolons, etc.)
- refactor: Code refactoring (neither bug fix nor feature)
- test: Adding or updating tests
- chore: Maintenance tasks
- perf: Performance improvements
- ci: CI/CD changes
- build: Build system or dependencies
- revert: Revert a previous commit

View File

@@ -1,83 +0,0 @@
{ lib
, pkgs
, config
, namespace
, ...
}:
let
inherit (lib) mkIf;
helpers = import ./lib.nix { inherit lib; };
llamaSwapConfig = import ./../../../../nixos/services/llama-swap/config.nix { inherit pkgs; };
cfg = config.${namespace}.programs.terminal.opencode;
in
{
options.${namespace}.programs.terminal.opencode = {
enable = lib.mkEnableOption "enable opencode";
};
config = mkIf cfg.enable {
# Enable OpenCode
programs.opencode = {
enable = true;
package = pkgs.reichard.opencode;
enableMcpIntegration = true;
agents = {
orchestrator = ./config/agents/orchestrator.md;
planner = ./config/agents/planner.md;
developer = ./config/agents/developer.md;
reviewer = ./config/agents/reviewer.md;
agent-creator = ./config/agents/agent-creator.md;
};
commands = {
commit = ./config/commands/commit.md;
};
};
# Define OpenCode Configuration
sops = {
secrets.context7_apikey = {
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
};
templates."opencode.json" = {
path = "${config.home.homeDirectory}/.config/opencode/opencode.json";
content = builtins.toJSON {
"$schema" = "https://opencode.ai/config.json";
theme = "catppuccin";
provider = {
"llama-swap" = {
npm = "@ai-sdk/openai-compatible";
options = {
baseURL = "https://llm-api.va.reichard.io/v1";
};
models = helpers.toOpencodeModels llamaSwapConfig;
};
};
lsp = {
biome = {
disabled = true;
};
starlark = {
command = [
"${pkgs.pyright}/bin/pyright-langserver"
"--stdio"
];
extensions = [ ".star" ];
};
};
mcp = {
context7 = {
type = "remote";
url = "https://mcp.context7.com/mcp";
headers = {
CONTEXT7_API_KEY = "${config.sops.placeholder.context7_apikey}";
};
enabled = true;
};
};
};
};
};
};
}

View File

@@ -1,51 +0,0 @@
{ lib }:
let
inherit (lib)
mapAttrs
filterAttrs
any
flatten
listToAttrs
nameValuePair
;
in
{
# Convert llama-swap models to opencode format
toOpencodeModels =
llamaSwapConfig:
let
textGenModels = filterAttrs (name: model: any (t: t == "coding") (model.metadata.type or [ ])) (
llamaSwapConfig.models or { }
);
localModels = mapAttrs
(
name: model:
{
inherit (model) name;
}
// (
if model.macros.ctx or null != null then
{
limit = {
context = lib.toInt model.macros.ctx;
input = lib.toInt model.macros.ctx;
output = lib.toInt model.macros.ctx;
};
}
else
{ }
)
)
textGenModels;
peerModels = listToAttrs (
flatten (
map (peer: map (modelName: nameValuePair modelName { name = modelName; }) peer.models) (
builtins.attrValues (llamaSwapConfig.peers or { })
)
)
);
in
localModels // peerModels;
}

View File

@@ -1,58 +0,0 @@
# AI Agent Guidelines
Be cognizant of context use; this file is loaded for all LLMs. Keep guidance concise and high-signal.
## Critical Rules
1. **Bash timeouts**: Every `bash` tool call MUST specify a timeout.
```bash
bash(command="some command", timeout=30)
```
2. **File writing**: Do NOT use `cat` with heredocs to write files. Use `write` for new/rewritten files and `edit` for targeted modifications.
3. **Scratch files**: Put temporary scripts, plans, notes, and reusable exploration artifacts in `_scratch/`. It is gitignored.
4. **Missing commands**: If a tool is not installed, prefer `nix run` instead of installing it.
```bash
nix run nixpkgs#python3 -- script.py
```
## Context Discipline
Prefer a **search → targeted read** pattern:
1. Search with `rg -n` / `grep -n` to find relevant line numbers.
2. Read only the needed range with `read(path, offset, limit)`.
Full-file reads are fine when genuinely needed, but avoid them as the default reflex.
## Principles
1. **KISS / YAGNI**: Keep solutions simple. Avoid abstractions, generics, or indirection unless there is a concrete need.
2. **Maintain AGENTS.md**: Keep project guidance up to date, but BLUF: concise, actionable, and context-size conscious.
3. **Knowledge Capture Check**: Before the final response, ask whether the task revealed a non-obvious convention, pitfall, repeatable workflow, or missing helper. If yes, briefly recommend exactly where to capture it: package/project AGENTS.md, global AGENTS.md, a skill, or a helper script. Skip this note when there is nothing meaningful.
## Style
### Comment Style
A logical block of code (not necessarily a language scope) should have a short Title Case comment above it:
```go
// Map Component Results
for _, comp := range components {
results[comp.Name] = comp.Result
}
```
If the block is more complicated or non-obvious, explain _why_ after the title:
```go
// Map Component Results - Downstream consumers expect a name-keyed lookup.
for _, comp := range components {
results[comp.Name] = comp.Result
}
```

View File

@@ -1,25 +0,0 @@
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
export default function replacePiWithClaudeCodeExtension(pi: ExtensionAPI) {
pi.on("before_agent_start", async (event, ctx) => {
// Anthropic-Only Guard - Other providers (e.g. OpenAI, Google) should
// see the unmodified pi system prompt.
if (ctx.model?.provider !== "anthropic") {
return undefined;
}
// Replace "pi" With "claude code"
const transformedSystemPrompt = event.systemPrompt.replace(
/(^|\s)pi(?![\w-])/gi,
"$1claude code",
);
if (transformedSystemPrompt === event.systemPrompt) {
return undefined;
}
return {
systemPrompt: transformedSystemPrompt,
};
});
}

View File

@@ -1,34 +0,0 @@
---
description: Review staged git changes
---
**Process:**
1. Review staged changes with `git diff --cached`
2. Read full files only if needed for context
3. Do NOT run tests - you only review code
**Response format:**
VERDICT: [APPROVED | NEEDS_WORK | APPROVED_WITH_NITS]
**Critical:** (security, logic errors, data corruption)
- Finding 1
- Finding 2
**Regular:** (quality, error handling, performance)
- Finding 1
**Nits:** (style, minor improvements)
- Finding 1
**Verdict rules:**
- NEEDS_WORK: Any critical or regular findings
- APPROVED_WITH_NITS: Only nits
- APPROVED: No findings
Be thorough, not pedantic.

View File

@@ -1,98 +0,0 @@
---
name: address-gh-review
description: 'Fetch and address unresolved GitHub PR review comments. Use when user asks to handle PR reviews, address review feedback, mentions "/address-gh-review", or wants to see what reviewers requested. Fetches unresolved threads, presents an actionable summary, lets the user select which items to address, and resolves threads on GitHub.'
---
# GitHub PR Review
## Overview
Fetch unresolved review threads from the current PR, consolidate them into actionable items, address selected items (each as a separate commit), and resolve threads on GitHub with reactions or explanatory comments.
## Prerequisites
- `gh` CLI authenticated and in a repo with an open PR on the current branch
- The `git-commit` skill available for committing changes
## Scripts
- `gh_review.sh` — Fetches unresolved threads, prints human-readable summary, and appends a compact thread mapping block at the end with item numbers, thread IDs, and comment IDs for downstream resolution.
- `gh_resolve_thread.sh` — Resolves a GitHub review thread, optionally adding reactions or a comment first.
### `gh_resolve_thread.sh` Usage
```bash
# Thumbs-up on comments, then resolve
bash gh_resolve_thread.sh thumbsup <thread-id> [comment-id1 comment-id2 ...]
# Post a reply explaining why, then resolve
bash gh_resolve_thread.sh comment "reason" <thread-id>
```
## Workflow
### 1. Fetch Review Comments
Run the bundled script to get unresolved threads:
```bash
bash gh_review.sh
```
If the script fails (no PR, not authenticated, etc.), report the error and stop.
The script appends a thread mapping block at the end of its output:
```
# Thread Mapping (item_num | thread_id | comment_ids)
1 PRRT_kwDOKhRdjM5-yKNp PRRC_kwDOKhRdjM6849mq PRRC_kwDOKhRdjM6849mr
2 PRRT_kwDOKhRdjM5-yKOJ PRRC_kwDOKhRdjM6849nZ
```
Capture these IDs from the script output — they are needed in step 5.
### 2. Consolidate into Actionable Items
Parse the output and group into actionable items. Combine threads that ask for the same change (e.g. multiple reviewers commenting on the same function about the same concern). Keep items separate when they require distinct code changes.
Present a numbered list to the user:
```
## Unresolved Review Items
1. **src/auth/login.ts:42** — Add rate limiting to prevent brute force attacks (alice-dev)
2. **src/utils/validators.ts:89** — Use stricter type checking for email validation (bob-coder)
3. **src/api/users.ts:156** — Add error handling for null responses (alice-dev, charlie-reviewer)
```
### 3. Ask User for Selection
**Always ask before proceeding.** Prompt the user to select which items to address:
```
Which items would you like me to address? (e.g. "1,3", "all", or "none")
```
**Do not proceed until the user responds.** Respect "none" — just stop.
### 4. Address Each Item
For each selected item, in order:
1. Read the relevant file and understand the context around the referenced line
2. Implement the requested change
3. Run relevant linting/tests if applicable (e.g. `quicklint`)
4. Commit using the `git-commit` skill — **one commit per item**
### 5. Resolve Threads on GitHub
After all code changes are committed, resolve the corresponding threads on GitHub using the thread IDs and comment IDs from the script output in step 1:
1. For **addressed items**: run `bash gh_resolve_thread.sh thumbsup <thread-id> <comment-ids...>` — adds 👍 to each comment, then resolves the thread.
2. For **skipped items**: ask the user for a brief reason, then run `bash gh_resolve_thread.sh comment "<reason>" <thread-id>` — posts an explanation, then resolves.
Only resolve threads for items the user explicitly selected (addressed or skipped). Leave untouched items unresolved.
### 6. Summary
After all selected items are addressed and threads resolved, print a brief summary of what was done (code changes + thread resolutions).

View File

@@ -1,85 +0,0 @@
#!/usr/bin/env bash
# Resolve a GitHub PR review thread, optionally adding reactions or a comment.
#
# Usage:
# gh_resolve_thread.sh thumbsup <thread-id> [comment-id1 comment-id2 ...]
# gh_resolve_thread.sh comment "reason" <thread-id>
#
# - thumbsup: adds 👍 to each supplied comment ID, then resolves the thread
# - comment: posts a reply explaining why the thread is being resolved, then resolves
#
# Requires: gh CLI authenticated, running on a branch with an open PR.
set -euo pipefail
err() { echo "error: $1" >&2; exit 1; }
command -v gh >/dev/null 2>&1 || err "'gh' CLI not found"
if [[ $# -lt 1 ]]; then
err "usage: $0 {thumbsup|comment} ..."
fi
ACTION="$1"
shift
case "$ACTION" in
thumbsup)
[[ $# -lt 1 ]] && err "usage: $0 thumbsup <thread-id> [comment-id ...]"
THREAD_ID="$1"
shift
COMMENT_IDS=("$@")
# Add thumbs-up reaction to each comment
for cid in "${COMMENT_IDS[@]+"${COMMENT_IDS[@]}"}"; do
[[ -z "$cid" ]] && continue
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
gh api graphql -f query='
mutation($subjectId: ID!, $content: ReactionContent!) {
addReaction(input: { subjectId: $subjectId, content: $content }) {
reaction { content }
}
}' -f subjectId="$cid" -f content=THUMBS_UP --jq '.data.addReaction.reaction.content' >/dev/null 2>&1 || true
done
# Resolve the thread
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: { threadId: $threadId }) {
clientMutationId
}
}' -f threadId="$THREAD_ID" --jq '.data.resolveReviewThread.clientMutationId' >/dev/null 2>&1 || err "failed to resolve thread $THREAD_ID"
echo "Resolved thread $THREAD_ID (thumbs-up on ${#COMMENT_IDS[@]} comment(s))"
;;
comment)
[[ $# -lt 2 ]] && err "usage: $0 comment \"reason\" <thread-id>"
REASON="$1"
THREAD_ID="$2"
# Post a reply on the thread
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
gh api graphql -f query='
mutation($threadId: ID!, $body: String!) {
addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) {
comment { id }
}
}' -f threadId="$THREAD_ID" -f body="$REASON" --jq '.data.addPullRequestReviewThreadReply.comment.id' >/dev/null 2>&1 || err "failed to post reply on thread $THREAD_ID"
# Resolve the thread
# shellcheck disable=SC2016 # GraphQL uses -f flags, not shell expansion
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: { threadId: $threadId }) {
clientMutationId
}
}' -f threadId="$THREAD_ID" --jq '.data.resolveReviewThread.clientMutationId' >/dev/null 2>&1 || err "failed to resolve thread $THREAD_ID"
echo "Resolved thread $THREAD_ID (commented: $REASON)"
;;
*)
err "unknown action: $ACTION (expected thumbsup|comment)"
;;
esac

View File

@@ -1,100 +0,0 @@
#!/usr/bin/env bash
# Fetch unresolved PR review threads, formatted for LLM consumption.
# Omits diff hunks (the LLM can read files from the repo directly).
# Groups comments into threads so conversation context is preserved.
# Appends a compact thread mapping block at the end with item numbers,
# thread IDs, and comment IDs for downstream resolution.
set -euo pipefail
err() { echo "error: $1" >&2; exit 1; }
# Verify gh CLI is installed
command -v gh >/dev/null 2>&1 || err "'gh' CLI not found. Install it from https://cli.github.com"
# Verify we're inside a git repository
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || err "not inside a git repository"
# Verify the remote is a GitHub repo
gh repo view --json name -q .name >/dev/null 2>&1 || err "this repo does not appear to be hosted on GitHub"
# Verify we're on a PR branch
PR_NUM=$(gh pr view --json number -q .number 2>/dev/null) || err "no pull request found for the current branch"
# Fetch current user, repo owner, and repo name
ME=$(gh api user -q .login 2>/dev/null) || err "failed to fetch GitHub user - are you authenticated? Run 'gh auth login'"
OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null) || err "failed to determine repository owner"
REPO=$(gh repo view --json name -q .name 2>/dev/null) || err "failed to determine repository name"
# Fetch Unresolved Review Threads via GraphQL
QUERY=$(cat <<EOF
query {
repository(owner: "${OWNER}", name: "${REPO}") {
pullRequest(number: ${PR_NUM}) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 100) {
nodes {
id
author { login }
body
path
line
}
}
}
}
}
}
}
EOF
)
RAW_JSON=$(gh api graphql -f query="$QUERY" 2>/dev/null | jq -c '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)') || err "GraphQL query failed - check your permissions and token scopes"
# Build human-readable output + thread mapping block
OUTPUT=""
MAPPING=""
ITEM_NUM=0
while IFS= read -r thread; do
[[ -z "$thread" ]] && continue
THREAD_ID=$(echo "$thread" | jq -r '.id')
FILE=$(echo "$thread" | jq -r '.comments.nodes[0].path')
LINE=$(echo "$thread" | jq -r '.comments.nodes[0].line')
# Collect comment IDs (for reactions) — exclude own comments
COMMENT_IDS=$(echo "$thread" | jq -r --arg me "$ME" '[.comments.nodes[] | select(.author.login != $me) | .id] | join(" ")')
# Build human-readable comments (exclude own comments)
COMMENTS=$(echo "$thread" | jq -r --arg me "$ME" '[.comments.nodes[] | select(.author.login != $me) | "- **\(.author.login)**: \(.body)"] | join("\n")')
if [[ -z "$COMMENTS" ]]; then
continue
fi
ITEM_NUM=$((ITEM_NUM + 1))
# Append human-readable block
OUTPUT+="## ${FILE}:${LINE}
${COMMENTS}
"
# Append mapping line
MAPPING+="${ITEM_NUM} ${THREAD_ID} ${COMMENT_IDS}
"
done <<< "$RAW_JSON"
# Format output
if [[ -z "$OUTPUT" ]]; then
echo "No unresolved review comments found."
else
echo "$OUTPUT"
echo "---"
echo "# Thread Mapping (item_num | thread_id | comment_ids)"
echo "$MAPPING"
fi

View File

@@ -1,53 +0,0 @@
---
name: create-skill
description: 'Create a new skill in the skills directory. Use when user asks to add a skill, create a skill, or mentions "/create-skill". Produces a SKILL.md with frontmatter and optional helper scripts, following conventions from existing skills.'
---
# Create Skill
## Overview
Scaffold a new skill directory with a `SKILL.md` and optional helper scripts under the project's skills directory.
## Workflow
### 1. Gather Requirements
Ask the user:
- **What does the skill do?** (trigger conditions, purpose)
- **Are there repeatable commands?** (if yes, these become scripts)
If the user already provided enough detail, skip straight to drafting.
### 2. Draft the Skill
Create `skills/<skill-name>/SKILL.md` with this structure:
```markdown
---
name: <skill-name>
description: '<One-liner: what it does and when to trigger. Keep under ~200 chars.>'
---
# <Skill Title>
## Overview
[1-2 sentences on purpose and scope]
## Workflow
[Numbered steps the agent follows]
```
**Guidelines:**
- **Be concise.** Skills are injected into agent context — every line costs tokens. Aim for the minimum needed to reliably guide the agent.
- **Use scripts for repeatable logic.** If a step involves a multi-line shell command, `jq` pipeline, or API call that won't change between runs, put it in a `.sh` file next to `SKILL.md` and reference it from the workflow. See `address-gh-review/` for an example.
- **Frontmatter is required.** `name` and `description` fields. The description is what the agent uses to decide whether to load the skill, so make it specific about trigger conditions.
- **Don't over-specify.** Trust the agent to fill gaps. Document the _what_ and _when_, not every micro-step.
### 3. Present for Review
Show the user the draft and wait for approval before finalizing. Apply any requested changes.
### 4. Git Add
Run `git add` on the new skill directory so the flake/tooling can discover it.

View File

@@ -1,136 +0,0 @@
---
name: git-commit
description: 'Execute git commit with conventional commit message analysis, intelligent staging, and message generation. Use when user asks to commit changes, create a git commit, mentions "/commit", or simply says "do it". Automatically commits without confirmation. Supports: (1) Auto-detecting type and scope from changes, (2) Generating conventional commit messages from diff, (3) Optional type/scope/description overrides, (4) Intelligent file staging for logical grouping'
---
# Git Commit with Conventional Commits
## Overview
Create standardized, semantic git commits using the Conventional Commits specification. Analyze the actual diff to determine appropriate type, scope, and message.
## Automatic Execution — CRITICAL
**When this skill is invoked, EXECUTE THE COMMIT IMMEDIATELY. Do NOT ask for confirmation. Do NOT say "I'm ready" or "let me know when you want to proceed." Do NOT wait for further input. Do NOT acknowledge and then wait.**
The skill is invoked when the user says "commit", "do it", "/commit", "make a commit", "create a commit", or any similar phrase. The moment you recognize this intent, analyze the diff and run `git commit`. That's it.
**Zero prompts. Zero confirmation requests. Zero hand-holding.**
Only skip the commit if:
- Nothing is staged or modified (say "Nothing to commit" and stop)
- Secrets appear to be about to be committed (refuse and stop)
For everything else — just commit.
## Conventional Commit Format
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
## Commit Types
| Type | Purpose |
| ---------- | ------------------------------ |
| `feat` | New feature |
| `fix` | Bug fix |
| `docs` | Documentation only |
| `style` | Formatting/style (no logic) |
| `refactor` | Code refactor (no feature/fix) |
| `perf` | Performance improvement |
| `test` | Add/update tests |
| `build` | Build system/dependencies |
| `ci` | CI/config changes |
| `chore` | Maintenance/misc |
| `revert` | Revert commit |
## Breaking Changes
```
# Exclamation mark after type/scope
feat!: remove deprecated endpoint
# BREAKING CHANGE footer
feat: allow config to extend other configs
BREAKING CHANGE: `extends` key behavior changed
```
## Workflow
### 1. Analyze Diff
```bash
# If files are staged, use staged diff
git diff --staged
# If nothing staged, use working tree diff
git diff
# Also check status
git status --porcelain
```
### 2. Stage Files (if needed)
If nothing is staged or you want to group changes differently:
```bash
# Stage specific files
git add path/to/file1 path/to/file2
# Stage by pattern
git add *.test.*
git add src/components/*
# Interactive staging
git add -p
```
**Never commit secrets** (.env, credentials.json, private keys).
### 3. Generate Commit Message
Analyze the diff to determine:
- **Type**: What kind of change is this?
- **Scope**: What area/module is affected?
- **Description**: One-line summary of what changed (present tense, imperative mood, <72 chars)
### 4. Execute Commit
```bash
# Single line
git commit -m "<type>[scope]: <description>"
# Multi-line with body/footer
git commit -m "$(cat <<'EOF'
<type>[scope]: <description>
<optional body>
<optional footer>
EOF
)"
```
## Best Practices
- One logical change per commit
- Present tense: "add" not "added"
- Imperative mood: "fix bug" not "fixes bug"
- Reference issues: `Closes #123`, `Refs #456`
- Keep description under 72 characters
## Git Safety Protocol
- NEVER update git config
- NEVER run destructive commands (--force, hard reset) without explicit request
- NEVER skip hooks (--no-verify) unless user asks
- NEVER force push to main/master
- If commit fails due to hooks, fix and create NEW commit (don't amend)

View File

@@ -1,71 +0,0 @@
---
name: planning
description: 'Create a structured plan before implementing broad or non-trivial changes. Use when user requests a new feature spanning multiple areas, significant refactoring, or explicitly asks to "plan this out". Produces a plan document in _scratch/, waits for user approval, then tracks progress as tasks are completed.'
---
# Planning Workflow Skill
## When to Use
Trigger this workflow when:
- A user requests a **broad or non-trivial change** (e.g. new feature spanning frontend/backend, significant refactoring)
- A user says things like "plan this out", "create a plan", or "do this properly"
Don't trigger for routine multi-file changes like renames, simple bug fixes, or mechanical refactors.
## Rules
### 1. Always Start with a Plan
**Before touching any code**, create a markdown plan in `_scratch/plan-[short-name].md` with:
- A clear title and status
- Clarifications section (if any unknowns)
- Phased tasks as checkboxes (`- [ ]`)
### 2. Ask Clarifying Questions First
When a requirement is **ambiguous or has multiple valid interpretations**:
- List your interpretations
- Ask the user to confirm
- Document answers in the plan's "Clarifications" section
### 3. Wait for User Approval
Present the plan and wait for the user to confirm before implementing. If the user requests changes, update the plan first.
### 4. Update the Plan as You Work
- Change `- [ ]` to `- [x]` for completed items
- Note any deviations from the original plan
- If you discover new tasks, add them with a note explaining why
### 5. Plan Document Format
```markdown
# Plan: [Feature Name]
> **Status**: ⏳ In Progress | ✅ Complete | ❌ Blocked
## Clarifications
- ~~Question?~~ **Answer**
## Tasks
### Phase 1: [Name]
- [ ] Task 1
- [ ] Task 2
### Phase 2: [Name]
- [ ] Task 3
## Rollback Plan (if destructive/irreversible)
[How to undo if needed]
```
## Anti-Patterns
- ❌ Coding before the user approves the plan
- ❌ Making assumptions about ambiguous requirements
- ❌ Plans that are too vague ("update code")
- ❌ Forgetting to update the plan as you work

View File

@@ -1,112 +0,0 @@
---
name: web-glimpse
description: 'Search the web, read pages, extract content, run JavaScript, or capture screenshots using the `glimpse` headless browser tool. Use when the user asks to search the web, look something up online, read/fetch a page, inspect dynamic content, or capture visual state. Does not replace curl for simple HTTP/API requests.'
---
# Web Browsing With Glimpse
`glimpse` runs headless Firefox via WebDriver. Use it for web search, reading rendered pages, running JavaScript, and screenshots. Prefer `curl` for simple APIs, static files, and direct downloads.
## Commands
| Command | Purpose |
| ------- | ------- |
| `reader <url>` | Extract page content as Markdown (Reader View → raw fallback) |
| `exec <url>` | Run JavaScript on a page, return the result |
| `screenshot <url>` | Save a PNG screenshot |
| `search <query>` | Search the web (Kagi) and return results |
| `serve` | Start a persistent browser for faster repeat commands |
## Persistent Server
For multi-command sessions, start a persistent browser server first. All subsequent commands auto-discover it via Unix socket — no extra flags needed.
```bash
# Start persistent server (keeps geckodriver + Firefox alive)
glimpse serve &
# All commands now reuse the running browser (~300ms vs ~2-3s each)
glimpse reader https://example.com
glimpse reader https://other.com
glimpse exec https://example.com --js='return document.title'
# Check server status
glimpse serve --status
# Stop when done
glimpse serve --stop
```
State (cookies, localStorage) persists between commands — this is intentional for sticky sessions. Without a running server, commands work normally with ad-hoc browser startup.
## Quick Reference
```bash
# Read a page (tries Reader View, falls back to raw Turndown)
glimpse reader https://example.com --timeout=15
# Read without Reader View (raw HTML → Markdown via Turndown)
glimpse reader https://example.com --no-reader --timeout=15
# Get structured JSON instead of Markdown (includes method: "reader"|"raw")
glimpse reader https://example.com --format=json
# Save extracted content to a file
glimpse reader https://example.com --output=page.md
# Run JavaScript and return a value
glimpse exec https://example.com --js='return document.title'
# Extract specific data with JavaScript
glimpse exec https://example.com --wait-until=complete --js='return {
title: document.title,
text: document.body.innerText.slice(0, 4000)
}'
# Wait for dynamic content before extracting
glimpse reader https://example.com \
--wait-js='return document.querySelector(".content")?.innerText?.length > 100' \
--timeout=30
# Capture a screenshot
glimpse screenshot https://example.com --output=page.png
# Search the web
glimpse search "query terms" --timeout=15
# Search and get JSON instead of Markdown
glimpse search "query terms" --format=json
```
## Common Options
| Option | Default | Purpose |
| ------ | ------- | ------- |
| `--timeout=<s>` | `10` | Max wait time in seconds; increase for slow/JS-heavy pages |
| `--wait-until=<state>` | `none` | Wait for `none`, `interactive`, or `complete` |
| `--wait-js=<code>` | — | Poll JS expression until truthy |
| `--js=<code>` | — | Run inline JS before command logic |
| `--script=<file>` | — | Run JS file before command logic |
| `--no-headless` | — | Show the browser window |
| `--format=<fmt>` | varies | Output format (reader: `markdown`/`html`/`text`/`json`; search: `markdown`/`json`) |
| `--output=<file>` | — | Write output to file (reader, screenshot) |
| `--no-reader` | — | Skip Reader View, use raw page extraction |
## Workflow
1. **Search first** when the user asks an open-ended question. Pick authoritative results to read.
2. **Read pages with `reader`** — it tries Firefox Reader View for clean article extraction, then falls back to converting the raw page HTML to Markdown via Turndown. Most pages work without extra options.
3. **Add `--wait-until=complete`** for JS-heavy pages, SPAs, or pages that load content dynamically.
4. **Use `exec`** when you need targeted data extraction via JavaScript rather than full page content.
5. **Use `screenshot`** when visual layout, charts, or rendering state matters.
6. **Increase timeouts** — start at `15`, go to `30` for slow sites. The default `10` is often too tight for real-world pages.
7. **Cite URLs** when summarizing web research. Distinguish search snippets from verified page content.
## Error Handling
| Error | Fix |
| ----- | --- |
| `TIMEOUT` | Increase `--timeout` (in seconds), add `--wait-until=complete`, or use `--wait-js` |
| `USAGE_ERROR` | Check arg order: `glimpse <command> <url>`, search is `glimpse search "query"` |
| Thin/empty content | Try `--wait-until=complete`, `--no-reader`, or targeted `exec` |
| Search auth errors | Kagi token is configured via `~/.config/glimpse/config.json` or `KAGI_TOKEN` env |

View File

@@ -1,10 +0,0 @@
---
name: scout
description: Fast codebase reconnaissance. Reads and searches files, then returns concise findings with paths.
approved_tools:
- read
- bash
---
You are a focused codebase scout running as a subagent with isolated context.
Find the information requested by the task and return a concise report. Prefer exact file paths, symbol names, and line numbers. Do not modify files.

View File

@@ -1,187 +0,0 @@
{ lib
, pkgs
, config
, namespace
, ...
}:
let
inherit (lib) mkIf;
helpers = import ./lib.nix { inherit lib; };
llamaSwapConfig = import ./../../../../nixos/services/llama-swap/config.nix { inherit pkgs; };
cfg = config.${namespace}.programs.terminal.pi;
# Nix-Owned Plugin List - Source of truth for `packages` in settings.json.
# Merged into the (mutable) settings.json on activation so pi can keep
# writing other fields (current model, etc.) without us clobbering them.
piPackages = [
"https://gitea.va.reichard.io/evan/pi-lsp.git@main"
"https://gitea.va.reichard.io/evan/pi-subagents.git@main"
"https://gitea.va.reichard.io/evan/pi-statusline.git@main"
];
piPackagesJson = pkgs.writeText "pi-packages.json" (builtins.toJSON piPackages);
# Pi Auth Api Keys - These are merged into mutable auth.json on activation.
# Add another entry here to manage another top-level provider auth key.
piAuthApiKeys = [
{
provider = "zai";
secretName = "zai_apikey";
jqVar = "zai";
sopsFile = lib.snowfall.fs.get-file "secrets/common/evanreichard.yaml";
}
];
piAuthJqRawfiles = lib.concatStringsSep " \\\n " (
map
(
auth: ''--rawfile ${auth.jqVar} "${config.sops.secrets.${auth.secretName}.path}"''
)
piAuthApiKeys
);
piAuthJqFilter = lib.concatStringsSep " | " (
map
(
auth: ''.["${auth.provider}"] = { type: "api_key", key: ($'' + auth.jqVar + ''| rtrimstr("\n")) }''
)
piAuthApiKeys
);
piAuthMergeScript = pkgs.writeShellScript "pi-auth-merge" ''
set -euo pipefail
PI_AUTH="$HOME/.pi/agent/auth.json"
mkdir -p "$(dirname "$PI_AUTH")"
if [ -L "$PI_AUTH" ]; then
rm "$PI_AUTH"
fi
for secret in ${
lib.concatStringsSep " " (
map (auth: ''"${config.sops.secrets.${auth.secretName}.path}"'') piAuthApiKeys
)
}; do
if [ ! -e "$secret" ]; then
echo "Skipping pi auth merge; missing sops secret: $secret" >&2
exit 0
fi
done
[ -s "$PI_AUTH" ] || echo '{}' > "$PI_AUTH"
tmp=$(mktemp)
${pkgs.jq}/bin/jq ${piAuthJqRawfiles} \
'${piAuthJqFilter}' "$PI_AUTH" > "$tmp"
mv "$tmp" "$PI_AUTH"
chmod 600 "$PI_AUTH"
'';
in
{
options.${namespace}.programs.terminal.pi = {
enable = lib.mkEnableOption "enable pi";
};
config = mkIf cfg.enable {
# Enable Glimpse
${namespace}.programs.terminal.glimpse.enable = true;
# Add Pi Coding Agent to Home Packages
home.packages = with pkgs; [ reichard.pi-coding-agent ];
# Define Pi Configuration
home.file = {
".pi/agent/AGENTS.md" = {
source = ./config/AGENTS.md;
};
".pi/agent/skills" = {
source = ./config/skills;
recursive = true;
};
".pi/agent/subagents" = {
source = ./config/subagents;
recursive = true;
};
".pi/agent/prompts" = {
source = ./config/prompts;
recursive = true;
};
".pi/agent/extensions" = {
source = ./config/extensions;
recursive = true;
};
};
# Pi Models Config - Inject llama-swap API key from sops into models.json
# so pi can authenticate against the llm-api endpoint.
sops = lib.mkIf config.${namespace}.security.sops.enable {
secrets = {
"llama_swap_api_keys/pi" = {
sopsFile = lib.snowfall.fs.get-file "secrets/common/llama-swap.yaml";
};
}
// lib.listToAttrs (
map
(auth: {
name = auth.secretName;
value.sopsFile = auth.sopsFile;
})
piAuthApiKeys
);
templates."pi-models.json" = {
path = "${config.home.homeDirectory}/.pi/agent/models.json";
content = builtins.toJSON {
providers = {
"llama-swap" = {
baseUrl = "https://llm-api.va.reichard.io/v1";
api = "openai-completions";
apiKey = config.sops.placeholder."llama_swap_api_keys/pi";
models = helpers.toPiModels llamaSwapConfig;
};
};
};
};
};
# Merge Nix-Defined Plugins Into Mutable settings.json - We can't symlink
# this file into the nix store because pi rewrites it at runtime (e.g. to
# persist the last-used model). Instead, on every activation we use jq to
# set `.packages` from Nix while preserving every other field.
home.activation.piSettingsMerge = config.lib.dag.entryAfter [ "writeBoundary" ] ''
PI_SETTINGS="$HOME/.pi/agent/settings.json"
mkdir -p "$(dirname "$PI_SETTINGS")"
[ -s "$PI_SETTINGS" ] || echo '{}' > "$PI_SETTINGS"
tmp=$(mktemp)
${pkgs.jq}/bin/jq --slurpfile pkgs ${piPackagesJson} \
'.packages = $pkgs[0]' "$PI_SETTINGS" > "$tmp"
mv "$tmp" "$PI_SETTINGS"
'';
# Merge Api Key Auth Into Mutable auth.json - Pi needs auth.json to stay
# writable, so merge sops-managed API keys instead of symlinking the whole
# file. Existing provider auth entries are preserved.
home.activation.piAuthMerge = lib.mkIf config.${namespace}.security.sops.enable (
config.lib.dag.entryAfter [ "sops-nix" "writeBoundary" ] ''
${piAuthMergeScript}
''
);
# Run Pi Auth Merge After Sops - During NixOS system activation, sops-nix
# can be restarted asynchronously and secrets may not exist yet. This user
# service retries the merge in the normal user systemd graph after sops-nix.
systemd.user.services.pi-auth-merge = lib.mkIf config.${namespace}.security.sops.enable {
Unit = {
Description = "Merge sops-managed Pi auth entries";
After = [ "sops-nix.service" ];
Requires = [ "sops-nix.service" ];
};
Service = {
Type = "oneshot";
ExecStart = piAuthMergeScript;
};
Install.WantedBy = [ "default.target" ];
};
};
}

View File

@@ -1,69 +0,0 @@
{ lib }:
let
inherit (lib)
mapAttrs
filterAttrs
any
flatten
listToAttrs
nameValuePair
;
in
{
toPiModels =
llamaSwapConfig:
let
hasType = type: model: any (t: t == type) (model.metadata.type or [ ]);
codingModels = filterAttrs (_name: model: hasType "coding" model) (llamaSwapConfig.models or { });
localModels = mapAttrs
(
name: model:
{
id = name;
inherit (model) name;
}
// (
if model.macros.ctx or null != null then
{
contextWindow = lib.toInt model.macros.ctx;
}
else
{ }
)
// (
if hasType "vision" model then
{
input = [
"text"
"image"
];
}
else
{ }
)
)
codingModels;
peerModels = listToAttrs (
flatten (
map
(
peer:
map
(
modelName:
nameValuePair modelName {
id = modelName;
name = modelName;
}
)
peer.models
)
(builtins.attrValues (llamaSwapConfig.peers or { }))
)
);
in
builtins.attrValues (localModels // peerModels);
}

View File

@@ -1,71 +0,0 @@
{ lib
, pkgs
, config
, namespace
, ...
}:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.tmux;
in
{
options.${namespace}.programs.terminal.tmux = {
enable = lib.mkEnableOption "tmux";
};
config = mkIf cfg.enable {
programs.tmux = {
enable = true;
clock24 = true;
plugins = with pkgs.tmuxPlugins; [
{
plugin = catppuccin;
extraConfig = ''
set -g @catppuccin_flavor "mocha"
set -g @catppuccin_status_background "none"
# Style & Separators
set -g @catppuccin_window_status_style "basic"
set -g @catppuccin_status_left_separator ""
set -g @catppuccin_status_middle_separator ""
set -g @catppuccin_status_right_separator ""
# Window Titles
set -g @catppuccin_window_text " #W"
set -g @catppuccin_window_current_text " #W"
'';
}
cpu
yank
];
extraConfig = ''
# Misc Settings
set -g status-position top
set -g mouse on
setw -g mode-keys vi
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",xterm-256color:Tc:Ms=\\E]52;c%p1%.0s;%p2%s\\7"
set -g extended-keys-format csi-u
set -g extended-keys on
set -sg escape-time 0
# Start Index 1
set -g base-index 1
setw -g pane-base-index 1
set -g renumber-windows on
# Maintain Directory
bind '"' split-window -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"
# Theme
set -g status-left ""
set -g status-right ""
set -ag status-right "#{E:@catppuccin_status_host}"
'';
};
};
}

View File

@@ -1,22 +0,0 @@
{ lib
, config
, namespace
, ...
}:
let
inherit (lib) mkIf;
cfg = config.${namespace}.programs.terminal.zk;
in
{
options.${namespace}.programs.terminal.zk = {
enable = lib.mkEnableOption "enable zk";
};
config = mkIf cfg.enable {
programs.zk = {
enable = true;
};
programs.fzf.enable = true;
};
}

View File

@@ -1,40 +0,0 @@
{ config
, lib
, namespace
, pkgs
, ...
}:
let
inherit (lib) mkIf mkEnableOption;
cfg = config.${namespace}.security.pass-keyring;
in
{
options.${namespace}.security.pass-keyring = {
enable = mkEnableOption "Enable pass-backed keyring";
};
config = mkIf cfg.enable {
home.packages = [ pkgs.pass ];
# GPG + Pass Keyring - Provides credential storage for CLI
# tools (e.g. python keyring) via pass (GPG-backed). The
# keyringrc.cfg forces keyring to use the pass backend instead
# of SecretService (which requires a working D-Bus provider).
programs.gpg.enable = true;
services.gpg-agent = {
enable = true;
enableBashIntegration = true;
pinentry.package = pkgs.pinentry-curses;
defaultCacheTtl = 86400; # 24 hours
maxCacheTtl = 604800; # 7 days
};
# Keyring Backend Config - Forces keyring to use the pass
# backend instead of SecretService (broken on headless Linux).
xdg.configFile."python_keyring/keyringrc.cfg".text = ''
[backend]
default-keyring=keyring_pass.PasswordStoreBackend
'';
};
}

View File

@@ -1,37 +0,0 @@
{ config
, lib
, namespace
, pkgs
, ...
}:
let
inherit (lib) mkIf mkEnableOption types;
inherit (lib.${namespace}) mkOpt;
getFile = lib.snowfall.fs.get-file;
cfg = config.${namespace}.security.sops;
in
{
options.${namespace}.security.sops = with types; {
enable = mkEnableOption "Enable sops";
defaultSopsFile = mkOpt str "secrets/common/evanreichard.yaml" "Default sops file.";
sshKeyPaths = mkOpt (listOf path) [ ] "Additional SSH key paths to use.";
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
age
sops
ssh-to-age
];
sops = {
defaultSopsFile = getFile cfg.defaultSopsFile;
age = {
keyFile = "${config.home.homeDirectory}/.config/sops/age/keys.txt";
sshKeyPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ] ++ cfg.sshKeyPaths;
};
};
};
}

Some files were not shown because too many files have changed in this diff Show More