Skip to content

dawsh2/org-agent

Repository files navigation

org-agent

!! WARNING !! - This is a VIBE SLOP codebase with git integration, so use at your own risk! Highly recommend starting with some dummy tasks to ensure everything mostly works before risking losing real work. Pull requests/forks welcome. This file is designed to be an interactive tutorial. Requires org-edna and claude-code.el https://github.com/stevemolitor/claude-code.el

What is this?

Any org heading can be an agent session. Press C-c C-x s on a heading and an AI agent spawns — isolated in its own git worktree, scoped to that task, with full context from the org tree. When it finishes, its changes auto-commit on a branch.

The tree is the persistent state. Sessions come and go, headings survive.

Requires claude-code.el for terminal management.

Install

(add-to-list 'load-path "~/repos/org-agent")

(use-package org-agent
  :after (org claude-code)
  :config
  (org-agent-mode 1))

Then initialize your project:

M-x org-agent-init-project

Try it

The headings below are live tasks. Open this file in Emacs with org-agent-mode enabled and try the workflow yourself.

First, set up the project:

M-x org-agent-init-project

This creates .org-agent/, installs guard hooks in .claude/settings.json, and adds .org-agent/ to .gitignore.

After running all three tutorials, clean up the test branches:

git branch -D org-agent/TRY-1 org-agent/TRY-2 org-agent/TRY-3 2>/dev/null
git worktree prune
rm -rf .org-agent/worktrees/TRY-*

[TRY-1] Create examples/hello.sh

Create a file examples/hello.sh that prints “Hello from org-agent!” and is executable (chmod +x).

How to run this test

  1. Put point on this heading. Press C-c C-x s.
  2. Watch the agent work in its terminal.
  3. When it finishes, this heading flips to DONE.
  4. Press C-c C-x g to review the diff in magit.
  5. Verify: bash examples/hello.sh prints the greeting.

What happens at the git level

Dispatch (=C-c C-x s=) — branch + worktree created:

$ git branch
* main
  org-agent/TRY-1          ← forked from main

$ git worktree list
/you/org-agent                                   [main]
/you/org-agent/.org-agent/worktrees/TRY-1        [org-agent/TRY-1]

Agent works — edits happen in .org-agent/worktrees/TRY-1/. Your main working tree is untouched.

NEXT → DONE — auto-commit on the branch:

$ git log --oneline org-agent/TRY-1
f00cafe [TRY-1] Create examples/hello.sh

$ git diff main..org-agent/TRY-1 --stat
 examples/hello.sh | 3 +++
 1 file changed, 3 insertions(+)

Review (=C-c C-x g=) — magit diff of main..org-agent/TRY-1.

[TRY-2] Add project metadata files

Add a CONTRIBUTORS file listing “Your Name” and a LICENSE file with the MIT license. These are independent — use parallel children.

How to run this test

  1. Create two children and dispatch them:
    emacsclient -e '(org-agent-add-child "TRY-2" "[TRY-2a] Create CONTRIBUTORS file" "NEXT")'
    emacsclient -e '(org-agent-add-child "TRY-2" "[TRY-2b] Create LICENSE file with MIT license" "NEXT")'
    emacsclient -e '(org-agent-dispatch-batch (list "TRY-2a" "TRY-2b"))'
        

    Or: put point on this heading, press C-c C-x p to let a planning agent decompose it, then C-c C-x b to batch-dispatch.

  2. Both agents work in parallel. Watch them in the dashboard (C-c C-x l).
  3. When both are DONE, mark this heading DONE (C-c C-x s or S-RIGHT).
  4. The hook merges both branches. Press C-c C-x g to review.
  5. Verify: cat CONTRIBUTORS and cat LICENSE show the expected content.

What happens at the git level

Parent branch created when TRY-2 is dispatched or children fork:

$ git branch
* main
  org-agent/TRY-2          ← parent branch (forked from main)

Children dispatched — each gets its own worktree:

$ git branch
* main
  org-agent/TRY-2
  org-agent/TRY-2a         ← forked from org-agent/TRY-2
  org-agent/TRY-2b         ← forked from org-agent/TRY-2

$ git worktree list
/you/org-agent                                   [main]
/you/org-agent/.org-agent/worktrees/TRY-2a       [org-agent/TRY-2a]
/you/org-agent/.org-agent/worktrees/TRY-2b       [org-agent/TRY-2b]

Two agents, two directories, two branches — simultaneous work.

Children finish (NEXT → DONE) — each auto-commits:

$ git log --oneline org-agent/TRY-2a
aaa1111 [TRY-2a] Create CONTRIBUTORS file

$ git log --oneline org-agent/TRY-2b
bbb2222 [TRY-2b] Create LICENSE file with MIT license

Parent finishes (TODO → DONE) — merges both, cleans up:

$ git log --oneline org-agent/TRY-2
ccc3333 Merge branch 'org-agent/TRY-2b' into org-agent/TRY-2
ddd4444 Merge branch 'org-agent/TRY-2a' into org-agent/TRY-2
bbb2222 [TRY-2b] Create LICENSE file with MIT license
aaa1111 [TRY-2a] Create CONTRIBUTORS file

$ git worktree list
/you/org-agent  [main]    ← worktrees removed

Child branches deleted. Since TRY-2 is a level-2 heading, TODO → DONE also merges org-agent/TRY-2 into main.

Merge conflict? If both children had edited the same file:

CONFLICT (content): Merge conflict in README.md
org-agent: merge conflict — TRY-2 stays TODO, resolve manually

Resolve, commit on org-agent/TRY-2, then mark DONE again.

[TRY-3] Add a version-stamped changelog

Create a VERSION file, then a CHANGELOG.md that references the version from that file. The changelog depends on the version file existing first — use a sequential chain.

How to run this test

  1. Create the chain:
    emacsclient -e '(org-agent-add-chain "TRY-3"
      (list "[TRY-3a] Create VERSION file containing 0.1.0"
            "[TRY-3b] Create CHANGELOG.md referencing the version from VERSION"
            "[TRY-3c] Add a test script that checks VERSION matches CHANGELOG.md"))'
        
  2. TRY-3a dispatches immediately. TRY-3b auto-dispatches when TRY-3a finishes — its worktree already contains the VERSION file.
  3. When the chain completes, mark this heading DONE.
  4. Verify: bash test-version.sh (or whatever TRY-3c created) passes.

What happens at the git level

Chain created — org-edna wires the dependencies:

*** NEXT [TRY-3a] Create VERSION file containing 0.1.0
    :PROPERTIES:
    :TRIGGER: ids(TRY-3b) todo!(NEXT)
    :END:

*** TODO [TRY-3b] Create CHANGELOG.md referencing the version from VERSION
    :PROPERTIES:
    :BLOCKER: ids(TRY-3a)
    :TRIGGER: ids(TRY-3c) todo!(NEXT)
    :END:

*** TODO [TRY-3c] Add a test script that checks VERSION matches CHANGELOG.md
    :PROPERTIES:
    :BLOCKER: ids(TRY-3b)
    :END:

Only TRY-3a is NEXT. TRY-3b and TRY-3c are blocked.

TRY-3a works and finishes — auto-commit, then rolling merge into the parent branch (this is the key difference from parallel):

$ git log --oneline org-agent/TRY-3
aaa1111 Merge branch 'org-agent/TRY-3a' into org-agent/TRY-3
fff0000 [TRY-3a] Create VERSION file containing 0.1.0

TRIGGER fires → TRY-3b flips to NEXT → auto-dispatches. Its worktree forks from org-agent/TRY-3, which already has TRY-3a’s work merged in:

$ ls .org-agent/worktrees/TRY-3b/
VERSION          ← written by TRY-3a, available to TRY-3b

TRY-3b finishes — rolling merge again, TRY-3c dispatches:

$ ls .org-agent/worktrees/TRY-3c/
VERSION          ← from TRY-3a
CHANGELOG.md     ← from TRY-3b

Chain complete — full history on the parent branch:

$ git log --oneline org-agent/TRY-3
eee5555 Merge branch 'org-agent/TRY-3c' into org-agent/TRY-3
ddd4444 [TRY-3c] Add a test script
ccc3333 Merge branch 'org-agent/TRY-3b' into org-agent/TRY-3
bbb2222 [TRY-3b] Create CHANGELOG.md
aaa1111 Merge branch 'org-agent/TRY-3a' into org-agent/TRY-3
fff0000 [TRY-3a] Create VERSION file

Mark TRY-3 DONE → merges org-agent/TRY-3 into main.

How it works

States

  • NEXT: execution. Agent gets edit access + worktree isolation.
  • TODO: planning/decomposition. Agent gets read-only access.
  • DONE: complete. Changes auto-committed, session closed.

The org file is the task file

org-agent works on whatever org file you open it in. This README, your project’s TODO.org, a sprint-planning.org — any org file. Set org-agent-org-filename to control which file org-agent-init-project creates by default.

Messaging

# Send a message to an agent's terminal
emacsclient -e '(org-agent-send "TRY-1" "Also add error handling." t)'

# Add a persistent note (survives restarts)
emacsclient -e '(org-agent-add-note "TRY-1" "Needs input validation.")'

# Read another agent's subtree
emacsclient -e '(org-agent-read-subtree "TRY-1")'

# Broadcast to all live agents
emacsclient -e '(org-agent-broadcast "Report status.")'

Always pass t as the third arg to org-agent-send.

Reviewing results

When a heading reaches DONE, press C-c C-x g to open a magit diff of everything the agent changed. Falls back to a plain diff buffer if magit isn’t installed.

For review agents (code review of children’s work): C-c C-x r.

Keybindings

All under C-c C-x in org-agent-mode:

KeyWhat it does
sSpawn agent on heading at point
bBatch-spawn all NEXT leaves
pSpawn planning agent (decompose)
rSpawn review agent for DONE children
qClose review/read-only sessions
kKill session (C-u to also purge)
dPurge session from disk (fresh start)
gShow magit diff for this node
lOpen agent dashboard
oSwitch to another agent session
nCapture a TODO from any buffer
jJump to next waiting agent

Global (when org-agent-global-keybindings is non-nil):

KeyWhat it does
C-c nCapture a TODO from any buffer
C-c jJump to next waiting agent

Configuration

Paths

(setq org-agent-org-filename "TODO.org")             ;; default task file name
(setq org-agent-state-directory ".org-agent")         ;; state/sessions dir
(setq org-agent-worktree-directory ".org-agent/worktrees")  ;; worktree dir
(setq org-agent-instructions-file ".claude/CLAUDE.md")      ;; project instructions

Pre-commit formatting

;; Rust
(setq org-agent-pre-commit-formatter "cargo fmt"
      org-agent-merge-formatter "rustfmt")

;; Python
(setq org-agent-pre-commit-formatter "black .")

;; JavaScript
(setq org-agent-pre-commit-formatter "prettier --write .")

;; None (default)
(setq org-agent-pre-commit-formatter nil)

Methodology injection

Every agent gets methodology text in its prompt. Override it:

;; Point to a file
(setq org-agent-methodology-file "docs/methodology.md")

;; Or set inline
(setq org-agent-methodology-text "## Our process\n1. Write tests first\n...")

;; Or use the default TDD stub (built-in)

Backend selection

;; Global default
(setq org-agent-default-backend "anthropic")  ;; "anthropic", "codex", "zai"

;; Per-heading (inheritable via org properties)
(org-agent-set-backend "A-1" "codex")

Hooks

Elisp hooks:

HookWhen it fires
org-agent-pre-execute-hookBefore spawning (receives plist)
org-agent-post-execute-hookAfter agent completion
org-agent-state-change-hookOn TODO state change

Claude Code PreToolUse hooks (shell scripts) enforce workflow rules:

  • Edit guard: blocks edits when agent state isn’t NEXT/TODO, or when writing outside declared :OWNS_PATHS:
  • Commit guard: blocks git commit for NEXT agents without worktrees (forces the auto-commit path)

Installed by org-agent-install-hooks / org-agent-init-project.

Adding a new backend

(push '("my-backend" . (:require my-backend-code
                         :term-make my-backend--term-make
                         :term-send-string my-backend--term-send-string
                         :term-configure my-backend--term-configure
                         :term-setup-keymap my-backend--term-setup-keymap
                         :term-customize-faces my-backend--term-customize-faces
                         :event-hook my-backend-event-hook))
      org-agent-backend-adapters)

Migrating from claude-org

All claude-org-* functions are aliased to org-agent-*. Existing guard scripts and running agents continue to work.

(setq org-agent-state-directory ".claude/tree"
      org-agent-worktree-directory ".claude/worktrees"
      org-agent-instructions-file ".claude/CLAUDE.md"
      org-agent-pre-commit-formatter "cargo fmt")

About

Orchestrate AI agents via org-mode headings with git worktree isolation

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors