Compare commits
No commits in common. "5902011dc6142d497e37742ad1ca2fa8504c8548" and "57c86c50833a6cc482ec8e738a9f18f29550721f" have entirely different histories.
5902011dc6
...
57c86c5083
14 changed files with 53 additions and 233 deletions
|
|
@ -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
6
flake.lock
generated
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
# };
|
||||
# };
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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+$"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
inherit lib;
|
||||
pkgs = prev;
|
||||
};
|
||||
harbor = inputs.self.packages.${system} or {};
|
||||
})
|
||||
];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,5 +17,5 @@
|
|||
# open-webui
|
||||
;
|
||||
|
||||
inherit (channels.master) install-nothing marimo happy-coder;
|
||||
inherit (channels.master) install-nothing marimo;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{pkgs, ...}:
|
||||
pkgs.writeShellApplication {
|
||||
name = "active-path";
|
||||
runtimeInputs = [pkgs.procps];
|
||||
text = builtins.readFile ./active-path;
|
||||
}
|
||||
|
|
@ -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
|
||||
'';
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{ pkgs }:
|
||||
|
||||
pkgs.writeShellScriptBin "mcp-npx" ''
|
||||
# Wrapper that provides npx with Node.js in PATH
|
||||
exec ${pkgs.nodejs_22}/bin/npx "$@"
|
||||
''
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
{pkgs, ...}:
|
||||
pkgs.writeShellApplication {
|
||||
name = "quick-zeal";
|
||||
runtimeInputs = [pkgs.harbor.active-window pkgs.jq];
|
||||
text = builtins.readFile ./quick-zeal;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue