playmaker-cli 0.5.1__tar.gz → 0.7.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 (35) hide show
  1. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/CHANGELOG.md +71 -0
  2. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/PKG-INFO +57 -13
  3. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/README.md +54 -10
  4. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/pyproject.toml +4 -2
  5. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/skills/playmaker-coach/SKILL.md +12 -7
  6. playmaker_cli-0.7.0/src/playmaker/agents/opencode.py +461 -0
  7. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/cli.py +50 -14
  8. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/quotas.py +160 -0
  9. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/registry.py +2 -0
  10. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/state.py +47 -3
  11. playmaker_cli-0.7.0/tests/test_batch.py +129 -0
  12. playmaker_cli-0.7.0/tests/test_opencode.py +430 -0
  13. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_permissions.py +18 -0
  14. playmaker_cli-0.7.0/tests/test_quotas_zai.py +165 -0
  15. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_registry.py +2 -2
  16. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_state.py +78 -0
  17. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/.gitignore +0 -0
  18. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/LICENSE +0 -0
  19. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/__init__.py +0 -0
  20. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/__main__.py +0 -0
  21. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/agents/__init__.py +0 -0
  22. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/agents/agy.py +0 -0
  23. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/agents/base.py +0 -0
  24. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/agents/claude.py +0 -0
  25. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/agents/codex.py +0 -0
  26. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/agents/gemini.py +0 -0
  27. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/config.py +0 -0
  28. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/notify.py +0 -0
  29. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/src/playmaker/watcher.py +0 -0
  30. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/__init__.py +0 -0
  31. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_agy.py +0 -0
  32. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_claude.py +0 -0
  33. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_codex.py +0 -0
  34. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_quotas_antigravity.py +0 -0
  35. {playmaker_cli-0.5.1 → playmaker_cli-0.7.0}/tests/test_skill.py +0 -0
