Compare commits

...

43 Commits

Author SHA1 Message Date
1692a34f6e Merge pull request 'feat: enable orchestrator + switch TTS to Edge (Katja voice)' (#8) from feature/orchestrator-edge-tts into master
Reviewed-on: #8
2026-05-01 16:15:13 +02:00
2403e54039 feat: enable orchestrator + switch TTS to Edge (Seraphina voice)
- Enable delegation.orchestrator_enabled with max_spawn_depth=2
- Switch TTS from ElevenLabs (paid) to Edge TTS (free)
- Voice: de-DE-SeraphinaMultilingualNeural — friendly, multilingual German female
- No API key required
2026-05-01 16:06:49 +02:00
m3tm3re
3e8c95944c chore: hermes update 2026-05-01 12:06:23 +02:00
fbc555feeb feat: pi guardrails 2026-04-29 20:14:07 +02:00
6a5d8f0011 feat(agents): add strict security hardening for Pi and OpenCode
Pi Guardrails:
- Enables @aliou/pi-guardrails with strict default config
- Sets onboarding.completed = true to skip onboarding prompt
- Enables pathAccess in ask mode for /nix/store and /tmp
- Adds noAccess policies for: SSH keys, GPG keys, AWS config,
  Kubernetes config, cloud CLI configs (gh/gcloud/1password/sops),
  agenix secrets, Pi auth/sessions, env files, private keys/certs
- Adds auto-deny patterns for env leakage commands:
  env, printenv, /proc/*/environ, GPG secret exports,
  ssh-add -D, password manager reads

OpenCode permissions:
- Adds permission section with global security rules
- external_directory: ask by default, allow /nix/store and /tmp
- read/edit: allow by default, deny SSH/GPG/AWS/Kube/cloud configs,
  agenix secrets, Pi auth/sessions, env files, private keys/certs
- glob: restrict sensitive path patterns
- grep: deny SSH/GPG/agenix, ask for PASSWORD/SECRET/API_KEY/PRIVATE_KEY
- bash: ask by default, allow safe git/nix commands,
  deny env/printenv/proc/GPG secret/sudo/ssh-add deletion/curl|sh
- webfetch: ask by default, allow github/nixos search
- doom_loop: ask
2026-04-29 19:48:29 +02:00
9c3d10836f Merge pull request 'fix: add uv to hermes-agent service PATH' (#7) from fix/hermes-agent-uv-path into master
Reviewed-on: #7
2026-04-29 16:24:17 +02:00
a615ab61e8 fix: add uv to hermes-agent service PATH
Add pkgs.uv to systemd.services.hermes-agent.path so that CronJobs
and terminal sessions can execute PEP 723 scripts via 'uv run'
(e.g. garmin-daily.py for Garmin Connect health data).

Also adds uv to environment.systemPackages for general availability.
2026-04-29 16:18:41 +02:00
193b8c0115 fix(git-identity): use existing gitea SSH key for agent commits
The m3ta-chiron SSH key was not accepted by Gitea.
Using the existing gitea key instead for push authentication.
2026-04-27 19:52:11 +02:00
m3tm3re
f76c4dd5d4 chore: smlink pip to uv pip 2026-04-27 19:36:52 +02:00
m3tm3re
05dc6bf608 chore: smlink pip to uv pip 2026-04-27 19:07:26 +02:00
d524864fc3 Merge pull request 'feature/agent-git-identity' (#6) from feature/agent-git-identity into master
Reviewed-on: #6
2026-04-27 17:55:06 +02:00
m3tm3re
09e2ba8538 chore: AGENTS + nixpkgs input urls 2026-04-27 17:53:08 +02:00
m3tm3re
a427f319d4 feat(agents): add gitIdentity config and git-identity rule
- coding.agents.gitIdentity enabled with m3ta-chiron identity
- coding.agents.pi.codingRules.concerns includes 'git-identity'
- Uses feature/agent-git-identity branches for m3ta-nixpkgs and agents
2026-04-27 13:24:34 +02:00
m3tm3re
936eb13794 feat: add global skills to hermes environment 2026-04-26 15:14:54 +02:00
m3tm3re
5b0e6cbd5d feat(hermes-agent): add copy-hermes-skills systemd service 2026-04-26 14:37:43 +02:00
m3tm3re
2302810d11 chore: update beads issue state and gitignore docs/plans 2026-04-26 14:35:38 +02:00
m3tm3re
25ac47a422 feat(hermes-agent): add mkOpencodeSkills integration for skills provisioning
- Add inputs parameter to module signature for flake input access
- Define hermesSkills via inputs.agents.lib.mkOpencodeSkills
- Includes customSkills from agents flake and external skills:
  - skills-basecamp (basecamp/basecamp-cli)
  - skills-anthropic (anthropics/skills)
  - skills-kestra (kestra-io/agent-skills)
- Verified with nixos-rebuild dry-run --flake .#m3-hermes (no errors)
2026-04-26 14:35:06 +02:00
m3tm3re
e6cfcc346b docs(agents): expand Beads workflow documentation
- Add 6-step core workflow with examples
- Document slash commands for agent integration
- Add 'Why Beads?' section emphasizing persistence
- Note to avoid bd edit in agent contexts
- Include dependency linking examples
2026-04-26 14:12:30 +02:00
m3tm3re
09bc9da6d9 chore: complete AGENTS.md documentation
- Add comprehensive project documentation to AGENTS.md
- Remove stale docs from docs/ directory
- Update agent configs (agents.nix, pi.nix)
- Update python.nix language config
- Update .gitignore
2026-04-26 14:10:54 +02:00
m3tm3re
eb06533174 Merge feature/home-profile-restructuring: home-manager profile refactoring
Refactor home-manager configuration structure:
- Reorganize from features/ to base/coding/desktop/server/profiles/
- Add language runtime modules (go, js, python, rust, typescript)
- Add LSP server configuration
- Add gaming and media profiles
- Add shell modules (fish, nushell, starship)
- Consolidate editor and git configuration
2026-04-26 13:53:00 +02:00
m3tm3re
0d81b0e5e9 chore: add beads issue tracker configuration 2026-04-26 13:49:23 +02:00
m3tm3re
0ea8b8d2eb feat(home): extract CLI tools into modular home/base structure
- Add individual modules for: bat, carapace, direnv, eza, fzf, lf, nitch,
  television, zellij, zellij-ps, zoxide
- Centralize in home/base/cli-tools/ with default.nix aggregator
- Simplify home/base/packages by removing extracted tools
2026-04-26 13:49:17 +02:00
m3tm3re
30a9a23de2 refactor: add language runtimes module and cleanup agent config
- Add home/coding/languages/ with Python, JavaScript, Rust, Go, TypeScript
- Move bun/nodejs from agents.nix to languages/javascript.nix
- Move python3 with packages to languages/python.nix
- Move npm config to javascript.nix (broader context)
- Add language options to m3-ares and m3-kratos host configs
- Move pyrefly from agents.nix to lsp/servers.nix
- Remove duplicate python3 reference (build conflict fix)
- Remove unused base/secrets/cli-tools/ duplicates
2026-04-26 13:20:22 +02:00
m3tm3re
6d0149ee6e feat: add AMD GPU tools, media packages, and productivity module
Task 3 - Gaming profile:
- Add gpu.nix with ROCm runtime/smi/info and vulkan-tools
- Import gpu.nix in gaming profile aggregator

Task 4 - Media profile:
- Add unimatrix to yt-dlp.nix packages
- (plexamp, webcord, mpv config were already present)

Task 5 - Desktop apps:
- Add productivity.nix with pomodoro-timer
- Import productivity.nix in desktop apps aggregator
2026-04-26 12:32:47 +02:00
m3tm3re
d19b87f8cd feat: add coding packages module (bruno, insomnia) 2026-04-26 12:29:14 +02:00
m3tm3re
8f5d076d7b fix: make base modules enabled by default; document lazylib→lazygit
- All base/* modules now use (mkEnableOption "...") // { default = true; }
  so they activate automatically when imported — no explicit .enable = true
  required in host configs
- packages.nix: add comment documenting that lazylib does not exist in
  nixpkgs; lazygit is the correct and intended package
- zellij-ps.nix: clarify that cli.zellij-ps namespace is intentional —
  it is the home-manager module convention from m3ta-nixpkgs
- nix flake check passes (warnings are pre-existing)
2026-04-26 12:16:44 +02:00
m3tm3re
3c9a107608 feat: add missing packages and programs to base cli-tools
- packages.nix: essential packages (jq, ripgrep, fd, htop, coreutils,
  lazygit, httpie, just, devenv, gcc, go, sqlite, sqlite-vec, nix-index,
  nix-update, progress, comma, fabric-ai, llm, basecamp, hyprpaper-random,
  libnotify, trash-cli, unzip, zip, yazi)
- bat.nix: bat with nix-colors derived syntax theme
- carapace.nix: multi-shell completion (fish, nushell, bash)
- direnv.nix: automatic env loading with nix-direnv
- eza.nix: modern ls with icons, git status, long format
- lf.nix: terminal file manager with bat preview
- zoxide.nix: smarter cd with fish and nushell integration
- zellij-ps.nix: project session manager wrapping cli.zellij-ps
2026-04-26 12:06:36 +02:00
m3tm3re
cc01c1d0aa fix(agents): make videoDrivers optional with safe default
For standalone Home Manager evaluation where videoDrivers may be absent
2026-04-26 11:37:17 +02:00
m3tm3re
d59a6b82b6 chore: remove features.old archive and format all files
- Delete home/features.old/ (archived old flat feature modules)
- All content migrated to new profile-based structure
- Run alejandra formatter over 13 changed files
- nix flake check passes cleanly
2026-04-26 11:29:49 +02:00
m3tm3re
d44bdad73a refactor: archive old features directory to features.old
The new profile-based structure (home/base, home/desktop, home/server,
home/profiles/, home/coding) is fully operational and imported via
home/lib/mkHomeConfig. The legacy home/features directory is no longer
referenced anywhere in the configuration.

Archived rather than deleted to preserve history for reference.
2026-04-26 11:22:17 +02:00
m3tm3re
797ffb2b8a fix: assert unknown profiles in mkHomeConfig; move agent modules to coding/agents
- home/lib/default.nix: add assertion for unknown profile names instead of
  silently filtering them out; remove unused 'inherit (lib) optional'
- home/coding/agents/{opencode,pi}.nix: moved from home/features/coding/
  to co-locate with agents.nix (eliminating cross-directory back-references)
- home/coding/agents/agents.nix: update imports to ./opencode.nix and ./pi.nix
- home/features/coding/: remove now-dead default.nix (nothing imported it)
2026-04-26 11:17:03 +02:00
m3tm3re
73bd2b1f2e fix: spec review - add missing fish module to base/shell
- Create home/base/shell/fish.nix
- Add to base/shell/default.nix imports
- Migrate remaining hosts from features.cli.fish to base.shell.fish
2026-04-26 11:09:50 +02:00
m3tm3re
f3749c5679 feat: implement profile system with mkHomeConfig and context constraints
- Add home/lib/default.nix with mkHomeConfig utility
  - Loads base + common modules always
  - Maps profiles (coding, gaming, media) to module imports
  - Enforces desktop/server mutual exclusion via assertion
  - Context must be 'desktop', 'server', or null

- Migrate all per-host home configs to new profile system
  - m3-ares: context=desktop, profiles=[coding, gaming, media]
  - m3-kratos: context=desktop, profiles=[coding, gaming, media]
  - m3-atlas: context=server, profiles=[coding]
  - m3-helios: context=server, profiles=[]
  - m3-hermes: context=server, profiles=[]
  - m3-aether: context=server, profiles=[]
  - m3-daedalus: context=desktop, profiles=[coding, media]

- Replace features.* options with new namespaces:
  - features.cli.* -> base.shell.* / base.cliTools.* / base.secrets
  - features.desktop.* -> desktop.wm.* / desktop.apps.* / desktop.theme.*
  - gaming/media moved to profiles.gaming.* / profiles.media.*

- Fix home/coding/editor/neovim.nix: remove duplicate option declaration
  (coding.editors.neovim.enable already declared by m3ta-nixpkgs)

- Fix home/coding/lsp/servers.nix: replace removed nodePackages.typescript-language-server
  with typescript-language-server

- Fix home/desktop/theme/wallpapers.nix: correct relative path
  (was ../../.. which resolved to project root, should be ../..)
2026-04-26 11:03:43 +02:00
m3tm3re
9908b9e335 fix: code review fixes
- Fix hardcoded user path in webapps.nix (use homeDirectory)
- Normalize wallpapers option to use .enable suffix
- Remove duplicate FZF keybind declaration
- Update comments to match actual implementation
2026-04-26 10:49:01 +02:00
m3tm3re
06b430e067 fix: code review fixes
- Fix hardcoded user path in webapps.nix (use homeDirectory)
- Normalize wallpapers option to use .enable suffix
- Remove duplicate FZF keybind declaration
- Update comments to match actual implementation
2026-04-26 10:48:52 +02:00
m3tm3re
1b5bcae686 feat: create new home/ directory structure for profile-based config
New structure:
- home/base/        - Always loaded (shell, cli-tools, secrets)
- home/coding/      - Profile-independent dev tooling (editor, lsp, git, agents)
- home/profiles/    - Freely combinable profiles (gaming, media)
- home/desktop/     - Desktop-only (wm, apps, theme)
- home/server/      - Minimal server stub

Migration sources:
- home/features/cli/ → home/base/{shell,cli-tools,secrets}
- home/features/desktop/hyprland,wayland,rofi → home/desktop/wm/
- home/features/desktop/obsidian,office,webapps,crypto → home/desktop/apps/
- home/features/desktop/fonts,theme,wallpapers → home/desktop/theme/
- gaming.nix split → home/profiles/gaming/{steam,gamescope}
- media.nix split  → home/profiles/media/{obs,ffmpeg,yt-dlp,kdenlive,handbrake}

Option namespaces updated:
- features.cli.*  → base.shell.* / base.cliTools.* / base.secrets
- features.desktop.* → desktop.wm.* / desktop.apps.* / desktop.theme.*
- features.desktop.gaming → profiles.gaming.*
- features.desktop.media  → profiles.media.*

Verified: nix flake check passes (warnings only)
2026-04-26 10:37:03 +02:00
m3tm3re
b1eb50a350 chore: add .worktrees to gitignore
For isolated feature development
2026-04-26 10:27:13 +02:00
m3tm3re
383f4ef56f feat: tuwunel matrix server + hermes update 2026-04-24 21:38:57 +02:00
m3tm3re
d47680aef7 chore: n8n update 2026-04-22 19:26:10 +02:00
m3tm3re
047b60a6a8 refactor: update Pi agent configuration and devShell
- Switch model provider from zai/glm-5.1 to minimax/MiniMax-M2.7
- Add coding rules for Nix language and standard concerns
- Add linting tools (statix, deadnix) to devShell
- Simplify devShell configuration
- Update AGENTS.md project rules
2026-04-22 17:59:23 +02:00
m3tm3re
382b4c8c98 chore: hermes update 2026-04-21 19:00:49 +02:00
90fbdfe346 Merge pull request 'feat: pi-agent wrapper' (#5) from feature/pi-agent-wrapper into master
Reviewed-on: #5
2026-04-14 18:51:44 +02:00
m3tm3re
7e0d60c95b feat: pi-agent wrapper 2026-04-14 18:36:55 +02:00
120 changed files with 3248 additions and 1619 deletions

73
.beads/.gitignore vendored Normal file
View File

@@ -0,0 +1,73 @@
# Dolt database (managed by Dolt, not git)
dolt/
embeddeddolt/
# Runtime files
bd.sock
bd.sock.startlock
sync-state.json
last-touched
.exclusive-lock
# Daemon runtime (lock, log, pid)
daemon.*
# Interactions log (runtime, not versioned)
interactions.jsonl
# Push state (runtime, per-machine)
push-state.json
# Lock files (various runtime locks)
*.lock
# Credential key (encryption key for federation peer auth — never commit)
.beads-credential-key
# Local version tracking (prevents upgrade notification spam after git ops)
.local_version
# Worktree redirect file (contains relative path to main repo's .beads/)
# Must not be committed as paths would be wrong in other clones
redirect
# Sync state (local-only, per-machine)
# These files are machine-specific and should not be shared across clones
.sync.lock
export-state/
export-state.json
# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned)
ephemeral.sqlite3
ephemeral.sqlite3-journal
ephemeral.sqlite3-wal
ephemeral.sqlite3-shm
# Dolt server management (auto-started by bd)
dolt-server.pid
dolt-server.log
dolt-server.lock
dolt-server.port
dolt-server.activity
# Corrupt backup directories (created by bd doctor --fix recovery)
*.corrupt.backup/
# Backup data (auto-exported JSONL, local-only)
backup/
# Per-project environment file (Dolt connection config, GH#2520)
.env
# Legacy files (from pre-Dolt versions)
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
db.sqlite
bd.db
# NOTE: Do NOT add negation patterns here.
# They would override fork protection in .git/info/exclude.
# Config files (metadata.json, config.yaml) are tracked by git by default
# since no pattern above ignores them.

81
.beads/README.md Normal file
View File

@@ -0,0 +1,81 @@
# Beads - AI-Native Issue Tracking
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
## What is Beads?
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
## Quick Start
### Essential Commands
```bash
# Create new issues
bd create "Add user authentication"
# View all issues
bd list
# View issue details
bd show <issue-id>
# Update issue status
bd update <issue-id> --claim
bd update <issue-id> --status done
# Sync with Dolt remote
bd dolt push
```
### Working with Issues
Issues in Beads are:
- **Git-native**: Stored in Dolt database with version control and branching
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
- **Branch-aware**: Issues can follow your branch workflow
- **Always in sync**: Auto-syncs with your commits
## Why Beads?
**AI-Native Design**
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs
🚀 **Developer Focused**
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way
🔧 **Git Integration**
- Automatic sync with git commits
- Branch-aware issue tracking
- Dolt-native three-way merge resolution
## Get Started with Beads
Try Beads in your own projects:
```bash
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# Initialize in your repo
bd init
# Create your first issue
bd create "Try out Beads"
```
## Learn More
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
- **Quick Start Guide**: Run `bd quickstart`
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
---
*Beads: Issue tracking that moves at the speed of thought*

56
.beads/config.yaml Normal file
View File

@@ -0,0 +1,56 @@
# Beads Configuration File
# This file configures default behavior for all bd commands in this repository
# All settings can also be set via environment variables (BD_* prefix)
# or overridden with command-line flags
# 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: ""
# Use no-db mode: JSONL-only, no Dolt database
# When true, bd will use .beads/issues.jsonl as the source of truth
# no-db: false
# Enable JSON output by default
# json: false
# Feedback title formatting for mutating commands (create/update/close/dep/edit)
# 0 = hide titles, N > 0 = truncate to N characters
# output:
# title-length: 255
# Default actor for audit trails (overridden by BEADS_ACTOR or --actor)
# actor: ""
# Export events (audit trail) to .beads/events.jsonl on each flush/sync
# When enabled, new events are appended incrementally using a high-water mark.
# Use 'bd export --events' to trigger manually regardless of this setting.
# events-export: false
# Multi-repo configuration (experimental - bd-307)
# Allows hydrating from multiple repositories and routing writes to the correct database
# repos:
# primary: "." # Primary repo (where this database lives)
# additional: # Additional repos to hydrate from (read-only)
# - ~/beads-planning # Personal planning repo
# - ~/work-planning # Work planning repo
# JSONL backup (periodic export for off-machine recovery)
# Auto-enabled when a git remote exists. Override explicitly:
# backup:
# enabled: false # Disable auto-backup entirely
# interval: 15m # Minimum time between auto-exports
# git-push: false # Disable git push (export locally only)
# git-repo: "" # Separate git repo for backups (default: project repo)
# Integration settings (access with 'bd config get/set')
# These are stored in the database, not in this file:
# - jira.url
# - jira.project
# - linear.url
# - linear.api-key
# - github.org
# - github.repo
sync.remote: "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixos-config.git"

24
.beads/hooks/post-checkout Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run post-checkout "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'post-checkout' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run post-checkout "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'post-checkout'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/post-merge Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run post-merge "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'post-merge' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run post-merge "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'post-merge'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/pre-commit Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run pre-commit "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run pre-commit "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'pre-commit'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/pre-push Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run pre-push "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run pre-push "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'pre-push'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/prepare-commit-msg Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'prepare-commit-msg' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run prepare-commit-msg "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'prepare-commit-msg'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

3
.beads/issues.jsonl Normal file
View File

@@ -0,0 +1,3 @@
{"id":"home-profile-restructuring-edz","title":"Create copy-hermes-skills systemd service","status":"closed","priority":1,"issue_type":"task","assignee":"m3tm3re","owner":"p@m3ta.dev","estimated_minutes":1,"created_at":"2026-04-26T12:30:10Z","created_by":"m3tm3re","updated_at":"2026-04-26T12:44:42Z","started_at":"2026-04-26T12:36:30Z","closed_at":"2026-04-26T12:44:42Z","close_reason":"Created systemd service in hosts/m3-hermes/services/hermes-agent.nix - copies skills to /var/lib/hermes/.agents/skills before hermes-agent starts","labels":["hermes-agent","nixos"],"dependencies":[{"issue_id":"home-profile-restructuring-edz","depends_on_id":"home-profile-restructuring-ycz","type":"blocks","created_at":"2026-04-26T14:30:57Z","created_by":"m3tm3re","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
{"id":"home-profile-restructuring-ycz","title":"Build hermes-agent skills using mkOpencodeSkills","status":"closed","priority":1,"issue_type":"task","assignee":"m3tm3re","owner":"p@m3ta.dev","estimated_minutes":2,"created_at":"2026-04-26T12:30:09Z","created_by":"m3tm3re","updated_at":"2026-04-26T12:35:15Z","started_at":"2026-04-26T12:31:35Z","closed_at":"2026-04-26T12:35:15Z","close_reason":"Added inputs to module signature and defined hermesSkills via inputs.agents.lib.mkOpencodeSkills with basecamp, anthropic, and kestra external skills. Verified with nixos-rebuild dry-run --flake .#m3-hermes (no errors).","labels":["hermes-agent","nixos"],"dependency_count":0,"dependent_count":1,"comment_count":0}
{"id":"home-profile-restructuring-cxa","title":"Verify skills available at /var/lib/hermes/.agents/skills","status":"closed","priority":2,"issue_type":"task","assignee":"m3tm3re","owner":"p@m3ta.dev","estimated_minutes":1,"created_at":"2026-04-26T12:30:10Z","created_by":"m3tm3re","updated_at":"2026-04-26T12:50:58Z","started_at":"2026-04-26T12:38:15Z","closed_at":"2026-04-26T12:50:58Z","close_reason":"Manually verified - skills are present at /var/lib/hermes/.agents/skills on m3-hermes","labels":["hermes-agent","testing"],"dependencies":[{"issue_id":"home-profile-restructuring-cxa","depends_on_id":"home-profile-restructuring-edz","type":"blocks","created_at":"2026-04-26T14:30:57Z","created_by":"m3tm3re","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}

7
.beads/metadata.json Normal file
View File

@@ -0,0 +1,7 @@
{
"database": "dolt",
"backend": "dolt",
"dolt_mode": "embedded",
"dolt_database": "home_profile_restructuring",
"project_id": "664fc7e3-94eb-4874-aab6-e47835abe9d8"
}

33
.gitignore vendored
View File

@@ -25,21 +25,22 @@ Thumbs.db
.opencode-rules
opencode.json
# Taskplane runtime artifacts (machine-specific, do not commit)
.pi/batch-state.json
.pi/batch-history.json
.pi/lane-state-*
.pi/merge-result-*
.pi/merge-request-*
.pi/worker-conversation-*
.pi/orch-logs/
.pi/orch-abort-signal
.pi/settings.json
# AI agent state
.sidecar/
.sidecar-*
.sisyphus/
.sidecar-agent
.sidecar-task
.sidecar-pr
.sidecar-start.sh
.sidecar-base
.td-root
.cache
.pi*
.worktrees/
.taskplane-tasks/
docs/plans/
# Pi project-local packages (if using pi install -l)
.pi/npm/
# Pi Lens local cache/runtime artifacts
.pi-lens/
# Beads / Dolt files (added by bd init)
.dolt/
*.db
.beads-credential-key

500
AGENTS.md
View File

@@ -1,157 +1,383 @@
# NIXOS CONFIGURATION KNOWLEDGE BASE
# Agent Instructions
**Generated:** 2025-12-31 16:13:40 UTC
**Commit:** ebc8291
**Branch:** HEAD
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
## OVERVIEW
Personal NixOS configuration managing 6 hosts (4 servers, 2 desktops) using flakes, agenix secrets, and feature-based home-manager setup.
## Quick Reference
## STRUCTURE
```
./
├── flake.nix # Main entry: host definitions, inputs, outputs
├── secrets.nix # Agenix public key mappings
├── hosts/
│ ├── common/ # Shared: base config, users, extraServices, ports
│ ├── m3-atlas/ # Server: 20+ containerized services with Traefik
│ ├── m3-helios/ # Server: AdGuard, internal routing
│ ├── m3-ares/ # Desktop: NVIDIA GPU, Btrfs
│ ├── m3-kratos/ # Desktop: AMD GPU, ZFS
│ └── m3-aether/ # Cloud VM
├── home/
│ ├── common/ # Home-manager base config
│ ├── features/ # Modular feature toggles (cli, desktop, coding)
│ └── m3tam3re/ # Per-host user configs
├── modules/ # Custom NixOS/home-manager modules
├── overlays/ # Package overlays (stable, locked, pinned, master)
├── pkgs/ # Custom package definitions
└── secrets/ # Agenix encrypted .age files (19 secrets)
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Add new host | `flake.nix` + `hosts/<name>/` | Copy template from m3-atlas (server) or m3-ares (desktop) |
| Add service to m3-atlas | `hosts/m3-atlas/services/` | See containers/ for Podman + Traefik pattern |
| Configure desktop features | `home/features/desktop/` | Feature toggles with mkEnableOption |
| Add CLI tool | `home/features/cli/` | Fish + Nushell integration expected |
| Manage secrets | `secrets.nix` + `agenix -e` | SSH keys defined in secrets.nix |
| Define ports | `hosts/common/ports.nix` | Centralized port registry |
| Add user | `hosts/common/users/` | Shared across all hosts |
| Custom packages | `pkgs/default.nix` | Exposed via flake outputs |
## CONVENTIONS
### Secrets (agenix)
- **Create**: `agenix -e secrets/<name>.age` after adding keys to `secrets.nix`
- **Reference**: `config.age.secrets.<name>.path` in service configs
- **Pattern**: Service env files use `environmentFiles = [config.age.secrets.<service>-env.path]`
### Service Organization
- **Native services**: `hosts/<host>/services/<service>.nix`
- **Containers**: `hosts/<host>/services/containers/<service>.nix`
- **Traefik integration**: All m3-atlas services include dynamic config for SSL + routing
- **Networking**: Containers use dedicated `web` network (10.89.0.0/24) with static IPs
### Port Management
- **Registry**: All ports defined in `hosts/common/ports.nix`
- **Access**: `config.m3ta.ports.get "service-name"`
- **Convention**: Internal services use 3000-3020 range
### Home-Manager Features
- **Enable**: `features.<category>.<feature>.enable = true` in user config
- **Categories**: `cli`, `desktop`, `coding`
- **Pattern**: Features are opt-in modules with default.nix aggregators
### Multiple nixpkgs Inputs
- **stable**: 25.11 release
- **locked/pinned**: Specific commits for compatibility
- **master**: Bleeding edge
- **m3ta-nixpkgs**: Custom local overlay at `path:/home/m3tam3re/p/nix/nixpkgs`
## COMMANDS
```bash
# Build/deploy specific host
sudo nixos-rebuild switch --flake .#m3-ares
# Build/deploy current host
sudo nixos-rebuild switch --flake .#$(uname -n)
# Home-manager update
home-manager --flake . switch
# Update all flake inputs
nix flake update
# Add/edit secret
agenix -e secrets/<name>.age
# Infrastructure shell (OpenTofu)
nix develop .#infraShell
# Check configuration (no activation)
nixos-rebuild dry-build --flake .#<hostname>
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work atomically
bd close <id> # Complete work
bd dolt push # Push beads data to remote
```
## TRAEFIK PATTERNS (m3-atlas only)
## Non-Interactive Shell Commands
### SSL Termination
- **Provider**: Godaddy DNS challenge
- **Cert storage**: `/var/lib/traefik/acme.json`
- **Config**: `hosts/m3-atlas/services/traefik.nix`
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
Shell commands like `cp`, `mv`, and `rm` may be aliased to include `-i` (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
**Use these forms instead:**
```bash
# Force overwrite without prompting
cp -f source dest # NOT: cp source dest
mv -f source dest # NOT: mv source dest
rm -f file # NOT: rm file
# For recursive operations
rm -rf directory # NOT: rm -r directory
cp -rf source dest # NOT: cp -r source dest
```
**Other commands that may prompt:**
- `scp` - use `-o BatchMode=yes` for non-interactive
- `ssh` - use `-o BatchMode=yes` to fail instead of prompting
- `apt-get` - use `-y` flag
- `brew` - use `HOMEBREW_NO_AUTO_UPDATE=1` env var
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
## Beads Issue Tracker
This project uses **bd (beads)** for persistent task tracking. Run `bd prime` for full workflow context.
### Why Beads?
- **Prefer Beads over ad-hoc markdown TODO lists** — Beads provides structured, queryable, shareable issue tracking with dependency management
- **Never use `bd edit`** — it opens an interactive editor which blocks agent workflows
- **Use flags and stdin instead** — `bd update <id> --claim`, `bd create --title "..." --estimate 2`
### Slash Commands (Agent Workflow)
| Command | Purpose |
|---------|---------|
| `/beads:ready` | Find unblocked issues |
| `/beads:create` | Create a new issue |
| `/beads:update` | Update an issue (claim, status) |
| `/beads:close` | Close completed work |
| `/beads:stats` | Project-level snapshot |
### Core Workflow (6 Steps)
#### 1. Find Unblocked Work
```bash
bd ready --json
```
Lists issues with no blocking dependencies that are ready to work on.
#### 2. Claim Work
```bash
bd update <id> --claim
```
Atomically assigns the issue to you (sets status to "in-progress").
#### 3. Inspect Details
```bash
bd show <id>
```
View full issue details including:
- Description and acceptance criteria
- Blocking/blocked-by dependencies
- Time estimates
- Status history
#### 4. Create Newly Discovered Work
```bash
# Create a new issue
bd create \
--title "Fix audio on m3-helios" \
--estimate 2 \
--priority high \
--labels nixos,audio
# Link dependencies
bd dep <id> --blocks <blocked-id> # This issue blocks another
bd dep <id> --after <after-id> # This issue after another completes
bd dep <id> --requires <requires-id> # This issue requires another
```
#### 5. Complete Work
```bash
bd close <id> --reason "Added PulseAudio fallback to configuration.nix"
```
Provide a concise summary of what was done. The `--reason` is mandatory.
#### 6. Project Snapshot
```bash
bd status --json # Current state of all issues
bd stats # Metrics: velocity, cycle time, bottlenecks
```
### Example Complete Workflow
```bash
# Start session - find work
bd ready --json
# Claim available issue
bd update 42 --claim
# Do the work...
# Discover something else needed
bd create --title "Document hermes-agent setup" --estimate 1
# Link as related
bd dep 43 --after 42
# Complete original
bd close 42 --reason "Added Hyprland idle timeout config"
# Close related
bd close 43 --reason "Added setup docs to AGENTS.md"
# Push state to remote
bd dolt push
```
### Rules
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
- Run `bd prime` for detailed command reference and session close protocol
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
## Session Completion
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
**MANDATORY WORKFLOW:**
1. **File issues for remaining work** - Create issues for anything that needs follow-up
2. **Run quality gates** (if code changed) - Tests, linters, builds
3. **Update issue status** - Close finished work, update in-progress items
4. **PUSH TO REMOTE** - This is MANDATORY:
```bash
git pull --rebase
bd dolt push
git push
git status # MUST show "up to date with origin"
```
5. **Clean up** - Clear stashes, prune remote branches
6. **Verify** - All changes committed AND pushed
7. **Hand off** - Provide context for next session
**CRITICAL RULES:**
- Work is NOT complete until `git push` succeeds
- NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds
<!-- END BEADS INTEGRATION -->
# Project Agent
**Workspace Path:** `/home/m3tam3re/p/NIX/nixos-config`
_(Note to Pi: Your file write/edit tools run in a different directory by default. You MUST use absolute paths starting with the Workspace Path above for ALL file operations!)_
**Generated:** 2026-04-26
---
## Stack
| Component | Version/Source |
| ---------------- | --------------------------------- |
| **Nixpkgs** | nixos-unstable + 25.05 stable |
| **Home Manager** | github:nix-community/home-manager |
| **Agenix** | github:ryantm/agenix |
| **Disko** | github:nix-community/disko |
| **NUR** | github:nix-community/NUR |
| **Formatter** | alejandra |
| **Linters** | statix, deadnix |
| **IDE** | nixd |
| **Hermes Agent** | NousResearch/hermes-agent |
| **LLM Agents** | numtide/llm-agents.nix |
---
## Structure
```
nixos-config/
├── flake.nix # Entry point: hosts, overlays, dev shells
├── coding-rules.json # Opencode rules configuration
├── hosts/ # Per-host NixOS configurations
│ ├── common/ # Shared across all hosts
│ │ ├── users/ # User definitions
│ │ ├── ports.nix # Network ports config
│ │ └── extraServices/ # Common service toggles
│ ├── m3-ares/ # Main desktop
│ ├── m3-atlas/ # Desktop with disko
│ ├── m3-helios/ # Desktop with disko
│ ├── m3-hermes/ # Desktop with disko + hermes-agent
│ └── m3-kratos/ # Server with NUR
├── modules/ # Reusable NixOS/home-manager modules
│ ├── nixos/ # NixOS-specific modules
│ │ └── default.nix # Imports common + service configs
│ └── home-manager/ # Home-manager configurations
├── home/ # Per-user, per-host home configs
│ └── m3tam3re/
│ └── m3-daedalus.nix
├── overlays/ # Package overlays
│ ├── default.nix # Stable/locked/master branches
│ └── mods/ # Package modifications
├── pkgs/ # Custom packages
├── secrets/ # Encrypted secrets (agenix)
│ └── secrets.nix
├── .opencode-rules/ # Opencode AI rules
│ ├── concerns/ # Coding style rules
│ ├── languages/nix.md # Nix conventions
│ └── USAGE.md
└── .pi/ # Agent configuration
```
---
## Commands
| Action | Command | Notes |
| -------------------- | ---------------------------------------------------------------------- | ------------------------------------------------- |
| **Enter dev shell** | `nix develop` | Includes alejandra, nixd, agenix, statix, deadnix |
| **Build host** | `sudo nixos-rebuild switch --flake .#m3-ares` | Replace hostname as needed |
| **Dry run build** | `sudo nixos-rebuild dry-run --flake .#m3-ares` | Validate without applying |
| **List hosts** | `nix flake show` | Shows all NixOS configurations |
| **Update flake** | `sudo nixos-rebuild switch --flake .#m3-ares --update-input nixpkgs` | Update specific input |
| **Format code** | `alejandra .` | Run before committing |
| **Check lint** | `statix check .` | Run statix for antipatterns |
| **Remove dead code** | `deadnix -w .` | Clean up unused let bindings |
| **Build ISO** | `nix build .#nixosConfigurations.m3-ares.config.system.build.isoImage` | Generate install ISO |
---
## Conventions
### Formatting & Style
- **Formatter:** `alejandra` (mandatory, run before commits)
- **Indentation:** 2 spaces (alejandra default)
- **Variables:** camelCase (e.g., `maxRetryAttempts`)
- **Types/Modules:** PascalCase (e.g., `MyService`)
- **Constants:** UPPER_SNAKE_CASE (e.g., `MAX_RETRIES`)
- **Files:** hyphen-case (e.g., `my-file.nix`)
### Nix Module Patterns
### Service Integration Template
```nix
services.traefik.dynamicConfigOptions.http = {
services.<name>.loadBalancer.servers = [{ url = "http://127.0.0.1:<port>"; }];
routers.<name> = {
rule = "Host(`<subdomain>.m3ta.dev`)";
service = "<name>";
tls.certResolver = "godaddy";
{ config, lib, pkgs, ... }:
{
options.myService.enable = lib.mkEnableOption "my service";
config = lib.mkIf config.myService.enable {
services.myService.enable = true;
};
};
}
```
### Container Pattern
- **Network**: `--network=web --ip=10.89.0.<sequential>`
- **Ports**: Bind localhost only (`127.0.0.1:<external>:<internal>`)
- **Database access**: `--add-host=mysql:10.89.0.1` (gateway IP)
### Conditionals
## HOST ROLES
```nix
config = lib.mkMerge [
(lib.mkIf cfg.enable { ... })
(lib.mkIf cfg.extraConfig { ... })
];
```
| Host | Type | Hardware | Purpose |
|------|------|----------|---------|
| m3-atlas | Server | x86_64, disko | 20+ services, Traefik hub, PostgreSQL, MySQL |
| m3-helios | Server | x86_64, disko | AdGuard DNS, internal routing |
| m3-ares | Desktop | NVIDIA, Btrfs | Personal workstation, n8n, PostgreSQL |
| m3-kratos | Desktop | AMD, ZFS | Workstation, mem0, PostgreSQL |
| m3-aether | Cloud | QEMU | General purpose VM |
| m3-daedalus | Laptop | home-only | Portable (no full NixOS config) |
### Anti-Patterns (AVOID)
## ANTI-PATTERNS (THIS PROJECT)
- **Never use `with pkgs;`** — always use explicit package references
- **Never use `builtins.fetchTarball`** — use flake inputs instead
- **Never use `import <nixpkgs>`** — always use inputs
- **Never use `builtins.getAttr/hasAttr`** — use `lib.attrByPath` or `lib.optionalAttrs`
- **Avoid anonymous functions in config** — extract to named lets
- **DON'T** add secrets to `secrets/` without updating `secrets.nix` public keys
- **DON'T** hardcode ports - use `config.m3ta.ports.get` or add to registry
- **DON'T** create containers outside the `web` network on m3-atlas
- **DON'T** skip Traefik config for public-facing services on m3-atlas
- **DON'T** bypass extraServices flags - use `hosts/common/extraServices/` pattern
- **DON'T** commit unencrypted secrets or test with real credentials
### Imports
## UNIQUE TO THIS CONFIG
- Use flake inputs for dependencies (e.g., `inputs.home-manager.nixosModules.home-manager`)
- Import relative paths with `./` or `../`
- Never use absolute paths in imports
- **Custom m3ta-nixpkgs**: Local overlay for unreleased/patched packages
- **extraServices abstraction**: Boolean flags to toggle Podman, Ollama, virtualisation per host
- **Mythological naming**: All hosts named after Greek mythology
- **Dual domain strategy**: New services on `m3ta.dev`, legacy redirects from `m3tam3re.com`
- **Per-host nixpkgs versions**: Different hosts can use different nixpkgs commits via specialArgs
- **Container IP registry**: Static IP assignments in 10.89.0.0/24 subnet for predictable networking
### Secrets
## NOTES
- Secrets managed via **agenix** in `secrets/` directory
- Never commit plaintext secrets
- Use `.nix` extension for secret files
- m3-atlas is the service hub - most complex configuration
- Secrets require host SSH keys defined in `secrets.nix` before `agenix -e` works
- Fish and Nushell both configured - choose per-user with shell aliases
- Color scheme (Dracula) applied via nix-colors across all visual tools
- See subdirectory AGENTS.md for deep dives on containers, desktop features, CLI tools
---
## Key Files
| File | Purpose |
| ---------------------------------- | ------------------------------------------------------------------------------------------ |
| `flake.nix` | Central entry point defining all hosts, overlays, packages, dev shells, and nixpkgs config |
| `hosts/common/default.nix` | Shared Nix settings, nixpkgs overlays, home-manager integration, user defaults |
| `hosts/m3-ares/default.nix` | Main desktop host configuration, imports common + service modules |
| `hosts/m3-ares/configuration.nix` | Desktop environment config (Hyprland, display, audio, etc.) |
| `hosts/m3-ares/programs.nix` | CLI tools, dev tools, shell configs |
| `hosts/m3-ares/services/` | Service-specific configs (firewall, printing, etc.) |
| `modules/nixos/default.nix` | Orchestrates common + configuration imports |
| `overlays/default.nix` | Package version overrides (stable/locked/master branches) |
| `.opencode-rules/languages/nix.md` | Nix-specific conventions and patterns |
---
## What to Avoid
1. **Don't modify `flake.lock`** directly — use `nix flake update`
2. **Don't use impure operations** — this is a pure flake-based config
3. **Don't commit without formatting** — always run `alejandra .` first
4. **Don't add packages to hosts directly** — prefer adding to overlays or using NUR
5. **Don't hardcode paths** — use `inputs` and relative imports
6. **Don't create monolithic modules** — keep functions under 20 lines
7. **Don't skip the dry-run** — always test with `--dry-run` before switching
8. **Don't use lib.mkDefault lightly** — understand the precedence implications
---
## Notes
### Adding a New Host
1. Add entry to `flake.nix` → `nixosConfigurations`
2. Create directory in `hosts/` with:
- `default.nix` — imports common + specific configs
- `configuration.nix` — host-specific system config
- `hardware-configuration.nix` — from `nixos-generate-config`
- `programs.nix`, `services/`, `secrets.nix` as needed
3. Run `sudo nixos-generate-config --dir ./hosts/new-host` first time
### Adding a New Package
1. For simple packages: add to appropriate overlay in `overlays/default.nix`
2. For complex packages: create in `pkgs/` directory
3. For upstream packages: use NUR or add as flake input
### Development Workflow
1. Edit config files
2. Run `alejandra .` to format
3. Run `statix check .` for linting
4. Run `sudo nixos-rebuild dry-run --flake .#m3-ares`
5. If successful: `sudo nixos-rebuild switch --flake .#m3-ares`
### Remote Building
```bash
# Build on remote machine
nix copy --to ssh://user@host .#nixosConfigurations.m3-ares.config.system.build.toplevel
ssh user@host 'sudo nixos-rebuild switch --flake /nix/store/...-closure'
```
### Home Manager
- Home configs live in `home/m3tam3re/`
- Use `home-manager.users.m3tam3re` in host config
- Access via `config.home-manager.users.m3tam3re`

1
coding-rules.json Normal file
View File

@@ -0,0 +1 @@
{"$schema":"https://opencode.ai/config.json","instructions":[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md",".opencode-rules/languages/nix.md"]}

228
flake.lock generated
View File

@@ -26,11 +26,27 @@
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1776092721,
"narHash": "sha256-avV4Snqp0K57I9s8D61+GHlg9DYZFSIvjaS4d4RYpG8=",
"lastModified": 1777399938,
"narHash": "sha256-xXPqUQezDdDtF8MbpZnwD1HkybOYwF92evx8rJ6OXCU=",
"ref": "refs/heads/master",
"rev": "0ad41acb03eee0e22cba611b2171a3d3ee30cb10",
"revCount": 72,
"rev": "9a91f1ee0cf011a7eaf1f16a9e17610b0457e055",
"revCount": 85,
"type": "git",
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
},
"original": {
"type": "git",
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
}
},
"agents_2": {
"flake": false,
"locked": {
"lastModified": 1777399938,
"narHash": "sha256-xXPqUQezDdDtF8MbpZnwD1HkybOYwF92evx8rJ6OXCU=",
"ref": "refs/heads/master",
"rev": "9a91f1ee0cf011a7eaf1f16a9e17610b0457e055",
"revCount": 85,
"type": "git",
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
},
@@ -89,11 +105,11 @@
]
},
"locked": {
"lastModified": 1771437256,
"narHash": "sha256-bLqwib+rtyBRRVBWhMuBXPCL/OThfokA+j6+uH7jDGU=",
"lastModified": 1776249299,
"narHash": "sha256-Dt9t1TGRmJFc0xVYhttNBD6QsAgHOHCArqGa0AyjrJY=",
"owner": "numtide",
"repo": "blueprint",
"rev": "06ee7190dc2620ea98af9eb225aa9627b68b0e33",
"rev": "56131e8628f173d24a27f6d27c0215eff57e40dd",
"type": "github"
},
"original": {
@@ -108,7 +124,6 @@
"llm-agents",
"flake-parts"
],
"import-tree": "import-tree",
"nixpkgs": [
"llm-agents",
"nixpkgs"
@@ -123,15 +138,16 @@
]
},
"locked": {
"lastModified": 1770895533,
"narHash": "sha256-v3QaK9ugy9bN9RXDnjw0i2OifKmz2NnKM82agtqm/UY=",
"lastModified": 1777369708,
"narHash": "sha256-1xW7cRZNsFNPQD+cE0fwnLVStnDth0HSoASEIFeT7uI=",
"owner": "nix-community",
"repo": "bun2nix",
"rev": "c843f477b15f51151f8c6bcc886954699440a6e1",
"rev": "e659e1cc4b8e1b21d0aa85f1c481f9db61ecfa98",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "staging-2.1.0",
"repo": "bun2nix",
"type": "github"
}
@@ -165,11 +181,11 @@
]
},
"locked": {
"lastModified": 1773889306,
"narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=",
"lastModified": 1776613567,
"narHash": "sha256-gC9Cp5ibBmGD5awCA9z7xy6MW6iJufhazTYJOiGlCUI=",
"owner": "nix-community",
"repo": "disko",
"rev": "5ad85c82cc52264f4beddc934ba57f3789f28347",
"rev": "32f4236bfc141ae930b5ba2fb604f561fed5219d",
"type": "github"
},
"original": {
@@ -263,20 +279,22 @@
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_3",
"npm-lockfile-fix": "npm-lockfile-fix",
"pyproject-build-systems": "pyproject-build-systems",
"pyproject-nix": "pyproject-nix_2",
"uv2nix": "uv2nix_2"
},
"locked": {
"lastModified": 1775818389,
"narHash": "sha256-Ex2WE+apD96FZt8e/uRgbTUxSWslxv37zd2Ovbd4LYA=",
"lastModified": 1777573861,
"narHash": "sha256-whY/1WL2fQUhPqDp7CGm3MSwOOo7FB1eADhNVnHeCRU=",
"owner": "NousResearch",
"repo": "hermes-agent",
"rev": "0b143f2ea3ddef4e0bf725bdd931541f8af27882",
"rev": "73bf3ab1b22314ed9dfecbb59242c03742fe72af",
"type": "github"
},
"original": {
"owner": "NousResearch",
"ref": "v2026.4.30",
"repo": "hermes-agent",
"type": "github"
}
@@ -309,11 +327,11 @@
]
},
"locked": {
"lastModified": 1775781825,
"narHash": "sha256-L5yKTpR+alrZU2XYYvIxCeCP4LBHU5jhwSj7H1VAavg=",
"lastModified": 1777476904,
"narHash": "sha256-EeLoE8n4+QCbteyAsYXxhfr97RFfWL1ga0xwfL6lpKw=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "e35c39fca04fee829cecdf839a50eb9b54d8a701",
"rev": "8c8e5389e75a36bee53920de8ee24f017b3ae03e",
"type": "github"
},
"original": {
@@ -372,11 +390,11 @@
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1774257081,
"narHash": "sha256-92ZbaBfsEXEE7VaWJjv9aRSk3l9nyoYYyMe2AwTqSZI=",
"lastModified": 1776923321,
"narHash": "sha256-QowlCOrE4jGOTDCUCEx/E8gHjqSx3r25y7v4dEBpBhk=",
"owner": "Jas-SinghFSU",
"repo": "HyprPanel",
"rev": "e919b4a8a8ab5f2a0752f68576ab3eed6993cefd",
"rev": "1961ba86ad5ab880beb639e5454054b2b5037e0d",
"type": "github"
},
"original": {
@@ -385,21 +403,6 @@
"type": "github"
}
},
"import-tree": {
"locked": {
"lastModified": 1763762820,
"narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=",
"owner": "vic",
"repo": "import-tree",
"rev": "3c23749d8013ec6daa1d7255057590e9ca726646",
"type": "github"
},
"original": {
"owner": "vic",
"repo": "import-tree",
"type": "github"
}
},
"llm-agents": {
"inputs": {
"blueprint": "blueprint",
@@ -410,11 +413,11 @@
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1775818866,
"narHash": "sha256-zkjkwAGYNxRsXm3TBSqTwqbQbJpTEqwBC6KPGnh7Wxw=",
"lastModified": 1777439951,
"narHash": "sha256-1Bs4ZbBayXWicrOrQQn3/BnnqhEy+tQjdFn40wHu1dw=",
"owner": "numtide",
"repo": "llm-agents.nix",
"rev": "290a25e026fa42bb74ae49a3a9a74904eb68be34",
"rev": "2641c18f5bb9d0b95e81beca1b0415e174d7e650",
"type": "github"
},
"original": {
@@ -425,21 +428,24 @@
},
"m3ta-nixpkgs": {
"inputs": {
"agents": "agents_2",
"basecamp": "basecamp",
"nixpkgs": "nixpkgs_6",
"nixpkgs-master": "nixpkgs-master",
"opencode": "opencode",
"openspec": "openspec"
},
"locked": {
"lastModified": 1776108132,
"narHash": "sha256-XViouXku09/lGDN4xpfUTejo+i9W09ecVEDYXqJdrwY=",
"path": "/home/m3tam3re/p/NIX/nixpkgs",
"type": "path"
"lastModified": 1777486032,
"narHash": "sha256-UStORUF67vzXQ1jtORzR2cTv8GCvrxweF7HFHJgPpqw=",
"ref": "refs/heads/master",
"rev": "1da8c96447eb74a316861f6242421ef0309c29a8",
"revCount": 267,
"type": "git",
"url": "https://code.m3ta.dev/m3tam3re/nixpkgs"
},
"original": {
"path": "/home/m3tam3re/p/NIX/nixpkgs",
"type": "path"
"type": "git",
"url": "https://code.m3ta.dev/m3tam3re/nixpkgs"
}
},
"nix-colors": {
@@ -576,11 +582,11 @@
},
"nixpkgs-master": {
"locked": {
"lastModified": 1775657231,
"narHash": "sha256-DP8FfybiZPp5WLB9eIk0TC2mdvuYzxLGgrBODDrwPEI=",
"lastModified": 1777470666,
"narHash": "sha256-uAi+pTjKLturTz3XqTwnsU0fJnqf8xx8orfPpRbdaKQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4e03baaa39b7746eac5704d623461422131cd03d",
"rev": "053b9fa5f0fbdac0bd9d248cea58a11223eb495d",
"type": "github"
},
"original": {
@@ -592,11 +598,11 @@
},
"nixpkgs-master_2": {
"locked": {
"lastModified": 1775820596,
"narHash": "sha256-DDZkqNCW97pb1WX6sDICPnDEYcnIhGMVNLyrDOA2RUQ=",
"lastModified": 1777483759,
"narHash": "sha256-luE+pNcTx3gz109lEC/xUxPHrx1aEZsp5X4OEBcnGaw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "8ccce85bee20142e7d9eb7f3423c5f4fb69fa985",
"rev": "b540331d6f1e343b6812b5aa1d97c707a0de0da2",
"type": "github"
},
"original": {
@@ -608,11 +614,11 @@
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1775595990,
"narHash": "sha256-OEf7YqhF9IjJFYZJyuhAypgU+VsRB5lD4DuiMws5Ltc=",
"lastModified": 1777077449,
"narHash": "sha256-AIiMJiqvGrN4HyLEbKAoCSRRYn0rnlW5VbKNIMIYqm4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "4e92bbcdb030f3b4782be4751dc08e6b6cb6ccf2",
"rev": "a4bf06618f0b5ee50f14ed8f0da77d34ecc19160",
"type": "github"
},
"original": {
@@ -672,11 +678,11 @@
},
"nixpkgs_5": {
"locked": {
"lastModified": 1775763530,
"narHash": "sha256-BuTK9z1QEwWPOIakQ1gCN4pa4VwVJpfptYCviy2uOGc=",
"lastModified": 1776949667,
"narHash": "sha256-GMSVw35Q+294GlrTUKlx087E31z7KurReQ1YHSKp5iw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b0188973b4b2a5b6bdba8b65381d6cd09a533da0",
"rev": "01fbdeef22b76df85ea168fbfe1bfd9e63681b30",
"type": "github"
},
"original": {
@@ -688,11 +694,11 @@
},
"nixpkgs_6": {
"locked": {
"lastModified": 1775423009,
"narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=",
"lastModified": 1777268161,
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9",
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
"type": "github"
},
"original": {
@@ -720,11 +726,11 @@
},
"nixpkgs_8": {
"locked": {
"lastModified": 1775710090,
"narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
"lastModified": 1777268161,
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "4c1018dae018162ec878d42fec712642d214fdfa",
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
"type": "github"
},
"original": {
@@ -750,6 +756,27 @@
"type": "github"
}
},
"npm-lockfile-fix": {
"inputs": {
"nixpkgs": [
"hermes-agent",
"nixpkgs"
]
},
"locked": {
"lastModified": 1775903712,
"narHash": "sha256-2GV79U6iVH4gKAPWYrxUReB0S41ty/Y3dBLquU8AlaA=",
"owner": "jeslie0",
"repo": "npm-lockfile-fix",
"rev": "c6093acb0c0548e0f9b8b3d82918823721930fe8",
"type": "github"
},
"original": {
"owner": "jeslie0",
"repo": "npm-lockfile-fix",
"type": "github"
}
},
"nur": {
"inputs": {
"flake-parts": "flake-parts_3",
@@ -758,11 +785,11 @@
]
},
"locked": {
"lastModified": 1775820600,
"narHash": "sha256-C2ffOYhqlKqKqc0KEkMQXIs6NHpM5ewEoO+o+XZCo8c=",
"lastModified": 1777486007,
"narHash": "sha256-5R0q8ESHux3Le76n4IuNUThkAo4o2M+Kj1Loj2J7ahI=",
"owner": "nix-community",
"repo": "NUR",
"rev": "38d9344bb5323e582090d0033428a8dd7e684fde",
"rev": "6f5d55cfd726ff4cd68d006bddbdf459d0dc471b",
"type": "github"
},
"original": {
@@ -771,28 +798,6 @@
"type": "github"
}
},
"opencode": {
"inputs": {
"nixpkgs": [
"m3ta-nixpkgs",
"nixpkgs-master"
]
},
"locked": {
"lastModified": 1775782812,
"narHash": "sha256-m+Ue7FWiTjKMAn1QefAwOMfOb2Vybk0mJPV9zcbkOmE=",
"owner": "anomalyco",
"repo": "opencode",
"rev": "877be7e8e04142cd8fbebcb5e6c4b9617bf28cce",
"type": "github"
},
"original": {
"owner": "anomalyco",
"ref": "v1.4.3",
"repo": "opencode",
"type": "github"
}
},
"openspec": {
"inputs": {
"nixpkgs": [
@@ -801,11 +806,11 @@
]
},
"locked": {
"lastModified": 1775372219,
"narHash": "sha256-MJakKC026Sarz7nMmiFrfONWc4xgaw8ApV0Hhp4ebhM=",
"lastModified": 1776788052,
"narHash": "sha256-L4LBHVVtgMhSJm+IzZSYOR0UXPbvIRg4xiEV5urYxdI=",
"owner": "Fission-AI",
"repo": "OpenSpec",
"rev": "64d476f8b924bb9b74b896ea0aa784970e37da69",
"rev": "3c7a05c5dc88b2397c478805890b55ed392b19e8",
"type": "github"
},
"original": {
@@ -925,6 +930,7 @@
"skills-anthropic": "skills-anthropic",
"skills-basecamp": "skills-basecamp",
"skills-kestra": "skills-kestra",
"skills-superpowers": "skills-superpowers",
"skills-vercel": "skills-vercel"
}
},
@@ -951,11 +957,11 @@
"skills-anthropic": {
"flake": false,
"locked": {
"lastModified": 1775755206,
"narHash": "sha256-H/oorOl5cch7bnziDz7gHNBv5Q0OAwFbk9w1WLku2kk=",
"lastModified": 1776964038,
"narHash": "sha256-xFsg66TCtKzSgRIW6Ab771FWEIhei3jPgfE4byMiB44=",
"owner": "anthropics",
"repo": "skills",
"rev": "12ab35c2eb5668c95810e6a6066f40f4218adc39",
"rev": "5128e1865d670f5d6c9cef000e6dfc4e951fb5b9",
"type": "github"
},
"original": {
@@ -967,11 +973,11 @@
"skills-basecamp": {
"flake": false,
"locked": {
"lastModified": 1775581454,
"narHash": "sha256-jDkRFNjxa+WIdpFBSPvHwGK4hbmF8bMZK5RM8ue2IGI=",
"lastModified": 1777481361,
"narHash": "sha256-GJ94Y1n+zR6zpOWjAGFYFWFIFpT1royFJOy2TaQXpzU=",
"owner": "basecamp",
"repo": "basecamp-cli",
"rev": "5fb837c7f0d6e04c0e6c308d8b79994a49b58b2d",
"rev": "59d59b66974d442190b0762129b4f1749adcedf0",
"type": "github"
},
"original": {
@@ -996,14 +1002,30 @@
"type": "github"
}
},
"skills-superpowers": {
"flake": false,
"locked": {
"lastModified": 1776996157,
"narHash": "sha256-0WupTacT1jIwVBloj1i0RF7wIllVtP8eMPRl7VrXdbE=",
"owner": "obra",
"repo": "superpowers",
"rev": "6efe32c9e2dd002d0c394e861e0529675d1ab32e",
"type": "github"
},
"original": {
"owner": "obra",
"repo": "superpowers",
"type": "github"
}
},
"skills-vercel": {
"flake": false,
"locked": {
"lastModified": 1775448549,
"narHash": "sha256-P7sHiKqeThQGIOfJ1nfjiECZnhQh+U2HuLex2sg8k5E=",
"lastModified": 1777394685,
"narHash": "sha256-YxCMuTl+pVJ7dXhaL7l9vDw9k2orlG31j7/0pgllMJk=",
"owner": "vercel-labs",
"repo": "skills",
"rev": "df0579f85cb8a360473c921e1343359006100d3c",
"rev": "7c0a9af3f8738965b71341712710ac7371089b34",
"type": "github"
},
"original": {

View File

@@ -22,8 +22,7 @@
nixpkgs-9e58ed7.url = "github:nixos/nixpkgs/9e58ed7ba759d81c98f033b7f5eba21ca68f53b0";
nixpkgs-master.url = "github:nixos/nixpkgs/master";
# m3ta-nixpkgs.url = "git+https://code.m3ta.dev/m3tam3re/nixpkgs";
m3ta-nixpkgs.url = "path:/home/m3tam3re/p/NIX/nixpkgs";
m3ta-nixpkgs.url = "git+https://code.m3ta.dev/m3tam3re/nixpkgs";
llm-agents.url = "github:numtide/llm-agents.nix";
#
@@ -61,11 +60,15 @@
url = "github:kestra-io/agent-skills";
flake = false;
};
skills-superpowers = {
url = "github:obra/superpowers";
flake = false;
};
skills-vercel = {
url = "github:vercel-labs/skills";
flake = false;
};
hermes-agent.url = "github:NousResearch/hermes-agent";
hermes-agent.url = "github:NousResearch/hermes-agent/v2026.4.30";
};
outputs = {
@@ -178,7 +181,7 @@
config.allowUnfree = true; # Allow unfree packages in devShell
};
m3taLib = m3ta-nixpkgs.lib.x86_64-linux;
rules = m3taLib.opencode-rules.mkOpencodeRules {
rules = m3taLib.coding-rules.mkCodingRules {
inherit agents;
languages = ["nix"];
};
@@ -189,37 +192,10 @@
nixd
openssh
agenix.packages.${system}.default
statix
deadnix
];
inherit (rules) instructions;
shellHook = ''
${rules.shellHook}
echo "======================================"
echo "🧑🚀 Nix Development Shell with Opencode Rules"
echo "======================================"
echo ""
echo "Active rules:"
echo " - Nix language conventions"
echo " - Coding-style best practices"
echo " - Naming conventions"
echo " - Documentation standards"
echo " - Testing guidelines"
echo " - Git workflow patterns"
echo " - Project structure guidelines"
echo ""
echo "Generated files:"
echo " - .opencode-rules/ (symlink to AGENTS repo)"
echo " - opencode.json (configuration file)"
echo ""
echo "Useful commands:"
echo " - cat opencode.json View rules configuration"
echo " - ls .opencode-rules/ Browse available rules"
echo " - nix develop Re-enter this shell"
echo ""
echo "Remember to add to .gitignore:"
echo " .opencode-rules"
echo " opencode.json"
echo "======================================"
'';
inherit (rules) instructions shellHook;
};
});
};

145
home/base/cli-tools/bat.nix Normal file
View File

@@ -0,0 +1,145 @@
# Bat — cat replacement with nix-colors syntax highlighting theme.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.base.cliTools.bat;
palette = config.colorScheme.palette;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.bat.enable = (mkEnableOption "enable bat with nix-colors theme") // {default = true;};
config = mkIf cfg.enable {
programs.bat = {
enable = true;
config = {theme = "universal";};
themes = {
universal = {
src = pkgs.writeText "universal.tmTheme" ''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>Universal (nix-colors)</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#${palette.base00}</string>
<key>foreground</key>
<string>#${palette.base05}</string>
<key>caret</key>
<string>#${palette.base05}</string>
<key>selection</key>
<string>#${palette.base02}</string>
<key>selectionForeground</key>
<string>#${palette.base05}</string>
<key>lineHighlight</key>
<string>#${palette.base01}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base03}</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base0A}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base0E}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base08}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function</string>
<key>scope</key>
<string>entity.name.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base0B}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Type</string>
<key>scope</key>
<string>entity.name.type, storage.type</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base0D}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base05}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Constant</string>
<key>scope</key>
<string>constant</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${palette.base0E}</string>
</dict>
</dict>
</array>
</dict>
</plist>
'';
};
};
};
};
}

View File

@@ -0,0 +1,21 @@
# Carapace — multi-shell completion engine with Fish, Nushell, and Bash integration.
{
config,
lib,
...
}:
with lib; let
cfg = config.base.cliTools.carapace;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.carapace.enable = (mkEnableOption "enable carapace completion engine") // {default = true;};
config = mkIf cfg.enable {
programs.carapace = {
enable = true;
enableFishIntegration = true;
enableNushellIntegration = true;
enableBashIntegration = true;
};
};
}

View File

@@ -0,0 +1,17 @@
# CLI tools aggregator — imports all base command-line utilities.
{...}: {
imports = [
./bat.nix
./carapace.nix
./direnv.nix
./eza.nix
./fzf.nix
./lf.nix
./nitch.nix
./packages.nix
./television.nix
./zellij.nix
./zellij-ps.nix
./zoxide.nix
];
}

View File

@@ -0,0 +1,20 @@
# Direnv — automatic environment loading with nix-direnv integration.
{
config,
lib,
...
}:
with lib; let
cfg = config.base.cliTools.direnv;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.direnv.enable = (mkEnableOption "enable direnv with nix-direnv") // {default = true;};
config = mkIf cfg.enable {
programs.direnv = {
enable = true;
enableNushellIntegration = true;
nix-direnv.enable = true;
};
};
}

View File

@@ -0,0 +1,21 @@
# Eza — modern ls replacement with icons, git status, and long format by default.
{
config,
lib,
...
}:
with lib; let
cfg = config.base.cliTools.eza;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.eza.enable = (mkEnableOption "enable eza modern ls replacement") // {default = true;};
config = mkIf cfg.enable {
programs.eza = {
enable = true;
enableFishIntegration = true;
enableBashIntegration = true;
extraOptions = ["-l" "--icons" "--git" "-a"];
};
};
}

View File

@@ -1,12 +1,14 @@
# Fuzzy finder with nix-colors palette and Wayland clipboard integration.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.fzf;
cfg = config.base.cliTools.fzf;
in {
options.features.cli.fzf.enable = mkEnableOption "enable fuzzy finder";
# Enabled by default — base modules are always-on.
options.base.cliTools.fzf.enable = (mkEnableOption "enable fuzzy finder") // {default = true;};
config = mkIf cfg.enable {
programs.fzf = {
@@ -30,7 +32,6 @@ in {
"--preview='bat --color=always -n {}'"
"--bind 'ctrl-/:toggle-preview'"
"--header 'Press CTRL-Y to copy command into clipboard'"
"--bind 'ctrl-/:toggle-preview'"
"--bind 'ctrl-y:execute-silent(echo -n {2..} | wl-copy)+abort'"
];
defaultCommand = "fd --type f --exclude .git --follow --hidden";

View File

@@ -0,0 +1,28 @@
# Lf — terminal file manager with bat preview and Dracula theme.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.base.cliTools.lf;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.lf.enable = (mkEnableOption "enable lf terminal file manager") // {default = true;};
config = mkIf cfg.enable {
home.packages = [pkgs.lf];
programs.lf = {
enable = true;
settings = {
preview = true;
drawbox = true;
hidden = true;
icons = true;
previewer = "bat";
};
};
};
}

View File

@@ -0,0 +1,17 @@
# Nitch — minimal system information display tool.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.base.cliTools.nitch;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.nitch.enable = (mkEnableOption "enable nitch") // {default = true;};
config = mkIf cfg.enable {
home.packages = [pkgs.nitch];
};
}

View File

@@ -0,0 +1,62 @@
# Essential CLI packages — core utilities always available on every host.
# NOTE: `lazylib` does not exist in nixpkgs. `lazygit` is the correct package
# (Git TUI) and is intentionally used here instead.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.base.cliTools.essentials;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.essentials.enable = (mkEnableOption "enable essential CLI packages") // {default = true;};
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Core utilities
coreutils
fd
htop
jq
ripgrep
# Nix
alejandra
comma
nixd
nix-diff
nix-index
nix-update
# Dev tools
bc
cmake
devenv
gcc
gnumake
go
httpie
just
lazygit
progress
sqlite
sqlite-vec
tldr
# AI tools
fabric-ai
llm
# Misc
basecamp
hyprpaper-random
libnotify
trash-cli
unzip
yazi
zip
];
};
}

View File

@@ -1,12 +1,14 @@
# Television — fuzzy finder with custom channels for tldr, git-diff, and git-log.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.television;
cfg = config.base.cliTools.television;
in {
options.features.cli.television.enable = mkEnableOption "enable nitch";
# Enabled by default — base modules are always-on.
options.base.cliTools.television.enable = (mkEnableOption "enable television") // {default = true;};
config = mkIf cfg.enable {
programs.television = {
@@ -16,9 +18,7 @@ in {
metadata = {
description = "Browse TLDR pages";
name = "tldr";
requirements = [
"tldr"
];
requirements = ["tldr"];
};
preview = {
command = "tldr '{}'";
@@ -31,9 +31,7 @@ in {
metadata = {
description = "A channel to select files from git diff commands";
name = "git-diff";
requirements = [
"git"
];
requirements = ["git"];
};
preview = {
command = "git diff HEAD --color=always -- '{}'";
@@ -46,9 +44,7 @@ in {
metadata = {
description = "A channel to select from git log entries";
name = "git-log";
requirements = [
"git"
];
requirements = ["git"];
};
preview = {
command = "git show -p --stat --pretty=fuller --color=always '{0}'";

View File

@@ -0,0 +1,30 @@
# Zellij-ps — project-aware Zellij session manager from m3ta-nixpkgs.
# Delegates to `cli.zellij-ps` — the home-manager module namespace provided by
# m3ta-nixpkgs (inputs.m3ta-nixpkgs.nixosModules.default). This is intentional;
# `cli.*` is the convention used by m3ta-nixpkgs home-manager modules.
{
config,
lib,
...
}:
with lib; let
cfg = config.base.cliTools.zellijPs;
in {
options.base.cliTools.zellijPs = {
# Enabled by default — base modules are always-on.
enable = (mkEnableOption "enable zellij-ps project session manager") // {default = true;};
projectFolders = mkOption {
type = types.listOf types.path;
description = "Project root folders scanned by zellij-ps.";
default = ["${config.home.homeDirectory}/p"];
};
};
config = mkIf cfg.enable {
cli.zellij-ps = {
enable = true;
projectFolders = cfg.projectFolders;
};
};
}

View File

@@ -1,12 +1,14 @@
# Zellij terminal multiplexer with nix-colors theming.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.zellij;
cfg = config.base.cliTools.zellij;
in {
options.features.cli.zellij.enable = mkEnableOption "enable tmux";
# Enabled by default — base modules are always-on.
options.base.cliTools.zellij.enable = (mkEnableOption "enable zellij multiplexer") // {default = true;};
config = mkIf cfg.enable {
programs.zellij = {

View File

@@ -0,0 +1,20 @@
# Zoxide — smarter cd with Fish and Nushell integration.
{
config,
lib,
...
}:
with lib; let
cfg = config.base.cliTools.zoxide;
in {
# Enabled by default — base modules are always-on.
options.base.cliTools.zoxide.enable = (mkEnableOption "enable zoxide smarter cd") // {default = true;};
config = mkIf cfg.enable {
programs.zoxide = {
enable = true;
enableFishIntegration = true;
enableNushellIntegration = true;
};
};
}

9
home/base/default.nix Normal file
View File

@@ -0,0 +1,9 @@
# Base home-manager configuration — always loaded on every host.
# Includes shell, CLI tools, and secrets modules.
{...}: {
imports = [
./shell
./cli-tools
./secrets/secrets.nix
];
}

View File

@@ -1,3 +1,4 @@
# Password store and secrets management via pass-wayland with OTP and import extensions.
{
config,
lib,
@@ -5,9 +6,10 @@
...
}:
with lib; let
cfg = config.features.cli.secrets;
cfg = config.base.secrets;
in {
options.features.cli.secrets.enable = mkEnableOption "enable secrets";
# Enabled by default — base modules are always-on.
options.base.secrets.enable = (mkEnableOption "enable secrets management") // {default = true;};
config = mkIf cfg.enable {
programs.password-store = {
@@ -17,6 +19,6 @@ in {
(exts: [exts.pass-otp exts.pass-import]);
settings = {PASSWORD_STORE_DIR = "$XDG_DATA_HOME/password-store";};
};
home.packages = with pkgs; [pinentry-gnome3];
home.packages = [pkgs.pinentry-gnome3];
};
}

View File

@@ -0,0 +1,8 @@
# Shell aggregator — imports Nushell (primary), Fish, and Starship prompt.
{...}: {
imports = [
./nushell.nix
./fish.nix
./starship.nix
];
}

View File

@@ -1,12 +1,14 @@
# Fish shell configuration exposed under the new base namespace.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.fish;
cfg = config.base.shell.fish;
in {
options.features.cli.fish.enable = mkEnableOption "enable fish shell";
# Enabled by default — base modules are always-on.
options.base.shell.fish.enable = (mkEnableOption "enable fish shell") // {default = true;};
config = mkIf cfg.enable {
programs.fish = {

View File

@@ -1,12 +1,14 @@
# Primary shell configuration — Nushell with environment, aliases, and integrations.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.nushell;
cfg = config.base.shell.nushell;
in {
options.features.cli.nushell.enable = mkEnableOption "enable nushell";
# Enabled by default — base modules are always-on.
options.base.shell.nushell.enable = (mkEnableOption "enable nushell") // {default = true;};
config = mkIf cfg.enable {
programs.nushell = {
@@ -20,9 +22,8 @@ in {
$env.EDITOR = "nvim"
$env.VISUAL = "zeditor"
$env.FZF_DEFAULT_COMMAND = "fd --type f --exclude .git --follow --hidden"
$env.FZF_DEFAULT_OPTS = "--preview='bat --color=always -n {}' --bind 'ctrl-/:toggle-preview' --header 'Press CTRL-Y to copy command into clipboard' --bind 'ctrl-/:toggle-preview' --bind 'ctrl-y:execute-silent(echo -n {2..} | wl-copy)+abort' --color bg:#282a36,bg+:#44475a,fg:#f8f8f2,fg+:#f8f8f2,header:#6272a4,hl:#bd93f9,hl+:#bd93f9,info:#ffb86c,marker:#ff79c6,pointer:#ff79c6,prompt:#50fa7b,spinner:#ffb86c"
$env.FZF_DEFAULT_OPTS = "--preview='bat --color=always -n {}' --bind 'ctrl-/:toggle-preview' --header 'Press CTRL-Y to copy command into clipboard' --bind 'ctrl-y:execute-silent(echo -n {2..} | wl-copy)+abort' --color bg:#282a36,bg+:#44475a,fg:#f8f8f2,fg+:#f8f8f2,header:#6272a4,hl:#bd93f9,hl+:#bd93f9,info:#ffb86c,marker:#ff79c6,pointer:#ff79c6,prompt:#50fa7b,spinner:#ffb86c"
$env.XDG_DATA_HOME = $"($env.HOME)/.local/share"
$env.FZF_DEFAULT_COMMAND = "fd --type f --exclude .git --follow --hidden"
$env.SSH_AUTH_SOCK = "/run/user/1000/gnupg/S.gpg-agent.ssh"
$env.PATH = ($env.PATH | split row (char esep) | append $"($env.HOME)/.cache/.bun/bin" | append $"($env.HOME)/.npm-global/bin" | uniq)
$env.NPM_CONFIG_PREFIX = $"($env.HOME)/.npm-global"
@@ -40,12 +41,6 @@ in {
}
'';
# if (tty) == "/dev/tty1" {
# exec uwsm start -S -F /run/current-system/sw/bin/Hyprland
# }
# if (tty) == "/dev/tty2" {
# exec gamescope -O HDMI-A-1 -W 1920 -H 1080 --adaptive-sync --hdr-enabled --rt --steam -- steam -pipewire-dmabuf -tenfoot
# }
configFile.text = ''
# Aliases
alias .. = cd ..

View File

@@ -1,12 +1,14 @@
# Starship cross-shell prompt with nix-colors theming.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.starship;
cfg = config.base.shell.starship;
in {
options.features.cli.starship.enable = mkEnableOption "enable starship prompt";
# Enabled by default — base modules are always-on.
options.base.shell.starship.enable = (mkEnableOption "enable starship prompt") // {default = true;};
config = mkIf cfg.enable {
programs.starship = {

View File

@@ -1,19 +1,43 @@
# AI agent system — OpenCode, Pi, and MCP server configuration.
# Relies on coding.agents options provided by home/common/default.nix
# (inputs.m3ta-nixpkgs.homeManagerModules.default).
{
config,
inputs,
lib,
pkgs,
videoDrivers,
videoDrivers ? [],
...
}: let
npmGlobalPrefix = "${config.home.homeDirectory}/.npm-global";
in {
home.file.".npmrc".text = ''
prefix=${npmGlobalPrefix}
'';
home.sessionVariables.NPM_CONFIG_PREFIX = npmGlobalPrefix;
}: {
# Agent Git Identity configuration
# Note: Uses existing gitea SSH key (m3tam3re identity) for push auth
coding.agents.gitIdentity = {
enable = true;
name = "m3ta-chiron";
email = "m3ta-chiron@agentmail.to";
sshKey = "/home/m3tam3re/.ssh/gitea";
};
imports = [
# OpenCode and Pi agent configurations
./opencode.nix
./pi.nix
];
coding.agents.skills = {
agentsInput = inputs.agents;
externalSkills = [
{
src = inputs.skills-anthropic;
selectSkills = ["pdf" "docx" "frontend-design"];
}
{src = inputs.skills-superpowers;}
{src = inputs.skills-vercel;}
{src = inputs.skills-basecamp;}
{src = inputs.skills-kestra;}
];
};
programs.mcp = {
enable = true;
servers = {
@@ -47,39 +71,15 @@ in {
home.packages = with pkgs; [
agenix-cli
alejandra
bc
bun
devpod
#devpod-desktop
claude-code
code2prompt
gnumake
cmake
(python3.withPackages (ps:
with ps; [
pip
uv
# Scientific packages
numba
numpy
torch
srt
]))
pyrefly
nixd
nodejs
# opencode-desktop
agent-browser
beads
pi
(qmd.override {
vulkanSupport = videoDrivers == ["amdgpu"];
cudaSupport = videoDrivers == ["nvidia"];
})
# opencode-desktop
openshell
openspec
alejandra
sidecar
tailwindcss
tailwindcss-language-server
td
];
}

View File

@@ -0,0 +1,260 @@
{
inputs,
lib,
...
}: {
coding.agents.opencode = {
enable = true;
agentsInput = inputs.agents;
};
coding.opencode = {
enable = true;
ohMyOpencodeSettings = {
agents = {
sisyphus.model = "litellm/claude-opus-4-6";
oracle.model = "litellm/claude-sonnet-4-6";
librarian.model = "litellm/claude-sonnet-4-6";
explore.model = "litellm/claude-haiku-4-5";
multimodal-looker.model = "litellm/gpt-5.3-codex";
prometheus.model = "litellm/claude-opus-4-6";
metis.model = "litellm/claude-opus-4-6";
momus.model = "litellm/claude-opus-4-6";
atlas.model = "litellm/claude-sonnet-4-6";
};
categories = {
visual-engineering.model = "zai-coding-plan/glm-5.1";
ultrabrain.model = "litellm/claude-opus-4-6";
deep.model = "litellm/claude-sonnet-4-6";
artistry.model = "zai-coding-plan/glm-5.1";
quick.model = "litellm/claude-haiku-4-5";
unspecified-low.model = "litellm/claude-sonnet-4-6";
unspecified-high.model = "litellm/claude-opus-4-6";
writing.model = "zai-coding-plan/glm-5.1";
};
};
};
# Keep TUI settings in programs.opencode.tui to satisfy OpenCode v1.2.15+.
programs.opencode.tui.theme = "opencode";
# Override legacy default settings to avoid deprecated TUI keys in settings.
programs.opencode.settings = lib.mkForce {
plugin = ["oh-my-openagent"];
formatter = {
alejandra = {
command = ["alejandra" "-q" "-"];
extensions = [".nix"];
};
};
# Security: permission hardening for OpenCode
# Last matching rule wins. Glob patterns: * = any chars, ? = single char.
# ~ and $HOME are expanded to the user's home directory.
# external_directory gates paths outside the working directory.
permission = {
# External directory access: ask by default, allow safe paths
"external_directory" = {
"*" = "ask";
"/nix/store/**" = "allow";
"/tmp/**" = "allow";
};
# Read access: allow by default, deny sensitive paths
"read" = {
"*" = "allow";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"~/.aws/**" = "deny";
"~/.kube/**" = "deny";
"~/.config/gh/**" = "deny";
"~/.config/gcloud/**" = "deny";
"~/.config/op/**" = "deny";
"~/.config/sops/**" = "deny";
"/run/agenix/**" = "deny";
"~/.pi/agent/auth.json" = "deny";
"~/.pi/agent/sessions/**" = "deny";
"*.env" = "deny";
"*.env.*" = "deny";
"*.pem" = "deny";
"*.key" = "deny";
"*.p12" = "deny";
"*.pfx" = "deny";
"*id_rsa*" = "deny";
"*id_ed25519*" = "deny";
"*id_ecdsa*" = "deny";
"*.example.env" = "allow";
"*.sample.env" = "allow";
"*.test.env" = "allow";
".env.example" = "allow";
".env.sample" = "allow";
".env.test" = "allow";
"~/.ssh/*.pub" = "allow";
"*.pub" = "allow";
"*.csr" = "allow";
};
# Edit access: ask by default, deny sensitive paths
"edit" = {
"*" = "ask";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"~/.aws/**" = "deny";
"~/.kube/**" = "deny";
"~/.config/gh/**" = "deny";
"~/.config/gcloud/**" = "deny";
"~/.config/op/**" = "deny";
"~/.config/sops/**" = "deny";
"/run/agenix/**" = "deny";
"~/.pi/agent/auth.json" = "deny";
"~/.pi/agent/sessions/**" = "deny";
"*.env" = "deny";
"*.env.*" = "deny";
"*.pem" = "deny";
"*.key" = "deny";
"*.p12" = "deny";
"*.pfx" = "deny";
"*id_rsa*" = "deny";
"*id_ed25519*" = "deny";
"*id_ecdsa*" = "deny";
"~/.ssh/*.pub" = "allow";
"*.pub" = "allow";
"*.csr" = "allow";
};
# Glob patterns: same rules as read for file matching
"glob" = {
"*" = "allow";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"/run/agenix/**" = "deny";
"*.env" = "deny";
"*.env.*" = "deny";
"*.pem" = "deny";
"*.key" = "deny";
"*.p12" = "deny";
"*.pfx" = "deny";
};
# Grep: allow search, but deny searching for secrets
"grep" = {
"*" = "allow";
"~/.ssh/**" = "deny";
"~/.gnupg/**" = "deny";
"/run/agenix/**" = "deny";
"*PASSWORD*" = "ask";
"*SECRET*" = "ask";
"*API_KEY*" = "ask";
"*PRIVATE_KEY*" = "ask";
};
# Bash: ask by default, deny dangerous and env-leak commands
"bash" = {
"*" = "ask";
"git status*" = "allow";
"git diff*" = "allow";
"git log*" = "allow";
"git branch*" = "allow";
"git show*" = "allow";
"git remote*" = "allow";
"nix --version" = "allow";
"nix eval*" = "allow";
"nix build*" = "allow";
"nix develop*" = "allow";
"nix shell*" = "allow";
"nix search*" = "allow";
"alejandra*" = "allow";
"git add*" = "allow";
"git commit*" = "allow";
"git push*" = "ask";
"git pull*" = "allow";
"rm *" = "ask";
"rm -rf *" = "deny";
"sudo *" = "ask";
"env" = "deny";
"printenv" = "deny";
"cat /proc/*/environ" = "deny";
"gpg *--export-secret*" = "deny";
"ssh-add -D" = "deny";
"docker run --privileged*" = "deny";
"curl *| *sh" = "deny";
"wget *| *sh" = "deny";
};
# Web fetch: ask for sensitive URLs
"webfetch" = {
"*" = "ask";
"https://api.github.com*" = "allow";
"https://search.nixos.org*" = "allow";
};
# Doom loop guard
"doom_loop" = "ask";
};
# AZ-Gruppe LiteLLM endpoint + available models
provider = {
litellm = {
npm = "@ai-sdk/openai-compatible";
name = "LiteLLM (AZ-Gruppe)";
options.baseURL = "https://llm.az-gruppe.com/v1";
models = {
"gpt-5.2" = {
name = "GPT-5.2";
limit = {
context = 400000;
output = 128000;
};
};
"gpt-5.3-codex" = {
name = "GPT-5.3 Codex";
limit = {
context = 400000;
output = 128000;
};
};
"claude-haiku-4-5" = {
name = "Claude Haiku 4.5";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 64000;
};
};
"claude-sonnet-4-6" = {
name = "Claude Sonnet 4.6";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 64000;
};
};
"claude-opus-4-6" = {
name = "Claude Opus 4.6";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 128000;
};
};
};
};
};
};
}

269
home/coding/agents/pi.nix Normal file
View File

@@ -0,0 +1,269 @@
{inputs, ...}: {
coding.agents.pi = {
enable = true;
agentsInput = inputs.agents;
modelOverrides = {
chiron = "minimax/MiniMax-M2.7";
chiron-forge = "minimax/MiniMax-M2.7";
};
# Coding rules for Pi agent
# Rules sourced from AGENTS repo
codingRules = {
# Language-specific rules
languages = [
"nix" # Nix language conventions
];
# Standard concerns from AGENTS repo
concerns = [
"coding-style" # General coding principles
"naming" # Naming conventions (camelCase, snake_case, etc.)
"documentation" # Documentation standards
"testing" # Testing guidelines (Arrange-Act-Assert)
"git-workflow" # Conventional commits, branch naming
"git-identity" # Git identity configuration for agents
"project-structure" # Project layout conventions
];
# No framework-specific rules for NixOS config
frameworks = [];
};
settings = {
packages = [
"npm:@dreadedzombie/pi-init"
"npm:@plannotator/pi-extension"
"npm:@thesethrose/pi-zai-provider"
"npm:pi-agent-browser-native"
"npm:pi-beads-extension"
"npm:pi-lens"
"npm:pi-markdown-preview"
"npm:pi-mcp-adapter"
"npm:pi-powerline-footer"
"npm:pi-prompt-template-model"
"npm:pi-subagents"
"npm:pi-tool-display"
"npm:pi-web-access"
"git:github.com/hk-vk/pi-connect"
];
defaultProvider = "minimax";
defaultModel = "MiniMax-M2.7";
defaultThinkingLevel = "high";
};
# pi-guardrails: strict security config
# NOTE: Path access checks are lexical (not symlink-safe).
# NOTE: Local project .pi/extensions/guardrails.json can override same rule IDs.
# For immutable global policies, consider a wrapper or upstream patch.
guardrails = {
enable = true;
config = {
enabled = true;
applyBuiltinDefaults = true;
onboarding = {
completed = true;
};
features = {
policies = true;
permissionGate = true;
pathAccess = true;
};
pathAccess = {
mode = "ask";
allowedPaths = [
"/nix/store/"
"/tmp/"
];
};
policies = {
rules = [
# ── SSH keys ───────────────────────────────────────────
{
id = "home-ssh";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "~/.ssh/**";}
{pattern = "~/.ssh/*_rsa";}
{pattern = "~/.ssh/*_ed25519";}
{pattern = "~/.ssh/*.pem";}
];
allowedPatterns = [
{pattern = "~/.ssh/*.pub";}
];
}
# ── GPG keys ─────────────────────────────────────────
{
id = "home-gpg";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "~/.gnupg/**";}
{pattern = "~/*.gpg";}
{pattern = "~/.gpg-agent.conf";}
];
}
# ── AWS credentials ────────────────────────────────────
{
id = "home-aws";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "~/.aws/**";}
{pattern = "~/.aws/credentials";}
{pattern = "~/.aws/config";}
];
}
# ── Kubernetes configs ────────────────────────────────
{
id = "home-kube";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "~/.kube/**";}
{pattern = "*kubeconfig*";}
];
}
# ── Cloud CLI configs ────────────────────────────────
{
id = "home-config";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "~/.config/gh/**";}
{pattern = "~/.config/gcloud/**";}
{pattern = "~/.config/op/**";}
{pattern = "~/.config/sops/**";}
];
}
# ── agenix secrets ───────────────────────────────────
{
id = "agenix-secrets";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "/run/agenix/**";}
];
}
# ── Pi auth and sessions ────────────────────────────
{
id = "pi-auth-sessions";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "~/.pi/agent/auth.json";}
{pattern = "~/.pi/agent/sessions/**";}
];
}
# ── Environment files ─────────────────────────────────
{
id = "secret-files";
enabled = true;
protection = "noAccess";
onlyIfExists = true;
patterns = [
{pattern = ".env";}
{pattern = ".env.*";}
{pattern = ".dev.vars";}
];
allowedPatterns = [
{pattern = "*.example.env";}
{pattern = "*.sample.env";}
{pattern = "*.test.env";}
{pattern = ".env.example";}
{pattern = ".env.sample";}
{pattern = ".env.test";}
];
}
# ── Private keys and certificates ───────────────────
{
id = "private-keys";
enabled = true;
protection = "noAccess";
onlyIfExists = false;
patterns = [
{pattern = "*.pem";}
{pattern = "*.key";}
{pattern = "*.p12";}
{pattern = "*.pfx";}
{pattern = "*id_rsa*";}
{pattern = "*id_ed25519*";}
{pattern = "*id_ecdsa*";}
];
allowedPatterns = [
{pattern = "*.pub";}
{pattern = "*.csr";}
];
}
];
};
permissionGate = {
explainCommands = false;
# Auto-deny patterns: env leakage and credential dumping
autoDenyPatterns = [
{
pattern = "\\benv\\b";
regex = true;
description = "env command (may dump environment)";
}
{
pattern = "\\bprintenv\\b";
regex = true;
description = "printenv command (dumps environment variables)";
}
{
pattern = "/proc/[0-9]+/environ";
regex = true;
description = "reading process environment files";
}
{
pattern = "gpg\\s+--export-secret-keys";
regex = true;
description = "GPG secret key export";
}
{
pattern = "gpg\\s+--export-secret-subkeys";
regex = true;
description = "GPG secret subkey export";
}
{
pattern = "ssh-add\\s+-D";
regex = true;
description = "delete all SSH identities";
}
{
pattern = "\\b(op|pass)\\s+(read|show|get)";
regex = true;
description = "password manager read operations";
}
];
};
};
};
# MCP servers auto-inherited from programs.mcp in default.nix
};
}

12
home/coding/default.nix Normal file
View File

@@ -0,0 +1,12 @@
# Coding environment aggregator — profile-independent development tooling.
# Imports editors, LSP servers, git configuration, the agent system, language runtimes, and optional packages.
{...}: {
imports = [
./editor
./lsp
./git/git.nix
./agents/agents.nix
./languages
./packages.nix
];
}

View File

@@ -0,0 +1,6 @@
# Editor aggregator — delegates to m3ta-nixpkgs editor modules.
{...}: {
imports = [
./neovim.nix
];
}

View File

@@ -0,0 +1,7 @@
# NeoVim base configuration via m3ta-nixpkgs coding.editors module.
# The option `coding.editors.neovim.enable` is declared by
# inputs.m3ta-nixpkgs.homeManagerModules.default — no re-declaration here.
{...}: {
# Placeholder for host-agnostic NeoVim overrides.
# Set coding.editors.neovim.enable = true in per-host files to activate.
}

41
home/coding/git/git.nix Normal file
View File

@@ -0,0 +1,41 @@
# Git configuration with signing, aliases, and global ignore.
# Identity and host-specific SSH keys are set per-host in home/m3tam3re/.
{
lib,
pkgs,
...
}:
with lib; {
programs.git = {
enable = true;
signing.format = null;
settings = {
user = {
name = lib.mkDefault "m3tam3re";
email = lib.mkDefault "p@m3ta.dev";
};
core.excludesfile = "~/.gitignore_global";
init.defaultBranch = "master";
alias = {
st = "status";
logd = "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit";
};
};
};
programs.difftastic.enable = true;
programs.jujutsu = {
enable = true;
settings = {
user = {
email = "m@m3tam3re.com";
name = "Sascha Koenig";
};
};
};
home.packages = with pkgs; [
lazygit
];
}

View File

@@ -0,0 +1,10 @@
# Language runtimes — Python, JavaScript, Rust, Go, TypeScript.
{...}: {
imports = [
./python.nix
./javascript.nix
./rust-toolchain.nix
./go.nix
./typescript.nix
];
}

View File

@@ -0,0 +1,19 @@
# Go toolchain — compiler and language server.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.coding.languages.go;
in {
options.coding.languages.go.enable = mkEnableOption "Go toolchain";
config = mkIf cfg.enable {
home.packages = with pkgs; [
go
gopls
];
};
}

View File

@@ -0,0 +1,25 @@
# JavaScript/TypeScript runtime — Node.js and Bun.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.coding.languages.javascript;
npmGlobalPrefix = "${config.home.homeDirectory}/.npm-global";
in {
options.coding.languages.javascript.enable = mkEnableOption "JavaScript runtime (Node.js + Bun)";
config = mkIf cfg.enable {
home.packages = with pkgs; [
nodejs
bun
];
home.file.".npmrc".text = ''
prefix=${npmGlobalPrefix}
'';
home.sessionVariables.NPM_CONFIG_PREFIX = npmGlobalPrefix;
};
}

View File

@@ -0,0 +1,35 @@
# Python runtime with pip and uv.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.coding.languages.python;
in {
options.coding.languages.python = {
enable = mkEnableOption "Python runtime with pip and uv";
extraPackages = mkOption {
type = types.listOf types.package;
default = [];
example = literalExpression "[ pkgs.python3Packages.numpy ]";
description = "Additional Python packages to include";
};
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
(pkgs.python3.withPackages (ps:
with ps;
[
uv
]
++ cfg.extraPackages))
(writeShellScriptBin "pip" "exec uv pip $@")
(writeShellScriptBin "pip3" "exec uv pip $@")
pyrefly
ruff
];
};
}

View File

@@ -0,0 +1,20 @@
# Rust toolchain — compiler, package manager, and language server.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.coding.languages.rustToolchain;
in {
options.coding.languages.rustToolchain.enable = mkEnableOption "Rust toolchain";
config = mkIf cfg.enable {
home.packages = with pkgs; [
rustc
cargo
rust-analyzer
];
};
}

View File

@@ -0,0 +1,19 @@
# TypeScript support — language server and type checking tools.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.coding.languages.typescript;
in {
options.coding.languages.typescript.enable = mkEnableOption "TypeScript support";
config = mkIf cfg.enable {
home.packages = with pkgs; [
typescript
typescript-language-server
];
};
}

View File

@@ -0,0 +1,6 @@
# LSP aggregator — language server protocol tooling.
{...}: {
imports = [
./servers.nix
];
}

View File

@@ -0,0 +1,23 @@
# LSP server configuration — language servers for the development environment.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.coding.lsp;
in {
options.coding.lsp.enable = mkEnableOption "enable LSP servers";
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Nix
nixd
# General
typescript-language-server
tailwindcss-language-server
pyrefly
];
};
}

20
home/coding/packages.nix Normal file
View File

@@ -0,0 +1,20 @@
# Additional coding packages — API clients and GUI development tools.
# Opt-in since not all coding hosts need these desktop-oriented tools.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.coding.packages;
in {
options.coding.packages.enable = mkEnableOption "additional coding packages (bruno, insomnia)";
config = mkIf cfg.enable {
home.packages = [
pkgs.bruno
pkgs.insomnia
];
};
}

