workstreams-cli 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- workstreams_cli-0.5.0/PKG-INFO +622 -0
- workstreams_cli-0.5.0/README.md +592 -0
- workstreams_cli-0.5.0/pyproject.toml +62 -0
- workstreams_cli-0.5.0/setup.cfg +4 -0
- workstreams_cli-0.5.0/src/workstreams/__init__.py +71 -0
- workstreams_cli-0.5.0/src/workstreams/cli.py +499 -0
- workstreams_cli-0.5.0/src/workstreams/config.py +227 -0
- workstreams_cli-0.5.0/src/workstreams/dashboard.py +341 -0
- workstreams_cli-0.5.0/src/workstreams/event_log.py +240 -0
- workstreams_cli-0.5.0/src/workstreams/manager.py +573 -0
- workstreams_cli-0.5.0/src/workstreams/models.py +121 -0
- workstreams_cli-0.5.0/src/workstreams/multiplexer/__init__.py +32 -0
- workstreams_cli-0.5.0/src/workstreams/multiplexer/base.py +44 -0
- workstreams_cli-0.5.0/src/workstreams/multiplexer/tmux.py +202 -0
- workstreams_cli-0.5.0/src/workstreams/multiplexer/tmux_compatible.py +22 -0
- workstreams_cli-0.5.0/src/workstreams/multiplexer/zellij.py +109 -0
- workstreams_cli-0.5.0/src/workstreams/notifier.py +147 -0
- workstreams_cli-0.5.0/src/workstreams/py.typed +0 -0
- workstreams_cli-0.5.0/src/workstreams/subagent_client.py +125 -0
- workstreams_cli-0.5.0/src/workstreams_cli.egg-info/PKG-INFO +622 -0
- workstreams_cli-0.5.0/src/workstreams_cli.egg-info/SOURCES.txt +27 -0
- workstreams_cli-0.5.0/src/workstreams_cli.egg-info/dependency_links.txt +1 -0
- workstreams_cli-0.5.0/src/workstreams_cli.egg-info/entry_points.txt +2 -0
- workstreams_cli-0.5.0/src/workstreams_cli.egg-info/requires.txt +7 -0
- workstreams_cli-0.5.0/src/workstreams_cli.egg-info/top_level.txt +1 -0
- workstreams_cli-0.5.0/tests/test_config.py +166 -0
- workstreams_cli-0.5.0/tests/test_event_log.py +249 -0
- workstreams_cli-0.5.0/tests/test_models.py +16 -0
- workstreams_cli-0.5.0/tests/test_notifier.py +125 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: workstreams-cli
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Visually dispatch coding-agent work to subagents in real terminal windows and monitor it in one dashboard - for any coding agent (Claude Code, Codex, OpenCode, Qwen Code, Hermes, Cline, and more).
|
|
5
|
+
Author: Dream-Pixels-Forge
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Dream-Pixels-Forge/workstreams-cli
|
|
8
|
+
Keywords: cli,tmux,zellij,parallel,subagents,coding-agents,claude-code,codex,opencode,monitoring,git-worktree
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
22
|
+
Classifier: Topic :: System :: Systems Administration
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Provides-Extra: yaml
|
|
26
|
+
Requires-Dist: pyyaml>=6.0; extra == "yaml"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pyyaml>=6.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
|
+
|
|
31
|
+
# workstreams
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
Visually dispatch coding-agent work to subagents in real terminal windows and monitor it in one dashboard — for any coding agent (Claude Code, Codex, OpenCode, Qwen Code, Hermes, Cline, and more).
|
|
36
|
+
|
|
37
|
+
`workstreams` runs your coding agents **in parallel across real terminal windows** (tmux, zellij), isolates each one in its own git worktree + branch, and gives you a single live dashboard to watch them all. Instead of running subagents invisibly inside one agent's process, you **see each agent working in its own visible window** and get event-streamed progress back to one shared log.
|
|
38
|
+
|
|
39
|
+
Key capabilities:
|
|
40
|
+
|
|
41
|
+
- **Parallel coding agents in visible terminals** — one agent per tmux window / zellij tab, all in a detached session you can attach to at any time
|
|
42
|
+
- **Git worktree isolation** — each workstream gets its own branch (`ws/N`) and working directory, so agents never step on each other's files
|
|
43
|
+
- **Live monitoring dashboard** — `workstreams monitor` renders a real-time TUI: git status, PIDs, log alerts, and the last subagent events, refreshing every 2 seconds
|
|
44
|
+
- **Cross-process subagent event logging** — any agent or script (in a pane, a CI job, a cron) appends JSONL events to one shared file; you read them from any terminal
|
|
45
|
+
- **Cross-terminal notifications** — desktop notifications (Linux `notify-send`, macOS `osascript`) plus a shared `notifications.jsonl` that other terminals can poll
|
|
46
|
+
- **Agent-agnostic** — no vendor lock-in. `dispatch` and `work` send arbitrary shell commands to panes, so it works with whatever agent binary you can run from a shell
|
|
47
|
+
|
|
48
|
+
## Why this exists
|
|
49
|
+
|
|
50
|
+
Coding agents increasingly support "subagents" that run in the background of the main agent's process. That means: no visibility (you can't watch them), no isolation (they share one working tree and one set of installed dependencies), no way to run several in parallel on independent branches, and no shared event stream you can watch from your main terminal.
|
|
51
|
+
|
|
52
|
+
`workstreams` solves this by moving each subagent into a **real, visible terminal window** running in its own git worktree, and wiring all of them back to **one shared event log + one dashboard**. Your main agent (or you, manually) dispatches tasks, watches progress in `monitor`, and finishes with PR/merge commands.
|
|
53
|
+
|
|
54
|
+
## Contents
|
|
55
|
+
|
|
56
|
+
- [Installation](#installation)
|
|
57
|
+
- [Core Concepts](#core-concepts)
|
|
58
|
+
- [Quick Start (5 commands)](#quick-start-5-commands)
|
|
59
|
+
- [The Full Workflow](#the-full-workflow)
|
|
60
|
+
- [Command Reference](#command-reference)
|
|
61
|
+
- [Configuration (.workstreams.yaml)](#configuration-workstreamsyaml)
|
|
62
|
+
- [How the Multiplexers Work](#how-the-multiplexers-work)
|
|
63
|
+
- [Subagent Event System (Python API + CLI)](#subagent-event-system)
|
|
64
|
+
- [Environment Variables](#environment-variables)
|
|
65
|
+
- [Exit Codes](#exit-codes)
|
|
66
|
+
- [Data Locations](#data-locations)
|
|
67
|
+
- [Integrating With Coding Agents (Skill)](#integrating-with-coding-agents-skill)
|
|
68
|
+
- [CI/CD Integration](#cicd-integration)
|
|
69
|
+
- [Troubleshooting](#troubleshooting)
|
|
70
|
+
- [Best Practices](#best-practices)
|
|
71
|
+
- [Contributing & License](#contributing--license)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
|
|
77
|
+
Requires **Python 3.9+** and **git**. A terminal multiplexer (`tmux` recommended, or `zellij`, `nami`, `lmux`, `wmux`, `herdr`) is required for `start`/`dispatch`/`work` to actually place agents into visible windows. `pr`/`merge` commands additionally require the `gh` CLI (GitHub) authenticated.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# from PyPI (core; JSON config fallback built-in)
|
|
81
|
+
pip install workstreams-cli
|
|
82
|
+
|
|
83
|
+
# with YAML config support (recommended — .workstreams.yaml becomes first-class)
|
|
84
|
+
pip install "workstreams-cli[yaml]"
|
|
85
|
+
|
|
86
|
+
# develop from source (this repo)
|
|
87
|
+
git clone https://github.com/Dream-Pixels-Forge/workstreams-cli.git
|
|
88
|
+
cd workstreams-cli
|
|
89
|
+
pip install -e ".[yaml,dev]" # dev extras add pytest
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> **Ubuntu / Debian (PEP 668 "externally-managed environment")** — system pip refuses bare `pip install`. Use `pipx` (recommended; installs a standalone `workstreams` command on your PATH) or a virtualenv:
|
|
93
|
+
>
|
|
94
|
+
> ```bash
|
|
95
|
+
> # option 1: pipx — cleanest, no venv juggling
|
|
96
|
+
> pipx install workstreams-cli
|
|
97
|
+
>
|
|
98
|
+
> # option 2: virtualenv
|
|
99
|
+
> python3 -m venv ~/.workstreams-venv
|
|
100
|
+
> ~/.workstreams-venv/bin/pip install "workstreams-cli[yaml]"
|
|
101
|
+
> export PATH="$HOME/.workstreams-venv/bin:$PATH"
|
|
102
|
+
> ```
|
|
103
|
+
|
|
104
|
+
Verify:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
workstreams --version # -> workstreams 0.5.0
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> **Note:** every command also accepts `--json` to emit machine-readable output (where supported), which coding agents can parse. All read-side commands work without a multiplexer installed; only `start`/`dispatch`/`work`/`attach` need one.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Core Concepts
|
|
115
|
+
|
|
116
|
+
Understanding these five terms makes everything else click.
|
|
117
|
+
|
|
118
|
+
### 1. Workstream
|
|
119
|
+
An isolated development lane. Each workstream has its own:
|
|
120
|
+
|
|
121
|
+
- **git branch** (default `ws/<id>`, e.g. `ws/1`, `ws/2`)
|
|
122
|
+
- **working directory** — a git worktree under `worktrees/<id>/` next to your repo (default mode)
|
|
123
|
+
- **terminal window/pane** — one tmux window or zellij tab named after the workstream
|
|
124
|
+
- **task assignment** — one or more issues, or a free-form prompt
|
|
125
|
+
- **its own log file** — `worktrees/<id>/logs/worker.log`
|
|
126
|
+
|
|
127
|
+
### 2. Multiplexer
|
|
128
|
+
The terminal tool that hosts the visible windows. `workstreams` currently supports:
|
|
129
|
+
|
|
130
|
+
| Multiplexer | Layouts | Notes |
|
|
131
|
+
|-------------|---------|-------|
|
|
132
|
+
| `tmux` (default) | `even-horizontal`, `even-vertical`, `main-horizontal`, `tiled` | One **window per workstream** (cleanest for agents), or all in one tiled window. Detached sessions survive your logout. |
|
|
133
|
+
| `zellij` | tabs | One **tab per workstream**. Simpler scripting surface; `dispatch` targets the current tab only. |
|
|
134
|
+
|
|
135
|
+
The tmux/zellij session is auto-named `workstreams-<project>`.
|
|
136
|
+
|
|
137
|
+
### 3. Subagent
|
|
138
|
+
The coding agent doing the work inside a workstream. This is deliberately **free-form**: `claude-code`, `codex`, `opencode`, `qwen-code`, `mimocode`, `hermes`, `kilo-code`, `cline`, or any label you want. There is no vendor lock-in — a subagent is just an identifier used in the event log. The agent that actually runs is whatever command you send into the pane (see `dispatch`/`work`).
|
|
139
|
+
|
|
140
|
+
### 4. Event
|
|
141
|
+
A JSONL record written to a shared log whenever a subagent reports progress. Event types: `started`, `progress`, `completed`, `failed`, `error`, `done`. Events are the backbone of the dashboard and the cross-terminal notification flow.
|
|
142
|
+
|
|
143
|
+
### 5. Dashboard
|
|
144
|
+
`workstreams monitor` — a full-screen ANSI TUI (no external TUI deps) that re-renders every 2s (configurable) showing, per workstream: id, name, branch, git status, PID, last commit/activity, recent log alerts — plus a "Subagent Activity (last 10)" feed. Exit with `q` or Ctrl+C.
|
|
145
|
+
|
|
146
|
+
### Worktree mode vs Branch mode (`init --mode`)
|
|
147
|
+
|
|
148
|
+
- `worktree` (default, **recommended**): creates a real git worktree — a separate checkout directory under `worktrees/`. Maximum isolation; each lane has its own files. Slightly more disk.
|
|
149
|
+
- `branch`: all lanes share one directory and you switch branches. Faster and shares `node_modules`/`.venv` naturally, but agents switching branches concurrently can collide.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Quick Start (5 commands)
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# 0) install (once)
|
|
157
|
+
pip install "workstreams-cli[yaml]"
|
|
158
|
+
|
|
159
|
+
# 1) create 3 parallel lanes for a project, each in its own worktree + branch
|
|
160
|
+
workstreams init --project myproj --workstreams 3
|
|
161
|
+
|
|
162
|
+
# 2) open visible tmux windows and drop an agent into each (one window per lane)
|
|
163
|
+
workstreams start --multiplexer tmux --cmd "claude"
|
|
164
|
+
|
|
165
|
+
# 3) hand a task to lane 1 (fires a "started" event, notifies, sends the prompt into that pane)
|
|
166
|
+
workstreams dispatch --workstream 1 --subagent claude-code --issue 42 --prompt "Fix auth"
|
|
167
|
+
|
|
168
|
+
# 4) (optional) block the main terminal until lane 1's agent reports done/failed
|
|
169
|
+
# workstreams dispatch ... --wait
|
|
170
|
+
|
|
171
|
+
# 5) watch everything in one live dashboard (Ctrl+C to leave)
|
|
172
|
+
workstreams monitor
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Now you have three tmux windows, each running its own agent on its own branch, and a single dashboard watching all of them.
|
|
176
|
+
|
|
177
|
+
> **Tip:** to actually *see* the windows: `workstreams attach` (or `tmux attach -t workstreams-myproj`).
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## The Full Workflow
|
|
182
|
+
|
|
183
|
+
A complete feature-from-scratch to merged loop:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# ── 1. Plan ──────────────────────────────────────────────────────────────
|
|
187
|
+
# Decide N lanes and what each owns. e.g. backend / frontend / database.
|
|
188
|
+
|
|
189
|
+
# ── 2. Initialize ───────────────────────────────────────────────────────
|
|
190
|
+
workstreams init --project myproj \
|
|
191
|
+
--workstreams 3 \
|
|
192
|
+
--multiplexer tmux \
|
|
193
|
+
--layout even-horizontal \
|
|
194
|
+
--base-branch main \
|
|
195
|
+
--mode worktree
|
|
196
|
+
|
|
197
|
+
# Creates:
|
|
198
|
+
# worktrees/ws1/ (branch ws/1)
|
|
199
|
+
# worktrees/ws2/ (branch ws/2)
|
|
200
|
+
# worktrees/ws3/ (branch ws/3)
|
|
201
|
+
# .workstreams.yaml (the config file, in your repo root)
|
|
202
|
+
|
|
203
|
+
# ── 3. Start visible terminals ──────────────────────────────────────────
|
|
204
|
+
workstreams start --multiplexer tmux
|
|
205
|
+
# -> tmux session 'workstreams-myproj', one window per lane, persistent shell
|
|
206
|
+
|
|
207
|
+
# ── 4. Dispatch work ────────────────────────────────────────────────────
|
|
208
|
+
# Option A: send a ready-made agent command into the pane
|
|
209
|
+
workstreams dispatch --workstream 1 --subagent claude-code --issue 42 \
|
|
210
|
+
--prompt "Implement JWT auth"
|
|
211
|
+
|
|
212
|
+
# Option B: run a specific agent binary with a task (agent-agnostic)
|
|
213
|
+
workstreams work --workstream 2 --agent "codex" --task "Build the login UI" \
|
|
214
|
+
--subagent codex --issue 43
|
|
215
|
+
|
|
216
|
+
# Option C: a plain shell command in a lane (tests, builds, migrations)
|
|
217
|
+
workstreams run --workstream 3 --cmd "npm test"
|
|
218
|
+
|
|
219
|
+
# ── 5. Watch ────────────────────────────────────────────────────────────
|
|
220
|
+
workstreams monitor # live dashboard
|
|
221
|
+
workstreams events --since 30 # last 30 min of subagent events
|
|
222
|
+
workstreams logs --workstream 1 --follow # tail one lane's log
|
|
223
|
+
|
|
224
|
+
# ── 6. Keep lanes fresh with main ──────────────────────────────────────
|
|
225
|
+
workstreams sync --workstream 1 # merge origin/main into ws/1
|
|
226
|
+
workstreams sync --workstream 1 --rebase # ... or rebase instead
|
|
227
|
+
|
|
228
|
+
# ── 7. Ship ─────────────────────────────────────────────────────────────
|
|
229
|
+
workstreams pr --workstream 1 --title "feat: add JWT auth" \
|
|
230
|
+
--body "Closes #42" --draft
|
|
231
|
+
# -> pushes ws/1, runs: gh pr create --base main --head ws/1
|
|
232
|
+
|
|
233
|
+
# ── 8. Merge & clean up ─────────────────────────────────────────────────
|
|
234
|
+
workstreams merge --workstream 1 --method squash --delete-branch
|
|
235
|
+
workstreams workstream cleanup --workstream 1 --force
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Command Reference
|
|
241
|
+
|
|
242
|
+
Run `workstreams <command> --help` for per-command flags. `--project` and `--json` are accepted by almost every command.
|
|
243
|
+
|
|
244
|
+
| Command | What it does |
|
|
245
|
+
|---------|--------------|
|
|
246
|
+
| `init` | Create workstreams (worktrees + branches + `.workstreams.yaml`) |
|
|
247
|
+
| `start` | Open the multiplexer session and start the configured lanes (optionally running a command in each) |
|
|
248
|
+
| `attach` | Attach to the existing multiplexer session so you can *see* the windows |
|
|
249
|
+
| `status` | Print a one-shot status table (or `--json`) |
|
|
250
|
+
| `monitor` | Live TUI dashboard (default: refresh 2s; `q`/Ctrl+C exits) |
|
|
251
|
+
| `dispatch` | Send a task/prompt to a lane's pane; logs a `started` event; notifies |
|
|
252
|
+
| `work` | Run an arbitrary agent command in a lane (`<agent> <task>`); optional `--wait` |
|
|
253
|
+
| `run` | Run a plain shell command in a lane's directory (not sent to the pane) |
|
|
254
|
+
| `logs` | Tail a lane's `worker.log` (`--follow` to stream) |
|
|
255
|
+
| `tail` | Snapshot of the last N lines of one or all lanes' logs |
|
|
256
|
+
| `events` | Print recent subagent events (filter by lane/type/subagent; `--json`) |
|
|
257
|
+
| `event` | **Emit one event from the calling process** (the key agent-integration command) |
|
|
258
|
+
| `notify` | Send a desktop + file notification to other terminals |
|
|
259
|
+
| `assign` | Record issue numbers against a lane (bookkeeping in the lane log) |
|
|
260
|
+
| `sync` | `git fetch` + merge/rebase a lane with the base branch |
|
|
261
|
+
| `pr` | Push the lane's branch and open a GitHub PR via `gh` |
|
|
262
|
+
| `merge` | Merge the lane's PR via `gh` (squash/merge/rebase; optional `--delete-branch`) |
|
|
263
|
+
| `workstream add` | Add a new lane to the config (and create its worktree) |
|
|
264
|
+
| `workstream remove` | Remove a lane (optionally `--force` to delete its worktree) |
|
|
265
|
+
| `workstream cleanup` | Delete completed lanes' worktrees |
|
|
266
|
+
|
|
267
|
+
### Flag details (the ones that matter)
|
|
268
|
+
|
|
269
|
+
`init`
|
|
270
|
+
- `--workstreams N` — how many lanes to create (default 4)
|
|
271
|
+
- `--multiplexer tmux|zellij`
|
|
272
|
+
- `--layout even-horizontal|even-vertical|main-horizontal|tiled`
|
|
273
|
+
- `--base-branch main` — the branch all lanes fork from
|
|
274
|
+
- `--mode worktree|branch`
|
|
275
|
+
- `--agent auto|claude|codex|opencode|qwen|generic` — hint stored in config
|
|
276
|
+
- `--force` — overwrite an existing `.workstreams.yaml`
|
|
277
|
+
- `--no-worktrees` — write config only, skip `git worktree add` (useful to pre-plan then materialize later)
|
|
278
|
+
|
|
279
|
+
`start`
|
|
280
|
+
- `--workstream N` — start a single lane
|
|
281
|
+
- `--cmd "<command>"` — command to send into each pane (e.g. `claude`, `codex`)
|
|
282
|
+
- `--multiplexer` / `--layout` — override config for this run
|
|
283
|
+
|
|
284
|
+
`dispatch`
|
|
285
|
+
- `--workstream N` (required)
|
|
286
|
+
- `--subagent <label>` (required, free-form identifier)
|
|
287
|
+
- `--issue N` — GitHub issue number
|
|
288
|
+
- `--prompt "<text>"` — the task text sent into the pane
|
|
289
|
+
- `--agent <binary>` — if set, the pane receives `<binary> <prompt>`; if omitted, the prompt is sent verbatim
|
|
290
|
+
- `--wait` — block until that subagent emits `completed`/`failed`/`done` (4h safety timeout)
|
|
291
|
+
- `--multiplexer` — override
|
|
292
|
+
|
|
293
|
+
`work`
|
|
294
|
+
- `--workstream N`, `--agent <cmd>`, `--task <text>` (required)
|
|
295
|
+
- `--subagent <label>`, `--issue N` — event-log metadata
|
|
296
|
+
- `--wait` — block for a terminal event
|
|
297
|
+
|
|
298
|
+
`events` / `event`
|
|
299
|
+
- `events`: `--workstream N`, `--since <minutes>` (default 10), `--type started|progress|completed|failed|error|done`, `--subagent <label>`, `--limit N`, `--clear`, `--json`
|
|
300
|
+
- `event`: `event <started|progress|completed|failed|error|done> --project P --subagent L [--workstream N] [--issue N] [--message "..."] [--data '{"files":3}'] [--json]`
|
|
301
|
+
|
|
302
|
+
`sync` — `--workstream N` (omit to sync all), `--rebase` (else merge)
|
|
303
|
+
|
|
304
|
+
`pr` — `--workstream N --title "..." --body "..." [--base BRANCH] [--draft]`
|
|
305
|
+
|
|
306
|
+
`merge` — `--workstream N [--method merge|squash|rebase] [--delete-branch] [--auto]`
|
|
307
|
+
|
|
308
|
+
`workstream add` — `--name X --branch B [--path P] [--command CMD]`
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Configuration (.workstreams.yaml)
|
|
313
|
+
|
|
314
|
+
Created by `init` in your repo's root. Edit by hand to give lanes meaningful names, commands, and env. If PyYAML is installed (`workstreams-cli[yaml]`) this is read/written as YAML; otherwise workstreams falls back to a JSON file (`.workstreams.json`).
|
|
315
|
+
|
|
316
|
+
```yaml
|
|
317
|
+
project: myproject
|
|
318
|
+
multiplexer: tmux # tmux | zellij | nami | lmux | wmux | herdr
|
|
319
|
+
layout: even-horizontal # even-horizontal | even-vertical | main-horizontal | tiled
|
|
320
|
+
base_branch: main
|
|
321
|
+
mode: worktree # worktree | branch
|
|
322
|
+
agent: auto # auto | claude | codex | opencode | qwen | generic
|
|
323
|
+
|
|
324
|
+
workstreams:
|
|
325
|
+
- id: 1
|
|
326
|
+
name: backend
|
|
327
|
+
path: ./backend # worktree dir (relative to repo root) or subdir
|
|
328
|
+
branch: ws/backend
|
|
329
|
+
command: "npm run dev" # auto-run on start (or leave "" and use --cmd)
|
|
330
|
+
env:
|
|
331
|
+
PORT: "3001"
|
|
332
|
+
- id: 2
|
|
333
|
+
name: frontend
|
|
334
|
+
path: ./frontend
|
|
335
|
+
branch: ws/frontend
|
|
336
|
+
command: "npm run dev"
|
|
337
|
+
env:
|
|
338
|
+
PORT: "3002"
|
|
339
|
+
|
|
340
|
+
shared_deps: # directories to symlink/share across lanes
|
|
341
|
+
- node_modules
|
|
342
|
+
- .venv
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Config resolution order** (highest wins): CLI flag → `.workstreams.yaml` → environment variable → default.
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## How the Multiplexers Work
|
|
350
|
+
|
|
351
|
+
### tmux (default, most robust)
|
|
352
|
+
- Session name: `workstreams-<project>` (detached, survives logout).
|
|
353
|
+
- **Window-per-workstream** (all layouts except `tiled`): each lane becomes a tmux *window* named after the workstream (`backend`, `frontend`, …). Windows are addressable **by name** (`<session>:<window-name>`), which is how `dispatch`/`work` send the prompt to the *correct* lane.
|
|
354
|
+
- **Tiled layout**: all lanes are panes in one window, targeted by pane index (`<session>:0.<idx>`).
|
|
355
|
+
- Every window/pane runs a persistent interactive `bash` so the session never dies when a command exits.
|
|
356
|
+
- `attach` = `tmux attach -t workstreams-<project>` (press `b d` to detach; `Ctrl+c`/`q` exits from the dashboard only).
|
|
357
|
+
|
|
358
|
+
### zellij
|
|
359
|
+
- Session name: `workstreams-<project>`; one *tab* per workstream.
|
|
360
|
+
- `send_command` targets the **current tab only** (Zellij has no `send-keys` equivalent); a warning is printed and the command runs via `zellij run`. Prefer tmux for precise per-lane dispatch.
|
|
361
|
+
- `attach` = `zellij attach workstreams-<project>`.
|
|
362
|
+
|
|
363
|
+
> If a command reports "tmux session 'workstreams-<project>' is not running. Run `workstreams start` first", that means the pane target exists but the session isn't up yet — start it.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Subagent Event System
|
|
368
|
+
|
|
369
|
+
This is the heart of the cross-process monitoring story. **Any** process — an agent inside a tmux pane, a CI job, a cron, a Python script, the main terminal — can write an event, and **any** other terminal can read it. Events are the data source for `monitor`, `events`, and `status --live`.
|
|
370
|
+
|
|
371
|
+
### Event shape (JSONL, one object per line)
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
{
|
|
375
|
+
"workstream_id": 1,
|
|
376
|
+
"subagent": "claude-code",
|
|
377
|
+
"issue": 42,
|
|
378
|
+
"event_type": "progress",
|
|
379
|
+
"message": "Created auth middleware",
|
|
380
|
+
"timestamp": "2026-01-01T12:00:00+00:00",
|
|
381
|
+
"data": {"files": 2}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
`event_type` ∈ `started | progress | completed | failed | error | done`.
|
|
386
|
+
(`done` is the signal `--wait` blocks on; `completed`/`failed`/`done` all unblock a `--wait`.)
|
|
387
|
+
|
|
388
|
+
### Two ways to emit an event
|
|
389
|
+
|
|
390
|
+
**A. One-line CLI (no Python import needed — easiest for shell/agent scripts):**
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# mark start
|
|
394
|
+
workstreams event started --project myproj --workstream 1 \
|
|
395
|
+
--subagent claude-code --issue 42 --message "Fix auth"
|
|
396
|
+
|
|
397
|
+
# report progress with structured data
|
|
398
|
+
workstreams event progress --project myproj --workstream 1 \
|
|
399
|
+
--subagent claude-code --issue 42 \
|
|
400
|
+
--message "Wrote middleware" --data '{"files": 2}'
|
|
401
|
+
|
|
402
|
+
# mark completion
|
|
403
|
+
workstreams event completed --project myproj --workstream 1 \
|
|
404
|
+
--subagent claude-code --issue 42 --message "Auth complete"
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**B. Python API (for agents/scripts written in Python):**
|
|
408
|
+
|
|
409
|
+
```python
|
|
410
|
+
from workstreams import (
|
|
411
|
+
subagent_started, subagent_progress,
|
|
412
|
+
subagent_completed, subagent_failed,
|
|
413
|
+
subagent_error, subagent_done, subagent_report,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
subagent_started("myproj", 1, "claude-code", 42, "Implement JWT auth")
|
|
417
|
+
subagent_progress("myproj", 1, "claude-code", 42, "Created auth middleware", {"files": 2})
|
|
418
|
+
subagent_completed("myproj", 1, "claude-code", 42, "Auth complete", {"tests": "passed"})
|
|
419
|
+
|
|
420
|
+
# generic form
|
|
421
|
+
subagent_report("myproj", 1, "claude-code", 42, "progress", "Working...", {"step": 3})
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Reading events
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
workstreams events --since 30 # last 30 min, all lanes
|
|
428
|
+
workstreams events --workstream 1 --type progress # one lane, filtered
|
|
429
|
+
workstreams events --json | jq . # machine-readable
|
|
430
|
+
workstreams monitor # live dashboard (includes event feed)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Concurrency & storage
|
|
434
|
+
Events are appended to `~/.workstreams/<project>/events.jsonl` using `O_APPEND` (atomic for small writes on POSIX) plus a short lock file that guards large lines and self-heals stale locks after 10s. Multiple processes can append concurrently without corruption.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Environment Variables
|
|
439
|
+
|
|
440
|
+
All are overridable in the config file / CLI; env vars are a fallback when neither is set.
|
|
441
|
+
|
|
442
|
+
| Variable | Controls | Default |
|
|
443
|
+
|----------|----------|---------|
|
|
444
|
+
| `WORKSTREAMS_PROJECT` | project name | repo dir name |
|
|
445
|
+
| `WORKSTREAMS_MULTIPLEXER` | `tmux`/`zellij` | `tmux` |
|
|
446
|
+
| `WORKSTREAMS_LAYOUT` | layout | `even-horizontal` |
|
|
447
|
+
| `WORKSTREAMS_BASE_BRANCH` | base branch | `main` |
|
|
448
|
+
| `WORKSTREAMS_WORKTREE_MODE` | `worktree`/`branch` | `worktree` |
|
|
449
|
+
| `WORKSTREAMS_SHARED_DEPS` | comma-separated shared dirs | *(none)* |
|
|
450
|
+
| `WORKSTREAMS_DATA_DIR` | relocate the event/notification store | `~/.workstreams` |
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Exit Codes
|
|
455
|
+
|
|
456
|
+
| Code | Meaning |
|
|
457
|
+
|------|---------|
|
|
458
|
+
| 0 | Success |
|
|
459
|
+
| 1 | General error (including a `failed` subagent event under `--wait`) |
|
|
460
|
+
| 2 | Invalid arguments / no command / `--wait` timed out (4h) |
|
|
461
|
+
| 3 | Git / `gh` error (sync, pr, merge, push) |
|
|
462
|
+
| 4 | Multiplexer unavailable / unsupported |
|
|
463
|
+
| 5 | Workstream not found |
|
|
464
|
+
| 6 | Already running |
|
|
465
|
+
| 7 | Dirty working tree |
|
|
466
|
+
| 8 | Merge conflict |
|
|
467
|
+
| 9 | Permission denied |
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## Data Locations
|
|
472
|
+
|
|
473
|
+
Everything workstreams writes lives in predictable places:
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
<repo root>/
|
|
477
|
+
├── .workstreams.yaml # config (or .workstreams.json without PyYAML)
|
|
478
|
+
└── worktrees/
|
|
479
|
+
├── ws1/ (branch ws/1) # git worktree for lane 1
|
|
480
|
+
│ └── logs/worker.log # lane 1 activity log
|
|
481
|
+
├── ws2/ (branch ws/2)
|
|
482
|
+
└── ws3/ (branch ws/3)
|
|
483
|
+
|
|
484
|
+
~/.workstreams/<project>/
|
|
485
|
+
├── events.jsonl # shared subagent event stream
|
|
486
|
+
├── events.lock # (transient) append lock
|
|
487
|
+
└── notifications.jsonl # shared notification queue
|
|
488
|
+
|
|
489
|
+
$WORKSTREAMS_DATA_DIR # override the whole ~/.workstreams base if set
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Integrating With Coding Agents (Skill)
|
|
495
|
+
|
|
496
|
+
The repo ships an agent skill under `skills/` that teaches your coding agent (Claude Code, Codex, OpenCode, Qwen Code, MiMoCode, Hermes, Kilo Code, Cline, …) the exact commands above, so it can plan, dispatch, monitor, and collect work autonomously:
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
npx skills add https://github.com/Dream-Pixels-Forge/workstreams-cli/tree/main/skills
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
The skill (`skills/SKILL.md`, plus `skills/REFERENCE.md` and `skills/EXAMPLES.md`) maps to this CLI:
|
|
503
|
+
|
|
504
|
+
- `init` / `start` — spin up parallel visible lanes
|
|
505
|
+
- `dispatch` / `work` — hand a task to a named subagent in a visible pane
|
|
506
|
+
- `event` / `events` / `monitor` — subagents report; you watch the dashboard
|
|
507
|
+
- `pr` / `merge` / `workstream cleanup` — finish the loop with git worktrees
|
|
508
|
+
|
|
509
|
+
Compare with a single-agent workflow: `workstreams` replaces "one agent, sequential, one PR at a time" with "many agents, parallel, many PRs at a time" while keeping each agent in its own branch + terminal.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## CI/CD Integration
|
|
514
|
+
|
|
515
|
+
workstreams is useful in CI to run a matrix of lane-scoped tests, and (locally) to drive auto-merge on green.
|
|
516
|
+
|
|
517
|
+
```yaml
|
|
518
|
+
# .github/workflows/workstreams.yml
|
|
519
|
+
name: Workstreams CI
|
|
520
|
+
on:
|
|
521
|
+
pull_request:
|
|
522
|
+
types: [opened, synchronize, reopened]
|
|
523
|
+
|
|
524
|
+
jobs:
|
|
525
|
+
lane-test:
|
|
526
|
+
runs-on: ubuntu-latest
|
|
527
|
+
strategy:
|
|
528
|
+
matrix:
|
|
529
|
+
workstream: [1, 2, 3, 4]
|
|
530
|
+
steps:
|
|
531
|
+
- uses: actions/checkout@v4
|
|
532
|
+
- run: pip install workstreams-cli
|
|
533
|
+
- run: |
|
|
534
|
+
workstreams init --project ci-test --workstreams 4
|
|
535
|
+
workstreams sync --workstream ${{ matrix.workstream }}
|
|
536
|
+
- run: workstreams run --workstream ${{ matrix.workstream }} --cmd "pytest"
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Auto-merge on green (local helper):
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
# after a lane's PR is green on CI:
|
|
543
|
+
workstreams merge --workstream 1 --auto --method squash
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Troubleshooting
|
|
549
|
+
|
|
550
|
+
| Symptom | What to do |
|
|
551
|
+
|---------|-----------|
|
|
552
|
+
| `pip install workstreams-cli` fails on Ubuntu/Debian with "externally-managed-environment" (PEP 668) | System pip is locked down. Use `pipx install workstreams-cli` or a virtualenv: `python3 -m venv ~/.workstreams-venv && ~/.workstreams-venv/bin/pip install "workstreams-cli[yaml]"`. The package on PyPI is `workstreams-cli` (the module/CLI command stays `workstreams`). |
|
|
553
|
+
| `tmux session 'workstreams-<p>' is not running` | Run `workstreams start` first; the pane targets exist but the detached session isn't up. |
|
|
554
|
+
| `git worktree add failed for ws/N` during `init` | Stale worktree metadata. Run `git worktree prune`, then retry. Or pre-plan with `init --no-worktrees` and materialize later. |
|
|
555
|
+
| Base branch `main` not found at init | workstreams falls back to `HEAD` and prints a note. Set `--base-branch` to the real branch. |
|
|
556
|
+
| Port conflicts between lanes | Give each lane a distinct port in its `env` block (e.g. 3001/3002). |
|
|
557
|
+
| Git conflicts on `sync` | `workstreams sync --workstream N --rebase` to rebase onto the base branch; resolve, then re-push. |
|
|
558
|
+
| Events not showing in `monitor` | The dashboard reads the last 10 min. Check `~/.workstreams/<project>/events.jsonl` and that the writer used the same `--project` name. |
|
|
559
|
+
| No desktop notification | Desktop sender is best-effort (`notify-send`/`terminal-notify`/`osascript`); the notification *file* is always written, so poll `notifications.jsonl` or rely on the dashboard. |
|
|
560
|
+
| `dispatch`/`work` says "pane send failed" but no error | The multiplexer binary may be missing or the session was killed. Verify with `workstreams attach`. |
|
|
561
|
+
| zellij `send_command` runs in the wrong tab | Known limitation — zellij dispatch targets the *current* tab. Use tmux for reliable per-lane targeting. |
|
|
562
|
+
| Config not loading | Without PyYAML, workstreams uses the built-in minimal parser (scalars + simple lists of maps). For full YAML, `pip install "workstreams-cli[yaml]"`. |
|
|
563
|
+
|
|
564
|
+
### Quick diagnostics
|
|
565
|
+
|
|
566
|
+
```bash
|
|
567
|
+
workstreams status # one-shot table, all lanes
|
|
568
|
+
workstreams events --since 60 --json # recent event stream
|
|
569
|
+
ls -la ~/.workstreams/<project>/ # confirm events.jsonl exists
|
|
570
|
+
tmux list-sessions # see if the session is actually up
|
|
571
|
+
workstreams logs --workstream 1 --lines 50
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## Best Practices
|
|
577
|
+
|
|
578
|
+
1. **Keep lanes small** — one or two issues per workstream; split if a lane grows.
|
|
579
|
+
2. **Prefer worktree mode** for isolation; reserve branch mode for small, fast, shared-dep projects.
|
|
580
|
+
3. **Share bulky deps** (`node_modules`, `.venv`, `target/`) via `shared_deps` to save disk + install time.
|
|
581
|
+
4. **Sync often** — `workstreams sync` every ~30–60 min to avoid large rebases later.
|
|
582
|
+
5. **Give each lane its own service ports/DB names** in `env` to prevent cross-lane interference.
|
|
583
|
+
6. **Watch from one terminal** — leave `workstreams monitor` open; dispatch from the others.
|
|
584
|
+
7. **Emit `completed`/`failed` events** from any agent you dispatch so `--wait` and the dashboard reflect true terminal states.
|
|
585
|
+
8. **Clean up after merge** — `workstream cleanup` to remove finished lanes' worktrees and keep `git worktree list` tidy.
|
|
586
|
+
9. **Use `--wait`** on `dispatch`/`work` when the next step depends on a lane finishing.
|
|
587
|
+
10. **Commit the `.workstreams.yaml`** into the repo so the whole team (and CI) uses the same lane layout.
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## Repository Layout
|
|
592
|
+
|
|
593
|
+
```
|
|
594
|
+
workstreams-cli/
|
|
595
|
+
├── src/workstreams/
|
|
596
|
+
│ ├── __init__.py # public API (models, manager, event fns, multiplexers)
|
|
597
|
+
│ ├── cli.py # argparse CLI (entry: workstreams.cli:main)
|
|
598
|
+
│ ├── config.py # YAML/JSON config load+save (with no-pyyaml fallback)
|
|
599
|
+
│ ├── models.py # dataclasses: WorkstreamConfig, WorkstreamsConfig,
|
|
600
|
+
│ │ # WorkstreamStatus, SubagentEvent
|
|
601
|
+
│ ├── manager.py # WorkstreamsManager: init/start/dispatch/work/
|
|
602
|
+
│ │ # sync/pr/merge/run/logs/events/notify
|
|
603
|
+
│ ├── event_log.py # append-only JSONL event log (concurrent-safe)
|
|
604
|
+
│ ├── notifier.py # desktop + file notifications
|
|
605
|
+
│ ├── dashboard.py # live ANSI TUI monitor
|
|
606
|
+
│ ├── subagent_client.py # subagent_* event emitters (Python API)
|
|
607
|
+
│ └── multiplexer/ # tmux + zellij backends (MultiplexerBase)
|
|
608
|
+
├── skills/ # agent skill: SKILL.md, REFERENCE.md, EXAMPLES.md
|
|
609
|
+
├── scripts/ # standalone script variants + helpers
|
|
610
|
+
├── pyproject.toml # packaging (pip install workstreams-cli)
|
|
611
|
+
└── .github/workflows/ # PyPI release on GitHub Release (OIDC)
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## Contributing & License
|
|
617
|
+
|
|
618
|
+
- License: **MIT**
|
|
619
|
+
- Author: **Dream-Pixels-Forge**
|
|
620
|
+
- Development: `pip install -e ".[yaml,dev]"` then `pytest` (testpaths `tests`, pythonpath `src`)
|
|
621
|
+
|
|
622
|
+
**Project page:** https://github.com/Dream-Pixels-Forge/workstreams-cli
|