Compare commits

...

75 Commits

Author SHA1 Message Date
6ccfb5097c feat(roles/kodi): source jellyfin-media-player from qt-pinned namespace
Update kodi role to use pkgs.qt-pinned.jellyfin-media-player instead of
pkgs.jellyfin-media-player. This decouples jellyfin from the main nixpkgs
update cycle, avoiding massive qt5webengine rebuilds when updating other
packages.

The qt-pinned namespace was added in commit 03f1692.

Closes: x-xiiep

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 16:49:12 -08:00
1b585847ab Merge branch 'polecat/chrome/x-ymkgu@mlebby8e': update renovate schedules to Saturday afternoon
All checks were successful
CI / check (push) Successful in 16m9s
2026-02-08 14:58:17 -08:00
e7906331dc feat(renovate): update schedules to Saturday afternoon
- lockFileMaintenance: Saturday 2-4pm (was Monday 5am)
- nix-stable-ecosystem: Saturday 2-4pm
- nix-unstable-ecosystem: Saturday 2-4pm
- Add nixpkgs-qt rule: Saturday 4-6pm (staggered)

This allows CI builds to run overnight Saturday→Sunday, with human
review Saturday evening and builds complete by Sunday morning.

Closes: x-ymkgu
2026-02-08 14:58:05 -08:00
dc722843a9 Merge branch 'polecat/rust/x-lnr8g@mlebamik': add nixpkgs-qt input for qt5webengine
Some checks failed
CI / check (push) Has been cancelled
2026-02-08 14:57:19 -08:00
03f169284d feat(flake): add nixpkgs-qt input for qt5webengine packages
Add separate nixpkgs input for qt5webengine-dependent packages like
jellyfin-media-player. This input updates on a separate Renovate
schedule from main nixpkgs to avoid massive qt5webengine rebuilds
when updating other packages.

- Add nixpkgs-qt input pinned to nixos-25.11
- Create pkgs.qt-pinned overlay namespace

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 14:35:35 -08:00
8908500073 feat(home-kodi): enable kdeconnect for kodi user on boxy
All checks were successful
CI / check (push) Successful in 3m26s
Allows KDE Connect discovery and pairing to work when logged in as
the kodi user on the media center.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 13:43:17 -08:00
87f6d5c759 feat(deps): update beads to 0.49.1 with dolt server mode, claude-code to 2.1.30
All checks were successful
CI / check (push) Successful in 5m17s
beads:
- Pin to commit 93965b4a (last before Go 1.25.6 requirement)
- Build locally with corrected vendorHash (upstream default.nix is stale)
- Enables dolt server mode support (gt-1mf.3)

claude-code: 2.1.19 → 2.1.30

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 14:56:24 -08:00
a851c2551c fix(deps): update gastown patch and pin beads to Go 1.24 compatible version
All checks were successful
CI / check (push) Successful in 5m25s
- Update gastown-fix-agent-bead-address-title.patch line numbers (326→315)
  for current upstream gastown source
- Remove obsolete gastown patches (rig-prefix, copydir-symlinks) that are
  now handled upstream
- Pin beads to 55e733c (v0.47.2) which uses Go 1.24.0 - newer versions
  require Go 1.25.6 which isn't in nixpkgs-unstable yet
- Remove beads-search-query-optimization.patch as it targets newer code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 13:18:46 -08:00
mayor
6cf63e86c1 Merge branch 'polecat/rust/x-0cf@ml2ye219': fix doom-intermediates.drv CI failure
All checks were successful
CI / check (push) Successful in 7m37s
Updated nix-doom-emacs-unstraightened flake input to fix stale IFD derivation.

Closes: x-0cf, x-qwd7, hq-cv-mnzq4
2026-01-31 15:50:43 -08:00
c3ed6c0a26 fix(deps): update nix-doom-emacs-unstraightened to fix live-usb flake check
Updates nix-doom-emacs-unstraightened from Jan 25 to Jan 31 release,
which fixes the stale doom-intermediates.drv reference that was causing
nixosConfigurations.live-usb to fail flake check.

Closes: x-0cf

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 15:48:34 -08:00
mayor
53fa89b2e9 Merge branch 'polecat/rust/hq-0h1p9m@ml2ugjq1': fix gastown statusline patch
All checks were successful
CI / check (push) Successful in 5m19s
Regenerated patch with correct hunk headers against locked rev 177094a2.
Root cause was malformed patch format, not a flake.lock issue.

Closes: hq-0h1p9m, x-bwld
2026-01-31 14:01:06 -08:00
3acf9d2796 fix(gastown): regenerate statusline optimization patch with correct line numbers
The patch file had malformed hunk headers with incorrect line numbers
and counts, causing it to fail to apply against the locked gastown rev
(177094a2). This was NOT a flake.lock issue - gastown source was properly
locked.

Changes:
- Regenerated patch from scratch against locked gastown revision
- Re-enabled the patch in default.nix (was commented out with TODO)
- Updated comment to accurately describe the optimization

The optimization skips expensive beads queries for detached tmux sessions
and caches status line output with a 10-second TTL, reducing Dolt CPU
usage from ~70% to ~20%.

Closes: hq-0h1p9m

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 13:59:11 -08:00
123e7d3b3a fix(gastown): repair malformed patch files for nixos-rebuild
Some checks failed
CI / check (push) Failing after 17m48s
- Remove 'index 0000000..1111111' lines that made patches appear as new files
- Fix hunk line counts in several patches
- Add missing leading spaces to blank context lines
- Temporarily disable statusline optimization patch (needs regenerating)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 13:20:12 -08:00
nixos_configs/crew/harry
56097aefa4 refactor(development): move gastown patches to separate files
All checks were successful
CI / check (push) Successful in 3m37s
Replace inline postPatch substituteInPlace calls with proper unified
diff patch files, following the pattern established by beads.

This improves maintainability:
- Each patch is in its own file with clear naming
- Patches use proper unified diff format
- Easier to review, update, and track individual fixes
- Default.nix is cleaner (237 lines of substituteInPlace -> 15 lines)

Patches included:
- gastown-fix-validate-recipient.patch
- gastown-fix-agent-bead-address-title.patch
- gastown-fix-agent-bead-rig-prefix.patch
- gastown-fix-role-home-paths.patch
- gastown-fix-town-root-detection.patch
- gastown-fix-copydir-symlinks.patch
- gastown-statusline-optimization.patch

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 09:06:05 -08:00
21a8b5c5d9 Fix bd SearchIssues inefficient WHERE IN query pattern for Dolt
All checks were successful
CI / check (push) Successful in 3m25s
The Dolt backend's SearchIssues was using a two-phase query:
1. SELECT id FROM issues WHERE ... -> collect all IDs
2. SELECT * FROM issues WHERE id IN (id1, id2, ... id8000+)

With 8000+ issues, this second query with 8000+ placeholders hammers
Dolt CPU at 100%+. The fix changes SearchIssues to select all columns
directly in the first query and scan results inline.

See: hq-ihwsj

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:29:46 -08:00
8f8582b0f3 feat(gastown): add statusline cache writes for CPU optimization
All checks were successful
CI / check (push) Successful in 3m24s
Complete the statusline optimization by adding cache writes to all
output functions. The existing patch added cache functions and cache
reads, but never wrote to the cache.

Changes:
- Add early-return for detached sessions (return static "○ |")
- Add cache read check for attached sessions
- Add setStatusLineCache() calls in all 5 output functions:
  - runWorkerStatusLine
  - runMayorStatusLine
  - runDeaconStatusLine
  - runWitnessStatusLine
  - runRefineryStatusLine

This should reduce Dolt CPU from ~70% to ~20% when agents are idle,
as tmux status lines will use cached results instead of spawning
beads queries every 5 seconds.

Testing: Run `nix switch` then monitor Dolt CPU with `top`

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 12:20:39 -08:00
94fb5a3e64 Fix gastown mail routing for rig-specific agent beads
All checks were successful
CI / check (push) Successful in 3m18s
- Add title-based lookup for hq- prefixed beads (uses title as address if contains "/")
- Add rig-specific prefix handling to parse IDs like j-java-crew-americano → java/crew/americano
- Handles crew, polecat, witness, refinery role patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 17:20:03 -08:00
7df68ba8c8 Add gastown postPatch bug fixes from jt flake
All checks were successful
CI / check (push) Successful in 5m17s
- Fix mail router normalization in validateRecipient
- Fix agentBeadToAddress to use title field for hq- prefixed beads
- Fix crew/polecat home paths (remove incorrect /rig suffix)
- Fix town root detection (RoleUnknown instead of RoleMayor)
- Fix copyDir symlink handling
- Pin to gastown commit 177094a matching jt flake

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:14:52 -08:00
2799632308 Add gastown postPatch bug fixes from jt flake
- Fix mail router normalization in validateRecipient
- Fix agentBeadToAddress to use title field for hq- prefixed beads
- Fix crew/polecat home paths (remove incorrect /rig suffix)
- Fix town root detection (RoleUnknown instead of RoleMayor)
- Fix copyDir symlink handling
- Pin to gastown commit 177094a matching jt flake

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:14:06 -08:00
346c031278 Match darwin configuration name to actual hostname
All checks were successful
CI / check (push) Successful in 3m33s
Use uppercase BLKFV4YF49KT7 so darwin-rebuild --flake ./ works without
explicitly specifying the configuration name.
2026-01-27 11:32:53 -08:00
188d2befb0 Fix sketchybar disk usage showing incorrect percentage
Monitor /System/Volumes/Data instead of / since root is a read-only
APFS snapshot with minimal usage. Also fix inverted formula that was
calculating 100-used instead of just using the capacity value directly.
2026-01-27 11:32:48 -08:00
8e8b5f4304 chore(machines): remove tart-agent-sandbox config
All checks were successful
CI / check (push) Successful in 5m44s
Pivoted to Docker container approach for agent sandboxing instead of
Tart VMs due to networking issues with Cloudflare WARP.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 10:54:33 -08:00
4098ee3987 revert tart agent sandbox sway idea
All checks were successful
CI / check (push) Successful in 3m34s
2026-01-27 09:58:50 -08:00
e1e37da7c2 feat(tart-agent-sandbox): add sway desktop with auto-login
All checks were successful
CI / check (push) Successful in 3m41s
- Enable desktop role with wayland/sway
- Use greetd for passwordless auto-login to sway
- Add video/input groups to agent user

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 09:48:03 -08:00
a46d11a770 feat(machines): add tart-agent-sandbox VM config
All checks were successful
CI / check (push) Successful in 4m26s
NixOS configuration for running LLM agents in isolated Tart VMs on
Apple Silicon. Includes:
- Headless server setup with SSH access
- Agent user with passwordless sudo
- Docker support
- Dev tools for cloning large repos
- Git config optimized for large repositories

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 09:24:47 -08:00
harry
8553b9826e feat(roles): add rclone-mount role for WebDAV mounts
Some checks failed
CI / check (push) Failing after 12m14s
Add a new system-level role for mounting WebDAV filesystems via rclone.
Includes rclone-torbox-setup helper script that uses rbw to bootstrap
credentials from Bitwarden.

