From 8a38d0768c657979d1059ff08f28355b2e48cb9f Mon Sep 17 00:00:00 2001 From: alexion Date: Sat, 18 Jul 2026 23:49:05 -0400 Subject: [PATCH] Add a Claude Code Module and enable it on neogaia MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Install Claude Code through home-manager's native programs.claude-code module, matching how the tmux and fish Modules use their home-manager options rather than a raw home.packages entry. The feature Module at modules/claude-code/claude-code.nix stays thin — just the enable option and the delegation — and writes no settings, so login and first-run configuration remain interactive and no auth material enters the repo. Signing in without a browser (needed over the console or SSH) is documented in modules/claude-code/authentication.md: the paste-code OAuth flow, where the printed URL is opened on another device and the code pasted back, and the ANTHROPIC_API_KEY path for non-interactive use. The neogaia toplevel builds with claude-code-2.1.209 included. --- .claude/tasks/0008-claude-code-module.md | 22 ++++++++++++++++ hosts/neogaia/default.nix | 3 +++ modules/claude-code/authentication.md | 32 ++++++++++++++++++++++++ modules/claude-code/claude-code.nix | 22 ++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 .claude/tasks/0008-claude-code-module.md create mode 100644 modules/claude-code/authentication.md create mode 100644 modules/claude-code/claude-code.nix diff --git a/.claude/tasks/0008-claude-code-module.md b/.claude/tasks/0008-claude-code-module.md new file mode 100644 index 0000000..10349a5 --- /dev/null +++ b/.claude/tasks/0008-claude-code-module.md @@ -0,0 +1,22 @@ +--- +spec: laptop-mvi +blocked-by: 0001-skeleton-and-building-host +--- + +## What to build + +Install Claude Code declaratively on `neogaia`, and make it authenticatable without a browser on the laptop so it can be used over the console/SSH via the paste-code flow or an API key. + +## Acceptance criteria + +- [x] Claude Code is installed declaratively (following the `Enable convention` if expressed as a `Module`) and enabled on `neogaia`. +- [x] The browserless authentication path (paste-code flow or API key) is documented so it works over console/SSH. +- [x] The `neogaia` toplevel still builds with Claude Code included. + +## Implementation Notes + +- **Native home-manager module, not a raw package.** Claude Code is enabled through home-manager's own `programs.claude-code` module (`home-manager.users..programs.claude-code.enable = true`), mirroring how `tmux`/`fish` use their native home-manager options rather than dropping a package into `home.packages`. The module ships within home-manager itself, so — unlike `nvim`/nixvim — no new flake input is needed. Per the invocation's steer to prefer the tmux/nvim conventions over the task wording, the feature `Module` at `modules/claude-code/claude-code.nix` is kept as thin as the `tmux` module: just the `enable` option and the delegation. +- **No settings written.** The module manages no `~/.claude` contents and writes no `settings.json`, so login and first-run configuration stay interactive. This keeps auth material (subscription token or API key) out of the repo. +- **Auth docs co-located with the module.** The browserless authentication guide lives at `modules/claude-code/authentication.md`, next to the module, following the repo pattern where each module directory holds its own supporting files. It covers both the paste-code OAuth flow (open the printed URL on another device, paste the code back — works unchanged over SSH) and the `ANTHROPIC_API_KEY` path. This is distinct from task 0009's OS-install docs, which cover `disko-install`, not the CLI login. +- **Verification.** `nix build .#checks.x86_64-linux.neogaia` (the primary Host seam) builds the toplevel with `claude-code-2.1.209` included; `config.modules.claude-code.enable` and the home-manager `programs.claude-code.enable` both evaluate `true`. +- **Note on flake evaluation.** The new module file had to be `git add`ed before the flake could see it — flakes evaluate the git tree, so an untracked Module is invisible to the Auto-loader and the host errors with "option does not exist". diff --git a/hosts/neogaia/default.nix b/hosts/neogaia/default.nix index 4991ea9..c8b7778 100644 --- a/hosts/neogaia/default.nix +++ b/hosts/neogaia/default.nix @@ -45,6 +45,9 @@ # Neovim, configured declaratively via nixvim. modules.nvim.enable = true; + # Claude Code, Anthropic's CLI, installed via home-manager. + modules.claude-code.enable = true; + # Locale preferences for the base system. time.timeZone = "America/New_York"; i18n.defaultLocale = "en_GB.UTF-8"; diff --git a/modules/claude-code/authentication.md b/modules/claude-code/authentication.md new file mode 100644 index 0000000..316e81e --- /dev/null +++ b/modules/claude-code/authentication.md @@ -0,0 +1,32 @@ +# Authenticating Claude Code without a browser + +`neogaia` is driven from the console and over SSH, where no local browser can service Claude Code's default OAuth redirect. +Either of the two paths below signs the CLI in from a bare terminal. +Both are one-time actions per machine; the credentials land under `~/.claude`, which home-manager does not overwrite. + +## Paste-code flow (Claude subscription or Console OAuth) + +Run `claude` and start the login with the `/login` command (the first run offers it automatically). +On a machine with no browser it cannot open the authorization page itself, so it prints the authorization URL and waits. + +1. Copy the printed URL to a browser on any other device (phone, another laptop). +2. Sign in and approve the request there. +3. The page returns a short authorization code; paste it back at the `claude` prompt still waiting in the terminal. + +The session then completes and the token is stored, so later runs need no further login. +Because the URL is opened on a *different* device, this works unchanged over SSH. + +## API key + +For non-interactive use, set an Anthropic API key from in the environment before launching `claude`: + +```console +$ export ANTHROPIC_API_KEY=sk-ant-... +$ claude +``` + +Claude Code reads `ANTHROPIC_API_KEY` on startup and skips the interactive login entirely, so this path needs neither a browser nor the paste-code exchange. +Usage is billed to the Console account that owns the key rather than to a Claude subscription. + +The key is a secret and is deliberately not baked into this configuration. +Export it from the shell for a one-off, or source it from a secret store once one exists on the Host. diff --git a/modules/claude-code/claude-code.nix b/modules/claude-code/claude-code.nix new file mode 100644 index 0000000..fa26140 --- /dev/null +++ b/modules/claude-code/claude-code.nix @@ -0,0 +1,22 @@ +{ + config, + lib, + ... +}: +# Claude Code — Anthropic's CLI — for the primary user, installed declaratively +# through home-manager. home-manager ships the package and owns ~/.claude; no +# settings are written here, so login and first-run configuration stay +# interactive. Signing in without a browser, as needed over the console or SSH, +# is covered in ./authentication.md. +let + cfg = config.modules.claude-code; + user = config.user.name; +in +{ + options.modules.claude-code.enable = + lib.mkEnableOption "Claude Code, Anthropic's CLI, installed via home-manager"; + + config = lib.mkIf cfg.enable { + home-manager.users.${user}.programs.claude-code.enable = true; + }; +}