- Convention over configuration - Auto-derive everything possible
- Single source of truth - Config file in repo, runtime data in
~/.agent-orchestrator/ - Zero path configuration - All paths determined automatically
- Global uniqueness - Hash-based namespacing prevents collisions
Repo (versioned):
~/any/path/to/agent-orchestrator/
agent-orchestrator.yaml ← Config file (only this matters)
packages/
...
Runtime Data (not versioned):
~/.agent-orchestrator/ ← Single parent directory
a3b4c5d6e7f8-integrator/ ← {hash}-{projectId}
sessions/
int-1 ← Session metadata files (no hash prefix)
int-2
worktrees/
int-1/ ← Git worktrees (no hash prefix)
int-2/
archive/
int-3_2026-02-17T10-30-00
.origin ← Config path reference
a3b4c5d6e7f8-backend/ ← Same hash (same config!)
sessions/
be-1 ← No hash prefix (already namespaced)
worktrees/
be-1/
.origin
Hash Derivation (from config location):
const configDir = path.dirname(configPath); // /Users/alice/code/agent-orchestrator
const hash = sha256(configDir).slice(0, 12); // a3b4c5d6e7f8
// Each project managed by this config gets a directory
// Format: {hash}-{projectId}
const projectId = path.basename(projectPath); // integrator, backend, etc.
const instanceId = `${hash}-${projectId}`; // a3b4c5d6e7f8-integrator
// Not configurable!
const projectBaseDir = `~/.agent-orchestrator/${instanceId}`;
const sessionsDir = `${projectBaseDir}/sessions`;
const worktreesDir = `${projectBaseDir}/worktrees`;Key insight: All projects from the same config share the same hash prefix!
# agent-orchestrator.yaml
projects:
- path: ~/repos/integrator # Required: where is the repo?
repo: ComposioHQ/integrator # Required: GitHub repo
defaultBranch: next # Required: base branch
# Optional overrides:
name: Composio Integrator # Display name (default: folder name)
sessionPrefix: int # Override auto-generated prefixAuto-derived:
- Project ID:
basename(path)→integrator - Session prefix:
generatePrefix("integrator")→int - Worktree path:
{worktreeDir}/integrator/
That's it! No dataDir, no worktreeDir, no explicit IDs.
{sessionPrefix}-{num}
int-1, int-2 (integrator)
ao-1, ao-2 (agent-orchestrator)
ss-1, ss-2 (safe-split)
{hash}-{sessionPrefix}-{num}
a3b4c5d6e7f8-int-1
a3b4c5d6e7f8-ao-1
f1e2d3c4b5a6-int-1 (different checkout, no collision!)
function generateSessionPrefix(projectId: string): string {
if (projectId.length <= 4) return projectId.toLowerCase();
// CamelCase: PyTorch → pt
const uppercase = projectId.match(/[A-Z]/g);
if (uppercase?.length > 1) {
return uppercase.join("").toLowerCase();
}
// kebab-case: agent-orchestrator → ao
if (projectId.includes("-") || projectId.includes("_")) {
const sep = projectId.includes("-") ? "-" : "_";
return projectId
.split(sep)
.map((w) => w[0])
.join("")
.toLowerCase();
}
// Single word: integrator → int
return projectId.slice(0, 3).toLowerCase();
}~/.agent-orchestrator/a3b4c5d6e7f8-integrator/
sessions/
int-1 ← Metadata file (user-facing session name)
int-2
worktrees/
int-1/
int-2/
archive/
int-3_2026-02-17T10-30-00
project=integrator
issue=INT-100
branch=feat/INT-100
status=working
tmuxName=a3b4c5d6e7f8-int-1
worktree=/Users/alice/.agent-orchestrator/a3b4c5d6e7f8-integrator/worktrees/int-1
createdAt=2026-02-17T10:30:00Z
pr=https://github.com/ComposioHQ/integrator/pull/123
Key fields:
project- Which project this session belongs to (for filtering)issue- Linear/GitHub issue IDbranch- Git branch nameworktree- Path to git worktreestatus- working/idle/pr_open/merged
# List all sessions
ao list
# List sessions for specific project
ao list integrator
# Spawn new session
ao spawn integrator INT-100
# Attach to session (orchestrator finds tmux name)
ao attach int-1
# Kill session
ao kill int-1
# Show instance info
ao infoNo config paths in commands! Everything auto-discovered.
# ~/code/my-orchestrator/agent-orchestrator.yaml
projects:
- path: ~/repos/integrator
- path: ~/repos/backendResults in:
~/.agent-orchestrator/
a3b4c5d6e7f8-integrator/ ← Same hash (same config)
a3b4c5d6e7f8-backend/ ← Same hash (same config)
~/code/orchestrator/ → hash: a3b4c5d6e7f8
~/code/orchestrator-v2/ → hash: f1e2d3c4b5a6
~/splitly-orchestrator/ → hash: 9876abcd5432
Results in:
~/.agent-orchestrator/
a3b4c5d6e7f8-integrator/ ← From ~/code/orchestrator
f1e2d3c4b5a6-integrator/ ← From ~/code/orchestrator-v2 (different checkout!)
9876abcd5432-safesplit/ ← From ~/splitly-orchestrator
# Sessions (no collisions):
a3b4c5d6e7f8-int-1 (main checkout)
f1e2d3c4b5a6-int-1 (v2 checkout)
9876abcd5432-ss-1 (splitly)
Each orchestrator checkout gets unique hash. Projects within same config share that hash.
# ~/code/my-orchestrator/agent-orchestrator.yaml
projects:
- path: ~/repos/integrator
repo: ComposioHQ/integrator
defaultBranch: next
- path: ~/repos/backend
repo: ComposioHQ/backend
defaultBranch: main
sessionPrefix: be # Override auto-generated "bac"Results in:
Config location:
~/code/my-orchestrator/
→ Hash: a3b4c5d6e7f8
Runtime data:
~/.agent-orchestrator/
a3b4c5d6e7f8-integrator/ ← Project 1
sessions/
int-1
worktrees/
int-1/
a3b4c5d6e7f8-backend/ ← Project 2 (same hash!)
sessions/
be-1
worktrees/
be-1/
Session names:
User-facing: int-1, be-1
Tmux: a3b4c5d6e7f8-int-1, a3b4c5d6e7f8-be-1
Commands:
ao spawn integrator INT-100
ao attach int-1
Required (3 fields per project):
path- Where is the repo?repo- GitHub owner/repodefaultBranch- Base branch name
Optional:
sessionPrefix- Override auto-generated prefixname- Display name
That's it! Everything else is automatic.