Compare commits

...

14 Commits

Author SHA1 Message Date
e4c082a547 feat(skills): Add contribution guidelines check to beads skills
All checks were successful
CI / check (pull_request) Successful in 2m57s
2026-01-14 14:00:28 -08:00
667f5b28dc feat(skills): Close Gitea issues when beads are reconciled
Some checks failed
CI / check (push) Has been cancelled
2026-01-14 13:59:30 -08:00
4bb71d0b7e Remove wixos (WSL) configuration
All checks were successful
CI / check (push) Successful in 3m0s
WSL is no longer used. This removes:
- machines/wixos/ directory and configuration.nix
- nixos-wsl input from flake.nix
- nixosConfigurations.wixos output
- References to wixos in AGENTS.md and .goosehints

Implements bead: nixos-configs-2mk
2026-01-13 18:02:36 -08:00
0bc134f557 fix(mu4e): Configure msmtp to preserve email body content
All checks were successful
CI / check (push) Successful in 6m0s
The mu4e msmtp configuration was causing email bodies to be stripped,
especially for multipart messages from org-msg. This was due to missing
critical msmtp settings.

Changes:
- Add message-sendmail-f-is-evil to prevent -f flag issues
- Add --read-envelope-from to msmtp arguments
- Set both send-mail-function and message-send-mail-function

Fixes: nixos-configs-9l8
2026-01-13 17:48:36 -08:00
1b9df3926e Fix conflicting audio role config: remove pulseaudio, keep pipewire
Some checks failed
CI / check (push) Has been cancelled
Remove services.pulseaudio configuration that conflicted with
services.pipewire. PipeWire replaces PulseAudio and provides
compatibility through pulse.enable.

Also added alsa.enable and alsa.support32Bit for better ALSA support.
2026-01-13 17:48:00 -08:00
bd98793528 feat(roles): Parameterize hardcoded values in printing, nfs-mounts, and virtualisation roles
Some checks failed
CI / check (push) Has been cancelled
- printing role: Add configurable printerName, printerUri, and printerModel options
  to replace hardcoded Brother printer values
- nfs-mounts role: Add configurable server, remotePath, and mountPoint options
  to replace hardcoded NFS server IP (10.0.0.43)
- virtualisation role: Add configurable dockerUsers option as list type
  to replace hardcoded 'johno' docker group membership

All options have sensible defaults matching the original hardcoded values,
ensuring backward compatibility while allowing per-host customization.

Implements bead: nixos-configs-fkt
2026-01-13 17:20:59 -08:00
d78637cf13 feat(home-manager): Add platform compatibility guards to cross-platform roles
Some checks failed
CI / check (push) Has been cancelled
Add lib.optionals pkgs.stdenv.isLinux guards to roles that contain
Linux-only packages or services to prevent build failures on Darwin:

- communication: Guard Electron apps (element-desktop, fluffychat,
  nextcloud-talk-desktop) that don't build on Darwin due to electron
  build-from-source limitations
- kdeconnect: Guard entire config block since services.kdeconnect
  requires D-Bus and systemd (Linux-only)
- sync: Guard syncthingtray package (requires Linux system tray)
- email: Guard systemd.user.services/timers (Darwin uses launchd)
- desktop: Guard Linux-only packages, services, and KDE-specific
  configurations including gnome-keyring, systemd services, and
  XDG mime associations

Implements bead: nixos-configs-tcu
2026-01-13 17:20:01 -08:00
08d16bd2c9 feat(scripts): Add --help flags to all flake apps
Some checks failed
CI / check (push) Has been cancelled
Add consistent --help/-h argument handling to update-doomemacs.sh,
rotate-wallpaper.sh, and upgrade.sh scripts. Each script now displays
usage information and a description of what it does.

update-claude-code already had --help support.
2026-01-13 17:18:46 -08:00
a14ff9be4d fix(flake): Remove duplicate home-manager imports from wixos and zix790prors
Some checks failed
CI / check (pull_request) Successful in 5m36s
CI / check (push) Has been cancelled
The nixosModules list already includes inputs.home-manager.nixosModules.home-manager,
so these individual configuration imports were redundant.
2026-01-13 16:37:41 -08:00
90217ec85a fix(ci): Use full Gitea URL for composite action
All checks were successful
CI / check (push) Successful in 7m29s
Gitea Actions defaults to GitHub for short-form action references.
Use full URL to reference actions from the same Gitea instance.
2026-01-13 15:58:20 -08:00
f99f4069f0 feat(ci): Add Gitea Actions workflow with Nix caching
Some checks failed
CI / check (push) Failing after 1s
Uses johno/gitea-actions/nix-setup composite action for:
- Nix installation via DeterminateSystems/nix-installer-action
- Nix store caching via actions/cache@v4
- Per-repo cache isolation based on flake.lock hash
2026-01-13 15:50:54 -08:00
320a2d3738 refactor: Move import_gitea_issues to user-level skill
Moves from project-level (.claude/commands/) to user-level
(home/roles/development/skills/) so it's available across all projects
via Home Manager activation.