Key features:
- Configurable WebDAV URL, username, mount point
- VFS cache mode and buffer size tuning for media streaming
- RequiresMountsFor option for ZFS pool dependencies
- Obscured password storage via environment file

Enable on john-endesktop for TorBox WebDAV access by rdt-client and
Jellyfin. Mount waits for /media ZFS pool before starting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 19:45:00 -08:00
a0c081e12e fix(aerospace): disable ctrl shortcuts
All checks were successful
CI / check (push) Successful in 5m43s
2026-01-26 17:22:33 -08:00
d92e4b3ddf feat(development): add perles TUI for beads 2026-01-26 17:22:28 -08:00
70b40966be chore(claude-code): update to 2.1.19 2026-01-26 17:10:45 -08:00
475a633ab7 feat(base): add watch to base role packages 2026-01-26 17:10:42 -08:00
a39416c9db chore: switch beads and gastown to upstream GitHub repos 2026-01-26 17:10:37 -08:00
63c3f4e84d fix(sketchybar): show disk used% instead of free%
All checks were successful
CI / check (push) Successful in 3m27s
Inverts the df output to show percentage used, matching the other
resource monitors (CPU, memory).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 16:20:31 -08:00
baf64f7f4a fix(emacs): make rbw password helper graceful when rbw unavailable
Add optional no-error parameter to my/get-rbw-password that returns nil
instead of signaling an error when rbw isn't installed or the entry is
missing. Use this for gptel API key so config loads without errors in
environments without rbw configured.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 14:20:33 -08:00
mayor
f0b6ede7ed add dolt to development role
All checks were successful
CI / check (push) Successful in 5m50s
Required for beads dolt backend migration in Gas Town.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 11:55:33 -08:00
d0cb16391f update gt and/or beads 2026-01-26 11:51:06 -08:00
d872293f19 fix(gastown): add ldflags for BuiltProperly check
All checks were successful
CI / check (push) Successful in 3m57s
The gastown build now requires BuiltProperly=1 to be set via ldflags,
otherwise gt errors with "This binary was built with 'go build' directly".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 08:20:20 -08:00
07182cfdcf chore(deps): lock file maintenance
All checks were successful
CI / check (push) Successful in 3m48s
CI / check (pull_request) Successful in 3m25s
2026-01-26 08:04:22 +00:00
hermione
65e91c20f7 fix(emacs): set org-caldav delete to never to prevent mass deletion
All checks were successful
CI / check (push) Successful in 4m5s
Sync state confusion was causing org-caldav to want to delete all
calendar entries. Setting to 'never' prevents accidental data loss.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:35:24 -08:00
hermione
01e376eac4 fix(emacs): correct org-caldav-todo-percent-states format
All checks were successful
CI / check (push) Successful in 3m35s
Format is (PERCENT "KEYWORD") not ("KEYWORD" . PERCENT).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:27:17 -08:00
hermione
9c5be2e27a fix(emacs): add KILL state to org-caldav-todo-percent-states
Some checks failed
CI / check (push) Has been cancelled
org-caldav needs percent mappings for all todo states. Added mappings
for TODO, IN-PROGRESS, WAIT, DONE, and KILL to prevent sync errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:26:08 -08:00
hermione
d9ffb14db5 refactor(emacs): remove DEADLINE logic, keep only CALDAV_UNTIL property
Some checks failed
CI / check (push) Has been cancelled
DEADLINE doesn't limit recurring event display - agenda skip function
handles that now. Simplified advice to only store CALDAV_UNTIL property.

Also made debug logging unconditional to diagnose why some events
don't get the property.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:23:55 -08:00
hermione
07ea05afab feat(emacs): filter recurring events past CALDAV_UNTIL in agenda
Some checks failed
CI / check (push) Has been cancelled
DEADLINE doesn't actually limit recurring event display in org-agenda.
Instead, use org-agenda-skip-function-global to filter entries where
today's date is past the CALDAV_UNTIL property.

The skip function:
- Checks for CALDAV_UNTIL property (set by caldav sync advice)
- Parses YYYYMMDD format
- Skips entry if today > UNTIL date

This properly hides expired recurring events from the agenda.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:20:44 -08:00
hermione
4f5108c9d9 fix(emacs): allow org-caldav export with broken links
All checks were successful
CI / check (push) Successful in 3m55s
mu4e message links in todo.org can't be resolved during iCalendar export,
causing sync to abort. Setting org-export-with-broken-links to 'mark'
allows export to continue (broken links get marked in output).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 13:16:53 -08:00
9243341ed7 fix comment
All checks were successful
CI / check (push) Successful in 4m2s
2026-01-25 12:58:10 -08:00
hermione
b729ee8c7a fix(emacs): use assoc instead of assq for UNTIL lookup in org-caldav advice
All checks were successful
CI / check (push) Successful in 3m48s
assq uses eq for key comparison, which can fail if the key symbols aren't
identical objects. assoc uses equal, which is what org-caldav itself uses
for rrule-props lookups (e.g., INTERVAL, FREQ).

This fixes DEADLINEs not being added to recurring events with UNTIL dates.

Also adds debug logging to help diagnose any remaining issues - will be
removed once verified working.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 12:46:11 -08:00
nixos_configs/crew/hermione
ebc28cebd4 fix(emacs): improve rbw password error handling
Show clear error message when rbw entry not found instead of
embedding error text in URLs/credentials.
2026-01-25 12:46:11 -08:00
c82358d586 gastown and beads: switch to git+https
All checks were successful
CI / check (push) Successful in 6m19s
2026-01-25 12:07:59 -08:00
74388e8c24 [remote-build] use full dns names 2026-01-25 12:07:39 -08:00
nixos_configs/crew/hermione
a98ccddab1 feat(emacs): add org-caldav UNTIL advice for recurring event end dates
Some checks failed
CI / check (push) Failing after 2m34s
Implements advice around org-caldav-insert-org-event-or-todo that:
- Extracts UNTIL from rrule-props
- Adds DEADLINE without repeater (Org 9.7+ treats as recurrence end)
- Stores :CALDAV_UNTIL: property for reference

Also fixes sync command to work before org is opened by requiring
org explicitly in the sync wrapper.

