diff --git a/flake.lock b/flake.lock index b085be2..b204d41 100644 --- a/flake.lock +++ b/flake.lock @@ -286,22 +286,6 @@ "type": "github" } }, - "ponytail": { - "flake": false, - "locked": { - "lastModified": 1788798420, - "narHash": "sha256-LPNMyHsri3+eeDmphEAKL1JgoRE4dLIPfZ4XZ+xu5UY=", - "owner": "DietrichGebert", - "repo": "ponytail", - "rev": "356918eba965ee1eac64bd3a7f0dd02108350de5", - "type": "github" - }, - "original": { - "owner": "DietrichGebert", - "repo": "ponytail", - "type": "github" - } - }, "root": { "inputs": { "disko": "disko", @@ -314,7 +298,6 @@ "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs", "noctalia": "noctalia", - "ponytail": "ponytail", "sops-nix": "sops-nix", "walker": "walker" } diff --git a/flake.nix b/flake.nix index 39e5a9e..9b14c35 100644 --- a/flake.nix +++ b/flake.nix @@ -29,10 +29,6 @@ url = "github:noctalia-dev/noctalia"; inputs.nixpkgs.follows = "nixpkgs"; }; - ponytail = { - url = "github:DietrichGebert/ponytail"; - flake = false; - }; sops-nix = { url = "github:Mic92/sops-nix"; inputs.nixpkgs.follows = "nixpkgs"; diff --git a/modules/capabilities/ai.nix b/modules/capabilities/ai.nix index 881f888..1ff95f5 100644 --- a/modules/capabilities/ai.nix +++ b/modules/capabilities/ai.nix @@ -7,18 +7,7 @@ in flake.modules.nixos.ai = { pkgs, ... }: let - context7Headers = pkgs.writeShellScript "context7-http-headers" '' - set -euo pipefail - - CONTEXT7_KEY_FILE="''${XDG_CONFIG_HOME:-$HOME/.config}/context7/api-key" - [[ -r "$CONTEXT7_KEY_FILE" ]] - CONTEXT7_KEY="$(< "$CONTEXT7_KEY_FILE")" - - exec ${pkgs.jq}/bin/jq --null-input --compact-output \ - --arg authorization "Bearer $CONTEXT7_KEY" \ - '{ Authorization: $authorization }' - ''; - + ai = import ./ai/_assets.nix { inherit pkgs; }; codexConfig = (pkgs.formats.toml { }).generate "codex-config.toml" { model = "gpt-5.6-sol"; model_reasoning_effort = "high"; @@ -43,18 +32,21 @@ in status_line_use_colors = true; }; - mcp_servers = { - context7 = { - url = "https://mcp.context7.com/mcp"; - http_headers_helper = "${context7Headers}"; + features.plugins = true; + marketplaces.nix-managed = { + source_type = "local"; + source = "${ai.marketplace}"; + }; + plugins."ponytail@nix-managed".enabled = true; + hooks = ai.codexHooks; + mcp_servers = ai.servers // { + context7 = ai.servers.context7 // { + http_headers_helper = "${ai.context7Headers}"; }; - nixos = { - command = "${pkgs.uv}/bin/uvx"; - args = [ "mcp-nixos" ]; + nixos = ai.servers.nixos // { startup_timeout_sec = 60.0; tools.nix.approval_mode = "approve"; }; - openaiDeveloperDocs.url = "https://developers.openai.com/mcp"; }; }; in @@ -82,7 +74,10 @@ in }; flake.modules.homeManager.ai = - { pkgs, ... }: + { pkgs, lib, ... }: + let + ai = import ./ai/_assets.nix { inherit pkgs; }; + in { services.podman = { volumes = { @@ -111,6 +106,7 @@ in home.packages = [ # Node-based Codex plugin hooks and skill scripts need node/npm outside project dev shells. pkgs.nodejs + pkgs.python3 pkgs.llm-agents.codex pkgs.llm-agents.pi pkgs.llm-agents.claude-code @@ -193,70 +189,18 @@ in height = "90%" ''; - home.file.".codex/AGENTS.md".text = '' - # Environment - - This is Nix; use `nix run` for missing commands. - - - Default Nix commands to `--quiet`; inspect logs only for failing derivations. - - Never emit full package, derivation, closure, GC, or store-tree listings. Capture them in `/tmp`, summarize or filter, then inspect targeted excerpts. - - For store sources, locate files first, then read exact ranges; avoid broad `rg -C` searches. - - # Work - - - Inspect the relevant implementation and follow project conventions. - - Prefer the simplest complete solution. - - Identify and address root causes rather than masking symptoms. - - Prefer existing capabilities and defaults. Add custom code, wrappers, helpers, defensive checks, fallbacks, compatibility layers, or abstractions only for a concrete need. - - Delete unnecessary code; keep related logic together. - - Use types, schemas, and interfaces to prevent invalid states where practical. - - Keep changes focused; preserve behavior unless requested or fixing a confirmed defect. - - Verify unfamiliar, version-sensitive, or consequential APIs against current documentation; prefer context7. - - Run the most relevant checks; report only verification performed. - - # Reviews and refactoring - - - Inspect complete relevant context, not just the obvious file or diff. - - Find unnecessary or duplicated code, redundant state, avoidable checks, reimplemented capabilities, and repeated defaults. - - Ask whether defensive code protects a realistic state or hides an unclear invariant. - - Optimize conceptual complexity, not line count. - - Separate confirmed problems from optional improvements; explain important tradeoffs. - - # Context - - Every action must justify its context. Minimize tools and skills; prefer targeted queries and bounded output. Ask for readily available large output instead of retrieving it. - - # Remote state - - GitHub and all other repository remotes are strictly read-only. Fetching, - cloning, and reading metadata, issues, pull requests, checks, and diffs are - allowed. Never push or mutate remote repositories, issues, pull requests, - reviews, comments, labels, assignments, projects, workflows, releases, - settings, keys, secrets, or variables. Local files and local Git state may - be modified as authorized. - ''; - - home.file = { - ".agents/plugins/marketplace.json".text = builtins.toJSON { - name = "nix-managed"; - plugins = [ - { - name = "ponytail"; - source = { - source = "local"; - path = "./.codex/plugins/ponytail"; - }; - policy = { - installation = "AVAILABLE"; - authentication = "ON_INSTALL"; - }; - category = "Productivity"; - } - ]; + # Codex owns its writable user config; Nix owns assets and system defaults. + home.file = + lib.mapAttrs' ( + name: source: lib.nameValuePair ".agents/skills/${name}" { inherit source; } + ) ai.skills + // { + ".codex/AGENTS.md".text = builtins.readFile ./ai/instructions/codex.md; + # Codex's installed-plugin scanner skips symlinked version directories. + "${ai.codexCache}" = { + source = ai.sources.ponytail; + recursive = true; + }; }; - ".agents/skills/context7-mcp".source = ./skills/context7-mcp; - ".agents/skills/nixos-maintainer".source = ./skills/nixos-maintainer; - ".codex/plugins/ponytail".source = inputs.ponytail; - }; }; } diff --git a/modules/capabilities/ai/README.md b/modules/capabilities/ai/README.md new file mode 100644 index 0000000..8a4b21b --- /dev/null +++ b/modules/capabilities/ai/README.md @@ -0,0 +1,41 @@ +# Codex installation + +`ai.nix` installs binaries and Codex defaults on Polaris and Zenith. +`_sources.nix` pins upstream assets; `_assets.nix` declares skills, MCP servers, +the Ponytail marketplace and the upstream Herdr session hook. +Edit `instructions/codex.md` for global Codex instructions. + +Nix owns installation files. Codex owns writable user settings, plugin toggles, +trust, credentials and sessions. Bundled/service-delivered capabilities remain +vendor-managed. Update Ponytail's revision, hash and `ponytailVersion` together. +The cache uses real directories with linked files because Codex skips symlinked +plugin version directories. Do not run plugin updaters on Nix-managed assets. + +Herdr's skill is linked directly from the same source as its binary. An agent +can also read it with `herdr --skill`; no generated file is necessary. +The upstream session hook supports restore, not model instruction injection. + +## Manual transition + +Close Codex before cleaning up the previous setup and activating the new one. +No migration or compatibility code runs automatically. + +- Remove old standalone skills: `~/.codex/skills/ponytail`, + `~/.agents/skills/find-skills`, `~/.agents/skills/context7-mcp.bak`, + and `~/.codex/skills/nixos-maintainer.bak`. +- Remove the old Ponytail cache `~/.codex/plugins/cache/ponytail/ponytail`. + Remove any unmanaged copy at `~/.codex/plugins/cache/nix-managed/ponytail/4.9.0` + if it blocks Home Manager activation. The obsolete managed + `~/.codex/plugins/ponytail` link is removed by Home Manager. +- In `~/.codex/config.toml`, remove the old `marketplaces.ponytail` entry and + rename `plugins."ponytail@ponytail"` to `plugins."ponytail@nix-managed"`, + preserving its enabled preference. Remove user launch fields for the + `context7`, `nixos` and `openaiDeveloperDocs` MCP servers so system defaults + apply; preserve toggles and unrelated preferences. +- Remove the old Herdr SessionStart command from `~/.codex/hooks.json` and its + `~/.codex/herdr-agent-state.sh` file. Preserve unrelated hooks. +- Activate your Nix configuration, then restart Codex. + +If an earlier version of this work was activated, Home Manager removes its +managed instruction and integration links for other harnesses. Unmanaged copies +must be removed manually if no longer wanted. diff --git a/modules/capabilities/ai/_assets.nix b/modules/capabilities/ai/_assets.nix new file mode 100644 index 0000000..f290d61 --- /dev/null +++ b/modules/capabilities/ai/_assets.nix @@ -0,0 +1,64 @@ +{ pkgs }: +let + sources = import ./_sources.nix { inherit pkgs; }; + json = pkgs.formats.json { }; + ponytailVersion = "4.9.0"; + context7Headers = pkgs.writeShellScript "context7-http-headers" '' + set -euo pipefail + CONTEXT7_KEY_FILE="''${XDG_CONFIG_HOME:-$HOME/.config}/context7/api-key" + CONTEXT7_KEY="$(< "$CONTEXT7_KEY_FILE")" + exec ${pkgs.jq}/bin/jq --null-input --compact-output \ + --arg authorization "Bearer $CONTEXT7_KEY" '{ Authorization: $authorization }' + ''; + codexHooks = { + SessionStart = [ + { + hooks = [ + { + type = "command"; + command = "${pkgs.bash}/bin/bash ${sources.herdr}/src/integration/assets/codex/herdr-agent-state.sh session"; + timeout = 10; + } + ]; + } + ]; + }; +in +{ + inherit sources ponytailVersion context7Headers; + inherit codexHooks; + codexCache = ".codex/plugins/cache/nix-managed/ponytail/${ponytailVersion}"; + marketplace = pkgs.runCommand "ai-marketplace" { } '' + mkdir -p "$out/.agents/plugins" "$out/plugins" + ln -s ${sources.ponytail} "$out/plugins/ponytail" + cp ${ + json.generate "ai-marketplace.json" { + name = "nix-managed"; + plugins = [ + { + name = "ponytail"; + source = { + source = "local"; + path = "./plugins/ponytail"; + }; + policy = { + installation = "AVAILABLE"; + authentication = "ON_INSTALL"; + }; + category = "Productivity"; + } + ]; + } + } "$out/.agents/plugins/marketplace.json" + ''; + skills = { + context7-mcp = ../skills/context7-mcp; + nixos-maintainer = ../skills/nixos-maintainer; + herdr = "${sources.herdr}/skills/herdr"; + }; + servers = { + context7.url = "https://mcp.context7.com/mcp"; + nixos.command = "${pkgs.mcp-nixos}/bin/mcp-nixos"; + openaiDeveloperDocs.url = "https://developers.openai.com/mcp"; + }; +} diff --git a/modules/capabilities/ai/_sources.nix b/modules/capabilities/ai/_sources.nix new file mode 100644 index 0000000..0ab2884 --- /dev/null +++ b/modules/capabilities/ai/_sources.nix @@ -0,0 +1,10 @@ +{ pkgs }: +{ + ponytail = pkgs.fetchFromGitHub { + owner = "DietrichGebert"; + repo = "ponytail"; + rev = "356918eba965ee1eac64bd3a7f0dd02108350de5"; + hash = "sha256-LPNMyHsri3+eeDmphEAKL1JgoRE4dLIPfZ4XZ+xu5UY="; + }; + herdr = pkgs.llm-agents.herdr.src; +} diff --git a/modules/capabilities/ai/instructions/codex.md b/modules/capabilities/ai/instructions/codex.md new file mode 100644 index 0000000..4d8b4f0 --- /dev/null +++ b/modules/capabilities/ai/instructions/codex.md @@ -0,0 +1,23 @@ +# Environment + +- This is Nix; use `nix run` for missing commands. Default Nix commands to `--quiet`. +- Keep output bounded. Capture large Nix listings in `/tmp`; inspect targeted excerpts and failing logs only. Locate store files before reading them. +- Manage persistent binaries, skills, plugins and MCP definitions in `/home/kiri/.config/nixos`. Keep interactive settings, credentials and session state outside Nix. +- `HERDR_ENV=1` means you are inside Herdr. Read the Herdr skill or run `herdr --skill` when Herdr interaction is needed. + +# Work + +- Inspect relevant code and callers; follow project conventions and fix root causes. +- Prefer existing code, standard libraries and native capabilities. Avoid speculative abstractions; optimize conceptual simplicity, not line count. +- Keep changes focused. Prefer clean breaking changes over compatibility layers or migration scripts; explain required manual steps. +- Use targeted reads and tool calls. Verify unfamiliar or consequential APIs against version-appropriate primary documentation; prefer Context7 when needed. +- In reviews, distinguish confirmed problems from optional improvements and explain meaningful tradeoffs. +- Never push without my explicit approval. Other remote actions must remain within the authorized task. +- User instructions take precedence over skill guidelines, including testing defaults. + +# Validation + +Do not add or run tests unless explicitly requested or necessary to resolve a +concrete correctness concern. Use the smallest relevant validation and stop once +it provides sufficient confidence. Avoid broad suites and repeated builds without +a specific reason. Report only checks actually performed.