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 editor = {
79 bufferline = "multiple";
80 file-picker = {
81 hidden = false;
82 git-ignore = true;
83 };
84 cursor-shape = {
85 insert = "bar";
86 normal = "block";
87 select = "underline";
88 };
89 line-number = "relative";
90 cursorline = true;
91 auto-format = true;
92 end-of-line-diagnostics = "hint";
93 soft-wrap = {
94 enable = true;
95 };
96 lsp = {
97 display-inlay-hints = true;
98 display-messages = true;
99 display-progress-messages = true;
100 };
101 inline-diagnostics = {
102 cursor-line = "hint";
103 };
104 };
105 keys = {
106 normal = {
107 esc = [
108 "keep_primary_selection"
109 "collapse_selection"
110 ];
111 };
112 };
113 };
114 languages = {
115 language-server.rust-analyzer = {
116 config = {
117 check = {
118 command = "clippy";
119 };
120 checkOnSave = true;
121 cargo = {
122 allFeatures = true;
123 };
124 };
125 };
126 language-server.deno-lsp = {
127 command = "deno";
128 args = [ "lsp" ];
129 config.deno.enable = true;
130 };
131
132 language = [
133 {
134 name = "html";
135 formatter = {
136 command = "prettier";
137 args = [
138 "--parser"
139 "html"
140 ];
141 };
142 }
143 {
144 name = "nix";
145 auto-format = true;
146 formatter = {
147 command = "${pkgs.nixfmt}/bin/nixfmt";
148 };
149 }
150 {
151 name = "kotlin";
152 auto-format = true;
153 }
154 {
155 name = "rust";
156 auto-format = true;
157 formatter = {
158 command = "rustfmt";
159 args = [
160 "--edition"
161 "2024"
162 ];
163 };
164 indent = {
165 tab-width = 4;
166 unit = "t";
167 };
168 }
169 {
170 name = "astro";
171 auto-format = true;
172 formatter = {
173 command = "npx";
174 args = [
175 "prettier"
176 "--plugin"
177 "prettier-plugin-astro"
178 "--parser"
179 "astro"
180 ];
181 };
182 }
183 {
184 name = "json";
185 auto-format = true;
186 }
187 {
188 name = "just";
189 auto-format = true;
190 formatter = {
191 command = "just";
192 args = [
193 "--justfile"
194 "/dev/stdin"
195 "--dump"
196 ];
197 };
198 }
199 {
200 name = "toml";
201 auto-format = true;
202 formatter = {
203 command = "taplo";
204 args = [
205 "format"
206 "-"
207 ];
208 };
209 }
210 ];
211 };
212 };
213
214 programs.git = {
215 enable = true;
216 settings = {
217 user = {
218 name = "seanaye";
219 email = "hello@seanaye.ca";
220 };
221 init.defaultBranch = "main";
222 core.sshCommand = "${yubikeyThen1PasswordSsh}/bin/yubikey-then-1password-ssh";
223 commit.gpgSign = true;
224 gpg.format = "ssh";
225 user.signingKey = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOIgEteUEW06dnBHe2z8vNLwz2iMKe8bba6JgMmOUpcBAAAABHNzaDo= sean@framework16";
226 gpg.ssh.allowedSignersFile = "${config.home.homeDirectory}/.ssh/allowed_signers";
227 diff.tool = "diffnav";
228 difftool.prompt = false;
229 "difftool \"diffnav\"".cmd = "diffnav \"$LOCAL\" \"$REMOTE\"";
230 };
231 };
232
233 programs.jujutsu = {
234 enable = true;
235 settings = {
236 user = {
237 email = "hello@seanaye.ca";
238 name = "Sean Aye";
239 };
240 ui."diff-formatter" = ":git";
241 signing = {
242 sign-all = true;
243 behavior = "own";
244 backend = "ssh";
245 key = "${config.home.homeDirectory}/.ssh/id_ed25519_sk_rk";
246 backends.ssh.allowed-signers = "${config.home.homeDirectory}/.ssh/allowed_signers";
247 };
248 };
249 };
250
251 xdg.configFile."jj/conf.d/diffnav.toml".text = ''
252 [[--scope]]
253 --when.commands = ["diff", "show"]
254 [--scope.ui]
255 pager = "diffnav"
256 '';
257
258 xdg.configFile."gh-dash/config.yml".text = ''
259 prSections:
260 - title: My Pull Requests
261 filters: is:open author:@me
262 - title: Review Requested
263 filters: is:open review-requested:@me
264 issuesSections:
265 - title: My Issues
266 filters: is:open author:@me
267 pager:
268 diff: diffnav
269 keybindings:
270 prs:
271 - key: T
272 name: enhance
273 command: >-
274 zellij run -- gh enhance -R {{.RepoName}} {{.PrNumber}}
275 '';
276 };
277}