Compare commits

..

No commits in common. "5902011dc6142d497e37742ad1ca2fa8504c8548" and "57c86c50833a6cc482ec8e738a9f18f29550721f" have entirely different histories.

14 changed files with 53 additions and 233 deletions

View file

@ -46,15 +46,6 @@ bosun.secrets.npmrc = { ... }; # Secret definitions
Profile definitions are in `modules/generic/profiles.nix`, implementations in `modules/nixos/profiles/`.
## Adding packages
Before writing a custom `packages/` derivation, always check if the package already exists in nixpkgs:
- Stable: https://search.nixos.org/packages?channel=25.11&query=<name>
- Unstable: https://search.nixos.org/packages?channel=unstable&query=<name>
- Master (bleeding edge): check open PRs at https://github.com/NixOS/nixpkgs/pulls
If it exists in `unstable` or `master` but not stable, pull it via `overlays/unstable.nix` using `channels.unstable` or `channels.master`. Only write a custom `packages/` derivation as a last resort.
## Architecture patterns
- **import-tree** auto-discovers and imports `.nix` files in `modules/flake/`. Files prefixed with `_` are excluded from auto-import.

6
flake.lock generated
View file

@ -778,11 +778,11 @@
},
"master": {
"locked": {
"lastModified": 1771495899,
"narHash": "sha256-UlAN9PHsBx1Kk65gR/KvLfO74zQcOjNZ+d/0td5T8eM=",
"lastModified": 1771456066,
"narHash": "sha256-CLuGt3yg70gnhSam+0qpcWgPnUdY98wVeH4lByklol4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f4f54061a12ebbdd03d7c53eed54d1e135840624",
"rev": "be6cf55e819f4d362aa6be60254bbb2537f9a5cb",
"type": "github"
},
"original": {

View file

@ -24,9 +24,6 @@
nodePackages.typescript-language-server
nil # nix lsp
# Node.js for MCP servers
nodejs_22
# trurl # Parsing and manipulating URLs via CLI
pandoc # Document converter
ripgrep # Grep file search
@ -42,7 +39,6 @@
gitui
tea
harbor.agent-deck # Terminal session manager for AI coding agents
happy-coder # Claude Code mobile client (happy.engineering)
harbor.oryx # TUI for sniffing network traffic using eBPF
# BMAD
@ -87,48 +83,33 @@
enable = true;
# package = inputs.unstable.${system}.claude-code;
mcpServers = {
# Official MCP servers (require Node.js)
fetch = {
command = "npx";
args = ["-y" "@modelcontextprotocol/server-fetch"];
type = "stdio";
};
playwright = {
command = "npx";
args = ["-y" "@modelcontextprotocol/server-playwright"];
type = "stdio";
};
stackexchange = {
command = "npx";
args = ["-y" "mcp-server-stackexchange"];
type = "stdio";
};
arxiv = {
command = "npx";
args = ["-y" "mcp-server-arxiv"];
type = "stdio";
};
# Custom MCP servers
claudezilla = {
command = "bun";
args = ["/home/toph/code/vendor/claudezilla/mcp/server.js"];
type = "stdio";
};
# Charlie MCP servers (local)
charlie-memory = {
command = "bun";
args = ["/home/toph/agent/mcp/memory/index.js"];
type = "stdio";
};
charlie-comunica = {
command = "bun";
args = ["/home/toph/agent/mcp/comunica/index.js"];
type = "stdio";
};
};
# mcpServers = {
# fetch = {
# args = ["-y" "@modelcontextprotocol/server-fetch"];
# command = "npx";
# type = "stdio";
# };
# playwright = {
# args = ["-y" "@modelcontextprotocol/server-playwright"];
# command = "npx";
# type = "stdio";
# };
# stackexchange = {
# args = ["-y" "mcp-server-stackexchange"];
# command = "npx";
# type = "stdio";
# };
# arxiv = {
# args = ["-y" "mcp-server-arxiv"];
# command = "npx";
# type = "stdio";
# };
# claudezilla = {
# command = "bun";
# args = ["/home/toph/code/vendor/claudezilla/mcp/server.js"];
# type = "stdio";
# };
# };
};
};
}

View file

