Gaia
GAIA — Generative AI Interface for Agents. Specialist agents for Claude Code and OpenCode, with a memory that outlives the session and a consent gate on everything that changes state.
4
732
161 in 7 days
43.3
Multi-signal model
9 hours ago
2026-10-20
Install and configure
opencode.jsonWrites to this project's opencode.json — applies to this repository only.
opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@jaguilar87/gaia@5.6.0"]
}Writes to ~/.config/opencode/opencode.json — applies to every project.
~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@jaguilar87/gaia@5.6.0"]
}If you want to modify the plugin locally, install it into the project and reference the local path.
shell
pnpm add -D @jaguilar87/gaiaOpenCode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.
Generative AI Interface for Agents. Specialist agents for Claude Code and OpenCode, with a memory that outlives the session and a consent gate on everything that changes state.
What it is and why it exists
Gaia is a plugin for two terminal AI hosts, Claude Code and OpenCode. You talk to one agent, and it holds the conversation; it never edits a file or runs a general command itself. For each piece of work it sends out a specialist that lives for one turn inside its own field -- application code, infrastructure as code, a cluster's desired state, a live system, planning, or Gaia itself -- and that specialist comes back with a contract: what it looked at, what it changed, how it checked the result, and how the turn ended. What a turn learns is kept in a database on your machine, so the next session starts from what the last one found instead of from zero. And every command any agent tries to run is classified before it runs: reads pass, changes wait for your yes, and a short list of irreversible commands never runs at all.
The four belong together. A conversation that forgets makes you re-explain the project every day; a specialist that reports in prose makes you trust a story instead of a record; an agent that can change your cluster without asking makes you supervise every keystroke. Gaia keeps the conversation in one place, the work in specialists, the record in contracts, and the risk behind a gate.
you
│ asks, in your words
▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ the one who holds the │◄──────│ memory that outlives the │
│ conversation │recalls│ session │
└──────────────┬───────────────┘ └──────────────▲───────────────┘
│ sends out one turn │ what stays
▼ │
┌──────────────────────────────┐ ┌──────────────┴───────────────┐
│ a specialist, born for this │──────►│ a contract: what it found, │
│ turn inside its own field │closes │ what it changed, how it ended│
└──────────────┬───────────────┘ on └──────────────────────────────┘
│ every command
▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ a gate: reads pass, changes │──────►│ your repository, cluster or │
│ wait for your yes │ once │ account │
└──────────────────────────────┘you say└──────────────────────────────┘
yes
The same boxes with their real names: the one who holds the conversation is the orchestrator, agents/gaia-orchestrator.md, the identity settings.json activates. The specialists are the other eight agent files in agents/. The contract is a row in ~/.gaia/gaia.db that the specialist writes through gaia contract while it works and closes with gaia contract finalize. Memory is the same database, read and curated through gaia memory. The gate is the tier classifier in hooks/modules/security/tiers.py: T0 reads, T1 validation and T2 dry-runs run freely; a T3 mutation stops with an approval_id you answer in the host's dialog (gaia approvals); a blocked command has no approval path at all.
What it can do for you
Ask the orchestrator "what is Gaia?" or "what can you do for me?" and it explains the picture above and offers this table. Each row names the skill or agent that answers it; every one exists under skills/ or agents/.
| You want to... | What answers |
|---|---|
| Change or investigate code, infrastructure, a cluster's desired state, or a live system | A specialist: developer, platform-architect, gitops-operator, cloud-troubleshooter |
| Understand something -- a system, a process, what happened, why it failed | technical-explanation |
| A README for a repository or a folder | readme-writing |
| A ticket or an issue | ticket-writing |
| A blog post | blog-writing |
| A diagram deck -- an architecture map, a timeline, a flow, a comparison | diagram-builder |
| Capture a feature before planning it | brief-spec |
| Plan it -- decompose it into verifiable tasks | gaia-planner (a skill and the agent of the same name) |
| A review of a module, a branch or a PR | code-review |
| Audit a Gaia component, live-check an area of Gaia, release it, verify the install | gaia-audit, gaia-check, gaia-release, gaia-verify, through the gaia-system agent |
| Look at repositories for something Gaia could take | gaia-research |
| Reflect on the session, or compact it | session-reflection |
| A reminder, or something offered routinely | reminders, recorded with gaia notifications add; nothing runs unattended, what is due waits for the next session |
| See or act on pending approvals | pending-approvals |
| Triage the mailbox, or connect a Google account | gmail-triage, gws-setup |
| Remember, find or curate what Gaia knows | memory |
| See what a session, an agent or a plan cost in tokens, or how a brief changed | gaia usage show --plan N or --session ID, and gaia brief history <slug>, which the orchestrator reads itself; the transcripts are loaded first with gaia usage ingest, run by gaia-operator |
Coming from 5.4? The behaviour changes you will meet -- declared workspaces, reminders in place of gaia schedule, append-only memory, signed package runners, the new approval questions and more -- are listed in CHANGELOG.md, Upgrading from 5.4.
Flow
One turn, from your prompt to the answer:
1. You write a prompt in Claude Code or OpenCode.
2. The orchestrator matches it against the surface_routing table (seeded from
each agent's routing: frontmatter) and dispatches one specialist.
3. The dispatch is validated and its contract row born; the specialist is
handed that contract, its CLI lane, and what Gaia already knows about it.
On Claude Code that is hooks/pre_tool_use.py and hooks/subagent_start.py;
on OpenCode, opencode/bridge.py prepends the same kernel to the Task
prompt, with the specialist's skills listed by name.
4. The specialist works. Every command passes the tier classifier:
T0-T2 run; T3 stops with an approval_id you answer in the host's dialog;
a blocked command is refused with nothing to approve.
5. The specialist fills its row as it goes (gaia contract set/add/fill) and
closes it (gaia contract finalize); the SubagentStop gate validates the
row and records the episode in ~/.gaia/gaia.db -- hooks/subagent_stop.py
on Claude Code, the child session's session.idle on OpenCode.
6. The orchestrator reads the row, not the message, and answers you.
Gaia interacts with three things outside itself: the host, which loads the hooks -- from hooks/hooks.json on the Claude Code plugin, from .claude/settings.local.json on the npm package, through opencode/plugin.ts on OpenCode, which forwards the same lifecycle (session start on the first user message, each message, tool calls, a child's start and stop, compaction, session end) to opencode/bridge.py -- only Claude Code's TaskCompleted hook, a logging passthrough, has no OpenCode counterpart; the ~/.gaia/ directory, where the database, evidence and logs live (gaia paths prints the resolved locations); and your repositories, which a specialist touches through its own git worktree (gaia worktree) and only mutates past the gate.
Requirements
- One host: Claude Code >= 2.1.295 (the floor declared in
.claude-plugin/plugin.json) or OpenCode. No OpenCode floor is declared or checked; the plugin was measured on OpenCode 1.18.32, which runs subagents in the background only withOPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=truein its environment (INSTALL.md, Surface 3). - Python >= 3.12 on
PATH(theenginesinpackage.json); the CLI and the hooks are Python. On the plugin the hooks start throughhooks/launch.sh, which needsshand takes the first ofpython3,pythonorpy -3that is really Python 3, so the python.org Windows install, which has nopython3, works too. - Node.js >= 18 and npm or pnpm, only for the package channels below.
- git, for the per-turn worktrees.
- Nothing is installed behind your back: there is no
postinstall, and the database is created lazily on the firstgaiacommand (_ensure_db_bootstrappedinbin/gaia).
How it is used
Gaia reaches your host through one of three channels. The folder you install in is where the host loads Gaia; it is not a workspace until you declare one (see Workspaces and projects).
| Channel | Host | What you install | Where gaia runs from |
|---|---|---|---|
| Plugin | Claude Code | gaia@gaia-marketplace, from this repository |
the plugin's own bin/gaia, run by the orchestrator |
| Package | Claude Code | @jaguilar87/gaia from npm, then gaia install --channel npm |
node_modules/.bin/gaia, or ~/.local/bin with --path |
| OpenCode | OpenCode | the same package, then gaia install --channel opencode |
as for the package |
gaia install and gaia dev take the channel with --channel; there is no default and no all. The package channel and the plugin exclude each other in one Claude Code install folder, since each registers Gaia's hooks: gaia install --channel npm refuses while the folder's settings enable a gaia@... plugin and names claude plugin uninstall <plugin> --scope <scope>, and gaia dev --channel plugin refuses while the package's hooks are registered in .claude/settings.local.json and names gaia uninstall --workspace <folder>, with --channel npm when OpenCode is recorded beside it (channel_conflict in bin/cli/install.py). OpenCode joins either and is removed on its own with gaia uninstall --channel opencode; npm and the plugin share .claude/, so uninstalling either takes back the Claude Code entries. gaia update re-wires the channels gaia install recorded in .claude/gaia-manifest.json. gaia doctor names the channel it finds.
Plugin. In Claude Code:
/plugin marketplace add metraton/gaia
/plugin install gaia@gaia-marketplace # terminal: claude plugin install gaia@gaia-marketplace
That is the whole install, and it does not put gaia on your terminal's PATH. The first session merges Gaia's permission set into .claude/settings.local.json, and asks for /reload-plugins (or a restart); when the folder lies outside every declared workspace, the session says so and names the command that declares one. Auto-update is off for third-party marketplaces; take a new release with claude plugin marketplace update gaia-marketplace, then claude plugin update gaia@gaia-marketplace and a restart.
Package and OpenCode. From the folder you install in:
npm install @jaguilar87/gaia # or: pnpm add @jaguilar87/gaia
npx gaia install --channel npm # or: pnpm exec gaia install --channel npm
# --channel opencode for OpenCode; --path
npx gaia doctor # one line per check, PASS or FAIL
gaia install migrates or creates ~/.gaia/gaia.db, links six directories (agents, tools, hooks, config, skills, opencode) plus CHANGELOG.md into .claude/, merges the permission set and the hook registrations into .claude/settings.local.json without removing what you had there, and records every file and key it wrote in .claude/gaia-manifest.json. It declares no workspace and scans nothing: it reports the declared workspace that holds the folder, or the command to declare one. --channel opencode writes opencode.json pointing at the packaged opencode/plugin.ts instead of touching .claude/, so it can sit beside either Claude Code channel; --path also writes the gaia launcher to ~/.local/bin. To take a new release: npm install @jaguilar87/gaia@latest, then npx gaia update, which re-wires the channels recorded in .claude/gaia-manifest.json. The step-by-step walk-through is in INSTALL.md.
Then declare a workspace. Installing is one step; declaring a workspace is another, and Gaia does not do it for you:
npx gaia workspace declare <name> <path> # package and OpenCode
npx gaia scan --workspace <name> <path>
On the plugin, gaia is on the Bash tool's PATH inside a Claude Code session in the install folder, so the same two commands run there without npx.
Workspaces and projects
Gaia is installed once. That one install, and its one database at ~/.gaia/gaia.db, serves every workspace you declare.
A workspace exists only when you declare it: a name and the folder it covers. Installing Gaia, opening a session or scanning never creates one. Declare it, then let Gaia find its projects:
gaia workspace declare <name> <path> # e.g. gaia workspace declare personal ~/code/personal
gaia scan --workspace <name> <path> # finds the projects inside it
A name or a folder already declared is never given to another. Outside every declared workspace Gaia does not guess one; the session start, gaia install, gaia workspace current and gaia worktree create say:
/home/you/somewhere is not inside a declared workspace.
Declare one with: gaia workspace declare <name> <path>
Inside a workspace, four rules decide what Gaia sees:
~/code/ workspace "code"
├── tools/ project of "code", no group
├── clients/acme/api/ project of "code", group "clients/acme"
├── notes/ not a project: no git
└── personal/ workspace "personal", declared inside "code"
└── blog/ project of "personal", not of "code"
- A project is a git repository: any folder holding
.git. A folder without git is not a project. - A group is the folder path between the workspace and the repository, at any depth:
clients/acmeabove. A repository sitting directly in the workspace has no group. The scan enters folders whose names start with a dot too, except tool folders such as.git,.claudeandnode_modules. - A repository belongs to the nearest declared workspace that contains it. Workspaces can sit inside each other; the inner one owns its repositories, and a scan of the outer one leaves them alone.
- A project is known by its
originremote before its folder. Moving the folder keeps the project: the next scan finds it in its new place. A repository with no remote is known by its folder, so moving it makes a new project.
A second clone of the same remote is not a second project. The scan reports it as a copy of the project already recorded, and both folders share that one project and its memory.
To move a project into another declared workspace:
gaia project move <project> --into <workspace> --dry-run # lists what moves and what stays
gaia project move <project> --into <workspace>
The project goes in one step, with its briefs (those created for it with gaia brief new --project; an existing brief can be tagged with its project, gaia brief set-project <brief> <project> [--dry-run]) and its profile: what Gaia knows about it, its declared workflow included. Its memory belongs to the project and is read from any workspace, so it moves with it without being copied. Briefs written for the workspace as a whole stay where they are. A workspace that is not declared is refused as the target, and a name found in two workspaces asks for --from <workspace>.
Database migrations. A new release may move ~/.gaia/gaia.db to a newer schema. gaia install and gaia update do it on their own, and so does the plugin at SessionStart when the database is behind. On its own means without asking: a backup goes to backups/ beside the database, and the whole chain runs in one transaction. What decides whether it can go on alone is what the chain reaches:
chain only adds structure -> applied on its own
chain reaches rows that exist -> stops, and the message (or the plugin's
startup notice) names the command to run:
gaia migrate plan # the chain and what it reaches
gaia migrate apply --consent-chain vA..vB # consent once, for that chain
Uninstall. Each channel takes back only what it wrote. ~/.gaia/gaia.db is never touched: delete ~/.gaia/ yourself if you want the memory gone too.
Plugin <installPath>/bin/gaia uninstall # installPath: claude plugin list --json
claude plugin uninstall gaia@gaia-marketplace
claude plugin marketplace remove gaia-marketplace # optional
Package npx gaia uninstall # --dry-run first shows what reverts
npm uninstall @jaguilar87/gaia # or: pnpm remove @jaguilar87/gaia
OpenCode npx gaia uninstall --channel opencode --workspace <folder>, then the npm step above
gaia uninstall with no channel takes back every channel recorded in the install folder. Where two run side by side -- the plugin and OpenCode -- gaia uninstall --channel npm|plugin|opencode takes back only that one: OpenCode owns opencode.json and .opencode/, the Claude Code channel the rest of the manifest, the package copy stays while a remaining channel runs from it, and the other channel stays recorded. A channel the install folder does not record fails, naming the recorded ones. Run gaia uninstall from the install folder before removing the package or the plugin, while gaia still exists. It reverts .claude/gaia-manifest.json -- every file, link and settings key back to its prior state, opencode.json and the --path launcher included -- and writes a gzip snapshot of the database to ~/.gaia/snapshots/ unless --no-backup. An OpenCode-only folder has no .claude/ to detect, hence --workspace. It also removes what the package manager and gaia dev left -- the Gaia package under node_modules and its .bin shim, Gaia's line in package.json and package-lock.json (your other dependencies stay), the tarballs gaia dev cached -- and lists as left in place, with the reason, what it cannot fix itself (a pnpm-lock.yaml or yarn.lock, .claude/logs); --dry-run lists exactly what the real run does. The plugin's sessions record what they write into the install folder in the same manifest -- the permissions and attribution merged into .claude/settings.local.json, the .claude/hooks link -- so gaia uninstall reverts the plugin's writes too, including the user entries the merge replaced, and keeps what you added since; a plugin install folder from before that record is recognized by Gaia's permissions and attribution.
On the plugin, gaia is not on your terminal's PATH and is gone once the plugin is removed, so run the plugin's own copy first. claude plugin list --json prints an installPath for each gaia@gaia-marketplace install; take the one installed for this folder (a local-scope install names it in projectPath) and run <installPath>/bin/gaia uninstall in a terminal in the install folder -- it needs only Python on PATH. Inside a Claude Code session in the install folder the same bin/gaia is on the Bash tool's PATH, so Gaia can run gaia uninstall there for you. Then remove the plugin.
First turn. Start the host in the install folder and ask:
claude # or: opencode
> what is Gaia, and what can you do for me?
The orchestrator answers with the picture above and the table of what it can offer. gaia scan --workspace <name> re-indexes a declared workspace's repositories when they change, and gaia status shows what is wired.
Structure
gaia/
├── agents/ # orchestrator + eight specialists; routing: seeds the table
├── skills/ # 40 techniques loaded by description match
├── hooks/ # host lifecycle entry points + security/context modules
├── gaia/ # host-neutral core: approvals, SQLite store, worktrees
├── opencode/ # the OpenCode plugin; registered by --channel opencode
├── config/ # context contracts, git standards and rules the hooks read
├── build/ # gaia.manifest.json -> plugin.json + hooks.json at pack
├── bin/ # the gaia CLI and its subcommands (bin/cli/)
├── tools/ # scanners, context providers and validators for CLI/hooks
├── scripts/ # build, migration and release scripts
├── tests/ # pytest suite plus prompt-regression and eval layers
├── INSTALL.md # full install, manual equivalent, troubleshooting
├── ARCHITECTURE.md # the component map at depth
├── CONTRIBUTING.md # how to propose a change
└── SECURITY.md # how to report a vulnerability
Each folder with a README explains what it is wired to and what breaks if you change it: agents/, skills/, hooks/, gaia/, config/, build/, bin/, tests/. Version history is in CHANGELOG.md.
License and ownership
MIT, see LICENSE. Created and maintained by Jorge Aguilar. Bugs and feature requests: GitHub Issues. Questions or contact: a GitHub direct message to @metraton or LinkedIn, linked from that GitHub profile.
Similar plugins
Workflows
@rphang/opencode-workflows
Claude Code-style dynamic workflows for opencode v2: the model writes a JS orchestration script that fans work out to parallel subagents (agent, parallel, pipeline, budget, resume)
Claude Smart
claude-smart
Self-improving Claude Code, Codex, and OpenCode plugin that turns corrections into Preferences, Project-specific skills, and Shared skills
Hiai Opencode
@hiai-gg/hiai-opencode
HiAi OpenCode Plugin — multi-agent orchestration for OpenCode with 10 agents, LSP tools, browser automation, Memory, Session management, and Caveman protocol