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
4 changes: 4 additions & 0 deletions nix/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@
homeModules = roles.macminiHeadless;
specialArgs = {
inherit user;
# hosts/macmini.nix and its home layer both take this; without it the whole macmini
# configuration stops evaluating, which is where it was found — nothing in CI builds
# this closure, so it went unnoticed until something needed the mini again.
nixpkgsAgents = nixpkgs-agents;
claudeAcp = claude-acp.packages.${system}.default;
sopsNix = sops-nix;
# 母艦からのリモートビルドを受ける側。接続してくるユーザーが trusted-users に
Expand Down
23 changes: 1 addition & 22 deletions nix/home/darwin.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,10 @@
lib,
user,
nixpkgsUnstable,
nixpkgsAgents,
secureEnclaveKey,
...
}:
let
# The rolling lineage the agent CLIs ride on (same one cli.nix uses). gemini-cli belongs here
# rather than on the pinned channel: Google refuses old clients outright — 0.42 answers
# "This client is no longer supported" and hands back no model at all — so a pinned version of
# this particular tool is a version that stops working on Google's schedule, not ours.
agentPkgs = import ../lib/unstable-pkgs.nix {
nixpkgsUnstable = nixpkgsAgents;
inherit (pkgs.stdenv.hostPlatform) system;
};

# Bound here rather than inline in home.packages because the LaunchAgent below
# needs the path too, and both must point at the same store path.
mechvibes-dx = pkgs.callPackage ../pkgs/mechvibes-dx.nix { };
Expand All @@ -37,6 +27,7 @@ in
../modules/home/darwin-services.nix
../modules/home/darwin-apps.nix
../modules/home/darwin-ai-client.nix
../modules/home/agy.nix
];

# Nothing puts an .app under ~/Applications any more: bundles come from environment.systemPackages
Expand Down Expand Up @@ -219,18 +210,6 @@ in
# mac-specific packages
home.packages = with pkgs; [
bun # generate/type-check karabiner.ts config
# agy: Google's terminal agent, for agents that want a second model rather than a browser.
# Auth is the account's own OAuth, which is what the AI Pro subscription applies to; an API
# key would be separately billed and the subscription does not cover it.
#
# Not gemini-cli, which was tried first and is over: Google answers it with "this client is
# no longer supported" and hands back no model at all, whatever the version. nixpkgs marks
# the package for removal for the same reason. Antigravity is where that account went.
#
# Signing in through an automated browser is not the alternative, and was also tried: Google
# returns the sign-in flow to its first step for anything driven over CDP, whatever the
# password is. OAuth is a different road — the human consents in their own browser, once.
agentPkgs.antigravity-cli
pngpaste # needed for macOS image paste in obsidian.nvim / img-clip
syncthing # Syncthing CLI (the resident is the LaunchAgent in services.syncthing)
xcodegen # generate .xcodeproj from project.yml (Mac-only, since meta.platforms = darwin in Linux nixpkgs)
Expand Down
5 changes: 4 additions & 1 deletion nix/home/macmini.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ let
};
in
{
imports = [ ../modules/home/darwin-agent-state-sync.nix ];
imports = [
../modules/home/darwin-agent-state-sync.nix
../modules/home/agy.nix
];

# macmini-specific layer. The base CLI/zsh/XDG set inherits home/common.nix
# composed on the flake side (no sops/age keys are brought in).
Expand Down
57 changes: 57 additions & 0 deletions nix/modules/home/agy.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Antigravity CLI component (ECS: profile). Google's terminal agent, `agy`.
#
# This is how a second model is reached from here. Not the browser: signing in to Gemini through
# an automated browser does not work, and was tried — Google returns the sign-in flow to its first
# step for anything driven over CDP, whatever the password is. And not gemini-cli, which was also
# tried and is over: it answers "this client is no longer supported" after authenticating, at any
# version, and nixpkgs marks it for removal for the same reason. Auth is the account's own OAuth
# (one interactive `agy` per machine), which is what the AI Pro subscription applies to.
#
# The rolling agents lineage on purpose: Google refuses old clients outright, so pinning this
# particular tool would only decide in advance the day it stops working.
{
config,
lib,
pkgs,
nixpkgsAgents,
...
}:
let
agentPkgs = import ../../lib/unstable-pkgs.nix {
nixpkgsUnstable = nixpkgsAgents;
inherit (pkgs.stdenv.hostPlatform) system;
};

# Declared keys only. Pro rather than Flash by default because this is the tool reached for a
# second opinion, where the answer matters more than what it costs; `--model` picks another per
# call (`agy models` lists them, Claude and gpt-oss included).
settings = {
model = "gemini-3.1-pro-high";
enableTelemetry = false;
};

wanted = pkgs.writeText "agy-settings.json" (builtins.toJSON settings);
settingsFile = "${config.home.homeDirectory}/.gemini/antigravity-cli/settings.json";
in
{
home.packages = [ agentPkgs.antigravity-cli ];

# Merged rather than linked: agy owns this file and writes to it as it runs (every directory
# trusted lands here). A read-only store symlink would be a file it cannot update, so the keys
# declared above are merged into whatever it already has, and everything else is left alone.
home.activation.agySettings = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
$DRY_RUN_CMD ${pkgs.python3}/bin/python3 - <<'PY'
import json, pathlib

target = pathlib.Path("${settingsFile}")
target.parent.mkdir(parents=True, exist_ok=True)
try:
current = json.loads(target.read_text())
except (FileNotFoundError, json.JSONDecodeError):
current = {}
merged = {**current, **json.loads(pathlib.Path("${wanted}").read_text())}
if merged != current:
target.write_text(json.dumps(merged, indent=2) + "\n")
PY
'';
}
Loading