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.
Files changed (29) hide show
  1. workstreams_cli-0.5.0/PKG-INFO +622 -0
  2. workstreams_cli-0.5.0/README.md +592 -0
  3. workstreams_cli-0.5.0/pyproject.toml +62 -0
  4. workstreams_cli-0.5.0/setup.cfg +4 -0
  5. workstreams_cli-0.5.0/src/workstreams/__init__.py +71 -0
  6. workstreams_cli-0.5.0/src/workstreams/cli.py +499 -0
  7. workstreams_cli-0.5.0/src/workstreams/config.py +227 -0
  8. workstreams_cli-0.5.0/src/workstreams/dashboard.py +341 -0
  9. workstreams_cli-0.5.0/src/workstreams/event_log.py +240 -0
  10. workstreams_cli-0.5.0/src/workstreams/manager.py +573 -0
  11. workstreams_cli-0.5.0/src/workstreams/models.py +121 -0
  12. workstreams_cli-0.5.0/src/workstreams/multiplexer/__init__.py +32 -0
  13. workstreams_cli-0.5.0/src/workstreams/multiplexer/base.py +44 -0
  14. workstreams_cli-0.5.0/src/workstreams/multiplexer/tmux.py +202 -0
  15. workstreams_cli-0.5.0/src/workstreams/multiplexer/tmux_compatible.py +22 -0
  16. workstreams_cli-0.5.0/src/workstreams/multiplexer/zellij.py +109 -0
  17. workstreams_cli-0.5.0/src/workstreams/notifier.py +147 -0
  18. workstreams_cli-0.5.0/src/workstreams/py.typed +0 -0
  19. workstreams_cli-0.5.0/src/workstreams/subagent_client.py +125 -0
  20. workstreams_cli-0.5.0/src/workstreams_cli.egg-info/PKG-INFO +622 -0
  21. workstreams_cli-0.5.0/src/workstreams_cli.egg-info/SOURCES.txt +27 -0
  22. workstreams_cli-0.5.0/src/workstreams_cli.egg-info/dependency_links.txt +1 -0
  23. workstreams_cli-0.5.0/src/workstreams_cli.egg-info/entry_points.txt +2 -0
  24. workstreams_cli-0.5.0/src/workstreams_cli.egg-info/requires.txt +7 -0
  25. workstreams_cli-0.5.0/src/workstreams_cli.egg-info/top_level.txt +1 -0
  26. workstreams_cli-0.5.0/tests/test_config.py +166 -0
  27. workstreams_cli-0.5.0/tests/test_event_log.py +249 -0
  28. workstreams_cli-0.5.0/tests/test_models.py +16 -0
  29. 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
+ ![Workstreams Banner](assets/workstreams-cli-banner-a.png)
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