Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 

Repository files navigation

If you're an agent reading this, stop here. This file is written for humans, and your protocol lives in framework/README.md.

Middle manager

shared-context

Mission Control for multi-repo Claude Code agents. Each agent works in its own repo and gossips with the others by appending timestamped files into per-feature folders here. As the human you open dashboard.html to stay aligned without having to parse logs.


Setup

One script. Run it after any change to the roster and it brings the whole machine back into line.

./framework/bin/setup-claude.sh

It drops you into an interactive menu and nothing on disk changes until you hit a to apply or f to force. The other keys are s for per-file state, p for target paths, and d for diffs. On apply it:

  • symlinks framework/commands/*.md into ~/.claude/commands/ so Claude Code picks them up in every CWD;
  • writes shared_context_root, the shared-context permission bundle, and the three hooks (lint, index-rebuild, session-cursor) into ~/.claude/settings.json;
  • reads the AGENTS.md roster and wires every listed checkout — identity marker, framework version marker, per-repo permissions, and a CLAUDE.md pointer line.

Adding a repo that isn't on the roster yet is the one case that needs its own command, because only you can say what the repo is called and what it does:

./framework/bin/wire-repo.sh /path/to/your-repo your-identity "Your one-line role"

That adds the roster row; from then on setup-claude.sh keeps it in sync with everything else.

It also re-stamps the shared command blocks (see below). Everything is idempotent, so re-running is always safe. Restart any open Claude sessions afterwards and that's the whole setup.

Shared command blocks. Several slash commands have to state the artefact formats and the version skip-rule identically. Those paragraphs live once in framework/command-blocks.md and are stamped into the command files between <!-- shared:… --> markers by framework/bin/sync-command-blocks.mjs (run automatically on apply; --check fails on drift). Edit the canonical file, never the copies. Hand-maintained, they drifted immediately: a version bump reached three of five commands, and the other two went on telling agents "you're current, skip the spec" above a description of the previous version's formats.

Why one entry point. The hooks used to be per-repo, written only by wire-repo.sh. A repo onboarded through /bootstrap and /join alone got permissions and a version marker — which made it look wired — but no lint and no session-cursor safety net. One feature ran to completion that way: nothing validated a single artifact write, and the formats drifted exactly where they are fiddliest. The hooks are global now, and the roster is reconciled here, so that state is no longer reachable.

If you'd rather do it by hand, the sub-steps are spelled out further down.


Slash commands

There are thirteen commands in total: two for setup, five for daily work, three situational, one cross-feature, one for wrap-up, and one that exists purely for you.

Set up

  • /bootstrap <slug> [--light] — founding repo. Scaffolds the feature, drops a [fy] announcement, writes the first snapshot. --light (or auto for single-repo features) skips the multi-repo ceremony; a later /join upgrades it.
  • /join <slug> [--as <identity>] — every other repo, once. Writes the repo's first positional + [fy]. Safe to re-run (redirects to /resume). --as picks the identity when the repo hosts several.

Work (every session)

  • /resume <slug> — first message. Clears the inbox; surfaces asks, blockers, contract changes, and open owed.dsl obligations.
  • /handoff <slug> — before /clear. Digest if useful; sweeps the session for promised-but-unwritten follow-ups into owed.dsl; always writes the cursor.
  • /pivot <slug> <reason> — direction shifted. Tombstones stale decisions/contracts and notifies affected repos at decision time (the deploy becomes an owed.dsl line).
  • /refresh <slug> — re-synthesise the orchestrator snapshot from a cold read. Rarely needed by hand: /handoff, /pivot and /close-project each write one from state they already hold.
  • /tighten <slug> — review + refactor + test the current branch's changes.

Situational

  • /catch-up <slug> — cold start after a long break. Deeper read than /resume.
  • /audit <slug> — full history including superseded entries. Use sparingly.
  • /diagnose <slug> — analyze opt-in diagnostics traces: index hit-rate, resume fast-exits, protocol deviations.

Cross-feature

  • /global-add <project> <category> <slug> — add or update a globals/<project>/ entry shared across features.

For you, not the agents

  • /dashboard — rebuild dashboard.html. Purely optional: no agent read path touches it, and agents are told not to run it unless you ask. The mandatory artifact is _index.md, which every state-writing command rebuilds for itself.

Wrap up

  • /close-project <slug> [done|paused] <reason> — flips MISSION.md status; dashboard moves the feature to Archived.

The first /bootstrap or /join per repo auto-adds blanket Read(<shared-context>/**) + Write(<shared-context>/features/**) permissions to that repo's .claude/settings.local.json, and it's idempotent so you can safely re-run it. Agent-side specs for each command live in framework/README.md#slash-commands.

Between commands, agents append log/*.dsl, repos/<self>/*.positional, contracts/<api>/*.dsl, and decisions/*.md files as side-effects of their work. Every command that writes feature state ends by rebuilding _index.md — the agent-facing index — and a hook rebuilds it again for writes that happen outside a command. dashboard.html is not on that path; run /dashboard when you want it current.


The optimal cycle

                  [ unborn ]
                      │
                      │  /bootstrap + /join×N
                      ▼
       ┌──────────────────────────────┐
       │             IDLE             │ ◄────┐
       │  no active session anywhere  │      │
       └──────────────────────────────┘      │
                      │                      │
                      │  /resume             │  /handoff → /clear
                      ▼                      │
       ┌──────────────────────────────┐      │
       │        ACTIVE SESSION        │      │
       │  work + logs;                │      │
       │  /pivot and /tighten are     │      │
       │  in-state actions            │ ─────┘
       └──────────────────────────────┘
                      │
                      │  /close-project [done|paused]
                      ▼
                  [ ARCHIVED ]

Bootstrap is only used to kick off a feature. Daily use is the loop of commands, usually you would only use /resume, /handoff, and occasionally /pivot. /tighten would only come towards the end of feature development, the other commands are self explanatory. /dashboard whenever you want the visual view rebuilt — it changes nothing an agent reads.

Per session and per repo, you open with /resume <slug> and close with /handoff <slug> then /clear. If you skip the handoff the next resume gets expensive, since the cursor is what keeps it cheap.

Ask for a digest at milestones, not only at session end. A mid-session digest pays back 10× on the next /resume — and /handoff turns each digest into a fresh checkpoint, so the synthesised view keeps up on its own. Reach for /refresh only when a feature has drifted past its last checkpoint and you want it re-synthesised from scratch.

When direction shifts, use /pivot rather than silently abandoning the prior plan. If the plan changes — say scope shrinks, an API approach turns out wrong, or a decision flips — /pivot <reason> writes the tombstones so other repos stop building on retired assumptions.

Cross-repo handshakes follow set shapes. A blocking ask is a [q] log with to: <repo>, and the answer is an [a] log with refs:. Long-form scoped work goes in tickets/<slug>.md and is referenced from logs. Contract changes always ship as a contract version file plus a [cc] log entry, never one without the other.

Wrap up explicitly with /close-project done|paused <reason> when the feature ships or stalls. The folder stays for the audit trail and the dashboard archives it.


What's where

shared-context/
├── README.md         ← you are here (human onboarding + setup)
├── AGENTS.md         ← repo identity roster (per-team; you edit this)
├── dashboard.html    ← generated on demand by /dashboard; the human view, optional
├── framework/        ← the agent protocol + scripts (don't hand-edit)
├── features/         ← one folder per active feature (agents write here)
└── .claude/          ← local Claude config

The framework folder is shareable across teams, while the repo root holds your team's roster, features, and dashboard.


Reading the dashboard

/dashboard        # rebuild it (in any Claude session)
open dashboard.html

The dashboard is generated on demand and nothing else depends on it, so it's only as current as your last /dashboard. Rebuild before you read; it takes under a second.

Items needing your attention pin to the top. Several sources feed them:

  • The orchestrator snapshot's "Open for the human" section — rich prose, rewritten whenever an agent runs /handoff, /pivot or /close-project, and re-synthesised from scratch by /refresh <slug>.
  • The per-repo positional blocked_on field — terse, and it surfaces from raw state the moment a repo writes a positional with a non-empty blocker, with no snapshot in between. Blockers prefixed human: mean only you can clear them; they pin to the top and never age out.
  • Open owed.dsl obligations — writes a repo promised for later (a follow-up notification, a contract bump gated on a deploy) that haven't happened yet. Lines whose trigger has already fired are badged TRIGGER FIRED — actionable now. /close-project done refuses while any are open; a dead promise gets cancelled in place (done: … refs: cancelled — <reason>).
  • Lifecycle flags — a feature still active with no writes for 14+ days gets a "close or pause?" nag; a repo whose cursor predates the latest pivot gets a "working from a pre-pivot brief" warning; an open obligation whose underlying ask was later answered (e.g. declined) gets an "owed-line trigger answered" flag so it can't dangle forever.

If a feature's snapshot has been overtaken by newer writes, the feature page shows a stale-snapshot banner (with a count of writes since) and a mechanically derived current state (latest per-repo positions, digests, unanswered questions) — trust that over the old snapshot prose. Past 10 writes, agents are instructed to run /refresh themselves before resuming; run it manually whenever you want the narrative rebuilt sooner.

Per-feature pages live alongside the main view, and the latest orchestrator snapshot under each feature is what you would skim if you've been away for a while.


Setup by hand

If the scripts feel like too much magic, here's exactly what they do under the hood.

1. Symlink the slash commands

export SHARED_CONTEXT_ROOT="/absolute/path/to/your/shared-context"
mkdir -p ~/.claude/commands
ln -sf "$SHARED_CONTEXT_ROOT"/framework/commands/*.md ~/.claude/commands/

Only files matching framework/commands/*.md get touched, so anything else already in ~/.claude/commands/ is left alone. Restart any open Claude sessions afterwards.

There are two optional environment variables you can set: CLAUDE_COMMANDS_DIR overrides the default ~/.claude/commands target, and NO_COLOR=1 disables colors.

2. Register the hooks — once, globally

All three go in ~/.claude/settings.json, not per-repo. Each one keys off the artifact path, so it is inert outside features/** and costs nothing in an unrelated repo. Global is also the only shape that survives a checkout nobody remembered to wire — a fresh git worktree, or a repo onboarded through /bootstrap and /join alone.

"hooks": {
  "PreToolUse": [
    { "matcher": "Write|Edit|Bash",
      "hooks": [{ "type": "command", "command": "<SHARED_CONTEXT_ROOT>/framework/bin/hook-lint.sh" }] }
  ],
  "PostToolUse": [
    { "matcher": "Write|Edit",
      "hooks": [{ "type": "command", "command": "<SHARED_CONTEXT_ROOT>/framework/bin/hook-render.sh" }] }
  ],
  "SessionEnd": [
    { "hooks": [{ "type": "command", "command": "<SHARED_CONTEXT_ROOT>/framework/bin/hook-session-cursor.sh" }] }
  ]
}
  • hook-lint.sh validates new files under features/<slug>/: word budgets, canonical filenames, format grammar, identity roster. It also blocks shell writes into artifact paths (redirects, tee, cp, sed -i, … — agents must use the Write tool so lint can run) and grammar-checks owed.dsl edits. It blocks violations by default (the agent sees why and re-plans); set SHARED_CONTEXT_LINT_MODE=warn to downgrade to stderr warnings.
  • hook-render.sh rebuilds the written feature's _index.md — the file agents read first. Pure aggregation, no LLM cost, no HTML. It's a safety net: the commands already rebuild the index themselves, so this only catches writes made outside a command.
  • hook-session-cursor.sh is the /handoff safety net. If a session ends without a handoff, it writes a trace-derived auto: true shadow cursor so the next resume stays cheap. Runs after the model is gone, so it costs zero tokens; it's inert unless diagnostics traces exist.

If a repo already carries one of these in its own .claude/settings.local.json, it just runs twice, harmlessly — wire-repo.sh skips writing a local copy of anything already registered globally.

Two more hooks are worth knowing about, both off by default:

  • framework/bin/hook-diagnostics.sh (PreToolUse on Read|Write|Edit|Bash) — records zero-token usage traces that /diagnose analyzes, and that the auto-cursor derives from. Worth turning on for at least one feature. It costs nothing at runtime and it is the only way to answer "did agents actually read the index, or did they greedy-read the log?" — the questions a retro otherwise has to infer. Add it the same way as the three above, with "matcher": "Read|Write|Edit|Bash". Trace files land in .diagnostics/sessions/ and are gitignored.
  • framework/bin/hook-orchestrate.sh (PostToolUse) — auto-snapshots on every digest write via a headless model call. The only hook that costs real tokens, and now largely redundant: /handoff writes the snapshot itself, in-session, from context it already has.

3. Point each repo's CLAUDE.md at this folder

One line:

Cross-repo coordination lives at /absolute/path/to/your/shared-context. For any multi-repo feature, read its framework/README.md and root AGENTS.md before starting.

4. Add the repo to AGENTS.md

Append a row with the repo's identity (lowercase kebab), its absolute CWD, and a one-line role. Until a repo is listed there its agent has no identity in the system and can't write valid filenames.

A repo whose collaborating units are modules gets a row per module instead, all sharing its CWD. Those agents can't resolve identity from a path, so each checkout's .claude/settings.local.json carries "shared_context_identity": "<module>"wire-repo.sh writes it when you pass an identity.


Working tips

  • Name the feature explicitly at session start. Use /resume <slug> rather than just "resume" so the agent isn't left guessing.
  • /join is once per repo per feature. After that the verb is /resume. Running /join twice is safe since it hard-refuses with a nudge.
  • /clear between features, not within them. Within a feature the cursor and digest carry continuity for you.
  • Use blocked_on to make waits visible. When a repo finishes its part but the rest isn't done, write a new positional with current_goal: idle — waiting on <X> and a blocked_on row. The dashboard pins it under "Needs your attention" automatically. See framework/CONVENTIONS.md → "Signalling I'm done with my part."
  • Confirm the checkpoint after /clear + /resume. Ask the agent "tell me which snapshot you read and which contract versions you're synced against" and you'll catch stale-state bugs in 5 seconds.
  • A repo whose units are modules can hold several identities. Add a row per module to AGENTS.md and set shared_context_identity in each checkout's .claude/settings.local.json; the agent reads that instead of matching a path. Size the modules so a handful are live at once — too coarse and one identity owns most of the repo, which is the context problem you were splitting to avoid.
  • One agent per working tree at a time. Cross-repo parallelism is the point, but two agents in one checkout race on the branch and the files even when their identities differ. Give each concurrently active identity its own git worktree and wire it with wire-repo.sh <worktree-path> <identity>.
  • Don't hand-edit anything under features/<slug>/. Append-only is structurally enforced for agents but not for humans, and breaking it desyncs everyone.

Where to dig deeper

About

Claude Skill for Context Sharing between Repos

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages