@bli-cockpit/cli 0.2.58 → 0.2.60

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 (43) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/browser-open.js +88 -0
  3. package/dist/commands/docs.js +32 -6
  4. package/dist/commands/doctor-report.js +17 -1
  5. package/dist/commands/doctor.js +12 -2
  6. package/dist/commands/heartbeat.js +65 -1
  7. package/dist/commands/jarvis-answer-envelope.js +82 -0
  8. package/dist/commands/jarvis-render.js +18 -0
  9. package/dist/commands/jarvis-turn.js +29 -2
  10. package/dist/commands/jarvis.js +3 -0
  11. package/dist/commands/local-args-collector-setup.js +17 -0
  12. package/dist/commands/local-args-tower-admin.js +12 -2
  13. package/dist/commands/local-args-tower-chat.js +5 -0
  14. package/dist/commands/local-args-tower-docs-msg.js +105 -6
  15. package/dist/commands/local-args-tower-search.js +50 -0
  16. package/dist/commands/local-args-tower.js +4 -1
  17. package/dist/commands/local-args.js +28 -13
  18. package/dist/commands/local-command-shapes.js +12 -0
  19. package/dist/commands/local-help-commands.js +655 -0
  20. package/dist/commands/local-help.js +11 -582
  21. package/dist/commands/local.js +3 -0
  22. package/dist/commands/login.js +91 -8
  23. package/dist/commands/memory-install-claude.js +35 -15
  24. package/dist/commands/memory-install-codex-hooks.js +200 -0
  25. package/dist/commands/memory-install-codex.js +12 -2
  26. package/dist/commands/memory-install-receipt.js +222 -0
  27. package/dist/commands/memory-install-report.js +25 -1
  28. package/dist/commands/memory-install.js +76 -2
  29. package/dist/commands/msg.js +85 -2
  30. package/dist/commands/onboard-completion.js +47 -0
  31. package/dist/commands/onboard-setup.js +82 -2
  32. package/dist/commands/ops-render-memory.js +76 -0
  33. package/dist/commands/ops-render.js +6 -0
  34. package/dist/commands/ops.js +59 -2
  35. package/dist/commands/public-root.js +1 -1
  36. package/dist/commands/search.js +122 -0
  37. package/dist/commands/setup-receipt-lines.js +71 -0
  38. package/dist/commands/setup-receipt.js +241 -0
  39. package/dist/commands/status.js +20 -1
  40. package/dist/commands/tower-mcp-install.js +4 -2
  41. package/dist/local-state-pairing-code.js +200 -0
  42. package/dist/local-state.js +6 -0
  43. package/package.json +4 -4
@@ -8,12 +8,14 @@
8
8
  * user-visible contract like any other.
9
9
  */
10
10
  import { DEFAULT_DASHBOARD_URL } from "../local-state.js";
