TeamOS is a virtual workplace system that orchestrates AI and human team members through structured work cycles. Each AI member gets a small cycle at a time to perform a unit of work, respond to messages, advance projects, and collaborate with other members.
TeamOS lives as its own repository and integrates into any project, giving every repo the same team orchestration without duplicating code.
Team members are defined in a team/ workspace directory within the host project. Each member has a profile, current state, todo list, schedule, and inbox. A runner script processes members through priority-cascading cycles, invoking an AI agent (Claude, Cursor, Augment) for each.
The runner provides full context — organization docs, news, projects, and the member's own files — then commits after each member completes. A clerk agent runs after each pass for cleanup (archiving old news, removing stale schedule items, etc.).
teamos/
├── README.md # This file — system architecture reference
├── scripts/
│ ├── run.mjs # Runner — orchestrates member cycles
│ ├── init.mjs # Project initialization
│ └── detach.mjs # Package removal
├── agent-rules/
│ ├── cycle.md # Rules for member cycle agents
│ ├── clerk.md # Rules for clerk agent
│ └── root.md # Section appended to host AGENTS.md
├── templates/
│ ├── *.d.ts # TypeScript type definitions
│ └── *-template.* # File templates for new members
└── ui/ # Web dashboard (Svelte + Vite)
├── package.json
├── vite.config.ts
└── src/
team/
├── org.md # Organization description
├── members.json # Member manifest
├── projects.json # Goals and projects
├── memos.json # Timely information for all members
├── members/
│ └── [memberName]/
│ ├── profile.md # Member description
│ ├── state.md # Current state of work
│ ├── todo.json # Task list
│ ├── schedule.json
│ ├── inbox/ # Messages from other members
│ └── archives/
├── data/ # Shared data artifacts
├── docs/ # Shared documentation
├── archives/ # Archived org-level items
└── .logs/ # Agent execution logs (git-ignored)
# Git submodule (recommended):
git submodule add <teamos-repo-url> teamos
node teamos/scripts/init.mjs
# Git subtree (works with git worktrees; submodules do not):
git subtree add --prefix=teamos <teamos-repo-url> main --squash
node teamos/scripts/init.mjs
# Symlink (teamos cloned elsewhere):
node /path/to/teamos/scripts/init.mjsThis creates the team/ workspace with directories, empty manifests, and agent-rule references.
Create a member directory with a profile:
mkdir -p team/members/alice/inboxteam/members/alice/profile.md:
---
name: alice
title: Software Engineer
roles: [developer]
active: true
type: ai
personality:
openness: 8
conscientiousness: 9
extraversion: 5
agreeableness: 7
neuroticism: 3
---
Alice is a detail-oriented software engineer focused on backend systems.Add her to team/members.json:
{
"members": [
{
"name": "alice",
"title": "Software Engineer",
"roles": ["developer"],
"active": true,
"type": "ai"
}
]
}Create her initial files:
cp teamos/templates/todo-template.json team/members/alice/todo.json
cp teamos/templates/schedule-template.json team/members/alice/schedule.json
cp teamos/templates/state-template.md team/members/alice/state.md# See who has work
node teamos/scripts/run.mjs --dry-run
# Run cycles for all members
node teamos/scripts/run.mjs
# Run only a specific member
node teamos/scripts/run.mjs --member alice
# Use a different agent
node teamos/scripts/run.mjs --agent cursor
# Don't auto-commit and keep looping
node teamos/scripts/run.mjs --loop --no-commit| Option | Default | Description |
|---|---|---|
--agent <name> |
claude |
Agent adapter: claude, cursor, or auggie |
--priority <level> |
pressing |
Starting priority level |
--member <name> |
— | Only run cycles for a specific member |
--max-cycles <n> |
10 |
Maximum cycle passes per scheduling pass |
--loop |
— | Enable continuous scheduling loop |
--interval <min> |
120 |
Minutes between passes (implies --loop) |
--push |
— | Push to remote after each commit |
--no-commit |
— | Skip automatic git commit after each cycle |
--no-clerk |
— | Skip clerk agent after each pass |
--clerk-only |
— | Run only the clerk agent, then exit |
--budget <pri:n> |
thisWeek:2, later:1 |
Max member cycles at a priority per pass (repeatable) |
--dry-run |
— | List members with work, don't invoke agent |
Instead of running via an external cron job, the runner can operate as a long-lived process with its own scheduling loop:
# Default: 2-hour interval
node teamos/scripts/run.mjs --loop
# Custom interval (90 minutes)
node teamos/scripts/run.mjs --interval 90
# With a specific agent
node teamos/scripts/run.mjs --loop --agent cursorIn loop mode the runner:
- Runs a full priority cascade pass (no hard time limit)
- If the pass completes before the interval elapses, idles — polling every 30 seconds for new work or a
.stopfile - If new work arrives during idle at pressing or today priority, starts the next pass early. Lower-priority work (thisWeek, later) waits for the interval timer.
- If the pass overruns the interval, starts the next pass immediately
Cycle budgets — Lower-priority work is nibbled at rather than fully processed each pass. Per-pass limits prevent the runner from being perpetually busy with non-urgent work:
| Priority | Default budget | Effect |
|---|---|---|
pressing |
unlimited | Always fully processed |
today |
unlimited | Always fully processed |
thisWeek |
2 member cycles | At most 2 members run per pass |
later |
1 member cycle | At most 1 member runs per pass |
Override with --budget <priority>:<count> (repeatable). Starvation cycles bypass budgets.
Member ordering — At unbounded priorities (pressing, today), members run in members.json order every pass. Place operationally critical roles (e.g. DevOps) earlier in the manifest so their urgent work is always handled first. At budgeted priorities (thisWeek, later), members are served in round-robin order so that no single member monopolizes the limited slots. The rotation state is persisted in scheduler-state.json across restarts.
Starvation prevention — If high-priority work dominates every pass, lower priorities get periodic forced cycles so they don't drift indefinitely:
| Priority | Guaranteed cycle every |
|---|---|
pressing |
Always (cascade default) |
today |
~24 hours |
thisWeek |
~4 days |
later |
~1 week |
Starvation state is persisted to team/.logs/scheduler-state.json so that interruptions and restarts don't reset the clocks. On startup the runner restores the last-served timestamps; if the file is missing or corrupted it falls back to "just now" for all priorities.
Without --loop, the runner behaves as before: a single pass with a 1-hour hard time limit.
Create a team/.stop file to gracefully halt the runner between members:
touch team/.stopThe runner checks for this file before each cycle, between each member, and during idle waits. When found, it commits any completed work, removes the stop file, and exits. In loop mode, this exits the outer loop as well.
pressing → today → thisWeek → later
- Pressing — Timely; should be processed within an hour
- Today — Should be handled today
- ThisWeek — Handle this week
- Later — Nibble at when there is time
The runner starts at the highest priority and cascades down. It only advances to the next level when no members have work at the current level. Within a priority level, member order follows members.json — so list members in descending operational importance.
A member is given a cycle when any of these are true:
- They have inbox messages (markdown files in their
inbox/directory) - They have todo items at or above the current priority level
- They have schedule events that are due
During a cycle, the agent:
- Reviews organization context (org, news, projects)
- Processes inbox messages (reads and deletes them)
- Performs one unit of work at the current priority
- Updates state.md with what was accomplished
- Maintains todos (completes items, adds new ones)
A unit of work can include:
- Maintaining TODOs or schedule
- Advancing a project by a modest increment
- Building a tool (JS/TS library) for self or team
- Sending messages to other members' inboxes
- Outputting artifacts to
team/data/orteam/docs/
Members communicate by dropping markdown files into each other's inbox/ directories:
---
from: alice
sentAt: 2026-03-13T10:00:00Z
requestResponse: true
projectCode: AUTH
---
The auth module is ready for review.If you're an interactive agent (e.g. Cursor, Claude chat) asked to "be" a team member rather than running through the automated runner, read the following files to replicate the context the runner provides:
- Cycle rules —
teamos/agent-rules/cycle.md(how to execute a cycle) - System architecture —
teamos/README.md(this file) - Organization —
team/org.md - Memos —
team/memos.json(timely info for all members) - Projects —
team/projects.json - Team roster —
team/members.json - Your profile —
team/members/<you>/profile.md - Your state —
team/members/<you>/state.md - Your TODOs —
team/members/<you>/todo.json - Your schedule —
team/members/<you>/schedule.json - Your inbox — all
.mdfiles inteam/members/<you>/inbox/
The runner also passes a header with the current priority level and timestamp. When working interactively, default to priority pressing and work down the cascade as described above.
Depending on your interaction, you may update your state, TODOs, and schedule. Do not commit — let the human handle that.
- Priority-driven — Pressing work is always handled before less urgent tasks
- Right-sized cycles — Each cycle does a modest amount of work to maintain continuity without overrunning context windows
- Agent-owned changes — The agent modifies files freely; the runner handles git commits
- Commit per member — Clean git history for human review between runs
- Clerk cleanup — Automated housekeeping after each pass (archiving, fixing inconsistencies)
- Zero dependencies — Uses only Node.js built-in modules
TeamOS includes a web dashboard for viewing team status, member details, inboxes, todos, and sending messages.
cd teamos/ui
npm install
npm run devThe dashboard starts on http://localhost:3003 by default.
On first launch, the dashboard prompts you to select your identity from the member list. This determines:
- The default From field when composing messages
- Visual indicators showing which member is "you" in the team grid and detail views
Your identity is stored in the browser's localStorage and can be changed anytime via the dropdown in the navigation bar.
The dashboard auto-discovers the team/ directory by resolving ../../ from the teamos/ui/ directory (which maps to the host project root for all installation modes). Override this by setting the TEAMOS_PROJECT_ROOT environment variable:
TEAMOS_PROJECT_ROOT=/path/to/project npm run devIf a tickets/ directory exists at the project root (e.g. from a ticket management system like tess), the dashboard displays a ticket pipeline summary. This feature is optional and hidden when no tickets directory is found.
node teamos/scripts/detach.mjsThis removes teamos-created artifacts (agent rule files, symlinks, gitignore entries) but never touches the team/ workspace data.