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/`.
|
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
|
## Architecture patterns
|
||||||
|
|
||||||
- **import-tree** auto-discovers and imports `.nix` files in `modules/flake/`. Files prefixed with `_` are excluded from auto-import.
|
- **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": {
|
"master": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771495899,
|
"lastModified": 1771456066,
|
||||||
"narHash": "sha256-UlAN9PHsBx1Kk65gR/KvLfO74zQcOjNZ+d/0td5T8eM=",
|
"narHash": "sha256-CLuGt3yg70gnhSam+0qpcWgPnUdY98wVeH4lByklol4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f4f54061a12ebbdd03d7c53eed54d1e135840624",
|
"rev": "be6cf55e819f4d362aa6be60254bbb2537f9a5cb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,6 @@
|
||||||
nodePackages.typescript-language-server
|
nodePackages.typescript-language-server
|
||||||
nil # nix lsp
|
nil # nix lsp
|
||||||
|
|
||||||
# Node.js for MCP servers
|
|
||||||
nodejs_22
|
|
||||||
|
|
||||||
# trurl # Parsing and manipulating URLs via CLI
|
# trurl # Parsing and manipulating URLs via CLI
|
||||||
pandoc # Document converter
|
pandoc # Document converter
|
||||||
ripgrep # Grep file search
|
ripgrep # Grep file search
|
||||||
|
|
@ -42,7 +39,6 @@
|
||||||
gitui
|
gitui
|
||||||
tea
|
tea
|
||||||
harbor.agent-deck # Terminal session manager for AI coding agents
|
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
|
harbor.oryx # TUI for sniffing network traffic using eBPF
|
||||||
|
|
||||||
# BMAD
|
# BMAD
|
||||||
|
|
@ -87,48 +83,33 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
# package = inputs.unstable.${system}.claude-code;
|
# package = inputs.unstable.${system}.claude-code;
|
||||||
|
|
||||||
mcpServers = {
|
# mcpServers = {
|
||||||
# Official MCP servers (require Node.js)
|
# fetch = {
|
||||||
fetch = {
|
# args = ["-y" "@modelcontextprotocol/server-fetch"];
|
||||||
command = "npx";
|
# command = "npx";
|
||||||
args = ["-y" "@modelcontextprotocol/server-fetch"];
|
# type = "stdio";
|
||||||
type = "stdio";
|
# };
|
||||||
};
|
# playwright = {
|
||||||
playwright = {
|
# args = ["-y" "@modelcontextprotocol/server-playwright"];
|
||||||
command = "npx";
|
# command = "npx";
|
||||||
args = ["-y" "@modelcontextprotocol/server-playwright"];
|
# type = "stdio";
|
||||||
type = "stdio";
|
# };
|
||||||
};
|
# stackexchange = {
|
||||||
stackexchange = {
|
# args = ["-y" "mcp-server-stackexchange"];
|
||||||
command = "npx";
|
# command = "npx";
|
||||||
args = ["-y" "mcp-server-stackexchange"];
|
# type = "stdio";
|
||||||
type = "stdio";
|
# };
|
||||||
};
|
# arxiv = {
|
||||||
arxiv = {
|
# args = ["-y" "mcp-server-arxiv"];
|
||||||
command = "npx";
|
# command = "npx";
|
||||||
args = ["-y" "mcp-server-arxiv"];
|
# type = "stdio";
|
||||||
type = "stdio";
|
# };
|
||||||
};
|
# claudezilla = {
|
||||||
|
# command = "bun";
|
||||||
# Custom MCP servers
|
# args = ["/home/toph/code/vendor/claudezilla/mcp/server.js"];
|
||||||
claudezilla = {
|
# type = "stdio";
|
||||||
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";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
enabled = true;
|
enabled = true;
|
||||||
size = 4;
|
size = 4;
|
||||||
passes = 2;
|
passes = 2;
|
||||||
noise = 0.05;
|
noise = 0.3;
|
||||||
};
|
};
|
||||||
shadow = {
|
shadow = {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
|
|
@ -69,16 +69,6 @@
|
||||||
|
|
||||||
# Single tiled window on main monitor: give it breathing room
|
# Single tiled window on main monitor: give it breathing room
|
||||||
workspace = [
|
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"
|
"w[t1] m[HDMI-A-1], gapsout:15 840 15 840"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
{...}: {
|
{...}: {
|
||||||
wayland.windowManager.hyprland.settings = {
|
wayland.windowManager.hyprland.settings = {
|
||||||
windowrulev2 = [
|
windowrulev2 = [
|
||||||
# Prevent garbage frame flash before first render
|
|
||||||
"immediate, class:.*"
|
|
||||||
|
|
||||||
# Privacy: block from screen capture
|
# Privacy: block from screen capture
|
||||||
"noscreenshare, class:^(1password)$"
|
"noscreenshare, class:^(1password)$"
|
||||||
"noscreenshare, class:^(thunderbird)$"
|
"noscreenshare, class:^(thunderbird)$"
|
||||||
|
|
@ -24,9 +21,8 @@
|
||||||
# Kitty: slight transparency
|
# Kitty: slight transparency
|
||||||
"opacity 0.97 0.97, class:^(kitty)$"
|
"opacity 0.97 0.97, class:^(kitty)$"
|
||||||
|
|
||||||
# JetBrains: suppress phantom windows (RE2 doesn't support lookaheads,
|
# JetBrains: suppress phantom windows
|
||||||
# so we can't exclude toolbox here — apply per-IDE if needed)
|
"nofocus, class:^jetbrains-(?!toolbox), floating:1, title:^win\\d+$"
|
||||||
# "nofocus, class:^(jetbrains-idea|jetbrains-rider|jetbrains-clion)$, floating:1, title:^win\\d+$"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
inherit lib;
|
inherit lib;
|
||||||
pkgs = prev;
|
pkgs = prev;
|
||||||
};
|
};
|
||||||
harbor = inputs.self.packages.${system} or {};
|
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,5 @@
|
||||||
# open-webui
|
# 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, ...}:
|
||||||
pkgs.writeShellApplication {
|
pkgs.writeShellApplication {
|
||||||
name = "quick-zeal";
|
name = "quick-zeal";
|
||||||
runtimeInputs = [pkgs.harbor.active-window pkgs.jq];
|
|
||||||
text = builtins.readFile ./quick-zeal;
|
text = builtins.readFile ./quick-zeal;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,13 @@ extract_major_version() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Detects the focused window and checks if it's Kitty
|
# Detects the focused window and checks if it's Kitty
|
||||||
WINDOW=$(active-window)
|
ACTIVE_WINDOW=$(hyprctl activewindow -j | jq -r '.class')
|
||||||
ACTIVE_WINDOW=$(printf '%s' "$WINDOW" | jq -r '.class // empty')
|
|
||||||
PID=$(printf '%s' "$WINDOW" | jq -r '.pid // empty')
|
|
||||||
|
|
||||||
# Check if the focused window is a Kitty terminal and if it's in a Git repository.
|
# 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
|
# If so, determine the project type and open Zeal with the appropriate argument
|
||||||
zeal_argument=""
|
zeal_argument=""
|
||||||
if [[ $ACTIVE_WINDOW == "kitty" ]]; then
|
if [[ $ACTIVE_WINDOW == "kitty" ]]; then
|
||||||
|
PID=$(hyprctl activewindow -j | jq -r '.pid')
|
||||||
CHILD_PID=$(pgrep -P "$PID" | tail -1)
|
CHILD_PID=$(pgrep -P "$PID" | tail -1)
|
||||||
|
|
||||||
SHELL_CWD=$(readlink -f "/proc/${CHILD_PID}/cwd")
|
SHELL_CWD=$(readlink -f "/proc/${CHILD_PID}/cwd")
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,25 @@
|
||||||
{pkgs, ...}:
|
{pkgs, ...}:
|
||||||
pkgs.writeNushellApplication {
|
pkgs.writeNushellApplication {
|
||||||
name = "spawn-term";
|
name = "spawn-term";
|
||||||
runtimeInputs = [pkgs.harbor.active-window];
|
runtimeInputs = with pkgs; [kdotool];
|
||||||
|
|
||||||
text = ''
|
text = ''
|
||||||
let window = (active-window | from json)
|
let compositor = $env.XDG_CURRENT_DESKTOP? | default ""
|
||||||
let window_info = {
|
|
||||||
is_kitty: ($window.class? == "kitty"),
|
let window_info = if ($compositor | str contains "niri") {
|
||||||
pid: $window.pid?
|
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 {
|
if $window_info.is_kitty {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue