Utilizes the IceDOS framework. The full bible — module structure, config flow, the
icedos rebuild --buildtest loop,validate.*helpers, dep loading — lives in core: https://github.com/IceDOS/core/blob/main/AGENTS.md — this file only covers what is specific to ai-tools.
- Build/test only via the
icedosCLI — neversudo nixos-rebuild. - Never
git commit/stash/reset/pull— the user manages git. - Every option uses a
validate.*/mk*Optionhelper; no untyped options. - Option-path segments that hold other options are kebab-case (
agent-bridge); only leaf value keys are camelCase (remoteLookup). - A module's
config.tomldefaults must mirror itsicedos.nixdefaults. - Format with
icedos nixf .after editing any.nix.
AI agents, local model servers and their glue: claude-code, antigravity, opencode, prime-agent
(agents), llamacpp, ollama (model servers), peon-ping (agent-event audio),
mcp-server (icedos mcp serve), one module per MCP server (basic-memory-mcp,
chrome-devtools-mcp, context7-mcp, ddev-mcp, kwin-mcp, nixos-mcp, opencart-mcp, pagespeed-insights-mcp, plesk-mcp, rea-mcp,
signal-mcp), alimit (usage limits for every agent and provider),
ai-accounts (several accounts per agent), agent-launch (fzf agent picker) and
code-intelligence (per-language LSP skills).
Every option here lives under icedos.ai-tools: the shared rules, skills and mcp-servers, and one
attrset per module (icedos.ai-tools.prime-agent, ...).
modules/<category>/<name>/{icedos.nix,config.toml}; flake.nix exposes them via
icedosLib.scanModules { path = ./modules; filename = "icedos.nix"; }, which recurses, and
meta.name (not the folder) names the module. The categories are agents (antigravity,
claude-code, opencode, prime-agent), models (llamacpp, ollama), mcp (one folder per MCP server: icedos-mcp, whose module name is mcp-server, and
basic-memory, chrome-devtools, context7, ddev, kwin, nixos, opencart, pagespeed-insights, plesk, rea, signal, each named
<folder>-mcp except kwin-mcp; mcp/lib holds their shared Nix helpers) and integrations (agent-launch, ai-accounts, alimit, code-intelligence, peon-ping).
modules/default loads whenever the repo is listed and owns the shared options.
icedos.ai-tools.rules— name = markdown. prime-agent writes them all to<dataDir>/APPEND_SYSTEM.md, which 0.10.0 appends to every system prompt, antigravity writes~/.gemini/config/rules/<name>.md, opencode writes~/.config/opencode/rules/<name>.mdand lists it ininstructions, andclaude-codesetsprograms.claude-code.rules(mkDefault per entry).icedos.ai-tools.skills— name = full SKILL.md. Same fan-out: prime-agentskills.extra-builtin, antigravity~/.gemini/antigravity-cli/skills/, opencodeprograms.opencode.skills, Claude Codeprograms.claude-code.skills. prime-agent bakes them into a wrapper layer overprime-agent.unwrapped, so skill edits never recompile Rust.- Per-agent options of the same name win over the shared entry
(
antigravity.{rules,skills},opencode.skills,prime-agent.extensions.rules,prime-agent.skills.extra-builtin). - Non-agent modules (
llamacpp,ollama,peon-pingand themcpmodules) have askillsoption that adds toicedos.ai-tools.skills, so a skill ships with the module it documents. icedos.ai-tools.mcp-servers— name =programs.mcp.serversentry.defaultgives every hm user these servers (mkDefault per entry, so a same-namedprograms.mcp.serversentry wins), which every agent module reads, and writes/etc/mcp/servers.json({"mcpServers": ...}, remote ones withtype = "http") for agents outside Home Manager.
- Each one packages its server as a real derivation, puts it on the system PATH, registers it
in
mcp-serversunder the server name agents already use (basic-memory,chrome-devtools,context7,ddev,icedos,kwin-mcp,nixos,opencart,pagespeed-insights,rea,signal-mcp) and ships its skills.plesk-mcpinstead registers oneplesk-<name>entry perservers.<name>, and its wrapper sources the credentials fromPLESK_ENV_FILE, so they stay out of the store.opencart-mcpsourcesOPENCART_ENV_FILE(envFile) the same way, andpagespeed-insights-mcpreadsGOOGLE_API_KEYfromGOOGLE_API_KEY_FILE(apiKeyFile). - Skills that upstream ships come from the package source:
upstreamSkillslists the names,lib/upstream-skills.nixreadsSKILL.mdand appendsreferences/*.md. They read${package.src}(pkgs.context7-mcp.srcfor context7), an IFD that only needs the fetch, so skills always match the packaged version. basic-memory builds from its GitHub release for this, since its wheel leaves outskills/. The module's ownskillsreplaces an upstream skill of the same name. lib/python.nixis Python 3 with the MCP Python SDK 2.x (nixpkgs ships 1.x), used bykwin-mcpandbasic-memory-mcp;basic-memory-mcpadds the FastMCP release it pins on top.lib/python-bin.nixruns an entry point throughpython.withPackages, since kwin-mcp re-runssys.executable -m kwin_mcp.*and needs its packages in the child.rea-mcp,ddev-mcpandpagespeed-insights-mcpbuild the npm tarball withpackage-lock.jsongenerated from the trimmedpackage.jsontheirpostPatchwrites (rea drops frida, ddev drops its build tools). Eachupdate.shregenerates the lock with the same jq trim, so keep the two filters identical.- Pins not in nixpkgs live in JSON next to the package:
source.jsonper server andlib/python-sdk.jsonfor the SDK wheels. Each has anupdate.sh(lib/update-python-sdk.shfor the SDK) and anupdate-<module>.ymlworkflow overmodule-update.yml. kwin-mcp, opencart-mcp and signal-mcp track upstreammain, which runs ahead of their tags; basic-memory follows its latest release and the exact FastMCP version itspyproject.tomlpins.
agent-launch owns icedos.ai-tools.agent-launch.agents.<name> (command, label,
resumeArgs, locator) and the agent-launch fzf picker. Every agent module lists it in
meta.dependencies and registers itself there. Don't set zed.terminalInitCommand from an
agent module: zed's agent bridge extends agent-launch and reads this registry.
zed stays in apps, so hasModule calls for it pass url = "github:icedos/apps"
instead of repoUrl.
Point this repo's overrideUrl at the local checkout in the config root
(path:/home/ice/Projects/icedos/ai-tools), then icedos rebuild --build.
peon-ping— Warcraft-peon-style agent-event audio. Standalone module: ownsicedos.ai-tools.peon-ping.users.<name>(self-materialised viagenDefaults). Claude Code integration is upstream's ownprograms.peon-ping.claudeCodeIntegration(per-user opt-in,claudeCodeIntegration = true), andopencodeconsumes the user settings for its own peon plugin. See core's Per-user (users) options.prime-agent— mirrors the bundled models.dev catalog intomodels.json. Only models in that snapshot show up today (it lags upstream). To add a newly released model without waiting for a prime-agent bump, define it undericedos.ai-tools.prime-agent.settings.providers.<name>.models: prime-agent'smodels[]merges with the catalog — an unknown id is added (inheriting the built-in provider's api/baseUrl), a known id replaces the bundled definition. Use the model's real dot id (e.g.glm-5.3-flash, notglm-5-3-flash) and the same id inmodelOverrides.claude-code— ownsicedos.ai-tools.claude-code.users.<name>(mkSubmoduleAttrsOption, filled per normal user viagenDefaults) and enablesprograms.claude-codefor every hm user (enable = lib.mkDefault true,enableMcpIntegration = true). It feeds the sharedicedos.ai-tools.{rules,skills}intoprograms.claude-code.{rules,skills}with mkDefault per entry.settingsand per-user skills go through the raw passthrough ([home-manager.users.<name>.programs.claude-code.*]). MCP servers come from the sharedprograms.mcp.serversregistry.marketplaces— fetcher results are not TOML-expressible, so list them under[[icedos.ai-tools.claude-code.users.<name>.marketplaces]]with asourceenum (fetchFromGitHub/fetchFromGitLab/fetchgit/path) and that source's fields.pathis copied into the store at eval time, and pure eval forbids host paths there.- It declares no hooks. peon-ping registers its own via
icedos.ai-tools.peon-ping.users.<u>.claudeCodeIntegration.
ai-accounts— standalone module that ownsicedos.ai-tools.ai-accounts.users.<name>(self-materialised viagenDefaults).ai-account exec <agent> --picks an account (flag,AI_ACCOUNT_<AGENT>, sticky, alimit headroom,default) and execs the real binary with per-account env. Agent modules wrap their package throughlib/wrap.nixonly whenlib/has-accounts.nixfinds declared accounts;opencodealso counts as declaring whenollamadoes, because it passes its key in.- State lives in
~/.local/state/ai-accounts/<agent>/<account>/. Shared mode links the real dir exceptadapters.*_PRIVATE(never linked) andadapters.*_SEED(copied once, then owned by the account, because agents rewrite those files by renaming over the target). - A logged-out account fails with exit 3 when stdin is not a tty. On a tty the launch signs in
first: ollama prompts for the key, opencode runs
opencode auth login, the other agents start and show their own sign-in. An opencode launch also prompts for the key of the ollama account it tags, when that account has no key. - alimit keys for non-default accounts are
<provider>@<tag>/<window>. Status lines readAI_ACCOUNT_TAGS, andalimit headroomfeeds auto-pick. - Per-account
settingsmerge over the agent module'sicedos.managed-settings.<agent>into the account's own settings file on each launch, recorded in.ai-account-settings.jsonso removing an override restores the value it replaced. - Antigravity accounts run behind
xdg-dbus-proxy, which hidesorg.freedesktop.secretsso agy stores its token in the account's~/.gemini.antigravity.busTalklists the reachable names.
- State lives in
alimit— standalone module that ownsicedos.ai-tools.alimit.users.<name>(self-materialised viagenDefaults). One SQLite DB at~/.local/share/alimit/usage.dbholds usage windows keyed<provider>/<window>(Claude keeps bare keys). Status lines record samples while agents run, and a user timer polls the providers inproviders(claude, opencode-go, ollama, openrouter) for idle periods. Antigravity has no quota API, so only its status line records it.- Every bar shows only its own agent's usage: Claude Code's status line shows claude, antigravity's shows antigravity, prime-agent's footer and opencode's sidebar show the provider of the current model.
- Weekly and monthly windows (
config.period_of) project their end from the average burn since the window opened (reset minus 7 days, or one calendar month), blended with the previous period's for its first 2/7 (rates.paced). A backtest on a week of samples put it closest to the final weekly usage; the last hour's rate and a 5h-derived rate did worse. Antigravity opens its week at first use, so its reset only drifts while usage is 0%. 5h and daily windows keep the last hour's rate. - Every weekly window paired with a 5h one (
config.weekly_of; Claude, Antigravity families, opencode-gorolling) carriesexchange, the 5h percent per 1% weekly (rates.exchange): rises both report at the same instants, polls only when any exist, idle-session redraws skipped, and a missingresets_at(an idle 5h window) counts as a reset. Agent bars and opencode's sidebar print it as1%=X% 5h, the dashboard one line per pair. Ollama'ssessionis unpaired: its length is undocumented, and current Ollama plans only have a monthly allowance. alimit headroomranks accounts by each window's peak (rates.compute), not its last row.- API keys come from env, then prime-agent's
auth.json, then opencode's; alimit never refreshes Claude Code's OAuth token. icedos ai limitsopens the full dashboard.widgetinstalls the Plasma appletorg.icedos.alimit.
code-intelligence— ships onecode-intelligence-<lang>skill per folder listed inicedos.ai-tools.code-intelligence.languages, plus acode-intelligencerule pointing agents at them. Both land in the sharedskillsandruleswith mkDefault.