# Auth Source: https://docs.enagrams.com/api-reference/auth API key validation and user profile ## Validate API Key Check if an API key is valid and get the associated user. ```bash theme={null} POST /users/validate-key Authorization: Bearer ek_your_key_here ``` **Response** ```json theme={null} { "valid": true, "user_id": "usr_abc123", "email": "developer@example.com" } ``` Invalid key: ```json theme={null} { "valid": false, "error": "Key not found or revoked" } ``` *** ## Get User Profile ```bash theme={null} GET /users/profile Authorization: Bearer ek_your_key_here ``` **Response** ```json theme={null} { "id": "usr_abc123", "email": "developer@example.com", "name": "Developer A", "created_at": "2026-01-01T00:00:00Z", "workspaces": [ { "id": "ws_xyz", "slug": "my-startup", "name": "My Startup", "role": "owner" } ] } ``` # Decisions Source: https://docs.enagrams.com/api-reference/decisions Record and search architectural decisions ## Record a Decision ```bash theme={null} POST /decisions Authorization: Bearer ek_... { "workspace_id": "ws_abc123", "title": "Use JWT for authentication", "rationale": "Stateless, works with edge deployment", "type": "architecture", "files": ["src/auth/login.ts", "src/auth/middleware.ts"] } ``` **Response** ```json theme={null} { "id": "dec_abc123", "title": "Use JWT for authentication", "rationale": "Stateless, works with edge deployment", "type": "architecture", "made_by": "usr_xyz", "workspace_id": "ws_abc123", "files": ["src/auth/login.ts"], "created_at": "2026-04-13T10:00:00Z" } ``` Decisions are also typically recorded via the [`decide`](/mcp/decide) MCP tool. *** ## Search Decisions Semantic vector search over all decisions in the workspace. ```bash theme={null} POST /decisions/search Authorization: Bearer ek_... { "workspace_id": "ws_abc123", "query": "authentication and session management", "limit": 5 } ``` **Response** ```json theme={null} { "results": [ { "id": "dec_abc", "title": "Use JWT for authentication", "rationale": "Stateless, works with edge deployment", "type": "architecture", "similarity": 0.92, "made_by": "Developer A", "created_at": "2026-04-13T10:00:00Z" } ] } ``` Search is also available via the [`search`](/mcp/search) MCP tool. *** ## List Decisions ```bash theme={null} GET /workspaces/:id/decisions Authorization: Bearer ek_... ``` Query params: * `type` — filter by decision type * `made_by` — filter by user ID * `limit` — max results (default: 50) * `before` — cursor for pagination *** ## Decision Types | Type | Description | | ---------------- | ------------------------------- | | `architecture` | High-level system design | | `approach` | Feature implementation approach | | `implementation` | Low-level implementation detail | | `rejection` | Explicitly rejected alternative | | `tradeoff` | Documented tradeoff | | `convention` | Adopted team pattern | | `bugfix` | Root cause and fix | # Meetings Source: https://docs.enagrams.com/api-reference/meetings Meeting transcript ingestion and live recording ## Ingest a Transcript Extract decisions, action items, and work packages from a meeting transcript. ```bash theme={null} POST /meetings/ingest Authorization: Bearer ek_... { "workspace_id": "ws_abc123", "title": "Sprint Planning — April 13", "transcript": "Developer A: Let's use Zod for all input validation. Developer B: Agreed. I'll take the auth routes. Developer A: I'll handle the user endpoints...", "participants": ["Developer A", "Developer B"] } ``` **Response** ```json theme={null} { "id": "mtg_abc", "title": "Sprint Planning — April 13", "decisions": [ { "id": "dec_xyz", "title": "Use Zod for input validation", "rationale": "Type-safe, TypeScript-native", "type": "architecture" } ], "action_items": [ { "action": "Build auth route validation", "assignee": "Developer B", "priority": "high" } ], "work_packages": [ { "id": "pkg_abc", "title": "Implement Zod validation for auth routes", "description": "Add Zod schemas for login, register, password reset", "files": ["src/auth/validators.ts"], "assignee": "Developer B", "status": "open" } ] } ``` *** ## Start Live Recording Creates a meeting in `recording` state. After this, open a WebSocket to `/ws/transcribe` to stream audio. ```bash theme={null} POST /meetings/start Authorization: Bearer ek_... { "workspace_id": "ws_abc123", "title": "Design Review" } ``` **Response** ```json theme={null} { "id": "mtg_xyz", "status": "recording", "ws_url": "wss://api.enagrams.com/ws/transcribe?meeting_id=mtg_xyz&workspace_id=ws_abc123" } ``` *** ## Live Transcription WebSocket Stream audio chunks from MediaRecorder to Whisper. ``` WS /ws/transcribe?meeting_id=mtg_xyz&workspace_id=ws_abc123 Authorization: Bearer ek_... (via query param or protocol header) ``` **Send:** Binary audio chunks (ArrayBuffer from MediaRecorder) **Receive:** Transcription chunks as JSON: ```json theme={null} { "type": "transcript_chunk", "text": "Developer A: Let's use JWT for authentication." } ``` **Stop the recording:** ```json theme={null} { "type": "stop" } ``` On stop, the server finalizes transcription, extracts decisions, generates work packages, and returns: ```json theme={null} { "type": "complete", "meeting_id": "mtg_xyz", "decisions": [...], "work_packages": [...] } ``` *** ## List Meetings ```bash theme={null} GET /meetings/recent Authorization: Bearer ek_... ``` Query params: * `workspace_id` — required * `limit` — default 20 **Response** ```json theme={null} [ { "id": "mtg_abc", "title": "Sprint Planning", "status": "completed", "participants": ["Developer A", "Developer B"], "decisions_count": 3, "work_packages_count": 4, "created_at": "2026-04-13T09:00:00Z" } ] ``` *** ## Meeting Status | Status | Description | | ------------ | ------------------------------------- | | `recording` | Live audio streaming in progress | | `processing` | LLM extraction running | | `completed` | Decisions and work packages available | # API Overview Source: https://docs.enagrams.com/api-reference/overview The Enagrams REST API ## Base URL ``` https://api.enagrams.com ``` ## Authentication All API requests require an API key passed as a Bearer token: ```bash theme={null} Authorization: Bearer ek_your_key_here ``` Get your API key from **Dashboard → API Keys**. ## Endpoints | Category | Endpoints | | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | [Auth](/api-reference/auth) | Key validation, user profile | | [Workspaces](/api-reference/workspaces) | CRUD, members, sessions, reservations, feed | | [Decisions](/api-reference/decisions) | Record, search, staleness, reaffirmation | | [Sessions](/api-reference/sessions) | Agent session management and hooks | | [Meetings](/api-reference/meetings) | Transcript ingestion and live recording | | Workstreams | `GET/POST/PATCH/DELETE /workspaces/:id/workstreams` — branch-scoped units of work. See [Workstreams](/concepts/workstreams). | | Tasks | `POST/GET/PATCH /workspaces/:id/tasks` — delegated follow-up inside a workstream. See [Tasks](/concepts/tasks). | | Conventions | `POST/GET /workspaces/:id/conventions` — team rules. See [Conventions](/concepts/conventions). | | Negotiations | `POST/GET /workspaces/:id/negotiations` — contested-symbol turn machine. See [Negotiation](/concepts/negotiation). | | Test gate | `POST /workspaces/:id/sync/confirm`, `/sync/commit`, `/sync/retract`. See [Test gate](/concepts/test-gate). | | Symbols | `GET /workspaces/:id/symbols` — query the [symbol graph](/concepts/symbol-graph). | Most coordination actions can be driven through either HTTP (for dashboards and CI) or the [MCP tools](/mcp/overview) (for agents). ## Response Format All responses use JSON. Errors include an `error` field: ```json theme={null} { "error": "Unauthorized", "message": "Invalid API key" } ``` ## Rate Limiting | Tier | Limit | | ---- | --------------------- | | Free | 1,000 requests / day | | Pro | 50,000 requests / day | | Team | Unlimited | Rate limit headers: ``` X-RateLimit-Limit: 50000 X-RateLimit-Remaining: 49823 X-RateLimit-Reset: 1713000000 ``` ## Health Check ```bash theme={null} GET /health ``` ```json theme={null} { "status": "ok", "version": "3.1.0" } ``` # Sessions Source: https://docs.enagrams.com/api-reference/sessions Agent session management and hook endpoint ## Overview Sessions track active coding agent conversations in a workspace. They're created and managed automatically by IDE hooks — you don't typically call these endpoints directly. ## Hook Endpoint The primary endpoint. Handles all IDE hook events. ```bash theme={null} POST /workspaces/sessions/hook Authorization: Bearer ek_... { "event": "sessionStart", "agent_type": "cursor", "workspace_slug": "my-startup", "conversation_id": "conv_abc123", "task": "Build user authentication" } ``` ### Event Types | Event | IDE | Effect | | ----------------------------------------- | --------------------------- | ----------------------------------------- | | `sessionStart` / `SessionStart` | Cursor / Claude Code, Codex | Creates session, returns context briefing | | `preToolUse` / `PreToolUse` | Cursor / Claude Code | Checks file reservation, may deny write | | `postToolUse` / `PostToolUse` | Cursor / Claude Code | Reserves file for this conversation | | `beforeSubmitPrompt` / `UserPromptSubmit` | Cursor / Claude Code, Codex | Captures task text | | `sessionEnd` / `SessionEnd` / `Stop` | Cursor / Claude Code, Codex | Releases reservations, ends session | | `file_check` | Any | Updates heartbeat | ### sessionStart Response ```json theme={null} { "session_id": "sess_xyz", "additional_context": "Team briefing:\n\nActive agents:\n - Developer B (Cursor): Build JWT auth flow\n\nLocked files:\n - src/auth/login.ts (Developer B)\n\nRecent decisions:\n - Use JWT for auth (Developer B, 5 min ago)" } ``` The `additional_context` field is injected as system context by the IDE. ### preToolUse Response (deny) ```json theme={null} { "permission": "deny", "message": "File src/auth/login.ts is reserved by Developer B — 'Build JWT auth flow' (6 min remaining)" } ``` ### preToolUse Response (allow) ```json theme={null} { "permission": "allow" } ``` *** ## List Active Sessions ```bash theme={null} GET /workspaces/:id/sessions Authorization: Bearer ek_... ``` **Response** ```json theme={null} [ { "id": "sess_abc", "user_name": "Developer A", "agent_type": "cursor", "current_task": "Build JWT auth flow", "current_file": "src/auth/login.ts", "branch": "feat/auth", "conversation_id": "conv_xyz", "last_heartbeat": "2026-04-13T10:30:00Z", "reserved_files": ["src/auth/login.ts", "src/auth/middleware.ts"] } ] ``` *** ## Heartbeat Keep a session alive. ```bash theme={null} POST /workspaces/sessions/heartbeat Authorization: Bearer ek_... { "workspace_slug": "my-startup", "session_id": "sess_abc" } ``` *** ## End Session ```bash theme={null} POST /workspaces/sessions/end Authorization: Bearer ek_... { "workspace_slug": "my-startup", "session_id": "sess_abc" } ``` Releases all file reservations for the session. *** ## Session Expiry Sessions auto-expire after **5 minutes** of inactivity (no hook events). File reservations auto-expire after **10 minutes** of inactivity. # Workspaces Source: https://docs.enagrams.com/api-reference/workspaces Workspace management, members, file reservations, and real-time feed ## Create Workspace ```bash theme={null} POST /workspaces Authorization: Bearer ek_... { "name": "My Startup", "slug": "my-startup" } ``` **Response** ```json theme={null} { "id": "ws_abc123", "name": "My Startup", "slug": "my-startup", "created_at": "2026-04-13T00:00:00Z" } ``` *** ## List Workspaces ```bash theme={null} GET /workspaces Authorization: Bearer ek_... ``` Returns all workspaces the authenticated user is a member of. *** ## Get Workspace ```bash theme={null} GET /workspaces/:id Authorization: Bearer ek_... ``` *** ## Update Workspace ```bash theme={null} PATCH /workspaces/:id Authorization: Bearer ek_... { "name": "New Name" } ``` *** ## Members ### List Members ```bash theme={null} GET /workspaces/:id/members Authorization: Bearer ek_... ``` ### Add Member ```bash theme={null} POST /workspaces/:id/members Authorization: Bearer ek_... { "user_id": "usr_...", "role": "member" } ``` Roles: `owner`, `admin`, `member` ### Remove Member ```bash theme={null} DELETE /workspaces/:id/members/:userId Authorization: Bearer ek_... ``` ### Member Detail Full drill-down for a specific member: sessions, decisions, traces, work packages, file reservations. ```bash theme={null} GET /workspaces/:id/members/:userId/detail Authorization: Bearer ek_... ``` *** ## File Reservations ### List Reservations ```bash theme={null} GET /workspaces/:id/reservations Authorization: Bearer ek_... ``` **Response** ```json theme={null} [ { "file_path": "src/auth/login.ts", "user_id": "usr_abc", "user_name": "Developer A", "conversation_id": "conv_xyz", "current_task": "Build JWT auth flow", "expires_at": "2026-04-13T10:35:00Z" } ] ``` *** ## Work Packages ### List Packages ```bash theme={null} GET /workspaces/:id/packages Authorization: Bearer ek_... ``` Query params: * `status` — filter by `open`, `claimed`, `in_progress`, `completed` * `assignee` — filter by user ID ### Claim Package ```bash theme={null} POST /workspaces/:id/packages/:pid/claim Authorization: Bearer ek_... ``` ### Update Package Status ```bash theme={null} PATCH /workspaces/:id/packages/:pid/status Authorization: Bearer ek_... { "status": "in_progress" } ``` *** ## Knowledge Graph Returns nodes and edges for the force-directed graph visualization. ```bash theme={null} GET /workspaces/:id/graph Authorization: Bearer ek_... ``` **Response** ```json theme={null} { "nodes": [ { "id": "dec_abc", "type": "decision", "label": "Use JWT for auth" }, { "id": "file_src/auth/login.ts", "type": "file", "label": "src/auth/login.ts" } ], "edges": [ { "source": "dec_abc", "target": "file_src/auth/login.ts" } ] } ``` *** ## Real-time Feed (SSE) Server-Sent Events stream of agent activity. ```bash theme={null} GET /workspaces/:id/feed Authorization: Bearer ek_... ``` Event types: `agent_sync`, `decision_recorded`, `file_reserved`, `conflict_detected` ``` data: {"type":"decision_recorded","decision":{"title":"Use JWT for auth"},"user":"Developer A"} data: {"type":"file_reserved","file":"src/auth/login.ts","by":"Developer A"} ``` # Branch Coordination Source: https://docs.enagrams.com/concepts/branch-coordination How Enagrams keeps teams on the same branches without nagging ## From Free-form Branches to Workstreams Early Enagrams tracked a single "active branch" per workspace and nudged agents to pull when someone else committed. That was a stopgap. The current model is [workstreams](/concepts/workstreams): a named unit of work mapped to a shared Git branch (`ena/`) that multiple agents and humans can join. Everything that used to be called "branch coordination" now runs through workstreams: | Old concept | Workstream equivalent | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `sync({ branch })` sets active branch | [`workstream_start`](/mcp/workstream-start) creates the workstream and branch, or [`workstream_join`](/mcp/workstream-join) joins an existing one | | `branch_directive` in `sync` | `sync` returns the caller's `active_workstream` with its branch, members, and reservations | | `pull_required` flag after `decide` | Auto-sync (`enagrams watch`) fetches and applies approved commits as they land | | Commit signals via `decide` | [`sync_commit`](/mcp/sync-commit) records the actual commit and links it to the [test-gate](/concepts/test-gate) receipt | ## Why the Change Free-form "active branch" tracking worked for two people editing a prototype. It broke for larger teams: * **No notion of work scope.** Two unrelated tasks ended up on the same "active branch" because no one explicitly started a new one. * **Commits were advisory.** `pull_required` was a hint; nothing verified the commit actually built or passed tests. * **No membership.** There was no way to say "I'm working on this with you." Workstreams fix all three: explicit scope (title + description), explicit membership (`workstream_join`/`workstream_leave`), and gated commits ([test gate](/concepts/test-gate) + `sync_commit`). ## Auto-sync Replaces "Pull Required" Run `enagrams watch` once. From then on: 1. Teammate clears the test gate on workstream `ena/auth-refactor`. 2. API emits a `gate_approved` event. 3. Your `watch` process fetches the commit and fast-forwards your local `ena/auth-refactor` (if you're on it). No more "please pull" in Slack. ## Passive Branch Detection Hooks still report your current `git branch --show-current` and changed files as metadata. That lets the dashboard and `sync` tell other agents what you're touching even when you haven't joined a workstream explicitly — useful for quick hotfixes on `main`. ## Best Practices * **Start a workstream for anything multi-commit.** Even solo work benefits from scope and gated commits. * **Keep `enagrams watch` running.** Auto-sync is the "zero ceremony" payoff. * **Don't rely on `active_branch` directives in new code.** They still exist for backwards compatibility but workstreams are the source of truth. See [Workstreams](/concepts/workstreams) for the full lifecycle and tools. # Conventions & Living Decisions Source: https://docs.enagrams.com/concepts/conventions Decisions that stay honest as the code evolves ## Decisions are Informational A decision records *why* you chose something. It's searchable. It's surfaced in `sync`. It has no enforcement teeth. That's deliberate — most architectural decisions are soft guidance. ## Conventions are Enforceable A convention is a team rule with a tier: | Tier | Behavior | | -------- | --------------------------------------------------------------------------------------------------------------------- | | `must` | Hard gate on PreToolUse — edits to matching files are **blocked** until the convention is superseded or marked stale. | | `should` | Surfaced as advisory context in `sync`. Agents see it, but writes aren't blocked. | | `may` | Recorded for awareness. No UI intrusion. | Create conventions with [`convention_propose`](/mcp/convention-propose). Discover the ones that will gate you with [`convention_list`](/mcp/convention-list). ## Why Two Tools? Decisions are cheap and plentiful — agents should record dozens per week. Conventions are load-bearing and rare — a few per quarter for "must" tier, more for "should". Keeping them separate keeps every `decide` call low-stakes while giving the team a real escalation path when a pattern needs enforcement. If you find yourself re-recording the same decision in multiple sessions ("please use zod for validation"), convert it into a `must`-tier convention so the next violation is blocked at write time, not review time. ## Staleness Every decision auto-links to the symbols it mentions. When those symbols change in git, Enagrams marks the decision **stale**. * [`decisions_stale`](/mcp/decisions-stale) lists them * [`decision_reaffirm`](/mcp/decision-reaffirm) marks them fresh again (when the decision still holds) * Recording a new decision with the same title (or an explicit `supersedes` link in the web UI) ends the stale one This matters because architectural context drifts silently. A decision from March ("we chose React Query over SWR") becomes misleading in November if the team migrated to TanStack — but only if you notice. Staleness detection makes that drift visible automatically. ## Scope `affected_files` accepts globs. Keep them narrow: * **Good**: `src/routes/**/*.ts` (all API routes) * **Too broad**: `**/*.ts` (whole codebase — every edit will hit this convention) Broad `must` conventions produce angry teammates. # Coordination Model Source: https://docs.enagrams.com/concepts/coordination How Enagrams keeps agents from working at cross-purposes ## The Core Problem When two developers use AI coding agents in parallel, those agents have no awareness of each other. They make independent decisions, edit the same files simultaneously, and contradict each other's architecture — all without knowing it. The conflict surfaces at merge time. ## The Three Layers Enagrams solves this with three layers. Most teams never interact with layers 2 and 3 directly — the hooks do the work. ### 1. Automatic coordination (hooks) IDE hooks fire on every relevant event. No manual MCP calls needed. ``` New conversation → sessionStart hook fires → Agent receives briefing: "Agent B is editing src/auth/, owns login.ts:validateToken" → Agent knows what's in-flight and which conventions apply User's agent writes a file → preToolUse (Write) hook fires → Enagrams checks: is this file/symbol reserved? Does any must-tier convention match? → If yes: write is denied with context ("Owned by Agent B — 'Build JWT auth'") → If no: write proceeds After successful write → postToolUse hook fires → Reservation refreshed, symbol graph re-extracted Conversation ends → sessionEnd hook fires → All reservations released ``` ### 2. Explicit context (MCP tools) Agents call [26 MCP tools](/mcp/overview) when they need richer context or want to record something: [`sync`](/mcp/sync), [`decide`](/mcp/decide), [`search`](/mcp/search), [`ask`](/mcp/ask), [`learn`](/mcp/learn), plus workstream, task, convention, negotiation, and test-gate tools. ### 3. Auto-sync (`enagrams watch`) Once a change clears the [test gate](/concepts/test-gate) on a workstream branch, `enagrams watch` on other developers' machines fetches and applies it automatically. Nobody manually pulls a teammate's in-progress branch. ## What an agent knows at session start A briefing like this is injected automatically: ``` Active agents: - Developer B's Cursor [workstream: auth-refactor] task: "Build JWT auth flow" editing: src/auth/login.ts · symbol: validateToken Reservations relevant to you: - src/auth/login.ts::validateToken (B, 8 min remaining) - src/auth/middleware.ts (B, 8 min remaining) Recent decisions (3): - "Use JWT for auth" (B, 12 min ago) — stateless, edge-friendly Applicable conventions: - must: "All API routes return { ok, data, error }" (affects src/api/**) - should: "Prefer zod over manual validation" Your task (from claimed work package): - Build user registration (files: src/users/register.ts, src/users/validate.ts) ``` The agent does not need to ask for this — `sessionStart` provides it. ## Conflict detection Beyond file/symbol locking, `sync` returns advisory signals: | Signal | Type | Description | | --------------------------------------------------------------- | -------- | ------------------------------------------------------ | | Same file/symbol reserved by another session | Conflict | Hard block via `preToolUse` | | Another agent working in same directory | Warning | Soft warning in sync | | File recently edited by another agent on a different workstream | Warning | Soft warning in sync | | Stale decision covering files you're editing | Warning | Surfaced via [`decisions_stale`](/mcp/decisions-stale) | When a hard block occurs, the affected agent can open a [negotiation](/concepts/negotiation) instead of silently backing off. ## Session model Sessions are keyed by `conversation_id` (Cursor) or `session_id` (Claude Code/Codex), so: * Multiple Cursor tabs → independent sessions, even when sharing one MCP process * Sessions auto-expire after 5 minutes of inactivity (no hook events) * Sessions appear in the dashboard with agent type, current task, workstream, and touched files/symbols # Decisions Source: https://docs.enagrams.com/concepts/decisions Shared architectural choices that keep agents aligned — and stay honest as the code changes ## What Is a Decision? A decision is a structured record of an architectural choice: what was decided, why, and which files or symbols it affects. Decisions are how Enagrams keeps agents (and humans) aligned on the same architectural approach across sessions, branches, and weeks. Decisions are related to but distinct from [conventions](/concepts/conventions): * A **decision** is a one-time choice ("We picked JWT over sessions"). * A **convention** is an ongoing rule ("All API routes must return `{ ok, data, error }`"). `must`-tier conventions can block writes. ## Recording a Decision Use the [`decide`](/mcp/decide) MCP tool: ```json theme={null} { "tool": "decide", "arguments": { "title": "Use JWT for authentication", "rationale": "Stateless tokens work with our edge deployment on Vercel. No session store needed.", "files": "src/auth/login.ts,src/auth/middleware.ts", "decision_type": "architecture" } } ``` The API parses the comma-separated `files`, resolves them against the [symbol graph](/concepts/symbol-graph) when possible, embeds the decision text for semantic search, and records the `symbol_graph_version` seen at the time. That version is what makes the decision "live" — see [staleness](#living-decisions-staleness) below. Decisions can also be extracted automatically from meeting transcripts and coding traces. ## Decision Types | Type | When to use | | ---------------- | ----------------------------------------------------------------------------------------------------------- | | `architecture` | High-level system design choices | | `approach` | How to implement a specific feature | | `implementation` | Low-level implementation details | | `tradeoff` | Documented tradeoffs between options | | `convention` | Patterns adopted as team standards (prefer [`convention_propose`](/mcp/convention-propose) for enforcement) | | `bugfix` | Root cause and fix for a notable bug | | `other` | Anything else worth remembering | ## Semantic Search Every decision is embedded with `text-embedding-3-small` (1536 dimensions) and stored in pgvector: ```json theme={null} { "tool": "search", "arguments": { "query": "authentication approach", "limit": 10 } } ``` Asking "how do we handle auth?" returns JWT-related decisions even if the original title used different words. ## Decisions in `sync` The [`sync`](/mcp/sync) response always includes the most relevant recent decisions for the agent's current files, plus any flagged stale: ```json theme={null} { "decisions": [ { "id": "dec_abc", "title": "Use JWT for authentication", "rationale": "Stateless, works with edge deployment", "decision_type": "architecture", "made_by": "Developer A", "created_at": "2026-04-13T10:00:00Z", "files": ["src/auth/login.ts"], "is_stale": false } ], "stale_decisions": [] } ``` Agents use this context to avoid contradicting existing decisions. ## Living Decisions: Staleness Decisions don't just sit in a ledger. When the code they describe changes, they get flagged. ``` decide() records decision dec_abc → linked to validateToken symbol ↓ validateToken is later rewritten (detected via symbol_graph diff) ↓ dec_abc is marked stale ↓ decisions_stale() lists it sync() surfaces it in stale_decisions for affected agents ``` Two tools resolve staleness: * [`decision_reaffirm`](/mcp/decision-reaffirm) — "still applies, fresh again" * [`decide`](/mcp/decide) with `supersedes: ` — records a new decision that replaces the old one This keeps the ledger honest. A team of five agents can't drift apart on "what's our auth approach?" because the moment the code moves, the decision surfaces for review. ## Automatic Extraction Decisions are also extracted automatically from: 1. **Meeting transcripts** — LLM identifies decisions in conversation text. 2. **Coding traces** — `POST /traces/push` can trigger extraction from significant diffs. ## Knowledge Graph The dashboard visualizes decisions as a force-directed graph: * **Nodes** — decisions, files, symbols, architectural areas. * **Edges** — shared files/symbols between decisions. Regions with many interconnected decisions signal hot zones of the codebase — and regions with many stale decisions signal architectural drift that needs attention. # File and Symbol Locking Source: https://docs.enagrams.com/concepts/file-locking How Enagrams prevents two agents from editing the same code at the same time ## Overview Locking prevents simultaneous edits at two levels: * **Symbol level** for TypeScript and JavaScript, using the [symbol graph](/concepts/symbol-graph). Two agents can edit the same file as long as they touch different functions or classes. * **File level** for everything else, or when a change spans an entire file. Whichever level applies, the flow is the same: an edit creates a reservation; a conflicting edit from another session is denied with a specific message. ## How Reservations Work ``` Agent A writes src/auth/login.ts (changes validateToken) ↓ postToolUse hook fires ↓ Enagrams parses the file, diffs it against symbol_graph_nodes ↓ Reservation inserted: workspace_id, file_path="src/auth/login.ts", symbol_ids=["validateToken"], session_id, expires_at (+10 min) ``` ``` Agent B tries to edit src/auth/login.ts (also touches validateToken) ↓ preToolUse hook fires ↓ Enagrams looks up reservations for this file + touched symbols ↓ Returns: { permission: "deny", message: "symbol validateToken owned by Developer A — 'Build JWT auth flow' (6 min remaining). Suggest: negotiate_open or pick a different symbol." } ↓ IDE blocks the write ``` If Agent B instead touches `refreshToken` in the same file, no conflict — the write goes through and creates a separate symbol reservation. ## Reservation Rules | Rule | Detail | | ---------------- | ---------------------------------------------------------------------------------------------- | | Duration | 10 minutes, sliding window (reset on every edit by the owning session) | | Scope | Workspace + file + (optional) symbol ID + session | | Uniqueness | `UNIQUE(workspace_id, file_path, symbol_id)` at the DB level — race-free | | Self-reservation | Same user, different conversation → still locks (two tabs lock each other) | | Auto-release | On `sessionEnd` or expiry | | Fallback | If we can't resolve symbols (parse failure, non-TS/JS), we fall back to whole-file reservation | ## Coexistence with `must`-tier Conventions `preToolUse` runs two checks in parallel: 1. Reservation conflict (someone else holds this file/symbol). 2. [Convention](/concepts/conventions) match (`must`-tier rule matches this file + change). Either one can deny the write, and the message explains which. ## IDE Support | IDE | Enforcement level | | ----------- | --------------------------------------------------------------------------------- | | Cursor | Full — `preToolUse`/`postToolUse` hooks intercept `write_file`, `edit_file`, etc. | | Claude Code | Full — hooks intercept both `Edit` and `Write` tools | | Codex | Full — hook intercepts tool calls in the same way | ## Viewing Reservations * **Dashboard** — workstream view shows owned files and symbols per member. * **`sync`** — returns a `reservations` array in the response. * **API** — `GET /workspaces/:id/reservations`. ```json theme={null} [ { "file_path": "src/auth/login.ts", "symbol_ids": ["validateToken"], "reserved_by": "Developer A", "session_id": "sess_…", "task": "Build JWT auth flow", "workstream_slug": "auth-refactor", "expires_at": "2026-04-18T10:35:00Z", "minutes_remaining": 6 } ] ``` ## When You Really Need Someone's Symbol Open a [negotiation](/concepts/negotiation). Actions include yielding the symbol, holding it, deferring, proposing a split, or counter-offering — all within a bounded number of turns so the conversation doesn't stall. ## Why Not Just Use Git? Git catches conflicts at merge time, after hours of duplicate work. Enagrams catches them at write time — before the first conflicting line is written — and gives agents the protocol to resolve them immediately. # Meetings Source: https://docs.enagrams.com/concepts/meetings Turn meeting transcripts into coordinated agent work packages ## Overview Meetings are the bridge between human planning and agent execution. Paste a transcript or record live — Enagrams extracts decisions and action items, then generates scoped work packages that agents receive in their `sync` responses. ## Ingestion Methods ### Paste a Transcript 1. Go to **Dashboard → Meetings** 2. Click **New Meeting** 3. Paste the transcript text 4. Click **Ingest** Enagrams extracts decisions and action items via LLM, then generates work packages. ### Live Recording (Whisper) 1. Click **Record Meeting** in the Meetings page 2. Allow microphone access 3. Browser captures audio via MediaRecorder and streams chunks every 5 seconds 4. Audio is transcribed by OpenAI Whisper in real-time 5. Click **Stop** — decisions and work packages are automatically generated ### API ```bash theme={null} POST /meetings/ingest Authorization: Bearer ek_... { "workspace_id": "ws_...", "title": "Sprint Planning", "transcript": "Developer A: Let's use Zod for validation. Developer B: Agreed, and we need auth by Friday...", "participants": ["Developer A", "Developer B"] } ``` ## What Gets Extracted **Decisions** — Architectural choices made in the meeting: ```json theme={null} { "title": "Use Zod for validation", "rationale": "Type-safe, works with our TypeScript setup", "type": "architecture", "affected_files": ["src/validators/"], "affected_areas": ["validation"] } ``` **Action items** — Tasks assigned during the meeting: ```json theme={null} { "action": "Build user registration endpoint", "assignee": "Developer B", "priority": "high", "deadline": "2026-04-15" } ``` **Work packages** — A second LLM pass divides decisions into non-overlapping, file-scoped work units: ```json theme={null} { "title": "Implement Zod validation for auth routes", "description": "Add Zod schemas for login, register, and password reset", "files": ["src/auth/validators.ts", "src/auth/login.ts"], "decisions": ["dec_abc"], "assignee": "Developer B" } ``` ## Work Package Flow ``` Meeting ingested → Decisions extracted and stored → Work packages generated (non-overlapping file sets) → Team claims packages in dashboard Developer B claims "Implement Zod validation" → Package status: Claimed → In Progress Developer B's agent calls sync() → sync response includes: { "your_work": [{ "title": "Implement Zod validation for auth routes", "files": ["src/auth/validators.ts", "src/auth/login.ts"], "decisions": [...] }] } → Agent knows exactly what to build, why, and which files to touch ``` ## Meeting Status | Status | Description | | ------------ | ------------------------------------- | | `recording` | Live recording in progress | | `processing` | LLM extraction running | | `completed` | Decisions and work packages available | ## Viewing Meetings Dashboard → Meetings shows all past meetings with: * Participant list * Extracted decisions count * Work packages generated * Full transcript # Negotiation Source: https://docs.enagrams.com/concepts/negotiation Bounded-turn resolution when two agents contest the same symbol ## The Problem Two agents want to edit `createSubscription`. One has it reserved; the other needs it. Without a protocol, the second agent either blocks indefinitely, edits in a fork, or escalates prematurely. ## The Protocol Negotiations are a state machine with bounded turns and a deadline. ``` negotiate_open (initiator) ↓ negotiate_respond (counterparty): yield | hold | defer | counter | split ↓ resolved | escalated | expired ``` ### Actions | Action | Effect | Terminal? | | --------- | -------------------------------------------------------------------- | --------- | | `yield` | Counterparty releases the symbol. Initiator proceeds. | Yes | | `hold` | Counterparty keeps the symbol. Initiator routes around or escalates. | Yes | | `defer` | Counterparty asks for `defer_ms` more time. | No | | `counter` | Free-form reply keeping the turn open. | No | | `split` | Partition the contested symbols between the two parties. | Yes | ### Auto-resolution If the counterparty doesn't respond before `deadline_ms` (default: 5 minutes), the negotiation auto-resolves in the initiator's favor — because a silent lockholder is no better than no lockholder. ### Escalation Hitting `max_turns` without a terminal action marks the negotiation `escalated` and surfaces it in the web dashboard for a human to resolve. ## When to Open One The file-reservation denial message includes the holder's session id and current task. If routing around is impractical (the symbol is the point), call [`negotiate_open`](/mcp/negotiate-open) with a clear `rationale`. Don't negotiate for symbols you'll touch once and move on — route around. Negotiate when you genuinely need the symbol and the other agent should know. ## Discovery Call [`negotiate_list`](/mcp/negotiate-list) at session start (or after a long break) to catch anything waiting on your response. # Symbol Graph Source: https://docs.enagrams.com/concepts/symbol-graph Per-workspace index of every top-level function, class, interface, and enum ## What It Is The symbol graph is a per-workspace pgvector index of the codebase at function/class/interface/enum granularity. Rows live in `symbol_graph_nodes`. Each row carries the symbol's kind, file, line range, neighbour symbols (who calls whom), and a semantic embedding of its signature + leading docstring. ## How It Stays Fresh A lightweight [`ts-morph`](https://ts-morph.com) extractor runs on every agent write via the `postToolUse` hook. It re-parses only the file that changed and upserts its top-level declarations. The graph is eventually consistent with the repo state within a few hundred milliseconds of an edit. ## What It Powers | Feature | How the graph helps | | ------------------------------ | ------------------------------------------------------------------------------------------------ | | Symbol-level file reservations | Narrow a lock to `createUser` instead of the whole `src/users.ts` | | Affected-test discovery | The [test gate](/concepts/test-gate) traces symbol-to-test edges to decide what to run | | Living decisions | Decisions auto-link to the symbols they mention; staleness is detected when those symbols change | | Negotiations | `negotiate_open` targets specific symbols by id or name | | Knowledge graph UI | The dashboard visualization renders nodes + call edges | ## Reservations vs Graph File reservations are a coordination primitive — a lock. The symbol graph is a data structure — a map. They interact via `granularity='symbol'` on a reservation: the lock scopes down to the symbol's line range. The graph decides what counts as a symbol; reservations enforce the lock. ## Language Support Today: TypeScript and JavaScript (via `ts-morph`). The extractor is pluggable; Python and Go are next on the roadmap. For languages without an extractor yet, file-level reservations still work — you just lose symbol-level precision. # Task Delegation Source: https://docs.enagrams.com/concepts/tasks Hand off scoped follow-up work with dependency-based auto-unblocking ## Why Delegate A coding agent that tries to ship everything in one change-set produces sprawling diffs nobody can review. Tasks let an agent break a feature into small, independently gate-able pieces and hand them off — to itself in a future session, to another agent, or to a teammate. ## Model A task lives inside a workstream and has: * `title` + `description` * Optional `assignee_session` * Optional `depends_on[]` (other task ids) * Status: `todo` · `in_progress` · `blocked` · `done` · `cancelled` * Free-form `context` payload Tasks with non-empty `depends_on` are created in `blocked` state. When every prerequisite reaches `done`, the task auto-flips to `todo` and becomes claimable. ## Typical Flow Agent A starts a workstream for "Stripe subscription upgrades" and creates three [`delegate_task`](/mcp/delegate-task) entries: wire webhook route, verify signatures (depends on route), add upgrade UI (depends on signatures). Agent A calls [`task_claim`](/mcp/task-claim) on the route task. The other two are blocked. Agent A finishes the route, calls [`confirm_ready`](/mcp/confirm-ready) with `completes_task_id` set to the route task. On gate pass the task flips to `done` — which auto-unblocks the signature task. Agent B (or Agent A's next session) sees an unblocked signature task in [`task_list`](/mcp/task-list), claims it, ships it. Completing it unblocks the UI task. And so on. ## Why Not Just Use GitHub Issues? Issues are external and asynchronous — an agent that creates an issue won't see it until a human re-surfaces it next week. Tasks live inside the workstream, show up in every participant's `sync`, and integrate with the test gate so completion is verifiable. Use issues for work the team might take weeks to start. Use tasks for follow-up the workstream will do in the next few hours. # Test Gate Source: https://docs.enagrams.com/concepts/test-gate The affected-test check that runs before a change lands ## Why "I'm done" is the riskiest signal in multi-agent coding. The test gate turns it from a self-report into a verifiable receipt: your change doesn't propagate to teammates until the tests that depend on the symbols you touched actually pass. ## The Flow Call [`confirm_ready`](/mcp/confirm-ready) with `touched_files` (and optionally `touched_symbols`). The gate walks the [symbol graph](/concepts/symbol-graph) backwards from your changes to the test files that transitively depend on them and returns `affected_tests`. Your agent runs exactly those tests locally. No point running the full suite if only three tests are affected. Call `confirm_ready` again with `test_results[]`. The gate evaluates pass/fail per test, correlates failures against your machine's flake history (`machine_id`), and returns a verdict. On pass the gate writes a `sync_log` row. Pass its `sync_log_id` to [`sync_commit`](/mcp/sync-commit) when you push — the auto-sync watcher uses gated receipts to decide which commits to fan out to other machines. ## Flake Handling The gate tracks per-machine pass/fail history per test. Flaky tests (intermittent failures on the same machine + commit) are surfaced as `failures[].flake=true`. Repeated failures across machines are treated as real and block the gate. ## Completing a Task Atomically If the change-set satisfies a delegated task, pass `completes_task_id` to `confirm_ready`. On gate pass the task is marked `done` in the same transaction, which triggers the unblock cascade for any tasks that depended on it. ## When the Gate Isn't Enough The gate checks "the tests I decided to run passed on my machine". It does not replace CI. Keep CI for: * Full-matrix runs (OS × node version × env) * Integration tests against real external services * Static analysis (linting, typechecks) that gates merge, not handoff The gate's job is to prevent breaking handoffs inside a workstream. CI's job is to prevent merging to `main` broken. # Work Packages Source: https://docs.enagrams.com/concepts/work-packages Scoped units of work generated from meetings that agents pick up automatically ## What Is a Work Package? A work package is a unit of work created from a meeting. It specifies: * What to build (`title` + `description`) * Why (`decisions` — the meeting decisions that motivated it) * Which files to touch (`files`, and when resolvable, symbol IDs) * Who should do it (`assignee`) Work packages are generated with non-overlapping file/symbol sets within the same workspace so two agents claiming two packages never step on each other. ## Work Packages vs Tasks | | Work package | [Task](/concepts/tasks) | | ----------- | ------------------------------------ | ----------------------------------------------------------------- | | Origin | Extracted from a meeting ingestion | Created live during a workstream | | Granularity | Coarse (often several hours of work) | Fine (single follow-up inside a workstream) | | Who assigns | Picked up by humans in the dashboard | Delegated by agents via [`delegate_task`](/mcp/delegate-task) | | Gate-aware | No | Yes — atomic completion via [`confirm_ready`](/mcp/confirm-ready) | Work packages are the "top of funnel" — once claimed, an agent typically starts or joins a [workstream](/concepts/workstreams) and the detailed follow-up work is tracked there as tasks. ## Lifecycle ``` Open → Claimed → In Progress → Completed ``` | Status | Description | | ------------- | ------------------------------- | | `open` | Available for anyone to claim | | `claimed` | Assigned to a developer | | `in_progress` | Agent is actively working on it | | `completed` | Done | ## Claiming a Package 1. Go to **Dashboard → Work Packages** 2. Find an `open` package 3. Click **Claim** ```bash theme={null} POST /workspaces/:id/packages/:pid/claim Authorization: Bearer ek_... ``` ## Receiving Packages in `sync` Claimed packages are included in [`sync`](/mcp/sync) responses automatically: ```json theme={null} { "your_work": [ { "id": "pkg_abc", "title": "Build user registration endpoint", "description": "Create POST /users/register with Zod validation and password hashing", "files": [ "src/users/register.ts", "src/users/validate.ts", "src/auth/password.ts" ], "decisions": [ { "title": "Use Zod for validation", "rationale": "Type-safe, TypeScript-native" }, { "title": "Use bcrypt for password hashing", "rationale": "Industry standard, adaptive cost" } ], "status": "claimed" } ] } ``` The agent sees exactly what to build, the relevant architectural decisions, and which files are in scope — without any additional prompting. ## Updating Status ```bash theme={null} PATCH /workspaces/:id/packages/:pid/status Authorization: Bearer ek_... { "status": "in_progress" } ``` Or, more commonly, the agent starts a [workstream](/concepts/workstreams) for the package and tracks detailed progress with [tasks](/concepts/tasks). ## Non-overlapping File Sets When Enagrams generates packages from a meeting, a second LLM pass divides the work into non-overlapping file sets: * Package A: `src/auth/login.ts`, `src/auth/middleware.ts` * Package B: `src/users/register.ts`, `src/users/validate.ts` Two agents claiming these can work in parallel without file conflicts. For work that can't be cleanly split, create a single workstream and use [tasks](/concepts/tasks) + [negotiation](/concepts/negotiation) to divide the work dynamically. ## Viewing Packages **Dashboard → Work Packages** shows a kanban board organized by status, with: * Who owns each package * Which files (and symbols) are covered * The originating meeting * Any current [reservations](/concepts/file-locking) covering those files # Workstreams Source: https://docs.enagrams.com/concepts/workstreams Scoped units of work mapped 1:1 to shared git branches ## The Idea A workstream is a named feature in flight. Every workstream maps to one git branch, `ena/`. Every agent that joins the workstream checks out that branch locally and works on it together. When the workstream finishes, you squash-merge and move on. This replaces the per-developer branch pattern (Alice on `alice/add-stripe`, Bob on `bob/add-stripe-v2`, both drifting) with a shared working branch that everyone coordinates on. ## Lifecycle Any agent calls [`workstream_start`](/mcp/workstream-start) with a title. The API creates the row and reserves `ena/`. Check the branch out locally. Teammates' agents call [`workstream_join`](/mcp/workstream-join) with the slug. Their sessions flip to coordinated mode; symbol-level reservations and negotiations become available. Everyone commits to the same branch. The [test gate](/concepts/test-gate) validates changes before they're fanned out to other machines via `enagrams watch`. Any participant calls [`workstream_complete`](/mcp/workstream-complete) without `pr_url` to get a squash-merge plan, runs the printed `git` + `gh` commands, then calls again with the PR URL to finalize. ## Auto-detection Any git branch matching the pattern `ena/` puts the session in coordinated mode automatically — you don't have to `workstream_join` explicitly if you already checked out the branch. The hook script detects the branch on `sessionStart` and wires up the current workstream. ## Visibility * `team` (default) — appears in every teammate's `workstream_list` and `sync` responses * `private` — only visible to participants; useful for experiments you don't want advertised ## When Not to Use One Workstreams are overhead for: * Single-file typo fixes * README updates * Solo spikes you plan to throw away Use the plain file-reservation flow for those. Workstreams earn their keep when two or more agents will touch the same area. # Claude Code Setup Source: https://docs.enagrams.com/guides/claude-code-setup Connect Claude Code to your Enagrams workspace ## One-command setup From the repo root: ```bash theme={null} npx enagrams init ``` The CLI writes everything Claude Code needs: * `.mcp.json` — MCP server config at the repo root (Claude Code reads this) * `.claude/settings.json` — hook wiring that points to the shared `.cursor/hooks/enagrams-hooks.mjs` script The hook script is shared with Cursor and Codex, so all three IDEs emit activity to the same workspace without duplication. Restart Claude Code after `init` so the MCP server registers. ## Verify In Claude Code, check that the `enagrams` server is connected (settings → MCP). You should see 26 tools. ```bash theme={null} enagrams status ``` With Claude Code open you should see an active agent session tagged `agent_type: claude_code`. ## What's in `.claude/settings.json` Claude Code's hook format is slightly different from Cursor's, but the underlying script is identical — the CLI translates automatically. ```json theme={null} { "hooks": { "PreToolUse": [{ "matcher": "Write|Edit|MultiEdit", "hooks": [{ "type": "command", "command": "node .cursor/hooks/enagrams-hooks.mjs preToolUse" }] }], "PostToolUse": [{ "matcher": "Write|Edit|MultiEdit", "hooks": [{ "type": "command", "command": "node .cursor/hooks/enagrams-hooks.mjs postToolUse" }] }], "SessionStart": [{ "hooks": [{ "type": "command", "command": "node .cursor/hooks/enagrams-hooks.mjs sessionStart" }] }], "SessionEnd": [{ "hooks": [{ "type": "command", "command": "node .cursor/hooks/enagrams-hooks.mjs sessionEnd" }] }] } } ``` `.mcp.json` (at repo root): ```json theme={null} { "mcpServers": { "enagrams": { "command": "npx", "args": ["-y", "enagrams", "--mcp"], "env": { "ENAGRAMS_API_KEY": "...", "ENAGRAMS_WORKSPACE": "..." } } } } ``` ## Troubleshooting **Server connects but no tools show** — your API key may be invalid. Run `enagrams login` to refresh. **File writes blocked unexpectedly** — another session owns the file, or a `must`-tier convention applies. The denial message explains which. # CLI Reference Source: https://docs.enagrams.com/guides/cli-reference Every enagrams command ## Install ```bash theme={null} npm install -g enagrams ``` Or invoke via `npx enagrams ` — no install required. `enagrams init` will offer to install globally after first auth. ## Environment | Variable | Purpose | Default | | -------------------- | -------------------------------------------------------------- | ----------------------- | | `ENAGRAMS_API_KEY` | Your API key (also stored in `~/.config/enagrams/config.json`) | — | | `ENAGRAMS_WORKSPACE` | Workspace slug or ID (usually set in `.env` by `init`) | — | | `ENAGRAMS_API_URL` | API server | `http://localhost:3001` | | `ENAGRAMS_WEB_URL` | Web dashboard | `http://localhost:3000` | The API key is written once to `~/.config/enagrams/config.json` and reused across every repo. The workspace slug lives in each repo's `.env`. ## Commands ### `enagrams init` Bootstrap the current repo. Runs browser-based device auth, creates or resumes a workspace, writes all config files, and optionally installs the CLI globally. ```bash theme={null} npx enagrams init ``` What gets written: ``` ~/.config/enagrams/config.json API key (global) .env ENAGRAMS_WORKSPACE= .cursor/mcp.json MCP server config for Cursor .cursor/hooks.json Cursor hook wiring .cursor/hooks/enagrams-hooks.mjs Shared hook script (all IDEs) .mcp.json MCP server config for Claude Code .claude/settings.json Claude Code hooks .codex/hooks.json Codex hooks ``` Re-running `init` is safe — existing files are updated in place. ### `enagrams login` Re-authenticate without touching the workspace. Writes `~/.config/enagrams/config.json`. ```bash theme={null} enagrams login ``` ### `enagrams join` Join an existing workspace in the current repo. Lists the workspaces you're a member of (or have been invited to) and writes the same config files as `init`. ```bash theme={null} npx enagrams join ``` ### `enagrams status` Show live team status: active agents, current file reservations, recent decisions and learnings. ```bash theme={null} enagrams status ``` ### `enagrams watch` Full-screen live dashboard that auto-refreshes every second. Same content as `status` but persistent. ```bash theme={null} enagrams watch ``` `Ctrl+C` to exit. Also available as `enagrams status --watch`. ### `enagrams learn ` Record a learning for the team. Calls the `learn` MCP tool directly so the insight is available to every agent on their next `sync`. ```bash theme={null} enagrams learn "Rate limiter resets at UTC midnight, not wall-clock" ``` With no text, prompts interactively. ### `enagrams compile-rules` Pulls recent learnings from your workspace and compiles them into `.cursor/rules/team-learnings.mdc`. Cursor will load them as project rules so every agent session starts with the team's accumulated wisdom. ```bash theme={null} enagrams compile-rules ``` ### `enagrams help` Usage summary. Aliases: `--help`, `-h`. ## Flags ### `--mcp` Launch the MCP server over stdio. Used by your IDE's MCP configuration — you should not normally invoke this by hand. `init` writes the right command into `.cursor/mcp.json` and `.mcp.json` automatically. ```json theme={null} { "mcpServers": { "enagrams": { "command": "npx", "args": ["-y", "enagrams", "--mcp"], "env": { "ENAGRAMS_API_KEY": "...", "ENAGRAMS_WORKSPACE": "..." } } } } ``` # Codex Setup Source: https://docs.enagrams.com/guides/codex-setup Connect OpenAI Codex CLI to your Enagrams workspace ## One-command setup From the repo root: ```bash theme={null} npx enagrams init ``` The CLI writes everything Codex needs: * `.codex/config.toml` — MCP server configuration for the Codex CLI * `.codex/hooks.json` — hook wiring pointing at the shared hook script Codex picks the config up on its next launch. ## Verify Start Codex in the project directory. At session start you should see a team briefing injected (active agents, decisions, conventions). ```bash theme={null} enagrams status ``` An active session tagged `agent_type: codex` confirms the wiring. ## Config Files `.codex/config.toml`: ```toml theme={null} [mcp_servers.enagrams] command = "npx" args = ["-y", "enagrams", "--mcp"] [mcp_servers.enagrams.env] ENAGRAMS_API_KEY = "..." ENAGRAMS_WORKSPACE = "..." ``` `.codex/hooks.json` wires Codex's hook names to the shared script — `npx enagrams init` generates this for you. ## Differences from Cursor and Claude Code * Codex doesn't have per-tool `matcher` semantics; the hook script inspects the tool name itself to decide whether to acquire a file reservation. * `SessionStart` context injection happens through stdout — the hook script writes the briefing which Codex prepends to the conversation. ## Troubleshooting **Codex says it can't find the MCP server** — make sure `npx` is on Codex's PATH. The global `npm -g` install path is the usual fix. **Briefing isn't appearing** — verify the hook script is executable (`chmod +x .cursor/hooks/enagrams-hooks.mjs`) and that `.codex/hooks.json` references it correctly. # Cursor Setup Source: https://docs.enagrams.com/guides/cursor-setup Connect Cursor to your Enagrams workspace ## One-command setup From the repo root: ```bash theme={null} npx enagrams init ``` The CLI writes everything Cursor needs: * `.cursor/mcp.json` — MCP server config pointing to `npx -y enagrams --mcp` * `.cursor/hooks.json` — event-to-script wiring * `.cursor/hooks/enagrams-hooks.mjs` — the hook script itself Restart Cursor (or re-open the project) so it picks up the new configuration. ## Verify Open the MCP panel in Cursor (command palette → `Cursor: Open MCP Settings`). You should see `enagrams` listed as a connected server with 26 tools. In a terminal: ```bash theme={null} enagrams status ``` With Cursor open on this project you should see at least one active agent session. ## What the Hooks Do | Event | Action | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `sessionStart` | Registers a new `agent_sessions` row and injects a team briefing (active agents, recent decisions, conventions that apply) into the agent's context. | | `preToolUse` (on writes) | Calls `file_lock` for the file. If another agent owns it — or a `must`-tier convention matches — the write is denied with an explanatory message. | | `postToolUse` (on writes) | Re-extracts the [symbol graph](/concepts/symbol-graph) for the changed file. | | `beforeSubmitPrompt` | Compact team-state refresh. | | `sessionEnd` | Releases reservations and closes the session. | ## Manual Configuration If you'd rather set it up by hand, `.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "enagrams": { "command": "npx", "args": ["-y", "enagrams", "--mcp"], "env": { "ENAGRAMS_API_KEY": "your-api-key", "ENAGRAMS_WORKSPACE": "your-workspace-slug" } } } } ``` For the hook scripts run `npx enagrams init` in a scratch directory and copy the generated files — keeping them in sync with the CLI is easier than maintaining them manually. ## Troubleshooting **MCP server not appearing in Cursor** — fully quit and relaunch Cursor. Restart-Cursor-window is not enough. **"Permission denied" on every write** — `must`-tier convention matched. Run [`convention_list`](/mcp/convention-list) with the file path to see which one. **Hooks not firing** — check `.cursor/hooks.json` exists and references `.cursor/hooks/enagrams-hooks.mjs`. Re-run `npx enagrams init` to regenerate. # How It Works Source: https://docs.enagrams.com/guides/how-it-works The coordination model behind Enagrams ## Architecture Overview Enagrams has three coordination layers working together: 1. **IDE hooks** — automatic, zero-effort coordination at every tool event 2. **MCP tools** — 26 tools agents call explicitly for context, decisions, workstreams, tasks, negotiations, and the test gate 3. **Auto-sync engine** — `enagrams watch` propagates gate-approved changes between developers ``` Agent A (Cursor) Agent B (Claude Code) │ │ ├── sessionStart ──────┐ ┌───────── SessionStart ─┤ ├── preToolUse (Write)─┤ ├── PreToolUse (Write) ──┤ ├── postToolUse (Write)┤ ├── PostToolUse (Write) ─┤ │ v v │ │ ┌────────────────────────────────┐ │ │ │ Enagrams API │ │ │ │ │ │ │ │ agent_sessions │ │ │ │ file_reservations (symbol- │ │ │ │ level via symbol_graph) │ │ │ │ decisions + conventions │ │ │ │ workstreams + tasks │ │ │ │ negotiations │ │ │ │ symbol_graph_nodes │ │ │ │ sync_log (test gate) │ │ │ └────────────────────────────────┘ │ │ │ │ │ Dashboard (SSE) │ │ enagrams watch (auto-sync) │ ``` ## Hook-Based Coordination (Always On) Hooks handle coordination automatically. Your IDE calls them for every relevant event — you never need to think about it. | Hook event | When it fires | What happens | | -------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sessionStart` | New conversation begins | Registers an `agent_sessions` row and injects a team briefing: active agents, locked files/symbols, recent decisions, applicable conventions | | `preToolUse` (Write/Edit) | Before any file write | Calls `file_lock`. If another session owns the file (or any touched symbol), or a `must`-tier [convention](/concepts/conventions) matches, the write is denied with a specific message | | `postToolUse` (Write/Edit) | After a successful write | Records the edit, re-extracts the [symbol graph](/concepts/symbol-graph) for the changed file, and refreshes the reservation | | `beforeSubmitPrompt` | User sends a message | Compact team-state refresh — keeps the agent's context current without a full briefing | | `sessionEnd` | Conversation ends | Releases reservations, closes the session | The hook script is a single file (`.cursor/hooks/enagrams-hooks.mjs`) shared by Cursor, Claude Code, and Codex. It detects the IDE from event-name casing (Cursor uses camelCase, Claude Code and Codex use PascalCase). ## MCP Tools (Explicit) Agents reach for MCP tools when they need richer context or want to record something. The 26 tools group into six areas: | Area | Tools | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Coordination & context | [`sync`](/mcp/sync), [`decide`](/mcp/decide), [`publish`](/mcp/publish), [`search`](/mcp/search), [`ask`](/mcp/ask), [`learn`](/mcp/learn) | | Workstreams | [`workstream_start`](/mcp/workstream-start), [`workstream_join`](/mcp/workstream-join), [`workstream_leave`](/mcp/workstream-leave), [`workstream_end`](/mcp/workstream-end), [`workstream_complete`](/mcp/workstream-complete), [`workstream_list`](/mcp/workstream-list) | | Test gate | [`confirm_ready`](/mcp/confirm-ready), [`sync_commit`](/mcp/sync-commit), [`sync_retract`](/mcp/sync-retract) | | Negotiation | [`negotiate_open`](/mcp/negotiate-open), [`negotiate_respond`](/mcp/negotiate-respond), [`negotiate_list`](/mcp/negotiate-list) | | Tasks | [`delegate_task`](/mcp/delegate-task), [`task_claim`](/mcp/task-claim), [`task_update`](/mcp/task-update), [`task_list`](/mcp/task-list) | | Conventions & living decisions | [`convention_propose`](/mcp/convention-propose), [`convention_list`](/mcp/convention-list), [`decisions_stale`](/mcp/decisions-stale), [`decision_reaffirm`](/mcp/decision-reaffirm) | See the [MCP overview](/mcp/overview) for a full breakdown. ## File and Symbol Locking Reservations prevent simultaneous edits, and they work at two levels: * **File level** — for languages we don't parse or files that changed entirely * **Symbol level** — for TypeScript/JavaScript files, via the [symbol graph](/concepts/symbol-graph). Two agents can edit the same file as long as they touch different functions/classes. Flow: 1. `postToolUse` (or `file_lock`) records the reservation keyed on session, file, and touched symbol IDs. 2. Reservations expire after 10 minutes of inactivity (sliding window) and are released at `sessionEnd`. 3. When another agent tries to write, `preToolUse` looks up conflicts and denies with a message naming the owner, their task, and the contested symbols. 4. The blocked agent can open a [negotiation](/concepts/negotiation) to work it out. The `UNIQUE(workspace_id, file_path, symbol_id)` constraint prevents race conditions under concurrent load. ## Workstreams and the Test Gate A [workstream](/concepts/workstreams) is a named unit of work mapped to a shared Git branch (`ena/`). Any change bound for the shared branch flows through the [test gate](/concepts/test-gate): 1. Agent touches files/symbols locally. 2. Agent calls [`confirm_ready`](/mcp/confirm-ready) — API returns the minimal set of affected tests derived from the symbol graph. 3. Agent runs those tests and resubmits the results with `test_results`. 4. If all affected tests pass, the API emits a `gate_approved` event; otherwise it returns `gate_blocked` with the failing suites. 5. Agents running `enagrams watch` auto-merge approved changes on their machines. Atomic task completion: if `confirm_ready` is called with `completes_task_id`, the task is only marked `done` when the gate approves — no more "I said done but it doesn't build." ## Session Lifecycle ``` sessionStart hook → API creates / re-uses session keyed by conversation_id → Returns briefing as additional_context [conversation in progress] → Every hook event refreshes last_heartbeat → Sessions auto-expire after 5 min inactivity sessionEnd hook → Releases reservations → Ends session ``` Multiple tabs sharing one MCP process each get independent sessions via their own `conversation_id` or `session_id`. ## Living Decisions and Conventions Every [`decide`](/mcp/decide) call stores: * `title`, `rationale`, `decision_type`, affected files and (when resolvable) symbol IDs * `embedding` — 1536-dim vector for semantic search * A link to the [symbol graph](/concepts/symbol-graph) so the decision can be marked stale when the code it describes changes Conventions are the enforcement side of the same system: [`convention_propose`](/mcp/convention-propose) records a rule with a tier (`must` / `should` / `may`). `must`-tier conventions run inside `preToolUse` and block writes that match their patterns. See [Conventions & Living Decisions](/concepts/conventions) for details. ## Real-time Dashboard `https://enagrams.com/dashboard` shows: * **Agent feed** — real-time SSE stream of syncs, decisions, file/symbol claims, gate results * **Workstreams** — active branches, members, touched files/symbols, open tasks * **Decisions** — timeline with stale flags * **Conventions** — tier-grouped list * **Tasks** — per-workstream kanban * **Negotiations** — open/resolved turns * **Knowledge graph** — decisions connected by shared symbols ## Meeting Ingestion 1. Paste a transcript or record live. 2. LLM extracts decisions and action items. 3. A second pass generates non-overlapping work packages scoped to files/symbols. 4. Team members claim packages. 5. Claimed packages arrive in `sync` responses so the assigned agent knows the task, rationale, and exact files to touch. # Quickstart Source: https://docs.enagrams.com/guides/quickstart Get your team's agents coordinating in under two minutes ## Prerequisites * Node.js 18+ * An AI coding IDE (Cursor, Claude Code, Codex, or any client with Model Context Protocol support) ## Install From the root of the repository you want to coordinate: ```bash theme={null} npx enagrams init ``` That single command: 1. Opens your browser for authentication — no manual API keys to copy 2. Creates the workspace (or joins yours if one exists for this slug) 3. Writes the global API key to `~/.config/enagrams/config.json` 4. Writes the workspace slug to `.env` (`ENAGRAMS_WORKSPACE=...`) 5. Drops IDE configs for Cursor (`.cursor/hooks.json`, `.cursor/mcp.json`), Claude Code (`.mcp.json`, `.claude/settings.json`), and Codex (`.codex/hooks.json`) 6. Installs the hook script at `.cursor/hooks/enagrams-hooks.mjs` 7. Offers to install `enagrams` globally so you can drop `npx` Reopen your IDE after init so the MCP server and hooks are picked up. The first `init` on a new machine triggers a browser-based device code flow. Subsequent `init`s in other repos reuse your saved API key automatically. ## Verify ```bash theme={null} enagrams status ``` You should see your workspace name and — once your IDE is open — an active agent session. ## Join an Existing Workspace If a teammate already created the workspace: ```bash theme={null} npx enagrams join ``` Pick the workspace from the list. The same config files are written for the current repo. ## What's Installed | File | Purpose | | --------------------------------------------------------- | ------------------------------------------------------- | | `~/.config/enagrams/config.json` | API key (global, like `gh` / `vercel`) | | `.env` | `ENAGRAMS_WORKSPACE=` for this repo | | `.cursor/mcp.json` | MCP server config for Cursor | | `.cursor/hooks.json` + `.cursor/hooks/enagrams-hooks.mjs` | Cursor event hooks | | `.mcp.json` | MCP server config for Claude Code | | `.claude/settings.json` | Claude Code hook config (reuses the Cursor hook script) | | `.codex/hooks.json` | Codex hook config | All configs point to the same hook script, so every agent in every IDE on your machine emits activity to the same workspace. ## Your First Coordinated Edit Cursor, Claude Code, or Codex. The `sessionStart` hook fires, your session is registered, and you'll see a team briefing in the hook output. On the first write, the `preToolUse` hook calls `file_lock` so you own that file until your session ends or your agent runs `session_end`. Their agent's `preToolUse` hook is denied with a message telling them who owns it and what you're working on. They route around. Call the `decide` MCP tool from your agent. Every teammate's next `sync` surfaces the decision immediately. ## Next Steps The coordination model from hooks to sync. Every tool your agents can call. Cursor, Claude Code, and Codex specifics. Every `enagrams` command. # Team Onboarding Source: https://docs.enagrams.com/guides/team-onboarding Getting your whole team set up with shared agent coordination ## Overview Enagrams is most powerful when every developer on the team connects their agent to the same workspace. This guide walks through onboarding a team of 2–10 developers. ## Workspace Setup (Owner) ### 1. Create the Workspace Pick the path that matches where you are: * **You have the repo cloned locally** — from the repo root, run `npx enagrams init`. The CLI authenticates you, binds the workspace to this repo, and writes all IDE configs in one shot. * **You want to set it up before cloning** — create it in the [dashboard](https://enagrams.com/dashboard) with **New Workspace**, then click **Connect GitHub** on the new workspace and pick the repo. The Enagrams GitHub App resolves the repo fingerprint server-side and binds it for you. See [Workspace Management → Creating a Workspace](/guides/workspace-management#creating-a-workspace) for all four paths and when to use each. If you create the workspace in the dashboard **without** the GitHub App, use the repo's basename as the slug (`github.com/org/chatgpt-wrapper` → `chatgpt-wrapper`). The CLI uses slug-matches-basename as the trigger for a one-keystroke **"bind this repo to ``"** shortcut when a teammate runs `init`. ### 2. Invite Team Members From the dashboard: **Workspace → Team → Invite** (enter email). The teammate gets an email with an accept link. Pick a role when you invite or change it later: * **Owner** — full access, can delete workspace, bind/unbind repo. * **Admin** — can manage members, bind/unbind repo, change settings. * **Member** — can create decisions, claim work packages, use MCP. Enagrams is invite-only — teammates can't join by guessing the slug or running `init` in the bound repo. The invite email is the only path in. See [Sharing a Workspace](/guides/workspace-management#sharing-a-workspace) for the full flow the teammate sees. ### 3. Tell Teammates to Run `enagrams login` Nothing to share manually — no slugs, no API keys, no invite codes. Once you've sent the dashboard invite, each teammate clones the repo and runs: ```bash theme={null} npx enagrams login ``` The CLI fingerprints the repo, recognizes it's already bound to your workspace, and — because the teammate just accepted your invite — fast-paths them to a **"Continue with ``"** prompt. One keystroke and they're in. If a teammate runs `login` **before** you've sent the invite, they'll see: ``` This repo is exactly already bound to an Enagrams workspace: My Startup (my-startup) Ask one of its admins for an invite: • owner@example.com Once you accept the invite, re-run `enagrams login`. ``` That's the hint — they forward it to you, you invite them, they retry. ## Per-Developer Setup Once a teammate has accepted an invite: ```bash theme={null} npx enagrams login # authenticate + continue with the bound workspace npx enagrams init # (optional) drop IDE hooks + MCP configs for this repo ``` `init` detects the already-bound workspace and skips straight to writing IDE configs for whichever of Cursor, Claude Code, and Codex it finds installed. After that: * IDE-specific tweaks: [Cursor](/guides/cursor-setup) · [Claude Code](/guides/claude-code-setup) · [Codex](/guides/codex-setup) * `enagrams status` to verify the session is live. * `enagrams watch` in a spare terminal for a live team dashboard. Each developer gets their own API key automatically, stored in `~/.config/enagrams/config.json` (global, like `gh` or `vercel`) — never in the repo. Never share API keys. Each developer needs their own — it's how Enagrams tracks which human is behind each agent session. ## Running Your First Coordinated Session ### Scenario: Two developers, same codebase **Developer A** starts a Cursor conversation: > "Build the user authentication flow with JWT tokens. Open a workstream for it." Their agent calls [`workstream_start`](/mcp/workstream-start) to create `ena/auth-jwt`, begins working, and acquires symbol-level reservations on the auth files. **Developer B** starts a Cursor conversation soon after: > "Add user registration to the API." Their agent's briefing shows: ``` Active workstream you can join: ena/auth-jwt ("Build JWT auth flow") members: Developer A reserved symbols: src/auth/login.ts::validateToken (Developer A, 8 min) src/auth/middleware.ts::requireAuth (Developer A, 8 min) Recent decisions (1): Chose JWT for auth (Developer A, 5 min ago) ``` Developer B's agent joins the workstream with [`workstream_join`](/mcp/workstream-join) (if registration is the same effort) or starts a new one, builds registration compatible with the JWT decision, and avoids the locked symbols. ## Recommended Workflow 1. **Start of day** — open the [Activity Feed](https://enagrams.com/dashboard/feed) to see active agents and workstreams. 2. **Before a task** — check [Work Packages](/concepts/work-packages) for anything from recent meetings; claim what's yours. 3. **Working** — start or join a [workstream](/concepts/workstreams). Hooks handle locking automatically. 4. **Shipping** — call [`confirm_ready`](/mcp/confirm-ready) to run the [test gate](/concepts/test-gate), then `sync_commit` after committing. Teammates on `enagrams watch` get the change automatically. 5. **After a meeting** — paste the transcript at [Meetings](https://enagrams.com/dashboard/meetings) to extract decisions and generate work packages. ## Dashboard Views | View | URL | Purpose | | --------------- | ------------------------- | --------------------------------------------------- | | Overview | `/dashboard` | Active agents, active workstreams, recent decisions | | Activity Feed | `/dashboard/feed` | Real-time event stream | | Workstreams | `/dashboard/workstreams` | Active branches, members, tasks | | Decisions | `/dashboard/decisions` | Full decision timeline with stale flags | | Conventions | `/dashboard/conventions` | Tier-grouped team rules | | Tasks | `/dashboard/tasks` | Per-workstream kanban | | Negotiations | `/dashboard/negotiations` | Open and resolved turns | | Work Packages | `/dashboard/packages` | Meeting-derived task board | | Team | `/dashboard/team` | Per-person drill-down | | Knowledge Graph | `/dashboard/graph` | Decisions connected by shared symbols | | Meetings | `/dashboard/meetings` | Transcript ingestion and history | # Workspace Management Source: https://docs.enagrams.com/guides/workspace-management Managing workspaces, members, and settings ## Workspaces A workspace is the shared coordination context for a team working on one codebase. All agents connected to the same workspace share decisions, conventions, reservations, workstreams, tasks, and the symbol graph. Every workspace is **bound to exactly one git repository**. Binding is what makes "which workspace is this?" an unambiguous question — any teammate who clones the repo gets routed to the same workspace without guessing at a name. ### Creating a Workspace Pick whichever path matches how you're onboarding — they all produce the same workspace. From the root of the repo you want to coordinate: ```bash theme={null} npx enagrams init ``` The CLI: 1. Opens your browser to authenticate (first run only — subsequent runs reuse `~/.config/enagrams/config.json`). 2. Computes the repo fingerprint (normalized `origin` URL + root commit SHA). 3. Checks whether this repo is already bound to a workspace — if so, it routes you to it instead of creating a duplicate. 4. Prompts for a name and slug, creates the workspace, binds it to this repo, and writes all IDE config files. See [Quickstart](/guides/quickstart) for what `init` writes to disk. Best when the owner doesn't want to clone the repo locally first. 1. Go to [enagrams.com/dashboard](https://enagrams.com/dashboard) and click **New Workspace**. 2. Set a name and slug. 3. In the new workspace's settings, click **Connect GitHub**. This launches the Enagrams GitHub App install flow (same pattern as Vercel/Netlify). 4. Pick the account and repo. The App resolves the repo's root commit server-side and binds the workspace. After this, teammates who clone the repo and run `enagrams login` are recognized automatically — no `init` needed on their end unless they want the hooks and MCP configs. You can create an unbound workspace and bind it from a clone later. 1. **New Workspace** in the dashboard with name + slug. 2. Later, whoever runs `npx enagrams init` in the matching repo will be offered a **one-keystroke "bind this repo to ``"** shortcut — provided the workspace slug matches the repo's basename. This is the natural team flow: owner creates `chatgpt-wrapper` in the dashboard, invites the teammate, teammate runs `init` in their `chatgpt-wrapper/` clone, answers `y`, done. ```bash theme={null} curl -X POST https://api.enagrams.com/workspaces \ -H "Authorization: Bearer ek_your_key" \ -H "Content-Type: application/json" \ -d '{"name": "My Startup", "slug": "my-startup"}' ``` Binding happens separately via `POST /workspaces/:id/repo-binding` with the fingerprint payload. The CLI and GitHub App are just wrappers around this endpoint. ### Repo Binding A binding is the pair `(repo_url, repo_root_commit)`: * **`repo_url`** — normalized form of `git remote get-url origin`. `git@github.com:Org/Repo.git`, `https://github.com/Org/Repo`, and `https://user:token@github.com/org/repo.git?ref=x` all fold to the same canonical URL. Host is lowercased; org+repo are lowercased on GitHub/GitLab/Bitbucket (case-insensitive hosts). * **`repo_root_commit`** — SHA of the earliest commit (`git rev-list --max-parents=0 --all`, sorted). Stable across branches, clones, and forks of the same history. The binding is **1:1**: any given repo can be bound to at most one workspace, and any given workspace can be bound to at most one repo. This is what the CLI uses to detect "teammate B just cloned the repo teammate A already bound." Shallow clones (`git clone --depth=N`) don't carry the real root commit. The CLI detects this, skips the SHA, and binds on URL alone. Run `git fetch --unshallow` for the full fingerprint. ### Workspace Slug The slug is a URL-safe identifier (e.g. `my-startup`). It's what you supply for `ENAGRAMS_WORKSPACE` in each developer's `.env`. Slugs are unique across all workspaces. When you create a workspace in the dashboard for a repo that doesn't exist locally yet, set the slug to the repo's basename (`github.com/org/chatgpt-wrapper` → slug `chatgpt-wrapper`). The CLI uses slug-matches-basename as the trigger for its one-keystroke bind shortcut. ## Sharing a Workspace Enagrams is **invite-only**. A teammate can't join a workspace by guessing its slug or by running `enagrams init` in a bound repo — workspace owners and admins invite from the dashboard, and teammates accept. ### The flow ```bash theme={null} git clone git@github.com:org/chatgpt-wrapper.git cd chatgpt-wrapper npx enagrams login ``` The CLI fingerprints the repo and asks the API whether any workspace already owns it. ``` This repo is exactly already bound to an Enagrams workspace: ChatGPT Wrapper (chatgpt-wrapper) Ask one of its admins for an invite: • Aaron Siddiky • Jimmy Charter Once you accept the invite, re-run `enagrams login`. ``` The CLI exits cleanly — it won't create a duplicate workspace or offer to bind over the top of the existing one. Owner or admin goes to **Dashboard → Team → Invite** and enters the teammate's email. The teammate gets an email with the invite link, clicks to accept, and is added as a `member`. ```bash theme={null} npx enagrams login ``` This time the CLI sees the teammate is a member of the bound workspace, fast-paths them to a one-keystroke **Continue with ``** prompt, and writes the workspace into `.env`. If the teammate wants Cursor / Claude Code / Codex hooks and MCP configs: ```bash theme={null} npx enagrams init ``` `init` detects the already-bound workspace and skips straight to writing the IDE files — no bind prompts. The "ask for an invite" hint is fired by an unauthenticated `POST /repo-lookup` probe, rate-limited to prevent enumeration. The endpoint only returns admin contacts when the fingerprint matches a real workspace — random probes get `{match: null}`. ### Fingerprint edge cases The CLI surfaces actionable messages for the five non-happy git states before showing any menu: | Status | What the CLI says | Fix | | ----------------------- | ------------------------------------------- | --------------------------------------------------------- | | `no_git` | This directory is not inside a git worktree | `cd` into your project or `git init` | | `no_commits` | This git repo has no commits yet | `git add -A && git commit -m "first"` | | `no_remote` | No remote configured — binding on SHA alone | `git remote add origin ` (optional; SHA still works) | | `shallow` | Shallow clone; binding on URL alone | `git fetch --unshallow` for the full fingerprint | | `no_remote_and_shallow` | No signal available | Fix one of the two above first | ## Members ### Roles | Role | Permissions | | -------- | ------------------------------------------------------------------------- | | `owner` | Full access, delete workspace, bind/unbind repo, manage all members | | `admin` | Manage members, bind/unbind repo, change settings | | `member` | Create decisions, claim work packages, propose conventions, use MCP tools | ### Inviting Members From the dashboard: **Workspace → Team → Invite** (enter email). The invitee gets an email; once they accept they're added as `member` by default (admins can change the role before or after). You can also use the API: ```bash theme={null} POST /workspaces/:id/members Authorization: Bearer ek_... { "user_id": "usr_...", "role": "member" } ``` ### Removing Members ```bash theme={null} DELETE /workspaces/:id/members/:userId Authorization: Bearer ek_... ``` When a member is removed their active sessions end, reservations release, and any workstreams they were the sole owner of are marked abandoned. ## API Keys Each team member has their own API key: 1. `npx enagrams login` creates one automatically and saves it to `~/.config/enagrams/config.json`. 2. Or generate one manually at **Dashboard → API Keys**. API keys have the prefix `ek_` and are tied to a user account. A key can access any workspace the user is a member of. ### Revoking Keys Revoke from **Dashboard → API Keys → Revoke**. Active sessions using the key are terminated immediately. ## Workstreams Workstreams are the primary unit of shared work within a workspace. Each one maps to a branch (`ena/`). See [Workstreams](/concepts/workstreams) for the full lifecycle. List active workstreams: ```bash theme={null} GET /workspaces/:id/workstreams?status=active Authorization: Bearer ek_... ``` Or via MCP: [`workstream_list`](/mcp/workstream-list). ## Reservations View current [reservations](/concepts/file-locking) (file and symbol level): ```bash theme={null} GET /workspaces/:id/reservations Authorization: Bearer ek_... ``` Reservations auto-expire after 10 minutes of inactivity and release on `sessionEnd`. Manual release: end the owning conversation. ## Conventions [Conventions](/concepts/conventions) are workspace-scoped rules. `must`-tier conventions are enforced by the `preToolUse` hook. Manage them through: * MCP: [`convention_propose`](/mcp/convention-propose), [`convention_list`](/mcp/convention-list). * Dashboard: **Workspace → Conventions**. ## Moving a Workspace to a New Repo If your team renames the repo or migrates hosts (GitHub → GitLab, etc.), the normalized URL usually still matches — the server silently backfills the new URL on the next bind. No action required. If you're moving the workspace to a **different** repo (history and all), use: ```bash theme={null} enagrams workspace migrate-repo ``` Owner/admin only. From within a clone of the *new* repo, this releases the workspace's current binding and rebinds it to the new repo's fingerprint. Atomic in practice: if the new repo is already claimed by another workspace, the migrate aborts and the workspace stays bound to the old repo. For workspaces bound through the **GitHub App** (Dashboard → Connect GitHub), `migrate-repo` is disabled — the CLI would leave the App's `installation_id` pointing at the old repo while webhooks fired against the new one. Disconnect from the dashboard and reconnect the new repo instead. ## Workspace Settings | Setting | Description | | ------------------------------- | ------------------------------------------------------------------------------------------------------- | | `name` | Display name | | `slug` | URL-safe identifier — changing requires updating every `.env` and MCP config | | `default_base_branch` | Branch new workstreams fork from (defaults to `main`) | | `repo_url` / `repo_root_commit` | The current binding. Read-only in `PATCH` — use `POST /workspaces/:id/repo-binding` or the CLI instead. | | GitHub App binding | Managed entirely from the dashboard. Disconnect releases the binding cleanly. | ```bash theme={null} PATCH /workspaces/:id Authorization: Bearer ek_... { "name": "New Name", "default_base_branch": "develop" } ``` # Introduction Source: https://docs.enagrams.com/introduction Shared memory for co-coding teams — so your AI agents never contradict each other # What is Enagrams? Enagrams is a coordination layer for AI coding agents. When multiple developers use parallel coding agents — Cursor, Claude Code, Codex, Windsurf — those agents make independent decisions that diverge: different libraries chosen, conflicting file edits, duplicate implementations. Enagrams gives every agent complete context about what every other agent is doing, so they naturally converge on the same architecture and never step on each other's work. ## The Problem Without coordination, parallel agents create chaos: * **Developer A's agent** picks `zod` for validation * **Developer B's agent** picks `joi` for the same purpose * Both rewrite the same route files simultaneously * You discover the conflict at merge time — hours of rework ## The Solution Enagrams maintains a shared knowledge graph of every architectural decision, a per-workspace symbol graph of every function and class, and a live coordination substrate of active work. Agents reserve files and symbols before editing. A gate blocks "I'm done" handoffs that break tests. Meeting transcripts automatically generate scoped work packages so every agent knows exactly what to build. ```bash theme={null} npx enagrams init ``` One command. Browser auth. Agents connect via the Model Context Protocol. Hooks are installed automatically for Cursor, Claude Code, and Codex. ## How It Works Run `enagrams init`. Browser auth creates or joins a workspace and writes the MCP + hook config for every IDE on the machine. File and symbol reservations prevent simultaneous edits. Workstreams map 1:1 to `ena/` branches so the team shares one working branch per feature. Decisions and conventions are shared across agents. `must`-tier conventions block edits. The test gate blocks "ready" signals that would break dependents. ## Key Features * **24 MCP tools** across six surfaces — coordination (`sync`, `decide`, `search`, `learn`, `ask`, `publish`), workstreams (`workstream_start/join/leave/end/complete/list`), tasks (`delegate_task`, `task_claim/update/list`), conventions (`convention_propose/list`, `decisions_stale`, `decision_reaffirm`), the test gate (`confirm_ready`, `sync_commit`, `sync_retract`), and negotiation (`negotiate_open/respond/list`) * **Automatic file + symbol locking** — IDE hooks reserve files on write and can narrow down to specific functions/classes (`granularity='symbol'`) * **Workstreams** — short-lived feature branches `ena/` that agents jump onto together; any branch matching that pattern puts the session in coordinated mode * **Symbol graph** — per-workspace pgvector index of every top-level function, class, interface, and enum, kept fresh on every agent write * **Test gate (`confirm_ready`)** — computes the affected-test set from the symbol graph, records pass/fail with flake tracking, writes a `sync_log` receipt the watcher uses to fan out commits * **Living decisions + conventions** — decisions auto-link to symbols and go stale when those symbols change; `must`-tier conventions are hard gates on PreToolUse * **Negotiation protocol** — when two agents both want a symbol, they run a bounded-turn state machine (`propose → yield / hold / split / defer → resolved | escalated | expired`) * **Meeting ingestion** — paste a transcript or record live via Whisper; decisions, action items, and file-scoped work packages are auto-extracted * **Real-time dashboard** — Supabase Realtime fans `agent_sessions`, `file_reservations`, `decisions`, `workstreams`, `tasks`, and `sync_log` changes to the web UI and `enagrams watch` subscribers ## Quick Start Install, connect, and start coding with coordinated agents. Deep dive into the coordination model. # ask Source: https://docs.enagrams.com/mcp/ask Ask a question against the workspace knowledge base ## Overview `ask` is a free-form query over the workspace's decisions, learnings, contracts, and recent activity. Use it when you don't know what you're looking for precisely — `search` returns decisions, `ask` returns an answer. ## Parameters | Parameter | Type | Required | Description | | ---------- | ------ | -------- | --------------------------------------- | | `question` | string | Yes | Your question | | `context` | string | No | Additional context to narrow the answer | ## Example ```json theme={null} { "tool": "ask", "arguments": { "question": "What do we use for server-side HTTP calls?", "context": "About to add a new third-party integration" } } ``` ## When to Call `ask` * Exploring a part of the codebase you haven't touched * Checking team conventions you can't find in code * Uncertain whether a pattern has been discussed For structured decision lookup prefer [`search`](/mcp/search). # confirm_ready Source: https://docs.enagrams.com/mcp/confirm-ready Run the affected-test gate before handing off ## Overview `confirm_ready` is Enagrams' test gate. Before you declare your change-set complete, it computes the **affected test set** from the [symbol graph](/concepts/symbol-graph) — tests that transitively depend on the symbols you touched — and asks you to run them. On pass the gate writes a `sync_log` receipt that the auto-sync watcher uses to fan the commit out to other machines. Two phases: 1. **First call, no `test_results`** — gate returns the affected test files + test names. Run them locally. 2. **Second call with `test_results[]`** — gate evaluates. On pass it records a `sync_log` receipt and (if `completes_task_id` was provided) marks the task `done` and cascade-unblocks its dependents. On fail it returns structured `failures[]` including flake status. ## Parameters | Parameter | Type | Required | Description | | ------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------- | | `workstream_slug` | string | No\* | Recommended so the gate can write `sync_log` | | `session_id` | string | No | Your `agent_sessions.id` | | `machine_id` | string | No | Stable machine id for flake tracking | | `touched_files` | string\[] | No | Files you changed | | `touched_symbols` | string\[] | No | `symbol_graph_nodes.id` values you changed | | `commit_sha` | string | No | Commit the tests were run against | | `completes_task_id` | string | No | If set, closes the task and unblocks dependents on pass | | `test_results` | object\[] | No | Omit on first call; on second call each entry is `{test_file,test_name,status,duration_ms?,error_message?,traceback?}` | `workstream_slug` is not strictly required but without it the gate cannot write a `sync_log` receipt, so the auto-sync watcher won't fan the commit out to other machines. ## Example — Phase 1 (discover affected tests) ```json theme={null} { "tool": "confirm_ready", "arguments": { "workstream_slug": "stripe-subscription-upgrades", "session_id": "sess_abc", "touched_files": ["src/billing/stripe.ts", "src/billing/proration.ts"] } } ``` Response: ```json theme={null} { "phase": "discover", "affected_tests": [ { "test_file": "tests/billing.test.ts", "test_names": ["proration covers mid-cycle upgrades"] } ] } ``` ## Example — Phase 2 (submit results) ```json theme={null} { "tool": "confirm_ready", "arguments": { "workstream_slug": "stripe-subscription-upgrades", "session_id": "sess_abc", "machine_id": "mbp-aaron", "commit_sha": "9f3c1a2", "completes_task_id": "task_xyz", "test_results": [ { "test_file": "tests/billing.test.ts", "test_name": "proration covers mid-cycle upgrades", "status": "pass", "duration_ms": 412 } ] } } ``` Response on pass includes `sync_log_id` — pass it to [`sync_commit`](/mcp/sync-commit) to link the gate receipt to the git commit. See [test gate](/concepts/test-gate) for the full model including flake handling. # convention_list Source: https://docs.enagrams.com/mcp/convention-list List active conventions ## Overview Discover the rules that will gate your edits. Call before touching an unfamiliar area; `must`-tier matches will block writes otherwise. ## Parameters | Parameter | Type | Required | Description | | --------- | --------- | -------- | ------------------------------------------------- | | `tier` | string | No | `must`, `should`, or `may` | | `files` | string\[] | No | Limit results to conventions matching these paths | ## Example ```json theme={null} { "tool": "convention_list", "arguments": { "tier": "must", "files": ["src/routes/users.ts"] } } ``` # convention_propose Source: https://docs.enagrams.com/mcp/convention-propose Record a team convention, optionally as a hard edit gate ## Overview Conventions are team rules with enforcement tiers: | Tier | Behavior | | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `must` | Hard gate on PreToolUse — edits to files matching `affected_files` are blocked until the convention is superseded or marked stale. | | `should` | Surfaced as advisory context in `sync`. | | `may` | Recorded for awareness; no enforcement. | Keep `affected_files` narrow — broad `must`-tier conventions create false blocks. ## Parameters | Parameter | Type | Required | Description | | ---------------- | --------- | -------- | --------------------------------------------- | | `title` | string | Yes | Short convention title | | `rationale` | string | No | Why the team adopted it | | `tier` | string | No | `must`, `should`, or `may` (default `should`) | | `affected_files` | string\[] | No | File paths the convention applies to | | `affected_areas` | string\[] | No | Logical areas (e.g. `auth`, `billing`) | | `session_id` | string | No | Your session id | ## Example ```json theme={null} { "tool": "convention_propose", "arguments": { "title": "Use zod for all request body validation", "rationale": "Single source of truth for schemas; prevents the joi/yup/zod triad we ended up with last quarter.", "tier": "must", "affected_files": ["src/routes/**/*.ts"] } } ``` # decide Source: https://docs.enagrams.com/mcp/decide Record an architectural decision for the team ## Overview `decide` records an architectural decision in the shared workspace knowledge graph. The decision is stored with a semantic embedding (pgvector) for later search, auto-linked to the symbols it touches in the [symbol graph](/concepts/symbol-graph), included in future `sync` responses for all agents, and visualized in the Knowledge Graph on the web dashboard. Because decisions link to symbols, when those symbols later change Enagrams marks the decision **stale** — surface them with [`decisions_stale`](/mcp/decisions-stale) and either [`decision_reaffirm`](/mcp/decision-reaffirm) or record a new superseding decision. For hard gates use [`convention_propose`](/mcp/convention-propose) with `tier=must` instead — decisions are informational; `must`-tier conventions block edits until resolved. ## Parameters | Parameter | Type | Required | Description | | ----------- | ------ | -------- | ----------------------------------- | | `title` | string | Yes | Short description of the decision | | `rationale` | string | Yes | Why this decision was made | | `files` | string | No | Comma-separated file paths affected | | `type` | string | No | Decision type (see below) | ### Decision Types | Type | When to Use | | ---------------- | ------------------------------------ | | `architecture` | High-level system design choices | | `approach` | How to implement a specific feature | | `implementation` | Low-level implementation details | | `tradeoff` | Documented tradeoffs | | `convention` | Patterns adopted as team standards | | `bugfix` | Root cause and fix for a notable bug | | `other` | Everything else | ## Response ```json theme={null} { "id": "dec_abc123", "status": "recorded", "title": "Use JWT for authentication", "workspace": "my-startup" } ``` ## Examples ### Architecture decision ```json theme={null} { "tool": "decide", "arguments": { "title": "Use JWT for authentication", "rationale": "Stateless tokens work with our Vercel edge deployment. No session store needed. We use short-lived access tokens (15 min) with refresh tokens.", "files": ["src/auth/login.ts", "src/auth/middleware.ts", "src/auth/refresh.ts"], "type": "architecture" } } ``` ### Rejected alternative ```json theme={null} { "tool": "decide", "arguments": { "title": "Rejected: cookie-based sessions", "rationale": "Would require a session store (Redis) which adds infrastructure complexity. Our edge deployment makes cookie-based sessions harder to coordinate.", "type": "rejection" } } ``` ### Convention ```json theme={null} { "tool": "decide", "arguments": { "title": "All API responses use { data, error } envelope", "rationale": "Consistent shape makes client-side error handling uniform. Error field is null on success, data is null on error.", "files": ["src/middleware/responseWrapper.ts"], "type": "convention" } } ``` ## When to Call `decide` * When choosing a library, framework, or service * When deciding on a data model or API shape * When rejecting an approach (document it so others don't revisit it) * When a pattern is used enough to become a convention * After fixing a non-obvious bug (record the root cause) Decisions are permanent — they're a historical record of how the codebase evolved. Don't worry about over-recording. More decisions mean better context for future agents. # decision_reaffirm Source: https://docs.enagrams.com/mcp/decision-reaffirm Mark stale decisions fresh again ## Overview Mark stale decisions as still valid even though their linked code changed. Use when the decision's rationale still holds and the drift was cosmetic (rename, file move, implementation swap that preserves the contract). For decisions that no longer hold, record a new one (`decide`) that supersedes the old one — do not reaffirm. ## Parameters | Parameter | Type | Required | Description | | -------------- | --------- | -------- | ------------------------ | | `decision_ids` | string\[] | Yes | Decision ids to reaffirm | ## Example ```json theme={null} { "tool": "decision_reaffirm", "arguments": { "decision_ids": ["dec_abc", "dec_def"] } } ``` # decisions_stale Source: https://docs.enagrams.com/mcp/decisions-stale List decisions whose linked code has drifted ## Overview Every decision is auto-linked to the symbols it mentions. When those symbols change, Enagrams marks the decision **stale**. Calling `decisions_stale` surfaces them for review — either [`decision_reaffirm`](/mcp/decision-reaffirm) them or record a new decision that supersedes the old one. ## Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------- | | `limit` | number | No | Max rows | ## Example ```json theme={null} { "tool": "decisions_stale", "arguments": { "limit": 20 } } ``` Each entry includes the decision, the symbols that drifted, and the commit that changed them. # delegate_task Source: https://docs.enagrams.com/mcp/delegate-task Create a task in a workstream, optionally assigned and with dependencies ## Overview Hand off follow-up work instead of ballooning your own change-set. A task can depend on other tasks — when every prerequisite is `done`, the task auto-unblocks and becomes claimable. If you close a delegated task by passing `completes_task_id` to [`confirm_ready`](/mcp/confirm-ready), the gate marks it `done` on test pass and cascade-unblocks dependents for you. ## Parameters | Parameter | Type | Required | Description | | --------------- | --------- | -------- | --------------------------------------------------------- | | `workstream_id` | string | Yes | Workstream this task belongs to | | `title` | string | Yes | Short task title | | `description` | string | No | Details | | `to_session` | string | No | Assignee `agent_sessions.id` (omit to leave in the queue) | | `depends_on` | string\[] | No | Task ids whose completion unblocks this one | | `from_session` | string | No | Your session id | | `context` | object | No | Free-form task payload | ## Example ```json theme={null} { "tool": "delegate_task", "arguments": { "workstream_id": "ws_42", "title": "Add Stripe webhook signature verification", "description": "Validate `stripe-signature` header against the endpoint secret; reject on mismatch.", "to_session": "sess_teammate", "depends_on": ["task_wire-webhook-route"] } } ``` Tasks with non-empty `depends_on` start in `blocked` state. They flip to `todo` when every prerequisite reaches `done`. # learn Source: https://docs.enagrams.com/mcp/learn Share a gotcha, workaround, pattern, or discovery with the team ## Overview `learn` records a piece of tacit knowledge — a non-obvious gotcha, a workaround for a quirk, a pattern the team has converged on, or a discovery from debugging. Learnings are surfaced in future `sync` responses so every agent inherits them. Run `enagrams compile-rules` periodically to bake recent learnings into `.cursor/rules/team-learnings.mdc` so new Cursor sessions load them as project rules automatically. ## Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | --------------------------------------------------------------- | | `insight` | string | Yes | What you learned, in one or two sentences | | `type` | string | No | `gotcha`, `workaround`, `pattern`, `discovery`, or `convention` | | `tags` | string | No | Comma-separated tags | | `files` | string | No | Comma-separated related file paths | ## Example ```json theme={null} { "tool": "learn", "arguments": { "insight": "Stripe test mode rate-limits webhook deliveries differently from live mode — retry backoff must accommodate 30s gaps.", "type": "gotcha", "tags": "stripe, webhooks, testing", "files": "src/billing/webhook.ts" } } ``` ## When to Call `learn` * You just spent 20 minutes figuring out something that wasn't obvious * You found a workaround for a library quirk * You discovered that a team pattern exists but wasn't documented * You fixed a bug whose root cause would trip up the next agent Decisions are permanent architectural choices; learnings are field-notes. Record both. # negotiate_list Source: https://docs.enagrams.com/mcp/negotiate-list List negotiations you participate in ## Overview Find negotiations waiting on your response. Useful to call at session start after a long absence. ## Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------ | | `session_id` | string | No | Filter to negotiations for this session | | `status` | string | No | `open`, `resolved`, `escalated`, `expired` | | `limit` | number | No | Max rows | ## Example ```json theme={null} { "tool": "negotiate_list", "arguments": { "session_id": "sess_me", "status": "open" } } ``` # negotiate_open Source: https://docs.enagrams.com/mcp/negotiate-open Open a bounded-turn negotiation over contested symbols ## Overview When you want to edit symbols currently reserved by another agent, open a negotiation rather than blocking. Negotiations run a state machine with bounded turns and a deadline — if the counterparty doesn't respond by the deadline, the negotiation auto-resolves in your favor. Hitting `max_turns` without a terminal action escalates to a human. ## Parameters | Parameter | Type | Required | Description | | ---------------------- | --------- | -------- | ---------------------------------------- | | `initiator_session` | string | Yes | Your `agent_sessions.id` | | `counterparty_session` | string | Yes | The session currently holding the symbol | | `file` | string | Yes | File path in question | | `workstream_id` | string | No | Workstream id (if applicable) | | `symbol_ids` | string\[] | No | Specific `symbol_graph_nodes.id` values | | `symbol_names` | string\[] | No | Symbol names (when ids aren't known) | | `rationale` | string | No | Why you need the symbols | | `max_turns` | number | No | Turn cap before escalation | | `deadline_ms` | number | No | Response deadline (default 5 minutes) | ## Example ```json theme={null} { "tool": "negotiate_open", "arguments": { "initiator_session": "sess_me", "counterparty_session": "sess_them", "file": "src/billing/stripe.ts", "symbol_names": ["createSubscription"], "rationale": "Need to add proration branch before your refactor ships." } } ``` The counterparty responds via [`negotiate_respond`](/mcp/negotiate-respond). # negotiate_respond Source: https://docs.enagrams.com/mcp/negotiate-respond Respond to a negotiation: yield, hold, defer, counter, or split ## Overview Respond to a negotiation opened against you (or follow up on one you opened). ## Actions | Action | Effect | | --------- | --------------------------------------------------------------------------------- | | `yield` | You release the symbol(s). The initiator proceeds. Terminal. | | `hold` | You keep the symbol(s). Initiator must route around or escalate. Terminal. | | `defer` | Ask the initiator to wait `defer_ms` milliseconds, then re-propose. | | `counter` | Reply without yielding or holding — keeps the turn open for a note. | | `split` | Partition symbols: `split={initiator_symbols:[...], counterparty_symbols:[...]}`. | ## Parameters | Parameter | Type | Required | Description | | ---------------- | ------ | -------- | ---------------------------- | | `negotiation_id` | string | Yes | Id from `negotiate_open` | | `from_session` | string | Yes | Your session id | | `action` | string | Yes | One of the actions above | | `note` | string | No | Free-form comment | | `defer_ms` | number | No | Required when `action=defer` | | `split` | object | No | Required when `action=split` | ## Example ```json theme={null} { "tool": "negotiate_respond", "arguments": { "negotiation_id": "neg_abc", "from_session": "sess_them", "action": "yield", "note": "Go ahead — I'll rebase onto your change." } } ``` # Overview Source: https://docs.enagrams.com/mcp/overview The 26 MCP tools your agents use to coordinate ## What is MCP? The [Model Context Protocol](https://modelcontextprotocol.io) is an open standard that lets AI agents call structured tools. Enagrams ships an MCP server bundled with the `enagrams` npm package. Every tool below is callable from any MCP-compatible client (Cursor, Claude Code, Codex, Windsurf, custom). ## Install `npx enagrams init` writes the MCP config for every supported IDE automatically. If you're setting up by hand: ```json theme={null} { "mcpServers": { "enagrams": { "command": "npx", "args": ["-y", "enagrams", "--mcp"], "env": { "ENAGRAMS_API_KEY": "...", "ENAGRAMS_WORKSPACE": "your-workspace-slug" } } } } ``` The server speaks stdio to your IDE and HTTP to the Enagrams API. ## Tools by Category ### Coordination and Context Core tools every agent calls several times a session. | Tool | Purpose | | ----------------------- | ----------------------------------------------------------------------------------------------------------------- | | [`sync`](/mcp/sync) | Report what you're working on; get teammate activity, decisions, conflicts, and learnings back in one round-trip. | | [`decide`](/mcp/decide) | Record an architectural choice (with rationale) so every other agent sees it. | | [`search`](/mcp/search) | Semantic search over past decisions. | | [`learn`](/mcp/learn) | Share a gotcha, workaround, or pattern you just discovered. | | [`ask`](/mcp/ask) | Ask a question against the workspace knowledge base. | ### Contracts Interface contracts let one agent publish a shape — API response, type definition, function signature — and other agents query it. | Tool | Purpose | | ------------------------- | ------------------------------------- | | [`publish`](/mcp/publish) | Register or update a shared contract. | Contract changes surface in everyone's next `sync`. Use `ask` (free-form) or `search` to discover existing contracts. ### Workstreams Workstreams map 1:1 to shared git branches `ena/` and enable symbol-level coordination with other participants. | Tool | Purpose | | ------------------------------------------------- | -------------------------------------------------------------------- | | [`workstream_start`](/mcp/workstream-start) | Create a workstream and its branch. | | [`workstream_join`](/mcp/workstream-join) | Join an existing workstream. | | [`workstream_leave`](/mcp/workstream-leave) | Leave without ending it for others. | | [`workstream_end`](/mcp/workstream-end) | Abandon a workstream for the whole team. | | [`workstream_complete`](/mcp/workstream-complete) | Wrap up — returns a squash-merge plan, then finalizes with `pr_url`. | | [`workstream_list`](/mcp/workstream-list) | List active or completed workstreams. | ### The Test Gate Gate changes before they land on a workstream. Two-phase: first call returns affected tests, second call submits results. | Tool | Purpose | | ------------------------------------- | ----------------------------------------------------------------------------------- | | [`confirm_ready`](/mcp/confirm-ready) | Tier-A/B gate. On pass writes a `sync_log` receipt. | | [`sync_commit`](/mcp/sync-commit) | Record that you pushed a commit. Prefer passing `sync_log_id` from `confirm_ready`. | | [`sync_retract`](/mcp/sync-retract) | Retract a previously synced commit so watchers revert locally. | ### Negotiation When two agents want the same symbol, `negotiate_*` runs a bounded-turn state machine with auto-resolution. | Tool | Purpose | | --------------------------------------------- | ------------------------------------------------ | | [`negotiate_open`](/mcp/negotiate-open) | Open a negotiation over contested symbols. | | [`negotiate_respond`](/mcp/negotiate-respond) | Respond: yield / hold / defer / counter / split. | | [`negotiate_list`](/mcp/negotiate-list) | See whether anyone is waiting on you. | ### Tasks Delegate follow-up work inside a workstream with dependency-based auto-unblocking. | Tool | Purpose | | ------------------------------------- | --------------------------------------------------- | | [`delegate_task`](/mcp/delegate-task) | Create (and optionally assign) a task. | | [`task_claim`](/mcp/task-claim) | Claim a queued task. | | [`task_update`](/mcp/task-update) | Update status (triggers unblock cascade on `done`). | | [`task_list`](/mcp/task-list) | List tasks by workstream, assignee, or status. | ### Conventions and Decision Staleness Living conventions layer on top of decisions. `must`-tier conventions are hard gates on PreToolUse. Stale decisions get surfaced for review when their linked code drifts. | Tool | Purpose | | ----------------------------------------------- | ------------------------------------------------------- | | [`convention_propose`](/mcp/convention-propose) | Record a `must`/`should`/`may` convention. | | [`convention_list`](/mcp/convention-list) | List conventions, optionally filtered by tier or files. | | [`decisions_stale`](/mcp/decisions-stale) | List decisions whose linked code has drifted. | | [`decision_reaffirm`](/mcp/decision-reaffirm) | Mark stale decisions fresh again. | ## How Agents Use Them A typical session looks like: ``` sessionStart (hook) → briefing injected sync → get team state workstream_start/join → claim branch decide / publish → record new choices / contracts [edit files] → hook calls file_lock on PreToolUse convention_list (on block) → understand why an edit was denied delegate_task → hand off follow-up to another agent confirm_ready (no results) → get affected tests [run tests locally] confirm_ready (results) → gate pass → sync_log receipt sync_commit → notify watchers of the new commit sessionEnd (hook) → release reservations ``` Most tools accept an optional `session_id`; the MCP server also automatically scopes each call to your workspace. # publish Source: https://docs.enagrams.com/mcp/publish Register or update a shared interface contract ## Overview `publish` records a contract — an API response shape, a type definition, a function signature, or any interface that other agents depend on. Other agents see the contract in their next `sync` and get notified on changes. Publishing a contract is how you tell the rest of the team "this is the shape I'm about to implement, please target it." ## Parameters | Parameter | Type | Required | Description | | ------------- | ------ | -------- | ------------------------------------------------------------------- | | `name` | string | Yes | Contract name (e.g. `GET /api/scrape response`, `UserProfile type`) | | `schema` | string | Yes | Contract definition as a JSON string | | `description` | string | No | Human-readable description | ## Example ```json theme={null} { "tool": "publish", "arguments": { "name": "GET /api/users/:id response", "schema": "{\"id\":\"string\",\"email\":\"string\",\"role\":\"'admin'|'member'\",\"createdAt\":\"string (ISO)\"}", "description": "Returned by the user detail endpoint. Role is a string literal, not enum." } } ``` ## When to Call `publish` * Before implementing a new API endpoint — agree the shape first * When adding a shared type other services will consume * When changing an existing contract (re-publish with the same `name`) Re-publishing the same `name` replaces the prior contract and surfaces a change notification in every agent's next `sync`. # search Source: https://docs.enagrams.com/mcp/search Semantic search over past architectural decisions ## Overview `search` runs a semantic vector search over all decisions in the workspace. It finds decisions by meaning — not just keyword matching — so "how do we handle auth?" returns JWT decisions even if "authentication" wasn't the exact word used. Decisions are embedded with OpenAI's `text-embedding-3-small` (1536 dimensions) and indexed in pgvector for fast approximate nearest-neighbor search. ## Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------- | | `query` | string | Yes | Natural language search query | | `limit` | number | No | Max results to return (default: 10) | ## Response ```json theme={null} { "results": [ { "id": "dec_abc", "title": "Use JWT for authentication", "rationale": "Stateless tokens work with our edge deployment. No session store needed.", "type": "architecture", "made_by": "Developer A", "created_at": "2026-04-13T10:00:00Z", "files": ["src/auth/login.ts", "src/auth/middleware.ts"], "similarity": 0.92 }, { "id": "dec_def", "title": "Rejected: cookie-based sessions", "rationale": "Adds Redis infrastructure complexity.", "type": "rejection", "similarity": 0.87 } ] } ``` ### Response Fields | Field | Description | | ------------ | ------------------------------------------------------ | | `id` | Decision ID | | `title` | Short description | | `rationale` | Full rationale text | | `type` | Decision type | | `made_by` | User who recorded the decision | | `created_at` | ISO timestamp | | `files` | Affected files | | `similarity` | Cosine similarity score (0–1, higher is more relevant) | ## Examples ### Find auth decisions ```json theme={null} { "tool": "search", "arguments": { "query": "authentication and session management", "limit": 5 } } ``` ### Find database choices ```json theme={null} { "tool": "search", "arguments": { "query": "database ORM query layer", "limit": 3 } } ``` ### Check for existing patterns ```json theme={null} { "tool": "search", "arguments": { "query": "error handling API responses" } } ``` ## When to Call `search` * Before implementing something — "did we already decide on this?" * When unfamiliar with part of the codebase — "what's the rationale for how this works?" * Before picking a library — "have we already adopted something for this use case?" * When debugging — "has anyone dealt with this type of issue before?" Use natural language queries. The semantic search understands intent — "how do we validate user input?" will find Zod decisions even if you don't mention Zod. # sync Source: https://docs.enagrams.com/mcp/sync Report agent activity and receive full workspace context in one round-trip ## Overview `sync` is the primary context-loading tool. It reports what the agent is currently working on and returns the full workspace state in a single call — agents, decisions, conflicts, file reservations, work packages, and branch directives. Agents typically call `sync` at the start of a task and when they need a context refresh mid-session. Session management (creation, heartbeat, termination) is handled automatically by IDE hooks — `sync` is for explicit context requests. ## Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------------------------------------------------- | | `task` | string | **Yes** | Brief description of the current task | | `session_id` | string | No | Session ID from a previous `sync` response — enables per-tab session tracking | | `files` | string | No | Comma-separated file paths the agent currently plans to work on | | `branch` | string | No | Current git branch | | `agent_type` | string | No | One of `cursor`, `claude_code`, `windsurf`, `copilot`, `custom` | | `plan` | string | No | JSON string: `{ goal, approach, patterns_needed, files_planned, dependencies }` | ## Response ```json theme={null} { "session_id": "sess_abc123", "agents": [ { "user": "Developer B", "task": "Build JWT auth flow", "branch": "feat/auth", "agent_type": "cursor", "files": ["src/auth/login.ts", "src/auth/middleware.ts"] } ], "decisions": [ { "id": "dec_xyz", "title": "Use JWT for authentication", "rationale": "Stateless, works with edge deployment", "type": "architecture", "made_by": "Developer B", "created_at": "2026-04-13T10:00:00Z", "files": ["src/auth/login.ts"] } ], "conflicts": [ { "type": "file_overlap", "severity": "conflict", "file": "src/auth/login.ts", "other_agent": "Developer B", "other_task": "Build JWT auth flow" } ], "reserved_files": [ { "file": "src/auth/login.ts", "owned_by": "Developer B", "expires_in_minutes": 7 } ], "your_work": [ { "id": "pkg_def", "title": "Build user registration", "files": ["src/users/register.ts"], "decisions": [] } ], "branch_directive": { "active_branch": "feat/auth", "set_by": "Developer B", "pull_required": false }, "meeting_context": { "summary": "Agreed to use JWT for auth and bcrypt for passwords. Registration due Friday." } } ``` ### Response Fields | Field | Description | | ------------------ | ------------------------------------------------------------------------------------------- | | `session_id` | Pass back on the next `sync` call to maintain session continuity | | `agents` | Other active agents in the workspace | | `decisions` | Recent architectural decisions | | `conflicts` | File overlaps and proximity warnings | | `reserved_files` | Files locked by other agents | | `your_work` | Work packages claimed by this user | | `branch_directive` | Current team branch and whether a pull is needed | | `meeting_context` | Summary from the most recent meeting | | `learnings` | Team learnings surfaced since your last sync | | `contracts` | Shared interface contracts other agents have published | | `stale_decisions` | Decisions whose linked symbols have drifted | | `conventions` | Active conventions that may gate your edits (see [`convention_list`](/mcp/convention-list)) | ## Example ```json theme={null} { "tool": "sync", "arguments": { "session_id": "sess_abc123", "task": "Add user registration endpoint", "files": ["src/users/register.ts", "src/users/validate.ts"], "branch": "feat/auth" } } ``` ## When to Call `sync` * **Start of a task** — get current team context before writing any code * **Before touching a new area** — check for conflicts and existing decisions * **After a long pause** — refresh context if the session has been idle * **When the agent mentions uncertainty** — "I'm not sure how the team handles auth" → call `sync` Pass the `session_id` from each response back into the next call. This ensures your activity is tracked against the same session rather than creating a new one each time. # sync_commit Source: https://docs.enagrams.com/mcp/sync-commit Record that you pushed a commit to the workstream branch ## Overview `sync_commit` notifies the auto-sync watcher that a new commit is available on the workstream branch. Other participants' `enagrams watch` processes pick it up and pull. Prefer passing `sync_log_id` from a passing [`confirm_ready`](/mcp/confirm-ready) response — it links the gate receipt to the commit so the watcher only fans out gated changes. ## Parameters | Parameter | Type | Required | Description | | ----------------- | --------- | -------- | --------------------------------------- | | `commit_sha` | string | Yes | Git commit sha | | `sync_log_id` | string | No | Row id from `confirm_ready` (preferred) | | `workstream_slug` | string | No\* | Required when `sync_log_id` is absent | | `session_id` | string | No\* | Required when `sync_log_id` is absent | | `files` | string\[] | No | Files the commit touched | | `symbols_changed` | string\[] | No | `symbol_graph_nodes.id` values touched | ## Example ```json theme={null} { "tool": "sync_commit", "arguments": { "sync_log_id": "sl_7f3a", "commit_sha": "9f3c1a2", "files": ["src/billing/stripe.ts"] } } ``` # sync_retract Source: https://docs.enagrams.com/mcp/sync-retract Retract a previously synced commit so watchers revert locally ## Overview Retract a commit you had previously announced with [`sync_commit`](/mcp/sync-commit). Watchers on other machines see the retraction and revert locally. Use when a just-pushed commit turns out to be broken in a way the gate didn't catch. ## Parameters | Parameter | Type | Required | Description | | --------------------- | ------ | -------- | ----------------------------------------------- | | `sync_log_id` | string | Yes | Row id of the commit receipt to retract | | `retracted_by_log_id` | string | No | The newer receipt that supersedes it (optional) | ## Example ```json theme={null} { "tool": "sync_retract", "arguments": { "sync_log_id": "sl_7f3a" } } ``` # task_claim Source: https://docs.enagrams.com/mcp/task-claim Claim an unassigned or queued task ## Overview Move a task from `todo` to `in_progress` and assign it to yourself. Fails if the task is still blocked on prerequisites or already claimed by another session. ## Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | --------------- | | `task_id` | string | Yes | Task id | | `session_id` | string | Yes | Your session id | ## Example ```json theme={null} { "tool": "task_claim", "arguments": { "task_id": "task_xyz", "session_id": "sess_me" } } ``` # task_list Source: https://docs.enagrams.com/mcp/task-list List tasks filtered by workstream, assignee, or status ## Overview Find something to claim, check what's blocking completion, or audit workstream progress. ## Parameters | Parameter | Type | Required | Description | | ------------------ | ------ | -------- | ----------------------------------------------------- | | `workstream_id` | string | No | Filter to one workstream | | `assignee_session` | string | No | Filter to a specific session's assigned tasks | | `status` | string | No | `todo`, `in_progress`, `blocked`, `done`, `cancelled` | | `limit` | number | No | Max rows | ## Example ```json theme={null} { "tool": "task_list", "arguments": { "workstream_id": "ws_42", "status": "todo" } } ``` # task_update Source: https://docs.enagrams.com/mcp/task-update Update a task's status ## Overview Transition a task. Setting `status=done` triggers the unblock cascade on dependents. ## Status values `todo` · `in_progress` · `blocked` · `done` · `cancelled` ## Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------- | | `task_id` | string | Yes | Task id | | `status` | string | Yes | New status | | `note` | string | No | Free-form note recorded on the transition | ## Example ```json theme={null} { "tool": "task_update", "arguments": { "task_id": "task_xyz", "status": "done" } } ``` Prefer closing tasks via [`confirm_ready`](/mcp/confirm-ready) with `completes_task_id` so the transition is gated by tests. # workstream_complete Source: https://docs.enagrams.com/mcp/workstream-complete Wrap up a workstream and finalize after merge ## Overview Two-phase completion: 1. **Plan phase** — call without `pr_url`. Returns a squash-merge plan: suggested title, markdown PR body, and literal `git` + `gh` commands to run locally. 2. **Finalize phase** — after you open and merge the PR, call again with `pr_url`. Status flips to `completed` and participants' sessions are notified. If the workstream still has open tasks the response includes `summary.has_blockers=true`. Resolve those (or cancel them) before finalizing. ## Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | -------------------------------------------- | | `slug` | string | Yes | Workstream slug | | `pr_url` | string | No | Once the PR exists, pass its URL to finalize | | `session_id` | string | No | Your current session id | ## Plan-phase example ```json theme={null} { "tool": "workstream_complete", "arguments": { "slug": "stripe-subscription-upgrades" } } ``` Response (abridged): ```json theme={null} { "plan": { "suggested_title": "feat(billing): stripe subscription upgrades", "pr_body_md": "## Summary\n- ...\n\n## Test plan\n- ...", "commands": [ "git checkout main && git pull", "git merge --squash ena/stripe-subscription-upgrades", "git commit -m 'feat(billing): stripe subscription upgrades'", "gh pr create --base main ..." ] }, "summary": { "has_blockers": false, "open_tasks": 0 } } ``` ## Finalize example ```json theme={null} { "tool": "workstream_complete", "arguments": { "slug": "stripe-subscription-upgrades", "pr_url": "https://github.com/acme/app/pull/417" } } ``` # workstream_end Source: https://docs.enagrams.com/mcp/workstream-end Abandon a workstream for the whole team ## Overview Abandon a workstream without landing it. Use when the feature is scrapped. For normal completion call [`workstream_complete`](/mcp/workstream-complete) instead. ## Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ------------------------------------- | | `slug` | string | Yes | Workstream slug | | `reason` | string | No | Why the workstream is being abandoned | | `session_id` | string | No | Your current session id | ## Example ```json theme={null} { "tool": "workstream_end", "arguments": { "slug": "stripe-subscription-upgrades", "reason": "Blocked on Stripe account verification; revisiting Q3." } } ``` # workstream_join Source: https://docs.enagrams.com/mcp/workstream-join Join an existing workstream ## Overview Join a workstream that another agent started. Your session is moved onto `ena/` and symbol-level coordination with the other participants is enabled — file reservations can be narrowed to specific symbols and negotiations can open over contested ones. ## Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ----------------------- | | `slug` | string | Yes | Workstream slug | | `session_id` | string | No | Your current session id | ## Example ```json theme={null} { "tool": "workstream_join", "arguments": { "slug": "stripe-subscription-upgrades" } } ``` Use [`workstream_list`](/mcp/workstream-list) first to find the slug. # workstream_leave Source: https://docs.enagrams.com/mcp/workstream-leave Leave a workstream without ending it for others ## Overview Leave a workstream you previously joined. The workstream continues for other participants — use [`workstream_end`](/mcp/workstream-end) if you want to abandon it for the whole team. ## Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ----------------------- | | `slug` | string | Yes | Workstream slug | | `session_id` | string | No | Your current session id | ## Example ```json theme={null} { "tool": "workstream_leave", "arguments": { "slug": "stripe-subscription-upgrades" } } ``` # workstream_list Source: https://docs.enagrams.com/mcp/workstream-list List workstreams in the workspace ## Overview List workstreams so you can find one to join or see what's in flight before starting a new one. ## Parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------------- | | `status` | string | No | `active` (default), `completed`, or `abandoned` | ## Example ```json theme={null} { "tool": "workstream_list", "arguments": { "status": "active" } } ``` Each entry includes `slug`, `title`, `branch` (`ena/`), `participants`, `created_at`, and task counts. # workstream_start Source: https://docs.enagrams.com/mcp/workstream-start Create a workstream and its shared git branch ## Overview A workstream is a scoped unit of work mapped 1:1 to a git branch named `ena/`. Starting a workstream tells every other agent in the workspace that this feature is in flight, and enables symbol-level coordination with anyone who joins it. When any agent is on a branch matching `ena/`, Enagrams puts the session in coordinated mode automatically. ## Parameters | Parameter | Type | Required | Description | | ------------- | ------ | -------- | --------------------------------------------- | | `title` | string | Yes | Short title for the workstream | | `slug` | string | No | Explicit slug (derived from title if omitted) | | `description` | string | No | Longer description | | `base_branch` | string | No | Git base branch (default `main`) | | `visibility` | string | No | `team` (default) or `private` | | `session_id` | string | No | Your current session id | ## Response Returns the created workstream including `slug` and `branch` (`ena/`). Check out the branch locally and keep working. ## Example ```json theme={null} { "tool": "workstream_start", "arguments": { "title": "Stripe subscription upgrades", "description": "Let existing subscribers change plans mid-cycle with proration.", "base_branch": "main" } } ``` See [workstreams](/concepts/workstreams) for the full lifecycle.