Closes: x-uv5f.1
2026-01-25 09:51:48 -08:00
18570628a5 update beads and gastown
Some checks failed
CI / check (push) Failing after 4m43s
2026-01-24 18:06:20 -08:00
hermione
0c484b6601 fix(emacs): embed credentials in URL for org-caldav auth
Some checks failed
CI / check (push) Failing after 4m14s
url-http-basic-auth-storage approach wasn't working.
Now dynamically sets org-caldav-url with user:pass embedded.
2026-01-24 17:52:02 -08:00
hermione
4853a18474 fix(emacs): correct url-http-basic-auth-storage format
Some checks failed
CI / check (push) Has been cancelled
Auth storage needs base64-encoded 'user:pass' string, not raw password.
2026-01-24 17:47:28 -08:00
hermione
8b8453a37a fix(emacs): move org-caldav sync function before use-package
Some checks failed
CI / check (push) Has been cancelled
Function must be defined before keybinding to avoid commandp error.
Added (require 'org-caldav) inside function for autoloading.
2026-01-24 17:46:02 -08:00
hermione
2b6e289b9a fix(emacs): limit org-caldav to 30 days of past events
Some checks failed
CI / check (push) Has been cancelled
Prevents downloading years of historical calendar entries.
2026-01-24 17:39:48 -08:00
hermione
70d364544f fix(emacs): change org-caldav keybinding to avoid conflict
Some checks failed
CI / check (push) Has been cancelled
Changed from SPC o C to SPC o a s (open -> agenda/calendar -> sync)
to avoid conflict with Claude Code IDE (SPC o c)
2026-01-24 17:34:07 -08:00
hermione
1ffa8524f0 fix(emacs): use rbw for org-caldav auth instead of GPG
Some checks failed
CI / check (push) Has been cancelled
GPG isn't installed, so .authinfo.gpg approach doesn't work.
Added wrapper function my/org-caldav-sync-with-rbw that fetches
credentials from rbw before calling org-caldav-sync.

Setup: rbw add nextcloud-caldav (app password as secret)
2026-01-24 17:27:02 -08:00
hermione
be3c27e868 update gastown
Some checks failed
CI / check (push) Has been cancelled
Executed-By: nixos_configs/crew/hermione
Rig: nixos_configs
Role: crew
2026-01-24 17:25:05 -08:00
c2d286087f fix(home-manager): ensure claude/beads plugin files are writable
Files copied from the nix store inherit read-only permissions, causing
subsequent home-manager activations to fail with "Permission denied".

Add rm -f before copy and chmod u+w after copy for all plugin files:
- humanlayer commands and agents
- local commands and skills
- micro-skills
- beads formulas

Executed-By: nixos_configs/crew/hermione
Rig: nixos_configs
Role: crew
2026-01-24 17:25:05 -08:00
hermione
1172818062 feat(emacs): add org-caldav integration for Nextcloud calendar sync
Some checks failed
CI / check (push) Failing after 1m59s
- Enable org-caldav package in packages.el
- Configure base org-caldav settings (URL, timezone, sync behavior)
- Add Personal calendar two-way sync (~/org/personal-calendar.org)
- Add Tasks calendar one-way sync from todo.org
- Add keybinding SPC o C for manual sync
- Document setup requirements in config comments

Note: Conflict resolution is 'Org always wins' (org-caldav limitation).
User needs to create Nextcloud app password and ~/.authinfo.gpg.

Refs: x-5tb, x-5tb.1, x-5tb.2, x-5tb.3
2026-01-24 17:18:45 -08:00
mayor
9f63e1430c fix(beads): set issue prefix to x-
Some checks failed
CI / check (push) Failing after 2m5s
Ensures beads created in this repo use x- prefix to match routes.jsonl
2026-01-24 16:37:04 -08:00
b14ef1f62a update gastown
Some checks failed
CI / check (push) Failing after 4m2s
2026-01-23 17:10:29 -08:00
87719fa9e6 update gastown
Some checks failed
CI / check (push) Has been cancelled
2026-01-23 17:10:06 -08:00
933612da4c update beads and gastown 2026-01-23 17:10:06 -08:00
shiny
d2c7599267 fix(beads): set routing mode to explicit instead of auto
Some checks failed
CI / check (push) Failing after 2m34s
The routing.mode was defaulting to 'auto', which uses git remote URL
to detect user role. Non-SSH URLs can cause mail and issues to be
routed to ~/.beads-planning instead of the local .beads directory.

Setting routing.mode to 'explicit' disables auto-routing entirely,
keeping all issues in the expected local directory.

Fixes: x-kho

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 18:15:21 -08:00
chrome
3d16824eac chore: add Gas Town directories to .gitignore
Some checks failed
CI / check (push) Has been cancelled
Added by gt polecat setup.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:56:37 -08:00
2cdc15163c fix(flake): apply claude-code overlay to all platforms
Some checks failed
CI / check (push) Failing after 2m17s
The custom claude-code overlay (for GCS-based builds) was only being
applied to darwinModules. Extract to shared customUnstableOverlays and
apply to nixosModules and nixosModulesUnstable as well.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:35:14 -08:00
a77b1230fe update beads
Some checks failed
CI / check (push) Failing after 4m50s
2026-01-21 15:15:39 -08:00
623a387127 flake: update gastown to a069ff34 (rebased on upstream)
Some checks failed
CI / check (push) Failing after 4m9s
Rebased local patches onto 45 new upstream commits.

Executed-By: mayor
Role: mayor
2026-01-20 22:19:45 -08:00
737f2b09e4 chore(flake): update gastown to gitea fork with timeout fix
Some checks failed
CI / check (push) Failing after 4m16s
Point gastown input to local Gitea fork which includes:
- Increased ClaudeStartTimeout from 60s to 120s
- Fixes intermittent refinery/polecat startup timeouts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 10:34:01 -08:00
cddc9de14a chore(deps): lock file maintenance
All checks were successful
CI / check (push) Successful in 6m18s
2026-01-19 20:15:34 -08:00
53e3bbe78f fix(claude-code): preserve bun appended bundle during NixOS build
All checks were successful
CI / check (push) Successful in 3m32s
Bun standalone executables store their JavaScript code by appending it
after the ELF sections, marked with "---- Bun! ----". The standard Nix
build process was corrupting this:

- autoPatchelfHook rewrites the entire ELF, losing appended data
- strip removes data after ELF sections
- patchelf shrink-rpath also rewrites the ELF

Fix by:
- Using dontStrip and dontPatchELF to skip automatic fixup
- Manually running patchelf --set-interpreter which modifies in-place
  without rewriting the entire file structure

This restores the binary from 99MB (bare bun runtime) to 220MB (full
claude-code application).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Executed-By: mayor
Role: mayor
2026-01-19 10:39:58 -08:00
c258eafe34 fix: apply claude-code overlay to NixOS modules
All checks were successful
CI / check (push) Successful in 4m11s
The custom claude-code package (from GCS) was only being applied to
darwinModules, causing NixOS systems to use the older version from
nixpkgs-unstable instead of the updated version from packages/claude-code.

Extract claudeCodeOverlay as a shared definition and apply it to all
module sets: nixosModules, nixosModulesUnstable, and darwinModules.

Executed-By: mayor
Role: mayor
2026-01-19 10:28:11 -08:00
03d0b76f97 feat: switch gastown input to local Gitea fork
All checks were successful
CI / check (push) Successful in 5m9s
Fork includes mayor startup protocol fix for escalation checking.
2026-01-19 08:58:23 -08:00
b5f7233214 ci: Use semver tag for gitea-actions/nix-setup
All checks were successful
CI / check (push) Successful in 3m21s
Switch from @main to @v1 for stability. Major version tag auto-updates
to latest v1.x.x while avoiding breaking changes.

Closes: nix-7zf
2026-01-18 17:54:24 -08:00
1203662237 feat(home/development): add sqlite3 package
Some checks failed
CI / check (push) Has been cancelled
2026-01-18 08:13:16 -08:00
31 changed files with 987 additions and 108 deletions

View File

@@ -6,7 +6,7 @@
# Issue prefix for this repository (used by bd init)
# If not set, bd init will auto-detect from directory name
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
# issue-prefix: ""
issue-prefix: "x"
# Use no-db mode: load from JSONL, no SQLite, write back after each command
# When true, bd will use .beads/issues.jsonl as the source of truth
@@ -59,4 +59,6 @@ sync-branch: "beads-sync"
# - linear.url
# - linear.api-key
# - github.org
# - github.repo
# - github.repo
routing.mode: "explicit"

View File

@@ -12,7 +12,7 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: https://git.johnogle.info/johno/gitea-actions/nix-setup@main
- uses: https://git.johnogle.info/johno/gitea-actions/nix-setup@v1
- name: Check flake
run: nix flake check

5
.gitignore vendored
View File

@@ -1,3 +1,8 @@
result
thoughts
.beads
# Gas Town (added by gt)
.runtime/
.claude/
.logs/

121
flake.lock generated
View File

@@ -8,27 +8,28 @@
]
},
"locked": {
"lastModified": 1768509852,
"narHash": "sha256-4oZXrqBjK9V8qKHoxlfil20qcJdOU8HXJA4627nX1nQ=",
"lastModified": 1769840331,
"narHash": "sha256-Yp0K4JoXX8EcHp1juH4OZ7dcCmkopDu4VvAgZEOxgL8=",
"owner": "steveyegge",
"repo": "beads",
"rev": "d3db8253ff09e0f139ffbb6f839afe13acbf7bdb",
"rev": "93965b4abeed920a4701e03571d1b6bb75810722",
"type": "github"
},
"original": {
"owner": "steveyegge",
"repo": "beads",
"rev": "93965b4abeed920a4701e03571d1b6bb75810722",
"type": "github"
}
},
"doomemacs": {
"flake": false,
"locked": {
"lastModified": 1767773143,
"narHash": "sha256-QL/t9v2kFNxBDyNJb/s411o3mxujan+QX5IZglTdpTk=",
"lastModified": 1768984347,
"narHash": "sha256-VvC4rgAAaFnYLCdcUoz7dTE3kuBNuHIc+GlXOrPCxpg=",
"owner": "doomemacs",
"repo": "doomemacs",
"rev": "3e15fb36d7f94f0a218bda977be4d3f5da983a71",
"rev": "57818a6da90fbef39ff80d62fab2cd319496c3b9",
"type": "github"
},
"original": {
@@ -47,11 +48,11 @@
]
},
"locked": {
"lastModified": 1768011937,
"narHash": "sha256-SnU2XTo34vwVaijs+4VwcXTNwMWO4nwzzs08N39UagA=",
"lastModified": 1769848312,
"narHash": "sha256-ggBocPd1L4l5MFNV0Fw9aSGZZO4aGzCfgh4e6hQ77RE=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "79abf71d9897cf3b5189f7175cda1b1102abc65c",
"rev": "be0b4f4f28f69be61e9174807250e3235ee11d50",
"type": "github"
},
"original": {
@@ -81,11 +82,11 @@
"gastown": {
"flake": false,
"locked": {
"lastModified": 1768682809,
"narHash": "sha256-PCP5PQasLqL5/OVNw6LsjiFfIU4RNniicTUcVq2ggHg=",
"lastModified": 1770098007,
"narHash": "sha256-CFlN57BXlR5FobTChdE2GgdIGx4xJcFFCk1E5Q98cSQ=",
"owner": "steveyegge",
"repo": "gastown",
"rev": "9cd2696abe68ac0defc612ace5028d327d4f207d",
"rev": "13461161063bf7b2365fe5fd4df88e32c3ba2a28",
"type": "github"
},
"original": {
@@ -101,11 +102,11 @@
]
},
"locked": {
"lastModified": 1761423376,
"narHash": "sha256-pMy3cnUFfue4vz/y0jx71BfcPGxZf+hk/DtnzWvfU0c=",
"lastModified": 1768846578,
"narHash": "sha256-82f/+e8HAwmBukiLlr7I3HYvM/2GCd5SOc+BC+qzsOQ=",
"ref": "refs/heads/main",
"rev": "a1f695665771841a988afc965526cbf99160cd77",
"revCount": 11,
"rev": "c11ff9d3c67372a843a0fa6bf23132e986bd6955",
"revCount": 14,
"type": "git",
"url": "https://git.johnogle.info/johno/google-cookie-retrieval.git"
},
@@ -121,11 +122,11 @@
]
},
"locked": {
"lastModified": 1767514898,
"narHash": "sha256-ONYqnKrPzfKEEPChoJ9qPcfvBqW9ZgieDKD7UezWPg4=",
"lastModified": 1768949235,
"narHash": "sha256-TtjKgXyg1lMfh374w5uxutd6Vx2P/hU81aEhTxrO2cg=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "7a06e8a2f844e128d3b210a000a62716b6040b7f",
"rev": "75ed713570ca17427119e7e204ab3590cc3bf2a5",
"type": "github"
},
"original": {
@@ -142,11 +143,11 @@
]
},
"locked": {
"lastModified": 1767556355,
"narHash": "sha256-RDTUBDQBi9D4eD9iJQWtUDN/13MDLX+KmE+TwwNUp2s=",
"lastModified": 1769397130,
"narHash": "sha256-TTM4KV9IHwa181X7afBRbhLJIrgynpDjAXJFMUOWfyU=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "f894bc4ffde179d178d8deb374fcf9855d1a82b7",
"rev": "c37679d37bdbecf11bbe3c5eb238d89ca4f60641",
"type": "github"
},
"original": {
@@ -164,11 +165,11 @@
]
},
"locked": {
"lastModified": 1767082077,
"narHash": "sha256-2tL1mRb9uFJThUNfuDm/ehrnPvImL/QDtCxfn71IEz4=",
"lastModified": 1769273817,
"narHash": "sha256-+iyLihi/ynJokMgJZMRXuMuI6DPGUQRajz5ztNCHgnI=",
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"rev": "efd4b22e6fdc6d7fb4e186ae333a4b74e03da440",
"rev": "98f988ad46e31f9956c5f6874dfb3580a7ff3969",
"type": "github"
},
"original": {
@@ -184,11 +185,11 @@
]
},
"locked": {
"lastModified": 1765066094,
"narHash": "sha256-0YSU35gfRFJzx/lTGgOt6ubP8K6LeW0vaywzNNqxkl4=",
"lastModified": 1767634391,
"narHash": "sha256-owcSz2ICqTSvhBbhPP+1eWzi88e54rRZtfCNE5E/wwg=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "688427b1aab9afb478ca07989dc754fa543e03d5",
"rev": "08585aacc3d6d6c280a02da195fdbd4b9cf083c2",
"type": "github"
},
"original": {
@@ -206,11 +207,11 @@
"systems": "systems_2"
},
"locked": {
"lastModified": 1768034604,
"narHash": "sha256-62pIZMvGHhYJmMiiBsxHqZt/dFyENPcFHlJq5NJF3Sw=",
"lastModified": 1769849328,
"narHash": "sha256-BjH1Ge6O8ObN6Z97un2U87pl4POO99Q8RSsgIuTZq8Q=",
"owner": "marienz",
"repo": "nix-doom-emacs-unstraightened",
"rev": "9b3b8044fe4ccdcbb2d6f733d7dbe4d5feea18bc",
"rev": "fc1d7190c49558cdc6af20d7657075943a500a93",
"type": "github"
},
"original": {
@@ -243,11 +244,27 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1767480499,
"narHash": "sha256-8IQQUorUGiSmFaPnLSo2+T+rjHtiNWc+OAzeHck7N48=",
"lastModified": 1769089682,
"narHash": "sha256-9yA/LIuAVQq0lXelrZPjLuLVuZdm03p8tfmHhnDIkms=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "30a3c519afcf3f99e2c6df3b359aec5692054d92",
"rev": "078d69f03934859a181e81ba987c2bb033eebfc5",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-qt": {
"locked": {
"lastModified": 1770464364,
"narHash": "sha256-z5NJPSBwsLf/OfD8WTmh79tlSU8XgIbwmk6qB1/TFzY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "23d72dabcb3b12469f57b37170fcbc1789bd7457",
"type": "github"
},
"original": {
@@ -259,11 +276,11 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1767379071,
"narHash": "sha256-EgE0pxsrW9jp9YFMkHL9JMXxcqi/OoumPJYwf+Okucw=",
"lastModified": 1769170682,
"narHash": "sha256-oMmN1lVQU0F0W2k6OI3bgdzp2YOHWYUAw79qzDSjenU=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "fb7944c166a3b630f177938e478f0378e64ce108",
"rev": "c5296fdd05cfa2c187990dd909864da9658df755",
"type": "github"
},
"original": {
@@ -273,6 +290,22 @@
"type": "github"
}
},
"perles": {
"flake": false,
"locked": {
"lastModified": 1769460725,
"narHash": "sha256-zM2jw+emxe8+mNyR1ebMWkQiEx8uSmhoqqI0IxXLDgs=",
"owner": "zjrosen",
"repo": "perles",
"rev": "57b20413eea461452b59e13f5a4a367953b1f768",
"type": "github"
},
"original": {
"owner": "zjrosen",
"repo": "perles",
"type": "github"
}
},
"plasma-manager": {
"inputs": {
"home-manager": [
@@ -283,11 +316,11 @@
]
},
"locked": {
"lastModified": 1763909441,
"narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=",
"lastModified": 1767662275,
"narHash": "sha256-d5Q1GmQ+sW1Bt8cgDE0vOihzLaswsm8cSdg8124EqXE=",
"owner": "nix-community",
"repo": "plasma-manager",
"rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4",
"rev": "51816be33a1ff0d4b22427de83222d5bfa96d30e",
"type": "github"
},
"original": {
@@ -306,11 +339,11 @@
]
},
"locked": {
"lastModified": 1763909441,
"narHash": "sha256-56LwV51TX/FhgX+5LCG6akQ5KrOWuKgcJa+eUsRMxsc=",
"lastModified": 1767662275,
"narHash": "sha256-d5Q1GmQ+sW1Bt8cgDE0vOihzLaswsm8cSdg8124EqXE=",
"owner": "nix-community",
"repo": "plasma-manager",
"rev": "b24ed4b272256dfc1cc2291f89a9821d5f9e14b4",
"rev": "51816be33a1ff0d4b22427de83222d5bfa96d30e",
"type": "github"
},
"original": {
@@ -330,7 +363,9 @@
"nix-darwin": "nix-darwin",
"nix-doom-emacs-unstraightened": "nix-doom-emacs-unstraightened",
"nixpkgs": "nixpkgs",
"nixpkgs-qt": "nixpkgs-qt",
"nixpkgs-unstable": "nixpkgs-unstable",
"perles": "perles",
"plasma-manager": "plasma-manager",
"plasma-manager-unstable": "plasma-manager-unstable"
}