Bead: nixos-configs-g72
2026-01-13 15:37:59 -08:00
92b6cfb710 fix(common): Add ghostty terminfo for SSH compatibility
Installs ghostty.terminfo on all NixOS machines so tmux works
when SSH'ing from a Ghostty terminal.
2026-01-13 14:09:57 -08:00
996fb86ed8 [nixos-configs-vru] Add skill for responding to Gitea PR review comments (#26)
## Summary
- Rewrote gitea_pr_review.md as a comprehensive interactive skill
- Accepts PR number as argument or auto-detects from current branch
- Reads Gitea config from tea CLI config file
- Fetches and displays review comments via REST API
- Interactive comment selection via AskUserQuestion
- Posts replies via `tea comment` with file:line context

## Bead Reference
Implements bead: nixos-configs-vru

## Changes
- Rewritten `home/roles/development/skills/gitea_pr_review.md` (+259/-155 lines)

## Testing
Please leave a review comment on this PR so we can test the skill!

## Limitations
- Thread replies are posted as top-level comments (Gitea API limitation)
- Uses first login from tea config

Reviewed-on: #26
Co-authored-by: John Ogle <john@ogle.fyi>
Co-committed-by: John Ogle <john@ogle.fyi>
2026-01-13 09:08:17 -08:00
27 changed files with 773 additions and 421 deletions

18
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,18 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: https://git.johnogle.info/johno/gitea-actions/nix-setup@main
- name: Check flake
run: nix flake check

View File

@@ -9,7 +9,7 @@ Directory Structure:
----------------------
• packages/ - Custom Nix packages leveraged across various configurations.
• roles/ - Role-based configurations (e.g., kodi, bluetooth) each with its own module (default.nix) for inclusion in machine setups.
• machines/ - Machine-specific configurations (e.g., nix-book, z790prors, boxy, wixos) including configuration.nix and hardware-configuration.nix tailored for each hardware.
• machines/ - Machine-specific configurations (e.g., nix-book, zix790prors, boxy) including configuration.nix and hardware-configuration.nix tailored for each hardware.
• home/ - Home-manager configurations for personal environments and application settings (e.g., home-nix-book.nix, home-z790prors.nix).
Design Principles:

View File

@@ -14,7 +14,7 @@ This repository uses `beads` for issue tracking and management. Run `bd quicksta
### Flake Structure
- **flake.nix**: Main entry point defining inputs (nixpkgs, home-manager, plasma-manager, etc.) and outputs for multiple NixOS configurations
- **Machines**: `nix-book`, `boxy`, `wixos` (WSL configuration), `zix790prors`, `live-usb`, `johno-macbookpro` (Darwin/macOS)
- **Machines**: `nix-book`, `boxy`, `zix790prors`, `live-usb`, `johno-macbookpro` (Darwin/macOS)
- **Home configurations**: Standalone home-manager configuration for user `johno`
### Directory Structure
@@ -78,7 +78,6 @@ The repository also uses a modular home-manager role system for user-space confi
- **nix-book**: Compact laptop → excludes office/media roles due to SSD space constraints
- **boxy**: Living room media center → optimized for media consumption, excludes sync/office (shared machine)
- **zix790prors**: All-purpose workstation → full desktop experience with all roles enabled
- **wixos**: WSL2 development → full desktop experience, inherits from zix790prors Windows host
- **live-usb**: Temporary environment → only base + desktop roles, no persistent services
- **johno-macbookpro**: macOS work laptop → Darwin-specific configuration with development tools
@@ -111,7 +110,6 @@ darwin-rebuild build --flake .#johno-macbookpro
- `nix-book`: Compact laptop with storage constraints, uses `home/home-laptop-compact.nix`
- `boxy`: Shared living room media center/gaming desktop with AMD GPU, uses `home/home-media-center.nix`
- `zix790prors`: Powerful all-purpose workstation (gaming, 3D modeling, development), dual-boots Windows 11 with shared btrfs /games partition, uses `home/home-desktop.nix`
- `wixos`: WSL2 development environment running in Windows partition of zix790prors, uses `home/home-desktop.nix`
- `live-usb`: Bootable ISO configuration, uses `home/home-live-usb.nix`
- `johno-macbookpro`: macOS work laptop, uses `home/home-darwin-work.nix`

67
flake.lock generated
View File

@@ -60,22 +60,6 @@
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1765121682,
"narHash": "sha256-4VBOP18BFeiPkyhy9o4ssBNQEvfvv1kXkasAYd0+rrA=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
@@ -241,38 +225,18 @@
"type": "github"
}
},
"nixos-wsl": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1765841014,
"narHash": "sha256-55V0AJ36V5Egh4kMhWtDh117eE3GOjwq5LhwxDn9eHg=",
"owner": "nix-community",
"repo": "NixOS-WSL",
"rev": "be4af8042e7a61fa12fda58fe9a3b3babdefe17b",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "main",
"repo": "NixOS-WSL",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1765472234,
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
"owner": "NixOS",
"lastModified": 1767480499,
"narHash": "sha256-8IQQUorUGiSmFaPnLSo2+T+rjHtiNWc+OAzeHck7N48=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
"rev": "30a3c519afcf3f99e2c6df3b359aec5692054d92",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
@@ -293,22 +257,6 @@
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1767480499,
"narHash": "sha256-8IQQUorUGiSmFaPnLSo2+T+rjHtiNWc+OAzeHck7N48=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "30a3c519afcf3f99e2c6df3b359aec5692054d92",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"plasma-manager": {
"inputs": {
"home-manager": [
@@ -364,8 +312,7 @@
"jovian": "jovian",
"nix-darwin": "nix-darwin",
"nix-doom-emacs-unstraightened": "nix-doom-emacs-unstraightened",
"nixos-wsl": "nixos-wsl",
"nixpkgs": "nixpkgs_2",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"plasma-manager": "plasma-manager",
"plasma-manager-unstable": "plasma-manager-unstable"

View File

@@ -4,7 +4,6 @@
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
nixos-wsl.url = "github:nix-community/NixOS-WSL/main";
nix-darwin = {
url = "github:nix-darwin/nix-darwin/nix-darwin-25.11";
@@ -55,7 +54,7 @@
};
};
outputs = { self, nixpkgs, nixpkgs-unstable, nixos-wsl, ... } @ inputs: let
outputs = { self, nixpkgs, nixpkgs-unstable, ... } @ inputs: let
# Shared overlay function to reduce duplication across module sets
# Parameters:
# unstableOverlays: Additional overlays to apply when importing nixpkgs-unstable
@@ -84,6 +83,7 @@
};
};
nixosModules = [
./roles
inputs.home-manager.nixosModules.home-manager
@@ -157,24 +157,10 @@
];
};
nixosConfigurations.wixos = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModules ++ [
nixos-wsl.nixosModules.default
./machines/wixos/configuration.nix
inputs.home-manager.nixosModules.home-manager
{
home-manager.users.johno = import ./home/home-desktop.nix;
home-manager.extraSpecialArgs = { inherit system; };
}
];
};
nixosConfigurations.zix790prors = nixpkgs.lib.nixosSystem rec {
system = "x86_64-linux";
modules = nixosModules ++ [
./machines/zix790prors/configuration.nix
inputs.home-manager.nixosModules.home-manager
{
home-manager.users.johno = import ./home/home-desktop.nix;
home-manager.extraSpecialArgs = { inherit system; };

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.communication;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.communication = {
@@ -12,14 +13,14 @@ in
config = mkIf cfg.enable {
home.packages = [
# Communication apps
# For logging back into google chat (cross-platform)
globalInputs.google-cookie-retrieval.packages.${system}.default
] ++ optionals isLinux [
# Linux-only communication apps (Electron apps don't build on Darwin)
pkgs.element-desktop
# Re-enabled in 25.11 after security issues were resolved
pkgs.fluffychat
pkgs.nextcloud-talk-desktop
# For logging back into google chat
globalInputs.google-cookie-retrieval.packages.${system}.default
];
};
}

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.desktop;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.desktop = {
@@ -12,27 +13,29 @@ in
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Desktop applications
# Cross-platform desktop applications
bitwarden-desktop
dunst
keepassxc
xdg-utils # XDG utilities for opening files/URLs with default applications
] ++ optionals isLinux [
# Linux-only desktop applications
dunst
unstable.ghostty
# Desktop utilities
# Linux-only desktop utilities
feh # Image viewer and wallpaper setter for X11
rofi # Application launcher for X11
solaar # Logitech management software
waybar
wofi # Application launcher for Wayland
xdg-utils # XDG utilities for opening files/URLs with default applications
# System utilities with GUI components
# Linux-only system utilities with GUI components
(snapcast.override { pulseaudioSupport = true; })
# KDE tiling window management
# KDE tiling window management (Linux-only)
kdePackages.krohnkite # Dynamic tiling extension for KWin 6
# KDE PIM applications for email, calendar, and contacts
# KDE PIM applications for email, calendar, and contacts (Linux-only)
kdePackages.kmail
kdePackages.kmail-account-wizard
kdePackages.kmailtransport
@@ -40,33 +43,33 @@ in
kdePackages.kaddressbook
kdePackages.kontact
# KDE System components needed for proper integration
# KDE System components needed for proper integration (Linux-only)
kdePackages.kded
kdePackages.systemsettings
kdePackages.kmenuedit
# Desktop menu support
# Desktop menu support (Linux-only)
kdePackages.plasma-desktop # Contains applications.menu
# KDE Online Accounts support
# KDE Online Accounts support (Linux-only)
kdePackages.kaccounts-integration
kdePackages.kaccounts-providers
kdePackages.signond
# KDE Mapping
# KDE Mapping (Linux-only)
kdePackages.marble # Virtual globe and world atlas
# KDE Productivity
# KDE Productivity (Linux-only)
kdePackages.kate # Advanced text editor with syntax highlighting
kdePackages.okular # Universal document viewer (PDF, ePub, etc.)
kdePackages.spectacle # Screenshot capture utility
kdePackages.filelight # Visual disk usage analyzer
# KDE Multimedia
# KDE Multimedia (Linux-only)
kdePackages.gwenview # Image viewer and basic editor
kdePackages.elisa # Music player
# KDE System Utilities
# KDE System Utilities (Linux-only)
kdePackages.ark # Archive manager (zip, tar, 7z, etc.)
kdePackages.yakuake # Drop-down terminal emulator
];
@@ -77,12 +80,15 @@ in
programs.spotify-player.enable = true;
services.gnome-keyring = {
# Linux-only: GNOME keyring service
services.gnome-keyring = mkIf isLinux {
enable = true;
};
# rbw vault unlock on login and resume from suspend
systemd.user.services.rbw-unlock-on-login = {
# Linux-only: systemd user services for rbw vault unlock
systemd.user.services = mkIf isLinux {
# rbw vault unlock on login
rbw-unlock-on-login = {
Unit = {
Description = "Unlock rbw vault at login";
After = [ "graphical-session.target" ];
@@ -101,7 +107,8 @@ in
};
};
systemd.user.services.rbw-unlock-on-resume = {
# rbw vault unlock on resume from suspend
rbw-unlock-on-resume = {
Unit = {
Description = "Unlock rbw vault after resume from suspend";
After = [ "suspend.target" ];
@@ -119,9 +126,10 @@ in
WantedBy = [ "suspend.target" ];
};
};
};
# KDE environment variables for proper integration
home.sessionVariables = {
# Linux-only: KDE environment variables for proper integration
home.sessionVariables = mkIf isLinux {
QT_QPA_PLATFORMTHEME = "kde";
KDE_SESSION_VERSION = "6";
};
@@ -141,13 +149,14 @@ in
"x-scheme-handler/https" = "firefox.desktop";
};
defaultApplications = {
# Web browsers
# Web browsers (cross-platform)
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
"x-scheme-handler/about" = "firefox.desktop";
"x-scheme-handler/unknown" = "firefox.desktop";
} // optionalAttrs isLinux {
# Linux-only: KDE application associations
# Documents
"application/pdf" = "okular.desktop";
"text/plain" = "kate.desktop";
@@ -190,9 +199,11 @@ in
};
};
# Fix for KDE applications.menu file issue on Plasma 6
# Linux-only: Fix for KDE applications.menu file issue on Plasma 6
# KDE still looks for applications.menu but Plasma 6 renamed it to plasma-applications.menu
xdg.configFile."menus/applications.menu".source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
xdg.configFile."menus/applications.menu" = mkIf isLinux {
source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
};
# Note: modules must be imported at top-level home config
};

View File

@@ -54,6 +54,8 @@ When this command is invoked:
- Read `thoughts/beads-{bead-id}/plan.md` FULLY
- Check for any existing checkmarks (- [x]) indicating partial progress
- Read any research at `thoughts/beads-{bead-id}/research.md`
- If plan's Success Criteria references contribution guidelines (e.g., "Per CONTRIBUTING.md:"),
verify the original CONTRIBUTING.md still exists and requirements are current
5. **Mark bead in progress** (if not already):
```bash
@@ -127,6 +129,10 @@ All phases completed and automated verification passed:
- {List manual verification items from plan}
Let me know when manual testing is complete so I can close the bead.
**Contribution guidelines compliance:**
- {List any contribution guideline requirements that were part of Success Criteria}
- {Note if any requirements could not be automated and need manual review}
```
**STOP HERE and wait for user confirmation.**

View File

@@ -51,13 +51,32 @@ When this command is invoked:
- Any linked tickets or docs
- Use Read tool WITHOUT limit/offset
2. **Spawn initial research tasks**:
2. **Check for contribution guidelines**:
```bash
# Check standard locations for contribution guidelines
for f in CONTRIBUTING.md .github/CONTRIBUTING.md docs/CONTRIBUTING.md; do
if [ -f "$f" ]; then
echo "Found: $f"
break
fi
done
```
If found:
- Read the file fully
- Extract actionable requirements (testing, code style, documentation, PR conventions)
- These requirements MUST be incorporated into the plan's Success Criteria
If not found, note "No contribution guidelines found" and proceed.
3. **Spawn initial research tasks**:
- **codebase-locator**: Find all files related to the task
- **codebase-analyzer**: Understand current implementation
- **codebase-pattern-finder**: Find similar features to model after
- **thoughts-locator**: Find any existing plans or decisions
3. **Read all files identified by research**:
4. **Read all files identified by research**:
- Read them FULLY into main context
- Cross-reference with requirements
@@ -273,6 +292,12 @@ Always separate into two categories:
- Performance under real conditions
- Edge cases hard to automate
**From Contribution Guidelines** (if CONTRIBUTING.md exists):
- Include any testing requirements specified in guidelines
- Include any code style/linting requirements
- Include any documentation requirements
- Reference the guideline: "Per CONTRIBUTING.md: {requirement}"
## Example Invocation
```

View File

@@ -51,6 +51,18 @@ When this command is invoked:
- Use the Read tool WITHOUT limit/offset parameters
- Read these files yourself in the main context before spawning sub-tasks
### Step 1.5: Check for contribution guidelines
Before spawning sub-agents, check if the repository has contribution guidelines:
```bash
for f in CONTRIBUTING.md .github/CONTRIBUTING.md docs/CONTRIBUTING.md; do
if [ -f "$f" ]; then echo "Found: $f"; break; fi
done
```
If found, read the file and note key requirements. These should be included in the research document under a "## Contribution Guidelines" section if relevant to the research question.
### Step 2: Analyze and decompose the research question
- Break down the query into composable research areas
- Identify specific components, patterns, or concepts to investigate
@@ -143,6 +155,10 @@ status: complete
## Architecture Documentation
{Current patterns, conventions found in codebase}
## Contribution Guidelines
{If CONTRIBUTING.md exists, summarize key requirements relevant to the research topic}
{If no guidelines found, omit this section}
## Historical Context (from thoughts/)
{Relevant insights from thoughts/ with references}

View File

@@ -1,217 +1,350 @@
---
description: Manage and respond to Gitea/Forgejo PR review comments
description: Address Gitea/Forgejo PR review comments with code changes
---
# Gitea PR Review Comments
# Gitea PR Review
This skill enables reading PR review comments and posting inline thread replies on Gitea/Forgejo instances.
You are tasked with **addressing** PR review comments by making code changes, then summarizing what was done. This skill drives PR progress, not just conversation.
## Philosophy
**Comments are work items, not conversation starters.**
When a reviewer leaves a comment, they're identifying something that needs attention. This skill:
1. Categorizes comments by actionability
2. Makes code changes to address actionable comments
3. Commits and pushes those changes
4. Posts a single summary comment describing what was done
## Prerequisites
- `tea` CLI configured with a Gitea/Forgejo instance
- Access token from tea config: `~/.config/tea/config.yml`
- Repository must be a Gitea/Forgejo remote (not GitHub)
- **Nix users**: All tools available via nixpkgs (`nix run nixpkgs#tea`)
## Configuration
## Initial Setup
Get the Gitea instance URL and token from tea config:
When this command is invoked:
1. **Parse the input for PR number**:
- If a PR number is provided as argument, use it
- If no PR number, detect from current branch (see PR Detection section)
2. **Verify required tools are available**:
```bash
# Get the default login URL and token
yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml
yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml
which tea
```
Or if you have a specific login name:
If tea is missing:
```
Error: `tea` CLI not found.
Please install:
- Nix: nix run nixpkgs#tea
- Other: https://gitea.com/gitea/tea
```
**STOP** if tea is missing.
3. **Extract configuration from tea config**:
```bash
yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .url' ~/.config/tea/config.yml
yq -r '.logins[] | select(.name == "YOUR_LOGIN") | .token' ~/.config/tea/config.yml
# Read tea config (it's YAML but simple enough to grep)
TEA_CONFIG="$HOME/.config/tea/config.yml"
GITEA_URL=$(grep -A1 'logins:' "$TEA_CONFIG" | grep 'url:' | head -1 | sed 's/.*url: //')
TOKEN=$(grep -A5 'logins:' "$TEA_CONFIG" | grep 'token:' | head -1 | sed 's/.*token: //')
```
## Commands
If config is missing or invalid:
```
Error: Could not read tea config at ~/.config/tea/config.yml
### 1. List PR Review Comments
Please ensure `tea` is installed and configured:
1. Install tea
2. Log in: tea login add --url https://your-gitea-instance --token YOUR_TOKEN
```
**STOP** if config is invalid.
Fetch all reviews and their comments for a PR:
4. **Detect repository info from git remote**:
```bash
REMOTE_URL=$(git remote get-url origin)
# Parse owner and repo from URL (handles both SSH and HTTPS)
OWNER=$(echo "$REMOTE_URL" | sed -E 's#.*[:/]([^/]+)/[^/]+\.git$#\1#')
REPO=$(echo "$REMOTE_URL" | sed -E 's#.*/([^/]+)\.git$#\1#')
```
5. **Ensure we're on the PR branch**:
```bash
CURRENT_BRANCH=$(git branch --show-current)
# Verify this branch corresponds to the PR
```
6. **Respond with**:
```
Addressing PR review comments for PR #{PR_NUMBER}...
Repository: {OWNER}/{REPO}
Branch: {CURRENT_BRANCH}
Gitea URL: {GITEA_URL}
```
## PR Detection
If no PR number is provided, detect from the current branch:
```bash
# Set environment variables
GITEA_URL="https://git.johnogle.info"
TOKEN="<your-token>"
OWNER="<repo-owner>"
REPO="<repo-name>"
PR_NUMBER="<pr-number>"
CURRENT_BRANCH=$(git branch --show-current)
tea pr list --fields index,head --output simple | grep "$CURRENT_BRANCH"
```
# Get all reviews for the PR
If no PR exists for the current branch, use `AskUserQuestion`:
```
No PR found for branch '{CURRENT_BRANCH}'.
Would you like to:
1. Enter a PR number manually
2. Cancel
```
## Workflow
### Step 1: Fetch and Parse Comments
Fetch all reviews and their comments:
```bash
# Fetch reviews (filter out dismissed reviews)
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | jq
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" \
| jq '[.[] | select(.dismissed != true)]'
# Get comments for a specific review
REVIEW_ID="<review-id>"
# For each review, fetch comments
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | jq
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments"
```
### 2. View All Review Comments (Combined)
**Filter resolved comments**: When processing comments, skip any that have been marked as resolved. Check the `resolver` field in the comment response - if it's not null, the comment has been resolved and should be skipped.
```bash
# Get all reviews and their comments in one view
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | \
jq -r '.[] | "Review \(.id) by \(.user.login): \(.state)\n Body: \(.body)"'
# For each review, show inline comments
for REVIEW_ID in $(curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | jq -r '.[].id'); do
echo "=== Review $REVIEW_ID comments ==="
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | \
jq -r '.[] | "[\(.path):\(.line)] \(.body)"'
done
# Example: Filter to only unresolved comments
jq '[.[] | select(.resolver == null)]'
```
### 3. Reply to Review Comments (Web Endpoint Method)
The Gitea REST API does not support replying to review comment threads. The web UI uses a different endpoint:
If no reviews found or all comments are resolved:
```
POST /{owner}/{repo}/pulls/{pr_number}/files/reviews/comments
Content-Type: multipart/form-data
No unresolved reviews found for PR #{PR_NUMBER}.
Nothing to address.
```
**STOP** here.
### Step 2: Categorize Comments
For each comment, categorize it as one of:
| Category | Description | Action |
|----------|-------------|--------|
| **actionable** | Requests a code change, addition, or fix | Launch subagent to make change |
| **question** | Asks for clarification or explanation | Include answer in summary |
| **acknowledged** | FYI, self-resolved, or "no action needed" noted | Note in summary |
| **blocked** | Requires external input or is out of scope | Flag for user |
**Categorization heuristics**:
- Contains "add", "change", "fix", "update", "consider adding", "should be" → **actionable**
- Contains "?" or "why", "how", "what" → **question**
- Contains "no need to update", "will be separate", "acknowledged" → **acknowledged**
- Contains "discuss", "later", "out of scope", "blocked by" → **blocked**
Display the categorization:
```
## Comment Analysis
### Actionable (will make changes):
1. {file}:{line} - "{comment_summary}" → Will add nix note to prerequisites
### Questions (will answer in summary):
2. {file}:{line} - "{comment_summary}" → Explain CI token approach
### Acknowledged (no action needed):
3. {file}:{line} - "{comment_summary}" → Reviewer noted separate skill
### Blocked (needs input):
(none)
```
**Required form fields:**
- `reply`: Review ID to reply to
- `content`: The reply message
- `path`: File path
- `line`: Line number
- `side`: `proposed` or `original`
- `single_review`: `true`
- `origin`: `timeline`
- `_csrf`: CSRF token (required for web endpoint)
### Step 3: User Confirmation
**Authentication Challenge:**
This endpoint requires session-based authentication, not API tokens. Options:
Use `AskUserQuestion` to confirm the plan:
#### Option A: Use Browser Session (Recommended)
```
I've categorized {N} comments. My plan:
1. Log in to Gitea in your browser
2. Open browser developer tools and copy cookies
3. Use the session cookies with curl
**Will make changes for:**
- {file}:{line}: {planned_change}
**Will explain in summary:**
- {file}:{line}: {planned_explanation}
**No action needed:**
- {file}:{line}: {reason}
Proceed with this plan?
```
Options:
1. **Proceed** - Execute the plan
2. **Modify** - Let user adjust categorization
3. **Cancel** - Exit without changes
### Step 4: Address Actionable Comments (Parallel Subagents)
For each actionable comment, launch a subagent using the Task tool:
```
Launch Task subagent with:
- subagent_type: "general-purpose"
- prompt: |
You are addressing a PR review comment. Make the requested change and nothing else.
**File**: {file_path}
**Line**: {line_number}
**Comment**: {comment_body}
**Diff context**:
```
{diff_hunk}
```
Instructions:
1. Read the file to understand context
2. Make the minimal change to address the comment
3. Do NOT commit - just make the edit
4. Report what you changed
Be precise. Only change what's needed to address this specific comment.
```
**Important**: Launch actionable comment subagents in parallel when they touch different files. For comments on the same file, run sequentially to avoid conflicts.
Wait for all subagents to complete and collect their results.
### Step 5: Commit and Push
After all subagents complete:
1. **Stage changes**:
```bash
git add -A
```
2. **Create commit with summary**:
```bash
git commit -m "Address PR review comments
Changes made:
- {file1}: {change_summary}
- {file2}: {change_summary}
Addresses comments from review by {reviewer}"
```
3. **Push to remote**:
```bash
git push
```
### Step 6: Post Summary Comment
Post a single comment summarizing all actions taken:
```bash
# First, get CSRF token from the PR page
CSRF=$(curl -s -c cookies.txt -b cookies.txt \
"$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files" | \
grep -oP 'name="_csrf" value="\K[^"]+')
tea comment $PR_NUMBER "$(cat <<'EOF'
## Review Comments Addressed
# Post the reply
curl -s -b cookies.txt \
-F "reply=$REVIEW_ID" \
-F "content=Your reply message here" \
-F "path=$FILE_PATH" \
-F "line=$LINE_NUMBER" \
-F "side=proposed" \
-F "single_review=true" \
-F "origin=timeline" \
-F "_csrf=$CSRF" \
"$GITEA_URL/$OWNER/$REPO/pulls/$PR_NUMBER/files/reviews/comments"
cc @{reviewer1} @{reviewer2}
**Changes made** (commit {SHORT_SHA}):
- `{file1}:{line}`: {what_was_changed}
- `{file2}:{line}`: {what_was_changed}
**Responses to questions**:
- `{file3}:{line}`: {answer_to_question}
**Acknowledged** (no action needed):
- `{file4}:{line}`: {reason_no_action}
---
*Automated response via /gitea_pr_review*
EOF
)"
```
#### Option B: Create Top-Level Comment (Fallback)
### Step 7: Final Summary
If thread replies are not critical, use the API to create a top-level comment:
Display to user:
```bash
# Create a top-level comment mentioning the review context
curl -s -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"body\": \"Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER\n\nYour reply here\"}" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/issues/$PR_NUMBER/comments"
```
## PR Review Complete
**Commit**: {SHA}
**Changes**: {N} files modified
### Actions Taken:
- [x] {file1}:{line} - Added nix prerequisite note
- [x] {file2}:{line} - Explained CI approach in comment
- [ ] {file3}:{line} - Acknowledged (separate skill)
**Reviewers tagged**: @{reviewer1}, @{reviewer2}
**Comment posted**: {comment_url}
PR URL: {GITEA_URL}/{OWNER}/{REPO}/pulls/{PR_NUMBER}
```
Or use tea CLI:
```bash
tea comment $PR_NUMBER "Re: @reviewer's comment on $FILE_PATH:$LINE_NUMBER
**Note**: When posting the summary comment, tag all reviewers who left comments so they receive notifications about the changes.
Your reply here"
## Error Handling
### Subagent failed to make change
If a subagent fails:
```
Warning: Could not address comment on {file}:{line}
Reason: {error}
Options:
1. Skip this comment and continue
2. Retry with manual guidance
3. Abort all changes
```
### 4. Submit a New Review
### Push failed
Create a new review with inline comments:
```
Error pushing changes: {error}
```bash
curl -s -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"body": "Overall review comments",
"event": "COMMENT",
"comments": [
{
"path": "path/to/file.py",
"body": "Comment on this line",
"new_position": 10
}
]
}' \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews"
Your changes are committed locally. You may need to:
1. Pull and resolve conflicts: git pull --rebase
2. Push again: git push
```
Event types: `COMMENT`, `APPROVE`, `REQUEST_CHANGES`
### No actionable comments
## Workflow Example
### Reading and Responding to Reviews
1. **Set up environment**:
```bash
export GITEA_URL=$(yq -r '.logins[] | select(.name == "default") | .url' ~/.config/tea/config.yml)
export TOKEN=$(yq -r '.logins[] | select(.name == "default") | .token' ~/.config/tea/config.yml)
export OWNER="johno"
export REPO="nixos-configs"
export PR_NUMBER="5"
If all comments are questions/acknowledged:
```
No code changes needed.
2. **List all pending review comments**:
```bash
# Get reviews
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" | \
jq -r '.[] | select(.state == "REQUEST_CHANGES" or .state == "COMMENT") |
"Review \(.id) by \(.user.login) (\(.state)):\n\(.body)\n"'
```
3. **Get detailed comments for a review**:
```bash
REVIEW_ID="2"
curl -s -H "Authorization: token $TOKEN" \
"$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews/$REVIEW_ID/comments" | \
jq -r '.[] | "File: \(.path):\(.line)\nComment: \(.body)\nID: \(.id)\n---"'
```
4. **Respond using top-level comment** (most reliable):
```bash
tea comment $PR_NUMBER "Addressing review feedback:
- File \`path/to/file.py\` line 10: Fixed the issue by...
- File \`other/file.py\` line 25: Updated as suggested..."
All comments are either questions or acknowledged items.
Posting summary comment with explanations...
```
## API Reference
### Endpoints
### Endpoints Used
| Action | Method | Endpoint |
|--------|--------|----------|
| List reviews | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` |
| Get review | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` |
| Get review comments | GET | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments` |
| Create review | POST | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews` |
| Submit review | POST | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` |
| Delete review | DELETE | `/api/v1/repos/{owner}/{repo}/pulls/{index}/reviews/{id}` |
| Create issue comment | POST | `/api/v1/repos/{owner}/{repo}/issues/{index}/comments` |
| Create issue comment | POST | via `tea comment` |
### Review States
@@ -220,25 +353,120 @@ Event types: `COMMENT`, `APPROVE`, `REQUEST_CHANGES`
- `APPROVE` - Approving the changes
- `REQUEST_CHANGES` - Requesting changes before merge
## Shell Command Patterns
Claude Code's bash execution has quirks. Use these patterns for reliability:
### curl requests
**DO** - Use single quotes for URL and header separately:
```bash
curl -s 'https://git.example.com/api/v1/repos/owner/repo/pulls/1/reviews' \
-H 'Authorization: token YOUR_TOKEN_HERE' | jq .
```
**DON'T** - Variable expansion in `-H` flag often fails:
```bash
# This may fail with "blank argument" errors
curl -s -H "Authorization: token $TOKEN" "$URL"
```
### Iterating over reviews
**DO** - Run separate commands for each review ID:
```bash
echo "=== Review 4 ===" && curl -s 'URL/reviews/4/comments' -H 'Authorization: token ...' | jq .
echo "=== Review 5 ===" && curl -s 'URL/reviews/5/comments' -H 'Authorization: token ...' | jq .
```
**DON'T** - For loops with multiline bodies often fail:
```bash
# This may cause syntax errors
for id in 4 5 6; do
curl -s "URL/reviews/$id/comments"
done
```
### tea comment
**DO** - Use single-quoted string for comment body:
```bash
tea comment 26 '## Summary
Changes made:
- Item 1
- Item 2'
```
**DON'T** - Heredocs may hang or timeout:
```bash
# This may hang indefinitely
tea comment 26 "$(cat <<'EOF'
...
EOF
)"
```
## Limitations
1. **Thread replies**: The Gitea REST API does not support replying directly to review comment threads. This is a known limitation. Workarounds:
- Use top-level comments with context
- Use the web UI manually for thread replies
- Implement session-based authentication to use the web endpoint
1. **Thread replies**: Gitea API doesn't support inline thread replies. We post a single summary comment instead.
2. **CSRF tokens**: The web endpoint for thread replies requires CSRF tokens, which expire and need to be fetched from the page.
2. **Complex changes**: For comments requiring significant refactoring, the subagent may need guidance. The skill will flag these as "blocked" for user input.
3. **Session auth**: API tokens work for REST API but not for web endpoints that require session cookies.
3. **Merge conflicts**: If the branch is behind, you may need to rebase before changes can be pushed.
## Tips
## Example Session
- Always quote file paths and line numbers when responding via top-level comments
- Use `tea pr view $PR_NUMBER --comments` to see all comments
- Use `tea open pulls/$PR_NUMBER` to open the PR in browser for manual thread replies
- Consider using `tea pr approve $PR_NUMBER` after addressing all comments
```
User: /gitea_pr_review 26
Assistant: Addressing PR review comments for PR #26...
Repository: johno/nixos-configs
Branch: bead/nixos-configs-vru
Gitea URL: https://git.johnogle.info
## Comment Analysis
### Actionable (will make changes):
1. gitea_pr_review.md:12 - "could we indicate nix+nixpkgs satisfies this?"
→ Will add note that nix users can get tools via nixpkgs
### Questions (will answer in summary):
(none)
### Acknowledged (no action needed):
2. gitea_pr_review.md:50 - "we eventually want to run this in CI..."
→ Reviewer noted this will be a separate skill
Proceed with this plan? [Proceed]
Launching subagent to address comment 1...
[Subagent completes edit]
Committing changes...
[abc1234] Address PR review comments
Pushing to remote...
Done.
Posting summary comment...
## PR Review Complete
**Commit**: abc1234
**Changes**: 1 file modified
### Actions Taken:
- [x] gitea_pr_review.md:12 - Added nix prerequisite note
- [ ] gitea_pr_review.md:50 - Acknowledged (separate skill)
**Comment posted**: https://git.johnogle.info/.../pulls/26#issuecomment-XXX
PR URL: https://git.johnogle.info/johno/nixos-configs/pulls/26
```
## See Also
- Gitea API Documentation: https://docs.gitea.com/api/1.20/
- `tea` CLI: https://gitea.com/gitea/tea
- Gitea API: https://docs.gitea.com/api/
- `/beads_workflow` for full development workflow

View File

@@ -68,10 +68,12 @@ Work on bead [BEAD_ID]: [BEAD_TITLE]
- Read the plan and find the "Automated Verification" section
- Extract each verification command (lines starting with `- [ ]` followed by a command)
- Example: `- [ ] Tests pass: \`make test\`` → extract `make test`
- Note any "Per CONTRIBUTING.md:" requirements for additional validation
- If no plan exists, use best-effort validation:
- Check if `Makefile` exists → try `make test` and `make lint`
- Check if `flake.nix` exists → try `nix flake check`
- Check if `package.json` exists → try `npm test`
- **Check for CONTRIBUTING.md** → read and extract testing/linting requirements
- If none found, note "No validation criteria found"
4. **Implement the changes**:

