me like nix
1{ ... }: {
2 flake.modules.homeManager.editor =
3 { pkgs, config, ... }:
4 let
5 yubikeyThen1PasswordSsh = pkgs.writeShellScriptBin "yubikey-then-1password-ssh" ''
6 set -u
7
8 ssh=${pkgs.openssh}/bin/ssh
9 yubi_key="''${YUBIKEY_SSH_KEY:-$HOME/.ssh/id_ed25519_sk_rk}"
10 op_sock="''${ONEPASSWORD_SSH_AUTH_SOCK:-$HOME/.1password/agent.sock}"
11 yubi_timeout="''${YUBIKEY_SSH_TIMEOUT:-8}"
12 err_file="$(${pkgs.coreutils}/bin/mktemp -t yubikey-ssh.XXXXXX)"
13 trap '${pkgs.coreutils}/bin/rm -f "$err_file"' EXIT
14
15 try_yubikey() {
16 ${pkgs.coreutils}/bin/timeout --foreground "$yubi_timeout" \
17 "$ssh" \
18 -o IdentityAgent=none \
19 -o IdentitiesOnly=yes \
20 -o PreferredAuthentications=publickey \
21 -i "$yubi_key" \
22 "$@"
23 }
24
25 # First try the local security-key identity without any agent. This
26 # makes the YubiKey win over whatever identities 1Password exposes.
27 # If the security-key path hangs, fall back after YUBIKEY_SSH_TIMEOUT
28 # seconds rather than blocking git/jj forever.
29 if [ -r "$yubi_key" ]; then
30 if [ -n "''${YUBIKEY_SSH_DEBUG:-}" ]; then
31 try_yubikey "$@"
32 else
33 try_yubikey "$@" 2>"$err_file"
34 fi
35 status=$?
36
37 if [ "$status" -eq 0 ]; then
38 exit 0
39 fi
40
41 # 124 = timeout, 137 = timeout killed after TERM was ignored.
42 # Treat both as "YubiKey unavailable" and fall back to the agent.
43 if [ "$status" -ne 124 ] && [ "$status" -ne 137 ] && ! ${pkgs.gnugrep}/bin/grep -Eiq 'permission denied|sign_and_send_pubkey|device not found|no such device|agent refused|security key|authenticat' "$err_file"; then
44 ${pkgs.coreutils}/bin/cat "$err_file" >&2
45 exit "$status"
46 fi
47 fi
48
49 # Fall back to the normal agent. Locally that is 1Password; inside an
50 # SSH session keep any forwarded agent instead of clobbering it.
51 if [ -z "''${SSH_CONNECTION:-}" ] && [ -S "$op_sock" ]; then
52 export SSH_AUTH_SOCK="$op_sock"
53 exec "$ssh" -o IdentityAgent="$op_sock" "$@"
54 fi
55
56 exec "$ssh" "$@"
57 '';
58 in
59 {
60 home.packages = (with pkgs; [
61 helix
62 diffnav
63 gh
64 forgejo-cli
65 gh-dash
66 nixfmt
67 nil
68 vscode-json-languageserver
69 ]) ++ [
70 yubikeyThen1PasswordSsh
71 ];
72
73 home.sessionVariables.GIT_SSH_COMMAND = "${yubikeyThen1PasswordSsh}/bin/yubikey-then-1password-ssh";
74
75 programs.helix = {
76 enable = true;
77 settings = {
78 theme = "noctalia";
79 editor = {
80 bufferline = "multiple";
81 file-picker = {
82 hidden = false;
83 git-ignore = true;
84 };
85 cursor-shape = {
86 insert = "bar";
87 normal = "block";
88 select = "underline";
89 };
90 line-number = "relative";
91 cursorline = true;
92 auto-format = true;
93 end-of-line-diagnostics = "hint";
94 soft-wrap = {
95 enable = true;
96 };
97 lsp = {
98 display-inlay-hints = true;
99 display-messages = true;
100 display-progress-messages = true;
101 };
102 inline-diagnostics = {
103 cursor-line = "hint";
104 };
105 };
106 keys = {
107 normal = {
108 esc = [
109 "keep_primary_selection"
110 "collapse_selection"
111 ];
112 };
113 };
114 };
115 languages = {
116 language-server.rust-analyzer = {
117 config = {
118 check = {
119 command = "clippy";
120 };
121 checkOnSave = true;
122 cargo = {
123 allFeatures = true;
124 };
125 };
126 };
127 language-server.deno-lsp = {
128 command = "deno";
129 args = [ "lsp" ];
130 config.deno.enable = true;
131 };
132
133 language = [
134 {
135 name = "html";
136 formatter = {
137 command = "prettier";
138 args = [
139 "--parser"
140 "html"
141 ];
142 };
143 }
144 {
145 name = "nix";
146 auto-format = true;
147 formatter = {
148 command = "${pkgs.nixfmt}/bin/nixfmt";
149 };
150 }
151 {
152 name = "kotlin";
153 auto-format = true;
154 }
155 {
156 name = "rust";
157 auto-format = true;
158 formatter = {
159 command = "rustfmt";
160 args = [
161 "--edition"
162 "2024"
163 ];
164 };
165 indent = {
166 tab-width = 4;
167 unit = "t";
168 };
169 }
170 {
171 name = "astro";
172 auto-format = true;
173 formatter = {
174 command = "npx";
175 args = [
176 "prettier"
177 "--plugin"
178 "prettier-plugin-astro"
179 "--parser"
180 "astro"
181 ];
182 };
183 }
184 {
185 name = "json";
186 auto-format = true;
187 }
188 {
189 name = "just";
190 auto-format = true;
191 formatter = {
192 command = "just";
193 args = [
194 "--justfile"
195 "/dev/stdin"
196 "--dump"
197 ];
198 };
199 }
200 {
201 name = "toml";
202 auto-format = true;
203 formatter = {
204 command = "taplo";
205 args = [
206 "format"
207 "-"
208 ];
209 };
210 }
211 ];
212 };
213 };
214
215 programs.git = {
216 enable = true;
217 settings = {
218 user = {
219 name = "seanaye";
220 email = "hello@seanaye.ca";
221 };
222 init.defaultBranch = "main";
223 core.sshCommand = "${yubikeyThen1PasswordSsh}/bin/yubikey-then-1password-ssh";
224 commit.gpgSign = true;
225 gpg.format = "ssh";
226 user.signingKey = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOIgEteUEW06dnBHe2z8vNLwz2iMKe8bba6JgMmOUpcBAAAABHNzaDo= sean@framework16";
227 gpg.ssh.allowedSignersFile = "${config.home.homeDirectory}/.ssh/allowed_signers";
228 diff.tool = "diffnav";
229 difftool.prompt = false;
230 "difftool \"diffnav\"".cmd = "diffnav \"$LOCAL\" \"$REMOTE\"";
231 };
232 };
233
234 programs.jujutsu = {
235 enable = true;
236 settings = {
237 user = {
238 email = "hello@seanaye.ca";
239 name = "Sean Aye";
240 };
241 ui."diff-formatter" = ":git";
242 signing = {
243 sign-all = true;
244 behavior = "own";
245 backend = "ssh";
246 key = "${config.home.homeDirectory}/.ssh/id_ed25519_sk_rk";
247 backends.ssh.allowed-signers = "${config.home.homeDirectory}/.ssh/allowed_signers";
248 };
249 };
250 };
251
252 xdg.configFile."jj/conf.d/diffnav.toml".text = ''
253 [[--scope]]
254 --when.commands = ["diff", "show"]
255 [--scope.ui]
256 pager = "diffnav"
257 '';
258
259 xdg.configFile."gh-dash/config.yml".text = ''
260 prSections:
261 - title: My Pull Requests
262 filters: is:open author:@me
263 - title: Review Requested
264 filters: is:open review-requested:@me
265 issuesSections:
266 - title: My Issues
267 filters: is:open author:@me
268 pager:
269 diff: diffnav
270 keybindings:
271 prs:
272 - key: T
273 name: enhance
274 command: >-
275 zellij run -- gh enhance -R {{.RepoName}} {{.PrNumber}}
276 '';
277 };
278}