@ -46,7 +46,7 @@
enabled = true;
size = 4;
passes = 2;
noise = 0.05;
noise = 0.3;
};
shadow = {
enabled = true;
@ -69,16 +69,6 @@
# Single tiled window on main monitor: give it breathing room
workspace = [
# Pin workspaces to monitors
"1, monitor:HDMI-A-1, default:true"
"2, monitor:HDMI-A-1"
"3, monitor:HDMI-A-1"
"4, monitor:HDMI-A-1"
"5, monitor:HDMI-A-1"
"6, monitor:DP-3, default:true"
"7, monitor:DP-3"
"8, monitor:DP-3"
# Single tiled window on main monitor: give it breathing room
"w[t1] m[HDMI-A-1], gapsout:15 840 15 840"
];

View file

@ -1,9 +1,6 @@
{...}: {
wayland.windowManager.hyprland.settings = {
windowrulev2 = [
# Prevent garbage frame flash before first render
"immediate, class:.*"
# Privacy: block from screen capture
"noscreenshare, class:^(1password)$"
"noscreenshare, class:^(thunderbird)$"
@ -24,9 +21,8 @@
# Kitty: slight transparency
"opacity 0.97 0.97, class:^(kitty)$"
# JetBrains: suppress phantom windows (RE2 doesn't support lookaheads,
# so we can't exclude toolbox here — apply per-IDE if needed)
# "nofocus, class:^(jetbrains-idea|jetbrains-rider|jetbrains-clion)$, floating:1, title:^win\\d+$"
# JetBrains: suppress phantom windows
"nofocus, class:^jetbrains-(?!toolbox), floating:1, title:^win\\d+$"
];
};
}

View file

@ -24,7 +24,6 @@
inherit lib;
pkgs = prev;
};
harbor = inputs.self.packages.${system} or {};
})
];
};

View file

@ -17,5 +17,5 @@
# open-webui
;
inherit (channels.master) install-nothing marimo happy-coder;
inherit (channels.master) install-nothing marimo;
}

View file

@ -1,116 +0,0 @@
#!/usr/bin/env bash
# active-path <pid>
#
# Given a PID (e.g. from active-window), output the active working directory
# or project root for that process. Handles:
# - VSCode/Electron: parses --folder-uri from the process tree
# - Terminals (kitty, etc.): finds the deepest meaningful cwd in the tree
# - Fallback: own cwd
set -o errexit
set -o nounset
set -o pipefail
PID="${1:?Usage: active-path <pid>}"
PROJECT_MARKERS=(.git Cargo.toml package.json Gemfile pyproject.toml go.mod flake.nix)
# Walk up from a path looking for a project root marker.
# Returns the marker directory, or the original path if nothing is found.
find_project_root() {
local dir="$1"
local d="$dir"
while [[ "$d" != "/" && -n "$d" ]]; do
for marker in "${PROJECT_MARKERS[@]}"; do
[[ -e "$d/$marker" ]] && { printf '%s\n' "$d"; return 0; }
done
d="$(dirname "$d")"
done
printf '%s\n' "$dir"
}
# Decode a percent-encoded URI path component (file:/// paths are rarely
# exotic, but spaces and non-ASCII chars do occur).
url_decode() {
printf '%b' "${1//%/\\x}"
}
# Read null-terminated argv of a pid and look for --folder-uri.
# Prints the decoded path and returns 0 if found, else returns 1.
folder_uri_from_cmdline() {
local pid="$1"
local prev=""
while IFS= read -r -d $'\0' arg; do
case "$arg" in
--folder-uri=file://*)
url_decode "${arg#--folder-uri=file://}"
return 0
;;
file://*)
if [[ "$prev" == "--folder-uri" ]]; then
url_decode "${arg#file://}"
return 0
fi
;;
esac
prev="$arg"
done < "/proc/$pid/cmdline" 2>/dev/null
return 1
}
# Collect PIDs of all processes in the subtree rooted at $1.
all_pids() {
local pid="$1"
printf '%s\n' "$pid"
for child in $(pgrep -P "$pid" 2>/dev/null); do
all_pids "$child"
done
}
# Given a list of PIDs, return the most specific (longest) cwd that lives
# under the user's home directory. Falls back to any non-root, non-proc cwd.
best_cwd_from_pids() {
local best=""
for pid in "$@"; do
local cwd
cwd=$(readlink -f "/proc/$pid/cwd" 2>/dev/null) || continue
[[ "$cwd" == "/" || "$cwd" == "/root" || "$cwd" == "${HOME}" ]] && continue
[[ "$cwd" =~ ^/(proc|sys|run|dev) ]] && continue
# Prefer paths under home; among those prefer longer (more specific) ones
if [[ "$cwd" == "${HOME}"/* ]]; then
if [[ -z "$best" || "${#cwd}" -gt "${#best}" ]]; then
best="$cwd"
fi
elif [[ -z "$best" ]]; then
best="$cwd"
fi
done
[[ -n "$best" ]] && printf '%s\n' "$best"
}
# --- Main ---
readarray -t TREE < <(all_pids "$PID")
# 1. VSCode / Electron: search entire process tree for --folder-uri
for pid in "${TREE[@]}"; do
if path=$(folder_uri_from_cmdline "$pid" 2>/dev/null); then
find_project_root "$path"
exit 0
fi
done
# 2. Terminal / generic: find the best cwd among all processes in the tree
# (prefers the shell or its running child over the terminal emulator itself,
# because terminal emulators typically sit at $HOME while the shell has moved)
if cwd=$(best_cwd_from_pids "${TREE[@]}"); then
find_project_root "$cwd"
exit 0
fi
# 3. Absolute fallback: own cwd, even if it's /
own=$(readlink -f "/proc/$PID/cwd" 2>/dev/null || true)
[[ -n "$own" ]] && find_project_root "$own" && exit 0
exit 1

View file

@ -1,6 +0,0 @@
{pkgs, ...}:
pkgs.writeShellApplication {
name = "active-path";
runtimeInputs = [pkgs.procps];
text = builtins.readFile ./active-path;
}

View file

@ -1,19 +0,0 @@
{pkgs, ...}:
pkgs.writeShellApplication {
name = "active-window";
runtimeInputs = [pkgs.jq];
text = ''
if [[ -n "''${NIRI_SOCKET:-}" ]]; then
FOCUSED_ID=$(niri msg --json focused-window | jq '.id')
niri msg --json windows \
| jq --argjson id "$FOCUSED_ID" \
'[.[] | select(.id == $id)] | first
| {compositor: "niri", class: .app_id, pid: .pid}'
elif [[ -n "''${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then
hyprctl activewindow -j \
| jq '{compositor: "hyprland", class: .class, pid: .pid}'
else
printf '{"compositor":"unknown","class":null,"pid":null}\n'
fi
'';
}

View file

@ -1,6 +0,0 @@
{ pkgs }:
pkgs.writeShellScriptBin "mcp-npx" ''
# Wrapper that provides npx with Node.js in PATH
exec ${pkgs.nodejs_22}/bin/npx "$@"
''

View file

@ -1,6 +1,5 @@
{pkgs, ...}:
pkgs.writeShellApplication {
name = "quick-zeal";
runtimeInputs = [pkgs.harbor.active-window pkgs.jq];
text = builtins.readFile ./quick-zeal;
}

View file

@ -6,14 +6,13 @@ extract_major_version() {
}
# Detects the focused window and checks if it's Kitty
WINDOW=$(active-window)
ACTIVE_WINDOW=$(printf '%s' "$WINDOW" | jq -r '.class // empty')
PID=$(printf '%s' "$WINDOW" | jq -r '.pid // empty')
ACTIVE_WINDOW=$(hyprctl activewindow -j | jq -r '.class')
# Check if the focused window is a Kitty terminal and if it's in a Git repository.
# If so, determine the project type and open Zeal with the appropriate argument
zeal_argument=""
if [[ $ACTIVE_WINDOW == "kitty" ]]; then
PID=$(hyprctl activewindow -j | jq -r '.pid')
CHILD_PID=$(pgrep -P "$PID" | tail -1)
SHELL_CWD=$(readlink -f "/proc/${CHILD_PID}/cwd")

View file

@ -1,13 +1,25 @@
{pkgs, ...}:
pkgs.writeNushellApplication {
name = "spawn-term";
runtimeInputs = [pkgs.harbor.active-window];
runtimeInputs = with pkgs; [kdotool];
text = ''
let window = (active-window | from json)
let window_info = {
is_kitty: ($window.class? == "kitty"),
pid: $window.pid?
let compositor = $env.XDG_CURRENT_DESKTOP? | default ""
let window_info = if ($compositor | str contains "niri") {
let focused_window = (niri msg --json focused-window | from json | get id?)
if ($focused_window | is-empty) {
{ is_kitty: false, pid: null }
} else {
let info = (niri msg --json windows | from json | where id == $focused_window | first)
{ is_kitty: ($info.app_id? == "kitty"), pid: $info.pid? }
}
} else {
let focused_window = (kdotool getactivewindow)
{
is_kitty: ((kdotool getwindowclassname $focused_window) == "kitty"),
pid: (kdotool getwindowpid $focused_window | into int)
}
}
if $window_info.is_kitty {