@@ -5,6 +5,77 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.0] - 2026-08-05
9
+
10
+ ### Fixed
11
+
12
+ - **A batch label is a name you reuse, so the summary now belongs to the
13
+ fan-out rather than the label.** `--batch dashboard` twice used to mean one
14
+ batch forever: the second fan-out never pinged at all, because exactly-once
15
+ was guarded by an `O_EXCL` sentinel in `logs/` that nothing ever removed —
16
+ and with the sentinel gone it would have counted yesterday's sessions too,
17
+ `4/4 done · codex ✓ · claude ✓ · codex ✓ · agy ✓` for a fan-out of two, with
18
+ the stale outputs pasted into the combined `/tmp` file. The claim now lives in
19
+ `state.db` as a `batch_notified` column: `list_batch` returns the sessions not
20
+ yet reported, and the finisher that wins the claim releases the label for the
21
+ next fan-out. Cross-process safety is unchanged — the loser's `UPDATE` finds
22
+ its set already claimed, rolls back and stays quiet.
23
+
24
+ - **`playmaker kill` drains the batch it empties.** `killed` is terminal like
25
+ `done` and `failed`, but `kill` never finalised, so killing a fan-out's last
26
+ live session left nobody to notice the batch had drained: no summary, ever.
27
+
28
+ ### Changed
29
+
30
+ - **The combined batch file moved out of `/tmp`.** It is what a batch
31
+ notification opens on click, so it now lands with the outputs it quotes —
32
+ `~/.playmaker/outputs/batch-<label>.md` rather than
33
+ `/tmp/playmaker-batch-<label>.md`, a predictable name in a world-writable
34
+ directory built from a label the user chose. It also meant the test suite
35
+ wrote outside `tmp_path`.
36
+
37
+ ## [0.6.0] - 2026-07-27
38
+
39
+ ### Added
40
+
41
+ - **`opencode` agent handler — one lane, ~75 providers.** The ask was z.ai GLM
42
+ support, but playmaker never talks to a model API: it drives agent CLIs. So
43
+ rather than teach the claude handler an `ANTHROPIC_BASE_URL` override — which
44
+ would have *replaced* the Anthropic lane rather than added one — the handler
45
+ wraps [opencode](https://opencode.ai), whose `--model provider/model` already
46
+ reaches GLM (`zai-coding-plan/glm-5.2`), local LMStudio/MLX models, and
47
+ everything else on models.dev. Oneshot via `opencode run --format json`,
48
+ resume via `-s <id>`; every JSONL event carries `sessionID`, so a detached
49
+ dispatch records the id from the first line rather than at completion.
50
+
51
+ Four behaviours worth knowing. opencode resolves its working directory from
52
+ `process.env.PWD`, which `subprocess.Popen(cwd=…)` does not update — left
53
+ alone it ignores `--cwd` and writes into whatever directory the coach was
54
+ sitting in, so the handler passes `--dir` and corrects `PWD`.
55
+ opencode ≥1.18 keeps transcripts in SQLite
56
+ (`opencode.db`), not one file per session, so playmaker writes a pointer at
57
+ `~/.playmaker/opencode/<id>.session` and reads the `session`/`message`/`part`
58
+ tables live — which keeps `thread --follow` current rather than frozen.
59
+ Cost comes from opencode's own per-session accounting rather than the stream,
60
+ because `run --format json` can exit before its final `step_finish`
61
+ ([opencode#26855](https://github.com/anomalyco/opencode/issues/26855)).
62
+ And `[agents.opencode] model` matters more than the usual `--model` default:
63
+ left unset, opencode falls back to whatever its own `opencode.json` says,
64
+ which is typically the model you last picked interactively rather than the
65
+ one you meant to dispatch to.
66
+
67
+ Permissions follow agy, for the same reason: `--auto` is opencode's only
68
+ lever, so a detached run either auto-approves or comes back having done
69
+ nothing. `yolo` defaults to `true`; narrow it with the `permission` block in
70
+ your own opencode.json, which `--auto` still honours for denies.
71
+ - **`zai` quota probe** — GLM Coding Plan windows (5-hour, weekly, and the
72
+ monthly MCP tool pool) from `api.z.ai/api/monitor/usage/quota/limit`, keyed
73
+ off the credential opencode already wrote to its `auth.json`, so playmaker
74
+ still stores no secret of its own. Reported as its own provider rather than
75
+ under `opencode`, because the quota belongs to the plan — an opencode lane
76
+ pointed at a local model spends nothing here. Degrades to *unsupported*, not
77
+ an error row, when no Z.ai credential exists.
78
+
8
79
  ## [0.5.1] - 2026-07-27
9
80
 
10
81
  ### Fixed
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: playmaker-cli
3
- Version: 0.5.1
4
- Summary: Playing-coach CLI for orchestrating Claude Code, Codex and Antigravity sub-agents in parallel.
3
+ Version: 0.7.0
4
+ Summary: Playing-coach CLI for orchestrating Claude Code, Codex, Antigravity and opencode sub-agents in parallel.
5
5
  Project-URL: Homepage, https://github.com/vladsafedev/playmaker
6
6
  Project-URL: Repository, https://github.com/vladsafedev/playmaker
7
7
  Project-URL: Issues, https://github.com/vladsafedev/playmaker/issues
8
8
  Author-email: Vladislav Shulyugin <vladislav.shulyugin@gmail.com>
9
9
  License-Expression: MIT
10
10
  License-File: LICENSE
11
- Keywords: agents,ai,antigravity,claude,claude-code,cli,codex,gemini,orchestration,subagents
11
+ Keywords: agents,ai,antigravity,claude,claude-code,cli,codex,gemini,glm,opencode,orchestration,subagents
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Environment :: Console
14
14
  Classifier: Intended Audience :: Developers
@@ -28,11 +28,11 @@ Description-Content-Type: text/markdown
28
28
  # playmaker
29
29
 
30
30
  [![CI](https://github.com/vladsafedev/playmaker/actions/workflows/ci.yml/badge.svg)](https://github.com/vladsafedev/playmaker/actions/workflows/ci.yml)
31
- [![PyPI](https://img.shields.io/pypi/v/playmaker-cli.svg)](https://pypi.org/project/playmaker-cli/)
32
- [![Python](https://img.shields.io/pypi/pyversions/playmaker-cli.svg)](https://pypi.org/project/playmaker-cli/)
31
+ [![PyPI](https://img.shields.io/pypi/v/playmaker-cli.svg?cacheSeconds=3600)](https://pypi.org/project/playmaker-cli/)
32
+ [![Python](https://img.shields.io/pypi/pyversions/playmaker-cli.svg?cacheSeconds=3600)](https://pypi.org/project/playmaker-cli/)
33
33
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
34
34
 
35
- **Run Claude Code, Codex and Antigravity as parallel sub-agents from one terminal — and spend three separate quotas instead of one.**
35
+ **Run Claude Code, Codex, Antigravity and opencode as parallel sub-agents from one terminal — and spend separate quotas instead of one.**
36
36
 
37
37
  You stay in your Claude Code session doing the part only you can do. `playmaker`
38
38
  fans the rest out to other agent CLIs as detached processes, tracks them,
@@ -74,10 +74,12 @@ in one serial session:
74
74
  1. **Wall-clock speed.** A task that decomposes into 3–5 independent
75
75
  work-streams (schema, backend, frontend, tests, docs) finishes 2–4× faster
76
76
  when each stream runs as its own parallel agent.
77
- 2. **Provider arbitrage.** Codex and Antigravity quotas are entirely separate
78
- pools from your Anthropic plan. Every slice you hand them is capacity your
79
- main session never spends — and Antigravity's roster includes Claude
80
- Sonnet/Opus, so even Claude-quality work can run on Google's pool.
77
+ 2. **Provider arbitrage.** Codex, Antigravity and opencode quotas are entirely
78
+ separate pools from your Anthropic plan. Every slice you hand them is
79
+ capacity your main session never spends — and Antigravity's roster includes
80
+ Claude Sonnet/Opus, so even Claude-quality work can run on Google's pool.
81
+ `opencode` widens this the most: one CLI fronting ~75 providers, from a z.ai
82
+ GLM coding plan to models running locally on your own machine.
81
83
  3. **Bucket arbitrage inside one plan.** Headless `claude -p` draws on the same
82
84
  subscription as your interactive session, but per-model weekly buckets are
83
85
  separate — dispatching `--model sonnet` spends Sonnet's usually-idle bucket
@@ -134,6 +136,16 @@ The other agents differ, because their CLIs do:
134
136
  either auto-approves or comes back having done nothing. It therefore
135
137
  defaults to `yolo = true`; layer `sandbox = true` on top for agy's own
136
138
  terminal restrictions.
139
+ - **opencode** is the same story: its only lever is `--auto`, so it also
140
+ defaults to `yolo = true`. The granular control lives in opencode's own
141
+ config rather than in playmaker — and `--auto` still honours it, because it
142
+ auto-approves only what you have not explicitly denied:
143
+
144
+ ```jsonc
145
+ // ~/.config/opencode/opencode.json
146
+ { "permission": { "edit": "allow", "bash": "allow", "webfetch": "deny" } }
147
+ ```
148
+
137
149
  - **gemini** (legacy) runs with `--yolo`.
138
150
 
139
151
  ## Install
@@ -170,6 +182,7 @@ playmaker agents # which agent CLIs are reachable
170
182
  | **Claude Code** | `npm i -g @anthropic-ai/claude-code` | `--model sonnet` / `opus` / `haiku` |
171
183
  | **Codex CLI** | `npm i -g @openai/codex` | the model roster depends on your plan; omit `--model` to use the account default |
172
184
  | **Antigravity (`agy`)** | bundled with [Antigravity](https://antigravity.google) | `--model claude-opus-4-6-thinking` — the roster moves, so read it from `agy models` |
185
+ | **opencode** | `brew install sst/tap/opencode` (or see [opencode.ai](https://opencode.ai)) | `--model provider/model`, e.g. `zai-coding-plan/glm-5.2`; roster from `opencode models`, providers from `opencode auth login` |
173
186
  | **Gemini CLI** (legacy) | `npm i -g @google/gemini-cli` | still supported, superseded by `agy` |
174
187
 
175
188
  At least one is required; `playmaker agents` tells you which it can see.
@@ -233,9 +246,11 @@ flowchart LR
233
246
  P --> A1["claude -p<br/>--model sonnet"]
234
247
  P --> A2["codex exec"]
235
248
  P --> A3["agy -p"]
249
+ P --> A4["opencode run<br/>-m zai-coding-plan/glm-5.2"]
236
250
  A1 --> S[("state.db<br/>outputs/ + logs/")]
237
251
  A2 --> S
238
252
  A3 --> S
253
+ A4 --> S
239
254
  S -->|"list / thread / summary"| C
240
255
  S -.->|"batch drained"| N["one ping"]
241
256
  ```
@@ -248,8 +263,10 @@ bookkeeping in between.
248
263
  ├── state.db SQLite — sessions, status, pids, models, output paths
249
264
  ├── config.toml
250
265
  ├── agents/ optional agent profile markdown (claude.md, codex.md, agy.md…)
251
- ├── outputs/ final output per session — .md, or .json if the agent returned JSON
266
+ ├── outputs/ final output per session — .md, or .json if the agent returned JSON,
267
+ │ plus batch-<label>.md, every output in one fan-out combined
252
268
  ├── logs/ subprocess stdout for detached runs
269
+ ├── opencode/ pointer per opencode session (its transcript lives in SQLite)
253
270
  └── quotas.json latest capacity snapshot
254
271
  ```
255
272
 
@@ -261,6 +278,7 @@ and locates the session file the tool writes locally. Empirically:
261
278
  | Claude | `~/.claude/projects/<cwd-with-slashes-as-dashes>/<id>.jsonl` |
262
279
  | Codex | `~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ts>-<thread_id>.jsonl` |
263
280
  | Antigravity | `~/.gemini/antigravity-cli/brain/<conversation-id>/.system_generated/logs/transcript_full.jsonl` |
281
+ | opencode | SQLite — `~/.local/share/opencode/opencode.db` (`session` / `message` / `part`); playmaker keeps a pointer at `~/.playmaker/opencode/<id>.session` |
264
282
  | Gemini | `~/.gemini/tmp/<cwd-basename>/chats/session-<ts>-<short_id>.{json,jsonl}` |
265
283
 
266
284
  `thread` and `summary` normalize all of them into the same turn list, so every
@@ -276,6 +294,21 @@ dispatch; and a wrong `--model` is a silent failure on both **codex** (reports
276
294
  `turn.failed` while exiting 0) and **agy** (runs its default model instead) —
277
295
  playmaker turns both into real errors.
278
296
 
297
+ **opencode** deserves its own note, because one CLI is many providers. Models
298
+ are `provider/model` strings from `opencode models`, and if you don't pass
299
+ `--model` opencode falls back to the default in *its* config — often whatever
300
+ you last picked interactively. Set the lane's default once:
301
+
302
+ ```toml
303
+ [agents.opencode]
304
+ model = "zai-coding-plan/glm-5.2"
305
+ ```
306
+
307
+ It also has agy's working-directory problem in a different costume: opencode
308
+ reads `process.env.PWD`, which a subprocess `cwd` does not update, so left
309
+ alone it would ignore `--cwd` and write into the directory *you* were standing
310
+ in. playmaker passes `--dir` and fixes up `PWD`, so `--cwd` means what it says.
311
+
279
312
  ## Notifications
280
313
 
281
314
  Every detached dispatch pings when it finishes. With
@@ -321,11 +354,16 @@ Antigravity (agy)
321
354
  Gemini weekly ███████████████████░ 95% left
322
355
  Claude/GPT 5h ██████████████████░░ 90% left resets in 2h 05m
323
356
  Claude/GPT weekly ██████████████░░░░░░ 70% left resets in 5d 1h
357
+
358
+ Z.ai (GLM, via opencode) Max
359
+ Session ████████████████████ 100% left
360
+ Weekly ███████████████████░ 95% left resets in 1d 11h
361
+ MCP tools ███████████████████░ 99% left resets in 25d 11h
324
362
  ```
325
363
 
326
364
  The `Weekly` and `Sonnet` rows above are the point: they are **separate
327
- buckets**. So is every agy row. Routing a subtask is choosing which of them to
328
- spend.
365
+ buckets**. So is every agy row, and so is the whole Z.ai block. Routing a
366
+ subtask is choosing which of them to spend.
329
367
 
330
368
  - **Claude** — OAuth usage API; token from the Claude Code Keychain entry.
331
369
  - **Codex** — ChatGPT `wham/usage` API; token from `~/.codex/auth.json`.
@@ -336,6 +374,12 @@ spend.
336
374
  `retrieveUserQuota` on the Antigravity backend, which surfaces only coarse
337
375
  Gemini buckets and is flagged *daemon offline*. Approach ported from
338
376
  [steipete/CodexBar](https://github.com/steipete/CodexBar).
377
+ - **Z.ai** — GLM Coding Plan usage API; key from opencode's
378
+ `~/.local/share/opencode/auth.json` (or `$ZAI_API_KEY`). Reported as its own
379
+ provider because the quota belongs to the plan, not to opencode — an opencode
380
+ lane pointed at a local model spends nothing here. Shows *unsupported* rather
381
+ than an error when no Z.ai credential exists. `MCP tools` is the monthly
382
+ web-search/reader pool, not inference.
339
383
 
340
384
  Reading these at *model* granularity is the point: they are the load-balancing
341
385
  input the coach skill uses to route each subtask.
@@ -1,11 +1,11 @@
1
1
  # playmaker
2
2
 
3
3
  [![CI](https://github.com/vladsafedev/playmaker/actions/workflows/ci.yml/badge.svg)](https://github.com/vladsafedev/playmaker/actions/workflows/ci.yml)
4
- [![PyPI](https://img.shields.io/pypi/v/playmaker-cli.svg)](https://pypi.org/project/playmaker-cli/)
5
- [![Python](https://img.shields.io/pypi/pyversions/playmaker-cli.svg)](https://pypi.org/project/playmaker-cli/)
4
+ [![PyPI](https://img.shields.io/pypi/v/playmaker-cli.svg?cacheSeconds=3600)](https://pypi.org/project/playmaker-cli/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/playmaker-cli.svg?cacheSeconds=3600)](https://pypi.org/project/playmaker-cli/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
 
8
- **Run Claude Code, Codex and Antigravity as parallel sub-agents from one terminal — and spend three separate quotas instead of one.**
8
+ **Run Claude Code, Codex, Antigravity and opencode as parallel sub-agents from one terminal — and spend separate quotas instead of one.**
9
9
 
10
10
  You stay in your Claude Code session doing the part only you can do. `playmaker`
11
11
  fans the rest out to other agent CLIs as detached processes, tracks them,
@@ -47,10 +47,12 @@ in one serial session:
47
47
  1. **Wall-clock speed.** A task that decomposes into 3–5 independent
48
48
  work-streams (schema, backend, frontend, tests, docs) finishes 2–4× faster
49
49
  when each stream runs as its own parallel agent.
50
- 2. **Provider arbitrage.** Codex and Antigravity quotas are entirely separate
51
- pools from your Anthropic plan. Every slice you hand them is capacity your
52
- main session never spends — and Antigravity's roster includes Claude
53
- Sonnet/Opus, so even Claude-quality work can run on Google's pool.
50
+ 2. **Provider arbitrage.** Codex, Antigravity and opencode quotas are entirely
51
+ separate pools from your Anthropic plan. Every slice you hand them is
52
+ capacity your main session never spends — and Antigravity's roster includes
53
+ Claude Sonnet/Opus, so even Claude-quality work can run on Google's pool.
54
+ `opencode` widens this the most: one CLI fronting ~75 providers, from a z.ai
55
+ GLM coding plan to models running locally on your own machine.
54
56
  3. **Bucket arbitrage inside one plan.** Headless `claude -p` draws on the same
55
57
  subscription as your interactive session, but per-model weekly buckets are
56
58
  separate — dispatching `--model sonnet` spends Sonnet's usually-idle bucket
@@ -107,6 +109,16 @@ The other agents differ, because their CLIs do:
107
109
  either auto-approves or comes back having done nothing. It therefore
108
110
  defaults to `yolo = true`; layer `sandbox = true` on top for agy's own
109
111
  terminal restrictions.
112
+ - **opencode** is the same story: its only lever is `--auto`, so it also
113
+ defaults to `yolo = true`. The granular control lives in opencode's own
114
+ config rather than in playmaker — and `--auto` still honours it, because it
115
+ auto-approves only what you have not explicitly denied:
116
+
117
+ ```jsonc
118
+ // ~/.config/opencode/opencode.json
119
+ { "permission": { "edit": "allow", "bash": "allow", "webfetch": "deny" } }
120
+ ```
121
+
110
122
  - **gemini** (legacy) runs with `--yolo`.
111
123
 
112
124
  ## Install
@@ -143,6 +155,7 @@ playmaker agents # which agent CLIs are reachable
143
155
  | **Claude Code** | `npm i -g @anthropic-ai/claude-code` | `--model sonnet` / `opus` / `haiku` |
144
156
  | **Codex CLI** | `npm i -g @openai/codex` | the model roster depends on your plan; omit `--model` to use the account default |
145
157
  | **Antigravity (`agy`)** | bundled with [Antigravity](https://antigravity.google) | `--model claude-opus-4-6-thinking` — the roster moves, so read it from `agy models` |
158
+ | **opencode** | `brew install sst/tap/opencode` (or see [opencode.ai](https://opencode.ai)) | `--model provider/model`, e.g. `zai-coding-plan/glm-5.2`; roster from `opencode models`, providers from `opencode auth login` |
146
159
  | **Gemini CLI** (legacy) | `npm i -g @google/gemini-cli` | still supported, superseded by `agy` |
147
160
 
148
161
  At least one is required; `playmaker agents` tells you which it can see.
@@ -206,9 +219,11 @@ flowchart LR
206
219
  P --> A1["claude -p<br/>--model sonnet"]
207
220
  P --> A2["codex exec"]
208
221
  P --> A3["agy -p"]
222
+ P --> A4["opencode run<br/>-m zai-coding-plan/glm-5.2"]
209
223
  A1 --> S[("state.db<br/>outputs/ + logs/")]
210
224
  A2 --> S
211
225
  A3 --> S
226
+ A4 --> S
212
227
  S -->|"list / thread / summary"| C
213
228
  S -.->|"batch drained"| N["one ping"]
214
229
  ```
@@ -221,8 +236,10 @@ bookkeeping in between.
221
236
  ├── state.db SQLite — sessions, status, pids, models, output paths
222
237
  ├── config.toml
223
238
  ├── agents/ optional agent profile markdown (claude.md, codex.md, agy.md…)
224
- ├── outputs/ final output per session — .md, or .json if the agent returned JSON
239
+ ├── outputs/ final output per session — .md, or .json if the agent returned JSON,
240
+ │ plus batch-<label>.md, every output in one fan-out combined
225
241
  ├── logs/ subprocess stdout for detached runs
242
+ ├── opencode/ pointer per opencode session (its transcript lives in SQLite)
226
243
  └── quotas.json latest capacity snapshot
227
244
  ```
228
245
 
@@ -234,6 +251,7 @@ and locates the session file the tool writes locally. Empirically:
234
251
  | Claude | `~/.claude/projects/<cwd-with-slashes-as-dashes>/<id>.jsonl` |
235
252
  | Codex | `~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ts>-<thread_id>.jsonl` |
236
253
  | Antigravity | `~/.gemini/antigravity-cli/brain/<conversation-id>/.system_generated/logs/transcript_full.jsonl` |
254
+ | opencode | SQLite — `~/.local/share/opencode/opencode.db` (`session` / `message` / `part`); playmaker keeps a pointer at `~/.playmaker/opencode/<id>.session` |
237
255
  | Gemini | `~/.gemini/tmp/<cwd-basename>/chats/session-<ts>-<short_id>.{json,jsonl}` |
238
256
 
239
257
  `thread` and `summary` normalize all of them into the same turn list, so every
@@ -249,6 +267,21 @@ dispatch; and a wrong `--model` is a silent failure on both **codex** (reports
249
267
  `turn.failed` while exiting 0) and **agy** (runs its default model instead) —
250
268
  playmaker turns both into real errors.
251
269
 
270
+ **opencode** deserves its own note, because one CLI is many providers. Models
271
+ are `provider/model` strings from `opencode models`, and if you don't pass
272
+ `--model` opencode falls back to the default in *its* config — often whatever
273
+ you last picked interactively. Set the lane's default once:
274
+
275
+ ```toml
276
+ [agents.opencode]
277
+ model = "zai-coding-plan/glm-5.2"
278
+ ```
279
+
280
+ It also has agy's working-directory problem in a different costume: opencode
281
+ reads `process.env.PWD`, which a subprocess `cwd` does not update, so left
282
+ alone it would ignore `--cwd` and write into the directory *you* were standing
283
+ in. playmaker passes `--dir` and fixes up `PWD`, so `--cwd` means what it says.
284
+
252
285
  ## Notifications
253
286
 
254
287
  Every detached dispatch pings when it finishes. With
@@ -294,11 +327,16 @@ Antigravity (agy)
294
327
  Gemini weekly ███████████████████░ 95% left
295
328
  Claude/GPT 5h ██████████████████░░ 90% left resets in 2h 05m
296
329
  Claude/GPT weekly ██████████████░░░░░░ 70% left resets in 5d 1h
330
+
331
+ Z.ai (GLM, via opencode) Max
332
+ Session ████████████████████ 100% left
333
+ Weekly ███████████████████░ 95% left resets in 1d 11h
334
+ MCP tools ███████████████████░ 99% left resets in 25d 11h
297
335
  ```
298
336
 
299
337
  The `Weekly` and `Sonnet` rows above are the point: they are **separate
300
- buckets**. So is every agy row. Routing a subtask is choosing which of them to
301
- spend.
338
+ buckets**. So is every agy row, and so is the whole Z.ai block. Routing a
339
+ subtask is choosing which of them to spend.
302
340
 
303
341
  - **Claude** — OAuth usage API; token from the Claude Code Keychain entry.
304
342
  - **Codex** — ChatGPT `wham/usage` API; token from `~/.codex/auth.json`.
@@ -309,6 +347,12 @@ spend.
309
347
  `retrieveUserQuota` on the Antigravity backend, which surfaces only coarse
310
348
  Gemini buckets and is flagged *daemon offline*. Approach ported from
311
349
  [steipete/CodexBar](https://github.com/steipete/CodexBar).
350
+ - **Z.ai** — GLM Coding Plan usage API; key from opencode's
351
+ `~/.local/share/opencode/auth.json` (or `$ZAI_API_KEY`). Reported as its own
352
+ provider because the quota belongs to the plan, not to opencode — an opencode
353
+ lane pointed at a local model spends nothing here. Shows *unsupported* rather
354
+ than an error when no Z.ai credential exists. `MCP tools` is the monthly
355
+ web-search/reader pool, not inference.
312
356
 
313
357
  Reading these at *model* granularity is the point: they are the load-balancing
314
358
  input the coach skill uses to route each subtask.
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "playmaker-cli"
3
- version = "0.5.1"
4
- description = "Playing-coach CLI for orchestrating Claude Code, Codex and Antigravity sub-agents in parallel."
3
+ version = "0.7.0"
4
+ description = "Playing-coach CLI for orchestrating Claude Code, Codex, Antigravity and opencode sub-agents in parallel."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
7
  license = "MIT"
@@ -19,6 +19,8 @@ keywords = [
19
19
  "codex",
20
20
  "antigravity",
21
21
  "gemini",
22
+ "opencode",
23
+ "glm",
22
24
  "subagents",
23
25
  ]
24
26
  classifiers = [
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: playmaker-coach
3
- description: Playing-coach orchestration of Claude/Codex/Antigravity (agy) sub-agents via the `playmaker` CLI and in-session Task sub-agents. Triggered when the user gives a complex multi-component task where decomposition gives >2x parallel speedup (e.g. "build admin dashboard with schema, backend, FE, tests, docs"). NOT triggered for single-file refactor, single bug fix, single component, or simple Q&A — those stay in this thread without delegation.
3
+ description: Playing-coach orchestration of Claude/Codex/Antigravity (agy)/opencode sub-agents via the `playmaker` CLI and in-session Task sub-agents. Triggered when the user gives a complex multi-component task where decomposition gives >2x parallel speedup (e.g. "build admin dashboard with schema, backend, FE, tests, docs"). NOT triggered for single-file refactor, single bug fix, single component, or simple Q&A — those stay in this thread without delegation.
4
4
  ---
5
5
 
6
6
  # playmaker-coach — playing-coach orchestration
7
7
 
8
- Use the `playmaker` CLI as a facade to dispatch sub-tasks to Codex / Antigravity (`agy`) / a sibling Claude, monitor them, read their threads, review their diffs, and feed back. The coach (this thread) does its own portion of the work in parallel.
8
+ Use the `playmaker` CLI as a facade to dispatch sub-tasks to Codex / Antigravity (`agy`) / opencode / a sibling Claude, monitor them, read their threads, review their diffs, and feed back. The coach (this thread) does its own portion of the work in parallel.
9
9
 
10
10
  > **`agy` (Antigravity CLI)** does not only serve Google models: alongside the Gemini Flash and Pro tiers, `agy models` carries **Claude Sonnet and Opus** and a **GPT-OSS** mid-tier. Opus via agy runs on *Google's* quota pool — top-tier Claude work that does not touch the Anthropic subscription's scarce Opus weekly bucket: `--model claude-opus-4-6-thinking`.
11
11
  >
@@ -23,7 +23,7 @@ All Claude work — coach, internal sub-agents, external `claude -p` — draws f
23
23
 
24
24
  3. **External dispatch — `playmaker dispatch <agent>`.** Separate OS processes, tracked in playmaker (`list`/`watch`/`thread`/`continue`):
25
25
  - **`claude -p` (sibling Claude):** same subscription. Its key lever is the model bucket — **Sonnet is a separate weekly bucket from Opus**, usually idle while Opus depletes, so default **`--model sonnet`** to spare the scarce Opus bucket (**`--model haiku`** for trivial mechanical work). It **can write files** — playmaker runs it in `acceptEdits`, so it edits and runs commands freely inside `--cwd` and is refused outside it (see §10). Use it over an internal sub-agent when you want a **tracked, detached work-stream** you can monitor/continue independently of the coach's turn.
26
- - **`codex` / `agy`:** each on its own subscription/quota — the right home for **write-heavy** parallel implementation that can leave the Anthropic subscription. `agy` is special: besides Gemini tiers it carries **Claude Sonnet/Opus 4.6 (Thinking)** on Google's pool, so even "must be Claude-quality" work can leave the Anthropic quota.
26
+ - **`codex` / `agy` / `opencode`:** each on its own subscription/quota — the right home for **write-heavy** parallel implementation that can leave the Anthropic subscription. `agy` is special: besides Gemini tiers it carries **Claude Sonnet/Opus 4.6 (Thinking)** on Google's pool, so even "must be Claude-quality" work can leave the Anthropic quota. `opencode` is the widest lane: one CLI over ~75 providers, addressed as `provider/model` — a GLM coding plan (`zai-coding-plan/glm-5.2`), or a model running locally on this machine, which spends **no** subscription quota at all.
27
27
 
28
28
  **Routing cheat-sheet for Claude-side work:**
29
29
 
@@ -32,8 +32,10 @@ All Claude work — coach, internal sub-agents, external `claude -p` — draws f
32
32
  | writes files and the coach integrates the result directly | **internal sub-agent** (Task tool) | in-session, write-capable, returns into context |
33
33
  | is an independent stream you'll monitor / continue separately | **external `dispatch claude --model sonnet`** | tracked & detached; Sonnet's own weekly bucket spares Opus |
34
34
  | is heavy reasoning only the coach can do | **coach** | top-tier Opus, serial |
35
- | is write-heavy and can leave Claude | **codex / agy** | their own quotas |
35
+ | is write-heavy and can leave Claude | **codex / agy / opencode** | their own quotas |
36
36
  | needs top-tier Claude but the Anthropic Opus weekly is precious | **`dispatch agy --model claude-opus-4-6-thinking`** | Opus quality on Google's pool |
37
+ | is bulk work and every subscription is running low | **`dispatch opencode --model zai-coding-plan/glm-5.2`** | a separate GLM plan, untouched by the others |
38
+ | is mechanical and privacy-sensitive, or all quotas are spent | **`dispatch opencode --model <local provider>/<model>`** | runs on this machine; costs no quota, just wall-clock |
37
39
 
38
40
  **Sonnet is your cheap parallel Claude worker** — a separate weekly bucket that usually sits idle while Opus depletes. Reach for it (via `dispatch claude --model sonnet`, or by pointing an internal sub-agent at Sonnet) instead of burning Opus on mid-tier work.
39
41
 
@@ -61,6 +63,7 @@ playmaker quotas --refresh # current capacity, broken out per model
61
63
  - Claude: two non-coach ways to run it — an **internal sub-agent** (Task tool; in-session, write-capable, result returns to the coach) and an **external `claude -p` dispatch** (tracked, detached stream). Both draw on the subscription; the difference is where results land, not cost. See "Execution lanes". Default external Claude to `--model sonnet` — its weekly bucket is separate from Opus and usually idle, so it spares the scarce coach (Opus) bucket.
62
64
  - Antigravity (`agy`): one Google pool split across model families — Gemini Flash tiers for cheap bulk work, Gemini 3.1 Pro for hard Gemini work, **Claude Sonnet/Opus 4.6 (Thinking)** as Anthropic-quality capacity that spends *Google's* quota, GPT-OSS 120B as a spare mid-tier. `playmaker quotas` shows the **full categorized breakdown** — `Gemini 5h` / `Gemini weekly` and `Claude/GPT 5h` / `Claude/GPT weekly`. Two things share a bucket: all Gemini models draw the Gemini bucket, and Claude *and* GPT-OSS share the Claude/GPT bucket. So dispatching Opus 4.6 via agy spends the same `Claude/GPT` bucket as Sonnet or GPT-OSS — watch the `Claude/GPT 5h` window when fanning out several agy-Claude jobs. (This needs agy's local daemon up — normally true when any agy process is running; if `playmaker quotas` tags agy "daemon offline" it fell back to a coarse Gemini-only view.)
63
65
  - Codex: top-tier vs lighter modes (where applicable).
66
+ - Z.ai (GLM): shown as its own provider because the plan is what has the quota, not the CLI — `Session` (5h) and `Weekly` windows plus a monthly `MCP tools` pool that only web-search/reader calls touch. It appears whenever `opencode auth login` has a Z.AI credential, and reads *unsupported* when it doesn't. An `opencode` dispatch pointed at a **local** model spends none of it, so local lanes never show up in this table at all.
64
67
 
65
68
  If `quotas.json` is more than 1h old (or shows errors), say so before relying on the numbers.
66
69
 
@@ -159,6 +162,8 @@ Two exceptions to "always pass `--model`":
159
162
  - **codex** — its model roster depends on the account plan, and an unavailable name fails the whole dispatch (`codex turn failed: … not supported when using Codex with a ChatGPT account`). Omitting `--model` uses whatever that account actually has, which is usually what you want.
160
163
  - **agy** — its own default is a top-tier model, so omitting `--model` on a dispatch meant to be cheap silently spends the expensive bucket. Always pass it here.
161
164
 
165
+ **opencode is `provider/model`, and the default is a trap.** Names look like `zai-coding-plan/glm-5.2` or `lmstudio/qwen/qwen3-coder-30b` — **run `opencode models` and copy a line** rather than writing one from memory; playmaker validates against that roster and fails the dispatch on a name it doesn't contain. Omitting `--model` does *not* fall back to something sensible: it uses whatever is set in the user's own `~/.config/opencode/opencode.json`, which is typically the last model they picked interactively — often a small local one. So for opencode, always pass `--model` unless `[agents.opencode] model` is already set in `~/.playmaker/config.toml`.
166
+
162
167
  **agy prompt discipline:** the agy agent's shell lives in a private scratch directory, not the workspace. playmaker automatically prepends a workspace preamble to every agy dispatch, but reinforce it: word file instructions with paths relative to the workspace root or absolute paths, never "in the current directory".
163
168
 
164
169
  **Bad-model handling (playmaker ≥0.4 does this for you):** a wrong `--model` is the classic silent failure. Codex returns a `turn.failed` while exiting 0 with empty output; agy silently runs its *default* model instead of erroring. playmaker catches both — a codex model/auth failure raises `codex turn failed: …`, and an unknown agy model raises with the valid roster before dispatch. So a dispatch that comes back **failed** with a model message means: fix the `--model` string (for agy, copy it exactly from `agy models`) and re-dispatch. Don't retry the same string.
@@ -230,7 +235,7 @@ If `playmaker dispatch` returns an error (binary missing, auth bad, agent unavai
230
235
  Both Claude lanes are on the subscription, so pick by **where the work lives**, not cost:
231
236
  - **Coach folds the result in directly → internal sub-agent (Task tool).** In-session, write-capable, returns into context. Default choice for "more Claude."
232
237
  - **Independent stream you'll monitor / continue separately → `playmaker dispatch claude --model sonnet`.** Tracked, detached; Sonnet's separate weekly bucket spares Opus.
233
- - **Work that can leave the Claude family → Codex / agy** (their own quotas).
238
+ - **Work that can leave the Claude family → Codex / agy / opencode** (their own quotas, or none at all for a local model).
234
239
 
235
240
  If a dispatch comes back with zero file changes, check `playmaker summary <id>`. Two common causes for Claude: the subtask tried to write outside `--cwd` and was refused (re-dispatch with the right `--cwd`), or the run needed a permission the configured mode doesn't grant — a "I need your permission" answer, not a crash. For **agy** specifically, a "done" with no file changes usually means the files landed in agy's private scratch dir (`~/.gemini/antigravity-cli/scratch/`) — the prompt referred to "the current directory" instead of workspace paths; re-dispatch with explicit paths.
236
241
 
@@ -244,7 +249,7 @@ If a dispatch comes back with zero file changes, check `playmaker summary <id>`.
244
249
  ## Quota awareness
245
250
 
246
251
  - **Read Claude quota at model granularity:** Sonnet weekly and Opus weekly are **independent buckets**. Coach and internal sub-agents on Opus spend the scarce one; Sonnet usually sits idle. Push mid-tier work to Sonnet (an internal sub-agent set to Sonnet, or `dispatch claude --model sonnet`) to spare Opus.
247
- - Read quotas at *model* granularity, not *agent* granularity. For agy, the probe reports four windows — `Gemini 5h/weekly` and `Claude/GPT 5h/weekly`; for Codex, top-tier vs lighter modes.
252
+ - Read quotas at *model* granularity, not *agent* granularity. For agy, the probe reports four windows — `Gemini 5h/weekly` and `Claude/GPT 5h/weekly`; for Codex, top-tier vs lighter modes; for opencode, the quota is reported under the **provider** it is pointed at (`Z.ai`), and a local model reports nothing because it spends nothing.
248
253
  - Skip a *model* if its `*_left` is below ~10%; reroute the subtask to the same agent with a different model, or to a different agent entirely.
249
254
  - If a top-tier `weekly_*_left` is degrading toward the deadline, push as much work as possible to mid- and cheap-tier models on the same provider (which are often nowhere near depleted), instead of switching providers blindly.
250
255
  - agy's **5-hour** window is the one that bites during a fan-out: it "smooths aggregate demand", so a burst of agy dispatches drains `Gemini 5h` or `Claude/GPT 5h` well before the weekly. If a 5h window is low, spread the burst or move some slices to Codex/coach.
@@ -269,7 +274,7 @@ playmaker watch # Rich live TUI of sessions
269
274
 
270
275
  Every command takes `--json` for machine-readable output.
271
276
 
272
- `--model NAME` is forwarded to the agent's native CLI: `claude --model sonnet`, `agy --model claude-opus-4-6-thinking`, `codex -m <whatever that account has>`. Without it the agent CLI uses its own default. Model is stored on the session row, so detached re-runs and `continue` inherit it; `continue --model X` overrides for that one turn.
277
+ `--model NAME` is forwarded to the agent's native CLI: `claude --model sonnet`, `agy --model claude-opus-4-6-thinking`, `codex -m <whatever that account has>`, `opencode -m zai-coding-plan/glm-5.2`. Without it the agent CLI uses its own default. Model is stored on the session row, so detached re-runs and `continue` inherit it; `continue --model X` overrides for that one turn.
273
278
 
274
279
  **The agy roster is not documented here on purpose** — it changes with Antigravity releases, and so does the spelling convention. Run `agy models` and copy a line. At the time of writing it returns bare slugs in the shape `gemini-3.6-flash-{low,medium,high}`, `gemini-3.1-pro-{low,high}`, `claude-sonnet-4-6`, `claude-opus-4-6-thinking`, `gpt-oss-120b-medium` — but treat that as an example of the *shape*, not a list to copy from.
275
280