Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/bun/bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[install]
minimumReleaseAge = 604800
exact = true
ignoreScripts = true
optional = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional = false skips required native binaries: rollup, esbuild, swc, lightningcss, sharp, and many other modern toolchain packages ship per-arch binaries via optionalDependencies. Disabling optionals globally lets bun install succeed but breaks the apps at runtime with Cannot find module @rollup/rollup-linux-x64-gnu (or equivalent). Same applies to the corresponding omit=optional lines in config/npm/npmrc and config/pnpm/rc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: optional = false will break packages that ship platform-specific native binaries via optionalDependencies. Many critical toolchain packages (rollup, esbuild, swc, lightningcss, sharp) use this pattern — e.g. @rollup/rollup-linux-x64-gnu is an optional dep. Install will succeed but the packages will fail at runtime with missing module errors. The same concern applies to omit=optional in the npmrc and pnpm rc files.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/bun/bunfig.toml, line 5:

<comment>`optional = false` will break packages that ship platform-specific native binaries via `optionalDependencies`. Many critical toolchain packages (rollup, esbuild, swc, lightningcss, sharp) use this pattern — e.g. `@rollup/rollup-linux-x64-gnu` is an optional dep. Install will succeed but the packages will fail at runtime with missing module errors. The same concern applies to `omit=optional` in the npmrc and pnpm rc files.</comment>

<file context>
@@ -0,0 +1,5 @@
+minimumReleaseAge = 604800
+exact = true
+ignoreScripts = true
+optional = false
</file context>
Suggested change
optional = false
# optional = false # WARNING: breaks packages using optionalDependencies for native binaries (rollup, esbuild, swc, etc.)

6 changes: 6 additions & 0 deletions config/bun/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
_: {
home.file.".bunfig.toml" = {
source = ./bunfig.toml;
force = true;
};
}
3 changes: 3 additions & 0 deletions config/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ in
[
./aichat
./amp
./bun
./ccs
./cliproxyapi
./codex
Expand All @@ -25,12 +26,14 @@ in
./karabiner
./llm
./mempalace
./npm
./obsidian
./omp
./openclaw
./opencode
./paperclip
./pi
./pnpm
./serena
./starship
./tmuxinator
Expand Down
27 changes: 22 additions & 5 deletions config/noctalia/ac-idle-inhibit.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
#!/usr/bin/env bash
# Inhibit idle when on AC power so noctalia's idle timeouts only fire on battery.
# Polls every 2s so AC state changes take effect well within the 5-min idle window.
# On battery: run swayidle for screen-off (5 min) and suspend (10 min).
# On AC: stop swayidle so only noctalia's lock fires.
set -euo pipefail

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardware-specific path: /sys/class/power_supply/ACAD/online only exists on certain laptops; common alternates are AC, AC0, ADP1, ACPI. Combined with cat ... 2>/dev/null inside $(...), a missing node yields ON_AC="", which falls into the else branch — so on any other machine the script will treat AC as battery and keep swayidle running with screen-off + suspend even when plugged in (the opposite of the PR's intent). Detect the Mains entry under /sys/class/power_supply/*/type instead.

AC=/sys/class/power_supply/ACAD/online
SWAYIDLE_PID=""

cleanup() { [ -n "$SWAYIDLE_PID" ] && kill "$SWAYIDLE_PID" 2>/dev/null; }
trap cleanup EXIT

while true; do
if [ "$(cat "$AC" 2>/dev/null)" = "1" ]; then
systemd-inhibit --what=idle --why="On AC power" --mode=block sleep 2
ON_AC="$(cat "$AC" 2>/dev/null)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The script uses set -e, which causes it to exit immediately if cat "$AC" fails (e.g., if the sysfs path does not exist on a desktop or different hardware). This will lead to the systemd service failing and potentially entering a restart loop. It's better to check for the file's existence and default to a safe value (like assuming AC power) if it's missing.

Suggested change
ON_AC="$(cat "$AC" 2>/dev/null)"
if [ -f "$AC" ]; then
ON_AC="$(cat "$AC")"
else
ON_AC="1"
fi
References
  1. Avoid using set -e in execution scripts that must handle failures gracefully to avoid blocking or service failure loops.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The AC-state read can terminate the whole script under set -e when the sysfs file read fails. Add a fallback so transient read errors don’t stop idle management.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/noctalia/ac-idle-inhibit.sh, line 13:

<comment>The AC-state read can terminate the whole script under `set -e` when the sysfs file read fails. Add a fallback so transient read errors don’t stop idle management.</comment>

<file context>
@@ -1,14 +1,31 @@
 while true; do
-  if [ "$(cat "$AC" 2>/dev/null)" = "1" ]; then
-    systemd-inhibit --what=idle --why="On AC power" --mode=block sleep 2
+  ON_AC="$(cat "$AC" 2>/dev/null)"
+
+  if [ "$ON_AC" = "1" ]; then
</file context>
Suggested change
ON_AC="$(cat "$AC" 2>/dev/null)"
ON_AC="$(cat "$AC" 2>/dev/null || echo 0)"


if [ "$ON_AC" = "1" ]; then
if [ -n "$SWAYIDLE_PID" ] && kill -0 "$SWAYIDLE_PID" 2>/dev/null; then
kill "$SWAYIDLE_PID" 2>/dev/null
SWAYIDLE_PID=""
hyprctl dispatch dpms on
fi
Comment on lines +16 to +20

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

kill under set -e can terminate the script on a tiny race.

Between the kill -0 liveness check on Line 16 and the kill on Line 17, swayidle may exit on its own (or be reaped elsewhere). With set -e, a non-zero exit from kill will abort the whole AC monitor service. The same applies in the cleanup function on Line 9, except there it is shielded by the && short-circuit — the standalone kill here is not.

🛡️ Suggested guard
-    if [ -n "$SWAYIDLE_PID" ] && kill -0 "$SWAYIDLE_PID" 2>/dev/null; then
-      kill "$SWAYIDLE_PID" 2>/dev/null
-      SWAYIDLE_PID=""
-      hyprctl dispatch dpms on
-    fi
+    if [ -n "$SWAYIDLE_PID" ] && kill -0 "$SWAYIDLE_PID" 2>/dev/null; then
+      kill "$SWAYIDLE_PID" 2>/dev/null || true
+      SWAYIDLE_PID=""
+      hyprctl dispatch dpms on || true
+    fi

I also added || true to the hyprctl call since a transient compositor hiccup shouldn't crash the long-running poller.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ -n "$SWAYIDLE_PID" ] && kill -0 "$SWAYIDLE_PID" 2>/dev/null; then
kill "$SWAYIDLE_PID" 2>/dev/null
SWAYIDLE_PID=""
hyprctl dispatch dpms on
fi
if [ -n "$SWAYIDLE_PID" ] && kill -0 "$SWAYIDLE_PID" 2>/dev/null; then
kill "$SWAYIDLE_PID" 2>/dev/null || true
SWAYIDLE_PID=""
hyprctl dispatch dpms on || true
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config/noctalia/ac-idle-inhibit.sh` around lines 16 - 20, The race between
the liveness check (kill -0 on SWAYIDLE_PID) and the subsequent kill can cause
the standalone kill to fail and, under set -e, abort the script; update the
block handling SWAYIDLE_PID to tolerate a failing kill by appending a no-op on
failure (e.g. change the dangerous call to kill "$SWAYIDLE_PID" 2>/dev/null ||
true) so the script won't exit if the process disappeared after the check,
ensure SWAYIDLE_PID is still cleared (SWAYIDLE_PID="") and likewise make the
hyprctl dispatch dpms on call resilient by adding || true so transient
compositor errors don't terminate the poller.

else
sleep 2
if [ -z "$SWAYIDLE_PID" ] || ! kill -0 "$SWAYIDLE_PID" 2>/dev/null; then
swayidle -w \
timeout 300 'hyprctl dispatch dpms off' \
resume 'hyprctl dispatch dpms on' \
timeout 600 'systemctl suspend' &
SWAYIDLE_PID=$!
fi
fi
sleep 2
done
17 changes: 13 additions & 4 deletions config/noctalia/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ in
force = true;
};

# Inhibit idle when plugged into AC; noctalia's idle timeouts apply on battery only.
# Screen-off and suspend on battery only; noctalia handles lock on both AC and battery.
systemd.user.services.ac-idle-inhibit = {
Unit = {
Description = "Inhibit idle when on AC power";
Description = "Screen-off and suspend on battery via swayidle";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hyprctl needs more than WAYLAND_DISPLAY: without HYPRLAND_INSTANCE_SIGNATURE (and XDG_RUNTIME_DIR), hyprctl falls back to scanning $XDG_RUNTIME_DIR/hypr/ and silently picks an arbitrary socket if multiple Hyprland instances exist. Consider PassEnvironment = [ "WAYLAND_DISPLAY" "HYPRLAND_INSTANCE_SIGNATURE" "XDG_RUNTIME_DIR" ]; so the user manager forwards them when present.

Suggested change
};
PassEnvironment = [ "WAYLAND_DISPLAY" "HYPRLAND_INSTANCE_SIGNATURE" "XDG_RUNTIME_DIR" ];

Service = {
Type = "simple";
PassEnvironment = "WAYLAND_DISPLAY";
Environment = "PATH=${
pkgs.lib.makeBinPath [
pkgs.swayidle
pkgs.hyprland
pkgs.coreutils
pkgs.systemd
]
}";
ExecStart = "${pkgs.bash}/bin/bash ${./ac-idle-inhibit.sh}";
Restart = "on-failure";
};
Expand Down Expand Up @@ -148,9 +157,9 @@ in
wallpaper.enabled = false;
idle = {
enabled = true;
screenOffTimeout = 300; # 5 min on battery
screenOffTimeout = 0;
lockTimeout = 300;
suspendTimeout = 600; # 10 min on battery
suspendTimeout = 0;
};
systemMonitor.enableDgpuMonitoring = true;
colorSchemes.schedulingMode = "location";
Expand Down
6 changes: 6 additions & 0 deletions config/npm/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
_: {
home.file.".npmrc" = {
source = ./npmrc;
force = true;
};
}
5 changes: 5 additions & 0 deletions config/npm/npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
minimum-release-age=10080

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm ignores this setting — npm's canonical key is min-release-age (not minimum-release-age), and the unit is days, not minutes (npm v11 docs; npm/cli #8965). Even after a rename, 10080 would mean ~27 years and break every install. The 7-day cooldown the commit message describes does not currently apply to npm install.

Suggested change
minimum-release-age=10080
min-release-age=7

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Wrong configuration key and unit. npm's setting is min-release-age (not minimum-release-age) and its unit is days, not minutes. The current value 10080 would be silently ignored since minimum-release-age isn't a recognized npm config key. For a 7-day cooldown, use min-release-age=7.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/npm/npmrc, line 1:

<comment>Wrong configuration key and unit. npm's setting is `min-release-age` (not `minimum-release-age`) and its unit is **days**, not minutes. The current value `10080` would be silently ignored since `minimum-release-age` isn't a recognized npm config key. For a 7-day cooldown, use `min-release-age=7`.</comment>

<file context>
@@ -0,0 +1,5 @@
+minimum-release-age=10080
+ignore-scripts=true
+save-exact=true
</file context>
Suggested change
minimum-release-age=10080
min-release-age=7

ignore-scripts=true
save-exact=true
omit=optional
engine-strict=true
6 changes: 6 additions & 0 deletions config/pnpm/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
_: {
xdg.configFile."pnpm/rc" = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong path on macOS: pnpm reads its global rc from ~/Library/Preferences/pnpm/rc on Darwin (unless XDG_CONFIG_HOME is exported), but xdg.configFile always lands at ~/.config/pnpm/rc. Since config/default.nix imports ./pnpm unconditionally, this module silently does nothing on macOS hosts. Branch on pkgs.stdenv.isDarwin or gate the import to Linux.

source = ./rc;
force = true;
};
}
5 changes: 5 additions & 0 deletions config/pnpm/rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
minimum-release-age=10080

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latent on pnpm v11+: pnpm v11 restricted the global rc file to auth/registry settings — non-auth settings like minimumReleaseAge must live in pnpm-workspace.yaml (project) or ~/.config/pnpm/config.yaml (global) as minimumReleaseAge: 10080 (camelCase, minutes). Works on pnpm ≤ v10, but on v11 this file is ignored for these settings and pnpm's own default of 1440 min wins. (Refs: https://pnpm.io/11.x/configuring, https://pnpm.io/11.x/cli/config)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: In pnpm v11+, non-registry/auth settings in the global rc file are silently ignored. To ensure these security defaults are actually enforced upon upgrade, use ~/.config/pnpm/config.yaml instead, formatted as YAML (e.g., minimumReleaseAge: 10080, ignoreScripts: true).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/pnpm/rc, line 1:

<comment>In pnpm v11+, non-registry/auth settings in the global `rc` file are silently ignored. To ensure these security defaults are actually enforced upon upgrade, use `~/.config/pnpm/config.yaml` instead, formatted as YAML (e.g., `minimumReleaseAge: 10080`, `ignoreScripts: true`).</comment>

<file context>
@@ -0,0 +1,5 @@
+minimum-release-age=10080
+ignore-scripts=true
+save-exact=true
</file context>

ignore-scripts=true
save-exact=true
omit=optional
engine-strict=true
Loading