Commit e33af02
Changed files (21)
modules
dev
agents
editor
helix
zellij
hosts
kevin
users
hpcesia
modules/dev/agents/skills/grilling/SKILL.md
@@ -0,0 +1,20 @@
+---
+name: grilling
+description: >
+ Grill the user relentlessly about a plan or design. Use when the user wants to
+ stress-test a plan before building, or uses any 'grill' trigger phrases.
+---
+
+# Grilling Me
+
+Interview me relentlessly about every aspect of this plan until we reach a shared
+understanding. Walk down each branch of the design tree, resolving dependencies
+between decisions one-by-one. For each question, provide your recommended answer.
+
+Ask the questions one at a time, waiting for feedback on each question before
+continuing. Asking multiple questions at once is bewildering.
+
+If a *fact* can be found by exploring the codebase, look it up rather than asking
+me. The *decisions*, though, are mine — put each one to me and wait for my answer.
+
+Do not enact the plan until I confirm we have reached a shared understanding.
modules/dev/agents/skills/jujutsu/SKILL.md
@@ -0,0 +1,467 @@
+---
+name: jujutsu
+description: >
+ Always activate FIRST on any git/VCS operations (commit, status, branch, push, etc.),
+ especially when HEAD is detached. If `.jj/` exists -> this is a Jujutsu (jj) repo,
+ raw git commands can corrupt data. Essential git safety instructions inside.
+allowed-tools: Bash(jj *)
+---
+
+# Jujutsu (jj) Version Control System
+
+This skill helps you work with Jujutsu, a Git-compatible VCS with mutable commits
+and automatic rebasing.
+
+**Tested with jj v0.37.0** - Commands may differ in other versions.
+
+## Important: Automated/Agent Environment
+
+When running as an agent:
+
+1. **Always use `--no-pager`** to prevent commands from opening an interactive
+ pager (like `less`), which will hang the agent:
+
+```bash
+# Always use --no-pager on commands that produce output
+jj --no-pager log # NOT: jj log
+jj --no-pager diff # NOT: jj diff
+jj --no-pager show <id> # NOT: jj show <id>
+```
+
+2. **Always use `-m` flags** to provide messages inline rather than relying on
+ editor prompts:
+
+```bash
+# Always use -m to avoid editor prompts
+jj desc -m "message" # NOT: jj desc
+jj squash -m "message" # NOT: jj squash (which opens editor)
+```
+
+Editor-based commands will fail in non-interactive environments.
+
+3. **Verify operations with `jj st`** after mutations (`squash`, `abandon`,
+`rebase`, `restore`) to confirm the operation succeeded.
+
+## Core Concepts
+
+### The Working Copy is a Commit
+
+In jj, your working directory is always a commit (referenced as `@`). Changes
+are automatically snapshotted when you run any jj command. There is no staging area.
+
+There is no need to run `jj commit`.
+
+### Commits Are Mutable
+
+**CRITICAL**: Unlike git, jj commits can be freely modified after creation. You
+can update descriptions, squash changes, rebase, and absorb — all without creating
+new commits. See "Essential Workflow" below for the recommended working pattern.
+
+### Change IDs vs Commit IDs
+
+- **Change ID**: A stable identifier (like `tqpwlqmp`) that persists when a commit
+ is rewritten — prefer these when referencing commits
+- **Commit ID**: A content hash (like `3ccf7581`) that changes when commit content
+ changes
+
+### Revsets
+
+jj uses a revset language to select commits in commands. Common revsets:
+
+- `@` — the working copy commit
+- `@-` — the parent of the working copy
+- `::@` — all ancestors of `@`
+- `@::` — all descendants of `@`
+- `trunk()..@` — commits between trunk and `@` (your branch)
+- `bookmarks()` — all commits with bookmarks
+
+Use revsets with `-r` flags: `jj log -r 'trunk()..@'`
+
+## Essential Workflow
+
+### Starting Work: Describe First, Then Code
+
+**Always create your commit message before writing code:**
+
+Validate that you're on a blank revision with `jj st`. If you are not, you should
+type:
+
+```bash
+jj new
+```
+
+```bash
+# First, describe what you intend to do
+jj desc -m "Add user authentication to login endpoint"
+
+# Then make your changes - they automatically become part of this commit
+# ... edit files ...
+
+# Check status
+jj st
+```
+
+### Creating Atomic Commits
+
+Each commit should represent ONE logical change. Use this format for commit messages:
+
+```text
+Examples:
+- "Add validation to user input forms"
+- "Fix null pointer in payment processor"
+- "Remove deprecated API endpoints"
+- "Update dependencies to latest versions"
+```
+
+### Viewing History
+
+```bash
+# View recent commits
+jj --no-pager log
+
+# View with patches
+jj --no-pager log -p
+
+# View specific commit
+jj --no-pager show <change-id>
+
+# View diff of working copy (use --git for familiar +/- format)
+jj --no-pager diff --git
+```
+
+**IMPORTANT: `jj diff` output format**: The default `jj diff` output uses a side-by-side
+line number format (e.g. `26 26:`) that looks very different from git's `+`/`-`
+prefix format. This is **normal and correct** — it is NOT corrupted or showing stale
+content. However, to avoid confusion, **always use `jj diff --git`** to get standard
+unified diff format with `+`/`-` lines.
+
+### Moving Between Commits
+
+```bash
+# Create a new empty commit on top of current
+jj new
+
+# Create new commit with message
+jj new -m "Commit message"
+
+# Edit an existing commit (working copy becomes that commit)
+jj edit <change-id>
+
+# Edit the previous commit
+jj prev -e
+
+# Edit the next commit
+jj next -e
+```
+
+## Refining Commits
+
+### Squashing Changes
+
+Move changes from current commit into its parent:
+
+```bash
+# Squash all changes into parent
+jj squash
+```
+
+**Note**: `jj squash -i` opens an interactive UI and will hang in agent environments.
+Avoid it.
+
+### Splitting Commits
+
+**Warning**: `jj split` is interactive and will hang in agent environments. To divide
+a commit, use `jj restore` to move changes out, then create separate commits manually.
+
+### Absorbing Changes
+
+Automatically distribute changes to the commits that last modified those lines:
+
+```bash
+# Absorb working copy changes into appropriate ancestor commits
+jj absorb
+```
+
+### Abandoning Commits
+
+Remove a commit entirely (descendants are rebased to its parent):
+
+```bash
+jj abandon <change-id>
+```
+
+### Undoing Operations
+
+Reverse the last jj operation:
+
+```bash
+jj undo
+```
+
+This reverts the repository to its state before the previous command. Useful
+for recovering from mistakes like accidental `abandon`, `squash`, or `rebase`.
+
+### Rebasing Commits
+
+Move commits to a different parent:
+
+```bash
+# Rebase current branch onto a destination
+jj rebase -d <destination>
+
+# Rebase a specific revision (without descendants) onto a destination
+jj rebase -r <change-id> -d <destination>
+
+# Rebase a revision and all its descendants
+jj rebase -s <change-id> -d <destination>
+
+# Rebase onto trunk (common: update your branch to latest main)
+jj rebase -d main
+```
+
+### Restoring Files
+
+Discard changes to specific files or restore files from another revision:
+
+```bash
+# Discard all uncommitted changes in working copy (restore from parent)
+jj restore
+
+# Discard changes to specific files
+jj restore path/to/file.txt
+
+# Restore files from a specific revision
+jj restore --from <change-id> path/to/file.txt
+```
+
+## Working with Bookmarks (Branches)
+
+Bookmarks are jj's equivalent to git branches:
+
+```bash
+# Create a bookmark at current commit
+jj bookmark create my-feature -r@
+
+# Move bookmark to a different commit
+jj bookmark move my-feature --to <change-id>
+
+# List bookmarks
+jj --no-pager bookmark list
+
+# Delete a bookmark
+jj bookmark delete my-feature
+```
+
+## Workspaces
+
+A **workspace** is a working copy plus its associated repo. One repo can have
+multiple workspaces — each with its own working directory and working-copy
+commit (`@`) — all sharing the same commits, operations, and bookmarks. This is
+jj's equivalent of `git worktree`.
+
+Useful for running a long build or test in one workspace while editing in another.
+Workspaces are a rarely-needed feature; consult the [official docs](https://docs.jj-vcs.dev/latest/working-copy/#workspaces)
+for anything beyond the basics below.
+
+### Common commands
+
+```bash
+# Create a new workspace (defaults: name = basename of path, parent = current @'s parent)
+jj workspace add ../my-tests
+jj workspace add --name tests -r <change-id> ../my-tests # explicit name and base
+
+# Inspect
+jj --no-pager workspace list
+jj workspace root [--name <ws>]
+
+# Remove (does NOT delete files on disk — rm the directory separately)
+jj workspace forget [<ws>]
+
+# Rename current workspace
+jj workspace rename <new-name>
+```
+
+In `jj log`, each workspace's `@` appears as `<workspace-name>@`.
+
+### Key semantics
+
+- **Isolation by default.** `jj workspace add` gives the new workspace its own
+ fresh empty commit; workspaces don't start out sharing `@`, and on-disk files
+ are never live-mirrored between them.
+- **Propagation at command boundaries.** Each jj command snapshots the current
+ workspace's files and reads the op log, so it sees commits/bookmarks made by
+ other workspaces. There is no filesystem watcher.
+- **Stale working copy.** If another workspace rewrites this workspace's `@`
+ (e.g. via `jj squash`, `rebase`, `abandon`), jj refuses commands here until you
+ run `jj workspace update-stale`. Same recovery path if a command was interrupted
+ mid-update.
+- **Shared `@` is sharp-edged.** `jj edit <id>` lets two workspaces point at the
+ same change without warning. When one mutates it, the other goes stale; if the
+ stale one had un-snapshotted edits, `update-stale` preserves them as a **divergent
+ commit** (same change ID, shown as `xyz??` in `jj log`) that you must resolve.
+ Avoid sharing `@` unless both workspaces are read-only.
+
+### Agent guidance
+
+- Always pass `--no-pager` to `jj workspace list`.
+- Don't `jj edit` a change another workspace already has as its `@` — main cause
+ of accidental divergence.
+- Don't `rm -rf` a workspace directory without also running
+ `jj workspace forget <name>`.
+
+## Git Integration
+
+### Working with Existing Git Repos
+
+```bash
+# Clone a git repository
+jj git clone <url>
+
+# Initialize jj in an existing git repo
+jj git init --colocate
+```
+
+### Fetching Remote Changes
+
+```bash
+# Fetch all branches from the default remote
+jj git fetch
+
+# Fetch from a specific remote
+jj git fetch --remote <remote-name>
+
+# Fetch specific branches
+jj git fetch -b <branch-name>
+```
+
+After fetching, rebase your work onto the updated trunk: `jj rebase -d main`
+
+### Switching Between jj and git (Colocated Repos Only)
+
+**This section only applies to colocated repos** (where both `.jj/` and `.git/`
+exist). In non-colocated repos, do not use git commands — they will corrupt jj state.
+
+In a colocated repository, you can use both jj and git commands with care:
+
+**Switching to git mode** (e.g., for merge workflows):
+
+```bash
+# First, ensure your jj working copy is clean
+jj st
+
+# Then checkout a branch with git
+git checkout <branch-name>
+```
+
+**Switching back to jj mode**:
+
+```bash
+# Use jj edit to resume working with jj
+jj edit <change-id>
+```
+
+**Important notes:**
+
+- Git may complain about uncommitted changes if jj's working copy differs from
+ the git HEAD
+- ALWAYS ensure your work is committed in jj before switching to git
+- After git operations, jj will detect and incorporate the changes on next command
+
+### Pushing Changes
+
+When the user asks you to push changes:
+
+```bash
+# Push a specific bookmark to the remote
+jj git push -b <bookmark-name>
+
+# Example: push the main bookmark
+jj git push -b main
+```
+
+**Before pushing, ensure:**
+
+1. Your bookmark points to the correct commit (bookmarks don't auto-advance like
+ git branches)
+2. The commits are refined and atomic
+3. The user has explicitly requested the push
+
+**IMPORTANT**: Unlike git branches, jj bookmarks do not automatically move when
+you create new commits. You must manually update them before pushing:
+
+```bash
+# Move an existing bookmark to the current commit
+jj bookmark move my-feature --to @
+
+# Then push it
+jj git push -b my-feature
+```
+
+If no bookmark exists for your changes, create one first:
+
+```bash
+# Create a bookmark at the current commit
+jj bookmark create my-feature
+
+# Then push it
+jj git push -b my-feature
+```
+
+## Handling Conflicts
+
+jj allows committing conflicts — you can resolve them later:
+
+```bash
+# View conflicts
+jj st
+```
+
+**Agent conflict resolution**: Do not use `jj resolve` (interactive). Instead, edit
+the conflicted files directly to remove conflict markers, then run `jj st` to verify
+resolution.
+
+## Preserving Commit Quality
+
+**IMPORTANT**: Because commits are mutable, always refine them before considering
+work done:
+
+1. **Review your commit**: `jj --no-pager show @` or `jj --no-pager diff --git`
+2. **Is it atomic?** One logical change per commit
+3. **Is the message clear?** Use imperative verb phrase in sentence case format
+ with no full stop: e.g. "Add login endpoint", "Fix null pointer in payment
+ processor", "Remove deprecated API endpoints"
+4. **Are there unrelated changes?** Use `jj restore` to move changes out, then create
+ separate commits
+5. **Should changes be elsewhere?** Use `jj squash` or `jj absorb`
+
+## Quick Reference
+
+| Action | Command |
+|--------|---------|
+| Describe commit | `jj desc -m "message"` |
+| View status | `jj st` |
+| View log | `jj --no-pager log` |
+| View diff | `jj --no-pager diff --git` |
+| New commit | `jj new -m "message"` (use `jj st` first; skip if `@` is empty) |
+| Edit commit | `jj edit <id>` |
+| Squash to parent | `jj squash` |
+| Auto-distribute | `jj absorb` |
+| Rebase | `jj rebase -d <destination>` |
+| Abandon commit | `jj abandon <id>` |
+| Undo last operation | `jj undo` |
+| Restore files | `jj restore [paths]` |
+| Create bookmark | `jj bookmark create <name>` |
+| Fetch remote | `jj git fetch` |
+| Push bookmark | `jj git push -b <name>` |
+| Add workspace | `jj workspace add <path>` |
+| List workspaces | `jj --no-pager workspace list` |
+| Forget workspace | `jj workspace forget [name]` |
+| Fix stale working copy | `jj workspace update-stale` |
+
+## Best Practices Summary
+
+1. **Describe first**: Set the commit message before coding
+2. **One change per commit**: Keep commits atomic and focused
+3. **Use change IDs**: They're stable across rewrites
+4. **Refine commits**: Leverage mutability for clean history
+5. **Embrace the workflow**: No staging area, no stashing - just commit
modules/dev/agents/default.nix
@@ -0,0 +1,18 @@
+{
+ den.aspects.dev.agents = {
+ hjem = {lib, ...}: let
+ skills = {
+ grilling = ./skills/grilling;
+ jujutsu = ./skills/jujutsu;
+ };
+ in {
+ xdg.config.files =
+ {
+ "agents/skills".type = "directory";
+ }
+ // lib.mapAttrs'
+ (n: v: lib.nameValuePair "agents/skills/${n}" {source = v;})
+ skills;
+ };
+ };
+}
modules/dev/agents/opencode.nix
@@ -0,0 +1,77 @@
+{den, ...}: {
+ den.aspects.dev.agents.opencode = {
+ includes = [den.aspects.dev.agents];
+
+ persistHome = {config, ...}: {
+ directories = [
+ "${config.xdg.config.directory}/opencode"
+ ];
+ };
+
+ cacheHome = {config, ...}: {
+ directories = [
+ "${config.xdg.data.directory}/opencode"
+ "${config.xdg.state.directory}/opencode"
+ ];
+ };
+
+ hjem = {
+ config,
+ pkgs,
+ ...
+ }: {
+ packages = [pkgs.opencode];
+
+ xdg.config.files."opencode/opencode.json" = {
+ generator = (pkgs.formats.json {}).generate "opencode-config.json";
+ value = {
+ permission = {
+ external_directory = {
+ "/nix/store/**" = "allow";
+ };
+ edit = {
+ "*" = "ask";
+ "/nix/store/**" = "deny";
+ };
+ grep = {
+ "/nix/store" = "deny";
+ };
+ webfetch = "ask";
+ };
+ mcp = {
+ deepwiki = {
+ type = "remote";
+ url = "https://mcp.deepwiki.com/mcp";
+ };
+ };
+ formatter = {
+ alejandra = {
+ command = ["alejandra" "$FILE"];
+ extensions = [".nix"];
+ };
+ };
+ plugin = [
+ "opencode-wakatime"
+ "@simonwjackson/opencode-direnv"
+ ];
+ };
+ };
+
+ xdg.config.files."opencode/tui.json" = {
+ generator = (pkgs.formats.json {}).generate "opencode-config-tui.json";
+ value = {
+ };
+ };
+
+ xdg.config.files."opencode/skills" = {
+ source = "${config.xdg.config.directory}/agents/skills";
+ };
+ };
+ };
+
+ den.aspects.develop.opencode.themes = {
+ catppuccin = {
+ hjem.xdg.config.files."opencode/tui.json".value.theme = "catppuccin";
+ };
+ };
+}
modules/dev/editor/helix/lang/nix.nix
@@ -0,0 +1,35 @@
+{den, ...}: {
+ den.aspects.dev.editor.helix.includes = [den.aspects.dev.editor.helix.lang.nix];
+ den.aspects.dev.editor.helix.lang.nix = {
+ hjem = {
+ xdg.config.files."helix/languages.toml".value = {
+ language = [
+ {
+ name = "nix";
+ auto-format = true;
+ indent = {
+ tab-width = 2;
+ unit = " ";
+ };
+ language-servers = ["nixd"];
+ }
+ ];
+ language-server = {
+ nil.config.nil = {
+ formatting.command = ["alejandra"];
+ nix = {
+ maxMemoryMB = 4096;
+ flake = {
+ autoArchive = false;
+ autoEvalInputs = true;
+ };
+ };
+ };
+ nixd.config.nixd = {
+ formatting.command = ["alejandra"];
+ };
+ };
+ };
+ };
+ };
+}
modules/dev/editor/helix/default.nix
@@ -0,0 +1,112 @@
+{
+ den.aspects.dev.editor.helix = {
+ cacheHome = {config, ...}: {
+ directories = [
+ "${config.xdg.config.directory}/helix"
+ "${config.xdg.data.directory}/helix" # Workspace trust data
+ "${config.xdg.data.directory}/steel" # Steel plugin system data
+ ];
+ };
+
+ hjem = {
+ config,
+ pkgs,
+ ...
+ }: let
+ steel-wrapped = pkgs.symlinkJoin {
+ pname = "steel-wrapped";
+ inherit (pkgs.steel) version;
+ paths = [pkgs.steel];
+ nativeBuildInputs = [pkgs.makeBinaryWrapper];
+ postBuild = ''
+ wrapProgram $out/bin/forge \
+ --prefix PATH : ${pkgs.lib.makeBinPath (with pkgs; [gcc cargo])}
+ '';
+ meta = {
+ inherit
+ (pkgs.steel.meta)
+ description
+ homepage
+ changelog
+ license
+ mainProgram
+ ;
+ };
+ };
+ in {
+ packages = [
+ pkgs.nur.repos.hpcesia.steelix
+
+ steel-wrapped
+ pkgs.schemat
+ ];
+
+ environment.sessionVariables = {
+ EDITOR = "hx";
+ VISUAL = "hx";
+ STEEL_HOME = "${config.xdg.data.directory}/steel";
+ };
+
+ xdg.config.files."helix/config.toml" = {
+ generator = (pkgs.formats.toml {}).generate "helix-config.toml";
+ value = {
+ editor = {
+ line-number = "relative";
+ cursorline = true;
+ bufferline = "multiple";
+ statusline = {
+ left = ["mode" "spinner" "diagnostics" "workspace-diagnostics"];
+ center = ["file-name" "read-only-indicator" "file-modification-indicator"];
+ right = ["file-type" "file-encoding" "separator" "position" "total-line-numbers"];
+ };
+ cursor-shape = {
+ normal = "block";
+ insert = "bar";
+ select = "block";
+ };
+ color-modes = true;
+ trim-trailing-whitespace = true;
+ inline-diagnostics.cursor-line = "warning";
+ end-of-line-diagnostics = "error";
+ lsp = {
+ display-inlay-hints = true;
+ inlay-hints-length-limit = 16;
+ };
+ indent-guides = {
+ render = true;
+ character = "┊";
+ skip-levels = 1;
+ };
+ file-picker = {
+ hidden = false;
+ };
+ };
+ keys = {
+ normal = {
+ "H" = "goto_previous_buffer";
+ "L" = "goto_next_buffer";
+ };
+ };
+ };
+ };
+
+ xdg.config.files."helix/languages.toml" = {
+ generator = (pkgs.formats.toml {}).generate "helix-languages.toml";
+ value = {};
+ };
+
+ xdg.config.files."helix/helix.scm" = {
+ source = ./helix.scm;
+ type = "copy";
+ };
+ xdg.config.files."helix/init.scm" = {
+ source = ./init.scm;
+ type = "copy";
+ };
+
+ xdg.data.files."steel/cogs/smith.hx" = {
+ source = "${pkgs.nur.repos.hpcesia.helixPlugins.smith}/cogs/smith.hx";
+ };
+ };
+ };
+}
modules/dev/editor/helix/helix.scm
@@ -0,0 +1,15 @@
+(require "helix/editor.scm")
+(require (prefix-in helix. "helix/commands.scm"))
+(require (prefix-in helix.static. "helix/static.scm"))
+
+(provide open-helix-scm open-init-scm)
+
+;;@doc
+;; Open the helix.scm file
+(define (open-helix-scm)
+ (helix.open (helix.static.get-helix-scm-path)))
+
+;;@doc
+;; Opens the init.scm file
+(define (open-init-scm)
+ (helix.open (helix.static.get-init-scm-path)))
modules/dev/editor/helix/init.scm
@@ -0,0 +1,24 @@
+(require "helix/configuration.scm")
+
+(define-lsp "steel-language-server" (command "steel-language-server") (args '()))
+(define-language "scheme"
+ (language-servers '("steel-language-server"))
+ (formatter (command "schemat")))
+
+(require (only-in "smith.hx/smith.scm"
+ smith-plugin
+ smith-prune
+ smith-init))
+
+(smith-plugin "https://github.com/mtul0729/helix-fcitx-focus.git")
+(smith-plugin "https://github.com/Xerxes-2/wakatime.hx.git")
+(smith-plugin "https://github.com/Ra77a3l3-jar/who.hx.git")
+
+(smith-plugin "https://github.com/Ra77a3l3-jar/forest.hx.git"
+ (config
+ (forest-configure! 'left #:ignore (list ".git" ".jj" "result" "target" "__pycache__"))
+ (forest-set-style! 'mini))
+ (bind
+ ("normal" ("space" "e") ":forest-open")))
+
+(smith-init)
modules/dev/zellij/layouts/dev.kdl
@@ -0,0 +1,27 @@
+layout {
+ pane_template name="help_bar" {
+ borderless true
+ plugin location="zellij:status-bar"
+ }
+ pane_template name="tab_bar" {
+ borderless true
+ plugin location="zellij:tab-bar"
+ }
+ pane_template name="editor" { command "$EDITOR"; }
+
+ default_tab_template {
+ tab_bar size=1
+ children
+ help_bar size=1
+ }
+
+ tab name="edit" {
+ editor
+ }
+ tab name="shell" {
+ pane
+ }
+ tab name="agent" {
+ pane
+ }
+}
modules/dev/zellij/default.nix
@@ -0,0 +1,25 @@
+{
+ den.aspects.dev.zellij = {
+ provides.to-users = {
+ hjem = {pkgs, ...}: {
+ packages = [pkgs.zellij];
+
+ xdg.config.files = {
+ "zellij/config.kdl".text =
+ ''
+ pane_frames false
+ show_release_notes false
+ show_startup_tips false
+ ''
+ + builtins.readFile ./keybinds.kdl;
+ "zellij/layouts".source = ./layouts;
+ };
+
+ files.".bashrc".text = ''
+ alias -- zj=zellij
+ alias -- zjd='zellij --layout dev'
+ '';
+ };
+ };
+ };
+}
modules/dev/zellij/keybinds.kdl
@@ -0,0 +1,43 @@
+keybinds {
+ shared {
+ // Previously: Ctrl + o (in helix: jump_backward)
+ bind "Ctrl e" { SwitchToMode "Session"; }
+ unbind "Ctrl o"
+ // Previously: Ctrl + s (in helix: save_selection)
+ bind "Ctrl y" { SwitchToMode "Scroll"; }
+ unbind "Ctrl s"
+ // Previously: Alt + i (in helix: shrink_selection)
+ bind "Alt w" { MoveTab "Left"; }
+ unbind "Alt i"
+ // Previously: Alt + o (in helix: expand_selection)
+ bind "Alt q" { MoveTab "Right"; }
+ unbind "Alt o"
+ // Previously: Alt + n (in helix: select_next_sibling)
+ bind "Alt m" { NewPane; }
+ unbind "Alt n"
+ // Previously: Ctrl + b (in helix: move_page_up)
+ bind "Alt 1" { SwitchToMode "Tmux"; }
+ unbind "Ctrl b"
+
+ bind "Alt f" {
+ ToggleFocusFullscreen
+ SwitchToMode "Normal"
+ }
+ }
+ session {
+ // Exit session mode
+ bind "Ctrl e" { SwitchToMode "Normal"; }
+ unbind "Ctrl o"
+ }
+ scroll {
+ // Exit scroll mode
+ bind "Ctrl y" { SwitchToMode "Normal"; }
+ unbind "Ctrl s"
+ }
+ tmux {
+ // Exit tmux mode
+ bind "Alt 1" { SwitchToMode "Normal"; }
+ unbind "Ctrl b"
+ }
+}
+
modules/dev/binfmt.nix
@@ -0,0 +1,16 @@
+{den, ...}: {
+ den.aspects.dev.includes = [den.aspects.dev.binfmt];
+ den.aspects.dev.binfmt = {
+ nixos = {
+ host,
+ lib,
+ ...
+ }: let
+ currentHostSystem = host.system;
+ allHostSystems = lib.filter (sys: lib.hasSuffix "linux" sys) (lib.attrNames den.hosts);
+ emulatedSystems = lib.filter (sys: sys != currentHostSystem) allHostSystems;
+ in {
+ boot.binfmt.emulatedSystems = emulatedSystems;
+ };
+ };
+}
modules/dev/direnv.nix
@@ -0,0 +1,28 @@
+{
+ den.aspects.dev.direnv = {
+ cacheHome = {config, ...}: {
+ directories = [
+ "${config.xdg.data.directory}/direnv" # Allowed directoies data
+ ];
+ };
+
+ provides.to-users = {
+ hjem = {
+ pkgs,
+ lib,
+ ...
+ }: {
+ packages = [pkgs.direnv];
+
+ files.".bashrc".text = lib.mkAfter ''
+ eval "$(${lib.getExe pkgs.direnv} hook bash)"
+ '';
+
+ xdg.config.files."direnv/direnvrc".text = ''
+ #Load nix-direnv
+ source ${pkgs.nix-direnv}/share/nix-direnv/direnvrc
+ '';
+ };
+ };
+ };
+}
modules/dev/git.nix
@@ -0,0 +1,59 @@
+{den, ...}: {
+ den.aspects.dev.includes = [den.aspects.dev.git];
+ den.aspects.dev.git = {
+ nixos = {pkgs, ...}: {
+ environment.systemPackages = [pkgs.git];
+ };
+
+ provides.to-users = {
+ hjem = {
+ user,
+ pkgs,
+ lib,
+ ...
+ }: {
+ packages = [
+ pkgs.git
+ pkgs.git-lfs
+ ];
+
+ files.".gitconfig".type = "delete";
+
+ xdg.config.files."git/config" = {
+ generator = lib.generators.toGitINI;
+ value = {
+ user = {
+ name = user.identity.displayName;
+ email = user.identity.email;
+ };
+ init.defaultBranch = "main";
+ trim.bases = "develop,master,main"; # for git-trim
+ push.autoSetupRemote = true;
+ pull.rebase = true;
+ filter.lfs = let
+ lfsPath =
+ lib.getExe pkgs.git-lfs;
+ in {
+ clean = "${lfsPath} clean -- %f";
+ process = lib.concatStringsSep " " [
+ lfsPath
+ "filter-process"
+ ];
+ required = true;
+ smudge = lib.concatStringsSep " " (
+ [
+ lfsPath
+ "smudge"
+ ]
+ ++ [
+ "--"
+ "%f"
+ ]
+ );
+ };
+ };
+ };
+ };
+ };
+ };
+}
modules/dev/jujutsu.nix
@@ -0,0 +1,26 @@
+{
+ den.aspects.dev.jujutsu = {
+ provides.to-users = {
+ hjem = {
+ user,
+ pkgs,
+ ...
+ }: {
+ packages = [
+ pkgs.jujutsu
+ pkgs.jjui
+ ];
+
+ xdg.config.files."jj/config.toml" = {
+ generator = (pkgs.formats.toml {}).generate "jj-config.toml";
+ value = {
+ user = {
+ name = user.identity.displayName;
+ email = user.identity.email;
+ };
+ };
+ };
+ };
+ };
+ };
+}
modules/dev/utils.nix
@@ -0,0 +1,40 @@
+{den, ...}: {
+ den.aspects.dev.includes = [den.aspects.dev.utils];
+ den.aspects.dev.utils = {
+ nixos = {
+ programs.nix-ld.enable = true;
+ };
+
+ provides.to-users = {
+ hjem = {pkgs, ...}: {
+ packages = with pkgs; [
+ # Nix
+ nixd # Nix LSP
+ alejandra # Nix formatter
+
+ # Make-like
+ gnumake
+ just
+ just-lsp
+
+ # Config file
+ vscode-json-languageserver
+ yaml-language-server
+ taplo # TOML LSP / formatter
+
+ # Script related
+ bash-language-server
+ shellcheck
+ shfmt
+
+ python3
+ ty # Python LSP
+ ruff # Python fomatter
+
+ # Documents
+ rumdl # Markdown linter / formatter
+ ];
+ };
+ };
+ };
+}
modules/dev/wakatime.nix
@@ -0,0 +1,21 @@
+{
+ den.aspects.dev.wakatime = {
+ persistHome = {config, ...}: {
+ directories = [
+ "${config.xdg.config.directory}/wakatime"
+ ];
+ };
+
+ hjem = {
+ pkgs,
+ config,
+ ...
+ }: {
+ packages = [pkgs.wakatime-cli];
+
+ environment.sessionVariables = {
+ WAKATIME_HOME = "${config.xdg.config.directory}/wakatime";
+ };
+ };
+ };
+}
modules/dev/yazi.nix
@@ -0,0 +1,65 @@
+{
+ den.aspects.dev.yazi = {
+ hjem = {pkgs, ...}: let
+ auto-layout = pkgs.yaziPlugins.mkYaziPlugin {
+ pname = "auto-layout.yazi";
+ version = "unstable-25-07-30";
+ src = pkgs.fetchFromGitHub {
+ owner = "luccahuguet";
+ repo = "auto-layout.yazi";
+ rev = "e24bee9f6dd15ff80eae1b3dc1a6b06ee7e66121";
+ hash = "sha256-4vRIGU/ArXhW9ervhyNhpfDN7UF4pqVYnxi6FExlgGk=";
+ };
+ meta = {
+ description = "Automatically change the column layout in yazi based on available window width";
+ homepage = "https://github.com/luccahuguet/auto-layout.yazi";
+ license = pkgs.lib.licenses.mit;
+ };
+ };
+ in {
+ packages = [pkgs.yazi];
+
+ files.".bashrc".text = ''
+ function yy() {
+ local tmp="$(mktemp -t "yazi-cwd.XXXXX")"
+ command yazi "$@" --cwd-file="$tmp"
+ if cwd="$(<"$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
+ builtin cd -- "$cwd"
+ fi
+ rm -f -- "$tmp"
+ }
+ '';
+
+ xdg.config.files = {
+ "yazi/plugins/auto-layout.yazi".source = auto-layout;
+ "yazi/plugins/git.yazi".source = pkgs.yaziPlugins.git;
+ "yazi/init.lua".text = ''
+ require("git"):setup()
+ require("auto-layout").setup()
+ '';
+
+ "yazi/yazi.toml" = {
+ generator = (pkgs.formats.toml {}).generate "yazi-settings.toml";
+ value = {
+ mgr = {
+ show_hidden = true;
+ sort_dir_first = true;
+ };
+ plugin.prepend_fetchers = [
+ {
+ url = "*";
+ run = "git";
+ group = "git";
+ }
+ {
+ url = "*/";
+ run = "git";
+ group = "git";
+ }
+ ];
+ };
+ };
+ };
+ };
+ };
+}
modules/dev/zoxide.nix
@@ -0,0 +1,21 @@
+{
+ den.aspects.dev.zoxide = {
+ cacheHome = {config, ...}: {
+ directories = [
+ "${config.xdg.data.directory}/zoxide" # Zoxide database
+ ];
+ };
+
+ hjem = {
+ pkgs,
+ lib,
+ ...
+ }: {
+ packages = [pkgs.zoxide];
+
+ files.".bashrc".text = lib.mkOrder 2000 ''
+ eval "$(${lib.getExe pkgs.zoxide} init bash)"
+ '';
+ };
+ };
+}
modules/hosts/kevin/default.nix
@@ -1,4 +1,4 @@
-{
+{den, ...}: {
den.hosts.kevin = {
system = "x86_64-linux";
description = ''
@@ -14,4 +14,10 @@
settings.secret.pubKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOO9CyAqGo/WbJkncrt1a9jxS1E+hd550SC4A08I/l0/ root@kevin";
};
+
+ den.aspects.kevin = {
+ includes = with den.aspects; [
+ dev
+ ];
+ };
}
modules/users/hpcesia/dev.nix
@@ -0,0 +1,36 @@
+{den, ...}: let
+ includeWhen = cond: aspects:
+ map (aspect: (den.lib.policy.when cond aspect)) aspects;
+in {
+ den.aspects.hpcesia = {
+ includes = includeWhen ({host, ...}: host.hasAspect den.aspects.dev) (with den.aspects; [
+ dev.agents.opencode
+ dev.direnv
+ dev.editor.helix
+ dev.jujutsu
+ dev.wakatime
+ dev.yazi
+ dev.zellij
+ dev.zoxide
+
+ {
+ name = "hpcesia/dev/git";
+ hjem = {
+ xdg.config.files."git/config".value = {
+ url = {
+ "ssh://git@github.com/HPCesia" = {
+ insteadOf = "https://github.com/HPCesia";
+ };
+ "ssh://git@codeberg.org/HPCesia" = {
+ insteadOf = "https://codeberg.org/HPCesia";
+ };
+ "ssh://git@git.net.trin.one/HPCesia" = {
+ insteadOf = "https://git.net.trin.one/HPCesia";
+ };
+ };
+ };
+ };
+ }
+ ]);
+ };
+}