11
+ import { localSubcommandHelp } from "./local-help-commands.js";
11
12
  export const rootCommandNames = new Set([
12
13
  "onboard",
13
14
  "update",
14
15
  "upgrade",
15
16
  "do-everything",
16
17
  "fix",
18
+ "doctor",
17
19
  "install",
18
20
  "login",
19
21
  "pair",
@@ -44,6 +46,7 @@ export const rootCommandNames = new Set([
44
46
  "msg",
45
47
  "issue",
46
48
  "project",
49
+ "search",
47
50
  "release",
48
51
  ]);
49
52
  export function localCommandHelp(command) {
@@ -55,17 +58,18 @@ export function localCommandHelp(command) {
55
58
  " cockpit upgrade [same flags as update]",
56
59
  " cockpit do-everything [--workspace <path>] [--dashboard-url <url>] [--update-tag <tag>] [--dry-run] [--json]",
57
60
  " cockpit fix [same flags as do-everything]",
61
+ " cockpit doctor [same flags as do-everything]",
58
62
  " cockpit install [--dashboard-url <url>] [--workspace <path>] [--allow-home-root] [--json]",
59
- " cockpit login [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--no-auth] [--json]",
60
- " cockpit pair [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--no-auth] [--json]",
63
+ " cockpit login [--pair <code>] [--no-browser] [--legacy-pair] [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--no-auth] [--json]",
64
+ " cockpit pair [--pair <code>] [--no-browser] [--legacy-pair] [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--no-auth] [--json]",
61
65
  " cockpit logout [--revoke] [--yes] [--dashboard-url <url>] [--json]",
62
66
  " cockpit start [--ticket <id>|--clear-ticket] [--topic <label>] [--intent <intent>] [--phase <phase>] [--workspace <path>] [--branch <name>] [--max-depth <n>] [--max-repos <n>] [--json]",
63
67
  " cockpit sync [--workspace <path>] [--dashboard-url <url>] [--max-depth <n>] [--max-repos <n>] [--json]",
64
68
  " cockpit analyze [--workspace <path>] [--dashboard-url <url>] [--max-depth <n>] [--max-repos <n>] [--json]",
65
- " cockpit jarvis [question] [--prompt <question>] [--as <person>] [--date <YYYY-MM-DD>] [--thread <name>] [--model <key>] [--image <path>|--file <path>] [--no-stream] [--threads|--history [--limit <n>]] [--trace <id|last>] [--dashboard-url <url>] [--json]",
69
+ " cockpit jarvis [question] [--prompt <question>] [--as <person>] [--date <YYYY-MM-DD>] [--thread <name>] [--model <key>] [--image <path>|--file <path>] [--no-stream] [--threads|--history [--limit <n>]] [--trace <id|last>] [--dashboard-url <url>] [--json [--show-approval-code]]",
66
70
  " cockpit model [show|set <provider:model>] [--json]",
67
71
  " cockpit scout [start|dismiss|undo <experiment-id>] [--days <n>] [--dashboard-url <url>] [--json]",
68
- " cockpit ops [status [--job <id>] [--skips] | recompile --person <email|name|id> [--dry-run]] [--dashboard-url <url>] [--json]",
72
+ " cockpit ops [status [--job <id>] [--skips] [--memory] | recompile --person <email|name|id> [--dry-run]] [--dashboard-url <url>] [--json]",
69
73
  " cockpit slack [coverage [--workspace bli|blue_pearl] [--stale-only] | read [--person <p>] [--channel <c>] [--query <text>] [--since <YYYY-MM-DD>] [--until <YYYY-MM-DD>] [--limit <n>]] [--json]",
70
74
  " cockpit settings [personal [--chat-model <key>] [--brief-model <key>] | switches [set <key> <value>] | models [set --chat <key>] [--memory <id>] | cli-floor [<version>] | env list|set --project <p> --file <f> --content-stdin|delete --id <uuid> [--yes]] [--json]",
71
75
  " cockpit team [members | invite <email> --role <role> [--team-id <uuid>] | role <userId> --role <role> [--yes] | device [list] | device revoke <id|name> --reason <label> [--note \"<text>\"] [--yes]] [--json]",
@@ -82,591 +86,16 @@ export function localCommandHelp(command) {
82
86
  " cockpit agent-rules [install|uninstall|status] [--host codex|claude|all] [--workspace <path>] [--json]",
83
87
  " cockpit memory [install|status] [--dashboard-url <url>] [--dry-run] [--json]",
84
88
  " cockpit clean [--dry-run] [--all-committed] [--reconcile] [--dashboard-url <url>] [--json]",
85
- " cockpit docs [list|tree|read <id|slug>|create --title <t>|update <id>] [--parent <id>|--clear-parent] [--visibility org|private] [--file <path>|--body-stdin] [--dashboard-url <url>] [--json]",
86
- " cockpit msg [channels|read <channel>|send <channel>|thread <id> --channel <channel>] [--thread <id>] [--limit <n>] [--dashboard-url <url>] [--json]",
89
+ " cockpit docs [list|tree|read <id|slug>|create --title <t>|update <id>] [--parent <id>|root|--clear-parent] [--query <text>] [--limit <n>] [--visibility org|private] [--file <path>|--body-stdin] [--allow-empty] [--dashboard-url <url>] [--json]",
90
+ " cockpit msg [channels|create <name>|dm <email>|read <channel>|send <channel>|thread <id> --channel <channel>] [--private] [--members a@x,b@y] [--description <text>] [--thread <id>] [--limit <n>] [--dashboard-url <url>] [--json]",
87
91
  " cockpit issue [list|show <BLI-id>|create --title <t>|update <BLI-id>|move <BLI-id> <state>|comment <BLI-id>|history <BLI-id>] [--state <s>] [--assignee me|unassigned|<uuid>] [--project <name|id>] [--limit <n>] [--priority 0-4] [--parent <BLI-id>] [--file <path>|--body-stdin] [--dashboard-url <url>] [--json]",
88
92
  " cockpit project [list] [--archived] [--dashboard-url <url>] [--json]",
93
+ " cockpit search \"<words>\" [--kind doc,msg,issue,note,memory] [--limit <n>] [--dashboard-url <url>] [--json]",
89
94
  " cockpit release [--dry-run] [--skip-checks] [--no-floor] [--tag <tag>] [--access <public|restricted>] [--otp <code>]",
90
95
  "",
91
96
  `Default dashboard: ${DEFAULT_DASHBOARD_URL}. Omit --dashboard-url for normal production use; pass it only for staging/custom dashboards or to force a different pairing.`,
92
97
  ].join("\n");
93
98
  }
94
- function localSubcommandHelp(command) {
95
- const helpByCommand = new Map([
96
- [
97
- "onboard",
98
- [
99
- "Usage: cockpit onboard [--ticket <id>] [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--workspace <path>] [--allow-home-root] [--branch <name>] [--no-auth] [--max-depth <n>] [--max-repos <n>] [--json]",
100
- "",
101
- "Installs, pairs, starts work context(s), syncs once, completes all-history",
102
- "Codex and Claude backfill for the saved roots, and then tells you it is working.",
103
- "If --workspace is a parent folder, scans child git repos/worktrees and rolls them up by repo.",
104
- "`--repo <path>` remains supported as a backward-compatible alias.",
105
- `Omit --dashboard-url for normal production setup (${DEFAULT_DASHBOARD_URL}).`,
106
- "Pass --dashboard-url only for staging/custom dashboards or to force a different pairing.",
107
- "Run with no flags in a terminal and it prompts for the dashboard email and OTP code; pass --email to skip the email prompt. Use --no-auth to force the manual approval fallback.",
108
- "Interactive runs also offer to add Tower ticket-binding rules to AGENTS.md and CLAUDE.md after readiness proof.",
109
- ],
110
- ],
111
- [
112
- "install",
113
- [
114
- "Usage: cockpit install [--dashboard-url <url>] [--workspace <path>] [--allow-home-root] [--json]",
115
- "",
116
- "Writes local collector config. Pair with `cockpit login`, then run `cockpit start` when work begins.",
117
- "`--repo <path>` remains supported as a backward-compatible alias.",
118
- "Omit --dashboard-url for the production dashboard; pass it only for staging/custom dashboards.",
119
- ],
120
- ],
121
- [
122
- "update",
123
- [
124
- "Usage: cockpit update [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--workspace <path>] [--allow-home-root] [--no-auth] [--json]",
125
- "",
126
- "Updates the global public CLI from npm, then reruns `cockpit onboard`",
127
- "with the same setup flags so pairing, saved roots, all-history backfill,",
128
- "agent rules, autostart, and the initial sync are refreshed in one command.",
129
- "`cockpit upgrade` is an alias.",
130
- ],
131
- ],
132
- [
133
- "upgrade",
134
- [
135
- "Usage: cockpit upgrade [same flags as cockpit update]",
136
- "",
137
- "Alias for `cockpit update`.",
138
- ],
139
- ],
140
- [
141
- "do-everything",
142
- [
143
- "Usage: cockpit do-everything [--workspace <path>] [--dashboard-url <url>] [--update-tag <tag>] [--dry-run] [--json]",
144
- "",
145
- "Gets this machine fully set up, whether it is brand new, already set up, or handed down: latest CLI, sign-in and collection-root recovery when needed, saved folders, background sync, catching up on old sessions, cleaning up old files, and one fresh upload.",
146
- "`cockpit fix` is an alias.",
147
- "Maintainers only: use `--update-tag next` so self-update and re-exec stay on the prerelease candidate.",
148
- "--dry-run shows what it would do without changing anything.",
149
- ],
150
- ],
151
- [
152
- "fix",
153
- [
154
- "Usage: cockpit fix [same flags as cockpit do-everything]",
155
- "",
156
- "Alias for `cockpit do-everything`.",
157
- ],
158
- ],
159
- [
160
- "login",
161
- [
162
- "Usage: cockpit login [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--no-auth] [--json]",
163
- "",
164
- "Signs in with an email OTP when interactive, starts dashboard device pairing, and stores the approved local session.",
165
- "Omit --dashboard-url for the production dashboard; pass it only for staging/custom dashboards.",
166
- ],
167
- ],
168
- [
169
- "pair",
170
- [
171
- "Usage: cockpit pair [--email <owner@email>] [--device-name <name>] [--dashboard-url <url>] [--no-auth] [--json]",
172
- "",
173
- "Alias for `cockpit login`.",
174
- ],
175
- ],
176
- [
177
- "logout",
178
- [
179
- "Usage: cockpit logout [--revoke] [--yes] [--dashboard-url <url>] [--json]",
180
- "",
181
- "Removes the local device session.",
182
- "--revoke also asks Tower to end this device's token, which is what you want",
183
- "when the machine is being handed back: without it this laptop stops collecting",
184
- "but the token it held is still one the server would honour.",
185
- "A device can only revoke ITSELF; revoking someone else's machine stays in the browser.",
186
- "The local session is removed either way, and the two outcomes are reported separately.",
187
- ],
188
- ],
189
- [
190
- "model",
191
- [
192
- "Usage: cockpit model [show|set <provider:model>] [--json]",
193
- "",
194
- "Shows or changes YOUR chat model — the short way to say",
195
- "`cockpit settings personal --chat-model`. Affects your account, not the org.",
196
- "An unrecognised key is refused by the dashboard, not by this command.",
197
- ],
198
- ],
199
- [
200
- "settings",
201
- [
202
- "Usage: cockpit settings [section] [verb] [flags] [--json]",
203
- "",
204
- "With no section it shows everything you can see; a section you cannot see reads",
205
- "`admin only` rather than failing, because that is the system working.",
206
- "",
207
- " cockpit settings personal [--chat-model <key>] [--brief-model <key>]",
208
- " Your teams and your two model choices. No flags = show.",
209
- " cockpit settings switches [set <key> <value>]",
210
- " Access and feature switches. Everyone may look; only an admin may set.",
211
- " cockpit settings models [set --chat <key>] [--memory <id>]",
212
- " The ORG-WIDE model map. Admin only, in both directions.",
213
- " cockpit settings cli-floor [<version>]",
214
- " The fleet forced-update floor: every collector below it updates on its",
215
- " next sync tick. Everyone may look; only an admin may raise it, and it",
216
- " only ever goes UP — lowering it is a rollback a human makes deliberately.",
217
- " A `--tag latest` publish raises it for you (scripts/publish-public-packages.mjs).",
218
- " cockpit settings env list",
219
- " Stored env files: project, name, when, id. Never a value — the server",
220
- " cannot return one and this command has no flag that prints one.",
221
- " cockpit settings env set --project <project> --file <name> --content-stdin",
222
- " Pipe the file in; there is no --content flag, because a secret on a",
223
- " command line lands in shell history and in every process listing.",
224
- " macOS: cat <path> | cockpit settings env set --project <project> --file <name> --content-stdin",
225
- " PowerShell: Get-Content <path> -Raw | cockpit settings env set --project <project> --file <name> --content-stdin",
226
- " Run `cockpit settings env list` to see which file names this project accepts.",
227
- " cockpit settings env delete --id <uuid> [--yes]",
228
- " Asks first unless --yes; a headless run without --yes is refused, not assumed.",
229
- ],
230
- ],
231
- [
232
- "team",
233
- [
234
- "Usage: cockpit team [members | invite <email> --role <role> | role <userId> --role <role> | device [list] | device revoke <id|name> --reason <label>] [--json]",
235
- "",
236
- " cockpit team [members]",
237
- " Everyone on your team — roster and accounts together, with each person's id.",
238
- " Open to every role; it says so when the view is read-only for you.",
239
- " cockpit team invite <email> --role <role> [--team-id <uuid>]",
240
- " Admin only. Defaults to your own team; Tower emails them a sign-in link.",
241
- " cockpit team role <userId> --role <role> [--yes]",
242
- " Admin only. Asks first unless --yes. You cannot change your own role.",
243
- " cockpit team device [list] [--json]",
244
- " Every paired collector device: name, id, trust state, owner, and why",
245
- " (revoked reason, or when it was approved, or that it's still waiting).",
246
- " Admin only.",
247
- " cockpit team device revoke <id|name> --reason <label> [--note \"<text>\"] [--yes]",
248
- " Admin only. Names the device, asks first unless --yes, then revokes it —",
249
- " this cannot be undone. --reason must be one of: stale_test_pairing,",
250
- " machine_retired, person_left, re_pairing, other.",
251
- ],
252
- ],
253
- [
254
- "start",
255
- [
256
- "Usage: cockpit start [--ticket <id>|--clear-ticket] [--topic <label>] [--topic-summary <summary>] [--intent <intent>] [--phase <phase>] [--intent-confidence <0..1>] [--workspace <path>] [--branch <name>] [--json]",
257
- "",
258
- "Starts collecting your work in the background. If you point it at a parent folder it covers every repo inside.",
259
- "Add --ticket only when the work already has a visible ticket; omit it to preserve an existing binding.",
260
- "Use --clear-ticket to go back to collecting general work with no ticket attached.",
261
- "Use --topic/--intent/--phase for planning, discovery, and learning work that has no ticket yet.",
262
- "Supported intents: implementation, bug_fix, root_cause_analysis, planning, discovery, review, testing, documentation, release, learning, coordination, maintenance, analysis, unknown, other.",
263
- "Supported phases: planning, discovery, implementation, debugging, review, testing, documentation, release, handoff, analysis, unknown, other.",
264
- "`--repo <path>` remains supported as a backward-compatible alias; use --workspace in agent guidance.",
265
- ],
266
- ],
267
- [
268
- "sync",
269
- [
270
- "Usage: cockpit sync [--workspace <path>] [--dashboard-url <url>] [--json]",
271
- "",
272
- "Uploads your latest collected work. If it cannot reach Tower it saves a retry and tries again later.",
273
- "Omit --dashboard-url for normal production sync; pass it only for staging/custom dashboards or forced re-pairing.",
274
- "`--repo <path>` remains supported as a backward-compatible alias.",
275
- "Parent folders sync each child git worktree; Codex AND Claude Code JSONL",
276
- "transcripts (and Claude subagent sidecars) are attributed to repos",
277
- "deterministically and ambiguous transcripts are retained as unattributed",
278
- "instead of being duplicated across repos. Use `cockpit sessions` to see why",
279
- "a session is or is not collected.",
280
- "New repos start being collected automatically, with no ticket attached.",
281
- "Discovery scans 3 folder levels and up to 50 repos by default; tune with",
282
- "--max-depth and --max-repos.",
283
- "Also self-updates the CLI from npm latest once per day, strictly after",
284
- "collection finishes; set COCKPIT_DISABLE_AUTO_UPDATE=1 to freeze the",
285
- "installed version during incident triage.",
286
- ],
287
- ],
288
- [
289
- "analyze",
290
- [
291
- "Usage: cockpit analyze [--workspace <path>] [--dashboard-url <url>] [--json]",
292
- "",
293
- "Uploads your latest collected work, then asks Tower to analyse it.",
294
- "The command returns after the batch is queued; view status and results in My Work.",
295
- "Omit --dashboard-url for production; pass it only for staging/custom dashboards.",
296
- "`--repo <path>` remains supported as a backward-compatible alias.",
297
- ],
298
- ],
299
- [
300
- "jarvis",
301
- [
302
- "Usage: cockpit jarvis [question] [--prompt <question>] [--as <person>] [--date <YYYY-MM-DD>] [--thread <name>] [--model <key>] [--image <path>|--file <path>] [--no-stream] [--threads|--history [--limit <n>]] [--trace <id|last>] [--dashboard-url <url>] [--json]",
303
- "",
304
- "Chats with the same JARVIS used by Tower web chat and the BLI Slack DM.",
305
- "--as selects the existing website person space; it changes who the chat is about, never who is authenticated.",
306
- "--date <YYYY-MM-DD> binds the page that was live on one of that person's days (`today` and `yesterday` work too), so the turn is about that day's page instead of the newest one. A day with nothing compiled binds no page and JARVIS says so rather than answering from a different day.",
307
- "--model <key> requests one provider:model pair (e.g. openai:gpt-5.6-terra); an unrecognised key is refused by the dashboard, not this command.",
308
- "--image <path> (alias --file) attaches one local PNG, JPEG, or WEBP under 10 MB with the question, the same one-image gate the JARVIS panel uses. A bad path, an unsupported type, or an oversized file is refused with its own plain sentence — never a stack trace.",
309
- "Run with no question for an interactive terminal conversation.",
310
- "Agents can pass --prompt, positional text, or pipe one question on stdin.",
311
- "Each answer prints a per-tool trace line and, only on a fallback or model mismatch, one model receipt line.",
312
- "Trace lines arrive live while JARVIS works; pass --no-stream to wait for the whole answer in one piece instead.",
313
- "--json writes one machine-readable response (including trace and model) to stdout; operational metadata stays on stderr. A --json run never streams fragments, whichever mode it is in.",
314
- "A turn that outlives the dashboard's own ceiling is reported as turn_timed_out — an incomplete answer is never printed as a finished one.",
315
- "The command uses the existing paired device identity. It cannot override the caller, team, role, or person scope.",
316
- "--threads lists the terminal conversations on this account; --thread <name> --history replays one (the newest --limit <n> messages). Neither takes a question, and neither can reach anybody else's conversations.",
317
- "--trace <id|last> prints the step tree of one turn — every model step, tool call and server-side memory step, with how long each took, which model ran it, the tokens it spent and what failed. `last` opens the turn this machine asked most recently. Like --threads it takes no question and reaches no model.",
318
- "Run `cockpit login` first if this machine is not paired.",
319
- ],
320
- ],
321
- [
322
- "scout",
323
- [
324
- "Usage: cockpit scout [start|dismiss|undo <experiment-id>] [--days <n>] [--dashboard-url <url>] [--json]",
325
- "",
326
- "Prints the same Scout board the Tower page shows, in the same words: the standing",
327
- "watch line, the cards waiting on a decision, and the raw signal watch underneath.",
328
- "This is the board itself. To ask a QUESTION about it — what it means, whether it is",
329
- "worth doing here — use `cockpit jarvis`, whose readScout tool reads the same rows.",
330
- "--days <n> widens or narrows the window (1 to 90; the board defaults to 14).",
331
- "Reading follows the Scout page audience setting, so whoever can open /scout can run this.",
332
- "start, dismiss, and undo move one card and are super_admin actions on every surface;",
333
- "a refusal is printed as Tower worded it and the board stays readable underneath.",
334
- "The id can be the first 6 or more characters printed beside each card; an ambiguous",
335
- "prefix is refused with the candidates rather than acting on the first match.",
336
- "--json writes one object to stdout; operational lines stay on stderr.",
337
- "Run `cockpit login` first if this machine is not paired.",
338
- ],
339
- ],
340
- [
341
- "ops",
342
- [
343
- "Usage: cockpit ops [status [--job <id>] [--skips] | recompile --person <email|name|id> [--dry-run]] [--json]",
344
- "",
345
- " cockpit ops status",
346
- " One line per scheduled job: when it last produced something, and whether that",
347
- " is late FOR THAT JOB. A once-daily job quiet for 18 hours reads ok; a",
348
- " quarter-hourly one does not. Every line names the schedule it is judged against.",
349
- " A job that is not ok also prints what its artifact does and does not prove —",
350
- " several only write a row when there is something new, so quiet can mean a quiet",
351
- " week rather than a broken cron.",
352
- " --job <id> asks about one; --skips adds the open Slack and external-sync skips,",
353
- " grouped by reason. Exits 1 if anything is stale, empty or unreadable.",
354
- " cockpit ops recompile --person <email|name|id> [--dry-run]",
355
- " Writes that person's page again, now. Your own page is always yours to",
356
- " recompile; somebody else's is for the people who read across the team.",
357
- " --dry-run resolves the person and says which page is being served, compiling",
358
- " nothing. A compile only ever ADDS a version — nothing is overwritten.",
359
- " It can take minutes, and if Tower's 800-second budget runs out first you are",
360
- " told exactly that: the compile may still have finished. Check with",
361
- " `cockpit brief --for <person>`.",
362
- " There is no --cadence: the compile path behind this writes a daily, and a flag",
363
- " that was accepted and dropped would be worse than no flag.",
364
- ],
365
- ],
366
- [
367
- "slack",
368
- [
369
- "Usage: cockpit slack [coverage [--workspace bli|blue_pearl] [--stale-only] | read [filters]] [--json]",
370
- "",
371
- " cockpit slack coverage",
372
- " What Cockpit's Slack bot can see: how many channels it is in, what each",
373
- " visibility state means, which cursors have not moved, and which ingest skips",
374
- " are open. No message text at all, so anybody signed in can run it.",
375
- " This is the command that tells an empty search apart from a coverage gap.",
376
- " --stale-only drops the per-state breakdown and shows just what is behind.",
377
- " cockpit slack read --person <p> | --channel <c> | --query <text>",
378
- " The messages themselves, through the same reader JARVIS uses. At least one",
379
- " filter is required. --since/--until are YYYY-MM-DD; --limit caps the rows.",
380
- " Reading Slack reads across the team, so this is for the people who do that",
381
- " job; being refused it never costs you `cockpit slack coverage`.",
382
- " An empty or partial answer prints Tower's own sentence about WHY, word for",
383
- " word — none of those reasons means nothing was said.",
384
- ],
385
- ],
386
- [
387
- "workbook",
388
- [
389
- "Usage: cockpit workbook [<project> [<doc>]] [--section <id>] [--markdown] [--width <n>] [--dashboard-url <url>] [--json]",
390
- "",
391
- "Reads the Tower workbook library: written project documents — workbooks, runbooks —",
392
- "each with an author and a date. With no arguments it lists every compartment and the",
393
- "command that opens each document.",
394
- "`cockpit workbook tower` lists one project's shelf; `cockpit workbook tower workbook`",
395
- "prints that document as wrapped plain text.",
396
- "--section <id> prints one section, named by the anchors listed with the document.",
397
- "--markdown prints the server's Markdown unchanged, for piping into anything else.",
398
- "--width <n> sets the wrap column; otherwise the terminal's width is used, held between 60 and 100.",
399
- "There is no pager: pipe it into one if you want paging.",
400
- "Sign-in is the only gate, exactly as on the website page.",
401
- "Run `cockpit login` first if this machine is not paired.",
402
- ],
403
- ],
404
- [
405
- "brief",
406
- [
407
- "Usage: cockpit brief [edit|rewrite|history] [--for <person>] [--date <YYYY-MM-DD>] [--delta [--against <YYYY-MM-DD>]] [--version <pageId>] [--tldr|--full] [--versions] [--claims] [--days <n>] [--reason \"<why>\"] [--wait|--no-wait] [--dashboard-url <url>] [--json]",
408
- "",
409
- "Prints the TODAY page — the same page the Tower website shows, rendered for a terminal.",
410
- "--for opens somebody else's page; the website's own rule decides whether you may, and it refuses in plain words when you may not.",
411
- "--tldr prints the ten-second version; the whole page is the default.",
412
- "--version <pageId> steps back to one exact earlier version; --versions lists them with their ids.",
413
- "--claims prints the [claimId] beside every line, which is what `cockpit correct --claim` takes.",
414
- "Reading it here counts as opening it, exactly as opening it in a browser does.",
415
- "",
416
- "--date <YYYY-MM-DD> reads the page that was live on one day — `today` and `yesterday` work too. Days are that person's own days, in their own timezone, so a page always sits on the date its own masthead says.",
417
- " A day with nothing compiled says so by name, and says which day is nearest. It never quietly hands you a different day's page.",
418
- "--delta says what changed between that day and the day before it, in the same words the website's version history uses. --against <day> compares against a day you name instead.",
419
- " Comparisons that skip versions are worked out fresh each time and say `(not kept)` — a day with several compiles is one of those.",
420
- "",
421
- "history — the days there are, newest first, each with its masthead line and how many times it compiled that day. --days <n> asks for more than the last 14.",
422
- " Pass any date it prints back to `--date`.",
423
- "",
424
- " cockpit brief status [--who <person>] [--render]",
425
- " Why your brief did or did not arrive this morning — one reason, from the same",
426
- " chain the Slack DM cron itself follows: not_due, not_monday, not_the_1st,",
427
- " no_page, page_stale, no_slack_identity, engagement_write_failed, delivered.",
428
- " It shows your delivery window, which cadence owns this morning, whether a",
429
- " Slack identity is on record, and the delivered/opened/clicked receipts.",
430
- " --render adds the TLDR the DM would have carried.",
431
- " --who <person> asks about somebody else and is for the people who read across",
432
- " the team. Exits 0 only on `delivered`.",
433
- "edit — opens the page in $VISUAL/$EDITOR as a document: one [claimId] line per sentence you may rewrite, everything else a # comment. Change the words after an id, save, close. Only the lines that really changed are sent, so an accidental save writes nothing.",
434
- " An id you delete or invent is refused by name BEFORE anything is sent: a missing line reads as no opinion at all, not as a sentence to remove, and Tower cannot tell those apart.",
435
- " --reason \"<why>\" rides on every row, exactly like a commit message. Optional; the before-and-after already teaches on its own.",
436
- " With no editor, or with something piped in, the edited document is read from stdin instead — `cockpit brief edit < page.md`.",
437
- " The edit IS the correction: each changed line is filed in the ledger before the page is republished, so a publish that falls over never costs you the edit.",
438
- "",
439
- "rewrite — asks Tower to compile the page again and waits for the new version. Writing a page takes minutes, so this queues the work and watches your page rather than holding one long request open.",
440
- " --no-wait returns as soon as the ask is on the record. Waiting is the default.",
441
- " Running out of patience is not a failure and says so: the work is still going, and `cockpit brief` will show it when it lands.",
442
- " Whose page you may ask about is the website's own rule — your own always, anybody's if you are an admin.",
443
- "The command uses the existing paired device identity. Run `cockpit login` first if this machine is not paired.",
444
- ],
445
- ],
446
- [
447
- "notes",
448
- [
449
- "Usage: cockpit notes [list|show <id>|shelf|shelves|upload <paths...>|paste|share <id>|unshare <id>|move <id>] [flags]",
450
- "",
451
- "The /meeting-notes surface, typed. Bare `cockpit notes` lists the library.",
452
- "list [--series <shelf>] [--kind <kind>] [--since <YYYY-MM-DD>] [--until <YYYY-MM-DD>] [--limit <n>] — every note you may open, grouped by shelf, newest meeting first. Never a word of a note.",
453
- "show <id> — one note with its text, its shelf, and why you are allowed to see it.",
454
- "shelf [--limit <n>] — the notes YOU have put in, and what became of each.",
455
- "shelves — the shelves in use, with how many notes are on each.",
456
- "upload <paths...> [--exclude \"<sentence>\"] — put one or more local files in. Explicit paths only; your shell does any globbing, and a file that does not exist is named rather than skipped.",
457
- "paste [--file <path>] [--name <n>] [--exclude \"<sentence>\"] — put text in. --file <path> is safest (any editor encoding is decoded). Stdin works too: `pbpaste | cockpit notes paste` on macOS; on Windows use PowerShell 7 (`Get-Clipboard | cockpit notes paste`) — PowerShell 5.1 turns non-ASCII into `?` on pipes.",
458
- "--exclude carries your own sentence about what to leave out, exactly as the browser's box does.",
459
- "share <id> [--yes] — let everyone signed in read the statements that are safe to share. Asks first in a terminal; --yes is required without one, and --json implies --yes.",
460
- "unshare <id> — take it back. Never asks: it only ever narrows who can read.",
461
- "move <id> (--to \"<shelf>\"|--clear-shelf) — put it on a different shelf, or take the shelf off.",
462
- "A large note is read by a model on the server and can take a couple of minutes; the terminal says so before it waits.",
463
- "--json writes one machine-readable object to stdout; every reason, receipt and progress line stays on stderr.",
464
- "Sharing and moving need a signed-in session the database can see. If this deployment cannot mint one, they are refused as needs_signed_in_session rather than done with no permission check — and reads say when they came back narrower than the browser's.",
465
- "The command uses the existing paired device identity. Run `cockpit login` first if this machine is not paired.",
466
- ],
467
- ],
468
- [
469
- "correct",
470
- [
471
- "Usage: cockpit correct --claim <claimId> --text \"<what is wrong>\" [--for <person>] [--version <pageId>] [--supersedes <id>] [--dashboard-url <url>] [--json]",
472
- "",
473
- "Tells Tower that one line on the page is wrong. Same door the website's correction form uses.",
474
- "Get the claim id from `cockpit brief --claims`; an id the page does not carry is refused before anything is written.",
475
- "--text may be omitted when the correction is piped in on stdin.",
476
- "The claim is checked against the live record FIRST. If the record disagrees, JARVIS says so and the correction is still filed with that outcome on it.",
477
- "--supersedes <correctionId> takes an earlier correction back; nothing is ever edited or deleted.",
478
- ],
479
- ],
480
- [
481
- "backfill",
482
- [
483
- "Usage: cockpit backfill (--since-days <n>|--all) [--source codex|claude] [--dry-run] [--max-files <n>] [--max-depth <n>] [--max-repos <n>] [--yes] [--workspace <path>] [--json]",
484
- "",
485
- "Backfills historical Codex and Claude Code session evidence using the saved collection roots from local config when --workspace is omitted.",
486
- "Omit --source to scan both sources; there is no --source all literal.",
487
- "The non-all window is capped at the collector session paired_at timestamp.",
488
- "--all requires a dry-run review and TTY confirmation; pass --yes for headless agent runs.",
489
- "Repo discovery scans 3 folder levels and up to 50 repos by default.",
490
- "Raise --max-depth or --max-repos when a partial result reports a discovery cap.",
491
- "--dry-run validates the paired collector and dashboard reachability, prints the same summary tables, and writes nothing.",
492
- ],
493
- ],
494
- [
495
- "status",
496
- [
497
- "Usage: cockpit status [--workspace <path>] [--json]",
498
- "",
499
- "Prints install, pairing, active work, upload, and retry state.",
500
- "`--repo <path>` remains supported as a backward-compatible alias.",
501
- ],
502
- ],
503
- [
504
- "sessions",
505
- [
506
- "Usage: cockpit sessions [--source codex|claude] [--since-days <n>|--all] [--workspace <path>] [--json]",
507
- "",
508
- "Read-only: re-runs Codex + Claude session attribution and prints each",
509
- "session's id, source, state, reason, scores, signals, and per-sidecar",
510
- "skip reasons. No upload, no cursor writes. Answers \"why is session X",
511
- "missing?\" locally — counts and labels only, never paths or content.",
512
- "--since-days uses the same paired_at cap as `cockpit backfill`; --all scans the full local history.",
513
- "`--repo <path>` remains supported as a backward-compatible alias.",
514
- ],
515
- ],
516
- [
517
- "serve",
518
- [
519
- "Usage: cockpit serve [--port <port>] [--workspace <path>]",
520
- "",
521
- "Starts the local collector HTTP status server.",
522
- "`--repo <path>` remains supported as a backward-compatible alias.",
523
- ],
524
- ],
525
- [
526
- "autostart",
527
- [
528
- "Usage: cockpit autostart [install|uninstall|status] [--workspace <path>] [--dashboard-url <url>] [--interval-seconds <n>] [--json]",
529
- "",
530
- "Installs background sync through launchd on Apple Silicon macOS or a",
531
- "per-user Task Scheduler task on native Windows.",
532
- "Runs `cockpit sync` every 15 min by default and survives reboots;",
533
- "the Windows task runs while the user is signed in.",
534
- "Action defaults to `install`. `--workspace` is the parent work folder to sync.",
535
- "`--repo <path>` remains supported as a backward-compatible alias.",
536
- "Omit --dashboard-url for production; pass it only for staging/custom dashboards.",
537
- "See docs/runbooks/cockpit-launchd-sync.md and",
538
- "docs/runbooks/cockpit-windows-task-scheduler-sync.md.",
539
- ],
540
- ],
541
- [
542
- "agent-rules",
543
- [
544
- "Usage: cockpit agent-rules [install|uninstall|status] [--host codex|claude|all] [--workspace <path>] [--json]",
545
- "",
546
- "Installs a managed Tower Ticket Binding block into ~/.codex/AGENTS.md",
547
- "and ~/.claude/CLAUDE.md by default. Pass --host to manage only one.",
548
- "The block is scoped to --workspace, or the current directory when omitted,",
549
- "so Codex and Claude only run Tower ticket binding inside that onboarded folder.",
550
- "Action defaults to `install`.",
551
- ],
552
- ],
553
- [
554
- "memory",
555
- [
556
- "Usage: cockpit memory [install|status] [--dashboard-url <url>] [--dry-run] [--json]",
557
- "",
558
- "Registers BLI Memory on this machine: the `bli-memory` MCP server plus the",
559
- "recall/save hooks, for Claude Code (~/.claude.json, ~/.claude/settings.json)",
560
- "and for Codex (~/.codex/config.toml, ~/.codex/skills/bli-memory/).",
561
- "Idempotent: it merges with what is already there, never duplicates its own",
562
- "entries, and reads the stored config back before reporting success.",
563
- "`do-everything` runs it, and the sync tick re-runs it at most once a day.",
564
- "Action defaults to `install`. See docs/runbooks/bli-memory-install.md.",
565
- ],
566
- ],
567
- [
568
- "clean",
569
- [
570
- "Usage: cockpit clean [--dry-run] [--all-committed] [--reconcile] [--dashboard-url <url>] [--json]",
571
- "",
572
- "Reclaims this machine's disk. Prints what it would delete and why, per bucket,",
573
- "then deletes: staged evidence Tower has already accepted goes after a 48-hour",
574
- "retry window, and anything the upload ledger has NOT vouched for is kept,",
575
- "counted and named — retention never deletes undelivered evidence.",
576
- "--dry-run stops after the printing. --all-committed drops the retry window,",
577
- "so every already-accepted copy goes now, and is the only thing that removes a",
578
- "manual-study-evidence-vault folder (and only when every file in it is accepted).",
579
- "--reconcile asks Tower's own upload ledger about every object this machine's",
580
- "local memory can no longer classify (\"unknown\") before the buckets print — a",
581
- "confirmed-committed pack prunes under the ordinary rule this same run; a pack",
582
- "the server has never seen becomes an ordinary, never-deleted \"uncommitted\"",
583
- "object instead of \"unknown\". A server failure never blocks the rest of clean",
584
- "and is printed on its own line. Combine with --dry-run to ask and print",
585
- "without deleting.",
586
- "The scheduled sync does the same prune once a day (reconciling one batch first",
587
- "when there is anything unknown); this is the door for a laptop that is full",
588
- "right now.",
589
- ],
590
- ],
591
- [
592
- "docs",
593
- [
594
- "Usage: cockpit docs [list|tree|read <id|slug>|create|update <id>] [flags]",
595
- "",
596
- "The Tower document library, typed. Bare `cockpit docs` lists every document you may read.",
597
- "list — every document you may see: id, visibility, slug, title.",
598
- "tree — the same documents nested under their parent, for a sidebar-shaped view.",
599
- "read <id|slug> — one document's title, slug, visibility, and its body.",
600
- "create --title \"<title>\" [--parent <id>] [--visibility org|private] [--file <path>|--body-stdin] — the body comes from --file, or stdin (`cat body.md | cockpit docs create --title \"...\"`); neither means an empty body, matching the browser's own default.",
601
- "update <id|slug> [--title \"<t>\"] [--visibility org|private] [--parent <id>|--clear-parent] [--file <path>|--body-stdin] — only the fields you pass change; a `--parent` change IS a move, there is no separate move verb.",
602
- "A document body is never accepted on the command line — --file (safest on Windows) or a pipe only, same discipline as `cockpit notes paste`.",
603
- "--json writes one machine-readable object to stdout; every reason and receipt line stays on stderr.",
604
- "A refusal keeps Tower's own reason label — needs_rls_client, document_not_found_or_unreadable, document_not_writable, slug_taken, circular_parent, and so on.",
605
- "The command uses the existing paired device identity. Run `cockpit login` first if this machine is not paired.",
606
- ],
607
- ],
608
- [
609
- "msg",
610
- [
611
- "Usage: cockpit msg [channels|read <channel>|send <channel>|thread <id> --channel <channel>] [flags]",
612
- "",
613
- "Channels and messages, typed. <channel> is a channel id, or its name with or without a leading #.",
614
- "channels — every channel you are a member of (or, as a super_admin, every channel).",
615
- "read <channel> [--limit <n>] [--thread <id>] — the channel's most recent top-level messages, oldest first; --thread <id> reads one thread's replies instead.",
616
- "send <channel> [--thread <id>] — posts a message. The content is never accepted on the command line: pipe it in, e.g. `echo \"hello\" | cockpit msg send general`.",
617
- "thread <id> --channel <channel> — one thread's replies by the parent message's id.",
618
- "--json writes one machine-readable object to stdout; every reason and receipt line stays on stderr.",
619
- "A refusal keeps Tower's own reason label — needs_rls_client, channel_not_found_or_unreadable, content_too_long, and so on.",
620
- "The command uses the existing paired device identity. Run `cockpit login` first if this machine is not paired.",
621
- ],
622
- ],
623
- [
624
- "issue",
625
- [
626
- "Usage: cockpit issue [list|show <id>|create|update <id>|move <id> <state>|comment <id>|history <id>] [flags]",
627
- "",
628
- "Tower's issue tracker. <id> is a BLI-#### identifier (BLI-3654) or an issue's uuid — both work everywhere an issue is named.",
629
- "list [--state <s>] [--assignee me|unassigned|<uuid>] [--project <name|id>] [--limit <n>] — the issues you may see, most recently updated first.",
630
- "show <id> — one issue: title, state, priority, assignee, description, and every comment on it.",
631
- "create --title \"<title>\" [--project <name|id>] [--priority 0-4] [--assignee me|<uuid>] [--parent <id>] [--file <path>] — the DESCRIPTION comes from --file or stdin (`cat plan.md | cockpit issue create --title \"...\"`); neither means no description.",
632
- "update <id> [--title \"<t>\"] [--priority 0-4] [--assignee me|<uuid>] [--project <name|id>] [--parent <id>] [--file <path>|--body-stdin] — only the fields you pass change. State does NOT move here; use move.",
633
- "move <id> <state> — moves an issue and records the move. States: backlog, todo, in_progress, in_review, done, canceled.",
634
- "comment <id> — posts a comment. The body is never taken on the command line: `echo \"shipped\" | cockpit issue comment BLI-3654`.",
635
- "history <id> [--limit <n>] — every recorded state move and reassignment, oldest first.",
636
- "A project may be named instead of id'd; the name is matched exactly (case-insensitively) against `cockpit project list`.",
637
- "--json writes one machine-readable object to stdout; every reason and receipt line stays on stderr.",
638
- "A refusal keeps Tower's own reason label — needs_rls_client, issue_not_found_or_unreadable, issue_not_writable, invalid_state, comment_too_long, and so on.",
639
- "The command uses the existing paired device identity. Run `cockpit login` first if this machine is not paired.",
640
- ],
641
- ],
642
- [
643
- "project",
644
- [
645
- "Usage: cockpit project [list] [--archived] [--json]",
646
- "",
647
- "The projects issues are filed under. Bare `cockpit project` lists them.",
648
- "list [--archived] — id, active/archived, name. --archived includes archived projects.",
649
- "There is no create/update/delete verb: `GET /api/work/projects` is the whole of Tower's project door today.",
650
- "--json writes one machine-readable object to stdout; every reason and receipt line stays on stderr.",
651
- ],
652
- ],
653
- [
654
- "release",
655
- [
656
- "Usage: cockpit release [--dry-run] [--skip-checks] [--no-floor] [--tag <tag>] [--access <public|restricted>] [--otp <code>]",
657
- "",
658
- "Maintainer-only helper. Run from inside the bli-cockpit repo checkout.",
659
- "Requires a clean `main` branch and runs `git pull --ff-only` before publishing.",
660
- "Delegates to `npm run publish:public -- ...` so public packages are",
661
- "built, checked, and published in the safe telemetry-core then CLI order.",
662
- "A successful --tag latest publish raises the fleet forced-update floor so",
663
- "every collector pulls the new CLI on its next sync tick; --no-floor skips",
664
- "that for a staged rollout, and --tag next never touches it.",
665
- ],
666
- ],
667
- ]);
668
- return (helpByCommand.get(command) ?? [localCommandHelp()]).join("\n");
669
- }
670
99
  /** `cockpit <command> --help` — the command name alone, with only the help flag. */
671
100
  export function isLocalHelpRequest(argv) {
672
101
  const command = argv[0];