A desktop chat with a Coordinator: a Claude Code session that listens to you and runs a team of worker sessions (coder, reviewer, git-coder). Each worker starts with a clean context and one skill, reports progress in its own chat box, and exits when its task is done.
jg-team-dev is a desktop app for running a small team of AI coding agents on your own projects. You chat with a Coordinator, which breaks the work into tasks on a Kanban board. The Coordinator writes the brief into the task, then coder, reviewer and git-coder take each task in turn, each in its own chat. Every task gets its own branch and worktree; the coder runs the tests and the reviewer checks the full diff. You approve each git step (commit, push, pull request, merge) unless you switch it to automatic. The app, its hub and its data run on your machine; the agents use Claude Code or Antigravity.
you ⇄ jg-team-dev app ⇄ jg-team-dev-hub (SQLite, unix socket) ⇄ MCP + hooks ⇄ Coordinator ─ claude -p ─▶ workers
The messaging layer consists of a background hub daemon (jg-team-dev-hub), an MCP server, session hooks, and an iced desktop application (jg-team-dev-desktop). The system operates with its own unix socket, SQLite database, and configuration, and passes its hooks to sessions with --settings instead of modifying ~/.claude/settings.json. If rtk is installed ([commands] rtk, ~/.local/bin, … or PATH), its Bash hook (rtk hook claude) is added to the sessions' settings too.
-
Coordinator chat:
jg-team-dev start [folder](or New team in the app) marks the folder trusted and starts a background Coordinator with jg-team-dev's hooks, MCP server and skills. It listens for 50 minutes ([timeouts] listen_secs), then its process stops; your next message wakes it with its context intact. -
Workers and skills: the Coordinator runs
jg-team-dev worker <skill> --task … --brief-file …(coder, reviewer, git-coder; there are no separate verifier or tester workers: the coder runs the build and tests, and the reviewer is the only gate). It returns at once; a detached process runsclaude -p "/jg-team-dev:<skill> <brief>"(or Antigravity / OpenCode as configured) and relays the result JSON to the Coordinator through the hub, waking it if needed. Workers get their own chats under their Coordinator; messaging a finished worker resumes it for one reply, and the Coordinator gets a summary.jg-team-dev workerslists them (the Coordinator keeps at most 3 running). -
Git flow: coders work in
.team/worktrees/<name>on branchjgt/<name>(jg-team-dev worker coder --worktree <name>; the reviewer points at the same worktree). In a git repository a coder without a worktree is refused unless the Coordinator passes--main-worktreeafter warning you..team/is kept out ofgit statusthrough the repository's local exclude file. The git-coder commits each task on its own branch, pushes it, opens a pull request and merges it (usinggh pr merge <n> --mergewithout--delete-branch, deleting the remote branch withgit push origin --delete, removing the worktree, and deleting the local branch withgit branch -d), each step only after a review, a test and your OK (or its auto-switch); it never commits, merges or pushes onmain, and removes the worktree once the pull request is merged. Inspect the live policy at any time withjg-team-dev git-policy. -
Runs: each request with workers is a run.
jg-team-dev run startwrites.team/runs/<run-id>/request.md; workers append toevents.jsonland save their result inworker-results/;jg-team-dev run finishwritesfinal-report.mdand archives the run's worker chats.jg-team-dev run listshows a project's runs. -
Team panel: a Coordinator's chat has a Team panel showing each worker's state, time and cost; click one to open its chat. Model, effort, permission mode and tools can be set per skill and per project (see Configuration below).
-
Topic change and reset: on a topic change the Coordinator asks, writes
.team/tasks/task-<date>-<slug>.md, and runsjg-team-dev reset --summary <file>: a fresh Coordinator takes over the folder and the old chat is archived, read-only. -
Kanban Board: each workspace (a Coordinator's folder) has a Board of tasks in the hub database,
JGT-1,JGT-2, … per workspace (keys are never reused), in ten columns: Backlog, On Queue, Working, Testing, Review, To commit, To push, To open PR, To merge, Done. Between Review and Done sits the fixed git line (To commit → To push → To open PR → To merge → Done), where each column represents the next git step awaiting the user's OK (To merge = merge the pull request). When the Coordinator moves a card todonefrom Review or a git column, the hub routes it to the first later column whose auto-switch is OFF, or to Done if all are ON. The hub returns the ON steps passed along the way (git_steps:commit,push,open_pr,merge_pr) and the task's branch name (branch), which the Coordinator hands to the git-coder. In a git repository with no remote the card stops in To push, and withoutghon PATH it stops in To open PR, whatever the switches say; the task log says why. Dragging a card forward along the line counts as the user's OK for every step it passes, and the hub notifies the Coordinator to run them. Leaving a git column whose switch is OFF requires a reason quoting the user's OK. The team can never move a card backward from a git column. Git columns do not count toward the WIP limit. Every move, edit, result and comment goes to the task's activity log with who made it. The hub enforces the gates: workers only read the Board; agents never edit the title, description or priority; the Coordinator creates tasks in Backlog and queues only subtasks of a task you queued; at mostwip_limittasks are in progress. The flow is: Coordinator writes the brief, plan and acceptance criteria into the task Details → coder implements and tests (stays in Working until tests are green) → one reviewer pass checks only critical things across the full diff. If clean, the card moves to Done and the hub routes it along the git line. If changes are requested, the card stays in Review for the user; when the user moves it back to Working (or asks for a fix), a fresh coder fixes the findings, runs tests, and a new full review runs. Medium and low findings no longer exist. You may move any task anywhere; a move that skips a gate is logged as "moved by you".jg-team-dev task(list,show,create,edit,details,move,result,comment,delete,log,export) drives it from a terminal or a session. -
Board view: a Coordinator's chat header has two tabs, Chat (the default) and Board. The Board shows the workspace's ten columns with their counts, scrolling sideways in a narrow window; Done shows the 20 newest tasks, with "Show all". "+" in Backlog and On Queue adds a task (a title is enough). A card shows its key, title, priority, the worker on it (
↳ reviewer), the review decision and Waiting for you or Waiting: usage limit; move it with ← → or "Move to…". Click a card for its panel: edit the title, description and priority; read the Coordinator's Details, the test and review results (critical findings first) and the activity log; open the run folder or a worker's chat; delete it. Changes from the team or a terminal show up live. -
Team on the board: moving a task to On Queue is the request. The hub queues
[jg-team-dev] Task JGT-n "…" is on the queuefor the folder's current Coordinator, and the app or the CLI that moved it wakes a sleeping Coordinator (team::wake, shared with worker results; the hub never starts agents). With no Coordinator running, the task's log says so and the app offers "Start team". The Coordinator works the task with the MCP toolstask_list,task_get,task_update,task_move,task_createandtask_log, following the "Board" section of its skill: it moves the card before announcing each step and follows the one-review-pass rule. Workers only read (task_get).jg-team-dev run start --task-id JGT-nnames the run after the key and links it to the task;jg-team-dev worker … --task-id JGT-nworks in.team/worktrees/jgt-n(branchjgt/jgt-n) in the task's run, and the card shows the worker on it. The reviewer checks only critical findings (severityblockingorhigh, categorybehavior,security,secret, orpatterns). -
Board interactions and export: drag a card to another column or position (Esc cancels; ← → and "Move to…" stay). Comment on a task from its panel or
jg-team-dev task comment: the comment reaches the Coordinator like a chat message and wakes it. A task reaching Done, entering a git column waiting for your OK, or getting Waiting for you, posts a notification in the Coordinator's chat, which also shows on the desktop. Done tasks are archived (kept, hidden;task list --allshows them) 7 days after they got there; moving one brings it back. Export the Board as Markdown with the Board's Export button orjg-team-dev task export [--out FILE] [--all](defaultdocs/board.md). -
Team efficiency and review gate: the coder runs the build and tests itself and reports them (
testsfield); while tests fail (or work is incomplete), the task stays in Working and the Coordinator starts a fresh coder to make them green, recording no test result. Once tests are green, the Coordinator records it as the task's test result (pass) and moves the card to Review. The reviewer is the only gate: it does not re-run tests, it checks only critical things (behavior,security,secret,patternswhen documented). One review pass per task, reviewing the full worktree diff (git diff, all coders). If the reviewer requests changes, the card stays in Review waiting for the user. A finished worker's result reaches the Coordinator as a short digest (at most 10 lines: status, finding counts, notes) with the path of the full result in the run, which also shows in the worker's chat. A worker stopped by the usage limit sets Waiting: usage limit on its task with the reset time; the Coordinator starts no worker meanwhile, and shortly after the reset the hub clears it and wakes the Coordinator, which resumes the task with a fresh worker from the saved resume brief.jg-team-dev worker … --model <m> --effort <e>raises one worker for a hard task without changing the config. -
IDE tab: built-in read-only viewer in the desktop app sidebar (and from task panels) for inspecting workspace files and git worktree changes across
mainand active tasks, featuring syntax highlighting, diff toggles, and safety guards (see IDE tab below).
Team sessions use [team] in config.toml. Each role runs on its own harness, model and
effort: its built-in default, shown below as the config that spells it out. A
[team.skills.<role>] table you write replaces the role's harness, model and effort as a whole:
[team]
# harness = "claude-code" # claude-code, opencode or antigravity (workers only)
# model = "opus" # below the role defaults: set a role's table to change that role
# effort = "medium" # low, medium, high, xhigh, max (checked against the harness)
permission_mode = "auto" # only prompts auto mode can't settle reach the app
wip_limit = 1 # Board tasks in progress at once (1..=3)
[team.skills.coordinator] # Claude Code only
model = "opus"
effort = "medium"
[team.skills.coder]
harness = "antigravity" # Google Antigravity (`agy`)
model = "gemini-3.8-flash-medium" # Gemini 3.8 Flash, as `agy models` names it
effort = "medium"
# permission_mode = "bypassPermissions" # agy --dangerously-skip-permissions: see below
allowed_tools = ["Bash(cargo test:*)"] # claude --allowedTools (Claude Code only)
disallowed_tools = ["WebFetch"] # claude --disallowedTools (Claude Code only)
[team.skills.coder.fallback] # used where `agy` is not installed
harness = "claude-code"
model = "sonnet"
effort = "medium"
[team.skills.reviewer]
harness = "claude-code"
model = "opus" # Claude Code's alias for its newest Opus (Opus 5.5)
effort = "medium"
[team.skills.git-coder]
model = "sonnet"
effort = "low"
[git]
auto_commit = false # commit a reviewed task on its own branch without asking (default: false)
branch_pattern = "" # task branch name; {key} {slug} {title}; empty = "jgt/{key}"
auto_push = false # push the task branch to the remote without asking (default: false)
auto_open_pr = false # open a pull request for the pushed branch (default: false)
auto_merge_pr = false # merge the pull request once its checks pass (default: false)
[commands]
# opencode = "/path/to/opencode" # when it is not on PATH (e.g. an nvm install)
# antigravity = "/path/to/agy"
# rtk = "/path/to/rtk" # its Bash hook is added to team sessions when foundThe [git] section configures git autonomy: the steps the team takes on its own once the
reviewer approved a task. They run in a fixed order, and each one that is false (the default)
makes the card wait in its column for your OK:
auto_commit: at the task's first commit the team creates the task's branch and commits only there.branch_patternnames it:{key}is the task key in lowercase (jgt-31),{slug}a slug of the title (a-z, 0-9 and-, at most 40 characters) and{title}the title with spaces turned into-. Empty, or a pattern that ends up empty or asmain,masterorHEAD, meansjgt/{key}.auto_push: push that branch to the remote (a plain push).auto_open_pr: open a pull request for it withgh, against the repository's default branch.auto_merge_pr: merge the pull request once its checks pass (usinggh pr merge <n> --mergewithout--delete-branch, deleting the remote branch withgit push origin --delete, removing the worktree, and deleting the local branch withgit branch -d), and fast-forward your local default branch when the main worktree is clean and on it.
The team never commits, merges or pushes on main, and force-push is never allowed, under any
setting. Opening and merging pull requests needs a git remote and the GitHub CLI (gh), logged
in. Inspect the live policy at any time with jg-team-dev git-policy: it prints the four
switches, the branch pattern and the "never" line.
Older configs migrate on load: auto_merge = true becomes auto_open_pr = true (which now talks
to GitHub) unless auto_open_pr is already set, and the old main-branch switch is read and dropped; the
next save writes only the new keys. On the Board, cards that were in the old To merge column (committed,
not merged) move to To push. Cards that were already in To push had been merged into your local
main: the team no longer pushes main, so push it yourself and move them to Done.
An Antigravity worker cannot ask for permission: a headless agy run denies every tool call
that needs it (shell commands included), and --mode auto is not available on every account.
Such a run is reported as failed, naming the denied calls. To let an agy coder build and test,
set permission_mode = "bypassPermissions" for that role, which runs agy --dangerously-skip-permissions; otherwise keep the coder on its Claude Code fallback.
The Coordinator compacts its context at compact_window = 120000 tokens unless set under
[team.skills.coordinator] (0 leaves it to Claude Code): it lives for days, and every step
re-reads its whole context. Any Claude Code role takes compact_window (100000 to 1000000).
A project can override these in .team/config.toml, with the same [team] and
[team.skills.<skill>] tables. Harness, model and effort come as one unit from the first of:
project skill, user skill, the role's built-in default, project [team], user [team], then
claude-code + opus + medium. A table that sets a model or effort without a harness runs on
claude-code (never on another layer's Antigravity or OpenCode); what it leaves unset comes from
the next layer on the same harness. So [team] model = "opus" leaves the coder on Antigravity
with its agy model, and [team.skills.coder] model = "sonnet" runs the coder on Claude Code.
When a role's harness is not installed, the worker runs on that role's fallback as a whole (the
coder: Claude Code + sonnet + medium), and its chat says so. Permission mode and tools merge
field by field, the most specific first. A bad setting (unknown harness or effort, a
Coordinator off Claude Code) is replaced and shown as a warning in Settings; an effort the
harness lacks maps to the nearest lower one, and a harness with no effort setting (OpenCode)
runs without one. jg-team-dev worker … --model <m> --effort <e> overrides one worker.
Settings save to .team/agents.json when a team starts. To change them live, edit them in Settings and click Apply to this team, or run jg-team-dev agents set <role> [--harness h] [--model m] [--effort e]; new workers use the changes immediately (the Coordinator row applies after a reset).
The desktop app includes an IDE module (in the sidebar below Board, or opened via the Open in IDE button on a task panel) for viewing project files and worktree changes:
- Read-only viewer: displays highlighted file contents with line numbers. There is no editing, saving, or code execution.
- Syntax highlighting: code colors follow the app theme: Dracula colors under the Dracula theme (pink keywords, yellow strings, green functions, purple numbers, grey-blue comments, cyan italic types), Tokyo Night colors under Tokyo Night, Base16 Mocha under the other dark themes and InspiredGitHub under light; the language is detected from the file extension (unknown extensions show plain text).
- Source selector: choose between
main(the committedmainbranch from git, reflecting committed state) and any task worktree under.team/worktrees/(showing working tree files, including uncommitted coder changes). - Folder with several repos: if the team folder is not a git repo itself but holds repos one level deep (hidden folders,
node_modules,targetand symlinks are skipped), the source selector lists each repo by folder name and shows its committedmain; pick the repo there. A folder with no repo shows "This folder is not a git repository" in the picker. For real work across several repos, one team per repository is still the safest setup (workers, worktrees and git steps assume a single repo). - Picker on the right: directory tree on the right side of the view, sorted folders-first and lazily expanded.
- M/A/D marks and "Changed only": worktree files display badges for Modified (
M), Added (A), Deleted (D), and Renamed (R) changes relative to the merge base withmain. A Changed only checkbox filters the tree to only show files with changes. - File | Diff toggle: for changed files, toggle between the full file text and the unified diff view with colored additions and deletions. Deleted files show the diff view directly.
- Limits and safety:
- File size cap: 1 MB (
File too large to show). - Binary files (NUL byte in the first 8 KB or invalid UTF-8) are refused (
Binary file). - Directory listings are capped at 20,000 entries (
Listing truncated). - Diff output capped at 1 MB / 20,000 lines.
- Automatically skipped folders:
.git,target,node_modules,.team. - Secret files (
.env,.env.*except.env.example,.env.sample,.env.template;*.pem,*.key,id_rsa*,credentials*,*.p12,.npmrc,.pypirc) are listed with a lock icon, and their contents and diffs are never read or shown (Secret file: not shown). - Symlinks are not followed (
Symlink: not followed), and path traversal (..components or paths escaping the root) is rejected.
- File size cap: 1 MB (
- Tabs: each file you open from the picker gets a tab above the viewer (clicking an open file focuses its tab). A tab remembers its source and File/Diff state; focusing a tab from another source switches the source selector. Close a tab with its x button or a middle-click; closing the active tab activates a neighbour. Up to 20 tabs (opening more closes the oldest inactive one); tabs of removed worktrees are dropped on refresh, and tabs are kept while the app runs (not across restarts).
- Refresh button: reloads sources and file listings manually on demand; there is no auto-refresh or background file watching.
- File-type icons: picker rows and tabs show a small colored icon per file type (Rust, TOML, JSON, YAML, Markdown, shell, JS/TS, Python, HTML/CSS, images, lock files, Dockerfile,
.gitignore, LICENSE, README, Makefile) and open/closed folder icons; unknown files get a generic file icon. The icons are our own SVGs incrates/jgt-desktop/assets/file-icons/, rasterized once on first use. - Collapsible side bars: the chevron (
‹/›) next to the sidebar search collapses the left side bar (sessions and workspaces) to a thin strip, and the one at the top of the picker collapses the file picker; the chevron left in the strip expands it again. Ctrl+B toggles the left bar and Ctrl+Alt+B the picker. Collapsing keeps the selection, open folders and scroll positions, and the state is saved with the other view settings (left_bar_collapsedandright_bar_collapsedunder[desktop]inconfig.toml).
Once published, the AUR package can be installed using an AUR helper:
yay -S jg-team-dev
# or
paru -S jg-team-devTo build and install the package locally from this repository:
cd packaging && makepkg -si- Claude Code (
claude-code): required for agent sessions. - Vulkan driver (
vulkan-icd-loader): optional, enables GPU-accelerated rendering.
- Enable and start the background hub service:
systemctl --user enable --now jg-team-dev-hub - Open JG Team Dev from your application launcher or run
jg-team-dev-desktop.
For packaging and publishing instructions, see the AUR Maintainer Guide.
cargo build # jg-team-dev, jg-team-dev-hub, jg-team-dev-desktop in target/debug
cargo test --workspace
./target/debug/jg-team-dev-hub # run the hub in the foreground (RUST_LOG=jgt_hub=debug)
./target/debug/jg-team-dev start ~/my-app # start (or wake) the Coordinator for a folder
./target/debug/jg-team-dev worker coder --task "fix average" --worktree fix-average --parent <id> "Remove the - 1"
./target/debug/jg-team-dev task list --workspace ~/my-app # the folder's Board
./target/debug/jg-team-dev task export --workspace ~/my-app # write it to docs/board.md
./target/debug/jg-team-dev settings # hooks for `claude --settings`
./target/debug/jg-team-dev settings --mcp # MCP server for `claude --mcp-config`Use JGT_RUNTIME_DIR (keep it short: unix socket paths are limited to ~100 bytes),
JGT_DATA_DIR and JGT_CONFIG_DIR to run an isolated hub next to the installed one.
| Path | What |
|---|---|
crates/jgt-core |
shared types, hub API client, config, paths, launcher |
crates/jgt-hub |
the daemon: SQLite, HTTP API over a unix socket, liveness monitor, notifications, tray |
crates/jgt-cli |
jg-team-dev: MCP server, Claude Code hooks, start, settings generator, CLI |
skills/ |
role skills, embedded in the binary and loaded as the jg-team-dev plugin |
crates/jgt-desktop |
jg-team-dev-desktop: the iced app |
packaging/ |
PKGBUILD, systemd user unit, desktop entry, icon |
Files at runtime: database ~/.local/share/jg-team-dev/jg-team-dev.db, socket
$XDG_RUNTIME_DIR/jg-team-dev/hub.sock, config ~/.config/jg-team-dev/config.toml.
MIT