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
43 changes: 42 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ setup: nix-setup ## Basic Nix setup (alias for nix-setup).
setup-dev: nix-setup git-submodule-sync shell-install ## Set up local development environment (Nix + submodules + shell).

.PHONY: switch
switch: nix-switch ## Apply Nix configuration (alias for nix-switch).
switch: nix-switch launchctl ## Apply Nix configuration and restart launchd agents.

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

The switch target now depends on launchctl, which will fail on Linux systems since launchctl is a macOS-specific command. Consider adding OS detection (e.g., [ "$(OS)" = "Darwin" ] && $(MAKE) launchctl || true) or making the launchctl target conditional on Darwin, similar to how nix-connect handles OS-specific logic.

Suggested change
switch: nix-switch launchctl ## Apply Nix configuration and restart launchd agents.
switch: nix-switch ## Apply Nix configuration and restart launchd agents.
@if [ "$(OS)" = "Darwin" ]; then \
$(MAKE) launchctl; \
fi

Copilot uses AI. Check for mistakes.

.PHONY: update
update: nix-update shell-update neovim-update ## Update Nix flake and configurations.
Expand Down Expand Up @@ -567,6 +567,47 @@ lua-check-hammerspoon-dev: ## Run the Hammerspoon Lua check inside the Nix dev s
@echo "🧪 Running Hammerspoon Lua check inside the Nix dev shell..."
@DEVENV_ROOT=$(CURDIR) $(NIX_ALLOW_UNFREE) $(NIX_EXEC) develop $(NIX_FLAGS) .# --command $(MAKE) lua-check-hammerspoon

##@ Launchd Services

.PHONY: launchctl
launchctl: launchctl-brew-upgrader launchctl-cliproxyapi launchctl-code-syncer launchctl-dotfiles-updater launchctl-neverssl-keepalive launchctl-ollama ## Restart all launchd agents.

.PHONY: launchctl-brew-upgrader
launchctl-brew-upgrader: ## Restart brew-upgrader launchd agent.
@echo "🔄 Restarting brew-upgrader..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.brew-upgrader || true
@echo "✅ brew-upgrader restarted"

.PHONY: launchctl-cliproxyapi
launchctl-cliproxyapi: ## Restart cliproxyapi launchd agent.
@echo "🔄 Restarting cliproxyapi..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.cliproxyapi || true
@echo "✅ cliproxyapi restarted"

.PHONY: launchctl-code-syncer
launchctl-code-syncer: ## Restart code-syncer launchd agent.
@echo "🔄 Restarting code-syncer..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.code-syncer || true
@echo "✅ code-syncer restarted"

.PHONY: launchctl-dotfiles-updater
launchctl-dotfiles-updater: ## Restart dotfiles-updater launchd agent.
@echo "🔄 Restarting dotfiles-updater..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.dotfiles-updater || true
@echo "✅ dotfiles-updater restarted"

.PHONY: launchctl-neverssl-keepalive
launchctl-neverssl-keepalive: ## Restart neverssl-keepalive launchd agent.
@echo "🔄 Restarting neverssl-keepalive..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.neverssl-keepalive || true
@echo "✅ neverssl-keepalive restarted"

.PHONY: launchctl-ollama
launchctl-ollama: ## Restart ollama launchd agent.
@echo "🔄 Restarting ollama..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.ollama || true
@echo "✅ ollama restarted"
Comment on lines +577 to +609

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

The launchctl targets execute macOS-specific commands without checking the operating system. These should be guarded with OS detection to prevent errors on Linux systems. Consider adding a check like @if [ "$(OS)" = "Darwin" ]; then ... fi or documenting that these targets are Darwin-only.

