atriumatrium

Sigils & auto-injection

How `+skill` and `++agent` shorthand becomes injected content at submit time.

atrium ships two prompt-time shorthands that any in-pane agent (or human) can drop into a message:

  • +skill-name — auto-injects a skill's body into the current turn.
  • ++agent-slug — auto-injects a named agent's full body (system prompt + every attached skill).
Both forms are scanned by the adapter's UserPromptSubmit hook when you press Enter. The resolved bodies become additionalContext for the model's current turn, and a one-line systemMessage confirms what loaded.

+skill-name

The bare form auto-resolves against the skills registry using a caller-aware priority order:

  1. The caller's own harness, project scope.
  2. The caller's own harness, user scope.
  3. atrium-project (cross-harness, this workspace).
  4. atrium-user (cross-harness, global).
  5. Every other installed harness, in your configured adapter order.
  6. vercel-labs/skills.
The first match wins. If you want to skip the priority and pin a specific source, use +skill-name@scope — for example +atrium@harness-claude-code to pull the canonical atrium skill from Claude Code's directory, or +code-review@atrium-project to force the workspace-scoped variant. Available scope tokens: atrium-user, atrium-project, harness-<adapter>, harness-project-<adapter>, vercel-labs-skills.

Sigils inside fenced code blocks ( ` ) or inline backtick spans are ignored, so prose like "use +atrium in a code review" doesn't trigger resolution.

What the user sees

After submit, the agent's pane shows a status line:

↻ loaded `atrium` from Claude Code adapter — Interact with the atrium workspace

Multiple resolved sigils stack into a bulleted block. Failures surface as ⚠ unresolved: +typo, +missing-skill on a second line — the prompt still goes through, the model just doesn't get any extra context for the missing entries.

++agent-slug

++ is the agent shorthand. Agent slugs are globally unique within an atrium install, so there's no @scope form. When you submit ++qa explain the failing test, the hook:

  1. Looks up the agent definition by slug.
  2. Builds an expanded body from its system prompt + every attached skill, partitioned by mode (see below).
  3. Injects the body as additionalContext for the current turn.
  4. Renders the systemMessage confirmation: ↻ loaded \qa\ from atrium agent.
The user's actual prompt text follows the injected body unchanged. The first run of an agent feels like asking a freshly-briefed teammate; subsequent runs in the same conversation don't reload — the system prompt is already in the agent's context.

Skill modes

A skill attached to an agent has one of three modes:

  • always-loaded — the full skill body is inlined into the agent's system prompt at launch. Counts against the model's context budget; use sparingly for short, high-value skills.
  • discovery — only a one-line bullet (name, description, and the exact atrium skills load … command to fetch the body) goes into the system prompt. The model reads the bullet, decides whether the skill is relevant for the current turn, and pulls the full body on demand by running the command itself.
  • disabled — the skill is attached to the agent but contributes nothing. Useful for staging a skill before you trust it enough to flip on.
Toggle the mode per skill in the agent editor's attached-skills list. Discovery is the default — it keeps the initial system prompt small and pays for skill bodies only when the agent actually needs them.

The expanded body the agent receives on a ++slug injection looks like this:

You are a QA engineer reviewing automated test runs…

# Always-loaded skills (1)

--- BEGIN SKILL: atrium (atrium-user) ---
…full SKILL.md body…
--- END SKILL: atrium (atrium-user) ---

# Discovery-mode skills (2) — load on demand

- code-review (harness-claude-code) — Review a PR against project conventions
  $ATRIUM_CLI_PATH skills load code-review --provenance harness-claude-code
- pipeline-report (atrium-user) — Generate weekly pipeline analysis
  $ATRIUM_CLI_PATH skills load pipeline-report --provenance atrium-user

Always-loaded sections are delimited with --- BEGIN/END SKILL --- markers so the model can tell where each runbook starts and ends. Discovery rows give the model everything it needs to fetch the body when the turn actually warrants it.

Autocomplete pickers

Sigils are also driven by an inline autocomplete dropdown that opens when you type the trigger character — same UX in MDXEditor composers (the agent / task composers) and in xterm terminal panes hosting an agent.

Active triggers:

  • + — skills picker. Filters the registry as you type. Rows group by adapter applicability (a code-review skill that exists under multiple harnesses — say Claude Code, Codex, and Gemini — collapses to a single row with an adapter facepile header). vercel-labs/skills appears as its own group.
  • ++ — agents picker. Filters named agents by name and slug.
  • ATR-<number> — tasks picker. Filters task cards across the active workspace.
  • pane: / note: / room: / workspace: — entity pickers. Search the named workspace object by title and insert a stable reference to it, each row shown with its workspace › room breadcrumb. Unlike a skill or agent sigil, these don't inject a body — they drop a durable handle the agent (or you) can resolve later.
Every picker has a Create row pinned at the top — + Create "myhelp" skill (or + New skill if the query is empty) jumps directly to Stage 2 of the scaffold modal with the query pre-filled, skipping scope choice. The agents equivalent opens the agent editor pre-filled with the typed name.

The dropdown keeps a small contract:

  • Arrow keys navigate. Enter or Tab commits the selection. Esc dismisses; re-typing the exact same fragment doesn't reopen (extend or shorten the query to reopen).
  • Mouse hover wins over keyboard selection while the cursor is moving — keyboard nav reclaims focus the moment you press an arrow key.
  • Auto-flips above the caret when there's no room below; stays attached to the caret rect when scrolling.
  • Browser-pane click-through is paused while the dropdown is open (the overlay-suspend bridge) so picking a row doesn't get eaten by a CEF surface underneath.
The picker is just a UI shortcut for the same +name / ++slug text the hook would resolve — it inserts the literal sigil into the composer or PTY input, then the rest of this page's pipeline takes over at submit time.

How injection happens

Auto-injection is driven by the adapter's UserPromptSubmit hook (or its equivalent). atrium-adapters wires this on install:

  • Claude Code, Codex, Gemini CLI, Antigravity, OpenCode, Pi.dev — the hook calls atrium skills resolve-prompt-sigils --pane-id $ATRIUM_PANE_ID --adapter <name>, reads the harness's prompt payload from stdin, and writes a hook-response envelope to stdout. The envelope's hookSpecificOutput.additionalContext becomes the model's extra turn context.
  • Cursor Agent — Cursor has no equivalent same-turn injection primitive, so the canonical atrium skill (installed into every adapter's tool-native skill directory) tells the agent to recognize +name / ++slug patterns and fall back to running atrium skills load <name> / atrium agent definition load <slug> from the Bash tool itself.
When atrium isn't running (the resolver TCP port isn't reachable), the hook exits 0 with empty stdout — sigils stay in the prompt as plain text, and submission isn't blocked. Hooks must never gate input on a background service.

Seeing what was injected

Sigils aren't the only thing atrium feeds an agent — it can also surface ambient context like which background commands are already running, so the agent doesn't relaunch a server that's up. Every agent pane carries a context chip answering "what did my agent get told": click it for a popover that lists each injected piece with what triggered it, plus per-source on/off controls.

How much atrium volunteers is governed by an awareness level in settings:

  • Off — inject nothing ambient; only what you explicitly sigil.
  • Minimal — a tight, high-signal context bundle.
  • Full — the broadest ambient context atrium can assemble.

CLI fallback

Both halves of the system are reachable from the CLI for scripts, Bash-tool agents, and anyone outside the hook path:

# Load a skill body to stdout (frontmatter stripped).
"$ATRIUM_CLI_PATH" skills load atrium

# Pin a specific scope when the name is ambiguous.
"$ATRIUM_CLI_PATH" skills load code-review --provenance harness-claude-code

# Load an agent's expanded body (prompt + attached skill bodies).
"$ATRIUM_CLI_PATH" agent definition load qa

atrium skills load is what the discovery-mode bullet lines tell the model to run. atrium agent definition load (alias atrium agent def load) is what the Cursor fallback (or any non-hook environment) uses to expand a ++slug mention.

Composite ids

Some surfaces (configuration files, telemetry, the Copy composite id menu action) need to address a skill by its full {scope}:{name} identifier. Examples:

  • atrium-user:atrium
  • atrium-project:code-review
  • harness-claude-code:pipeline-report
  • harness-project-codex:incident-postmortem
  • vercel-labs-skills:typescript-pr-review
Composite ids are what gets stored when you attach a skill to an agent, so an agent keeps working even if a same-named skill later appears under a different source.