agentic-sage
v1.3.1
Published
Passive fleet judge for parallel AI coding agent sessions (Claude Code, Grok Build CLI, etc.) — board, territory, merge briefings, optional live-judge briefs.
Maintainers
Readme
Session Awareness & Guidance Engine: a passive, read-only fleet judge for running many parallel agent coding sessions (Claude Code, Grok Build CLI, etc.). It does no work, spawns nothing, edits nothing — it watches every session, holds each one's self-declared truth (time-aware), and answers two questions cheaply:
- Who is doing what — and how stale is that knowledge?
- Why did these branches diverge / am I about to collide with another session?
One judge per repo. Zero dependencies, Node ≥ 20, node --test.
Two layers — CLI facts + live judge
SAGE is a session-communication system, not only a CLI status dump. Two layers stack:
| Layer | What you get | When |
|-------|----------------|------|
| 1 · Sensor (always after install + sage on) | Deterministic facts: board, war, territory, merge-brief, why-diverged, claims | Every worker session can call the CLI / skills — no extra pane |
| 2 · Live judge (optional, NEW) | A passive agent session that watches the fleet and publishes short briefs (narrative + per-audience advice) into the same store workers already read | One command: sage judge run |
Convention (easy default):
install + sage on → sessions already coordinate via CLI (layer 1)
sage judge run → same desk, but CLI answers get a living judge on top (layer 2)Workers still treat CLI contested/clear as authority. Briefs layer after facts when fresh (including a short grace window after the judge pane exits). No second protocol to learn — sage territory / merge-brief just get smarter when a judge is live.
# Layer 1 only — already enough for collision awareness
sage board
sage territory 'src/**'
# Layer 2 — one passive watcher pane for the whole desk (or this repo)
sage judge run # auto scope + harness (grok → claude → fact-only)
# sage judge run --fleet # force desk-wide
# sage judge run --harness none # fact-only keeper, no LLMRecipe: Live judge · skill sage-judge · concept Fleet judge.
Quickstart
npm install -g agentic-sage
sage init # interactive wizard — 4 questions, safe defaults: global + OFF
sage on # opt in (skip if you enabled during the wizard)
sage doctor # ✓/✗ per check
# optional elevation:
sage judge run # living passive judge for this desk/reposage init asks scope (global vs this-project-only), harness, storage, and
enable now — defaulting to global + built-in storage + OFF at every step. No TTY
(CI/agents/piped)? Same defaults apply with no prompts — see sage init --global/--project
flags in AGENTS.md. Check the resolved wiring any time with sage where
(this repo) or sage init --show (full breakdown).
Then paste templates/CLAUDE.snippet.md into your repo/user
CLAUDE.md (or use templates/GROK.snippet.md in AGENTS.md for Grok-native) and run /sage-doctor to verify. Full walkthrough — optional tiers + the exact
config we run ourselves — in SETUP.md. Grok users get the same value; native hooks via ~/.grok/hooks (or rely on Claude compat which is on by default).
Prefer to let your agent do it? Install, then tell your coding agent "set up agentic-sage for this
repo." It reads AGENTS.md — the deterministic setup runbook — and walks the
install → enable → wire → (optional) adapter → verify steps for you. Fully reversible:
node uninstall/uninstall.mjs (see uninstall/).
Why — keep the human at fleet altitude
Every popular multi-agent harness scales by removing the human: a queen/PM agent drives workers, replans, auto-confirms. SAGE scales by keeping the human — at the right altitude.
A single agent session is already a harness for thousands of sub-agents; the human can no longer supervise tasks. But independent autonomous sessions still collide — two edit the same config, a migration touches every collection, a shared component diverges. That arbitration is the irreducible human moment, and it's the real time sink.
SAGE is a passive advisor at the fleet layer, not the task layer. It never spawns, never drives, never types into a pane (unless you opt in). It makes human-as-orchestrator tractable at scale — human-in-the-loop at the fleet altitude. That inversion is the project's reason to exist.
Universal core vs your project
SAGE has one boundary, and everything in these docs hangs off it:
| | Universal core (any repo, zero config) | Your project (optional) |
|---|---|---|
| Reads | git (worktree/branch/HEAD/diff --numstat), tmux, the session registry, a generic handoff sidecar | your repo's backlog rows, program/phase notes, architectural-zone glob ownership |
| Gives | board, liveness, territory, why-diverged, merge-brief, the guard | named rows/zones in all of the above |
| How | install + sage on — nothing else required | an adapter (ownsZone/claimedWork/backlogRows/generatedGlobs) + your own controller conventions |
| If absent | always present | core still fully works — warnings reference paths, not named rows/zones |
A repo with no adapter is first-class. Scaffold one with sage adapter init (writes
.agentic-sage/adapter.mjs from adapters/template.mjs; guide in
ADAPTERS.md) only when you want named work and zones.
What's tailor-made vs universal. This repo ships one person's setup as a worked example — the
adapters/acme.mjsadapter, a backlog format, worktrees under.claude/worktrees, a superpowers-style harness, an autopilotCLAUDE.md. None of that is required. The universal core knows nothing about it. Treatadapters/acme.mjsandCONVENTIONS.mdas examples to adapt, not steps to copy.
Parts & options — what each piece is, and whether you need it
| Part | What it does | Universal or example | Need it? | Turn on |
|---|---|---|---|---|
| sage CLI + emitter hook | the judge: records sessions, answers board/territory/… | universal | required | install.mjs + sage on |
| sage-fleet skill + CLAUDE pointer | sessions coordinate themselves (claim, merge-brief, why-diverged) | universal | recommended | paste templates/CLAUDE.snippet.md |
| sage-judge skill + sage judge run | NEW live passive judge: continuous fleet/repo briefs layered on CLI facts | universal | optional | auto-linked; sage judge run (or judge on) |
| sage-doctor skill (/sage-doctor) | one-command config-validity check | universal | recommended | auto-linked by install.mjs |
| Adapter (.agentic-sage/adapter.mjs) | names your rows + zones on the board | your project | optional | sage adapter init |
| Backlog coordination | who-holds-which-row + .md drift, without owning the file | needs an adapter | optional | adapter's backlogRows + sage backlog |
| Worktree-at-go convention | register intent the instant a worktree exists | example (controller) | optional | adapt from CONVENTIONS.md |
| The guard | blocks edits to contested paths (exit 2) | universal | optional, off | sage guard add <p> + sage guard on |
| tmux fleet pane | bind j → popup sage board | universal | optional | tmux source-file ~/.tmux.conf |
| Statusline segment | ⚖️ Asking Sage while consulting | universal | optional | wire templates/statusline.snippet.md |
| /handoff sidecar, token-forecast | integrations with other tooling | example/integration | optional | see SETUP.md / Optional integrations |
What install.mjs wires (so you can trust it)
It merges seven lifecycle hooks into ~/.claude/settings.json (back up once · skip-if-present ·
abort on malformed JSON · never auto-enable; Grok reads this by default via compat). All fire the one emitter, all fail-open and
no-op while SAGE is OFF:
| Hook event | What SAGE does on it |
|---|---|
| SessionStart | record/refresh this session; the one optional one-line fleet brief |
| UserPromptSubmit | refresh liveness/timestamp |
| PostToolUse | refresh liveness timestamp (throttled to ~1/30 s) |
| Stop | last-turn-fresh record (survives /clear) |
| PreCompact | lightweight handoff sidecar dump |
| SessionEnd | mark the session closed |
| PreToolUse | the guard — inert unless a guard is armed (cheap breadcrumb skip otherwise) |
Undo all of it any time: node uninstall/uninstall.mjs (surgical — see uninstall/).
Install
Scope vs storage — two independent axes
sage init sets where the hook is wired (scope) and where data lives (storage) —
independently:
| | Global scope (default) | Project scope (sage init --project) |
|---|---|---|
| Hook wired into | ~/.claude/settings.json (Grok reads via compat by default) | <repo>/.claude/settings.json |
| Storage default | ~/.claude/agentic-sage | <repo>/.agentic-sage (or --storage sibling\|agent-home) |
| Master switch | sage on / sage off | ignored — a project install works even with the global master OFF |
| Per-repo switch | sage enable / sage disable | sage enable / sage disable (the only switch in this scope) |
Storage resolves through a precedence chain (env override → in-repo marker → registry →
global default → built-in → legacy fallback) — full order in
CONVENTIONS.md. sage where prints the resolved scope + storage +
which rule matched for the current repo.
Option 1 — global npm (recommended):
npm install -g agentic-sage
sage init # wizard, or --global/--project — see AGENTS.md for flags
sage on # enable globally (default OFF)Option 2 — Grok Build plugin (skills + session hooks in the Plugins UI):
# After this package is listed on xAI Official, install from /marketplace.
# Until then, install from the repo (trusted):
grok plugin install muslewski/agentic-sage --trust
# also need the CLI for board/territory/judge:
npm install -g agentic-sage && sage onOption 3 — Claude Code marketplace:
/plugin marketplace add muslewski/agentic-sage
/plugin install agentic-sage@agentic-sageCommunity catalog (after form approval at clau.de/plugin-directory-submission):
/plugin marketplace add anthropics/claude-plugins-community
/plugin install agentic-sage@claude-communityOption 4 — skills.sh (any agent that uses SKILL.md):
# Recommended — sage product skills only (do not use --all: it also pulls Atlas helpers)
npx skills add muslewski/agentic-sage \
--skill sage-fleet --skill sage-judge --skill sage-doctor \
-a claude-code -a cursor -a codex -a gemini-cli -g -yIndex: https://www.skills.sh/muslewski/agentic-sage
Option 5 — Cursor:
Manifest .cursor-plugin/plugin.json — publisher form + GitHub repo listing.
Option 6 — Codex (ChatGPT Work / Codex CLI):
codex plugin marketplace add muslewski/agentic-sage
# then /plugins → install agentic-sage
# Official directory: https://platform.openai.com/plugins (Skills only ZIP)Option 7 — Gemini CLI:
gemini skills install https://github.com/muslewski/agentic-sage.git --path skills --consentSkills (sage-fleet, sage-judge, sage-doctor) ship with Grok/Claude/Cursor/Codex plugins and
skills.sh. CLI verbs still need npm + sage on.
Full matrix + directory listings: docs/distribution.md.
Option 3 — git clone (for contributors / local development):
git clone https://github.com/muslewski/agentic-sage.git
cd agentic-sage
node install.mjs # same as sage init --global, from source
sage onUpgrading from an older SAGE? Nothing breaks after
npm update— an existing~/.claude/sage/(the pre-rename state dir) keeps working in place (reads and writes) for config, storage, and adapter discovery, so no re-init is required. Runsage initorsage init --repairwhen convenient to perform the one-time, non-destructive rename to~/.claude/agentic-sage/(never clobbers; if both exist, the new dir wins and a warning prints).
Use
sage board [--json] # who's live, on what branch, how stale, what they touch
sage war [--json] # live full-screen cockpit of every session across every repo
# (TTY interactive: ↵ enter a session (tmux jump or cd), ↑↓/j/k move, / filter, w working-only, c cd, a all, q quit)
sage fleet [--json] # one-line fleet summary (fold into a status tick)
sage repos [--all] # product/orphan atlas with live gauges + activity sparklines
sage territory 'src/**' # before you start: does another session already claim this?
sage why-diverged f.ts # per-session intent + cross-branch diff for one file
sage merge-brief # all contested paths + the regenerate-don't-merge rule
sage prune [--days N] [--yes] # remove closed/dead sessions older than N days (default 7)
sage adapter init # scaffold .agentic-sage/adapter.mjs (optional, for named work/zones)
sage doctor # validate config / hook / settings / linked skills / adapter
sage where # this repo's resolved scope + storage + which rule matched
sage off # freeze judging (global master — see "Scope vs storage" above)Machine-readable output (board --json, fleet --json) follows the schema-1 envelope documented in SCHEMA.md.
Sessions as participants — the flywheel
The verbs above have two audiences. The human reads board / fleet at fleet
altitude. But the payoff — many sessions adding features in parallel and merging smoothly —
only lands when the sessions themselves coordinate: each one runs territory + claim
when it starts, and merge-brief + why-diverged before it opens a PR or resolves a conflict.
That protocol ships as a Claude Code skill, skills/sage-fleet:
install.mjssymlinks it into~/.claude/skills/sage-fleet(Grok discovers via compat + native ~/.grok/skills; opt out withSAGE_SKIP_SKILL=1).- Paste
templates/CLAUDE.snippet.md— a single always-loaded pointer line — into your repo or userCLAUDE.md(or AGENTS.md) so sessions reach for the skill at the right moments. The protocol stays in the on-demand skill, so a disabled SAGE costs ~nothing. Grok natively loads AGENTS.md/CLAUDE.md equivalents.
It is advisory: the skill runs the verbs and surfaces collisions; it never blocks and never decides — that's the guard's job (opt-in) and the human's call. SAGE off ⇒ the skill is a no-op.
Coordinating the backlog (optional)
A backlog is a shared, human-readable work-index — the one file (e.g. BACKLOG.md) where every
parallel session finds its place and claims work, so N sessions don't all grab the same task or trip
over each other. It is the fleet's source of what's in flight.
SAGE helps coordinate it without owning the file. The backlog has two layers: the stable prose
(a row exists, its mission, refs) and the volatile truth (who holds it now, is the holder alive).
The volatile layer is what 8 sessions actually collide on — so SAGE keeps it in its own state
(claimed_row on the session record), reads your BACKLOG.md through the adapter, and reports each
row's live truth — never editing the file:
sage backlog # rows × live sessions: who holds what, orphaned 🟡, .md glyph drift
sage backlog claim D11 # register THIS session's row (writes only SAGE's own state)sage backlog flags drift between the file and reality — a row marked ⬜ that a live session holds
(held-but-open), or a 🟡 row whose holder has died (orphaned). The .md stays the human's
at-a-glance doc (with whatever glyphs you keep); SAGE is the live truth and flags where they disagree —
the same freshness model a frozen snapshot + a staleness chip uses. You (or the human) reconcile the
glyph; SAGE never writes the row, keeping the human at fleet altitude (§0).
Backlog support is adapter-gated: your project supplies backlogRows(ctx) (see ADAPTERS.md). With
no adapter, sage backlog simply says so — the core stays project-agnostic.
Statusline segment (optional)
See when a session is currently taking SAGE's advice — an ephemeral status-bar segment
(default ⚖️ Asking Sage) that shows only while a session runs a consult verb
(territory/why-diverged/merge-brief/claim/fleet), then disappears.
It's driven by a flat per-session breadcrumb ~/.claude/agentic-sage/asking/<session_id> (mtime =
last consult) — keyed by the same session_id your statusline already receives, with no repoId,
so you can read it two ways (see templates/statusline.snippet.md):
- the verb — append
sage statusline --session "$ID" --cwd "$CWD"to your statusline output; - in-process —
stat ~/.claude/agentic-sage/asking/<session_id>and render your label ifnow - mtime < ttl(zero extra spawn).
sage statusline is fail-open — any error prints nothing and exits 0, so it can never break your
status bar; it prints nothing when SAGE is off. Configure statuslineLabel / statuslineTtlMs
(default ⚖️ Asking Sage / 8000ms) in ~/.claude/agentic-sage/config.json. The statusline is
polled (your refreshInterval), so the segment shows for a tick or two around a consult — not
sub-second.
Safety
The emitter (hooks/agentic-sage-emit.mjs — legacy installs symlink hooks/sage-emit.mjs) fires on
every session, so it's built to be invisible:
- Fail-open. All work is inside a
try/catch; any error →exit 0. It never blocks or slows a hook. - Default-OFF. Global scope: no global config (
~/.claude/agentic-sage/config.json; a legacy-only~/.claude/sage/config.jsonstill counts and stays in use until migrated) ⇒ disabled — first-line no-op. Project scope ignores the global master and uses the per-repoconfig.jsoninstead (init seeds{enabled:false}unless--enable). - Non-clobbering installer. Backs up, skips-if-present, aborts on malformed
settings.json.
The guard (the one thing that can act) — built, default OFF
Optionally, SAGE can block an edit to a contested path (PreToolUse → exit 2). It's gated by
two independent flags, both default off: judging enabled for this install (global: sage on;
project: sage enable) and per-repo sage guard on. Three invariants keep it safe to ship:
fail-open (any error → allow), default-off (nothing blocks until you arm it),
hot-path-cheap (no guard armed anywhere ⇒ the hook short-circuits on a single breadcrumb check,
before any git spawn). See CONVENTIONS.md.
Optional integrations
- token-forecast — if you run a token-forecast system, add
"tokenForecastPath": "~/.local/share/token-forecast"to~/.claude/agentic-sage/config.jsonto surface it insage doctor. Unset ⇒ the check stays green and says "not configured". - tmux fleet pane —
install.mjsoffers abind j→display-popuprunningsage board(runtmux source-file ~/.tmux.confto apply). - status-herald (sibling project: per-pane curtains/cards) — adjacent, not a dependency.
Shared compact/hot vocabulary only; full observational contract in
docs/interop-status-herald.md(see alsoCONVENTIONS.md).
Portability notes
- Handoff sidecars are prefixed by the repo basename — no project literal in any path.
- Glob dialect is
*and?only;[ ] { }are literal (no brace expansion) — so dynamic-route paths like[channelSlug]match themselves. - On macOS (
/procabsent),sage claimneedsSAGE_SELF_SIDset to the session id, since it can't pid-walk to find its own record.
Layout
bin/sage CLI (argv dispatch, async adapter load)
lib/*.mjs pure, unit-tested logic (zero deps) — incl. roots.mjs (storage
resolver) and init.mjs (wizard + non-interactive flags)
hooks/agentic-sage-emit.mjs the one hook entry (fail-open, default-OFF)
adapters/ template.mjs (scaffold) + acme.mjs (worked example) — out of the observed tree
install.mjs conservative global-scope wiring into ~/.claude (equivalent to
`sage init --global`)
uninstall/ surgical reversible uninstall (uninstall.mjs + README)
test/*.test.mjs node --test, hermetic (temp HOME, temp git repos)Docs: docs/ — product documentation hub (getting started, concepts, CLI, recipes; pilot for fleet docs-kit). ·
AGENTS.md — agent setup runbook ("set it up for my repo"). ·
SETUP.md — human walkthrough (required/optional tiers). ·
ADAPTERS.md — write a per-project adapter. ·
CONVENTIONS.md — an example controller setup (worktree-at-go, the guard). ·
agentic-sage-mind/ — Atlas vault (zones, specs, plans — not public marketing docs). ·
uninstall/ — undo it. · LICENSE — MIT.
Community
- Issues — bugs + feature requests
- Discussions — Q&A + ideas
- CONTRIBUTING.md — how to contribute
Contact
SAGE is in early, active development — a beta. I run it daily and it works, but expect rough edges, and expect things to change as it finds its shape.
I'm building it in the open and I'd genuinely value any feedback — a bug, a question, a half-formed idea, or just hello. Nothing is too small.
- Email — [email protected]
- GitHub — open an issue — the lightest way to reach me
— Mateusz