View File

@@ -4,12 +4,13 @@ description: Reconcile beads with merged PRs and close completed beads
# Reconcile Beads Workflow
This skill reconciles beads that are in `in_review` status with their corresponding PRs. If a PR has been merged, the bead is closed.
This skill reconciles beads that are in `in_review` status with their corresponding PRs. If a PR has been merged, the bead is closed and any linked Gitea issue is also closed.
## Prerequisites
- Custom status `in_review` must be configured: `bd config set status.custom "in_review"`
- Beads in `in_review` status should have a PR URL in their notes
- `tea` CLI must be configured for closing Gitea issues
## Workflow
@@ -52,6 +53,34 @@ If the PR is merged:
bd close [BEAD_ID] --reason="PR merged: [PR_URL]"
```
### Step 3.1: Close corresponding Gitea issue (if any)
After closing a bead, check if it has a linked Gitea issue:
1. **Check for Gitea issue URL in bead notes**:
Look for the pattern `Gitea issue: <URL>` in the notes. Extract the URL.
2. **Extract issue number from URL**:
```bash
# Example: https://git.johnogle.info/johno/nixos-configs/issues/16 -> 16
echo "$GITEA_URL" | grep -oP '/issues/\K\d+'
```
3. **Close the Gitea issue**:
```bash
tea issues close [ISSUE_NUMBER]
```
4. **Handle errors gracefully**:
- If issue is already closed: Log warning, continue
- If issue not found: Log warning, continue
- If `tea` fails: Log error, continue with other beads
Example warning output:
```
Warning: Could not close Gitea issue #16: issue already closed
```
### Step 4: Report summary
Present results:
@@ -60,10 +89,17 @@ Present results:
## Beads Reconciliation Summary
### Closed (PR Merged)
| Bead | PR | Title |
|------|-----|-------|
| beads-abc | #123 | Feature X |
| beads-xyz | #456 | Bug fix Y |
| Bead | PR | Gitea Issue | Title |
|------|-----|-------------|-------|
| beads-abc | #123 | #16 closed | Feature X |
| beads-xyz | #456 | (none) | Bug fix Y |
### Gitea Issues Closed
| Issue | Bead | Status |
|-------|------|--------|
| #16 | beads-abc | Closed successfully |
| #17 | beads-def | Already closed (skipped) |
| #99 | beads-ghi | Error: issue not found |
### Still in Review
| Bead | PR | Status | Title |
@@ -80,9 +116,14 @@ Present results:
- **Missing PR URL**: Skip the bead and report it
- **PR not found**: Report the error but continue with other beads
- **API errors**: Report and continue
- **Gitea issue already closed**: Log warning, continue (not an error)
- **Gitea issue not found**: Log warning, continue (issue may have been deleted)
- **No Gitea issue linked**: Normal case, no action needed
- **tea command fails**: Log error with output, continue with other beads
## Notes
- This skill complements `/parallel_beads` which sets beads to `in_review` status
- Run this skill periodically or after merging PRs to keep beads in sync
- Beads with closed (but not merged) PRs are not automatically closed - they may need rework
- Gitea issues are only closed for beads that have a `Gitea issue: <URL>` in their notes

View File

@@ -225,11 +225,16 @@
mu4e-headers-time-format "%H:%M")
;; Sending mail via msmtp
(setq message-send-mail-function 'message-send-mail-with-sendmail
sendmail-program (executable-find "msmtp")
message-sendmail-envelope-from 'header
mail-envelope-from 'header
mail-specify-envelope-from t))
;; NOTE: message-sendmail-f-is-evil and --read-envelope-from are required
;; to prevent msmtp from stripping the email body when processing headers.
;; Without these, multipart messages (especially from org-msg) may arrive
;; with empty bodies.
(setq sendmail-program (executable-find "msmtp")
send-mail-function #'message-send-mail-with-sendmail
message-send-mail-function #'message-send-mail-with-sendmail
message-sendmail-f-is-evil t
message-sendmail-extra-arguments '("--read-envelope-from")
message-sendmail-envelope-from 'header))
;; Whenever you reconfigure a package, make sure to wrap your config in an
;; `after!' block, otherwise Doom's defaults may override your settings. E.g.

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.email;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.email = {
@@ -89,8 +90,9 @@ in
account default : proton
'';
# Systemd service for mail sync
systemd.user.services.mbsync = {
# Linux-only: Systemd service for mail sync (Darwin uses launchd instead)
systemd.user.services = mkIf isLinux {
mbsync = {
Unit = {
Description = "Mailbox synchronization service";
After = [ "network-online.target" ];
@@ -104,9 +106,11 @@ in
StandardError = "journal";
};
};
};
# Systemd timer for automatic sync
systemd.user.timers.mbsync = {
# Linux-only: Systemd timer for automatic sync
systemd.user.timers = mkIf isLinux {
mbsync = {
Unit = {
Description = "Mailbox synchronization timer";
};
@@ -120,4 +124,5 @@ in
};
};
};
};
}

View File

@@ -4,13 +4,15 @@ with lib;
let
cfg = config.home.roles.kdeconnect;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.kdeconnect = {
enable = mkEnableOption "Enable KDE Connect for device integration";
};
config = mkIf cfg.enable {
# KDE Connect services are Linux-only (requires D-Bus and systemd)
config = mkIf (cfg.enable && isLinux) {
services.kdeconnect = {
enable = true;
indicator = true;

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.sync;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.sync = {
@@ -11,9 +12,10 @@ in
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Linux-only: syncthingtray requires system tray support
home.packages = optionals isLinux (with pkgs; [
syncthingtray
];
]);
services.syncthing = {
enable = true;

View File

@@ -1,56 +0,0 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
# NixOS-WSL specific options are documented on the NixOS-WSL repository:
# https://github.com/nix-community/NixOS-WSL
{ config, lib, pkgs, ... }:
{
imports = [
];
roles = {
audio.enable = true;
desktop = {
enable = true;
wayland = true;
};
nvidia = {
enable = true;
package = "latest";
graphics.extraPackages = with pkgs; [
mesa
libvdpau-va-gl
libva-vdpau-driver
];
};
users.enable = true;
};
networking.hostName = "wixos";
wsl.enable = true;
wsl.defaultUser = "johno";
wsl.startMenuLaunchers = true;
wsl.useWindowsDriver = true;
wsl.wslConf.network.hostname = "wixos";
wsl.wslConf.user.default = "johno";
# WSL-specific environment variables for graphics
environment.sessionVariables = {
LD_LIBRARY_PATH = [
"/usr/lib/wsl/lib"
"/run/opengl-driver/lib"
];
};
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. It's perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "24.05"; # Did you read the comment?
}

View File

@@ -21,6 +21,8 @@ in
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};

View File

@@ -8,6 +8,7 @@
environment.systemPackages = with pkgs; [
git
glances
ghostty.terminfo # So tmux works when SSH'ing from ghostty
pciutils
tree
usbutils

View File

@@ -8,6 +8,21 @@ in
{
options.roles.nfs-mounts = {
enable = mkEnableOption "Enable default NFS mounts";
server = mkOption {
type = types.str;
default = "10.0.0.43";
description = "IP address or hostname of the NFS server";
};
remotePath = mkOption {
type = types.str;
default = "/media";
description = "Remote path to mount from the NFS server";
};
mountPoint = mkOption {
type = types.str;
default = "/media";
description = "Local mount point for the NFS share";
};
# TODO: implement requireMount
requireMount = mkOption {
type = types.bool;
@@ -18,8 +33,8 @@ in
config = mkIf cfg.enable
{
fileSystems."/media" = {
device = "10.0.0.43:/media";
fileSystems.${cfg.mountPoint} = {
device = "${cfg.server}:${cfg.remotePath}";
fsType = "nfs";
options = [
"defaults"

View File

@@ -8,6 +8,21 @@ in
{
options.roles.printing = {
enable = mkEnableOption "Enable default printing setup";
printerName = mkOption {
type = types.str;
default = "MFC-L8900CDW_series";
description = "Name for the default printer";
};
printerUri = mkOption {
type = types.str;
default = "ipp://brother.oglehome/ipp/print";
description = "Device URI for the default printer (e.g., ipp://hostname/ipp/print)";
};
printerModel = mkOption {
type = types.str;
default = "everywhere";
description = "PPD model for the printer (use 'everywhere' for driverless IPP)";
};
};
config = mkIf cfg.enable
@@ -21,11 +36,11 @@ in
};
hardware.printers.ensurePrinters = [{
name = "MFC-L8900CDW_series";
deviceUri = "ipp://brother.oglehome/ipp/print";
model = "everywhere";
name = cfg.printerName;
deviceUri = cfg.printerUri;
model = cfg.printerModel;
}];
hardware.printers.ensureDefaultPrinter = "MFC-L8900CDW_series";
hardware.printers.ensureDefaultPrinter = cfg.printerName;
# Fix ensure-printers service to wait for network availability
systemd.services.ensure-printers = {

View File

@@ -8,6 +8,11 @@ in
{
options.roles.virtualisation = {
enable = mkEnableOption "Enable virtualisation";
dockerUsers = mkOption {
type = types.listOf types.str;
default = [ "johno" ];
description = "List of users to add to the docker group";
};
};
config = mkIf cfg.enable
@@ -15,6 +20,6 @@ in
virtualisation.libvirtd.enable = true;
programs.virt-manager.enable = true;
virtualisation.docker.enable = true;
users.extraGroups.docker.members = [ "johno" ];
users.extraGroups.docker.members = cfg.dockerUsers;
};
}

View File

@@ -1,6 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Rotate to the next wallpaper in the configured list."
echo ""
echo "This script increments the currentIndex in home/wallpapers/default.nix,"
echo "cycling through available wallpapers. Rebuild your system to apply"
echo "the new wallpaper."
echo ""
echo "Options:"
echo " --help, -h Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'

View File

@@ -1,6 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Update Doom Emacs to the latest commit from the doomemacs repository."
echo ""
echo "This script fetches the latest commit SHA from the default branch,"
echo "updates the rev and sha256 in home/roles/emacs/default.nix, and"
echo "prepares the configuration for a system rebuild."
echo ""
echo "Options:"
echo " --help, -h Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'

View File

@@ -1,6 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Perform a major upgrade of the NixOS configuration."
echo ""
echo "This script runs the following steps:"
echo " 1. Update all flake inputs (nix flake update)"
echo " 2. Update Doom Emacs to the latest commit"
echo " 3. Update Claude Code to the latest version"
echo " 4. Rotate to the next wallpaper"
echo ""
echo "After completion, review changes with 'git diff' and rebuild"
echo "your system with 'sudo nixos-rebuild switch --flake .'"
echo ""
echo "Options:"
echo " --help, -h Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'