Suggested change
@echo "🔄 Restarting brew-upgrader..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.brew-upgrader || true
@echo "✅ brew-upgrader restarted"
.PHONY: launchctl-cliproxyapi
launchctl-cliproxyapi: ## Restart cliproxyapi launchd agent.
@echo "🔄 Restarting cliproxyapi..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.cliproxyapi || true
@echo "✅ cliproxyapi restarted"
.PHONY: launchctl-code-syncer
launchctl-code-syncer: ## Restart code-syncer launchd agent.
@echo "🔄 Restarting code-syncer..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.code-syncer || true
@echo "✅ code-syncer restarted"
.PHONY: launchctl-dotfiles-updater
launchctl-dotfiles-updater: ## Restart dotfiles-updater launchd agent.
@echo "🔄 Restarting dotfiles-updater..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.dotfiles-updater || true
@echo "✅ dotfiles-updater restarted"
.PHONY: launchctl-neverssl-keepalive
launchctl-neverssl-keepalive: ## Restart neverssl-keepalive launchd agent.
@echo "🔄 Restarting neverssl-keepalive..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.neverssl-keepalive || true
@echo "✅ neverssl-keepalive restarted"
.PHONY: launchctl-ollama
launchctl-ollama: ## Restart ollama launchd agent.
@echo "🔄 Restarting ollama..."
@launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.ollama || true
@echo "✅ ollama restarted"
@if [ "$(OS)" = "Darwin" ]; then \
echo "🔄 Restarting brew-upgrader..."; \
launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.brew-upgrader || true; \
echo "✅ brew-upgrader restarted"; \
else \
echo "❌ brew-upgrader restart is only supported on macOS (Darwin)."; \
fi
.PHONY: launchctl-cliproxyapi
launchctl-cliproxyapi: ## Restart cliproxyapi launchd agent.
@if [ "$(OS)" = "Darwin" ]; then \
echo "🔄 Restarting cliproxyapi..."; \
launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.cliproxyapi || true; \
echo "✅ cliproxyapi restarted"; \
else \
echo "❌ cliproxyapi restart is only supported on macOS (Darwin)."; \
fi
.PHONY: launchctl-code-syncer
launchctl-code-syncer: ## Restart code-syncer launchd agent.
@if [ "$(OS)" = "Darwin" ]; then \
echo "🔄 Restarting code-syncer..."; \
launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.code-syncer || true; \
echo "✅ code-syncer restarted"; \
else \
echo "❌ code-syncer restart is only supported on macOS (Darwin)."; \
fi
.PHONY: launchctl-dotfiles-updater
launchctl-dotfiles-updater: ## Restart dotfiles-updater launchd agent.
@if [ "$(OS)" = "Darwin" ]; then \
echo "🔄 Restarting dotfiles-updater..."; \
launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.dotfiles-updater || true; \
echo "✅ dotfiles-updater restarted"; \
else \
echo "❌ dotfiles-updater restart is only supported on macOS (Darwin)."; \
fi
.PHONY: launchctl-neverssl-keepalive
launchctl-neverssl-keepalive: ## Restart neverssl-keepalive launchd agent.
@if [ "$(OS)" = "Darwin" ]; then \
echo "🔄 Restarting neverssl-keepalive..."; \
launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.neverssl-keepalive || true; \
echo "✅ neverssl-keepalive restarted"; \
else \
echo "❌ neverssl-keepalive restart is only supported on macOS (Darwin)."; \
fi
.PHONY: launchctl-ollama
launchctl-ollama: ## Restart ollama launchd agent.
@if [ "$(OS)" = "Darwin" ]; then \
echo "🔄 Restarting ollama..."; \
launchctl kickstart -k gui/$$(id -u)/org.nix-community.home.ollama || true; \
echo "✅ ollama restarted"; \
else \
echo "❌ ollama restart is only supported on macOS (Darwin)."; \
fi

Copilot uses AI. Check for mistakes.

##@ Git Submodule

.PHONY: git-submodule-sync
Expand Down
6 changes: 6 additions & 0 deletions home-manager/programs/neovim/lua/keymaps.lua
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ keymap({ "n", "v" }, "<leader>d", '"_d', opts)
keymap("n", "<leader>gs", ":tab Git<cr>", opts)
-- @keymap <F9>: Open Git mergetool in new tab
keymap("n", "<F9>", ":tab Git mergetool<cr>", opts)
-- @keymap <leader>gg: Open LazyGit

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

The comment says <leader>gg but the actual keymap uses <leader>lg. The comment should be updated to match the keymap: -- @keymap <leader>lg: Open LazyGit

Suggested change
-- @keymap <leader>gg: Open LazyGit
-- @keymap <leader>lg: Open LazyGit

Copilot uses AI. Check for mistakes.
keymap("n", "<leader>lg", ":LazyGit<cr>", opts)
-- @keymap <leader>gD: Open VS Code style diff
keymap("n", "<leader>gD", function()
require("vscode-diff").diff()
end, opts)
Comment on lines +120 to +125

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