View File

@@ -4,6 +4,9 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
# Separate nixpkgs for qt5webengine-dependent packages (jellyfin-media-player, etc.)
# Updates on separate Renovate schedule to avoid massive qt rebuilds
nixpkgs-qt.url = "github:nixos/nixpkgs/nixos-25.11";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-25.11";
@@ -43,7 +46,9 @@
};
beads = {
url = "github:steveyegge/beads";
# v0.49.1 has dolt server mode support (gt-1mf.3)
# Pinned to 259ddd92 - uses Go 1.24 compatible with nixpkgs
url = "github:steveyegge/beads/93965b4abeed920a4701e03571d1b6bb75810722";
inputs.nixpkgs.follows = "nixpkgs-unstable";
};
@@ -52,6 +57,11 @@
flake = false; # No flake.nix upstream yet
};
perles = {
url = "github:zjrosen/perles";
flake = false; # No flake.nix upstream yet
};
nix-doom-emacs-unstraightened = {
url = "github:marienz/nix-doom-emacs-unstraightened";
# Don't follow nixpkgs to avoid rebuild issues with emacs-overlay
@@ -69,6 +79,11 @@
config.allowUnfree = true;
overlays = unstableOverlays;
};
# Separate nixpkgs for qt5webengine-heavy packages to avoid rebuild churn
qt-pinned = import inputs.nixpkgs-qt {
system = prev.stdenv.hostPlatform.system;
config.allowUnfree = true;
};
custom = prev.callPackage ./packages {};
# Compatibility: bitwarden renamed to bitwarden-desktop in unstable
bitwarden-desktop = prev.bitwarden-desktop or prev.bitwarden;
@@ -89,11 +104,20 @@
};
# Shared unstable overlays for custom package builds
customUnstableOverlays = [
# Override claude-code in unstable to use our custom GCS-based build
# (needed for corporate networks that block npm registry)
(ufinal: uprev: {
claude-code = uprev.callPackage ./packages/claude-code {};
})
];
nixosModules = [
./roles
inputs.home-manager.nixosModules.home-manager
{
nixpkgs.overlays = [ (mkBaseOverlay {}) ];
nixpkgs.overlays = [ (mkBaseOverlay { unstableOverlays = customUnstableOverlays; }) ];
}
(mkHomeManagerConfig {
sharedModules = [ inputs.plasma-manager.homeModules.plasma-manager ];
@@ -106,7 +130,7 @@
inputs.home-manager-unstable.nixosModules.home-manager
inputs.jovian.nixosModules.jovian
{
nixpkgs.overlays = [ (mkBaseOverlay {}) ];
nixpkgs.overlays = [ (mkBaseOverlay { unstableOverlays = customUnstableOverlays; }) ];
}
(mkHomeManagerConfig {
sharedModules = [ inputs.plasma-manager-unstable.homeModules.plasma-manager ];
@@ -117,17 +141,7 @@
./roles/darwin.nix
inputs.home-manager.darwinModules.home-manager
{
nixpkgs.overlays = [
(mkBaseOverlay {
# Override claude-code in unstable to use our custom GCS-based build
# (needed for corporate networks that block npm registry)
unstableOverlays = [
(ufinal: uprev: {
claude-code = uprev.callPackage ./packages/claude-code {};
})
];
})
];
nixpkgs.overlays = [ (mkBaseOverlay { unstableOverlays = customUnstableOverlays; }) ];
}
(mkHomeManagerConfig { sharedModules = []; })
];
@@ -211,7 +225,7 @@
};
# Darwin/macOS configurations
darwinConfigurations."blkfv4yf49kt7" = inputs.nix-darwin.lib.darwinSystem rec {
darwinConfigurations."BLKFV4YF49KT7" = inputs.nix-darwin.lib.darwinSystem rec {
system = "aarch64-darwin";
modules = darwinModules ++ [
./machines/johno-macbookpro/configuration.nix

View File

@@ -107,7 +107,7 @@
aerospace = {
enable = true;
leader = "cmd";
ctrlShortcuts.enable = true;
ctrlShortcuts.enable = false;
sketchybar.enable = true;
# Optional: Add per-machine userSettings overrides
# userSettings = {

View File

@@ -12,6 +12,7 @@
home.roles = {
base.enable = true;
plasma-manager-kodi.enable = true;
kdeconnect.enable = true;
};
home.packages = with pkgs; [

View File

@@ -632,7 +632,9 @@ in
text = ''
#!/bin/bash
DISK_USAGE=$(df -H / | grep -v Filesystem | awk '{print $5}')
# Monitor /System/Volumes/Data which contains user data on APFS
# The root / is a read-only snapshot with minimal usage
DISK_USAGE=$(df -H /System/Volumes/Data | grep -v Filesystem | awk '{print $5}')
${pkgs.sketchybar}/bin/sketchybar --set $NAME label="$DISK_USAGE"
'';

View File

@@ -22,6 +22,7 @@ in
shellcheck
tmux
tree
watch
];
# Automatic garbage collection for user profile (home-manager generations).

View File

@@ -0,0 +1,44 @@
diff --git a/internal/storage/dolt/queries.go b/internal/storage/dolt/queries.go
index 7d8214ee..8acdaae2 100644
--- a/internal/storage/dolt/queries.go
+++ b/internal/storage/dolt/queries.go
@@ -212,8 +212,21 @@ func (s *DoltStore) SearchIssues(ctx context.Context, query string, filter types
}
// nolint:gosec // G201: whereSQL contains column comparisons with ?, limitSQL is a safe integer
+ // Performance fix: SELECT all columns directly instead of id-only + WHERE IN (all_ids)
+ // See: hq-ihwsj - bd list uses inefficient WHERE IN (all_ids) query pattern
querySQL := fmt.Sprintf(`
- SELECT id FROM issues
+ SELECT id, content_hash, title, description, design, acceptance_criteria, notes,
+ status, priority, issue_type, assignee, estimated_minutes,
+ created_at, created_by, owner, updated_at, closed_at, external_ref,
+ compaction_level, compacted_at, compacted_at_commit, original_size, source_repo, close_reason,
+ deleted_at, deleted_by, delete_reason, original_type,
+ sender, ephemeral, pinned, is_template, crystallizes,
+ await_type, await_id, timeout_ns, waiters,
+ hook_bead, role_bead, agent_state, last_activity, role_type, rig, mol_type,
+ event_kind, actor, target, payload,
+ due_at, defer_until,
+ quality_score, work_type, source_system
+ FROM issues
%s
ORDER BY priority ASC, created_at DESC
%s
@@ -225,7 +238,15 @@ func (s *DoltStore) SearchIssues(ctx context.Context, query string, filter types
}
defer rows.Close()
- return s.scanIssueIDs(ctx, rows)
+ var issues []*types.Issue
+ for rows.Next() {
+ issue, err := scanIssueRow(rows)
+ if err != nil {
+ return nil, err
+ }
+ issues = append(issues, issue)
+ }
+ return issues, rows.Err()
}
// GetReadyWork returns issues that are ready to work on (not blocked)

View File

@@ -5,21 +5,62 @@ with lib;
let
cfg = config.home.roles.development;
# FIXME: Temporary override for upstream beads vendorHash mismatch
# Remove after upstream fix: https://github.com/steveyegge/beads/issues/XXX
beadsPackage = globalInputs.beads.packages.${system}.default.overrideAttrs (old: {
vendorHash = "sha256-YU+bRLVlWtHzJ1QPzcKJ70f+ynp8lMoIeFlm+29BNPE=";
});
# Build beads from flake input with corrected vendorHash
# The upstream default.nix has stale vendorHash for commits with server mode
beadsRev = builtins.substring 0 8 (globalInputs.beads.rev or "unknown");
beadsPackage = pkgs.buildGoModule {
pname = "beads";
version = "0.49.1-${beadsRev}";
src = globalInputs.beads;
subPackages = [ "cmd/bd" ];
doCheck = false;
# Regenerated vendorHash for commit 93965b4a (has dolt server mode, Go 1.24)
vendorHash = "sha256-gwxGv8y4+1+k0741CnOYcyJPTJ5vTrynqPoO8YS9fbQ=";
nativeBuildInputs = [ pkgs.git ];
meta = with lib; {
description = "beads (bd) - An issue tracker designed for AI-supervised coding workflows";
homepage = "https://github.com/steveyegge/beads";
license = licenses.mit;
mainProgram = "bd";
};
};
# Gastown - multi-agent workspace manager (no upstream flake.nix yet)
# Source is tracked via flake input for renovate updates
gastownRev = builtins.substring 0 8 (globalInputs.gastown.rev or "unknown");
gastownPackage = pkgs.buildGoModule {
pname = "gastown";
version = "unstable-${builtins.substring 0 8 globalInputs.gastown.rev or "unknown"}";
version = "unstable-${gastownRev}";
src = globalInputs.gastown;
vendorHash = "sha256-ripY9vrYgVW8bngAyMLh0LkU/Xx1UUaLgmAA7/EmWQU=";
subPackages = [ "cmd/gt" ];
doCheck = false;
# Must match ldflags from gastown Makefile - BuiltProperly=1 is required
# or gt will error with "This binary was built with 'go build' directly"
ldflags = [
"-X github.com/steveyegge/gastown/internal/cmd.Version=${gastownRev}"
"-X github.com/steveyegge/gastown/internal/cmd.Commit=${gastownRev}"
"-X github.com/steveyegge/gastown/internal/cmd.BuildTime=nix-build"
"-X github.com/steveyegge/gastown/internal/cmd.BuiltProperly=1"
];
# Bug fixes not yet merged upstream
# Each patch is stored in a separate file for clarity and maintainability
patches = [
# Fix validateRecipient bug: normalize addresses before comparison
./gastown-fix-validate-recipient.patch
# Fix agentBeadToAddress to use title field for hq- prefixed beads
./gastown-fix-agent-bead-address-title.patch
# Fix crew/polecat home paths: remove incorrect /rig suffix
./gastown-fix-role-home-paths.patch
# Fix town root detection: don't map to Mayor (causes spurious mismatch warnings)
./gastown-fix-town-root-detection.patch
# Statusline optimization: skip expensive beads queries for detached sessions
# Reduces Dolt CPU from ~70% to ~20% by caching and early-exit
./gastown-statusline-optimization.patch
];
meta = with lib; {
description = "Gas Town - multi-agent workspace manager by Steve Yegge";
homepage = "https://github.com/steveyegge/gastown";
@@ -28,6 +69,28 @@ let
};
};
# Perles - TUI for beads issue tracking (no upstream flake.nix yet)
# Source is tracked via flake input for renovate updates
perlesRev = builtins.substring 0 8 (globalInputs.perles.rev or "unknown");
perlesPackage = pkgs.buildGoModule {
pname = "perles";
version = "unstable-${perlesRev}";
src = globalInputs.perles;
vendorHash = "sha256-JHERJDzbiqgjWXwRhXVjgDEiDQ3AUXRIONotfPF21B0=";
doCheck = false;
ldflags = [
"-X main.version=${perlesRev}"
];
meta = with lib; {
description = "Perles - Terminal UI for beads issue tracking";
homepage = "https://github.com/zjrosen/perles";
license = licenses.mit;
mainProgram = "perles";
};
};
# Fetch the claude-plugins repository (for humanlayer commands/agents)
# Update the rev to get newer versions of the commands
claudePluginsRepo = builtins.fetchGit {
@@ -62,9 +125,12 @@ in
home.packages = [
beadsPackage
gastownPackage
perlesPackage
pkgs.unstable.claude-code
pkgs.unstable.claude-code-router
pkgs.unstable.codex
pkgs.dolt
pkgs.sqlite
# Custom packages
pkgs.custom.tea-rbw
@@ -85,12 +151,14 @@ in
if [ -f "$file" ]; then
filename=$(basename "$file" .md)
dest="$HOME/.claude/commands/humanlayer:''${filename}.md"
rm -f "$dest" 2>/dev/null || true
# Copy file and conditionally remove the "model:" line from frontmatter
${if cfg.allowArbitraryClaudeCodeModelSelection
then "cp \"$file\" \"$dest\""
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
}
chmod u+w "$dest" 2>/dev/null || true
fi
done
@@ -99,12 +167,14 @@ in
if [ -f "$file" ]; then
filename=$(basename "$file" .md)
dest="$HOME/.claude/agents/humanlayer:''${filename}.md"
rm -f "$dest" 2>/dev/null || true
# Copy file and conditionally remove the "model:" line from frontmatter
${if cfg.allowArbitraryClaudeCodeModelSelection
then "cp \"$file\" \"$dest\""
else "${pkgs.gnused}/bin/sed '/^model:/d' \"$file\" > \"$dest\""
}
chmod u+w "$dest" 2>/dev/null || true
fi
done
@@ -119,6 +189,7 @@ in
sleep 0.5
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to commands"
fi
chmod u+w "$dest" 2>/dev/null || true
fi
done
@@ -133,13 +204,17 @@ in
sleep 0.5
cp "$file" "$dest" || echo "Warning: Failed to copy $filename.md to skills"
fi
chmod u+w "$dest" 2>/dev/null || true
fi
done
# Copy micro-skills (compact reusable knowledge referenced by formulas)
for file in ${./skills/micro}/*.md; do
if [ -f "$file" ]; then
cp "$file" "$HOME/.claude/commands/skills/$(basename "$file")"
dest="$HOME/.claude/commands/skills/$(basename "$file")"
rm -f "$dest" 2>/dev/null || true
cp "$file" "$dest"
chmod u+w "$dest" 2>/dev/null || true
fi
done
@@ -147,7 +222,10 @@ in
mkdir -p ~/.beads/formulas
for file in ${./formulas}/*.formula.toml; do
if [ -f "$file" ]; then
cp "$file" "$HOME/.beads/formulas/$(basename "$file")"
dest="$HOME/.beads/formulas/$(basename "$file")"
rm -f "$dest" 2>/dev/null || true
cp "$file" "$dest"
chmod u+w "$dest" 2>/dev/null || true
fi
done

View File

@@ -0,0 +1,15 @@
diff --git a/internal/mail/router.go b/internal/mail/router.go
--- a/internal/mail/router.go
+++ b/internal/mail/router.go
@@ -315,7 +315,10 @@ func agentBeadToAddress(bead *agentBead) string {
}
// For other hq- agents, fall back to description parsing
- return parseAgentAddressFromDescription(bead.Description)
+ if bead.Title != "" && strings.Contains(bead.Title, "/") {
+ return bead.Title
+ }
+ return parseAgentAddressFromDescription(bead.Description)
}
// Handle gt- prefixed IDs (legacy format)

View File

@@ -0,0 +1,35 @@
diff --git a/internal/mail/router.go b/internal/mail/router.go
--- a/internal/mail/router.go
+++ b/internal/mail/router.go
@@ -330,8 +330,29 @@ func agentBeadToAddress(bead *agentBead) string {
}
// Handle gt- prefixed IDs (legacy format)
- if !strings.HasPrefix(id, "gt-") {
- return "" // Not a valid agent bead ID
+ // Handle rig-specific prefixes: <prefix>-<rig>-<role>-<name>
+ // Examples: j-java-crew-americano -> java/crew/americano
+ idParts := strings.Split(id, "-")
+ if len(idParts) >= 3 {
+ for i, part := range idParts {
+ if part == "crew" || part == "polecat" || part == "polecats" {
+ if i >= 1 && i < len(idParts)-1 {
+ rig := idParts[i-1]
+ name := strings.Join(idParts[i+1:], "-")
+ return rig + "/" + part + "/" + name
+ }
+ }
+ if part == "witness" || part == "refinery" {
+ if i >= 1 {
+ return idParts[i-1] + "/" + part
+ }
+ }
+ }
+ }
+
+ // Handle gt- prefixed IDs (legacy format)
+ if !strings.HasPrefix(id, "gt-") {
+ return "" // Not a valid agent bead ID
}
// Strip prefix

View File

@@ -0,0 +1,25 @@
diff --git a/internal/git/git.go b/internal/git/git.go
--- a/internal/git/git.go
+++ b/internal/git/git.go
@@ -73,7 +73,19 @@ func copyDir(src, dest string) error {
srcPath := filepath.Join(src, entry.Name())
destPath := filepath.Join(dest, entry.Name())
- if entry.IsDir() {
+ // Handle symlinks (recreate them, do not follow)
+ if entry.Type()&os.ModeSymlink != 0 {
+ linkTarget, err := os.Readlink(srcPath)
+ if err != nil {
+ return err
+ }
+ if err := os.Symlink(linkTarget, destPath); err != nil {
+ return err
+ }
+ continue
+ }
+
+ if entry.IsDir() {
if err := copyDir(srcPath, destPath); err != nil {
return err
}

View File

@@ -0,0 +1,18 @@
diff --git a/internal/cmd/role.go b/internal/cmd/role.go
--- a/internal/cmd/role.go
+++ b/internal/cmd/role.go
@@ -326,11 +326,11 @@ func getRoleHome(role Role, rig, polecat, townRoot string) string {
if rig == "" || polecat == "" {
return ""
}
- return filepath.Join(townRoot, rig, "polecats", polecat, "rig")
+ return filepath.Join(townRoot, rig, "polecats", polecat)
case RoleCrew:
if rig == "" || polecat == "" {
return ""
}
- return filepath.Join(townRoot, rig, "crew", polecat, "rig")
+ return filepath.Join(townRoot, rig, "crew", polecat)
default:
return ""
}

View File

@@ -0,0 +1,18 @@
diff --git a/internal/cmd/prime.go b/internal/cmd/prime.go
--- a/internal/cmd/prime.go
+++ b/internal/cmd/prime.go
@@ -276,11 +276,12 @@ func detectRole(cwd, townRoot string) RoleInfo {
// Check for mayor role
// At town root, or in mayor/ or mayor/rig/
if relPath == "." || relPath == "" {
- ctx.Role = RoleMayor
- return ctx
+ return ctx // RoleUnknown - town root is shared space
}
+
+ // Check for mayor role: mayor/ or mayor/rig/
if len(parts) >= 1 && parts[0] == "mayor" {
ctx.Role = RoleMayor
return ctx
}

View File

@@ -0,0 +1,13 @@
diff --git a/internal/mail/router.go b/internal/mail/router.go
index b864c069..4b6a045b 100644
--- a/internal/mail/router.go
+++ b/internal/mail/router.go
@@ -646,7 +646,7 @@ func (r *Router) validateRecipient(identity string) error {
}
for _, agent := range agents {
- if agentBeadToAddress(agent) == identity {
+ if AddressToIdentity(agentBeadToAddress(agent)) == AddressToIdentity(identity) {
return nil // Found matching agent
}
}

View File

@@ -0,0 +1,135 @@
diff --git a/internal/cmd/statusline.go b/internal/cmd/statusline.go
index 2edf1be8..00253eea 100644
--- a/internal/cmd/statusline.go
+++ b/internal/cmd/statusline.go
@@ -6,6 +6,7 @@ import (
"path/filepath"
"sort"
"strings"
+ "time"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
@@ -14,6 +15,37 @@ import (
"github.com/steveyegge/gastown/internal/tmux"
"github.com/steveyegge/gastown/internal/workspace"
)
+// statusLineCacheTTL is how long cached status output remains valid.
+const statusLineCacheTTL = 10 * time.Second
+
+// statusLineCachePath returns the cache file path for a session.
+func statusLineCachePath(session string) string {
+ return filepath.Join(os.TempDir(), fmt.Sprintf("gt-status-%s", session))
+}
+
+// getStatusLineCache returns cached status if fresh, empty string otherwise.
+func getStatusLineCache(session string) string {
+ path := statusLineCachePath(session)
+ info, err := os.Stat(path)
+ if err != nil {
+ return ""
+ }
+ if time.Since(info.ModTime()) > statusLineCacheTTL {
+ return ""
+ }
+ data, err := os.ReadFile(path)
+ if err != nil {
+ return ""
+ }
+ return string(data)
+}
+
+// setStatusLineCache writes status to cache file.
+func setStatusLineCache(session, status string) {
+ path := statusLineCachePath(session)
+ _ = os.WriteFile(path, []byte(status), 0644)
+}
+
var (
statusLineSession string
@@ -34,6 +66,19 @@ func init() {
func runStatusLine(cmd *cobra.Command, args []string) error {
t := tmux.NewTmux()
+ // Optimization: skip expensive beads queries for detached sessions
+ if statusLineSession != "" {
+ if !t.IsSessionAttached(statusLineSession) {
+ fmt.Print("○ |")
+ return nil
+ }
+ // Check cache for attached sessions too
+ if cached := getStatusLineCache(statusLineSession); cached != "" {
+ fmt.Print(cached)
+ return nil
+ }
+ }
+
// Get session environment
var rigName, polecat, crew, issue, role string
@@ -150,7 +195,11 @@ func runWorkerStatusLine(t *tmux.Tmux, session, rigName, polecat, crew, issue st
// Output
if len(parts) > 0 {
- fmt.Print(strings.Join(parts, " | ") + " |")
+ output := strings.Join(parts, " | ") + " |"
+ if statusLineSession != "" {
+ setStatusLineCache(statusLineSession, output)
+ }
+ fmt.Print(output)
}
return nil
@@ -389,7 +438,11 @@ func runMayorStatusLine(t *tmux.Tmux) error {
}
}
- fmt.Print(strings.Join(parts, " | ") + " |")
+ output := strings.Join(parts, " | ") + " |"
+ if statusLineSession != "" {
+ setStatusLineCache(statusLineSession, output)
+ }
+ fmt.Print(output)
return nil
}
@@ -458,7 +511,11 @@ func runDeaconStatusLine(t *tmux.Tmux) error {
}
}
- fmt.Print(strings.Join(parts, " | ") + " |")
+ output := strings.Join(parts, " | ") + " |"
+ if statusLineSession != "" {
+ setStatusLineCache(statusLineSession, output)
+ }
+ fmt.Print(output)
return nil
}
@@ -526,7 +583,11 @@ func runWitnessStatusLine(t *tmux.Tmux, rigName string) error {
}
}
- fmt.Print(strings.Join(parts, " | ") + " |")
+ output := strings.Join(parts, " | ") + " |"
+ if statusLineSession != "" {
+ setStatusLineCache(statusLineSession, output)
+ }
+ fmt.Print(output)
return nil
}
@@ -617,7 +678,11 @@ func runRefineryStatusLine(t *tmux.Tmux, rigName string) error {
}
}
- fmt.Print(strings.Join(parts, " | ") + " |")
+ output := strings.Join(parts, " | ") + " |"
+ if statusLineSession != "" {
+ setStatusLineCache(statusLineSession, output)
+ }
+ fmt.Print(output)
return nil
}

View File

@@ -53,6 +53,22 @@
;; change `org-directory'. It must be set before org loads!
(setq org-directory "~/org/")
(after! org
;; Skip recurring events past their CALDAV_UNTIL date
;; org-caldav ignores UNTIL from RRULE, so we store it as a property
;; and filter here in the agenda
(defun my/skip-if-past-until ()
"Return non-nil if entry has CALDAV_UNTIL and current date is past it."
(let ((until-str (org-entry-get nil "CALDAV_UNTIL")))
(when (and until-str
(string-match "^\\([0-9]\\{4\\}\\)\\([0-9]\\{2\\}\\)\\([0-9]\\{2\\}\\)" until-str))
(let* ((until-year (string-to-number (match-string 1 until-str)))
(until-month (string-to-number (match-string 2 until-str)))
(until-day (string-to-number (match-string 3 until-str)))
(until-time (encode-time 0 0 0 until-day until-month until-year))
(today (current-time)))
(when (time-less-p until-time today)
(org-end-of-subtree t))))))
(setq org-agenda-span 'week
org-agenda-start-with-log-mode t
my-agenda-dirs '("projects" "roam")
@@ -61,6 +77,7 @@
"\.org$"))
my-agenda-dirs))
org-log-done 'time
org-agenda-skip-function-global #'my/skip-if-past-until
org-agenda-custom-commands '(("n" "Agenda"
((agenda "")
(tags-todo "-someday-recurring")))
@@ -83,25 +100,135 @@
"d" #'org-agenda-day-view
"w" #'org-agenda-week-view))
;; (use-package! org-caldav
;; :defer t
;; :config
;; (setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno"
;; org-caldav-calendar-id "personal"
;; org-icalendar-timezone "America/Los_Angeles"
;; org-caldav-inbox "~/org/calendar.org"
;; org-caldav-files nil
;; org-caldav-sync-direction 'cal->org))
;; org-caldav: Sync Org entries with Nextcloud CalDAV
;; Setup requirements:
;; 1. Create Nextcloud app password: Settings -> Security -> Devices & sessions
;; 2. Store in rbw: rbw add nextcloud-caldav (put app password as the secret)
;; 3. Run: doom sync
;; 4. Test: M-x my/org-caldav-sync-with-rbw (or SPC o a s)
;;
;; Note: Conflict resolution is "Org always wins" - treat Org as source of truth
;; for entries that originated in Org.
(defun my/get-rbw-password (alias)
"Return the password for ALIAS via rbw, unlocking the vault only if needed."
(let* ((cmd (format "rbw get %s 2>&1" alias))
(output (shell-command-to-string cmd)))
(string-trim output)))
;; Define sync wrapper before use-package (so keybinding works)
(defun my/org-caldav-sync-with-rbw ()
"Run org-caldav-sync with credentials from rbw embedded in URL."
(interactive)
(require 'org)
(require 'org-caldav)
(let* ((password (my/get-rbw-password "nextcloud-caldav"))
;; Embed credentials in URL (url-encode password in case of special chars)
(encoded-pass (url-hexify-string password)))
(setq org-caldav-url
(format "https://johno:%s@nextcloud.johnogle.info/remote.php/dav/calendars/johno"
encoded-pass))
(org-caldav-sync)))
(use-package! org-caldav
:after org
:commands (org-caldav-sync my/org-caldav-sync-with-rbw)
:init
(map! :leader
(:prefix ("o" . "open")
(:prefix ("a" . "agenda/calendar")
:desc "Sync CalDAV" "s" #'my/org-caldav-sync-with-rbw)))
:config
;; Nextcloud CalDAV base URL (credentials added dynamically by sync wrapper)
(setq org-caldav-url "https://nextcloud.johnogle.info/remote.php/dav/calendars/johno")
;; Timezone for iCalendar export
(setq org-icalendar-timezone "America/Los_Angeles")
;; Sync state storage (in org directory for multi-machine sync)
(setq org-caldav-save-directory (expand-file-name ".org-caldav/" org-directory))
;; Backup file for entries before modification
(setq org-caldav-backup-file (expand-file-name ".org-caldav/backup.org" org-directory))
;; Limit past events to 30 days (avoids uploading years of scheduled tasks)
(setq org-caldav-days-in-past 30)
;; Sync behavior: bidirectional by default
(setq org-caldav-sync-direction 'twoway)
;; What changes from calendar sync back to Org (conservative: title and timestamp only)
(setq org-caldav-sync-changes-to-org 'title-and-timestamp)
;; Deletion handling: never auto-delete to prevent accidental mass deletion
(setq org-caldav-delete-calendar-entries 'never)
(setq org-caldav-delete-org-entries 'never)
;; Enable TODO/VTODO sync
(setq org-icalendar-include-todo 'all)
(setq org-caldav-sync-todo t)
;; Map VTODO percent-complete to org-todo-keywords
;; Format: (PERCENT "KEYWORD") - percent thresholds map to states
(setq org-caldav-todo-percent-states
'((0 "TODO")
(25 "WAIT")
(50 "IN-PROGRESS")
(100 "DONE")
(100 "KILL")))
;; Allow export with broken links (mu4e links can't be resolved during export)
(setq org-export-with-broken-links 'mark)
;; Calendar-specific configuration
(setq org-caldav-calendars
'(;; Personal calendar: two-way sync with family-shared Nextcloud calendar
(:calendar-id "personal"
:inbox "~/org/personal-calendar.org"
:files ("~/org/personal-calendar.org"))
;; Tasks calendar: one-way sync (org → calendar only)
;; SCHEDULED/DEADLINE items from todo.org push to private Tasks calendar.
;; No inbox = no download from calendar (effectively one-way).
;; Note: Create 'tasks' calendar in Nextcloud first, keep it private.
(:calendar-id "tasks"
:files ("~/org/todo.org"))))
;; Handle UNTIL in recurring events
;; org-caldav ignores UNTIL from RRULE - events repeat forever.
;; This advice extracts UNTIL and stores it as a property for agenda filtering.
(defun my/org-caldav-add-until-property (orig-fun eventdata-alist)
"Advice to store CALDAV_UNTIL property for recurring events."
(let ((result (funcall orig-fun eventdata-alist)))
(let* ((rrule-props (alist-get 'rrule-props eventdata-alist))
(until-str (cadr (assoc 'UNTIL rrule-props)))
(summary (alist-get 'summary eventdata-alist)))
;; Debug: log what we're seeing
(message "CALDAV-DEBUG: %s | rrule-props: %S | until: %s"
(or summary "?") rrule-props until-str)
(when until-str
(save-excursion
(org-back-to-heading t)
(org-entry-put nil "CALDAV_UNTIL" until-str))))
result))
(advice-add 'org-caldav-insert-org-event-or-todo
:around #'my/org-caldav-add-until-property)
)
(defun my/get-rbw-password (alias &optional no-error)
"Return the password for ALIAS via rbw, unlocking the vault only if needed.
If NO-ERROR is non-nil, return nil instead of signaling an error when
rbw is unavailable or the entry is not found."
(if (not (executable-find "rbw"))
(if no-error
nil
(user-error "rbw: not installed or not in PATH"))
(let* ((cmd (format "rbw get %s 2>/dev/null" (shell-quote-argument alias)))
(output (string-trim (shell-command-to-string cmd))))
(if (string-empty-p output)
(if no-error
nil
(user-error "rbw: no entry found for '%s' - run: rbw add %s" alias alias))
output))))
(after! gptel
:config
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el")
(setq! gptel-api-key (my/get-rbw-password "openai-api-key-chatgpt-el" t)
gptel-default-mode 'org-mode
gptel-use-tools t
gptel-confirm-tool-calls 'always

View File

@@ -49,7 +49,7 @@
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
;; (unpin! t)
;; (package! org-caldav)
(package! org-caldav)
;; Note: Packages with custom recipes must be pinned for nix-doom-emacs-unstraightened
;; to build deterministically. Update pins when upgrading packages.

View File

@@ -90,6 +90,8 @@ with lib;
htop
tmux
zfs
rclone
custom.rclone-torbox-setup # Helper script to set up TorBox credentials via rbw
];
# Enable SSH
@@ -126,6 +128,26 @@ with lib;
roles.virtualisation.enable = true;
# TorBox WebDAV mount for rdt-client and Jellyfin
roles.rclone-mount = {
enable = true;
mounts.torbox = {
webdavUrl = "https://webdav.torbox.app";
username = "john@ogle.fyi"; # TorBox account email
mountPoint = "/media/media/torbox-rclone";
environmentFile = "/etc/rclone/torbox.env";
vfsCacheMode = "full"; # Best for streaming media
dirCacheTime = "5m";
extraArgs = [
"--buffer-size=64M"
"--vfs-read-chunk-size=32M"
"--vfs-read-chunk-size-limit=off"
];
# Wait for ZFS media pool to be mounted before starting
requiresMountsFor = [ "/media" ];
};
};
# Time zone
time.timeZone = "America/Los_Angeles"; # Adjust as needed

View File

@@ -23,12 +23,12 @@
printing.enable = true;
remote-build.builders = [
{
hostName = "zix790prors";
hostName = "zix790prors.oglehome";
maxJobs = 16;
speedFactor = 3;
}
{
hostName = "john-endesktop";
hostName = "john-endesktop.oglehome";
maxJobs = 1;
speedFactor = 1;
}

View File

@@ -19,11 +19,18 @@
desktopSession = "plasma";
};
};
remote-build.builders = [{
hostName = "zix790prors";
maxJobs = 16;
speedFactor = 4; # Prefer remote heavily on Steam Deck
}];
remote-build.builders = [
{
hostName = "zix790prors.oglehome";
maxJobs = 16;
speedFactor = 4;
}
{
hostName = "john-endesktop.oglehome";
maxJobs = 1;
speedFactor = 2;
}
];
users = {
enable = true;
extraGroups = [ "video" ];

View File

@@ -1,28 +1,29 @@
{ lib
, stdenv
, fetchurl
, autoPatchelfHook
, patchelf
, glibc
}:
let
version = "2.1.12";
version = "2.1.30";
srcs = {
aarch64-darwin = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-arm64/claude";
sha256 = "40be59519a84bd35eb1111aa46f72aa6b3443866d3f6336252a198fdcaefbbe5";
sha256 = "3ccc14f322b1e8da0cd58afc254fd5100eee066fa14729f30745e67a3f7979f7";
};
x86_64-darwin = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/darwin-x64/claude";
sha256 = "0eee4b46c91749480bf856f88e49b15a3e944faa9d346679c5f0c0d7fa6f2f54";
sha256 = "8a083696006483b8382ec0e47cd8f2e3223f3d2cab1a21c524fa08c082b5600e";
};
x86_64-linux = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-x64/claude";
sha256 = "3fe979215489dc1b31463fadf95ed2d2d5473a9969447bb7a46431f4578847d4";
sha256 = "ada8f1cf9272965d38b10f1adb6cea885e621c83f7e7bb233008c721f43fad54";
};
aarch64-linux = {
url = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases/${version}/linux-arm64/claude";
sha256 = "e214b1d3b5afd4cd2de9177359001d41a3eb98cb1e3665fe97edc592f5aa132f";
sha256 = "45fbf35a1011b06f86170b20beb64c599db0658aac70e2de2410c45d15775596";
};
};
@@ -38,8 +39,14 @@ in stdenv.mkDerivation {
dontUnpack = true;
dontBuild = true;
# Bun standalone binaries have JS code appended after the ELF sections
# stripping/patching would remove or corrupt this appended data
dontStrip = true;
dontPatchELF = true;
nativeBuildInputs = lib.optionals stdenv.isLinux [ autoPatchelfHook ];
# Don't use autoPatchelfHook - it rewrites the ELF and strips the appended
# bun bundle (the JS code is appended after the ELF sections)
nativeBuildInputs = lib.optionals stdenv.isLinux [ patchelf ];
installPhase = ''
runHook preInstall
@@ -49,6 +56,14 @@ in stdenv.mkDerivation {
runHook postInstall
'';
# Manually patch the interpreter for bun standalone binaries
# patchelf --set-interpreter modifies in-place without rewriting the entire ELF,
# preserving the appended JS bundle that bun needs at runtime
postFixup = lib.optionalString stdenv.isLinux ''
interpreter="${glibc}/lib/${if stdenv.hostPlatform.system == "aarch64-linux" then "ld-linux-aarch64.so.1" else "ld-linux-x86-64.so.2"}"
patchelf --set-interpreter "$interpreter" $out/bin/claude
'';
meta = with lib; {
description = "Terminal-based AI coding assistant from Anthropic";
homepage = "https://www.anthropic.com/claude-code";

View File

@@ -4,4 +4,5 @@
app-launcher-server = pkgs.callPackage ./app-launcher-server {};
claude-code = pkgs.callPackage ./claude-code {};
mcrcon-rbw = pkgs.callPackage ./mcrcon-rbw {};
rclone-torbox-setup = pkgs.callPackage ./rclone-torbox-setup {};
}

View File

@@ -0,0 +1,98 @@
{ pkgs, ... }:
pkgs.writeShellScriptBin "rclone-torbox-setup" ''
set -euo pipefail
# Default values
RBW_ENTRY="''${1:-torbox}"
ENV_FILE="''${2:-/etc/rclone/torbox.env}"
usage() {
echo "Usage: rclone-torbox-setup [rbw-entry] [env-file]"
echo ""
echo "Sets up rclone credentials for TorBox WebDAV mount."
echo "Retrieves password from rbw (Bitwarden), obscures it for rclone,"
echo "and writes it to the environment file for the systemd service."
echo ""
echo "Arguments:"
echo " rbw-entry Name of the Bitwarden entry containing the password (default: torbox)"
echo " env-file Path to write the environment file (default: /etc/rclone/torbox.env)"
echo ""
echo "The Bitwarden entry should contain your TorBox password as the password field."
echo ""
echo "Example:"
echo " rclone-torbox-setup torbox-password /etc/rclone/torbox.env"
exit 1
}
if [[ "''${1:-}" == "-h" ]] || [[ "''${1:-}" == "--help" ]]; then
usage
fi
echo "rclone TorBox credential setup"
echo "=============================="
echo ""
# Check if rbw is available
if ! command -v rbw &> /dev/null; then
echo "Error: rbw is not available. Please ensure rbw is installed and configured."
exit 1
fi
# Check if rclone is available
if ! command -v rclone &> /dev/null; then
echo "Error: rclone is not available. Please ensure rclone is installed."
exit 1
fi
echo "Retrieving password from rbw entry: $RBW_ENTRY"
# Retrieve password from Bitwarden
if ! TORBOX_PASS=$(rbw get "$RBW_ENTRY" 2>/dev/null); then
echo ""
echo "Error: Failed to retrieve password from rbw entry '$RBW_ENTRY'"
echo ""
echo "Please ensure:"
echo " 1. The entry '$RBW_ENTRY' exists in Bitwarden"
echo " 2. rbw is unlocked: rbw unlock"
echo " 3. rbw is synced: rbw sync"
echo ""
echo "To create the entry in Bitwarden:"
echo " - Name: $RBW_ENTRY"
echo " - Password: Your TorBox password"
exit 1
fi
echo "Password retrieved successfully"
# Obscure the password for rclone
echo "Obscuring password for rclone..."
if ! OBSCURED_PASS=$(echo -n "$TORBOX_PASS" | rclone obscure -); then
echo "Error: Failed to obscure password with rclone"
exit 1
fi
# Create the directory if needed (requires sudo)
ENV_DIR=$(dirname "$ENV_FILE")
if [[ ! -d "$ENV_DIR" ]]; then
echo "Creating directory $ENV_DIR (requires sudo)..."
sudo mkdir -p "$ENV_DIR"
fi
# Write the environment file
echo "Writing environment file to $ENV_FILE (requires sudo)..."
echo "RCLONE_WEBDAV_PASS=$OBSCURED_PASS" | sudo tee "$ENV_FILE" > /dev/null
sudo chmod 600 "$ENV_FILE"
echo ""
echo "Setup complete!"
echo ""
echo "The environment file has been created at: $ENV_FILE"
echo "The rclone-mount-torbox systemd service will use this file."
echo ""
echo "To activate the mount after NixOS rebuild:"
echo " sudo systemctl start rclone-mount-torbox"
echo ""
echo "To check status:"
echo " sudo systemctl status rclone-mount-torbox"
''

View File

@@ -13,7 +13,7 @@
"lockFileMaintenance": {
"enabled": true,
"schedule": [
"before 5am on monday"
"after 2pm and before 4pm on Saturday"
]
},
"dependencyDashboard": true,
@@ -37,6 +37,9 @@
"/^nixpkgs$/",
"/^home-manager$/",
"/^nix-darwin$/"
],
"schedule": [
"after 2pm and before 4pm on Saturday"
]
},
{
@@ -48,6 +51,21 @@
"matchPackageNames": [
"/nixpkgs-unstable/",
"/home-manager-unstable/"
],
"schedule": [
"after 2pm and before 4pm on Saturday"
]
},
{
"description": "nixpkgs-qt updates on Saturday (staggered from main ecosystem)",
"matchManagers": [
"nix"
],
"matchPackageNames": [
"/nixpkgs-qt/"
],
"schedule": [
"after 4pm and before 6pm on Saturday"
]
},
{

View File

@@ -14,6 +14,7 @@ with lib;
./nfs-mounts
./nvidia
./printing
./rclone-mount
./remote-build
./spotifyd
./users

View File

@@ -47,23 +47,23 @@ in
if cfg.jellyfinScaleFactor != null
then pkgs.symlinkJoin {
name = "jellyfin-media-player-scaled";
paths = [ pkgs.jellyfin-media-player ];
paths = [ pkgs.qt-pinned.jellyfin-media-player ];
nativeBuildInputs = [ pkgs.makeWrapper ];
postBuild = ''
mkdir -p $out/bin
rm -f $out/bin/jellyfin-desktop
makeWrapper ${pkgs.jellyfin-media-player}/bin/jellyfin-desktop $out/bin/jellyfin-desktop \
makeWrapper ${pkgs.qt-pinned.jellyfin-media-player}/bin/jellyfin-desktop $out/bin/jellyfin-desktop \
--add-flags "--tv --scale-factor ${toString cfg.jellyfinScaleFactor}"
# Update .desktop file to include scale factor and TV mode arguments
mkdir -p $out/share/applications
rm -f $out/share/applications/org.jellyfin.JellyfinDesktop.desktop
substitute ${pkgs.jellyfin-media-player}/share/applications/org.jellyfin.JellyfinDesktop.desktop \
substitute ${pkgs.qt-pinned.jellyfin-media-player}/share/applications/org.jellyfin.JellyfinDesktop.desktop \
$out/share/applications/org.jellyfin.JellyfinDesktop.desktop \
--replace-fail "Exec=jellyfin-desktop" "Exec=jellyfin-desktop --tv --scale-factor ${toString cfg.jellyfinScaleFactor}"
'';
}
else pkgs.jellyfin-media-player;
else pkgs.qt-pinned.jellyfin-media-player;
in mkIf cfg.enable
{
users.extraUsers.kodi = {

View File

@@ -0,0 +1,149 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.roles.rclone-mount;
# Generate systemd service for a single mount
mkMountService = name: mountCfg: {
description = "rclone mount for ${name}";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
# Wait for parent mount points (e.g., ZFS pools) to be available
unitConfig = mkIf (mountCfg.requiresMountsFor != []) {
RequiresMountsFor = mountCfg.requiresMountsFor;
};
serviceConfig = {
Type = "notify";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${mountCfg.mountPoint}";
ExecStart = concatStringsSep " " ([
"${pkgs.rclone}/bin/rclone mount"
":webdav:${mountCfg.remotePath}"
"${mountCfg.mountPoint}"
"--webdav-url=${mountCfg.webdavUrl}"
"--webdav-vendor=${mountCfg.webdavVendor}"
"--webdav-user=${mountCfg.username}"
"--allow-other"
"--vfs-cache-mode=${mountCfg.vfsCacheMode}"
"--dir-cache-time=${mountCfg.dirCacheTime}"
"--poll-interval=${mountCfg.pollInterval}"
"--log-level=${mountCfg.logLevel}"
] ++ mountCfg.extraArgs);
ExecStop = "${pkgs.fuse}/bin/fusermount -uz ${mountCfg.mountPoint}";
Restart = "on-failure";
RestartSec = "10s";
EnvironmentFile = mountCfg.environmentFile;
};
};
in
{
options.roles.rclone-mount = {
enable = mkEnableOption "Enable rclone WebDAV mounts";
mounts = mkOption {
type = types.attrsOf (types.submodule {
options = {
webdavUrl = mkOption {
type = types.str;
description = "WebDAV server URL (e.g., https://webdav.torbox.app)";
};
webdavVendor = mkOption {
type = types.enum [ "other" "nextcloud" "owncloud" "sharepoint" "sharepoint-ntlm" "fastmail" ];
default = "other";
description = "WebDAV server vendor for optimizations";
};
username = mkOption {
type = types.str;
description = "WebDAV username (often email address)";
};
environmentFile = mkOption {
type = types.path;
description = ''
Path to environment file containing RCLONE_WEBDAV_PASS.
The password should be obscured using: rclone obscure <password>
File format: RCLONE_WEBDAV_PASS=<obscured_password>
'';
};
mountPoint = mkOption {
type = types.str;
description = "Local mount point path";
};
remotePath = mkOption {
type = types.str;
default = "/";
description = "Remote path on WebDAV server to mount";
};
vfsCacheMode = mkOption {
type = types.enum [ "off" "minimal" "writes" "full" ];
default = "full";
description = ''
VFS cache mode. For streaming media, 'full' is recommended.
- off: No caching (direct reads/writes)
- minimal: Cache open files only
- writes: Cache writes and open files
- full: Full caching of all files
'';
};
dirCacheTime = mkOption {
type = types.str;
default = "5m";
description = "Time to cache directory entries";
};
pollInterval = mkOption {
type = types.str;
default = "1m";
description = "Poll interval for remote changes";
};
logLevel = mkOption {
type = types.enum [ "DEBUG" "INFO" "NOTICE" "ERROR" ];
default = "INFO";
description = "rclone log level";
};
extraArgs = mkOption {
type = types.listOf types.str;
default = [];
description = "Extra arguments to pass to rclone mount";
};
requiresMountsFor = mkOption {
type = types.listOf types.str;
default = [];
description = ''
List of mount points that must be available before this service starts.
Use this when the mount point's parent is on a ZFS pool or other filesystem
that may not be mounted at boot time.
Example: [ "/media" ] to wait for the media ZFS pool to mount.
'';
};
};
});
default = {};
description = "Attribute set of rclone WebDAV mounts to configure";
};
};
config = mkIf cfg.enable {
# Ensure FUSE is available
environment.systemPackages = [ pkgs.rclone pkgs.fuse ];
programs.fuse.userAllowOther = true;
# Create systemd services for each mount
systemd.services = mapAttrs' (name: mountCfg:
nameValuePair "rclone-mount-${name}" (mkMountService name mountCfg)
) cfg.mounts;
};
}

View File

@@ -35,12 +35,12 @@
# a) Configure builders in configuration.nix:
# roles.remote-build.builders = [
# {
# hostName = "zix790prors";
# hostName = "zix790prors.oglehome";
# maxJobs = 16; # Number of parallel build jobs
# speedFactor = 3; # Higher = prefer this builder
# }
# {
# hostName = "john-endesktop";
# hostName = "john-endesktop.oglehome";
# maxJobs = 1; # Conservative for busy machines
# speedFactor = 1;
# }