One config. Every coding agent.
Project-level configuration for AI coding agents, materialized into whatever each tool expects to find on disk.
agnos is a project-level configuration manager for AI coding agents. You declare your docs, rules, skills, MCP servers, and hooks once in a single agnos.json at the root of your repo. agnos materializes that declaration into whatever each agent expects to find on disk: CLAUDE.md + .mcp.json + .claude/settings.json for Claude Code, AGENTS.md + .codex/config.toml for OpenAI Codex, GEMINI.md + .gemini/settings.json for Gemini CLI, and so on.
agnos ships as a single package (@luxia/agnos) with a fixed, built-in set of agents (Claude Code, Codex, Gemini CLI) and domains (docs, rules, skills, mcp, hooks, agents). Point it at your project, run it once, or leave it in watch mode: every agent's files stay in sync with your one source of truth.
Every coding agent invents its own on-disk configuration format. Keeping them consistent by hand is tedious and error-prone:
- Drift:
CLAUDE.mdandAGENTS.mdsay different things because you updated one and forgot the other. - Duplication: the same MCP server, the same hook, the same rule copied into three tool-specific files with three different syntaxes.
- Onboarding cost: adding a new agent to a repo means learning yet another file layout and hand-translating everything you already wrote.
- No single source of truth: nothing in the repo tells you what the intended configuration is; it's scattered across whatever files each tool happens to read.
agnos collapses all of that into one declarative agnos.json. You edit intent; agnos renders the per-agent reality. Adding an agent becomes "render its files"; removing one becomes "delete its files."
- What is agnos?
- What does it solve?
- Table of contents
- Features
- The model: config writers + one config reader
- Installation
- Usage
- Configuration (
agnos.json) - Commands
- Active development
- Contributing
- License
- 🎯 One source of truth: declare docs, rules, skills, MCP servers, and hooks once in
agnos.json. - 🔌 Multi-agent output: renders native files for Claude Code, OpenAI Codex, and Gemini CLI from the same config.
- 👀 Watch mode: a per-domain watcher tree keeps agent files in sync as your sources change; edit a rule fragment and the canonical files re-render.
- 🧩 Composable rules: inject titled sections (by frontmatter
title) from fragment files into your canonical rules file, preserving your hand-written sections. - 📚 Docs index: compile a metadata index from your docs directory and surface it to agents.
- 📦 Skills management: fetch, pin, verify, and update agent skills from GitHub/GitLab/Bitbucket or local paths, linked per-agent.
- 🛰️ MCP registry integration: search and add MCP servers from the registry, or configure them manually; agnos renders them per-agent.
- 🪝 Portable hooks: declare command hooks once against a normalized event vocabulary; each agent gets them in its native format.
- ♻️ Migrate what you already have: import existing MCP servers, hooks, and skills from your agents' current config into
agnos.json. - 🧪 Dry runs:
--dryshows exactly what would change without writing anything.
agnos splits its work in two:
- Config-writer domains manage their own slice of the project.
skills/mcp/hookswrite entries intoagnos.json;docscompiles a documentation index;rulesinjects titled sections into your canonical rules file(s). - One config-reader domain,
agents: readsagnos.jsonand the canonical outputs and renders every per-agent native file via per-agent adapters. Installing an agent is just "render its files"; removing it is "delete its files."
┌──────────── config WRITERS ────────────┐ ┌── reader ──┐
docs ──▶ .docs/index.md │ │
rules ─▶ ./AGENTS.md (titled sections injected) ───────▶│ agents │──▶ CLAUDE.md, .mcp.json,
skills ▶ agnos.json#skills + .agnos/skills/ │ (adapters) │ .claude/settings.json,
mcp ───▶ agnos.json#mcp │ │ .claude/skills, .codex/*
hooks ─▶ agnos.json#hooks │ │
└─────────────────────────────────────────┘ └────────────┘
Domains run in priority order (skills → docs → rules → mcp → hooks → agents), so every writer has produced its canonical output before the reader renders.
Requires Node.js 24 or newer. agnos is ESM-only. This project uses pnpm exclusively.
# global (recommended for the CLI)
pnpm add -g @luxia/agnos
# or per-project
pnpm add -D @luxia/agnoscd my-project
agnos --init # bootstrap agnos.json + pick agents (add -y for defaults)
agnos --once # run the pipeline once: prepare skills → docs → rules → render agents
agnos # watch mode: keep agent files in sync as sources changeNOTE: agnos always tries a symlink first when the
agentsdomain links skills or mirrors rules. On Windows, creating a file symlink can require an elevated terminal or Developer Mode; when that's not available it falls back to a hardlink, then a plain copy. Directories use junctions on Windows instead, which don't need elevation. macOS/Linux create symlinks without any special privileges.
Typical workflow:
agnos --initcreatesagnos.jsonand walks you through picking agents, a docs root, a rules file, and a skills directory. Add-yto accept every default non-interactively.- Edit
agnos.json(or use the domain subcommands likeagnos mcp add githubandagnos skills add owner/repo) to declare what you want. agnosin one terminal watches all domains and re-renders on change. Runagnos --oncein CI or a pre-commit hook for a single deterministic pass, oragnos --dryto preview.
You can also run a single domain: agnos rules --once, agnos docs, etc.
agnos --init walks you through building agnos.json interactively; running it seeds agents, docs.root, rules.files, and skills.route. mcp and hooks entries are added afterward via their subcommands (or by hand). Here's an example showing every domain populated at once:
{
"$schema": "https://unpkg.com/@luxia/agnos/schema.json",
"schemaVersion": 1,
"agents": ["claude-code", "codex", "gemini-cli"],
"docs": { "root": ".docs" },
"rules": {
"files": {
"./AGENTS.md": ["./.docs/index.md", "./.rules/security.md", "./.rules/style.md"]
}
},
"skills": {
"sources": {
"pdf": "github:vercel-labs/agent-skills/skills/pdf"
}
},
"mcp": [
{
"name": "github",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
}
],
"hooks": [
{
"event": "PreToolUse",
"matcher": "git",
"type": "command",
"command": "echo 'about to run git'",
"message": "checking git usage"
}
]
}agnos.json is parsed as strict JSON (no comments, no trailing commas), so the block above is copy-pasteable as-is. ${VAR} placeholders inside mcp.env/mcp.headers are resolved at render time.
Field reference (see schema.json for the authoritative definition):
| Field | Type | Description |
|---|---|---|
schemaVersion | 1 | Required. Config schema version; must be 1. |
agents | string[] | Active agent ids: "claude-code", "codex", "gemini-cli". |
docs.root | string | Directory the docs index is compiled from (default .docs). |
rules.files | { [canonical]: string[] } | Maps each canonical rules file → fragment files whose titled sections are injected into it. |
skills.route | string | Canonical skills directory (default .agnos/skills); agents link their own skills dir here. |
skills.sources | { [name]: string } | Local skill name → composite source ref (github:/gitlab:/bitbucket:/file:). |
mcp | object[] | MCP server declarations (name, transport, command, args, env, headers, …). |
hooks | object[] | Flat array of command hooks (event, matcher?, type, command, message?). |
agnos [domain] [subcommand] [args] [flags]
agnos: watch all domains and keep agent files in sync.agnos --once: run the full pipeline once and exit.agnos <domain>: run a single domain (docs|rules|skills|mcp|hooks|agents).agnos <domain> <subcommand>: mutate config or run a domain action.agnos <domain> --help: show help for a domain and its subcommands.
Available on every command:
| Flag | Description |
|---|---|
--dry | Resolve and log planned actions; write nothing (implies --once). |
--once | Single pass, no watchers. |
--quiet | Errors only. |
--init | Run initialization (bootstrap), then exit. |
-y, --yes | Accept defaults (non-interactive). |
--cwd <dir> | Run as if invoked from <dir>. |
--debug | Print debug output and full stack traces on error. |
-h, --help | Show help. |
Compiles a documentation index from docs.root. Surface it to agents by listing the generated index in rules.files. No subcommands: configure it via agnos.json or agnos docs --init.
| Command | Args | Description |
|---|---|---|
agnos docs | none | Compile the docs index (watch mode unless --once/--dry). |
Injects titled sections (by frontmatter title) from fragment files into your canonical rules file(s). Hand-written sections are preserved; removed fragments are pruned. No subcommands: configure via agnos.json or agnos rules --init.
| Command | Args | Description |
|---|---|---|
agnos rules | none | Inject rules (watch mode unless --once/--dry). |
Fetches, pins, verifies, and installs skills into the canonical skills dir (linked per-agent by agents). Install work is concurrent, with live percentage, total, reused, and fetched counters. Each unique locked skill tree is stored once in a user-level content store shared across repositories. Projects receive independent materializations, so removing the store does not break installed skills. Git checkouts are isolated under .agnos/tmp/repos/ for the current run and always removed afterward. Set AGNOS_STORE_DIR to override the operating system data location used by the store.
| Subcommand | Args / Flags | Description |
|---|---|---|
add | <skills_address...> · -p, --provider <p> · -s, --skills <names> | Add skills from one or more sources (owner/repo[#ref] or paths); discovers skills and prompts to pick. --provider sets the default provider (github|gitlab|bitbucket|file) for un-prefixed addresses; --skills pdf,docx skips the prompt. |
remove | [names...] | Remove skill sources (multiselect prompt when no name is given). |
fetch | none | Check that every skill source still resolves (reports moved). |
version | none | Check whether skills are on their pinned commit (reports outdated). |
integrity | none | Verify skill content matches the lock (reports changed). |
install | none | Run the prep pipeline (fetch → version → integrity → install). |
update | [names...] | Re-pin + reinstall skills, accepting upstream changes (default: all). |
migrate | [file] · --missing | --force | --skip | Import skill sources from a lock file (name → ref JSON; default skills-lock.json). |
Manages MCP servers in agnos.json (rendered per-agent by the agents domain).
| Subcommand | Args / Flags | Description |
|---|---|---|
add | [term] | Add MCP servers from the registry (search term), or configure one manually (omit the term for an interactive prompt). |
update | [names...] | Update registry-managed MCP servers to their latest version (all if none given). |
remove | [names...] | Remove MCP servers (multiselect prompt when no name is given). |
migrate | --missing | --force | --skip | Import MCP servers from the active agents' native config. |
Manages hooks in agnos.json (a flat array; the agents domain regroups + renders per-agent).
| Subcommand | Args / Flags | Description |
|---|---|---|
add | <event> <command> [matcher] | Add a command hook. event is one of the normalized events below. |
remove | [event] [command] [matcher] | Remove command hooks (multiselect prompt when no args are given). |
migrate | --missing | --force | --skip | Import hooks from the active agents' native config. |
Normalized hook events: PreToolUse, PostToolUse, UserPromptSubmit, Notification, Stop, SubagentStop, PreCompact, SessionStart, SessionEnd. Each agent renders only the events it supports and skips the rest.
The sole config reader: renders every active agent's native files. add/remove toggle agnos.json#agents (remove also cleans up the agent's rendered files).
| Subcommand | Args | Description |
|---|---|---|
add | [agents...] | Enable agents (their files render on the next agnos run). Omit ids to pick interactively. |
remove | [agents...] | Remove agents' rendered files, then disable them. |
--missing/--force/--skip(themigrateconflict policy):--missing(default) adds only entries not already present;--forceoverwrites conflicting entries and adds missing ones;--skipaborts if any entry conflicts.
Node 24+, ESM only, TypeScript throughout. Contributions are welcome: issues and PRs at github.com/rgdevme/luxia.
pnpm install
pnpm build # bundle with tsup + copy templates
pnpm typecheck # tsc --noEmit
pnpm test# vitest
pnpm lint # eslint
pnpm format # prettier --writeCI runs format-check, lint, typecheck, test, and build on every pull request; run the same locally before pushing:
pnpm format:check && pnpm lint && pnpm typecheck && pnpm test&& pnpm buildCommits are linted via Husky + lint-staged. Please keep changes focused and add tests for new behavior.
Released under the MIT license.