View File

@@ -0,0 +1,16 @@
# Cryptocurrency applications — Bisq, Monero GUI, and Trezor Suite.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.desktop.apps.crypto;
in {
options.desktop.apps.crypto.enable = mkEnableOption "enable crypto applications";
config = mkIf cfg.enable {
home.packages = with pkgs; [bisq2 monero-gui trezor-suite];
};
}

View File

@@ -0,0 +1,10 @@
# Desktop apps aggregator — Obsidian, Office, web apps, crypto tools, and productivity.
{...}: {
imports = [
./obsidian.nix
./office.nix
./webapps.nix
./crypto.nix
./productivity.nix
];
}

View File

@@ -1,13 +1,13 @@
# Obsidian knowledge base with markdown MIME association.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.desktop.obsidian;
cfg = config.desktop.apps.obsidian;
in {
options.features.desktop.obsidian.enable =
mkEnableOption "enable Obsidian knowledge base";
options.desktop.apps.obsidian.enable = mkEnableOption "enable Obsidian knowledge base";
config = mkIf cfg.enable {
programs.obsidian.enable = true;

View File

@@ -0,0 +1,16 @@
# Office and productivity applications — LibreOffice and document tools.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.desktop.apps.office;
in {
options.desktop.apps.office.enable = mkEnableOption "install office and paperwork apps";
config = mkIf cfg.enable {
home.packages = [pkgs.libreoffice-fresh];
};
}

View File

@@ -0,0 +1,18 @@
# Productivity tools — Pomodoro timer and focus utilities.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.desktop.apps.productivity;
in {
options.desktop.apps.productivity.enable = mkEnableOption "enable productivity tools";
config = mkIf cfg.enable {
home.packages = with pkgs; [
pomodoro-timer
];
};
}

View File

@@ -1,6 +1,7 @@
# Web application desktop entries — Teams, Outlook, Basecamp, and OpenCode launchers.
{
config,
pkgs,
lib,
...
}: let
icons = {
@@ -41,7 +42,7 @@ in {
comment = "Open Basecamp as a Desktop App";
categories = ["Application" "Network"];
terminal = false;
icon = "/home/sascha.koenig/.local/share/icons/basecamp-logo.png";
icon = "${config.home.homeDirectory}/.local/share/icons/basecamp-logo.png";
};
opencode = {
name = "Opencode";

View File

@@ -1,22 +1,14 @@
# Desktop environment aggregator — only loaded when context=desktop.
# Includes window manager, applications, theming, and desktop session config.
{
config,
pkgs,
...
}: {
imports = [
./coding.nix
./crypto.nix
./fonts.nix
./gaming.nix
./hyprland.nix
./media.nix
./obsidian.nix
./office.nix
./rofi.nix
./theme.nix
./wallpapers.nix
./wayland.nix
./webapps.nix
./wm
./apps
./theme
];
xdg = {
@@ -55,7 +47,13 @@
XDG_SESSION_TYPE = "wayland";
XDG_SESSION_DESKTOP = "Hyprland";
};
home.sessionPath = ["\${XDG_BIN_HOME}" "\${HOME}/.cargo/bin" "$HOME/.npm-global/bin" "$HOME/.cache/.bun/bin"];
home.sessionPath = [
"\${XDG_BIN_HOME}"
"\${HOME}/.cargo/bin"
"$HOME/.npm-global/bin"
"$HOME/.cache/.bun/bin"
];
fonts.fontconfig.enable = true;
@@ -66,17 +64,11 @@
settings = {
font-family = "Fira Code";
copy-on-select = true;
# Base colors from nix-colors
foreground = "#${config.colorScheme.palette.base05}";
background = "#${config.colorScheme.palette.base00}";
selection-foreground = "#${config.colorScheme.palette.base07}";
selection-background = "#${config.colorScheme.palette.base02}";
# Cursor
cursor-color = "#${config.colorScheme.palette.base05}";
# Palette (16 colors)
palette = [
"0=#${config.colorScheme.palette.base01}"
"1=#${config.colorScheme.palette.base08}"
@@ -107,56 +99,27 @@
home.packages = with pkgs; [
appimage-run
# blueberry
bemoji
brave
# brightnessctl
# clipman
distrobox
eigent
(element-desktop.override {
commandLineArgs = "--password-store=gnome-libsecret";
})
launch-webapp
# eww
# firefox-devedition
file-roller
hyprpanel
seahorse
sushi
# glib
# google-chrome
# gsettings-desktop-schemas
# graphviz
ksnip
msty-studio
nwg-look
# opencode-desktop
# pamixer
# pavucontrol
# libsForQt5.qtstyleplugins
# stable.nyxt
# pcmanfm
rose-pine-hyprcursor
# qt5ct
# qt6.qtwayland
#rustdesk
# socat
# unrar
# unzip
# usbutils
# v4l-utils
remmina
slack
telegram-desktop
vivaldi
vivaldi-ffmpeg-codecs
vibetyper
# wl-clipboard
# wlogout
# wtype
# xdg-utils
# ydotool
# zip
];
}

View File

@@ -0,0 +1,8 @@
# Theme aggregator — fonts, GTK/Qt theming, and wallpapers.
{...}: {
imports = [
./fonts.nix
./theme.nix
./wallpapers.nix
];
}

View File

@@ -1,3 +1,4 @@
# Font packages — Fira Code, JetBrains Mono Nerd Font, and supporting icon fonts.
{
config,
lib,
@@ -5,10 +6,9 @@
...
}:
with lib; let
cfg = config.features.desktop.fonts;
cfg = config.desktop.theme.fonts;
in {
options.features.desktop.fonts.enable =
mkEnableOption "install additional fonts for desktop apps";
options.desktop.theme.fonts.enable = mkEnableOption "install desktop fonts";
config = mkIf cfg.enable {
home.packages = with pkgs; [

View File

@@ -1,12 +1,14 @@
# GTK and Qt theming — Dracula theme with matching icons and cursor.
{
config,
pkgs,
config,
...
}: {
qt = {
enable = true;
platformTheme.name = "gtk";
};
gtk = {
enable = true;
theme = {

View File

@@ -0,0 +1,19 @@
# Wallpaper collection — copies wallpapers to Hyprland config directory.
{
config,
lib,
...
}:
with lib; let
cfg = config.desktop.theme.wallpapers.enable;
in {
options.desktop.theme.wallpapers.enable = mkEnableOption "wallpapers for Hyprland";
config = mkIf cfg {
xdg.configFile."hypr/wallpapers" = {
# Wallpapers are stored relative to the home/m3tam3re directory.
source = ../../m3tam3re/wallpapers;
recursive = true;
};
};
}

View File

@@ -0,0 +1,8 @@
# Window manager aggregator — Hyprland, Wayland tools, and Rofi launcher.
{...}: {
imports = [
./hyprland.nix
./wayland.nix
./rofi.nix
];
}

View File

@@ -1,13 +1,13 @@
# Hyprland window manager with keybindings, window rules, idle/lock, and hyprpaper.
{
config,
lib,
...
}:
with lib; let
cfg = config.features.desktop.hyprland;
cfg = config.desktop.wm.hyprland;
in {
options.features.desktop.hyprland.enable =
mkEnableOption "Hyprland related stuff";
options.desktop.wm.hyprland.enable = mkEnableOption "Hyprland window manager";
config = mkIf cfg.enable {
wayland.windowManager.hyprland = {
@@ -19,8 +19,8 @@ in {
exec-once = [
"hyprpanel"
"while ! hyprpaper-random; do sleep 0.5; done"
"wl-paste --type text --watch cliphist store" # Stores only text data
"wl-paste --type image --watch cliphist store" # Stores only image data "wl-paste -p -t text --watch clipman store -P --histpath=\"~/.local/share/clipman-primary.json\""
"wl-paste --type text --watch cliphist store"
"wl-paste --type image --watch cliphist store"
"vibetyper"
];
@@ -49,7 +49,6 @@ in {
gaps_in = 5;
gaps_out = 5;
border_size = 1;
# Keeping the existing active border as requested
"col.active_border" = "rgba(9742b5ee) rgba(9742b5ee) 45deg";
"col.inactive_border" = "rgba(${config.colorScheme.palette.base03}aa)";
layout = "dwindle";
@@ -110,8 +109,8 @@ in {
kb_layout = "us";
}
];
windowrule = [
# Floating dialogs
"match:class file_progress, float on"
"match:class confirm, float on"
"match:class dialog, float on"
@@ -125,24 +124,19 @@ in {
"match:class pavucontrol-qt, float on"
"match:class pavucontrol, float on"
"match:class class:^(espanso)$, float on"
# wlogout
"match:class wlogout, fullscreen on"
"match:title wlogout, float on"
"match:title wlogout, fullscreen on"
# mpv
"match:class mpv, float on"
"match:class mpv, idle_inhibit focus"
"match:class mpv, opacity 1.0 override"
# Media/Volume/PiP
"match:title ^(Media viewer)$, float on"
"match:title ^(Volume Control)$, float on"
"match:title ^(Picture-in-Picture)$, float on"
# Pomodoro timer
"match:title ^(floating-pomodoro)$, float on"
"match:title ^(floating-pomodoro)$, size 250 50"
"match:title ^(floating-pomodoro)$, move 12 (monitor_h-150)"
"match:title ^(floating-pomodoro)$, pin on"
# Streamlabs overlays
"match:initial_title .*streamlabs.com.*, float on"
"match:initial_title .*streamlabs.com.*, pin on"
"match:initial_title .*streamlabs.com.*, size 800 400"
@@ -154,16 +148,15 @@ in {
"match:initial_title .*streamlabs.com.*, decorate off"
"match:initial_title .*streamlabs.com.*, no_shadow on"
"match:initial_title .*streamlabs.com.*, no_blur on"
# Vibetyper recording indicator
"match:class ^vibe-typer$, match:title ^Recording Indicator$, no_blur on"
"border_color rgb(ffffff), match:xwayland 1"
];
"$mainMod" = "SUPER";
"$terminal" = "ghostty";
bind = [
"$mainMod, return, exec, $terminal nu -c zellij-ps"
# "$mainMod, t, exec, warp-terminal"
"$mainMod, t, exec, $terminal -e nu -c 'nitch; exec nu'"
"$mainMod SHIFT, t, exec, launch-timer"
"$mainMod, n, exec, $terminal -e nvim"
@@ -214,16 +207,19 @@ in {
"$mainMod, mouse_down, workspace, e+1"
"$mainMod, mouse_up, workspace, e-1"
];
bindr = [
"$mainMod, R, exec, stt-ptt stop"
"$mainMod, S, exec, stt-ptt format-stop"
];
bindm = [
"$mainMod, mouse:272, movewindow"
"$mainMod, mouse:273, resizewindow"
];
};
};
services.hypridle = {
enable = true;
settings = {
@@ -232,14 +228,13 @@ in {
after_sleep_cmd = "hyprctl dispatch dpms on";
inhibit_sleep = 3;
};
listener = [
{
timeout = 300; # 5 min
timeout = 300;
on-timeout = "hyprlock";
}
{
timeout = 420; # 5.5 min
timeout = 420;
on-timeout = "hyprctl dispatch dpms off";
on-resume = "hyprctl dispatch dpms on";
}

View File

@@ -1,3 +1,4 @@
# Rofi application launcher with nix-colors theme, pass integration, and project opener.
{
config,
pkgs,
@@ -5,23 +6,23 @@
...
}:
with lib; let
cfg = config.features.desktop.rofi;
cfg = config.desktop.wm.rofi;
in {
options.features.desktop.rofi.enable = mkEnableOption "enable rofi";
options.desktop.wm.rofi.enable = mkEnableOption "enable rofi";
config = mkIf cfg.enable {
programs.rofi = with pkgs; {
programs.rofi = {
enable = true;
package = rofi.override {
package = pkgs.rofi.override {
plugins = [
rofi-calc
rofi-emoji
stable.rofi-file-browser
pkgs.rofi-calc
pkgs.rofi-emoji
pkgs.stable.rofi-file-browser
];
};
pass = {
enable = true;
package = rofi-pass-wayland;
package = pkgs.rofi-pass-wayland;
};
terminal = "${pkgs.ghostty}/bin/ghostty";
font = "Fira Code";
@@ -169,7 +170,6 @@ in {
text-color: @background;
}
/* Scrollbar */
scrollbar {
width: 4px;
border: 0;
@@ -179,6 +179,7 @@ in {
}
'');
};
cli.rofi-project-opener = {
enable = true;
projectDirs = {

View File

@@ -1,3 +1,4 @@
# Wayland extra tooling — screenshot, clipboard, cursor, and display utilities.
{
config,
lib,
@@ -5,9 +6,9 @@
...
}:
with lib; let
cfg = config.features.desktop.wayland;
cfg = config.desktop.wm.wayland;
in {
options.features.desktop.wayland.enable = mkEnableOption "wayland extra tools and config";
options.desktop.wm.wayland.enable = mkEnableOption "wayland extra tools and config";
config = mkIf cfg.enable {
home.packages = with pkgs; [

View File

@@ -1,65 +0,0 @@
# CLI FEATURES (home-manager)
**Shell and terminal tooling with Fish + Nushell dual configuration**
## OVERVIEW
8 CLI modules with integrated tooling across Fish and Nushell shells.
## STRUCTURE
```
cli/
├── default.nix # Imports + shared tools (bat, eza, direnv)
├── fish.nix # Fish shell + aliases
├── fzf.nix # Fuzzy finder
├── nitch.nix # System info tool
├── nushell.nix # Nushell + aliases
├── secrets.nix # Password-store integration
├── starship.nix # Shell prompt
└── zellij.nix # Terminal multiplexer
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Add CLI tool | default.nix home.packages | Check if shell integration needed |
| Shell aliases | fish.nix or nushell.nix | Kept in sync between shells |
| Prompt config | starship.nix | Uses nerd-fonts symbols |
| Secret access | secrets.nix | Agenix integration |
## CONVENTIONS
### Shell Integration Pattern
Tools with shell hooks enabled in both Fish and Nushell:
- **carapace**: Completions
- **zoxide**: Smart cd
- **eza**: ls replacement
- **direnv**: Directory environments
- **fzf**: Fuzzy finding
### NixOS Rebuild Aliases (both shells)
```
nr/nrs - nixos-rebuild [switch]
snr/snrs - sudo nixos-rebuild [switch]
hms - home-manager switch
```
### Bat Theme
Custom `universal` theme generated from nix-colors palette in default.nix (lines 34-157).
### Secrets Integration
Fish/Nushell source `$HOME/.secrets` if `secrets.enable = true` (CLI secrets feature).
## ANTI-PATTERNS
- **DON'T** add aliases to only one shell - keep Fish/Nushell in sync
- **DON'T** use `programs.bash` - Nushell is default shell
- **DON'T** bypass carapace for completions - integrated by default
## NOTES
- zellij-ps custom package for project session management
- Default shell set to Nushell in hosts/common/default.nix
- Bat theme dynamically generated (no external theme files)
- lf file manager uses bat for previews
- Agenix CLI (agenix-cli) included for secret management

View File

@@ -1,230 +0,0 @@
{
config,
pkgs,
videoDrivers,
...
}: {
imports = [
./fish.nix
./fzf.nix
./nitch.nix
./nushell.nix
./secrets.nix
./starship.nix
./television.nix
./zellij.nix
];
programs.carapace = {
enable = true;
enableFishIntegration = true;
enableNushellIntegration = true;
enableBashIntegration = true;
};
programs.zoxide = {
enable = true;
enableFishIntegration = true;
enableNushellIntegration = true;
};
programs.bat = {
enable = true;
config = {theme = "universal";};
themes = {
universal = {
src = pkgs.writeText "universal.tmTheme" ''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>Universal (nix-colors)</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#${config.colorScheme.palette.base00}</string>
<key>foreground</key>
<string>#${config.colorScheme.palette.base05}</string>
<key>caret</key>
<string>#${config.colorScheme.palette.base05}</string>
<key>selection</key>
<string>#${config.colorScheme.palette.base02}</string>
<key>selectionForeground</key>
<string>#${config.colorScheme.palette.base05}</string>
<key>lineHighlight</key>
<string>#${config.colorScheme.palette.base01}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base03}</string>
<key>fontStyle</key>
<string>italic</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base0A}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base0E}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base08}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function</string>
<key>scope</key>
<string>entity.name.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base0B}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Type</string>
<key>scope</key>
<string>entity.name.type, storage.type</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base0D}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base05}</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Constant</string>
<key>scope</key>
<string>constant</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#${config.colorScheme.palette.base0E}</string>
</dict>
</dict>
</array>
</dict>
</plist>
'';
};
};
};
programs.direnv = {
enable = true;
enableNushellIntegration = true;
nix-direnv.enable = true;
};
programs.eza = {
enable = true;
enableFishIntegration = true;
enableBashIntegration = true;
extraOptions = ["-l" "--icons" "--git" "-a"];
};
programs.lf = {
enable = true;
settings = {
preview = true;
drawbox = true;
hidden = true;
icons = true;
theme = "Dracula";
previewer = "bat";
};
};
cli.zellij-ps = {
enable = true;
projectFolders = ["/home/m3tam3re/p"];
};
home.packages = with pkgs; [
basecamp
comma
coreutils
devenv
fabric-ai
fd
gcc
go
htop
httpie
hyprpaper-random
jq
just
lazygit
llm
lf
nix-index
nix-update
libnotify
nushellPlugins.skim
progress
ripgrep
rocmPackages.rocm-smi
rocmPackages.rocminfo
rocmPackages.rocm-runtime
sqlite
sqlite-vec
tldr
pomodoro-timer
trash-cli
unimatrix
unzip
vulkan-tools
wttrbar
wireguard-tools
yazi
zellij-ps
zip
];
}

View File

@@ -1,15 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.cli.nitch;
in {
options.features.cli.nitch.enable = mkEnableOption "enable nitch";
config = mkIf cfg.enable {
home.packages = with pkgs; [nitch];
};
}

View File

@@ -1,126 +0,0 @@
{
inputs,
lib,
...
}: {
coding.agents.opencode = {
enable = true;
agentsInput = inputs.agents;
externalSkills = [
{
src = inputs.skills-anthropic;
selectSkills = ["pdf" "docx" "frontend-design"];
}
{src = inputs.skills-vercel;}
{src = inputs.skills-basecamp;}
{src = inputs.skills-kestra;}
];
};
coding.opencode = {
enable = true;
ohMyOpencodeSettings = {
agents = {
sisyphus.model = "litellm/claude-opus-4-6";
oracle.model = "litellm/claude-sonnet-4-6";
librarian.model = "litellm/claude-sonnet-4-6";
explore.model = "litellm/claude-haiku-4-5";
multimodal-looker.model = "litellm/gpt-5.3-codex";
prometheus.model = "litellm/claude-opus-4-6";
metis.model = "litellm/claude-opus-4-6";
momus.model = "litellm/claude-opus-4-6";
atlas.model = "litellm/claude-sonnet-4-6";
};
categories = {
visual-engineering.model = "zai-coding-plan/glm-5.1";
ultrabrain.model = "litellm/claude-opus-4-6";
deep.model = "litellm/claude-sonnet-4-6";
artistry.model = "zai-coding-plan/glm-5.1";
quick.model = "litellm/claude-haiku-4-5";
unspecified-low.model = "litellm/claude-sonnet-4-6";
unspecified-high.model = "litellm/claude-opus-4-6";
writing.model = "zai-coding-plan/glm-5.1";
};
};
};
# Keep TUI settings in programs.opencode.tui to satisfy OpenCode v1.2.15+.
programs.opencode.tui.theme = "opencode";
# Override legacy default settings to avoid deprecated TUI keys in settings.
programs.opencode.settings = lib.mkForce {
plugin = ["oh-my-openagent"];
formatter = {
alejandra = {
command = ["alejandra" "-q" "-"];
extensions = [".nix"];
};
};
# AZ-Gruppe LiteLLM endpoint + available models
provider = {
litellm = {
npm = "@ai-sdk/openai-compatible";
name = "LiteLLM (AZ-Gruppe)";
options.baseURL = "https://llm.az-gruppe.com/v1";
models = {
"gpt-5.2" = {
name = "GPT-5.2";
limit = {
context = 400000;
output = 128000;
};
};
"gpt-5.3-codex" = {
name = "GPT-5.3 Codex";
limit = {
context = 400000;
output = 128000;
};
};
"claude-haiku-4-5" = {
name = "Claude Haiku 4.5";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 64000;
};
};
"claude-sonnet-4-6" = {
name = "Claude Sonnet 4.6";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 64000;
};
};
"claude-opus-4-6" = {
name = "Claude Opus 4.6";
options = {
thinking = {
type = "enabled";
budget_tokens = 16000;
};
};
limit = {
context = 200000;
output = 128000;
};
};
};
};
};
};
}

View File

@@ -1,44 +0,0 @@
{inputs, ...}: {
coding.agents.pi = {
enable = true;
agentsInput = inputs.agents;
container = {
enable = true;
name = "pi-agent";
projectRoots = ["/home/m3tam3re/p"];
autoStart = true;
autoNixDevelop = true;
extraRunArgs = [];
extraEnv = {
NIX_CONFIG = "experimental-features = nix-command flakes";
};
};
modelOverrides = {
chiron = "zai-coding-plan/glm-5.1";
chiron-forge = "zai-coding-plan/glm-5.1";
};
settings = {
packages = [
"npm:@thesethrose/pi-zai-provider"
"npm:pi-mcp-adapter"
"npm:pi-subagents"
"npm:pi-lens"
"npm:pi-web-access"
"npm:pi-prompt-template-model"
"npm:@plannotator/pi-extension"
"npm:pi-powerline-footer"
"npm:pi-markdown-preview"
];
defaultProvider = "zai";
defaultModel = "glm-5.1";
defaultThinkingLevel = "medium";
};
# MCP servers auto-inherited from programs.mcp in default.nix
};
}

View File

@@ -1,79 +0,0 @@
# DESKTOP FEATURES (home-manager)
**Wayland/Hyprland environment with color-coordinated tooling**
## OVERVIEW
12 modular desktop features with nix-colors (Dracula) integration across all components.
## STRUCTURE
```
desktop/
├── default.nix # Imports + XDG + Kitty config
├── coding.nix # Development tools (VSCode, etc.)
├── crypto.nix # Crypto wallets/tools
├── fonts.nix # Font packages
├── gaming.nix # Gaming tools/Steam
├── hyprland.nix # Hyprland WM configuration
├── media.nix # Media players/editors
├── office.nix # LibreOffice, document tools
├── rofi.nix # Application launcher
├── theme.nix # GTK/Qt theming
├── wayland.nix # Wayland utilities
└── webapps.nix # Browser-based apps
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Add desktop app | Relevant feature .nix | Update home.packages |
| Configure Hyprland | hyprland.nix | Window manager settings |
| Fix colors | Check colorScheme references | Uses config.colorScheme.palette.base* |
| Add font | fonts.nix | Increases system closure size |
## CONVENTIONS
### Color Scheme Integration
All color-aware tools reference `config.colorScheme.palette.base00` through `base0F`:
- **base00-07**: Grayscale (dark to light)
- **base08**: Red/errors
- **base09**: Orange
- **base0A**: Yellow/strings
- **base0B**: Green/functions
- **base0C**: Cyan
- **base0D**: Blue/types
- **base0E**: Purple/constants
- **base0F**: Brown
Template:
```nix
foreground = "#${config.colorScheme.palette.base05}";
background = "#${config.colorScheme.palette.base00}";
```
### Session Variables
Set in default.nix for Wayland/Hyprland:
```nix
NIXOS_OZONE_WL = "1";
QT_QPA_PLATFORM = "wayland";
XDG_CURRENT_DESKTOP = "Hyprland";
```
### XDG Defaults
- **PDF**: okular
- **Text**: nvim
- **Browser**: Zen (io.github.zen_browser.zen)
- **Archive**: file-roller
## ANTI-PATTERNS
- **DON'T** hardcode hex colors - use colorScheme palette
- **DON'T** install fonts globally - keep in user packages
- **DON'T** bypass XDG defaults - set in mimeApps
## NOTES
- Kitty terminal configured in default.nix (not separate file)
- Bibata-Modern-Ice cursor theme hardcoded
- Session path includes cargo, npm-global, bun
- Desktop features are always-enabled (no feature flags in this dir)

View File

@@ -1,23 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.coding;
in {
options.features.desktop.coding.enable =
mkEnableOption "install coding related stuff";
config = mkIf cfg.enable {
home.packages = with pkgs; [
bruno
insomnia
];
coding.editors = {
neovim.enable = true;
zed.enable = true;
};
};
}

View File

@@ -1,15 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.crypto;
in {
options.features.desktop.crypto.enable = mkEnableOption "Enable Crypto";
config = mkIf cfg.enable {
home.packages = with pkgs; [bisq2 monero-gui trezor-suite];
};
}

View File

@@ -1,22 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.gaming;
in {
options.features.desktop.gaming.enable =
mkEnableOption "install gaming related stuff";
config = mkIf cfg.enable {
home.packages = with pkgs; [
gamescope
gamemode
goverlay
mangohud
protonplus
];
};
}

View File

@@ -1,55 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.media;
in {
options.features.desktop.media.enable =
mkEnableOption "enable media features";
config = mkIf cfg.enable {
home.packages = with pkgs; [
# handbrake
# kdePackages.kdenlive
# makemkv
# mediainfo
amf
ffmpeg_6-full
gimp
gst_all_1.gstreamer
gst_all_1.gst-vaapi
handbrake
inkscape
kdePackages.kdenlive
libation
#makemkv
pamixer
pavucontrol
qpwgraph
v4l-utils
plexamp
# uxplay
# vlc
webcord
# yt-dlp
unimatrix
];
programs = {
mpv = {
enable = true;
bindings = {
WHEEL_UP = "seek 10";
WHEEL_DOWN = "seek -10";
};
config = {
profile = "gpu-hq";
ytdl-format = "bestvideo+bestaudio";
};
};
};
};
}

View File

@@ -1,18 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.office;
in {
options.features.desktop.office.enable =
mkEnableOption "install office and paperwork stuff";
config = mkIf cfg.enable {
home.packages = with pkgs; [
libreoffice-fresh
];
};
}

View File

@@ -1,18 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.wallpapers;
in {
options.features.desktop.wallpapers = mkEnableOption "Wallpapers for Hyprland";
config = mkIf cfg {
xdg.configFile."hypr/wallpapers" = {
source = ../../m3tam3re/wallpapers;
recursive = true;
};
};
}

95
home/lib/default.nix Normal file
View File

@@ -0,0 +1,95 @@
# home/lib/default.nix
# Profile loading utilities for home-manager configurations.
#
# Usage:
# let homeLib = import ../lib { inherit lib; };
# in {
# imports = [
# (homeLib.mkHomeConfig { profiles = ["coding" "gaming"]; context = "desktop"; })
# ];
# }
{lib}: let
# Infrastructure layer — nixpkgs overlays, nix-colors, m3ta-nixpkgs modules.
# Always loaded on every host.
commonModule = ../common;
# Base user environment — shell (nushell, starship), CLI tools, secrets.
# Always loaded on every host.
baseModule = ../base;
# Context-specific modules — desktop and server are mutually exclusive.
contextModuleMap = {
desktop = ../desktop;
server = ../server;
};
# Profile modules — freely combinable additions on top of base + context.
profileModuleMap = {
coding = ../coding;
gaming = ../profiles/gaming;
media = ../profiles/media;
};
in {
# Generate a home-manager module with imports based on profiles and context.
#
# Args:
# profiles: list of profile names (e.g. ["coding" "gaming" "media"])
# context: host context, one of "desktop" | "server" | null
#
# Returns: a home-manager module attrset with imports and assertions.
# Desktop and server contexts are mutually exclusive by design — passing
# any value other than "desktop", "server", or null causes an assertion
# failure at evaluation time.
mkHomeConfig = {
profiles ? [],
context ? null,
}: let
contextImport =
if context == "desktop"
then [contextModuleMap.desktop]
else if context == "server"
then [contextModuleMap.server]
else [];
# Partition profiles into known and unknown for assertion + safe import.
unknownProfiles =
builtins.filter
(profileName: ! builtins.hasAttr profileName profileModuleMap)
profiles;
# Only import known profiles; the assertion below catches unknowns.
activeProfiles =
builtins.filter
(profileName: builtins.hasAttr profileName profileModuleMap)
profiles;
profileImports = map (profileName: profileModuleMap.${profileName}) activeProfiles;
contextStr =
if context == null
then "null"
else context;
in {
imports =
[commonModule baseModule]
++ contextImport
++ profileImports;
assertions = [
{
assertion = builtins.elem context ["desktop" "server" null];
message =
"m3ta home: context must be 'desktop', 'server', or null"
+ " (got: '${contextStr}')";
}
{
assertion = unknownProfiles == [];
message =
"m3ta home: unknown profiles requested:"
+ " [ ${builtins.concatStringsSep " " unknownProfiles} ]."
+ " Valid profiles are:"
+ " [ ${builtins.concatStringsSep " " (builtins.attrNames profileModuleMap)} ]";
}
];
};
}

View File

@@ -1,17 +1,25 @@
{
# m3-aether — cloud VM.
# Context: server | Profiles: (none)
{lib, ...}: let
homeLib = import ../lib {inherit lib;};
in {
imports = [
../common
../features/cli
(homeLib.mkHomeConfig {
profiles = [];
context = "server";
})
./home-server.nix
];
features = {
cli = {
# Base CLI tools (new namespace)
base = {
shell = {
fish.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
secrets.enable = false;
starship.enable = true;
};
};
}

View File

@@ -1,98 +1,142 @@
# m3-ares — TUXEDO laptop desktop workstation.
# Context: desktop | Profiles: coding, gaming, media
{
config,
lib,
...
}:
with lib; {
imports = [
../common
./home.nix
../features/cli
../features/coding
../features/desktop
];
}: let
homeLib = import ../lib {inherit lib;};
in
with lib; {
imports = [
(homeLib.mkHomeConfig {
profiles = ["coding" "gaming" "media"];
context = "desktop";
})
./home.nix
];
config = mkMerge [
{
xdg = {
# TODO: better structure
enable = true;
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
associations.added = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
config = mkMerge [
{
# Base CLI tools (new namespace)
base = {
shell = {
fish.enable = true;
nushell.enable = true;
starship.enable = true;
};
defaultApplications = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"application/md" = ["dev.zed.Zed.desktop"];
"application/text" = ["dev.zed.Zed.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
cliTools = {
fzf.enable = true;
nitch.enable = true;
television.enable = true;
};
};
};
features = {
cli = {
fish.enable = true;
nushell.enable = true;
fzf.enable = true;
nitch.enable = true;
secrets.enable = true;
starship.enable = true;
television.enable = true;
};
desktop = {
coding.enable = true;
crypto.enable = true;
gaming.enable = true;
hyprland.enable = true;
media.enable = true;
obsidian.enable = true;
office.enable = true;
rofi.enable = true;
fonts.enable = true;
wayland.enable = true;
wallpapers = true;
};
};
}
(mkIf config.features.desktop.hyprland.enable {
wayland.windowManager.hyprland = {
enable = true;
settings = {
exec-once = ["tuxedo-backlight"];
monitor = [
"eDP-1,preferred,0x0,1.25"
"HDMI-A-1,1920x1080@120,2560x0,1"
];
workspace = [
"1, monitor:eDP-1, default:true"
"2, monitor:eDP-1"
"3, monitor:eDP-1"
"4, monitor:HDMI-A-1,"
"5, monitor:HDMI-A-1,border:false,rounding:false"
"6, monitor:HDMI-A-1"
];
windowrule = [
"match:class dev.zed.Zed, workspace 1"
"match:class Msty, workspace 1"
"match:class ^(com.obsproject.Studio)$, workspace 2"
"match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"match:class ^steam_app_\\d+$, fullscreen on"
"match:class ^steam_app_\\d+$, workspace 5"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
];
# Desktop features (new namespace)
desktop = {
wm = {
hyprland.enable = true;
rofi.enable = true;
wayland.enable = true;
};
apps = {
crypto.enable = true;
obsidian.enable = true;
office.enable = true;
};
theme = {
fonts.enable = true;
wallpapers.enable = true;
};
};
};
})
];
}
# Coding environment
coding = {
editors = {
neovim.enable = true;
zed.enable = true;
};
lsp.enable = true;
packages.enable = true;
languages = {
python.enable = true;
javascript.enable = true;
rustToolchain.enable = true;
go.enable = true;
typescript.enable = true;
};
};
# Gaming profile features
profiles.gaming = {
steam.enable = true;
gamescope.enable = true;
};
# Media profile features
profiles.media = {
obs.enable = true;
ffmpeg.enable = true;
kdenlive.enable = true;
ytDlp.enable = true;
};
xdg = {
enable = true;
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
associations.added = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
};
defaultApplications = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"application/md" = ["dev.zed.Zed.desktop"];
"application/text" = ["dev.zed.Zed.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
};
};
};
}
# Host-specific Hyprland monitor and workspace layout
(mkIf config.desktop.wm.hyprland.enable {
wayland.windowManager.hyprland = {
enable = true;
settings = {
exec-once = ["tuxedo-backlight"];
monitor = [
"eDP-1,preferred,0x0,1.25"
"HDMI-A-1,1920x1080@120,2560x0,1"
];
workspace = [
"1, monitor:eDP-1, default:true"
"2, monitor:eDP-1"
"3, monitor:eDP-1"
"4, monitor:HDMI-A-1,"
"5, monitor:HDMI-A-1,border:false,rounding:false"
"6, monitor:HDMI-A-1"
];
windowrule = [
"match:class dev.zed.Zed, workspace 1"
"match:class Msty, workspace 1"
"match:class ^(com.obsproject.Studio)$, workspace 2"
"match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"match:class ^steam_app_\\d+$, fullscreen on"
"match:class ^steam_app_\\d+$, workspace 5"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
];
};
};
})
];
}

View File

@@ -1,19 +1,29 @@
{
# m3-atlas — primary server, Traefik hub and container host.
# Context: server | Profiles: coding
{lib, ...}: let
homeLib = import ../lib {inherit lib;};
in {
imports = [
../common
../features/cli
../features/coding/opencode.nix
(homeLib.mkHomeConfig {
profiles = ["coding"];
context = "server";
})
./home-server.nix
];
coding.editors.neovim.enable = true;
features = {
cli = {
# Base CLI tools (new namespace)
base = {
shell = {
nushell.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
secrets.enable = false;
starship.enable = true;
zellij.enable = true;
};
};
# Coding environment
coding.editors.neovim.enable = true;
}

View File

@@ -1,101 +1,127 @@
# m3-daedalus — portable laptop (standalone home-manager).
# Context: desktop | Profiles: coding, media
{
config,
lib,
...
}:
with lib; let
cfg = config.features.desktop.hyprland;
in {
imports = [
../common
./home.nix
../features/cli
../features/coding
../features/desktop
#./services/librechat.nix
];
}: let
homeLib = import ../lib {inherit lib;};
in
with lib; {
imports = [
(homeLib.mkHomeConfig {
profiles = ["coding" "media"];
context = "desktop";
})
./home.nix
];
options.features.desktop.hyprland.enable =
mkEnableOption "enable Hyprland";
config = mkMerge [
# Base configuration
{
xdg = {
# TODO: better structure
enable = true;
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
associations.added = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
config = mkMerge [
{
# Base CLI tools (new namespace)
base = {
shell = {
fish.enable = true;
nushell.enable = true;
starship.enable = true;
};
defaultApplications = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"application/md" = ["dev.zed.Zed.desktop"];
"application/text" = ["dev.zed.Zed.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
cliTools = {
fzf.enable = true;
nitch.enable = true;
television.enable = true;
};
};
};
features = {
cli = {
fish.enable = true;
nushell.enable = true;
fzf.enable = true;
nitch.enable = true;
secrets.enable = true;
starship.enable = true;
};
desktop = {
coding.enable = true;
crypto.enable = false;
gaming.enable = false;
hyprland.enable = false;
media.enable = true;
office.enable = false;
rofi.enable = true;
fonts.enable = true;
wayland.enable = false;
};
};
}
(mkIf cfg.enable {
wayland.windowManager.hyprland = {
enable = true;
settings = {
monitor = [
"eDP-1,preferred,0x0,1.25"
"HDMI-A-1,preferred,2560x0,1"
];
workspace = [
"1, monitor:eDP-1, default:true"
"2, monitor:eDP-1"
"3, monitor:eDP-1"
"4, monitor:HDMI-A-1"
"5, monitor:HDMI-A-1,border:false,rounding:false"
"6, monitor:HDMI-A-1"
];
windowrule = [
"match:class dev.zed.Zed, workspace 1"
"match:class Msty, workspace 1"
"match:class ^(com.obsproject.Studio)$, workspace 2"
"match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"match:class ^steam_app_\\d+$, fullscreen on"
"match:class ^steam_app_\\d+$, workspace 5"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
];
# Desktop features (new namespace)
desktop = {
wm = {
hyprland.enable = false;
rofi.enable = true;
wayland.enable = false;
};
apps = {
obsidian.enable = true;
office.enable = false;
crypto.enable = false;
};
theme = {
fonts.enable = true;
wallpapers.enable = false;
};
};
};
})
];
}
# Coding environment
coding = {
editors = {
neovim.enable = true;
zed.enable = true;
};
lsp.enable = true;
};
# Media profile features
profiles.media = {
obs.enable = false;
ffmpeg.enable = false;
kdenlive.enable = false;
ytDlp.enable = true;
};
xdg = {
enable = true;
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
associations.added = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
};
defaultApplications = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"application/md" = ["dev.zed.Zed.desktop"];
"application/text" = ["dev.zed.Zed.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
};
};
};
}
# Host-specific Hyprland layout — only applies when hyprland is enabled
(mkIf config.desktop.wm.hyprland.enable {
wayland.windowManager.hyprland = {
enable = true;
settings = {
monitor = [
"eDP-1,preferred,0x0,1.25"
"HDMI-A-1,preferred,2560x0,1"
];
workspace = [
"1, monitor:eDP-1, default:true"
"2, monitor:eDP-1"
"3, monitor:eDP-1"
"4, monitor:HDMI-A-1"
"5, monitor:HDMI-A-1,border:false,rounding:false"
"6, monitor:HDMI-A-1"
];
windowrule = [
"match:class dev.zed.Zed, workspace 1"
"match:class Msty, workspace 1"
"match:class ^(com.obsproject.Studio)$, workspace 2"
"match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"match:class ^steam_app_\\d+$, fullscreen on"
"match:class ^steam_app_\\d+$, workspace 5"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
];
};
};
})
];
}

View File

@@ -1,17 +1,25 @@
{
# m3-helios — AdGuard DNS and internal routing server.
# Context: server | Profiles: (none)
{lib, ...}: let
homeLib = import ../lib {inherit lib;};
in {
imports = [
../common
../features/cli
(homeLib.mkHomeConfig {
profiles = [];
context = "server";
})
./home-server.nix
];
features = {
cli = {
# Base CLI tools (new namespace)
base = {
shell = {
fish.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
secrets.enable = false;
starship.enable = true;
};
};
}

View File

@@ -1,17 +1,25 @@
{
# m3-hermes — secondary server.
# Context: server | Profiles: (none)
{lib, ...}: let
homeLib = import ../lib {inherit lib;};
in {
imports = [
../common
../features/cli
(homeLib.mkHomeConfig {
profiles = [];
context = "server";
})
./home-server.nix
];
features = {
cli = {
# Base CLI tools (new namespace)
base = {
shell = {
fish.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
secrets.enable = false;
starship.enable = true;
};
};
}

View File

@@ -1,96 +1,138 @@
# m3-kratos — AMD desktop workstation.
# Context: desktop | Profiles: coding, gaming, media
{
config,
lib,
...
}:
with lib; {
imports = [
../common
./home.nix
../features/cli
../features/coding
../features/desktop
];
}: let
homeLib = import ../lib {inherit lib;};
in
with lib; {
imports = [
(homeLib.mkHomeConfig {
profiles = ["coding" "gaming" "media"];
context = "desktop";
})
./home.nix
];
config = mkMerge [
{
xdg = {
# TODO: better structure
enable = true;
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
associations.added = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
config = mkMerge [
{
# Base CLI tools (new namespace)
base = {
shell = {
nushell.enable = true;
starship.enable = true;
};
defaultApplications = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"application/md" = ["dev.zed.Zed.desktop"];
"application/text" = ["dev.zed.Zed.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
cliTools = {
fzf.enable = true;
nitch.enable = true;
television.enable = true;
};
};
};
features = {
cli = {
nushell.enable = true;
fzf.enable = true;
nitch.enable = true;
secrets.enable = true;
starship.enable = true;
television.enable = true;
};
# Desktop features (new namespace)
desktop = {
crypto.enable = true;
coding.enable = true;
gaming.enable = true;
hyprland.enable = true;
media.enable = true;
obsidian.enable = true;
office.enable = true;
rofi.enable = true;
fonts.enable = true;
wayland.enable = true;
wallpapers = true;
wm = {
hyprland.enable = true;
rofi.enable = true;
wayland.enable = true;
};
apps = {
crypto.enable = true;
obsidian.enable = true;
office.enable = true;
};
theme = {
fonts.enable = true;
wallpapers.enable = true;
};
};
};
}
(mkIf config.features.desktop.hyprland.enable {
wayland.windowManager.hyprland = {
enable = true;
settings = {
monitor = [
"DP-1,2560x1440@144,0x0,1"
"DP-2,2560x1440@144,2560x0,1"
];
workspace = [
"1, monitor:DP-1, default:true"
"2, monitor:DP-1"
"3, monitor:DP-1"
"4, monitor:DP-2"
"5, monitor:DP-2"
"6, monitor:DP-2"
"7, monitor:DP-2"
];
windowrule = [
"match:class dev.zed.Zed, workspace 1"
"match:class Msty, workspace 1"
"match:class ^(com.obsproject.Studio)$, workspace 2"
"match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
];
# Coding environment
coding = {
editors = {
neovim.enable = true;
zed.enable = true;
};
lsp.enable = true;
languages = {
python.enable = true;
javascript.enable = true;
rustToolchain.enable = true;
go.enable = true;
typescript.enable = true;
};
};
};
})
];
}
# Gaming profile features
profiles.gaming = {
steam.enable = true;
gamescope.enable = true;
};
# Media profile features
profiles.media = {
obs.enable = true;
ffmpeg.enable = true;
kdenlive.enable = true;
ytDlp.enable = true;
};
xdg = {
enable = true;
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
associations.added = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
};
defaultApplications = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["vivaldi-stable.desktop"];
"application/md" = ["dev.zed.Zed.desktop"];
"application/text" = ["dev.zed.Zed.desktop"];
"x-scheme-handler/http" = ["vivaldi-stable.desktop"];
"x-scheme-handler/https" = ["vivaldi-stable.desktop"];
};
};
};
}
# Host-specific Hyprland monitor and workspace layout (dual 1440p monitors)
(mkIf config.desktop.wm.hyprland.enable {
wayland.windowManager.hyprland = {
enable = true;
settings = {
monitor = [
"DP-1,2560x1440@144,0x0,1"
"DP-2,2560x1440@144,2560x0,1"
];
workspace = [
"1, monitor:DP-1, default:true"
"2, monitor:DP-1"
"3, monitor:DP-1"
"4, monitor:DP-2"
"5, monitor:DP-2"
"6, monitor:DP-2"
"7, monitor:DP-2"
];
windowrule = [
"match:class dev.zed.Zed, workspace 1"
"match:class Msty, workspace 1"
"match:class ^(com.obsproject.Studio)$, workspace 2"
"match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
];
};
};
})
];
}

View File

@@ -0,0 +1,8 @@
# Gaming profile aggregator — Steam platform, Gamescope session, and AMD GPU tools.
{...}: {
imports = [
./steam.nix
./gamescope.nix
./gpu.nix
];
}

View File

@@ -0,0 +1,16 @@
# Gamescope — Valve's micro-compositor for Steam gaming sessions.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.gaming.gamescope;
in {
options.profiles.gaming.gamescope.enable = mkEnableOption "enable Gamescope session";
config = mkIf cfg.enable {
home.packages = [pkgs.gamescope];
};
}

View File

@@ -0,0 +1,21 @@
# AMD GPU tools — ROCm runtime, monitoring, and Vulkan utilities for gaming.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.gaming.gpu;
in {
options.profiles.gaming.gpu.enable = mkEnableOption "enable AMD GPU tools";
config = mkIf cfg.enable {
home.packages = with pkgs; [
rocmPackages.rocm-runtime
rocmPackages.rocm-smi
rocmPackages.rocminfo
vulkan-tools
];
};
}

View File

@@ -0,0 +1,21 @@
# Steam gaming platform with Steam Deck compatibility tools and gaming utilities.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.gaming.steam;
in {
options.profiles.gaming.steam.enable = mkEnableOption "enable Steam gaming";
config = mkIf cfg.enable {
home.packages = with pkgs; [
gamemode
goverlay
mangohud
protonplus
];
};
}

View File

@@ -0,0 +1,10 @@
# Media profile aggregator — OBS, FFmpeg, yt-dlp, Kdenlive, and HandBrake.
{...}: {
imports = [
./obs.nix
./ffmpeg.nix
./yt-dlp.nix
./kdenlive.nix
./handbrake.nix
];
}

View File

@@ -0,0 +1,24 @@
# FFmpeg — full-featured multimedia processing toolchain.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.media.ffmpeg;
in {
options.profiles.media.ffmpeg.enable = mkEnableOption "enable FFmpeg tools";
config = mkIf cfg.enable {
home.packages = with pkgs; [
amf
ffmpeg_6-full
gst_all_1.gstreamer
gst_all_1.gst-vaapi
pamixer
pavucontrol
qpwgraph
];
};
}

View File

@@ -0,0 +1,21 @@
# HandBrake — open-source video transcoder.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.media.handbrake;
in {
options.profiles.media.handbrake.enable = mkEnableOption "enable HandBrake transcoder";
config = mkIf cfg.enable {
home.packages = with pkgs; [
handbrake
gimp
inkscape
libation
];
};
}

View File

@@ -0,0 +1,16 @@
# Kdenlive — KDE non-linear video editor.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.media.kdenlive;
in {
options.profiles.media.kdenlive.enable = mkEnableOption "enable Kdenlive video editor";
config = mkIf cfg.enable {
home.packages = [pkgs.kdePackages.kdenlive];
};
}

View File

@@ -0,0 +1,21 @@
# OBS Studio — open broadcaster software for streaming and recording.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.media.obs;
in {
options.profiles.media.obs.enable = mkEnableOption "enable OBS Studio";
config = mkIf cfg.enable {
home.packages = with pkgs; [
v4l-utils
];
# OBS is managed via NixOS programs.obs-studio at the system level.
# Home-manager only installs supporting tools.
};
}

View File

@@ -0,0 +1,32 @@
# yt-dlp and media playback — YouTube downloader with MPV integration.
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.profiles.media.ytDlp;
in {
options.profiles.media.ytDlp.enable = mkEnableOption "enable yt-dlp and media playback";
config = mkIf cfg.enable {
home.packages = with pkgs; [
plexamp
unimatrix
webcord
];
programs.mpv = {
enable = true;
bindings = {
WHEEL_UP = "seek 10";
WHEEL_DOWN = "seek -10";
};
config = {
profile = "gpu-hq";
ytdl-format = "bestvideo+bestaudio";
};
};
};
}

6
home/server/default.nix Normal file
View File

@@ -0,0 +1,6 @@
# Server context home-manager configuration — minimal headless setup.
# Loaded on server hosts: m3-atlas, m3-helios, m3-aether.
{...}: {
# Server hosts use the base and coding modules directly.
# No desktop environment or GUI applications.
}

View File

@@ -78,11 +78,4 @@
nixPath = ["/etc/nix/path"];
};
users.defaultUserShell = pkgs.nushell;
# Compatibility shim: the hermes-agent nixos module references the legacy
# agenix activation script name "setupSecrets" (renamed to "agenixInstall"
# in current agenix). This empty passthrough satisfies the dependency so
# any host importing hermes-agent.nixosModules.default can build.
# Remove once upstream fixes: github:NousResearch/hermes-agent nix/nixosModules.nix:564
system.activationScripts.setupSecrets = lib.stringAfter [ "agenixInstall" ] "";
}

View File

@@ -38,7 +38,7 @@
kestra = 3018;
outline = 3019;
authentik = 3023;
conduit = 3024;
tuwunel = 3024;
# Home automation
homarr = 7575;

View File

@@ -25,6 +25,7 @@
openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC3YEmpYbM+cpmyD10tzNRHEn526Z3LJOzYpWEKdJg8DaYyPbDn9iyVX30Nja2SrW4Wadws0Y8DW+Urs25/wVB6mKl7jgPJVkMi5hfobu3XAz8gwSdjDzRSWJrhjynuaXiTtRYED2INbvjLuxx3X8coNwMw58OuUuw5kNJp5aS2qFmHEYQErQsGT4MNqESe3jvTP27Z5pSneBj45LmGK+RcaSnJe7hG+KRtjuhjI7RdzMeDCX73SfUsal+rHeuEw/mmjYmiIItXhFTDn8ZvVwpBKv7xsJG90DkaX2vaTk0wgJdMnpVIuIRBa4EkmMWOQ3bMLGkLQeK/4FUkNcvQ/4+zcZsg4cY9Q7Fj55DD41hAUdF6SYODtn5qMPsTCnJz44glHt/oseKXMSd556NIw2HOvihbJW7Rwl4OEjGaO/dF4nUw4c9tHWmMn9dLslAVpUuZOb7ykgP0jk79ldT3Dv+2Hj0CdAWT2cJAdFX58KQ9jUPT3tBnObSF1lGMI7t77VU= m3tam3re@m3-nix"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBZcjCKl0DRuOUOMXbM0GKY5JjvmyFpVZ/tRlTKWu/zp razr"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEZbg/Z9mnflXuLahGY8WOSBMqbgeqVIkIwRkquys1Ml sascha.koenig@azintec.com"
];
packages = [inputs.home-manager.packages.${pkgs.stdenv.hostPlatform.system}.default];
};

View File

@@ -1,24 +1,28 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.ens18.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}: {
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = ["ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod"];
boot.initrd.kernelModules = [];
boot.kernelModules = [];
boot.extraModulePackages = [];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.ens18.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}

Some files were not shown because too many files have changed in this diff Show More