| author | |
| committer | |
| log | b1b433e6c16b2ca52b33bbe8aea31734aa23212c |
| tree | 31a702ff97d856c39357a91c61fea712f7733f4b |
| parent | f60c39943834cc26bc64c715c9b94d541e98526a |
| signature | Signed by SSH key SHA256:xbd+BjjhyBfwk7GVoURf9Yx0gzDerHbvYv7SddNWmAs |
12 files changed, 289 insertions(+), 103 deletions(-)
modules/home/default.nix+6| ... | ... | @@ -59,6 +59,12 @@ in |
| 59 | 59 | home.shell = { |
| 60 | 60 | enableShellIntegration = true; |
| 61 | 61 | }; |
| 62 | home.sessionPath = [ | |
| 63 | "$HOME/.local/bin" | |
| 64 | "$HOME/.opencode/bin" | |
| 65 | "$HOME/.vite-plus/bin" | |
| 66 | "$HOME/bin" | |
| 67 | ]; | |
| 62 | 68 | programs = { |
| 63 | 69 | home-manager.enable = true; |
| 64 | 70 | nix-index.enable = true; |
packages/autofmt/default.nix+9-7| ... | ... | @@ -15,11 +15,13 @@ pkgs.writeTextFile { |
| 15 | 15 | name = "autofmt"; |
| 16 | 16 | destination = "/bin/autofmt"; |
| 17 | 17 | executable = true; |
| 18 | text = builtins.replaceStrings | |
| 19 | [ "#!/usr/bin/env node" ] | |
| 20 | [ | |
| 21 | ''#!${pkgs.nodejs}/bin/node | |
| 22 | process.env.PATH = "${pkgs.lib.makeBinPath runtimeInputs}:" + (process.env.PATH ?? "");'' | |
| 23 | ] | |
| 24 | (builtins.readFile ./autofmt.js); | |
| 18 | text = | |
| 19 | builtins.replaceStrings | |
| 20 | [ "#!/usr/bin/env node" ] | |
| 21 | [ | |
| 22 | '' | |
| 23 | #!${pkgs.nodejs}/bin/node | |
| 24 | process.env.PATH = "${pkgs.lib.makeBinPath runtimeInputs}:" + (process.env.PATH ?? "");'' | |
| 25 | ] | |
| 26 | (builtins.readFile ./autofmt.js); | |
| 25 | 27 | } |
packages/jj-import-merge/jj-import-merge.js+42-13| ... | ... | @@ -86,10 +86,14 @@ function usage(code) { |
| 86 | 86 | const stream = code === 0 ? process.stdout : process.stderr; |
| 87 | 87 | stream.write(`usage: ${exe} [--all-jj] [--] <file>...\n`); |
| 88 | 88 | stream.write(`\n`); |
| 89 | stream.write(`merge top-of-file JS/TS imports from 2-sided Jujutsu conflicts\n`); | |
| 89 | stream.write( | |
| 90 | `merge top-of-file JS/TS imports from 2-sided Jujutsu conflicts\n`, | |
| 91 | ); | |
| 90 | 92 | stream.write(`\n`); |
| 91 | 93 | stream.write(`options:\n`); |
| 92 | stream.write(` --all-jj resolve all conflicted JS/TS files from 'jj resolve --list'\n`); | |
| 94 | stream.write( | |
| 95 | ` --all-jj resolve all conflicted JS/TS files from 'jj resolve --list'\n`, | |
| 96 | ); | |
| 93 | 97 | stream.write(` -h show help\n`); |
| 94 | 98 | process.exit(code); |
| 95 | 99 | } |
| ... | ... | @@ -158,7 +162,11 @@ function processFile(file) { |
| 158 | 162 | leftImports.start, |
| 159 | 163 | rightImports.start, |
| 160 | 164 | ); |
| 161 | const end = projectBoundaryToOriginal(parsed.parts, leftImports.end, rightImports.end); | |
| 165 | const end = projectBoundaryToOriginal( | |
| 166 | parsed.parts, | |
| 167 | leftImports.end, | |
| 168 | rightImports.end, | |
| 169 | ); | |
| 162 | 170 | if (start == null || end == null || end < start) { |
| 163 | 171 | return { kind: "noop" }; |
| 164 | 172 | } |
| ... | ... | @@ -168,7 +176,8 @@ function processFile(file) { |
| 168 | 176 | rightImports.statements, |
| 169 | 177 | detectNewline(original), |
| 170 | 178 | ); |
| 171 | const updated = original.slice(0, start) + mergedImports + original.slice(end); | |
| 179 | const updated = original.slice(0, start) + mergedImports + | |
| 180 | original.slice(end); | |
| 172 | 181 | |
| 173 | 182 | if (updated === original) { |
| 174 | 183 | return { kind: "noop" }; |
| ... | ... | @@ -193,7 +202,9 @@ function processFile(file) { |
| 193 | 202 | if (formatResult.error || formatResult.status !== 0) { |
| 194 | 203 | fs.writeFileSync(file, original, "utf8"); |
| 195 | 204 | if (formatResult.error) { |
| 196 | throw new Error(`autofmt failed for ${file}: ${formatResult.error.message}`); | |
| 205 | throw new Error( | |
| 206 | `autofmt failed for ${file}: ${formatResult.error.message}`, | |
| 207 | ); | |
| 197 | 208 | } |
| 198 | 209 | throw new Error(`autofmt failed for ${file}`); |
| 199 | 210 | } |
| ... | ... | @@ -259,7 +270,9 @@ function parseConflictBlock(lines, startIndex) { |
| 259 | 270 | continue; |
| 260 | 271 | } |
| 261 | 272 | if (line.startsWith("%%%%%%% diff from:")) { |
| 262 | if (index + 1 >= lines.length || !/^\\\\+\s*to:/.test(lines[index + 1].text)) { | |
| 273 | if ( | |
| 274 | index + 1 >= lines.length || !/^\\\\+\s*to:/.test(lines[index + 1].text) | |
| 275 | ) { | |
| 263 | 276 | return { supported: false, nextIndex: startIndex + 1 }; |
| 264 | 277 | } |
| 265 | 278 | index += 2; |
| ... | ... | @@ -512,7 +525,7 @@ function scanLine(line, state) { |
| 512 | 525 | index += 1; |
| 513 | 526 | continue; |
| 514 | 527 | } |
| 515 | if (char === "'" || char === "\"" || char === "`") { | |
| 528 | if (char === "'" || char === '"' || char === "`") { | |
| 516 | 529 | state.stringQuote = char; |
| 517 | 530 | continue; |
| 518 | 531 | } |
| ... | ... | @@ -716,12 +729,18 @@ function canMergeImports(left, right) { |
| 716 | 729 | } |
| 717 | 730 | |
| 718 | 731 | const defaultBinding = mergeField(left.defaultBinding, right.defaultBinding); |
| 719 | const namespaceImport = mergeField(left.namespaceImport, right.namespaceImport); | |
| 732 | const namespaceImport = mergeField( | |
| 733 | left.namespaceImport, | |
| 734 | right.namespaceImport, | |
| 735 | ); | |
| 720 | 736 | if (defaultBinding === undefined || namespaceImport === undefined) { |
| 721 | 737 | return false; |
| 722 | 738 | } |
| 723 | 739 | |
| 724 | const namedSpecifiers = unique([...left.namedSpecifiers, ...right.namedSpecifiers]); | |
| 740 | const namedSpecifiers = unique([ | |
| 741 | ...left.namedSpecifiers, | |
| 742 | ...right.namedSpecifiers, | |
| 743 | ]); | |
| 725 | 744 | if (namespaceImport && namedSpecifiers.length > 0) { |
| 726 | 745 | return false; |
| 727 | 746 | } |
| ... | ... | @@ -731,8 +750,14 @@ function canMergeImports(left, right) { |
| 731 | 750 | |
| 732 | 751 | function mergeImports(left, right) { |
| 733 | 752 | const defaultBinding = mergeField(left.defaultBinding, right.defaultBinding); |
| 734 | const namespaceImport = mergeField(left.namespaceImport, right.namespaceImport); | |
| 735 | const namedSpecifiers = unique([...left.namedSpecifiers, ...right.namedSpecifiers]); | |
| 753 | const namespaceImport = mergeField( | |
| 754 | left.namespaceImport, | |
| 755 | right.namespaceImport, | |
| 756 | ); | |
| 757 | const namedSpecifiers = unique([ | |
| 758 | ...left.namedSpecifiers, | |
| 759 | ...right.namedSpecifiers, | |
| 760 | ]); | |
| 736 | 761 | |
| 737 | 762 | return { |
| 738 | 763 | kind: "binding", |
| ... | ... | @@ -782,13 +807,17 @@ function renderImport(analysis, newline) { |
| 782 | 807 | pieces.push(`{ ${analysis.namedSpecifiers[0]} }`); |
| 783 | 808 | } else { |
| 784 | 809 | pieces.push( |
| 785 | `{${newline}${analysis.namedSpecifiers.map((spec) => ` ${spec},`).join(newline)}${newline}}`, | |
| 810 | `{${newline}${ | |
| 811 | analysis.namedSpecifiers.map((spec) => ` ${spec},`).join(newline) | |
| 812 | }${newline}}`, | |
| 786 | 813 | ); |
| 787 | 814 | } |
| 788 | 815 | } |
| 789 | 816 | |
| 790 | 817 | return ensureTrailingNewline( |
| 791 | `import ${analysis.isTypeImport ? "type " : ""}${pieces.join(", ")} from ${analysis.quote}${analysis.source}${analysis.quote}${analysis.attributes};`, | |
| 818 | `import ${analysis.isTypeImport ? "type " : ""}${ | |
| 819 | pieces.join(", ") | |
| 820 | } from ${analysis.quote}${analysis.source}${analysis.quote}${analysis.attributes};`, | |
| 792 | 821 | newline, |
| 793 | 822 | ); |
| 794 | 823 | } |
switch+6-3| ... | ... | @@ -12,13 +12,16 @@ if [ -z "$hostname" ]; then |
| 12 | 12 | hostname="$(hostname)" |
| 13 | 13 | fi |
| 14 | 14 | printf "%s" "$hostname" >.hostname |
| 15 | if [ "$(uname -o)" = "Darwin" ]; then | |
| 15 | if [ "$(uname -s)" = "Darwin" ]; then | |
| 16 | 16 | nh_subcommand="darwin" |
| 17 | 17 | fallback_command="nix run .#darwin-rebuild" |
| 18 | hostname="darwinConfigurations.$hostname" | |
| 19 | 18 | fi |
| 20 | 19 | if command -v nh >/dev/null; then |
| 21 | nh $nh_subcommand switch '.#'"$hostname" | |
| 20 | if [ "$nh_subcommand" = "darwin" ]; then | |
| 21 | nh darwin switch . --hostname "$hostname" | |
| 22 | else | |
| 23 | nh os switch '.#'"$hostname" | |
| 24 | fi | |
| 22 | 25 | else |
| 23 | 26 | $fallback_command -- switch --flake '.#'"$hostname" |
| 24 | 27 | fi |
users/clover/AGENTS.md+4| ... | ... | @@ -13,6 +13,10 @@ You are working with Clover, an intelligent web/systems engineer. Please avoid e |
| 13 | 13 | - Never include a section on checks saying "pnpm test passed" or "checks passed" or whatever. Just make sure your validation is strong in the first place; You're expected to have that done. |
| 14 | 14 | - Feedback should be useful and actionable. A couple words is OK if there is genuinely nothing to add. |
| 15 | 15 | |
| 16 | ## Verification | |
| 17 | ||
| 18 | - Use the `code-reviewer` agent for all verification after meaningful code, config, test, or doc changes. The top-level agent should pass only a concise list of what it did, including changed files and intended behavior; the reviewer independently inspects the diff, runs relevant checks, and reports findings. The `code-reviewer` agent is told it is the reviewer agent, so it must not recurse into itself. | |
| 19 | ||
| 16 | 20 | ## Comments |
| 17 | 21 | |
| 18 | 22 | Comments must always be placed and edited with intent to serialize codebase theory for future versions of ourselves and coworkers. They are professionally written, containing relevant context to the current iteration. Clover's comments are always timeless and rarely written from the first person (no `I`, `we`) -- refer directly to the code. Rewording comments this way helps solidify your own understanding, and can even reveal logic bugs in the designed systems. Prefer documentation comments over line comments. |
users/clover/home.nix-4| ... | ... | @@ -10,10 +10,6 @@ in |
| 10 | 10 | { |
| 11 | 11 | home = { |
| 12 | 12 | stateVersion = "23.05"; # Please read the comment before changing. |
| 13 | sessionPath = [ | |
| 14 | "$HOME/.vite-plus/bin" | |
| 15 | "$HOME/.local/bin" | |
| 16 | ]; | |
| 17 | 13 | packages = |
| 18 | 14 | with pkgs; |
| 19 | 15 | let |
users/clover/paperback/configuration.nix+5-1| ... | ... | @@ -1,2 +1,6 @@ |
| 1 | _: { | |
| 1 | { lib, ... }: | |
| 2 | { | |
| 3 | nix.enable = false; | |
| 4 | nix.optimise.automatic = lib.mkForce false; | |
| 5 | nix.gc.automatic = lib.mkForce false; | |
| 2 | 6 | } |
users/clover/pnpm-lock.yaml+62-44| ... | ... | @@ -1,99 +1,117 @@ |
| 1 | lockfileVersion: '9.0' | |
| 1 | lockfileVersion: "9.0" | |
| 2 | 2 | |
| 3 | 3 | settings: |
| 4 | 4 | autoInstallPeers: true |
| 5 | 5 | excludeLinksFromLockfile: false |
| 6 | 6 | |
| 7 | 7 | importers: |
| 8 | ||
| 9 | 8 | .: |
| 10 | 9 | dependencies: |
| 11 | '@clo/lib': | |
| 10 | "@clo/lib": | |
| 12 | 11 | specifier: jsr:* |
| 13 | version: '@jsr/clo__lib@4.0.0' | |
| 14 | '@lydell/node-pty': | |
| 12 | version: "@jsr/clo__lib@4.0.0" | |
| 13 | "@lydell/node-pty": | |
| 15 | 14 | specifier: 1.2.0-beta.12 |
| 16 | 15 | version: 1.2.0-beta.12 |
| 17 | '@types/node': | |
| 16 | "@types/node": | |
| 18 | 17 | specifier: ^24.12.2 |
| 19 | 18 | version: 24.12.2 |
| 20 | 19 | |
| 21 | 20 | packages: |
| 22 | ||
| 23 | '@jsr/clo__lib@4.0.0': | |
| 24 | resolution: {integrity: sha512-uOzRjwbFn9HizCpJeqPXzuiri3hqqcePNLA7ESi1Vgwon9dOmhMSzHfmb9kz599VsKF1RLH7i3NK302K60Oe/A==, tarball: https://npm.jsr.io/~/11/@jsr/clo__lib/4.0.0.tgz} | |
| 25 | ||
| 26 | '@lydell/node-pty-darwin-arm64@1.2.0-beta.12': | |
| 27 | resolution: {integrity: sha512-tqaifcY9Cr41SblO1+FLzh8oxxtkNhuW9Dhl22lKme9BreYvKvxEZcdPIXTuqkJc5tagOEC4QHShKmJjLyLXLQ==} | |
| 21 | "@jsr/clo__lib@4.0.0": | |
| 22 | resolution: { | |
| 23 | integrity: sha512-uOzRjwbFn9HizCpJeqPXzuiri3hqqcePNLA7ESi1Vgwon9dOmhMSzHfmb9kz599VsKF1RLH7i3NK302K60Oe/A==, | |
| 24 | tarball: https://npm.jsr.io/~/11/@jsr/clo__lib/4.0.0.tgz, | |
| 25 | } | |
| 26 | ||
| 27 | "@lydell/node-pty-darwin-arm64@1.2.0-beta.12": | |
| 28 | resolution: { | |
| 29 | integrity: sha512-tqaifcY9Cr41SblO1+FLzh8oxxtkNhuW9Dhl22lKme9BreYvKvxEZcdPIXTuqkJc5tagOEC4QHShKmJjLyLXLQ==, | |
| 30 | } | |
| 28 | 31 | cpu: [arm64] |
| 29 | 32 | os: [darwin] |
| 30 | 33 | |
| 31 | '@lydell/node-pty-darwin-x64@1.2.0-beta.12': | |
| 32 | resolution: {integrity: sha512-4LrS5pCJwqHKDVf1zS2gyNV0m4hKAXch+XZNhbZ6LY8uwVL8BhchzQBO40Os5anuRxRCWzHpw4Sp64Ie8q7E4Q==} | |
| 34 | "@lydell/node-pty-darwin-x64@1.2.0-beta.12": | |
| 35 | resolution: { | |
| 36 | integrity: sha512-4LrS5pCJwqHKDVf1zS2gyNV0m4hKAXch+XZNhbZ6LY8uwVL8BhchzQBO40Os5anuRxRCWzHpw4Sp64Ie8q7E4Q==, | |
| 37 | } | |
| 33 | 38 | cpu: [x64] |
| 34 | 39 | os: [darwin] |
| 35 | 40 | |
| 36 | '@lydell/node-pty-linux-arm64@1.2.0-beta.12': | |
| 37 | resolution: {integrity: sha512-Sx+A71x5BDGHt9ansfrtGxwq2VFVDWvJUAdlUL0Hv0qeiJUfts+hgopx+CgT4PSwahKjdEgtu0+FAfY9rICKRw==} | |
| 41 | "@lydell/node-pty-linux-arm64@1.2.0-beta.12": | |
| 42 | resolution: { | |
| 43 | integrity: sha512-Sx+A71x5BDGHt9ansfrtGxwq2VFVDWvJUAdlUL0Hv0qeiJUfts+hgopx+CgT4PSwahKjdEgtu0+FAfY9rICKRw==, | |
| 44 | } | |
| 38 | 45 | cpu: [arm64] |
| 39 | 46 | os: [linux] |
| 40 | 47 | |
| 41 | '@lydell/node-pty-linux-x64@1.2.0-beta.12': | |
| 42 | resolution: {integrity: sha512-bJzs94njofYhGg/UDqW1nj0dtvvu+2OvxMY+RlLS1T17VgcktKoIR6PuenTwE5HJ/D6StCPADmXcT0nNsCKmIQ==} | |
| 48 | "@lydell/node-pty-linux-x64@1.2.0-beta.12": | |
| 49 | resolution: { | |
| 50 | integrity: sha512-bJzs94njofYhGg/UDqW1nj0dtvvu+2OvxMY+RlLS1T17VgcktKoIR6PuenTwE5HJ/D6StCPADmXcT0nNsCKmIQ==, | |
| 51 | } | |
| 43 | 52 | cpu: [x64] |
| 44 | 53 | os: [linux] |
| 45 | 54 | |
| 46 | '@lydell/node-pty-win32-arm64@1.2.0-beta.12': | |
| 47 | resolution: {integrity: sha512-p7POgjVEiFaBC3/y+AKuV1FzePCsJ6HmZDv2XK+jBZSfwP8+uBAw181ZiKYN1YuRa/XpmBGaWezcI8hZkbW++g==} | |
| 55 | "@lydell/node-pty-win32-arm64@1.2.0-beta.12": | |
| 56 | resolution: { | |
| 57 | integrity: sha512-p7POgjVEiFaBC3/y+AKuV1FzePCsJ6HmZDv2XK+jBZSfwP8+uBAw181ZiKYN1YuRa/XpmBGaWezcI8hZkbW++g==, | |
| 58 | } | |
| 48 | 59 | cpu: [arm64] |
| 49 | 60 | os: [win32] |
| 50 | 61 | |
| 51 | '@lydell/node-pty-win32-x64@1.2.0-beta.12': | |
| 52 | resolution: {integrity: sha512-IDFa00g7qUDGUYgByrUBJtC+mOjYVt/8KYyWivCg5JjGOHbBUACUQZLl0jTWmnr+tld/UyTpX90a2PY6oTVtRw==} | |
| 62 | "@lydell/node-pty-win32-x64@1.2.0-beta.12": | |
| 63 | resolution: { | |
| 64 | integrity: sha512-IDFa00g7qUDGUYgByrUBJtC+mOjYVt/8KYyWivCg5JjGOHbBUACUQZLl0jTWmnr+tld/UyTpX90a2PY6oTVtRw==, | |
| 65 | } | |
| 53 | 66 | cpu: [x64] |
| 54 | 67 | os: [win32] |
| 55 | 68 | |
| 56 | '@lydell/node-pty@1.2.0-beta.12': | |
| 57 | resolution: {integrity: sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g==} | |
| 69 | "@lydell/node-pty@1.2.0-beta.12": | |
| 70 | resolution: { | |
| 71 | integrity: sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g==, | |
| 72 | } | |
| 58 | 73 | |
| 59 | '@types/node@24.12.2': | |
| 60 | resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} | |
| 74 | "@types/node@24.12.2": | |
| 75 | resolution: { | |
| 76 | integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==, | |
| 77 | } | |
| 61 | 78 | |
| 62 | 79 | undici-types@7.16.0: |
| 63 | resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} | |
| 80 | resolution: { | |
| 81 | integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==, | |
| 82 | } | |
| 64 | 83 | |
| 65 | 84 | snapshots: |
| 85 | "@jsr/clo__lib@4.0.0": {} | |
| 66 | 86 | |
| 67 | '@jsr/clo__lib@4.0.0': {} | |
| 68 | ||
| 69 | '@lydell/node-pty-darwin-arm64@1.2.0-beta.12': | |
| 87 | "@lydell/node-pty-darwin-arm64@1.2.0-beta.12": | |
| 70 | 88 | optional: true |
| 71 | 89 | |
| 72 | '@lydell/node-pty-darwin-x64@1.2.0-beta.12': | |
| 90 | "@lydell/node-pty-darwin-x64@1.2.0-beta.12": | |
| 73 | 91 | optional: true |
| 74 | 92 | |
| 75 | '@lydell/node-pty-linux-arm64@1.2.0-beta.12': | |
| 93 | "@lydell/node-pty-linux-arm64@1.2.0-beta.12": | |
| 76 | 94 | optional: true |
| 77 | 95 | |
| 78 | '@lydell/node-pty-linux-x64@1.2.0-beta.12': | |
| 96 | "@lydell/node-pty-linux-x64@1.2.0-beta.12": | |
| 79 | 97 | optional: true |
| 80 | 98 | |
| 81 | '@lydell/node-pty-win32-arm64@1.2.0-beta.12': | |
| 99 | "@lydell/node-pty-win32-arm64@1.2.0-beta.12": | |
| 82 | 100 | optional: true |
| 83 | 101 | |
| 84 | '@lydell/node-pty-win32-x64@1.2.0-beta.12': | |
| 102 | "@lydell/node-pty-win32-x64@1.2.0-beta.12": | |
| 85 | 103 | optional: true |
| 86 | 104 | |
| 87 | '@lydell/node-pty@1.2.0-beta.12': | |
| 105 | "@lydell/node-pty@1.2.0-beta.12": | |
| 88 | 106 | optionalDependencies: |
| 89 | '@lydell/node-pty-darwin-arm64': 1.2.0-beta.12 | |
| 90 | '@lydell/node-pty-darwin-x64': 1.2.0-beta.12 | |
| 91 | '@lydell/node-pty-linux-arm64': 1.2.0-beta.12 | |
| 92 | '@lydell/node-pty-linux-x64': 1.2.0-beta.12 | |
| 93 | '@lydell/node-pty-win32-arm64': 1.2.0-beta.12 | |
| 94 | '@lydell/node-pty-win32-x64': 1.2.0-beta.12 | |
| 95 | ||
| 96 | '@types/node@24.12.2': | |
| 107 | "@lydell/node-pty-darwin-arm64": 1.2.0-beta.12 | |
| 108 | "@lydell/node-pty-darwin-x64": 1.2.0-beta.12 | |
| 109 | "@lydell/node-pty-linux-arm64": 1.2.0-beta.12 | |
| 110 | "@lydell/node-pty-linux-x64": 1.2.0-beta.12 | |
| 111 | "@lydell/node-pty-win32-arm64": 1.2.0-beta.12 | |
| 112 | "@lydell/node-pty-win32-x64": 1.2.0-beta.12 | |
| 113 | ||
| 114 | "@types/node@24.12.2": | |
| 97 | 115 | dependencies: |
| 98 | 116 | undici-types: 7.16.0 |
| 99 | 117 |
users/clover/sandwich/home.nix+77-2| ... | ... | @@ -1,7 +1,82 @@ |
| 1 | { pkgs, ... }: | |
| 2 | { | |
| 1 | { config, pkgs, ... }: | |
| 2 | let | |
| 3 | # Hourly backup: rsync the local working drives up to the NAS, then (only on a | |
| 4 | # fully clean push) trigger a ZFS snapshot + tiered prune on the NAS. The backup | |
| 5 | # on the NAS is plain browsable files; history lives in ZFS snapshots. | |
| 6 | sandwich-backup = pkgs.writeShellApplication { | |
| 7 | name = "sandwich-backup"; | |
| 8 | runtimeInputs = with pkgs; [ | |
| 9 | rsync | |
| 10 | openssh | |
| 11 | coreutils | |
| 12 | ]; | |
| 13 | text = '' | |
| 14 | NAS=clo@git.paperclover.net | |
| 15 | DEST=$NAS:/mnt/storage1/backup/sandwich | |
| 16 | LOCK=/tmp/sandwich-backup.lock | |
| 17 | ||
| 18 | # Dedicated passphraseless key so the launchd job runs unattended; the | |
| 19 | # interactive key keeps its passphrase. IdentitiesOnly stops ssh from | |
| 20 | # offering the passphrased key first. | |
| 21 | SSH=(ssh -i "$HOME/.ssh/id_sandwich_backup" -o IdentitiesOnly=yes -o BatchMode=yes) | |
| 22 | ||
| 23 | # Single-instance guard: the initial sync takes hours, far longer than the | |
| 24 | # hourly interval. mkdir is atomic; recover if a previous run died uncleanly. | |
| 25 | if ! mkdir "$LOCK" 2>/dev/null; then | |
| 26 | if [ -f "$LOCK/pid" ] && kill -0 "$(cat "$LOCK/pid")" 2>/dev/null; then | |
| 27 | echo "already running (pid $(cat "$LOCK/pid"))" | |
| 28 | exit 0 | |
| 29 | fi | |
| 30 | echo "clearing stale lock" | |
| 31 | rm -rf "$LOCK" | |
| 32 | mkdir "$LOCK" | |
| 33 | fi | |
| 34 | echo $$ >"$LOCK/pid" | |
| 35 | trap 'rm -rf "$LOCK"' EXIT | |
| 36 | ||
| 37 | # rsync exit 24 (source files vanished mid-transfer) is benign on a live | |
| 38 | # machine; anything else is a real failure and aborts before the snapshot. | |
| 39 | rsync_one() { | |
| 40 | local rc=0 | |
| 41 | rsync -a --delete --partial --human-readable --stats \ | |
| 42 | -e "''${SSH[*]}" \ | |
| 43 | --exclude '.DS_Store' \ | |
| 44 | --exclude '.Spotlight-V100' \ | |
| 45 | --exclude '.Trashes' \ | |
| 46 | --exclude '.fseventsd' \ | |
| 47 | --exclude '.TemporaryItems' \ | |
| 48 | --exclude '.DocumentRevisions-V100' \ | |
| 49 | "$@" || rc=$? | |
| 50 | if [ "$rc" -ne 0 ] && [ "$rc" -ne 24 ]; then | |
| 51 | return "$rc" | |
| 52 | fi | |
| 53 | } | |
| 3 | 54 | |
| 55 | rsync_one /Volumes/Asset/ "$DEST/Asset/" | |
| 56 | rsync_one /Volumes/Project/ "$DEST/Project/" | |
| 57 | rsync_one --exclude 'LMStudio/' /Volumes/Documents/ "$DEST/Documents/" | |
| 58 | rsync_one "$HOME/Desktop/" "$DEST/Desktop/" | |
| 59 | ||
| 60 | # Snapshot only reached if every push above succeeded. | |
| 61 | "''${SSH[@]}" "$NAS" /mnt/storage1/apps/home-infra/backup/snapshot-prune.sh storage1/backup/sandwich | |
| 62 | ''; | |
| 63 | }; | |
| 64 | in | |
| 65 | { | |
| 4 | 66 | home.packages = with pkgs; [ |
| 5 | 67 | nodejs_24 |
| 68 | rsync | |
| 69 | sandwich-backup | |
| 6 | 70 | ]; |
| 71 | ||
| 72 | launchd.agents.sandwich-backup = { | |
| 73 | enable = true; | |
| 74 | config = { | |
| 75 | ProgramArguments = [ "${sandwich-backup}/bin/sandwich-backup" ]; | |
| 76 | StartInterval = 3600; | |
| 77 | RunAtLoad = true; | |
| 78 | StandardOutPath = "${config.home.homeDirectory}/Library/Logs/sandwich-backup.log"; | |
| 79 | StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/sandwich-backup.log"; | |
| 80 | }; | |
| 81 | }; | |
| 7 | 82 | } |
users/clover/scripts/jj-ci.ts+76-24| ... | ... | @@ -153,8 +153,9 @@ if (!fs.existsSync(ciScript)) { |
| 153 | 153 | if (invalid) process.exit(1); |
| 154 | 154 | |
| 155 | 155 | if (parallelize) { |
| 156 | if (dryRun) log.info("would run", "jj parallelize " + selectedRevisions.join(" ")); | |
| 157 | else jj(["parallelize", ...selectedRevisions]); | |
| 156 | if (dryRun) { | |
| 157 | log.info("would run", "jj parallelize " + selectedRevisions.join(" ")); | |
| 158 | } else jj(["parallelize", ...selectedRevisions]); | |
| 158 | 159 | } |
| 159 | 160 | |
| 160 | 161 | const changeIds = [...new Set(selectedRevisions.flatMap(resolveChangeIds))]; |
| ... | ... | @@ -185,14 +186,14 @@ const plans: Plan[] = changeIds.map((changeId) => ({ |
| 185 | 186 | .filter((candidate) => candidate !== "" && candidate !== changeId), |
| 186 | 187 | ), |
| 187 | 188 | ], |
| 188 | label: changeId.slice(0, 6) + ': ' + jj( | |
| 189 | label: changeId.slice(0, 6) + ": " + jj( | |
| 189 | 190 | [ |
| 190 | 191 | "log", |
| 191 | 192 | "--no-graph", |
| 192 | 193 | "-r", |
| 193 | 194 | `${changeId}`, |
| 194 | 195 | "-T", |
| 195 | 'self.description().first_line()', | |
| 196 | "self.description().first_line()", | |
| 196 | 197 | ], |
| 197 | 198 | { ignoreWorkingCopy: true }, |
| 198 | 199 | ), |
| ... | ... | @@ -272,7 +273,9 @@ try { |
| 272 | 273 | if (state === "ok") passed += 1; |
| 273 | 274 | else if (state === "failed" || state === "skipped") failed += 1; |
| 274 | 275 | } |
| 275 | return `${ansi.fgGreen}${passed} pass${ansi.reset}, ${failed > 0 ? ansi.fgRed : ""}${failed} fail${ansi.reset}`; | |
| 276 | return `${ansi.fgGreen}${passed} pass${ansi.reset}, ${ | |
| 277 | failed > 0 ? ansi.fgRed : "" | |
| 278 | }${failed} fail${ansi.reset}`; | |
| 276 | 279 | }, |
| 277 | 280 | }); |
| 278 | 281 | |
| ... | ... | @@ -313,7 +316,14 @@ try { |
| 313 | 316 | |
| 314 | 317 | try { |
| 315 | 318 | plainLog.push("--- prep ---\n"); |
| 316 | ensureWorkspace(slot, plan.changeId, plan.label, trackedWorkspaces, node, plainLog); | |
| 319 | ensureWorkspace( | |
| 320 | slot, | |
| 321 | plan.changeId, | |
| 322 | plan.label, | |
| 323 | trackedWorkspaces, | |
| 324 | node, | |
| 325 | plainLog, | |
| 326 | ); | |
| 317 | 327 | plainLog.push("\n"); |
| 318 | 328 | |
| 319 | 329 | phase = "ci"; |
| ... | ... | @@ -324,7 +334,9 @@ try { |
| 324 | 334 | node, |
| 325 | 335 | }); |
| 326 | 336 | plainLog.push( |
| 327 | `--- ci ---\nexit: ${ci.exitCode ?? "signal"}\nduration_ms: ${ci.durationMs}\n\n`, | |
| 337 | `--- ci ---\nexit: ${ | |
| 338 | ci.exitCode ?? "signal" | |
| 339 | }\nduration_ms: ${ci.durationMs}\n\n`, | |
| 328 | 340 | ); |
| 329 | 341 | plainLog.push(ci.plainText); |
| 330 | 342 | if (ci.plainText !== "" && !ci.plainText.endsWith("\n")) { |
| ... | ... | @@ -369,7 +381,9 @@ try { |
| 369 | 381 | ? (error.stack ?? error.message) |
| 370 | 382 | : String(error); |
| 371 | 383 | node.log.error(error); |
| 372 | plainLog.push(`--- ${phase} error ---\n${normalizePlain(message)}\n`); | |
| 384 | plainLog.push( | |
| 385 | `--- ${phase} error ---\n${normalizePlain(message)}\n`, | |
| 386 | ); | |
| 373 | 387 | writeFailureLog(plan.logPath, plainLog); |
| 374 | 388 | return { |
| 375 | 389 | changeId: plan.changeId, |
| ... | ... | @@ -393,10 +407,13 @@ try { |
| 393 | 407 | for (const plan of plans) { |
| 394 | 408 | if (stateByChangeId.get(plan.changeId) !== "pending") continue; |
| 395 | 409 | |
| 396 | const blockedBy = plan.dependencyChangeIds.filter((dependencyChangeId) => { | |
| 397 | const dependencyState = stateByChangeId.get(dependencyChangeId); | |
| 398 | return dependencyState === "failed" || dependencyState === "skipped"; | |
| 399 | }); | |
| 410 | const blockedBy = plan.dependencyChangeIds.filter( | |
| 411 | (dependencyChangeId) => { | |
| 412 | const dependencyState = stateByChangeId.get(dependencyChangeId); | |
| 413 | return dependencyState === "failed" || | |
| 414 | dependencyState === "skipped"; | |
| 415 | }, | |
| 416 | ); | |
| 400 | 417 | |
| 401 | 418 | if (blockedBy.length === 0) continue; |
| 402 | 419 | |
| ... | ... | @@ -456,12 +473,17 @@ try { |
| 456 | 473 | |
| 457 | 474 | if (!result.ok) continue; |
| 458 | 475 | |
| 459 | for (const childChangeId of childrenByChangeId.get(result.changeId) ?? []) { | |
| 476 | for ( | |
| 477 | const childChangeId of childrenByChangeId.get(result.changeId) ?? [] | |
| 478 | ) { | |
| 460 | 479 | if (stateByChangeId.get(childChangeId) !== "pending") continue; |
| 461 | 480 | |
| 462 | 481 | const remainingDependencies = |
| 463 | 482 | (remainingDependenciesByChangeId.get(childChangeId) ?? 1) - 1; |
| 464 | remainingDependenciesByChangeId.set(childChangeId, remainingDependencies); | |
| 483 | remainingDependenciesByChangeId.set( | |
| 484 | childChangeId, | |
| 485 | remainingDependencies, | |
| 486 | ); | |
| 465 | 487 | |
| 466 | 488 | if (remainingDependencies === 0) { |
| 467 | 489 | const childPlan = planByChangeId.get(childChangeId); |
| ... | ... | @@ -509,7 +531,14 @@ try { |
| 509 | 531 | summary.push( |
| 510 | 532 | `${ansi.style(failStyle, "x")} ${result.label} (${result.phase})`, |
| 511 | 533 | result.blockedBy?.length |
| 512 | ? ` ${ansi.style(dimStyle, `[waiting on ${result.blockedBy.map((id) => id.slice(0, 12)).join(", ")}]`)}` | |
| 534 | ? ` ${ | |
| 535 | ansi.style( | |
| 536 | dimStyle, | |
| 537 | `[waiting on ${ | |
| 538 | result.blockedBy.map((id) => id.slice(0, 12)).join(", ") | |
| 539 | }]`, | |
| 540 | ) | |
| 541 | }` | |
| 513 | 542 | : "", |
| 514 | 543 | result.logPath |
| 515 | 544 | ? ` ${ansi.style(dimStyle, `[log ${result.logPath}]`)}` |
| ... | ... | @@ -525,7 +554,14 @@ try { |
| 525 | 554 | summary.push( |
| 526 | 555 | `${ansi.style(skipStyle, "-")} ${result.label}`, |
| 527 | 556 | result.blockedBy?.length |
| 528 | ? ` ${ansi.style(dimStyle, `[blocked by ${result.blockedBy.map((id) => id.slice(0, 12)).join(", ")}]`)}` | |
| 557 | ? ` ${ | |
| 558 | ansi.style( | |
| 559 | dimStyle, | |
| 560 | `[blocked by ${ | |
| 561 | result.blockedBy.map((id) => id.slice(0, 12)).join(", ") | |
| 562 | }]`, | |
| 563 | ) | |
| 564 | }` | |
| 529 | 565 | : "", |
| 530 | 566 | "\n", |
| 531 | 567 | ); |
| ... | ... | @@ -543,7 +579,9 @@ try { |
| 543 | 579 | if (successfulChangeIds.length > 0) { |
| 544 | 580 | summary.push( |
| 545 | 581 | "\n", |
| 546 | `${ansi.style(infoStyle, "push successful commits:")} jj git push -c "${successfulChangeIds.join("|")}"`, | |
| 582 | `${ansi.style(infoStyle, "push successful commits:")} jj git push -c "${ | |
| 583 | successfulChangeIds.join("|") | |
| 584 | }"`, | |
| 547 | 585 | "\n", |
| 548 | 586 | ); |
| 549 | 587 | } |
| ... | ... | @@ -616,7 +654,6 @@ function resolveGitDir(cwd: string): string | null { |
| 616 | 654 | return value; |
| 617 | 655 | } |
| 618 | 656 | |
| 619 | ||
| 620 | 657 | function jj(args: string[], options: CommandOptions = {}): string { |
| 621 | 658 | const result = child_process.spawnSync( |
| 622 | 659 | "jj", |
| ... | ... | @@ -637,7 +674,9 @@ function jj(args: string[], options: CommandOptions = {}): string { |
| 637 | 674 | const text = stdout + stderr; |
| 638 | 675 | |
| 639 | 676 | if (options.node && text !== "") options.node.log.write(text); |
| 640 | if (options.plainLog && text !== "") options.plainLog.push(normalizePlain(text)); | |
| 677 | if (options.plainLog && text !== "") { | |
| 678 | options.plainLog.push(normalizePlain(text)); | |
| 679 | } | |
| 641 | 680 | |
| 642 | 681 | if (result.error) throw result.error; |
| 643 | 682 | if (result.status !== 0) { |
| ... | ... | @@ -891,7 +930,10 @@ function forgetPoolWorkspaces( |
| 891 | 930 | } |
| 892 | 931 | |
| 893 | 932 | if (errors.length > 0) { |
| 894 | throw new AggregateError(errors, "failed to forget one or more jj-ci workspaces"); | |
| 933 | throw new AggregateError( | |
| 934 | errors, | |
| 935 | "failed to forget one or more jj-ci workspaces", | |
| 936 | ); | |
| 895 | 937 | } |
| 896 | 938 | } |
| 897 | 939 | |
| ... | ... | @@ -964,7 +1006,12 @@ function runScript( |
| 964 | 1006 | ? preferredShell |
| 965 | 1007 | : "bash"; |
| 966 | 1008 | |
| 967 | const term = pty.spawn(shell, ["-c", 'set -o pipefail; set -eux; source "$1"', "jj-ci", scriptPath], { | |
| 1009 | const term = pty.spawn(shell, [ | |
| 1010 | "-c", | |
| 1011 | 'set -o pipefail; set -eux; source "$1"', | |
| 1012 | "jj-ci", | |
| 1013 | scriptPath, | |
| 1014 | ], { | |
| 968 | 1015 | cols: process.stdout.columns ?? 120, |
| 969 | 1016 | cwd: options.cwd, |
| 970 | 1017 | encoding: "utf8", |
| ... | ... | @@ -1055,11 +1102,16 @@ function tryMatchDescendant( |
| 1055 | 1102 | matchedDependencyByDescendant: Map<string, string>, |
| 1056 | 1103 | seenDescendants: Set<string>, |
| 1057 | 1104 | ): boolean { |
| 1058 | for (const descendantChangeId of descendantsByChangeId.get(dependencyChangeId) ?? []) { | |
| 1105 | for ( | |
| 1106 | const descendantChangeId of descendantsByChangeId.get(dependencyChangeId) ?? | |
| 1107 | [] | |
| 1108 | ) { | |
| 1059 | 1109 | if (seenDescendants.has(descendantChangeId)) continue; |
| 1060 | 1110 | seenDescendants.add(descendantChangeId); |
| 1061 | 1111 | |
| 1062 | const matchedDependency = matchedDependencyByDescendant.get(descendantChangeId); | |
| 1112 | const matchedDependency = matchedDependencyByDescendant.get( | |
| 1113 | descendantChangeId, | |
| 1114 | ); | |
| 1063 | 1115 | if ( |
| 1064 | 1116 | matchedDependency == null || |
| 1065 | 1117 | tryMatchDescendant( |
| ... | ... | @@ -1092,7 +1144,7 @@ function progressAttachToScreen( |
| 1092 | 1144 | widget ??= startWidget({ |
| 1093 | 1145 | format: ({ now }) => progress.formatAnsi(now, root.active), |
| 1094 | 1146 | }) ?? null; |
| 1095 | if (!widget)return; | |
| 1147 | if (!widget) return; | |
| 1096 | 1148 | widget.fps = items.some((x) => x.showTotal !== false && x.total > 0) |
| 1097 | 1149 | ? 15 |
| 1098 | 1150 | : null; |
users/clover/work/home.nix-3| ... | ... | @@ -25,7 +25,4 @@ |
| 25 | 25 | typescript |
| 26 | 26 | zig |
| 27 | 27 | ]; |
| 28 | home.sessionPath = [ | |
| 29 | "$HOME/.opencode/bin" | |
| 30 | ]; | |
| 31 | 28 | } |
users/natalie/laptop/configuration.nix+2-2| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | # Custom configuration modules in "modules" are shared between users, |
| 8 | 8 | # and can be configured in this "shared" namespace |
| 9 | 9 | shared.darwin = { |
| 10 | # macAppStoreApps = [ "wireguard" ]; | |
| 10 | # macAppStoreApps = [ "wireguard" ]; | |
| 11 | 11 | }; |
| 12 | 12 | |
| 13 | 13 | system.defaults.NSGlobalDomain."com.apple.trackpad.scaling" = 1.0; |
| ... | ... | @@ -40,7 +40,7 @@ |
| 40 | 40 | casks = [ |
| 41 | 41 | "battle-net" |
| 42 | 42 | "stremio" |
| 43 | # "alt-tab" | |
| 43 | # "alt-tab" | |
| 44 | 44 | "legcord" |
| 45 | 45 | "zulip" |
| 46 | 46 | "zen" |