@botbuddy/cli 1.61.9 → 1.62.0
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.
- package/daemon/bin/darwin-arm64/botbuddy-daemon +0 -0
- package/daemon/bin/linux-x64/botbuddy-daemon +0 -0
- package/daemon-release/darwin-arm64.json +4 -4
- package/daemon-release/linux-x64.json +4 -4
- package/daemon-release/manifest.json +5 -5
- package/package.json +1 -1
- package/src/commands.mjs +114 -50
- package/src/wait-core.mjs +3 -0
- package/src/wait.mjs +176 -68
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.62.0",
|
|
3
|
+
"source_commit": "d239018a4df5137e9b013792274dbfe6316f46e1",
|
|
4
|
+
"source_sha256": "cd5a5bd1f0a409d0a79a3db600b218fccca101102df138d7414766a3bba1b16a",
|
|
5
5
|
"target": "darwin-arm64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "7b0ddb31fbbf568503a4d079badbae185cac44fe27a5ea2a0823d1ae83d52b2b"
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release_version": "1.
|
|
3
|
-
"source_commit": "
|
|
4
|
-
"source_sha256": "
|
|
2
|
+
"release_version": "1.62.0",
|
|
3
|
+
"source_commit": "d239018a4df5137e9b013792274dbfe6316f46e1",
|
|
4
|
+
"source_sha256": "cd5a5bd1f0a409d0a79a3db600b218fccca101102df138d7414766a3bba1b16a",
|
|
5
5
|
"target": "linux-x64",
|
|
6
|
-
"sha256": "
|
|
6
|
+
"sha256": "4c5065a8cc25ef6c01967ec96f109aa39e23799ef1043a4fd942780b0adb6c95"
|
|
7
7
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
|
-
"release_version": "1.
|
|
4
|
-
"source_commit": "
|
|
5
|
-
"source_sha256": "
|
|
3
|
+
"release_version": "1.62.0",
|
|
4
|
+
"source_commit": "d239018a4df5137e9b013792274dbfe6316f46e1",
|
|
5
|
+
"source_sha256": "cd5a5bd1f0a409d0a79a3db600b218fccca101102df138d7414766a3bba1b16a",
|
|
6
6
|
"targets": {
|
|
7
7
|
"darwin-arm64": {
|
|
8
8
|
"goos": "darwin",
|
|
9
9
|
"goarch": "arm64",
|
|
10
10
|
"binary": "daemon/bin/darwin-arm64/botbuddy-daemon",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "7b0ddb31fbbf568503a4d079badbae185cac44fe27a5ea2a0823d1ae83d52b2b",
|
|
12
12
|
"mode": "0755"
|
|
13
13
|
},
|
|
14
14
|
"linux-x64": {
|
|
15
15
|
"goos": "linux",
|
|
16
16
|
"goarch": "amd64",
|
|
17
17
|
"binary": "daemon/bin/linux-x64/botbuddy-daemon",
|
|
18
|
-
"sha256": "
|
|
18
|
+
"sha256": "4c5065a8cc25ef6c01967ec96f109aa39e23799ef1043a4fd942780b0adb6c95",
|
|
19
19
|
"mode": "0755"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/package.json
CHANGED
package/src/commands.mjs
CHANGED
|
@@ -56,6 +56,23 @@ export function shouldCheckForUpdates(argv) {
|
|
|
56
56
|
return true;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// BOT-2715: local commands with their own --help. `bb help <command>` (and
|
|
60
|
+
// `bb <command> help`) print that help instead of asking MCP discovery for a tool
|
|
61
|
+
// of the same name. MCP tools are snake_case, so no name here can shadow one.
|
|
62
|
+
export const LOCAL_HELP_COMMANDS = Object.freeze([
|
|
63
|
+
"wait", "stack", "pw", "docker", "run", "mcp", "login", "setup", "doctor", "host",
|
|
64
|
+
"staging-pool", "daemon", "evidence", "usage", "telemetry", "allocation",
|
|
65
|
+
]);
|
|
66
|
+
const HELP_WORDS = new Set(["help", "--help", "-h"]);
|
|
67
|
+
|
|
68
|
+
export function routeHelpArgv(argv) {
|
|
69
|
+
const [command, ...args] = argv;
|
|
70
|
+
if (HELP_WORDS.has(command) && args.length === 1 && LOCAL_HELP_COMMANDS.includes(args[0])) return [args[0], "--help"];
|
|
71
|
+
// pw parses its own argv (`bb pw help` is its lane grammar's business).
|
|
72
|
+
if (args.length === 1 && args[0] === "help" && command !== "pw" && LOCAL_HELP_COMMANDS.includes(command)) return [command, "--help"];
|
|
73
|
+
return argv;
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
const TICKET_KEY_RE = /^[A-Za-z][A-Za-z0-9]{1,9}-\d{1,6}$/;
|
|
60
77
|
|
|
61
78
|
export function validateBootstrapLinkage(ticketId, noTicketReason) {
|
|
@@ -73,7 +90,7 @@ export async function run(argv, {
|
|
|
73
90
|
bootstrap = cmdBootstrap,
|
|
74
91
|
managedAllocation = cmdManagedAllocation,
|
|
75
92
|
} = {}) {
|
|
76
|
-
const [command, ...args] = argv;
|
|
93
|
+
const [command, ...args] = routeHelpArgv(argv);
|
|
77
94
|
// BOT-2069: `bootstrap close` can retire a live server session. Parse its
|
|
78
95
|
// own arguments before loading config, checking npm, reading the runtime
|
|
79
96
|
// cache, or resolving any credential. Help and malformed invocations return
|
|
@@ -84,7 +101,7 @@ export async function run(argv, {
|
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
await load();
|
|
87
|
-
if (shouldCheckForUpdates(
|
|
104
|
+
if (shouldCheckForUpdates([command, ...args])) await warnStale({ version: VERSION });
|
|
88
105
|
|
|
89
106
|
switch (command) {
|
|
90
107
|
case "start": return cmdStart(args);
|
|
@@ -184,19 +201,28 @@ export function cmdHelp(log = console.log) {
|
|
|
184
201
|
log(`${bold("bb")} ${dim(`v${VERSION}`)} — Swarm coordination CLI
|
|
185
202
|
|
|
186
203
|
${dim(`Run everything as ${cyan("bb <command>")} — the short, preferred alias. ${cyan("botbuddy <command>")} is the identical long form.`)}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
bb
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
204
|
+
${dim(`Detailed help: ${cyan("bb help <command>")} or ${cyan("bb <command> --help")} for ${LOCAL_HELP_COMMANDS.join(", ")}; ${cyan("bb test --help")} for test.`)}
|
|
205
|
+
|
|
206
|
+
${bold("COMMON TASKS")}
|
|
207
|
+
bb setup sign in and cache this worktree's agent session (once)
|
|
208
|
+
bb doctor --fix repair a stale or rejected agent session
|
|
209
|
+
bb wait 'timer:duration=1800' wait 30 min (the only allowed way to wait for time)
|
|
210
|
+
bb wait 'ci:repo=<owner/repo>,scope=latest,pr=<n>' --heartbeat
|
|
211
|
+
wait for CI on a PR's current head to finish
|
|
212
|
+
bb wait 'pr-review:repo=<owner/repo>,pr=<n>,sha=<head>' --heartbeat
|
|
213
|
+
wait for review activity on the head you just pushed
|
|
214
|
+
bb wait 'unblocked:BOT-123' wait for a ticket's last blocker to clear
|
|
215
|
+
bb wait result --from-file "$wait_artifact" --exit-code "$wait_exit"
|
|
216
|
+
read a redirected wait's receipt once it exits
|
|
217
|
+
(the full wrapper is in bb help wait → EXAMPLES)
|
|
218
|
+
bb stack run --repo botbuddy-web --ticket BOT-123 -- pnpm test:integration
|
|
219
|
+
run a batch on a leased, isolated local Supabase stack
|
|
220
|
+
bb test unit run a configured test lane as a tracked test run
|
|
221
|
+
bb pw --environment local 3 open http://localhost:5173
|
|
222
|
+
drive Playwright lane 3 (lock it first)
|
|
223
|
+
bb call get_staging_gate --repo bot-buddy/botbuddy-webapp
|
|
224
|
+
call any BotBuddy MCP tool (${cyan("bb help <tool>")} for its args)
|
|
225
|
+
More wait recipes: ${cyan("bb help wait")} → EXAMPLES.
|
|
200
226
|
|
|
201
227
|
${bold("AUTH")}
|
|
202
228
|
login [--no-browser] [--tenant <slug>] [--device | --loopback] [--caller <name>] [--caller-harness <h>] [--caller-ticket <ref>]
|
|
@@ -221,6 +247,7 @@ ${bold("MCP CONFIG KEY")}
|
|
|
221
247
|
${bold("TOOLS")}
|
|
222
248
|
help --tools List every BotBuddy tool
|
|
223
249
|
help <tool> Show one tool's arguments
|
|
250
|
+
help <command> Detailed help for a command named at the top (same as <command> --help)
|
|
224
251
|
help test Safe test-lane launch and recovery guidance
|
|
225
252
|
help symptom <name> Diagnose a known block without source-diving
|
|
226
253
|
call <tool> [--key value] Invoke any tool
|
|
@@ -228,49 +255,37 @@ ${bold("TOOLS")}
|
|
|
228
255
|
call <tool> --pin <slug> Pin the connection tenant (a user token needs one;
|
|
229
256
|
default: BOTBUDDY_TENANT → repo profile → sole tenant)
|
|
230
257
|
|
|
231
|
-
${bold("
|
|
258
|
+
${bold("AGENT WAITS")} (${cyan("bb help wait")} — every condition, with examples)
|
|
259
|
+
wait [--any] <condition>... [options]
|
|
260
|
+
Wait once for a pushed BotBuddy signal; one JSON receipt on exit
|
|
261
|
+
wait List this worktree's saved waits (after a restart)
|
|
262
|
+
wait result [<local_wait_id>] [--from-file <path> --exit-code <n>]
|
|
263
|
+
Read a terminal receipt without re-arming
|
|
264
|
+
wait resume|cancel|acknowledge <local_wait_id>
|
|
265
|
+
Recover, stop, or mark read one saved wait
|
|
266
|
+
|
|
267
|
+
${bold("STACK LEASES")} (${cyan("bb help stack")})
|
|
268
|
+
stack run [options] -- <command> Own one Helper-backed test/fix batch end-to-end (preferred)
|
|
232
269
|
stack up [options] Request a batch-scoped local stack lease; park if full; hold it active
|
|
233
270
|
stack status <lease_id> Show a lease's state + connection
|
|
234
271
|
stack touch <lease_id> Bump the lease idle clock
|
|
235
|
-
stack done <lease_id> Release (done) a lease — reap
|
|
236
|
-
stack
|
|
272
|
+
stack done <lease_id> Release (done) a lease — idle it for re-adoption (--reap tears it down)
|
|
273
|
+
stack cancel <lease_id> Give a queued lease's place up
|
|
237
274
|
allocation status <id> <generation> <target_revision> Verify managed placement before Compose
|
|
238
275
|
|
|
239
|
-
${bold("
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
${bold("GO DAEMON")}
|
|
245
|
-
daemon <render|install|status|enroll|bootstrap> …
|
|
246
|
-
Verify and manage the native Go daemon service
|
|
247
|
-
|
|
248
|
-
${bold("DURABLE WORKLOADS")}
|
|
249
|
-
run --session-id <id> --environment <env> -- <command>
|
|
250
|
-
Launch a receipt-bearing command under a detached owner
|
|
251
|
-
run --foreground --kind <kind> --environment <env> -- <command>
|
|
252
|
-
Run locally with a durable lifecycle outbox
|
|
253
|
-
telemetry <status|replay|doctor>
|
|
254
|
-
Inspect, replay, or gate durable lifecycle delivery
|
|
276
|
+
${bold("TEST LANES")}
|
|
277
|
+
test <lane> [--ticket <KEY>] [--pr <n>] [--wait] [-- <extra args>]
|
|
278
|
+
Run a lane from .botbuddy/lanes.json as a tracked test run
|
|
279
|
+
test list List the configured lanes (${cyan("bb test --help")})
|
|
255
280
|
test-ingest --file <payload.json|-> [--tenant <slug>]
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
${bold("USAGE INGEST")}
|
|
259
|
-
usage sync [--since 2d] [--provider claude|codex|all] [--file <transcript>] [--dry-run] [--quiet]
|
|
260
|
-
Send local Claude/Codex token usage (counters only, no prompts)
|
|
261
|
-
usage status Last sync runs and tracked transcripts
|
|
281
|
+
Upload a test-run report with this worktree's agent session
|
|
262
282
|
|
|
263
|
-
${bold("
|
|
264
|
-
wait [--any] <condition>... [options]
|
|
265
|
-
Wait once for a pushed BotBuddy signal
|
|
266
|
-
|
|
267
|
-
${bold("BROWSER LANES")}
|
|
283
|
+
${bold("BROWSER LANES")} (${cyan("bb pw --help")})
|
|
268
284
|
pw <lane> <verb> [args…] Drive a lock-gated Playwright lane
|
|
269
285
|
(also the short ${cyan("pw")} binary, or ${cyan("bb-pw")} alias)
|
|
270
286
|
pw <lane> verdict pass|fail Record the session's acceptance verdict
|
|
271
287
|
(sessions report live timing/results; start one
|
|
272
288
|
with --environment <env> or BB_PW_ENV)
|
|
273
|
-
pw --help Show lane usage
|
|
274
289
|
|
|
275
290
|
${bold("STAGING SIGN-IN")} (BOT-2084)
|
|
276
291
|
pw --environment staging <lane> auth --persona <alias>
|
|
@@ -284,10 +299,56 @@ ${bold("STAGING SIGN-IN")} (BOT-2084)
|
|
|
284
299
|
Human only, any directory (${cyan("bb staging-pool --help")})
|
|
285
300
|
help symptom staging-pool Every refusal code with its cause and next command
|
|
286
301
|
|
|
287
|
-
${bold("
|
|
288
|
-
locks -m [--host name]
|
|
302
|
+
${bold("LOCKS")}
|
|
303
|
+
locks -m [--host name] Reserve typed local resources, including Playwright MCP lanes
|
|
304
|
+
lock <resource_name> <type> Acquire one named lock (acquire_lock)
|
|
305
|
+
unlock <resource_name> Release one named lock (release_lock)
|
|
306
|
+
|
|
307
|
+
${bold("DURABLE WORKLOADS")} (${cyan("bb help run")})
|
|
308
|
+
run --session-id <id> --environment <env> -- <command>
|
|
309
|
+
Launch a receipt-bearing command under a detached owner
|
|
310
|
+
run --foreground --kind <kind> --environment <env> -- <command>
|
|
311
|
+
Run locally with a durable lifecycle outbox
|
|
312
|
+
run inspect|cancel <run_id> Read or stop one command run
|
|
313
|
+
telemetry <status|replay|doctor>
|
|
314
|
+
Inspect, replay, or gate durable lifecycle delivery
|
|
315
|
+
|
|
316
|
+
${bold("EVIDENCE")} (${cyan("bb help evidence")})
|
|
289
317
|
evidence require <file> Record an exact-head evidence disposition → botbuddy/ready
|
|
290
318
|
evidence publish <file> Publish a local evidence statement → botbuddy/ready check-run
|
|
319
|
+
|
|
320
|
+
${bold("HOST + DOCKER")}
|
|
321
|
+
docker hygiene [options] Dry-run/apply safe stale Supabase cleanup on OrbStack
|
|
322
|
+
docker preflight [options] Refuse a managed-stack start before network pressure exhausts the host
|
|
323
|
+
docker help Full explicit-context safety contract
|
|
324
|
+
daemon <render|install|status|config-render|enroll|bootstrap> …
|
|
325
|
+
Verify and manage the native Go daemon service (${cyan("bb help daemon")})
|
|
326
|
+
|
|
327
|
+
${bold("USAGE INGEST")}
|
|
328
|
+
usage sync [--since 2d] [--provider claude|codex|all] [--file <transcript>] [--dry-run] [--quiet]
|
|
329
|
+
Send local Claude/Codex token usage (counters only, no prompts)
|
|
330
|
+
usage status Last sync runs and tracked transcripts
|
|
331
|
+
|
|
332
|
+
${bold("SHORTCUTS")} (thin wrappers over ${cyan("bb call <tool>")})
|
|
333
|
+
heartbeat [<current task>] Refresh this session's heartbeat (heartbeat)
|
|
334
|
+
agents List agents (list_agents)
|
|
335
|
+
resources List resources and locks (list_resources)
|
|
336
|
+
tasks Read the task board (botbuddy://tasks)
|
|
337
|
+
task create <title> [-d <desc>] [-p <priority>] | task claim|done <task_id>
|
|
338
|
+
Create, claim or complete a task
|
|
339
|
+
hours [derive | set '<json>'] Read, derive or set working hours
|
|
340
|
+
browse <agents|tasks|resources|activity|settings> Read a botbuddy:// resource
|
|
341
|
+
|
|
342
|
+
${bold("CODEX BRIDGE")}
|
|
343
|
+
start [-p <port>] [-r <repo>] [-m <model>] [--no-server]
|
|
344
|
+
Start BotBuddy (login + codex app-server + bridge); defaults:
|
|
345
|
+
port 4500, repo = cwd, model gpt-5.4
|
|
346
|
+
codex bridge Legacy alias of start
|
|
347
|
+
bootstrap --session-id <uuid> (--ticket-id <BOT-n> | --no-ticket-reason <reason>)
|
|
348
|
+
Internal launcher contract: persist a wait-capable
|
|
349
|
+
harness session without printing its secret
|
|
350
|
+
|
|
351
|
+
${bold("OTHER")}
|
|
291
352
|
update Install the latest @botbuddy/cli in the background via npm
|
|
292
353
|
help Show this help
|
|
293
354
|
version Show version
|
|
@@ -433,7 +494,7 @@ async function cmdToolHelp(args) {
|
|
|
433
494
|
const res = await fetch(discoveryUrlFor(SERVER_URL, tool));
|
|
434
495
|
if (!res.ok) {
|
|
435
496
|
die(res.status === 404
|
|
436
|
-
? `Unknown tool: ${tool}. Run ${cyan("bb help --tools")} to list
|
|
497
|
+
? `Unknown tool: ${tool}. Run ${cyan("bb help --tools")} to list tools, or ${cyan("bb help")} for CLI commands.`
|
|
437
498
|
: `Discovery failed: ${res.status} ${res.statusText}`);
|
|
438
499
|
}
|
|
439
500
|
console.log(formatDiscovery(await res.json(), { tool }));
|
|
@@ -468,7 +529,10 @@ ${bold("WHEN TEST TELEMETRY IS UNAVAILABLE")}
|
|
|
468
529
|
${bold("WAITING")}
|
|
469
530
|
Use ${cyan("bb wait 'test-run:id=<id>' --heartbeat")} only when the launch output
|
|
470
531
|
contains a real test-run ID. Do not replace a missing lifecycle signal with a
|
|
471
|
-
timer or status-polling loop
|
|
532
|
+
timer or status-polling loop.
|
|
533
|
+
|
|
534
|
+
${bold("LANE COMMAND")}
|
|
535
|
+
${cyan("bb test --help")} lists the flags; ${cyan("bb test list")} shows this repo's lanes.`;
|
|
472
536
|
|
|
473
537
|
if (topic === "symptom" || topic === "symptoms") return `${bold("bb help symptom <name>")} — supported recovery topics
|
|
474
538
|
|
package/src/wait-core.mjs
CHANGED
|
@@ -459,6 +459,9 @@ const VALIDATORS = {
|
|
|
459
459
|
},
|
|
460
460
|
};
|
|
461
461
|
|
|
462
|
+
// BOT-2715: the condition types `bb wait --help` must document (pinned by a test).
|
|
463
|
+
export const CONDITION_TYPES = Object.freeze(Object.keys(VALIDATORS));
|
|
464
|
+
|
|
462
465
|
// pr-state / pr-review share a target grammar. The spine subject_key is
|
|
463
466
|
// `owner/repo#number` (github-webhook / pr_state read model convention), so a
|
|
464
467
|
// condition names an owner/repo and, optionally, a PR number. Accept three forms:
|
package/src/wait.mjs
CHANGED
|
@@ -49,14 +49,32 @@ export function shouldRenewCachedSession(error, credentialSource, attempts) {
|
|
|
49
49
|
return isRejectedCachedSession(error, credentialSource, attempts);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export const HELP = `
|
|
52
|
+
export const HELP = `bb wait — one wait command instead of a polling loop (BOT-989)
|
|
53
|
+
|
|
54
|
+
Park until a condition holds, then exit ONCE with one JSON receipt on stdout. Zero polling
|
|
55
|
+
and zero tokens while parked. Every wait — time included — is a bb wait: never sleep loops,
|
|
56
|
+
until/while polling, watch, or gh run watch. Full guide: docs/agent-wait.md.
|
|
53
57
|
|
|
54
58
|
USAGE
|
|
55
|
-
|
|
59
|
+
bb wait [--any] <condition>... [options] arm a wait (also: bb help wait)
|
|
60
|
+
bb wait <subcommand> … inspect or recover a saved wait (SUBCOMMANDS)
|
|
61
|
+
(botbuddy wait … is the identical long form.)
|
|
62
|
+
|
|
63
|
+
Pick the condition that proves the state your NEXT action needs, not a correlated proxy:
|
|
64
|
+
CI finished on the current PR head ......... ci:…,scope=latest,pr=<n> (or exact sha=)
|
|
65
|
+
review activity (Codex, a human) ........... pr-review:… (arm BEFORE requesting it)
|
|
66
|
+
PR merged/closed/conflict/ready flip ........ pr-state:…
|
|
67
|
+
your merged PR reached staging ............. staging-release:…
|
|
68
|
+
staging recovered after an incident ........ staging-green:…
|
|
69
|
+
a ticket's last blocker cleared ............ unblocked:<KEY>
|
|
70
|
+
a named bb stack run finished .............. stack-run:id=<UUID>
|
|
71
|
+
Docker capacity freed after a refusal ...... capacity:… (copy the refusal's own condition)
|
|
72
|
+
a fixed time passed ........................ timer:…
|
|
56
73
|
|
|
57
74
|
CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the first)
|
|
58
|
-
timer:duration=<seconds>
|
|
59
|
-
|
|
75
|
+
timer:duration=<seconds> | timer:deadline=<ISO-8601>
|
|
76
|
+
wake after N seconds (DB-clocked server-side) or at an
|
|
77
|
+
absolute time. Exempt from the 2 h --timeout minimum.
|
|
60
78
|
chat:channel=<id>|* new Direct Chat message (or * = any channel you are in;
|
|
61
79
|
you are never woken by your own messages). Level-
|
|
62
80
|
triggered at arm: an unread reply from another
|
|
@@ -65,6 +83,7 @@ CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the fir
|
|
|
65
83
|
so send-then-arm never misses a fast reply. Respects
|
|
66
84
|
your read marker (mark_direct_chat_read) and your own
|
|
67
85
|
last message; a sibling session's reply still wakes.
|
|
86
|
+
include_self= is reserved (accepted, no effect yet).
|
|
68
87
|
lock:subtype=<t>,host=<h>[,slot=<s>][,claim=true]
|
|
69
88
|
a resource lock becomes available
|
|
70
89
|
subtype ∈ playwright_lane|vite_port|backend_port|supabase_local
|
|
@@ -82,9 +101,10 @@ CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the fir
|
|
|
82
101
|
in the repo; or pass pr=<owner/repo#n> in one value. A
|
|
83
102
|
conflict/clean flip sets payload.mergeable_changed. The
|
|
84
103
|
payload carries head_ref_oid; sha=<head> (7-40 hex; either
|
|
85
|
-
side may be an abbreviated prefix) wakes only on signals for
|
|
86
|
-
previous head's check result can't wake you.
|
|
87
|
-
emits nothing — the head rides on the next
|
|
104
|
+
side may be an abbreviated prefix) wakes only on signals for
|
|
105
|
+
that head, so a previous head's check result can't wake you.
|
|
106
|
+
A push alone emits nothing — the head rides on the next
|
|
107
|
+
transition.
|
|
88
108
|
pr-review:repo=<owner/repo>[,pr=<n>][,reviewer=<login>][,sha=<commit>]
|
|
89
109
|
a submitted review, a new review comment, a thread
|
|
90
110
|
resolved/re-opened (payload carries the unresolved-thread
|
|
@@ -96,27 +116,62 @@ CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the fir
|
|
|
96
116
|
has an outstanding review at arm, bb-wait prints a one-line
|
|
97
117
|
stderr notice (unresolved count, review_decision, age) so a
|
|
98
118
|
silent park isn't mistaken for "no review yet" — the
|
|
99
|
-
existing review will not re-wake it.
|
|
119
|
+
existing review will not re-wake it. Your own thread reply or
|
|
120
|
+
resolve also wakes it: check reviewer_login + commit_sha.
|
|
100
121
|
reviewer=<login> wakes only on that reviewer (case-
|
|
101
122
|
insensitive; a trailing [bot] is ignored). sha=<commit>
|
|
102
|
-
wakes only on verdicts for that commit (either side may be an
|
|
103
|
-
and clean passes carry
|
|
104
|
-
so they never match sha=);
|
|
105
|
-
passes sha=. Any other key is
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
123
|
+
wakes only on verdicts for that commit (either side may be an
|
|
124
|
+
abbreviated prefix; submitted reviews and clean passes carry
|
|
125
|
+
commit_sha; thread comments do not, so they never match sha=);
|
|
126
|
+
the usage-limit decline always passes sha=. Any other key is
|
|
127
|
+
refused (exit 4).
|
|
128
|
+
ci:repo=<owner/repo>,{scope=latest,pr=<n> | run_id=<id> | sha=<sha>[,workflow=<name>]
|
|
129
|
+
| scope=next[,sha=<sha>][,branch=<name>][,workflow=<name>]
|
|
130
|
+
[,workflow_id=<id>][,workflow_path=<path>]}
|
|
131
|
+
a run's terminal conclusion (owner-scoped) — not a
|
|
132
|
+
PR state or review change (use pr-state / pr-review);
|
|
133
|
+
GitHub Actions or an external check_suite provider.
|
|
134
|
+
A repo with no CI feed is rejected (no_signal_source).
|
|
135
|
+
scope=latest,pr=<n> follows the PR's current head.
|
|
136
|
+
scope=next is LIVE-ONLY: it wakes on the first terminal
|
|
137
|
+
run delivered AFTER arm (post-merge / cron / scheduled),
|
|
138
|
+
never replays a finished run, and needs no run_id/pr.
|
|
139
|
+
Optional sha= pins it to a commit (BOT-1685: arm before
|
|
140
|
+
re-dispatching a run for a known SHA so it wakes on the
|
|
141
|
+
NEW run, not an earlier terminal run for the same SHA);
|
|
142
|
+
branch= (exact) / workflow= (GitHub workflow DISPLAY
|
|
143
|
+
name, case-insensitive) / workflow_id= (GitHub's numeric
|
|
144
|
+
id) / workflow_path= (e.g. .github/workflows/staging.yml,
|
|
145
|
+
exact) narrow that first run too. A filter matching no
|
|
146
|
+
known workflow exits 4 unknown_workflow and prints the
|
|
147
|
+
valid workflow=/workflow_id=/workflow_path= lines.
|
|
148
|
+
test-run:id=<run_id> a test run reaching a terminal status (owner-scoped);
|
|
149
|
+
bb test <lane> prints the exact line to arm.
|
|
150
|
+
stack-run:id=<run UUID> one named bb stack run reaching its terminal outcome
|
|
151
|
+
(BOT-2629). Choose the UUID BEFORE dispatch and pass it as
|
|
152
|
+
bb stack run --run-id <UUID>; the receipt carries exit_code,
|
|
153
|
+
child_exit_code, category, outcome and code. Live-only and
|
|
154
|
+
recipient-scoped to the run's agent. Never substitute a
|
|
155
|
+
lease: transition, a log tail or a polling loop.
|
|
156
|
+
lease:id=<lease_id>[,state=<s>] a batch stack lease (BOT-1218). Without state=: the lease
|
|
157
|
+
leaving the queue / becoming active — the zero-poll wake
|
|
158
|
+
for a parked bb stack up (recipient-scoped to the owner).
|
|
110
159
|
Level-triggered: an already-granted lease wakes at once.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
accepted successful reap receipt or a
|
|
116
|
-
never-provisioned no-op; preserves stop/destroy
|
|
160
|
+
state=provisioning|active (BOT-2400): that exact state;
|
|
161
|
+
both end on lease loss (exit 9, with the provisioning
|
|
162
|
+
failure's reason and class).
|
|
163
|
+
state=reaped (BOT-1948): proven cleanup of that owned
|
|
164
|
+
lease. Requires an accepted successful reap receipt or a
|
|
165
|
+
verified never-provisioned no-op; preserves stop/destroy
|
|
117
166
|
disposition. An already-cleaned lease matches at once;
|
|
118
|
-
reaping keeps waiting. A verified failed reap exits 12;
|
|
119
|
-
|
|
167
|
+
reaping keeps waiting. A verified failed reap exits 12; an
|
|
168
|
+
unverified gave-up reap exits 9 (lease_lost).
|
|
169
|
+
managed-allocation:id=<allocation UUID>,generation=<n>,target_revision=<n>
|
|
170
|
+
one exact managed allocation reaching an accepted cleanup
|
|
171
|
+
outcome (BOT-2330), fenced to that generation/revision. A
|
|
172
|
+
released match needs a zero permit count and the exact
|
|
173
|
+
accepted Helper cleanup receipt; an accepted cleanup_failed
|
|
174
|
+
receipt exits 10. A lifecycle wait, not host capacity.
|
|
120
175
|
guidance:request=<post_id> a command-post guidance request being answered
|
|
121
176
|
(scoped to the asking agent; fetch the body with
|
|
122
177
|
get_guidance_response)
|
|
@@ -132,6 +187,8 @@ CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the fir
|
|
|
132
187
|
Tenant-scoped. Also accepts unblocked:ticket=<KEY>.
|
|
133
188
|
capacity:policy=<p>,slots>=<N>[,host=<h>][,stale_grace=<sec>]
|
|
134
189
|
a managed-container host reaching >= N free slots.
|
|
190
|
+
Copy the condition a capacity refusal prints (e.g.
|
|
191
|
+
policy=default or host_docker_pressure_v1).
|
|
135
192
|
Host-shared; a silently-dead host is detected server-side
|
|
136
193
|
(capacity_source_stale) via a host beacon. BOT-2400: a stale
|
|
137
194
|
source is DEGRADED, never terminal — the wait stays armed and
|
|
@@ -141,25 +198,21 @@ CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the fir
|
|
|
141
198
|
one and its receipt carries grant:{position,tier,score};
|
|
142
199
|
the others keep parking. Older servers broadcast without
|
|
143
200
|
the ranking (first-come). Needs cli >= 1.20.0.
|
|
144
|
-
ci:repo=<owner/repo>,{scope=latest,pr=<n> | run_id=<id> | sha=<sha>
|
|
145
|
-
| scope=next[,sha=<sha>][,branch=<name>][,workflow=<name>]}
|
|
146
|
-
a run's terminal conclusion (owner-scoped) — not a
|
|
147
|
-
PR state or review change (use pr-state / pr-review);
|
|
148
|
-
GitHub Actions or an external check_suite provider.
|
|
149
|
-
A repo with no CI feed is rejected (no_signal_source).
|
|
150
|
-
scope=next is LIVE-ONLY: it wakes on the first terminal
|
|
151
|
-
run delivered AFTER arm (post-merge / cron / scheduled),
|
|
152
|
-
never replays a finished run, and needs no run_id/pr.
|
|
153
|
-
Optional sha= pins it to a commit (BOT-1685: arm before
|
|
154
|
-
re-dispatching a run for a known SHA so it wakes on the
|
|
155
|
-
NEW run, not an earlier terminal run for the same SHA);
|
|
156
|
-
branch= (exact) / workflow= (GitHub workflow DISPLAY
|
|
157
|
-
name, case-insensitive) narrow that first run too.
|
|
158
201
|
staging-green:repo=<owner/repo> verified automatic recovery of an enabled staging gate.
|
|
159
202
|
Wakes only after a complete non-vacuous workflow success;
|
|
160
203
|
manual resolutions, overrides, skipped, and neutral runs do not match.
|
|
204
|
+
staging-release:repo=<owner/repo>,pr=<n>[,after_run=<run_id>[:<attempt>]]
|
|
205
|
+
the first staging release train whose head CONTAINS your
|
|
206
|
+
merged PR (git ancestry, never timestamps) reaching a
|
|
207
|
+
terminal conclusion — success OR failure (payload.conclusion
|
|
208
|
+
+ verified; on verified:false check staging yourself).
|
|
209
|
+
Level-triggered: a train that already shipped your merge
|
|
210
|
+
grants at once (payload.reason=already_released).
|
|
211
|
+
after_run= skips a run you already saw, so a wait re-armed
|
|
212
|
+
from a failure receipt parks for the NEXT train.
|
|
161
213
|
event:type=<signal_type>[,subject=<key>]
|
|
162
|
-
raw signal-spine match (escape hatch
|
|
214
|
+
raw signal-spine match (escape hatch only when that raw
|
|
215
|
+
signal IS the outcome). Host setup uses
|
|
163
216
|
event:type=host_approval,subject=bbh-<24 lowercase hex>;
|
|
164
217
|
it wakes only for the authenticated owner's one bound
|
|
165
218
|
tenant after current server-side approval revalidation.
|
|
@@ -181,25 +234,32 @@ OPTIONS
|
|
|
181
234
|
(--agent-key and --session-token are one-release aliases.)
|
|
182
235
|
--session-id <uuid> advanced attribution override; normal worktrees derive it automatically.
|
|
183
236
|
--help show this help
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
237
|
+
|
|
238
|
+
SUBCOMMANDS (saved waits live in this worktree's .botbuddy/waits/)
|
|
239
|
+
bb wait List this worktree's saved waits (after a restart or crash);
|
|
240
|
+
[--offset <n>] pages. Non-interactive callers get JSON.
|
|
241
|
+
bb wait result [<local_wait_id>] [--from-file <path> --exit-code <n>]
|
|
242
|
+
read a terminal receipt without re-arming or acknowledging;
|
|
243
|
+
--from-file validates redirected output AFTER the original
|
|
244
|
+
command exits (see EXAMPLES)
|
|
245
|
+
bb wait resume <local_wait_id> recover one explicitly named saved wait
|
|
246
|
+
bb wait cancel <local_wait_id> explicitly stop one saved wait
|
|
247
|
+
bb wait acknowledge <local_wait_id> mark one saved terminal receipt read (after acting on it)
|
|
190
248
|
|
|
191
249
|
AUTH
|
|
192
250
|
In a configured project worktree, bb wait restores or renews its session automatically.
|
|
193
|
-
For first-time setup or repair, run bb setup once
|
|
194
|
-
variables, copied identifiers, or credential
|
|
251
|
+
For first-time setup or repair, run bb setup once (bb doctor --fix for a stale session),
|
|
252
|
+
then retry the same wait. No environment variables, copied identifiers, or credential
|
|
253
|
+
flags are needed for normal waits.
|
|
195
254
|
--token and --profile are RETIRED (BOT-1574); the session override above is for managed
|
|
196
255
|
runners only. The per-machine client key is a setup credential, never a wait credential.
|
|
197
256
|
|
|
198
257
|
OUTPUT
|
|
199
258
|
Exactly one JSON receipt line on stdout at exit, with client semver and
|
|
200
259
|
protocol identity. Diagnostics go to stderr — including the arm-time notices
|
|
201
|
-
(outstanding review, latest CI run)
|
|
202
|
-
notice is on stderr and you will not
|
|
260
|
+
(outstanding review, latest CI run) and the 🔗 /waits link to show the user.
|
|
261
|
+
Do not pipe bb wait through tail: the notice is on stderr and you will not
|
|
262
|
+
see it until the wait exits.
|
|
203
263
|
Exit codes: 0 matched · 2 timeout · 3 auth · 4 invalid conditions ·
|
|
204
264
|
5 backend unavailable · 6 cursor expired · 7 internal ·
|
|
205
265
|
8 superseded (wait replaced server-side) · 9 lease lost (a
|
|
@@ -216,24 +276,78 @@ OUTPUT
|
|
|
216
276
|
only on a queued/busy result (granted = you hold it — just resume);
|
|
217
277
|
otherwise re-run from the receipt's next_cursor. docs/agent-wait.md.
|
|
218
278
|
|
|
219
|
-
EXAMPLES
|
|
220
|
-
#
|
|
221
|
-
|
|
279
|
+
EXAMPLES (concrete values shown — substitute your repo, PR, SHA, ticket and ids)
|
|
280
|
+
# Wait for time. The ONLY sanctioned way to wait N seconds or until a clock time.
|
|
281
|
+
bb wait 'timer:duration=1800'
|
|
282
|
+
bb wait 'timer:deadline=2026-11-02T09:05:00Z'
|
|
283
|
+
|
|
284
|
+
# After a push: CI on the PR's current head finished (any conclusion).
|
|
285
|
+
bb wait 'ci:repo=bot-buddy/botbuddy-webapp,scope=latest,pr=1234' --heartbeat
|
|
286
|
+
|
|
287
|
+
# After a merge or a re-dispatch: the NEXT run for that exact commit (arm first, then dispatch).
|
|
288
|
+
bb wait 'ci:repo=bot-buddy/botbuddy-webapp,scope=next,sha=9f3c2a1b4d5e6f708192a3b4c5d6e7f809a1b2c3' --heartbeat
|
|
289
|
+
bb wait 'ci:repo=bot-buddy/botbuddy-webapp,scope=next,branch=main,workflow_path=.github/workflows/staging.yml' --heartbeat
|
|
290
|
+
|
|
291
|
+
# Codex review of the head you just pushed (arm BEFORE posting @codex review).
|
|
292
|
+
bb wait 'pr-review:repo=bot-buddy/botbuddy-webapp,pr=1234,reviewer=chatgpt-codex-connector[bot],sha=9f3c2a1' --heartbeat
|
|
293
|
+
|
|
294
|
+
# The next review comment OR a PR state change (merge, conflict, checks), whichever first.
|
|
295
|
+
bb wait --any 'pr-review:repo=bot-buddy/botbuddy-webapp,pr=1234' \\
|
|
296
|
+
'pr-state:repo=bot-buddy/botbuddy-webapp,pr=1234,sha=9f3c2a1' --heartbeat
|
|
297
|
+
|
|
298
|
+
# A ticket changes (state, comment, assignee) / its last blocker clears.
|
|
299
|
+
bb wait 'linear:issue=BOT-1234' --heartbeat
|
|
300
|
+
bb wait 'unblocked:BOT-1234' --heartbeat
|
|
301
|
+
|
|
302
|
+
# Your merged PR reached staging (success or failure) / staging recovered.
|
|
303
|
+
bb wait 'staging-release:repo=bot-buddy/botbuddy-webapp,pr=1234' --timeout 28800 --heartbeat
|
|
304
|
+
bb wait 'staging-green:repo=bot-buddy/botbuddy-webapp' --heartbeat
|
|
305
|
+
|
|
306
|
+
# A named stack run finished: choose the UUID, arm, then bb stack run --run-id <same UUID> -- ...
|
|
307
|
+
bb wait 'stack-run:id=2f9d6a3c-0b1e-4c5d-8e7f-1a2b3c4d5e6f' --heartbeat
|
|
222
308
|
|
|
223
|
-
#
|
|
224
|
-
|
|
225
|
-
|
|
309
|
+
# A parked lease was granted / a lease's cleanup was proven.
|
|
310
|
+
bb wait 'lease:id=7c1e2d3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f' --heartbeat
|
|
311
|
+
bb wait 'lease:id=7c1e2d3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f,state=reaped'
|
|
226
312
|
|
|
227
|
-
#
|
|
228
|
-
|
|
313
|
+
# Docker capacity after HOST_CAPACITY_SOURCE_STALE (phase transient) or a pressure refusal:
|
|
314
|
+
# copy the refusal's own condition, then re-run the refused command once.
|
|
315
|
+
bb wait 'capacity:policy=host_docker_pressure_v1,slots>=1' --heartbeat
|
|
229
316
|
|
|
230
|
-
#
|
|
231
|
-
|
|
232
|
-
|
|
317
|
+
# A tracked test run finished (bb test <lane> prints this line with the real id).
|
|
318
|
+
bb wait 'test-run:id=5b0c7e2a-9d41-4f3e-8a6b-1c2d3e4f5a6b' --heartbeat
|
|
319
|
+
|
|
320
|
+
# Wait for a Playwright lane to free up, then acquire it yourself.
|
|
321
|
+
bb wait 'lock:subtype=playwright_lane,host=Jonos-MBP'
|
|
322
|
+
|
|
323
|
+
# Queue for a specific lane and be HANDED it when it frees (no acquire race):
|
|
324
|
+
# acquire_lock (gets queued) → bb wait claim=true → wake already holding lane 3
|
|
325
|
+
bb wait 'lock:subtype=playwright_lane,host=Jonos-MBP,slot=3,claim=true' --heartbeat
|
|
326
|
+
|
|
327
|
+
# A Direct Chat reply on any channel you are in (arm before sending).
|
|
328
|
+
bb wait 'chat:channel=*' --heartbeat
|
|
329
|
+
|
|
330
|
+
# Redirect a wait's output and still return its receipt: run these three lines as ONE
|
|
331
|
+
# pending (background) command, and keep that command's result pending until it exits.
|
|
332
|
+
wait_artifact=$(mktemp /tmp/bb-wait-result.XXXXXX); wait_exit=0
|
|
333
|
+
bb wait 'pr-review:repo=bot-buddy/botbuddy-webapp,pr=1234,sha=9f3c2a1' --heartbeat > "$wait_artifact" 2>&1 || wait_exit=$?
|
|
334
|
+
bb wait result --from-file "$wait_artifact" --exit-code "$wait_exit"
|
|
335
|
+
|
|
336
|
+
# After a restart: list saved waits, then recover exactly one by id.
|
|
337
|
+
bb wait
|
|
338
|
+
bb wait resume <local_wait_id>
|
|
233
339
|
|
|
234
340
|
${SETUP_BLOCK}
|
|
235
341
|
`;
|
|
236
342
|
|
|
343
|
+
// BOT-2715: exit only once stdout has taken the whole text. On a macOS pipe the
|
|
344
|
+
// write is async, so a process.exit() straight after it can drop everything past
|
|
345
|
+
// the first 8 KiB (CI cut the ~22 KiB help at exactly 8192 bytes). Never resolves:
|
|
346
|
+
// the process ends in the write callback.
|
|
347
|
+
export function writeThenExit(text, { stream = process.stdout, exit = (code) => process.exit(code) } = {}) {
|
|
348
|
+
return new Promise(() => { stream.write(text, () => exit(EXIT.MATCHED)); });
|
|
349
|
+
}
|
|
350
|
+
|
|
237
351
|
// BOT-2400: a wait's timeout is a SAFETY CAP measured in hours or days, never a polling
|
|
238
352
|
// interval. Each re-arm wakes the agent and re-reads its whole context (ENT-4850 §18: 15
|
|
239
353
|
// wakes ≈ 50M tokens), so the default is a day, the cap three days, and a cap under two hours
|
|
@@ -1121,10 +1235,7 @@ async function inspectSavedWaits(argv = []) {
|
|
|
1121
1235
|
});
|
|
1122
1236
|
process.exit(EXIT.MATCHED);
|
|
1123
1237
|
}
|
|
1124
|
-
if (inventory.items.length === 0)
|
|
1125
|
-
process.stdout.write(HELP);
|
|
1126
|
-
process.exit(EXIT.MATCHED);
|
|
1127
|
-
}
|
|
1238
|
+
if (inventory.items.length === 0) return writeThenExit(HELP);
|
|
1128
1239
|
// A TTY gets discovery guidance without an implicit selection. This is a
|
|
1129
1240
|
// deliberately conservative menu: a restart must name an ID before mutation.
|
|
1130
1241
|
process.stdout.write("Saved bb waits:\n");
|
|
@@ -1608,10 +1719,7 @@ export async function runWait(argv, { recoveryLocalWaitId = null } = {}) {
|
|
|
1608
1719
|
writeLeaseLostNotice(receipt);
|
|
1609
1720
|
return emit(receipt, { ...options, maxBytes: opts.receiptMaxBytes });
|
|
1610
1721
|
};
|
|
1611
|
-
if (opts.help)
|
|
1612
|
-
process.stdout.write(HELP);
|
|
1613
|
-
process.exit(0);
|
|
1614
|
-
}
|
|
1722
|
+
if (opts.help) return writeThenExit(HELP);
|
|
1615
1723
|
// BOT-1574 (AC3): --token / --profile are retired. Emit the migration hint and
|
|
1616
1724
|
// a typed `unknown_option` receipt, then exit 4 — before any relay call.
|
|
1617
1725
|
if (opts.retiredFlag) {
|