8000 GitHub - kirahowe/compositor: Composite in-flight work from many concurrent live agent sessions into a single hot-reloading dev server · GitHub
[go: up one dir, main page]

Skip to content

About

Composite in-flight work from many concurrent live agent sessions into a single hot-reloading dev server

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

24 Commits

Folders and files

Repository files navigation

compositor

Important

This library is under development and still being dogfooded. The documentation below is AI-generated and not reviewed yet.

Feel free to use or explore (and if you do please let me know what issues you run into!), but please know this is mostly here for the sake of building in public and not considered ready for release yet.

Manage many concurrent coding-agent sessions against one repo, and composite all their in-flight work into one live, running dev server. Review by using the app, not reading diffs. Version control (jujutsu) is the storage engine and stays invisible.

See docs/SPEC.md for the vision, docs/REVIEW.md for the (evidence-backed) design review, docs/PLAN.md for the implementation plan, and docs/concurrency-notes.md for the daemon's concurrency model.

Status: M0–M4, verified end-to-end locally. Every milestone's exit criterion is a scripted, hermetic test and all are green — the M1 loop (bb e2e), M2 crash-recovery (bb recovery), the M3 conflict/absorb/ exclusive flows (bb conflict), and the M4 status pane driven by real tmux keystrokes (bb tui) — through the real daemon, real tmux, and a real dev server, with scripted fake agents. See TESTING.md for exactly what is and isn't proven (a live claude session and the F8 agent-done hooks are the notable not-yets).

Requirements

  • babashka (bb) — latest (≥ 1.12.218; pinned via :min-bb-version in bb.edn). 1.12.215+ is what makes the M4 status pane (charm.clj) work on bb's bundled JLine3; we just track latest so it's never a question.
  • jujutsu (jj) ≥ 0.43
  • tmux ≥ 3.4
  • your agent CLI (claude / codex) and a dev server in the target project

Install/upgrade babashka to the latest:

brew install borkdude/brew/babashka        # macOS/Linux (Homebrew)
# or:  bash < <(curl -s https://raw.githubusercontent.com/babashka/babashka/master/install)
bb --version                                # expect ≥ 1.12.218

Try it

cd your-project
bb --config /path/to/compositor/bb.edn comp init      # from source
# or, once installed via bbin/Homebrew:
comp init            # interview → plan → confirm → daemon + dev server + ui
tmux -L compositor attach                             # the 'ui' window:

comp init tells you exactly what it will change before doing it: it detects :trunk from your checked-out branch (never a hardcoded main) and the dev command from the project (package.json/bb.edn/justfile/Makefile), asks three questions when there's no config yet, preflights every command through your login shell, and checks that the dev server didn't die on the spot — if it did, you get its output, not a silent missing window. Everything it creates is inventoried, and comp destroy exactly inverts it (tmux, workspaces, unlanded commits, the config alias, the state dir — landed work is trunk history and is never touched). comp destroy --full also removes .jj/ when init created it. Until --full, jj's op log can restore anything destroy abandoned.

One sharp edge to know: agent/dev commands run non-interactively through your login shell — PATH and exported env apply, but aliases and functions from .zshrc don't. Put env inline instead, e.g. :agent-cmd "CLAUDE_CONFIG_DIR=$HOME/.claude-kira claude {prompt}". Every window's output is captured to <state>/logs/<window>.log (comp config prints where that is).

compositor   ● daemon

  id  state        on? change        intent
  1   running      ▣   xqrnzxlrtkrp  add a dark mode toggle
> 2   running      ▣   yotpstloxupq  make the footer sticky

↑/↓ select · n new · t toggle · K keep · D drop · s stack · a absorb · u undo · q quit

The status pane is the cockpit. n starts a take (type the intent, the agent spawns in its own window); t toggles the selected take in and out of the running app; K lands it on trunk; D abandons it; when two takes collide the pane says so on both rows — s stacks the selected take onto another (press the target's id), a absorbs your hand-tweaks from the dev app back into whichever take owns them, u undoes the last command. The same verbs exist as CLI commands (they drive the same daemon):

comp new "add a dark mode toggle"
comp ls              # sessions, applied state, ⚠ collision badges
comp toggle 2        # the running app changes under your cursor
comp keep 1          # land it — the app doesn't flinch
comp stack 3 2       # takes collided? re-parent 3 onto 2, markers → its agent
comp send 3 "resolve the conflict markers, keep both behaviors"
comp attach 3        # hand your terminal to that agent — images, paste, the lot
comp absorb          # hand-tweaks in the dev app flow back into their takes
comp undo            # restore the state before the last comp command
comp status          # megamerge + collisions + last op, as data
comp ui              # the status pane, in whatever terminal you're in
comp config edit     # this project's config.edn, in $EDITOR
comp destroy         # take it all down again (--full: de-colocate too)

The tmux session (tmux -L compositor) holds everything: window 0 the daemon, dev the dev server, ui the status pane, and one window per agent — switch to a take's window to watch or talk to its agent.

If the daemon is down, any comp command starts it (tmux window 0) and retries; reconcile() on boot makes that safe at any moment — after a crash, a kill -9, or a stray jj undo behind its back, comp ls tells the truth.

Develop

cd compositor
bb test          # unit tests (queue serialization, config, graph)
bb integration   # jj/tmux command-sequence test (needs jj + tmux)
bb e2e           # M1 exit criterion, live: daemon + tmux + dev server + fake agents
bb recovery      # M2 exit criterion: kill -9 + sabotage, reconcile repairs
bb conflict      # M3 exit criterion: collide/stack/resolve, absorb, exclusive+DB hooks
bb tui           # M4 exit criterion: drive the status pane with real tmux keystrokes
bb destroy-test  # init↔destroy symmetry: interview, detection, full/default destroy
bb lint          # clj-kondo (zero warnings)
bb gen-script    # emit the standalone ./compositor uberscript (inlines charm.clj)

Layout

src/compositor/
  main.clj       CLI dispatch (ui loads lazily so plain calls stay fast)
  setup.clj      init (interview/plan/preflight) + destroy, exact inverses
  client.clj     socket transport shared by CLI + tui; auto-starts the daemon
  tui.clj        the status pane: charm.clj (Bubble Tea for Clojure), Elm-shaped
  jj.clj         the single jj contract: templates→data, the serialization queue
  tmux.clj       socket-scoped tmux driver
  graph.clj      megamerge lifecycle (create once, rebase -s in place) + collision badges
  session.clj    create / toggle / keep / drop / stack / send / absorb
  daemon.clj     core.async loop: jj queue + watch + socket + undo
  reconcile.clj  repo → (store, world) repair; runs on every daemon boot (F10)
  store.clj      session records (EDN now, sqlite pod when the schema settles)
  config.clj     project config + paths
  gen_script.clj uberscript emitter

About

Composite in-flight work from many concurrent live agent sessions into a single hot-reloading dev server

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0