Fix @keymap annotation to match <leader>lg mapping

The doc comment advertises <leader>gg while the actual mapping is <leader>lg, which will confuse any tooling that parses these @keymap annotations.

- -- @keymap <leader>gg: Open LazyGit
-keymap("n", "<leader>lg", ":LazyGit<cr>", opts)
+-- @keymap <leader>lg: Open LazyGit
+keymap("n", "<leader>lg", ":LazyGit<cr>", opts)
📝 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
-- @keymap <leader>gg: Open LazyGit
keymap("n", "<leader>lg", ":LazyGit<cr>", opts)
-- @keymap <leader>gD: Open VS Code style diff
keymap("n", "<leader>gD", function()
require("vscode-diff").diff()
end, opts)
-- @keymap <leader>lg: Open LazyGit
keymap("n", "<leader>lg", ":LazyGit<cr>", opts)
-- @keymap <leader>gD: Open VS Code style diff
keymap("n", "<leader>gD", function()
require("vscode-diff").diff()
end, opts)
🤖 Prompt for AI Agents
In home-manager/programs/neovim/lua/keymaps.lua around lines 120 to 125, the
@keymap docstring incorrectly documents "<leader>gg" while the actual mapping is
"<leader>lg"; update the annotation to "@keymap <leader>lg: Open LazyGit" so the
comment matches the mapping and any tooling parsing these annotations will be
correct.

-- @keymap <leader>gd: Preview hunk inline
keymap("n", "<leader>gd", ":Gitsigns preview_hunk_inline<cr>", opts)
-- @keymap <leader>hs: Stage hunk (Gitsigns)
Expand Down
4 changes: 4 additions & 0 deletions home-manager/programs/neovim/lua/plugins.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ vim.pack.add({

-- TERMINAL
{ src = "https://github.com/akinsho/toggleterm.nvim" },

-- GIT
{ src = "https://github.com/kdheepak/lazygit.nvim" },
{ src = "https://github.com/esmuellert/vscode-diff.nvim" },
})

require("other-nvim").setup({ mappings = { "golang" } })
Expand Down
4 changes: 4 additions & 0 deletions home-manager/programs/neovim/nvim-pack-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
"rev": "2d7c2db2507fa3c4956142ee607431ddb2828639",
"src": "https://github.com/stevearc/dressing.nvim"
},
"fff.nvim": {
"rev": "9edf195c8fe71f1ab8f84e863fb27b469d2342bf",
"src": "https://github.com/dmtrKovalenko/fff.nvim"
},
Comment on lines +68 to +71

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

The lazygit.nvim and vscode-diff.nvim plugins are referenced in lua/plugins.lua (lines 93-94) and lua/keymaps.lua (lines 121, 123-125), but they are missing from this lock file. This will cause runtime errors when trying to use the keymaps. These plugins need to be added to the lock file with their respective repository URLs and commit hashes.

Copilot uses AI. Check for mistakes.
"fidget.nvim": {
"rev": "e32b672d8fd343f9d6a76944fedb8c61d7d8111a",
"src": "https://github.com/j-hui/fidget.nvim"
Expand Down
2 changes: 1 addition & 1 deletion home-manager/services/brew-upgrader/upgrade.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

set -euo pipefail

brew upgrade
/opt/homebrew/bin/brew upgrade

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

Hardcoding the path to the brew executable to /opt/homebrew/bin/brew makes this script specific to Apple Silicon Macs and will cause it to fail on Intel-based Macs where Homebrew is typically installed in /usr/local/bin. To make the script more portable across different architectures, it's better to dynamically determine the path to the brew executable.

Suggested change
/opt/homebrew/bin/brew upgrade
if [ -x "/opt/homebrew/bin/brew" ]; then
/opt/homebrew/bin/brew upgrade
elif [ -x "/usr/local/bin/brew" ]; then
/usr/local/bin/brew upgrade
else
echo "Error: Homebrew not found" >&2
exit 1
fi

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 | 🔴 Critical

Homebrew path is hardcoded to Apple Silicon Macs only.

The absolute path /opt/homebrew/bin/brew only works on Apple Silicon Macs. Intel Macs use /usr/local/bin/brew, and the script will fail silently on unsupported architectures due to set -e. This breaks portability.

Consider using a more robust approach that detects the correct Homebrew path:

-/opt/homebrew/bin/brew upgrade
+if command -v brew &> /dev/null; then
+  brew upgrade
+else
+  echo "Error: brew not found in PATH" >&2
+  exit 1
+fi

Alternatively, if the script is intentionally Apple Silicon-only, document this constraint clearly in a comment or README.

🤖 Prompt for AI Agents
In home-manager/services/brew-upgrader/upgrade.sh around line 5, the script
hardcodes /opt/homebrew/bin/brew which only exists on Apple Silicon and will
fail on Intel Macs; replace the hardcoded path with logic that locates Homebrew
dynamically (e.g., use command -v brew or check common locations like
/opt/homebrew/bin/brew and /usr/local/bin/brew) and fail with a clear error if
not found, or if the script is intended to be Apple Silicon–only, add a top-line
comment and README note documenting that constraint.

@cubic-dev-ai cubic-dev-ai Bot Dec 6, 2025

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.

P3: Hardcoded Homebrew path is specific to Apple Silicon Macs. On Intel Macs, Homebrew is at /usr/local/bin/brew. Consider using a conditional or checking for the path existence, though this may be intentional if only targeting ARM Macs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/services/brew-upgrader/upgrade.sh, line 5:

<comment>Hardcoded Homebrew path is specific to Apple Silicon Macs. On Intel Macs, Homebrew is at `/usr/local/bin/brew`. Consider using a conditional or checking for the path existence, though this may be intentional if only targeting ARM Macs.</comment>

<file context>
@@ -2,4 +2,4 @@
 set -euo pipefail
 
-brew upgrade
+/opt/homebrew/bin/brew upgrade
</file context>
Suggested change
/opt/homebrew/bin/brew upgrade
BREW_PATH="/opt/homebrew/bin/brew"
[ -x "$BREW_PATH" ] || BREW_PATH="/usr/local/bin/brew"
"$BREW_PATH" upgrade
Fix with Cubic

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

The hardcoded path /opt/homebrew/bin/brew only works on Apple Silicon Macs. Intel Macs install Homebrew to /usr/local/bin/brew.

Consider using a fallback pattern like other scripts in this codebase:

if [ -x /opt/homebrew/bin/brew ]; then
  /opt/homebrew/bin/brew upgrade
elif [ -x /usr/local/bin/brew ]; then
  /usr/local/bin/brew upgrade
else
  echo "brew binary not found" >&2
  exit 1
fi

This pattern is used in home-manager/services/ollama/default.nix:11-14 and home-manager/services/cliproxyapi/start.sh:33-36.

Suggested change
/opt/homebrew/bin/brew upgrade
if [ -x /opt/homebrew/bin/brew ]; then
/opt/homebrew/bin/brew upgrade
elif [ -x /usr/local/bin/brew ]; then
/usr/local/bin/brew upgrade
else
echo "brew binary not found" >&2
exit 1
fi

Copilot uses AI. Check for mistakes.

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

The hardcoded path /opt/homebrew/bin/brew assumes Homebrew is installed at the default Apple Silicon location. This will fail on Intel Macs where Homebrew is at /usr/local/bin/brew. Consider checking both locations or using the PATH from the launchd agent configuration which already includes both paths.

Suggested change
/opt/homebrew/bin/brew upgrade
PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
brew upgrade

Copilot uses AI. Check for mistakes.
24 changes: 22 additions & 2 deletions home-manager/services/cliproxyapi/default.nix
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
{ pkgs, ... }:
let
inherit (pkgs) lib;
in
{
launchd.agents.cliproxyapi = pkgs.lib.mkIf pkgs.stdenv.isDarwin {
launchd.agents.cliproxyapi = lib.mkIf pkgs.stdenv.isDarwin {
enable = true;
config = {
ProgramArguments = [
"${pkgs.bash}/bin/bash"
"${./start.sh}"
];
Environment = {
PATH = "${pkgs.lib.makeBinPath [ pkgs.gnused ]}:/opt/homebrew/bin:/usr/local/bin";
PATH = "${lib.makeBinPath [ pkgs.gnused ]}:/opt/homebrew/bin:/usr/local/bin";
};
KeepAlive = true;
RunAtLoad = true;
StandardOutPath = "/tmp/cliproxyapi.log";
StandardErrorPath = "/tmp/cliproxyapi.error.log";
};
};

systemd.user.services.cliproxyapi = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "CLI Proxy API server";
After = [ "network.target" ];
};
Service = {
Type = "simple";
Environment = "PATH=${lib.makeBinPath [ pkgs.gnused pkgs.bash ]}";
ExecStart = "${pkgs.bash}/bin/bash ${./start.sh}";
Restart = "always";
RestartSec = 3;
};
Install = {
WantedBy = [ "default.target" ];
};
};
}
47 changes: 37 additions & 10 deletions home-manager/services/dotfiles-updater/default.nix
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
{ pkgs }:
{ pkgs, ... }:
let
inherit (pkgs) lib;
in
{
systemd.user.services.dotfiles-updater = {
launchd.agents.dotfiles-updater = lib.mkIf pkgs.stdenv.isDarwin {
enable = true;
config = {
ProgramArguments = [
"${pkgs.bash}/bin/bash"
"${./update.sh}"
];
Environment = {
PATH = "${
lib.makeBinPath [
pkgs.git
pkgs.bash
pkgs.coreutils
]
}:/opt/homebrew/bin:/usr/local/bin";
};
StartCalendarInterval = [
{
Hour = 0;
Minute = 0;
}
];
StandardOutPath = "/tmp/dotfiles-updater.log";
StandardErrorPath = "/tmp/dotfiles-updater.error.log";
};
};
Comment on lines +6 to +31

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for launchd configuration examples in the repository
find . -type f -name "*.nix" -exec grep -l "launchd.agents" {} \;

Repository: shunkakinoki/dotfiles

Length of output: 361


🏁 Script executed:

# Look for any existing launchd agent configurations to see what key pattern is used
rg "launchd\.agents" -A 20 --max-count=5

Repository: shunkakinoki/dotfiles

Length of output: 8607


🌐 Web query:

nix-darwin launchd.agents config EnvironmentVariables documentation

💡 Result:

nix-darwin exposes Launchd options in the launchd module. Relevant keys for environment variables:

  • launchd.agents..environment — per-agent environment (attribute set of string or list). Example: { LANG = "nl_NL.UTF-8"; PATH = "/foo/bar/bin"; } . [1]
  • launchd.agents..serviceConfig.EnvironmentVariables — maps to the plist ServiceConfig key (attribute set of string). Use this to set additional EnvironmentVariables in the generated plist. [1]
  • launchd.envVariables / launchd.user.envVariables — global environment variables applied to all future processes launched by launchd (attribute set of string or list; lists are joined with colons). [1][2]

Minimal example (nix expression):
{
launchd.agents.myagent = {
command = "/path/to/bin";
environment = {
LANG = "en_US.UTF-8";
PATH = [ "/opt/my/tool/bin" ];
};
serviceConfig = {
EnvironmentVariables = {
FOO = "bar";
};
};
};
launchd.envVariables = {
LANG = "en_US.UTF-8";
};
}

Docs / references:

  • nix-darwin manual (launchd options: environment, serviceConfig.EnvironmentVariables, envVariables). [1]
  • alternate nix-darwin options listing (launchd.envVariables / launchd.user.envVariables). [2]

🏁 Script executed:

# Check how nix-darwin's launchd module actually handles the config attribute
# Search for nix-darwin source or documentation on launchd structure
rg "launchd.*config" --type nix -A 5 -B 5 | head -100

Repository: shunkakinoki/dotfiles

Length of output: 47


🌐 Web query:

nix-darwin launchd agents config plist passthrough Environment vs EnvironmentVariables

💡 Result:

Short answer: use EnvironmentVariables. Nix-darwin exposes two higher-level options — launchd.*.environment (and global launchd.envVariables / environment.variables) — which it converts into the plist key EnvironmentVariables, while serviceConfig.EnvironmentVariables lets you set the raw plist key directly. There is no separate launchd plist key named "Environment" (the correct launchd key is EnvironmentVariables). (nix-darwin.github.io)

Practical notes:

  • If you want a simple, Nix-native way to pass vars to an agent/daemon, set launchd.user.agents..environment (or launchd.daemons...environment). Nix-darwin will generate the plist with EnvironmentVariables. (nix-darwin.github.io)
  • If you need to control arbitrary plist fields (including exact types/structure) or add other launchd keys, use launchd.*.serviceConfig.EnvironmentVariables to place the key directly into the generated plist. (nix-darwin.github.io)
  • Do not expect shell expansions there — EnvironmentVariables values must be strings (launchd won’t do variable expansion). (launchd.info)

If you want, tell me which nix-darwin config snippet you’re using and I’ll show the minimal example.

Citations:


Change Environment to EnvironmentVariables — PATH override will otherwise be ignored

The launchd plist standard uses EnvironmentVariables for environment variables, not Environment. Using the wrong key causes launchd to ignore it silently, and update.sh will run without the custom PATH you've configured, potentially failing to find git/bash/coreutils.

Change:

     config = {
       ProgramArguments = [ ... ];
-      Environment = {
+      EnvironmentVariables = {
         PATH = "${...}";
       };

Additionally, add documentation above this block explaining what the agent does and why the PATH is customized, as per guidelines for service parameter documentation:

  # macOS: daily dotfiles auto-updater via launchd.
  # Ensures Git/Bash/coreutils and Homebrew are on PATH under launchd.
  launchd.agents.dotfiles-updater = lib.mkIf pkgs.stdenv.isDarwin {
🤖 Prompt for AI Agents
In home-manager/services/dotfiles-updater/default.nix around lines 6 to 31, the
launchd agent uses the wrong key "Environment" so the PATH override is ignored;
change the key to "EnvironmentVariables" (preserve the same PATH value and
lib.makeBinPath contents) so launchd applies the PATH, and add the two-line
documentation comment above the block as suggested: a short description that
this is a macOS daily dotfiles auto-updater and that Git/Bash/coreutils and
Homebrew are ensured on the PATH under launchd.


systemd.user.services.dotfiles-updater = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "Dotfiles auto-updater service";
};
Service = {
Type = "oneshot";
Environment = "PATH=${
pkgs.lib.makeBinPath (
with pkgs;
[
git
bash
]
)
lib.makeBinPath [
pkgs.git
pkgs.bash
]
}";
ExecStart = "${./update.sh}";
};
};

systemd.user.timers.dotfiles-updater = {
systemd.user.timers.dotfiles-updater = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "Timer for dotfiles auto-updater";
};
Expand Down
38 changes: 24 additions & 14 deletions home-manager/services/neverssl-keepalive/default.nix
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
{ pkgs }:
{ pkgs, ... }:
let
keepaliveScript = pkgs.writeShellApplication {
name = "neverssl-keepalive";
runtimeInputs = [ pkgs.curl ];
text = ''
set -euo pipefail
if ! curl -fsS --max-time 10 http://neverssl.com > /dev/null 2>&1; then
exit 0
fi
'';
};
inherit (pkgs) lib;
in
{
systemd.user.services.neverssl-keepalive = {
# macOS (launchd)
launchd.agents.neverssl-keepalive = lib.mkIf pkgs.stdenv.isDarwin {
enable = true;
config = {
ProgramArguments = [
"${pkgs.bash}/bin/bash"
"${./keepalive.sh}"
];
Environment = {
PATH = lib.makeBinPath [ pkgs.curl ];
};
StartInterval = 3;
StandardOutPath = "/tmp/neverssl-keepalive.log";
StandardErrorPath = "/tmp/neverssl-keepalive.error.log";
};
};

# Linux (systemd)
systemd.user.services.neverssl-keepalive = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "Keep captive portal alive via neverssl.com";
Wants = [ "network-online.target" ];
After = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${keepaliveScript}/bin/neverssl-keepalive";
Environment = "PATH=${lib.makeBinPath [ pkgs.curl pkgs.bash ]}";
ExecStart = "${pkgs.bash}/bin/bash ${./keepalive.sh}";
};
};

systemd.user.timers.neverssl-keepalive = {
systemd.user.timers.neverssl-keepalive = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "Timer for neverssl captive portal keepalive";
};
Expand Down
8 changes: 8 additions & 0 deletions home-manager/services/neverssl-keepalive/keepalive.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

# Keep captive portal connections alive by periodically hitting neverssl.com

set -euo pipefail

# Silently ping neverssl.com - ignore failures (network may be unavailable)
curl -fsS --max-time 10 http://neverssl.com > /dev/null 2>&1 || true
44 changes: 22 additions & 22 deletions home-manager/services/ollama/default.nix
Original file line number Diff line number Diff line change
@@ -1,30 +1,13 @@
{ pkgs }:
{ pkgs, ... }:
let
inherit (pkgs) lib writeShellApplication;

# launchd wrapper so we can reuse the Homebrew-installed ollama binary
ollamaHomebrew = writeShellApplication {
name = "ollama-homebrew";
text = ''
set -euo pipefail

if [ -x /opt/homebrew/bin/ollama ]; then
exec /opt/homebrew/bin/ollama "$@"
elif [ -x /usr/local/bin/ollama ]; then
exec /usr/local/bin/ollama "$@"
else
echo "ollama binary not found; install it with \"brew install ollama\"" >&2
exit 1
fi
'';
};
inherit (pkgs) lib;
in
lib.mkIf pkgs.stdenv.isDarwin {
launchd.agents.ollama = {
{
launchd.agents.ollama = lib.mkIf pkgs.stdenv.isDarwin {
enable = true;
config = {
ProgramArguments = [
(lib.getExe ollamaHomebrew)
"/opt/homebrew/bin/ollama"
"serve"
Comment on lines +10 to 11

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

The hardcoded path /opt/homebrew/bin/ollama assumes Homebrew is installed at the default Apple Silicon location. This will fail on Intel Macs where Homebrew is at /usr/local/bin/ollama. Consider using a more flexible approach similar to the previous implementation that checked both locations, or use pkgs.lib.getExe with a Homebrew-wrapped package.

Suggested change
"/opt/homebrew/bin/ollama"
"serve"
"/bin/sh"
"-c"
"if [ -x /opt/homebrew/bin/ollama ]; then exec /opt/homebrew/bin/ollama serve; elif [ -x /usr/local/bin/ollama ]; then exec /usr/local/bin/ollama serve; else echo 'ollama not found in Homebrew locations' >&2; exit 1; fi"

Copilot uses AI. Check for mistakes.
];
KeepAlive = true;
Expand All @@ -34,4 +17,21 @@ lib.mkIf pkgs.stdenv.isDarwin {
StandardErrorPath = "/tmp/ollama.error.log";
};
};

systemd.user.services.ollama = lib.mkIf pkgs.stdenv.isLinux {
Unit = {
Description = "Ollama AI model server";
After = [ "network.target" ];
};
Service = {
Type = "simple";
ExecStart = "${pkgs.ollama}/bin/ollama serve";
Environment = "OLLAMA_HOST=0.0.0.0";

Copilot AI Dec 6, 2025

Copy link

Choose a reason for hiding this comment

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

Setting OLLAMA_HOST=0.0.0.0 exposes the Ollama service on all network interfaces, making it accessible from the local network and potentially beyond. Unless this is intentional for network access, consider using 127.0.0.1 to restrict access to localhost only for better security.

Copilot uses AI. Check for mistakes.
Restart = "always";
RestartSec = 3;
};
Install = {
WantedBy = [ "default.target" ];
};
};
Comment on lines +21 to +36

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add documentation for service parameters.

The systemd service configuration is technically sound and follows systemd conventions correctly. However, it lacks documentation explaining the service parameters, which violates the coding guidelines.

Consider adding comments to document the configuration:

+  # Ollama AI model server service for Linux
+  # Configured to listen on all interfaces (0.0.0.0) for network access
   systemd.user.services.ollama = lib.mkIf pkgs.stdenv.isLinux {
     Unit = {
       Description = "Ollama AI model server";
+      # Start after network is available
       After = [ "network.target" ];
     };
     Service = {
       Type = "simple";
       ExecStart = "${pkgs.ollama}/bin/ollama serve";
+      # Allow network access from other machines
       Environment = "OLLAMA_HOST=0.0.0.0";
+      # Restart on failure with 3-second delay
       Restart = "always";
       RestartSec = 3;
     };

Based on learnings, service configurations should include clear documentation for service parameters.

🤖 Prompt for AI Agents
In home-manager/services/ollama/default.nix around lines 21 to 36, the
systemd.user.services.ollama block lacks inline documentation for the Unit,
Service and Install parameters; add brief comments above the Unit, Service and
Install blocks describing their purpose and intent, and add short inline
comments for key fields (e.g. Description, After, Type, ExecStart with what is
being started, Environment explaining the exposed variable and value,
Restart/RestartSec semantics, and WantedBy) so readers understand why each
parameter is set and what changing them would do.

}
Loading