@devdogsuga/devtools 0.1.26 → 0.1.28

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 (271) hide show
  1. package/README.md +72 -11
  2. package/bin/devtools.mjs +4 -4
  3. package/dist/cli-CZKkNHK5.js +6619 -0
  4. package/dist/failure-log-BBPGejQv.js +256 -0
  5. package/dist/launch.js +267 -281
  6. package/dist/local-env-D8HC2JK_.js +351 -0
  7. package/dist/local-env-DrBCXqD8.js +2 -0
  8. package/dist/menu-CQBsQRgH.js +1684 -0
  9. package/dist/peer-redirect-hooks.js +15 -0
  10. package/dist/peers-RBplwovA.js +229 -0
  11. package/dist/root-C2OmZDwD.js +94 -0
  12. package/dist/stack-Bx-55oi_.js +2 -0
  13. package/dist/stack-BynCgRVS.js +181 -0
  14. package/dist/supabase-project-0knM0mqd.js +426 -0
  15. package/dist/telemetry-C4foD8MD.js +249 -0
  16. package/dist/telemetry.js +2 -148
  17. package/package.json +10 -29
  18. package/bin/devtools-ci-bare.mjs +0 -48
  19. package/bin/devtools-ci.mjs +0 -31
  20. package/dist/args.d.ts +0 -26
  21. package/dist/args.js +0 -78
  22. package/dist/bws/bw.d.ts +0 -18
  23. package/dist/bws/bw.js +0 -70
  24. package/dist/bws/client.d.ts +0 -43
  25. package/dist/bws/client.js +0 -363
  26. package/dist/bws/environments.d.ts +0 -96
  27. package/dist/bws/environments.js +0 -111
  28. package/dist/bws/pace.d.ts +0 -25
  29. package/dist/bws/pace.js +0 -43
  30. package/dist/bws/retry.d.ts +0 -12
  31. package/dist/bws/retry.js +0 -39
  32. package/dist/bws/store.d.ts +0 -1
  33. package/dist/bws/store.js +0 -32
  34. package/dist/bws/token.d.ts +0 -31
  35. package/dist/bws/token.js +0 -88
  36. package/dist/bws/vault.d.ts +0 -29
  37. package/dist/bws/vault.js +0 -200
  38. package/dist/catalog.d.ts +0 -57
  39. package/dist/catalog.js +0 -57
  40. package/dist/cf/build.d.ts +0 -13
  41. package/dist/cf/build.js +0 -65
  42. package/dist/cf/commands.d.ts +0 -1
  43. package/dist/cf/commands.js +0 -172
  44. package/dist/cf/local-env.d.ts +0 -58
  45. package/dist/cf/local-env.js +0 -177
  46. package/dist/ci.d.ts +0 -1
  47. package/dist/ci.js +0 -328
  48. package/dist/cli.d.ts +0 -11
  49. package/dist/cli.js +0 -1094
  50. package/dist/commands.d.ts +0 -261
  51. package/dist/commands.js +0 -1318
  52. package/dist/completions.d.ts +0 -4
  53. package/dist/completions.js +0 -110
  54. package/dist/cron/commands.d.ts +0 -47
  55. package/dist/cron/commands.js +0 -372
  56. package/dist/cron/discovery.d.ts +0 -55
  57. package/dist/cron/discovery.js +0 -85
  58. package/dist/cron/schema.d.ts +0 -45
  59. package/dist/cron/schema.js +0 -35
  60. package/dist/db/config-push.d.ts +0 -8
  61. package/dist/db/config-push.js +0 -11
  62. package/dist/db/connection.d.ts +0 -79
  63. package/dist/db/connection.js +0 -114
  64. package/dist/db/exec.d.ts +0 -1
  65. package/dist/db/exec.js +0 -11
  66. package/dist/db/generate-types.d.ts +0 -1
  67. package/dist/db/generate-types.js +0 -4
  68. package/dist/db/generated-env.d.ts +0 -69
  69. package/dist/db/generated-env.js +0 -113
  70. package/dist/db/introspect.d.ts +0 -1
  71. package/dist/db/introspect.js +0 -122
  72. package/dist/db/local-env.d.ts +0 -65
  73. package/dist/db/local-env.js +0 -23
  74. package/dist/db/new-migration.d.ts +0 -8
  75. package/dist/db/new-migration.js +0 -69
  76. package/dist/db/pooler.d.ts +0 -23
  77. package/dist/db/pooler.js +0 -55
  78. package/dist/db/run.d.ts +0 -68
  79. package/dist/db/run.js +0 -156
  80. package/dist/db/seed-buckets.d.ts +0 -2
  81. package/dist/db/seed-buckets.js +0 -4
  82. package/dist/db/seed-production.d.ts +0 -8
  83. package/dist/db/seed-production.js +0 -79
  84. package/dist/db/seed-roles.d.ts +0 -7
  85. package/dist/db/seed-roles.js +0 -33
  86. package/dist/db/session-refresh.d.ts +0 -43
  87. package/dist/db/session-refresh.js +0 -40
  88. package/dist/deploy/migrations.d.ts +0 -20
  89. package/dist/deploy/migrations.js +0 -68
  90. package/dist/deploy/orphans.d.ts +0 -20
  91. package/dist/deploy/orphans.js +0 -177
  92. package/dist/deploy/preflight.d.ts +0 -53
  93. package/dist/deploy/preflight.js +0 -189
  94. package/dist/deploy/report.d.ts +0 -26
  95. package/dist/deploy/report.js +0 -70
  96. package/dist/deploy/require-planner.d.ts +0 -2
  97. package/dist/deploy/require-planner.js +0 -60
  98. package/dist/deploy/require-token.d.ts +0 -15
  99. package/dist/deploy/require-token.js +0 -78
  100. package/dist/deploy/secrets-file.d.ts +0 -17
  101. package/dist/deploy/secrets-file.js +0 -166
  102. package/dist/deploy/write-env.d.ts +0 -40
  103. package/dist/deploy/write-env.js +0 -395
  104. package/dist/docs/index-pages.d.ts +0 -38
  105. package/dist/docs/index-pages.js +0 -186
  106. package/dist/emails/commands.d.ts +0 -15
  107. package/dist/emails/commands.js +0 -130
  108. package/dist/emails/email-types.d.ts +0 -13
  109. package/dist/emails/email-types.js +0 -1
  110. package/dist/emails/fixtures.d.ts +0 -20
  111. package/dist/emails/fixtures.js +0 -20
  112. package/dist/env/audit.d.ts +0 -206
  113. package/dist/env/audit.js +0 -436
  114. package/dist/env/cloudflare.d.ts +0 -21
  115. package/dist/env/cloudflare.js +0 -61
  116. package/dist/env/commands.d.ts +0 -99
  117. package/dist/env/commands.js +0 -613
  118. package/dist/env/discovery.d.ts +0 -15
  119. package/dist/env/discovery.js +0 -239
  120. package/dist/env/document.d.ts +0 -119
  121. package/dist/env/document.js +0 -323
  122. package/dist/env/example.d.ts +0 -68
  123. package/dist/env/example.js +0 -605
  124. package/dist/env/selection.d.ts +0 -105
  125. package/dist/env/selection.js +0 -228
  126. package/dist/env-entry.d.ts +0 -49
  127. package/dist/env-entry.js +0 -204
  128. package/dist/environment-doctor.d.ts +0 -45
  129. package/dist/environment-doctor.js +0 -429
  130. package/dist/environment.d.ts +0 -92
  131. package/dist/environment.js +0 -185
  132. package/dist/fingerprint.d.ts +0 -10
  133. package/dist/fingerprint.js +0 -18
  134. package/dist/gen/campus-map.d.ts +0 -1
  135. package/dist/gen/campus-map.js +0 -473
  136. package/dist/gen/commands.d.ts +0 -1
  137. package/dist/gen/commands.js +0 -25
  138. package/dist/gen/hypno.d.ts +0 -1
  139. package/dist/gen/hypno.js +0 -134
  140. package/dist/gh/client.d.ts +0 -80
  141. package/dist/gh/client.js +0 -259
  142. package/dist/gh/environments.d.ts +0 -83
  143. package/dist/gh/environments.js +0 -178
  144. package/dist/gh/rulesets/actors.d.ts +0 -14
  145. package/dist/gh/rulesets/actors.js +0 -69
  146. package/dist/gh/rulesets/api.d.ts +0 -15
  147. package/dist/gh/rulesets/api.js +0 -104
  148. package/dist/gh/rulesets/commands.d.ts +0 -3
  149. package/dist/gh/rulesets/commands.js +0 -203
  150. package/dist/gh/rulesets/desired.d.ts +0 -112
  151. package/dist/gh/rulesets/desired.js +0 -271
  152. package/dist/gh/rulesets/diff.d.ts +0 -65
  153. package/dist/gh/rulesets/diff.js +0 -170
  154. package/dist/gh/rulesets/types.d.ts +0 -76
  155. package/dist/gh/rulesets/types.js +0 -13
  156. package/dist/gh/settings/api.d.ts +0 -40
  157. package/dist/gh/settings/api.js +0 -180
  158. package/dist/gh/settings/commands.d.ts +0 -1
  159. package/dist/gh/settings/commands.js +0 -254
  160. package/dist/gh/settings/desired.d.ts +0 -53
  161. package/dist/gh/settings/desired.js +0 -43
  162. package/dist/gh/settings/diff.d.ts +0 -57
  163. package/dist/gh/settings/diff.js +0 -161
  164. package/dist/gh/settings/types.d.ts +0 -92
  165. package/dist/gh/settings/types.js +0 -11
  166. package/dist/gh/settings/workflows.d.ts +0 -41
  167. package/dist/gh/settings/workflows.js +0 -128
  168. package/dist/grantRoot.d.ts +0 -42
  169. package/dist/grantRoot.js +0 -89
  170. package/dist/help.d.ts +0 -18
  171. package/dist/help.js +0 -169
  172. package/dist/images/commands.d.ts +0 -32
  173. package/dist/images/commands.js +0 -231
  174. package/dist/images/events.d.ts +0 -47
  175. package/dist/images/events.js +0 -116
  176. package/dist/images/graphics.d.ts +0 -70
  177. package/dist/images/graphics.js +0 -234
  178. package/dist/images/open-graph-types.d.ts +0 -75
  179. package/dist/images/open-graph-types.js +0 -1
  180. package/dist/images/prompts.d.ts +0 -34
  181. package/dist/images/prompts.js +0 -100
  182. package/dist/images/render.d.ts +0 -21
  183. package/dist/images/render.js +0 -61
  184. package/dist/images/select.d.ts +0 -64
  185. package/dist/images/select.js +0 -96
  186. package/dist/instance.d.ts +0 -69
  187. package/dist/instance.js +0 -102
  188. package/dist/invocation.d.ts +0 -49
  189. package/dist/invocation.js +0 -78
  190. package/dist/launch-ci.d.ts +0 -1
  191. package/dist/launch-ci.js +0 -90
  192. package/dist/launch.d.ts +0 -18
  193. package/dist/menu.d.ts +0 -43
  194. package/dist/menu.js +0 -300
  195. package/dist/moderation.d.ts +0 -70
  196. package/dist/moderation.js +0 -128
  197. package/dist/oauth/browser.d.ts +0 -1
  198. package/dist/oauth/browser.js +0 -13
  199. package/dist/oauth/config.d.ts +0 -24
  200. package/dist/oauth/config.js +0 -24
  201. package/dist/oauth/connect-url.d.ts +0 -17
  202. package/dist/oauth/connect-url.js +0 -23
  203. package/dist/oauth/db.d.ts +0 -37
  204. package/dist/oauth/db.js +0 -108
  205. package/dist/oauth/device.d.ts +0 -93
  206. package/dist/oauth/device.js +0 -198
  207. package/dist/oauth/discovery.d.ts +0 -28
  208. package/dist/oauth/discovery.js +0 -54
  209. package/dist/oauth/env-file.d.ts +0 -6
  210. package/dist/oauth/env-file.js +0 -35
  211. package/dist/oauth/exchange.d.ts +0 -43
  212. package/dist/oauth/exchange.js +0 -93
  213. package/dist/oauth/label.d.ts +0 -1
  214. package/dist/oauth/label.js +0 -25
  215. package/dist/oauth/loopback.d.ts +0 -52
  216. package/dist/oauth/loopback.js +0 -168
  217. package/dist/oauth/pkce.d.ts +0 -6
  218. package/dist/oauth/pkce.js +0 -34
  219. package/dist/oauth/target.d.ts +0 -54
  220. package/dist/oauth/target.js +0 -102
  221. package/dist/oauth/transport.d.ts +0 -41
  222. package/dist/oauth/transport.js +0 -32
  223. package/dist/oauth/wizard.d.ts +0 -40
  224. package/dist/oauth/wizard.js +0 -578
  225. package/dist/persona.d.ts +0 -18
  226. package/dist/persona.js +0 -221
  227. package/dist/pick.d.ts +0 -6
  228. package/dist/pick.js +0 -64
  229. package/dist/pipes.d.ts +0 -1
  230. package/dist/pipes.js +0 -25
  231. package/dist/planner/checks.d.ts +0 -26
  232. package/dist/planner/checks.js +0 -104
  233. package/dist/planner/commands.d.ts +0 -10
  234. package/dist/planner/commands.js +0 -367
  235. package/dist/planner/db.d.ts +0 -15
  236. package/dist/planner/db.js +0 -33
  237. package/dist/planner/role.d.ts +0 -33
  238. package/dist/planner/role.js +0 -68
  239. package/dist/repo/peer-redirect-hooks.d.ts +0 -16
  240. package/dist/repo/peer-redirect-hooks.js +0 -10
  241. package/dist/repo/peer-redirect.d.ts +0 -4
  242. package/dist/repo/peer-redirect.js +0 -26
  243. package/dist/repo/peers.d.ts +0 -39
  244. package/dist/repo/peers.js +0 -139
  245. package/dist/repo/resolve.d.ts +0 -41
  246. package/dist/repo/resolve.js +0 -152
  247. package/dist/repo/root.d.ts +0 -23
  248. package/dist/repo/root.js +0 -91
  249. package/dist/repo/source.d.ts +0 -14
  250. package/dist/repo/source.js +0 -54
  251. package/dist/repo/supabase-project.d.ts +0 -60
  252. package/dist/repo/supabase-project.js +0 -124
  253. package/dist/repo/tsx-loader.d.ts +0 -6
  254. package/dist/repo/tsx-loader.js +0 -41
  255. package/dist/run/pick.d.ts +0 -90
  256. package/dist/run/pick.js +0 -664
  257. package/dist/setup.d.ts +0 -1
  258. package/dist/setup.js +0 -280
  259. package/dist/stack.d.ts +0 -48
  260. package/dist/stack.js +0 -403
  261. package/dist/telemetry.d.ts +0 -59
  262. package/dist/tier.d.ts +0 -40
  263. package/dist/tier.js +0 -96
  264. package/dist/ui.d.ts +0 -40
  265. package/dist/ui.js +0 -71
  266. package/dist/version.d.ts +0 -7
  267. package/dist/version.js +0 -17
  268. package/dist/workers.d.ts +0 -5
  269. package/dist/workers.js +0 -33
  270. package/dist/workflows/commands.d.ts +0 -54
  271. package/dist/workflows/commands.js +0 -763
package/dist/commands.js DELETED
@@ -1,1318 +0,0 @@
1
- /**
2
- * The command tree, as data.
3
- *
4
- * One declaration, three readers:
5
- *
6
- * * `help.ts` renders it, one level at a time.
7
- * * `menu.ts` walks its interactive commands and options without a second
8
- * list to keep in step.
9
- * * contributor documentation uses the same group and command vocabulary.
10
- *
11
- * It is deliberately inert: names, summaries and option shapes, no imports of
12
- * anything that runs. `cli.ts` owns dispatch; the wizard turns a walk of this
13
- * tree into an argv and hands it to that same dispatcher, which keeps menu and
14
- * CLI behavior aligned by construction rather than by review.
15
- *
16
- * One exception: `WORKER_APP_CHOICES` imports the shared `WORKER_APPS` list
17
- * (root `workers.json`) so `cf preview`/`typegen`/`build`'s `--app` choices
18
- * cannot drift from the six-site source of truth. It is data — a string
19
- * array read from a JSON file — not a function this file calls.
20
- *
21
- * ## What a summary is for
22
- *
23
- * Every `summary` is one line and is the ONLY thing `--help` prints for a
24
- * command at the level above it. Rationale, target tables, credential lookup
25
- * order and deploy internals live in `docs/`, not here: `--help` is a map, and
26
- * a map that reprints the territory is the thing this replaced.
27
- */
28
- import { workerApps } from "./workers.js";
29
- /**
30
- * The `--app <slug>` choices shared by `cf preview`/`typegen`/`build`.
31
- *
32
- * `workerApps()` reads `workers.json` via `findRepoRoot()`, which throws
33
- * outside a DevDogsUGA checkout — and this module (via `help.ts`/`menu.ts`)
34
- * is imported for `devtools --help`, which must work from anywhere,
35
- * `/tmp` included (see `workers.ts`'s header). The tree below is built once
36
- * at import time regardless, so this falls back to an empty choice list
37
- * rather than propagating the throw: `--help` still renders the command
38
- * shape with no `--app` values listed, and any actual `cf preview --app …`
39
- * invocation outside a repo fails with the same clear `RepoNotFoundError`
40
- * it always would have, just a little further downstream.
41
- */
42
- const WORKER_APP_CHOICES = (() => {
43
- try {
44
- return workerApps().map((app) => ({ value: app, label: app }));
45
- }
46
- catch {
47
- return [];
48
- }
49
- })();
50
- /**
51
- * How each scope reads, in the two places that draw it.
52
- *
53
- * `menu` sits inline in a hint, so it is one word. `help` heads a block of
54
- * commands, so it can be a phrase. Both live here rather than in the renderers
55
- * because they are labels, the same kind of data as a group title.
56
- */
57
- export const SCOPES = {
58
- machine: { menu: "This machine", help: "Supabase on this machine" },
59
- repo: { menu: "Repo", help: "files in the repo" },
60
- endpoint: { menu: "Database", help: "the session's database (--tier)" },
61
- infra: {
62
- menu: "Hosted",
63
- help: "hosted infrastructure, each naming its own connection",
64
- },
65
- };
66
- // ── Exit codes ───────────────────────────────────────────────────────────────
67
- /** Standard success. */
68
- export const EXIT_OK = 0;
69
- /** Command failed. */
70
- export const EXIT_FAIL = 1;
71
- /** `--check` found drift. Distinct from failure so scripts can tell them apart. */
72
- export const EXIT_DRIFT = 2;
73
- // ── Shared option shapes ─────────────────────────────────────────────────────
74
- /**
75
- * `docs index`'s delete acknowledgment — NOT a database selector.
76
- *
77
- * The db namespace's old `--target local|remote` flag is retired outright
78
- * (the SESSION names the database now — see `db/connection.ts`), but `docs
79
- * index` keeps this spelling for a different job: its prune deletes rows,
80
- * and running that against a non-local `DB_URL` requires saying so out
81
- * loud. The database itself still comes from the session's env.
82
- */
83
- export const DOCS_TARGET = {
84
- flag: "--target",
85
- value: "<local|remote>",
86
- summary: "Acknowledge indexing a non-local DB_URL. Defaults to local-only.",
87
- prompt: {
88
- kind: "select",
89
- message: "May this prune a non-local database?",
90
- choices: [
91
- { value: "local", label: "Local only", hint: "the default" },
92
- { value: "remote", label: "Yes — the session's remote DB_URL" },
93
- ],
94
- },
95
- };
96
- /**
97
- * The deployment-tier selector, for the commands that resolve a tier of
98
- * their OWN (cron/workflows run against a chosen tier's env). The session's
99
- * global `--tier` — `development:local|development:remote|staging|production`,
100
- * stripped by the launcher before dispatch — is a superset of this
101
- * vocabulary; this per-command flag still reads plain tiers.
102
- */
103
- export const TIER = {
104
- flag: "--tier",
105
- value: "<t>",
106
- summary: "Which deployment tier. Asked for when absent.",
107
- };
108
- /** Print what would change; write nothing; exit 0. On every command that writes. */
109
- export const DRY_RUN = {
110
- flag: "--dry-run",
111
- summary: "Print what would change, write nothing, exit 0.",
112
- };
113
- /** Verify correctness; write nothing; exit 2 on drift. */
114
- export const CHECK = {
115
- flag: "--check",
116
- summary: "Verify, write nothing, exit 2 on drift.",
117
- };
118
- /**
119
- * Machine-readable output to stdout.
120
- *
121
- * Promptless by design: the wizard is already interactive; a flag that switches
122
- * its output format has no meaning there. `commands.test.ts` pins the promptless
123
- * set with a "scripting-only" category for exactly this kind of flag.
124
- */
125
- export const JSON_FLAG = {
126
- flag: "--json",
127
- summary: "Print machine-readable JSON to stdout.",
128
- };
129
- const VAULT_TARGET = {
130
- flag: "--target",
131
- value: "<t>",
132
- summary: "preflight, staging or production. Asked for when absent.",
133
- // No prompt: `pick.ts` already owns this question, and it orders the list
134
- // least- to most-dangerous so a reflexive Enter cannot select production.
135
- // Duplicating it here would put production one keystroke closer.
136
- };
137
- /**
138
- * The three flags every `run` task takes.
139
- *
140
- * None carries a `prompt`, and that is the point rather than an omission.
141
- * `run` opens a multiselect of the apps defining the task, so a wizard that
142
- * asked "every package?", "which filter?" and "which tier?" up front would
143
- * ask the same question three times and let the answers disagree. `--tier`
144
- * is promptless for a different reason than `--filter`/`--all`, though: it is
145
- * not a question `run` asks itself elsewhere (unlike `cron run`'s own
146
- * `--tier`, which opens a live picker) — it is a scripting input with no
147
- * wizard equivalent, the same category `--json` sits in. `--help` still
148
- * documents all three, which is where someone scripting this will look.
149
- *
150
- * Same reasoning as `VAULT_TARGET` above: the command owns the question, so the
151
- * tree declares the flag and stays quiet.
152
- */
153
- const RUN_OPTIONS = [
154
- {
155
- flag: "--filter",
156
- value: "<pkg>",
157
- summary: "Limit to a package. pnpm's own flag; skips the question.",
158
- },
159
- {
160
- flag: "--all",
161
- summary: "Every package, unfiltered, with nothing asked.",
162
- },
163
- {
164
- flag: "--tier",
165
- value: "<t>",
166
- summary: "Load a tier's env into the run. CLI-only; never prompted.",
167
- },
168
- ];
169
- const ENV_FILE = {
170
- flag: "--file",
171
- value: "<path>",
172
- summary: "Read and write this file instead of the target's own.",
173
- };
174
- /** Skip every interactive confirmation prompt. On every destructive command. */
175
- export const YES = {
176
- flag: "--yes",
177
- summary: "Skip the confirmations.",
178
- };
179
- const ACCESS_TOKEN = {
180
- flag: "--access-token",
181
- value: "<token>",
182
- summary: "Bitwarden Secrets Manager token. Prefer the vault or the env var.",
183
- };
184
- const DB_URL = {
185
- flag: "--db-url",
186
- value: "<url>",
187
- summary: "Privileged connection. Defaults to .env.production's DB_URL.",
188
- prompt: {
189
- kind: "text",
190
- message: "Connection URL? (blank uses .env.production's DB_URL)",
191
- optional: true,
192
- },
193
- };
194
- // ── The tree ─────────────────────────────────────────────────────────────────
195
- const DECLARED_GROUPS = [
196
- {
197
- title: "Start here",
198
- commands: [
199
- {
200
- name: "setup",
201
- summary: "Check prerequisites and seed .env.",
202
- hint: "run this first",
203
- },
204
- {
205
- name: "completions",
206
- summary: "Output a shell completion script for devtools.",
207
- hint: "pipe to source or write to a file",
208
- surface: "cli-only",
209
- options: [
210
- {
211
- flag: "--shell",
212
- value: "<bash|zsh>",
213
- summary: "Target shell. Asked for when absent.",
214
- prompt: {
215
- kind: "select",
216
- message: "Which shell?",
217
- choices: [{ value: "bash" }, { value: "zsh" }],
218
- },
219
- },
220
- ],
221
- },
222
- ],
223
- },
224
- {
225
- title: "Workspace",
226
- commands: [
227
- {
228
- name: "run",
229
- summary: "Run a pnpm workspace task, asking which apps first.",
230
- hint: "build, dev, lint…",
231
- // The six with a root alias, which are the six a contributor types.
232
- // NOT an exhaustive list of every package script: `run` forwards
233
- // whatever name it is given, so `test:coverage` works without being
234
- // listed here, and this CLI's own unrelated `deploy` command group
235
- // stays out of a menu where a same-named package script would sit
236
- // one line away from it.
237
- subcommands: [
238
- {
239
- name: "build",
240
- summary: "Compile every package an app needs.",
241
- options: RUN_OPTIONS,
242
- },
243
- {
244
- name: "dev",
245
- summary: "Start the development servers.",
246
- options: RUN_OPTIONS,
247
- },
248
- {
249
- name: "typecheck",
250
- summary: "Run tsc across the workspace.",
251
- options: RUN_OPTIONS,
252
- },
253
- {
254
- name: "lint",
255
- summary: "Run ESLint across the workspace.",
256
- options: RUN_OPTIONS,
257
- },
258
- {
259
- name: "lint:fix",
260
- summary: "Run ESLint and write what it can fix.",
261
- options: RUN_OPTIONS,
262
- },
263
- {
264
- name: "test",
265
- summary: "Run the unit tests.",
266
- options: RUN_OPTIONS,
267
- },
268
- ],
269
- },
270
- ],
271
- },
272
- {
273
- title: "Brand",
274
- commands: [
275
- {
276
- name: "emails",
277
- summary: "Render populated transactional email previews.",
278
- hint: "HTML or plain text, one template or all",
279
- // Like `images`, this command owns its dependent questions: formats
280
- // and output only make sense after the templates have been selected.
281
- options: [
282
- {
283
- flag: "--format",
284
- value: "<html,text>",
285
- summary: "Outputs to write. Defaults to html.",
286
- },
287
- {
288
- flag: "--out",
289
- value: "<dir>",
290
- summary: "Output directory. Defaults to ./email-previews.",
291
- },
292
- {
293
- flag: "--dry-run",
294
- summary: "List subjects and destination files without writing.",
295
- },
296
- ],
297
- },
298
- {
299
- name: "images",
300
- summary: "Render a club image at one or more sizes.",
301
- hint: "brand/*, page/*, app/*, event/*, or * for all",
302
- // No subcommands: graphics are positional, and several can be named at
303
- // once. The command owns its interactive path because it can ask in CLI
304
- // order — graphic, format, output — and derive each question from the
305
- // previous answer. The outer wizard therefore dispatches bare `images`;
306
- // these options remain here for help and scripted invocations.
307
- options: [
308
- {
309
- flag: "--format",
310
- value: "<a,b,…>",
311
- summary: "Sizes to render: og, gdgc-wide, gdgc-square, savvycal, email-*, icon-*.",
312
- // Not prompted here: the command asks itself, from the formats the
313
- // chosen graphics actually support, which a static list cannot know.
314
- },
315
- {
316
- flag: "--all-formats",
317
- summary: "Every size the named graphics support.",
318
- },
319
- {
320
- flag: "--out",
321
- value: "<dir>",
322
- summary: "Write everything into one directory, flat.",
323
- },
324
- {
325
- flag: "--default-out",
326
- summary: "Write each image where it belongs in the repo.",
327
- },
328
- {
329
- flag: "--dry-run",
330
- summary: "List what would be written, and what each size is for.",
331
- },
332
- ],
333
- },
334
- ],
335
- },
336
- {
337
- title: "Moderation",
338
- commands: [
339
- {
340
- name: "moderation",
341
- summary: "An app's moderation integration: the catalog and its wiring.",
342
- hint: "check",
343
- subcommands: [
344
- {
345
- name: "check",
346
- summary: "List the catalog, or check one app's moderation integration.",
347
- hint: "reasons and content types with no --app; per-app wiring with one",
348
- // No `needs: "instance-running"` any more — this resolves through
349
- // the session system now (`resolveInstance`, `instance.ts`), the
350
- // same as `db migrate`/`db reset`, so it works against a hosted
351
- // development database with no local stack at all. Refuses
352
- // staging/production itself, with its own message, rather than a
353
- // menu-only hint.
354
- options: [
355
- {
356
- flag: "--app",
357
- value: "<slug>",
358
- summary: "App to check. Prints the catalog when absent.",
359
- },
360
- JSON_FLAG,
361
- ],
362
- },
363
- ],
364
- },
365
- {
366
- name: "grant-root",
367
- summary: "Give an account every permission on your own database.",
368
- // Also on the session system now — every tier, not just local. See
369
- // `check`'s note above; production additionally gets `YES`'s stern
370
- // confirmation, the same treatment `db reset` gives it.
371
- options: [
372
- {
373
- flag: "--user",
374
- value: "<email>",
375
- summary: "Account to grant Root to. Asked for when absent.",
376
- },
377
- YES,
378
- ],
379
- },
380
- ],
381
- },
382
- {
383
- title: "Personas",
384
- commands: [
385
- {
386
- name: "persona",
387
- summary: "A throwaway member or moderator account for development.",
388
- hint: "member, moderator, or --clean",
389
- options: [
390
- {
391
- flag: "--clean",
392
- summary: "Delete every account this command has created, instead of making one.",
393
- prompt: {
394
- kind: "confirm",
395
- message: "Clean up every persona this command created, instead of making one?",
396
- initial: false,
397
- },
398
- },
399
- ],
400
- },
401
- ],
402
- },
403
- {
404
- title: "Environment",
405
- commands: [
406
- {
407
- name: "doctor",
408
- summary: "Check this machine's environment against what the repo needs.",
409
- hint: "node, pnpm, Docker, .env, hosted Supabase, OAuth — read-only",
410
- options: [
411
- {
412
- flag: "--app",
413
- value: "<slug>",
414
- summary: "Scope checks to one app. Defaults to every app you have env for.",
415
- },
416
- {
417
- flag: "--report",
418
- summary: "Print a redacted, paste-able block (versions, OS, results — no secrets).",
419
- },
420
- ],
421
- },
422
- ],
423
- },
424
- {
425
- title: "Generate",
426
- commands: [
427
- {
428
- name: "gen",
429
- summary: "Regenerate committed, generated source.",
430
- hint: "refresh a tracked artifact",
431
- subcommands: [
432
- {
433
- name: "campus-map",
434
- summary: "Rebuild the FindUs campus map from OpenStreetMap.",
435
- hint: "occasional — rerun when OSM improves the area",
436
- },
437
- {
438
- name: "hypno",
439
- summary: "Bake the hero spiral into a pre-blurred raster.",
440
- hint: "needs Playwright",
441
- },
442
- {
443
- name: "og-assets",
444
- summary: "Re-embed OG fonts, icons and brand art.",
445
- hint: "after a brand, font or Phosphor bump",
446
- },
447
- {
448
- name: "email-templates",
449
- summary: "Recompile the transactional email chunks.",
450
- hint: "render → tokenize → emit",
451
- },
452
- ],
453
- },
454
- ],
455
- },
456
- {
457
- title: "Cron",
458
- commands: [
459
- {
460
- name: "cron",
461
- summary: "Cloudflare cron triggers: list schedules or fire one now.",
462
- hint: "list schedules, run a job",
463
- subcommands: [
464
- {
465
- name: "list",
466
- summary: "Every registered cron: schedule, English description, routes.",
467
- hint: "the audit view",
468
- options: [
469
- {
470
- flag: "--app",
471
- value: "<slug>",
472
- summary: "Limit to one app. All apps if omitted.",
473
- },
474
- {
475
- flag: "--tier",
476
- value: "<t>",
477
- summary: "Whose wrangler schedules to read. All tiers if omitted.",
478
- prompt: {
479
- kind: "select",
480
- message: "Which tier's wrangler schedules?",
481
- choices: [
482
- { value: "development", hint: "the default" },
483
- { value: "staging" },
484
- { value: "production" },
485
- ],
486
- },
487
- },
488
- JSON_FLAG,
489
- ],
490
- },
491
- {
492
- name: "run",
493
- summary: "Choose and fire a configured cron schedule now.",
494
- hint: "pick a job from the Worker configuration",
495
- options: [
496
- {
497
- flag: "--app",
498
- value: "<slug>",
499
- summary: "Limit the discovered jobs to one app.",
500
- },
501
- {
502
- flag: "--tier",
503
- value: "<t>",
504
- summary: "development, staging or production. Asked when absent.",
505
- },
506
- {
507
- flag: "--cron",
508
- value: "<expr>",
509
- summary: "Fire this schedule without opening the picker.",
510
- },
511
- YES,
512
- ],
513
- },
514
- ],
515
- },
516
- {
517
- name: "workflows",
518
- summary: "Cloudflare Workflows: list configured bindings or trigger one.",
519
- hint: "pick a workflow from wrangler.jsonc",
520
- subcommands: [
521
- {
522
- name: "list",
523
- summary: "List every Workflow binding declared by each Wrangler tier.",
524
- options: [
525
- {
526
- flag: "--app",
527
- value: "<slug>",
528
- summary: "Limit to one app.",
529
- },
530
- {
531
- flag: "--tier",
532
- value: "<t>",
533
- summary: "Limit to development, staging or production.",
534
- },
535
- JSON_FLAG,
536
- ],
537
- },
538
- {
539
- name: "run",
540
- summary: "Choose and trigger a Workflow through Wrangler.",
541
- hint: "local session or a deployed tier",
542
- options: [
543
- {
544
- flag: "--app",
545
- value: "<slug>",
546
- summary: "Limit the discovered Workflows to one app.",
547
- },
548
- {
549
- flag: "--tier",
550
- value: "<t>",
551
- summary: "development, staging or production. Asked when absent.",
552
- },
553
- {
554
- flag: "--workflow",
555
- value: "<name>",
556
- summary: "Trigger this configured name without opening the picker.",
557
- },
558
- {
559
- flag: "--params",
560
- value: "<json>",
561
- summary: "JSON parameters passed to the Workflow instance.",
562
- },
563
- {
564
- flag: "--port",
565
- value: "<n>",
566
- summary: "Local Wrangler session port. Defaults to 8787.",
567
- },
568
- YES,
569
- ],
570
- },
571
- {
572
- name: "serve",
573
- summary: "Start an app-scoped local Wrangler runtime until Ctrl+C.",
574
- hint: "secure alternative to bare wrangler dev",
575
- options: [
576
- {
577
- flag: "--app",
578
- value: "<slug>",
579
- summary: "Serve the development Workflow for this app.",
580
- },
581
- {
582
- flag: "--port",
583
- value: "<n>",
584
- summary: "Local Wrangler session port. Defaults to 8787.",
585
- },
586
- ],
587
- },
588
- ],
589
- },
590
- ],
591
- },
592
- {
593
- title: "GitHub",
594
- commands: [
595
- {
596
- name: "github",
597
- summary: "Reconcile the repository's rulesets and settings.",
598
- hint: "rulesets: main, production, ~ALL, team/**, tags; settings: security, Actions, environments",
599
- subcommands: [
600
- {
601
- name: "rulesets",
602
- summary: "Diff the fixed rulesets against live GitHub; --apply to write.",
603
- hint: "dry-run plan by default",
604
- envFree: true,
605
- options: [
606
- {
607
- flag: "--org",
608
- value: "<org>",
609
- summary: "GitHub org. Defaults to DevDogsUGA.",
610
- },
611
- {
612
- flag: "--repo",
613
- value: "<repo>",
614
- summary: "Repository name. Defaults to DevDogsUGA.",
615
- },
616
- {
617
- flag: "--app-slug",
618
- value: "<slug>",
619
- summary: "The GitHub App whose id bypasses team/** creation. Defaults to devdogs-platform.",
620
- },
621
- {
622
- flag: "--apply",
623
- summary: "Write the plan instead of only printing it.",
624
- },
625
- YES,
626
- JSON_FLAG,
627
- ],
628
- },
629
- {
630
- name: "settings",
631
- summary: "Diff security/Actions/environment settings vs live GitHub.",
632
- hint: "dry-run plan by default",
633
- envFree: true,
634
- options: [
635
- {
636
- flag: "--org",
637
- value: "<org>",
638
- summary: "GitHub org. Defaults to DevDogsUGA.",
639
- },
640
- {
641
- flag: "--repo",
642
- value: "<repo>",
643
- summary: "Repository name. Defaults to DevDogsUGA.",
644
- },
645
- {
646
- flag: "--apply",
647
- summary: "Write fixable drift instead of only printing it.",
648
- },
649
- YES,
650
- JSON_FLAG,
651
- ],
652
- },
653
- ],
654
- },
655
- ],
656
- },
657
- {
658
- title: "Project setup",
659
- commands: [
660
- {
661
- name: "oauth",
662
- summary: 'Configure "Sign in with DevDogs" for this directory.',
663
- hint: "works outside a DevDogsUGA checkout too",
664
- // Talks only to whatever local Supabase project is running in `cwd`
665
- // (see `oauth/db.ts`'s `detectLocalSupabase`) and, for a hosted
666
- // target, to the platform's own OAuth endpoints — never a
667
- // DevDogsUGA env file, database, or `DEPLOY_ENV`. TASK-345: a
668
- // workshop repo with no DevDogsUGA checkout at all must still be
669
- // able to run `pnpm dlx @devdogsuga/devtools oauth`, so this joins
670
- // `github rulesets`/`github settings` in the catalog-driven bypass
671
- // rather than the hardcoded `setup`/`completions` check — see
672
- // `launch.ts`'s `isEnvFreeCommand`. Inside a checkout, the command's
673
- // own hosted-target path still resolves a tier itself, lazily, only
674
- // when a hosted target is actually chosen (see `oauth/wizard.ts`).
675
- envFree: true,
676
- options: [
677
- {
678
- flag: "--base-url",
679
- value: "<url>",
680
- summary: 'DevDogs API URL for "Paste credentials instead". Defaults to https://api.devdogsuga.org.',
681
- },
682
- {
683
- flag: "--platform-url",
684
- value: "<url>",
685
- summary: "Platform URL for one-click connect. Defaults to https://devdogsuga.org.",
686
- },
687
- {
688
- flag: "--device",
689
- summary: "Force the device-code flow instead of the local loopback listener.",
690
- },
691
- {
692
- flag: "--loopback",
693
- summary: "Force the local loopback listener even over SSH/Codespaces/a dev container.",
694
- },
695
- ],
696
- },
697
- {
698
- name: "docs",
699
- summary: "The documentation search index.",
700
- subcommands: [
701
- {
702
- name: "index",
703
- summary: "Push the built docs artifact into the search index.",
704
- hint: "prunes stale rows in the target database",
705
- scope: "endpoint",
706
- options: [DOCS_TARGET],
707
- },
708
- ],
709
- },
710
- ],
711
- },
712
- {
713
- title: "Env files",
714
- commands: [
715
- {
716
- name: "env",
717
- summary: "One env file per target, synced to Bitwarden and GitHub.",
718
- subcommands: [
719
- {
720
- name: "pull",
721
- summary: "Bitwarden → the target's file, in place.",
722
- options: [VAULT_TARGET, ENV_FILE, YES, ACCESS_TOKEN],
723
- },
724
- {
725
- name: "push",
726
- summary: "The target's file → Bitwarden and GitHub.",
727
- options: [VAULT_TARGET, ENV_FILE, YES, ACCESS_TOKEN],
728
- },
729
- {
730
- name: "audit",
731
- summary: "Compare the file, Bitwarden, GitHub and Cloudflare.",
732
- hint: "reads only",
733
- options: [VAULT_TARGET, ENV_FILE, YES, ACCESS_TOKEN, JSON_FLAG],
734
- },
735
- {
736
- name: "init",
737
- summary: "Create a target file, or append newly declared keys.",
738
- hint: "existing lines are never changed",
739
- options: [
740
- {
741
- flag: "--target",
742
- value: "<t>",
743
- summary: "Which file to create. Defaults to development.",
744
- prompt: {
745
- kind: "select",
746
- message: "Create a file for which target?",
747
- choices: [
748
- { value: "development", hint: ".env — the default" },
749
- { value: "preflight", hint: ".env.preflight" },
750
- { value: "staging", hint: ".env.staging" },
751
- { value: "production", hint: "⚠️ .env.production" },
752
- ],
753
- },
754
- },
755
- {
756
- flag: "--apps",
757
- value: "<a,b,…>",
758
- summary: "Which sections to render. Development only; asks.",
759
- },
760
- ],
761
- },
762
- {
763
- name: "example",
764
- summary: "Regenerate .env.example from the manifests.",
765
- options: [
766
- {
767
- flag: "--check",
768
- summary: "Verify it is current, as CI does. Writes nothing.",
769
- prompt: {
770
- kind: "confirm",
771
- message: "Check only, without rewriting .env.example?",
772
- initial: true,
773
- },
774
- },
775
- ],
776
- },
777
- {
778
- name: "reset",
779
- summary: "Blank every value in .env, keeping each commented out.",
780
- hint: "local only, no target",
781
- options: [ENV_FILE, YES],
782
- },
783
- ],
784
- },
785
- {
786
- // The Bitwarden CLI, not a command of this one: everything after `bw`
787
- // is handed to it untouched. It is here because it is the login that
788
- // `env pull` depends on, it ships as a devtools dependency, and the
789
- // root `bw` alias it replaces was the last thing at the workspace root
790
- // reaching into this package. Declaring no subcommands is deliberate:
791
- // Bitwarden's commands are its own to document, and mirroring a slice
792
- // of them here would go stale on their release schedule, not ours.
793
- name: "bw",
794
- summary: "Run the Bitwarden CLI. `bw login` is the one you want.",
795
- hint: "passes everything through",
796
- },
797
- ],
798
- },
799
- {
800
- title: "Database",
801
- // One command, `db`. Its OWN subcommands carry the scopes — see the
802
- // header comment on `Scope` and `db`'s `hint` below. Declared in scope
803
- // order (machine, repo, endpoint, infra, then the unscoped escape hatch)
804
- // so `--help` and the wizard render one contiguous block per scope
805
- // instead of several.
806
- commands: [
807
- {
808
- name: "db",
809
- summary: "Supabase endpoints, and the databases inside them.",
810
- hint: "start, migrate, reset, types…",
811
- subcommands: [
812
- // ── machine — Supabase on this machine ─────────────────────────
813
- {
814
- name: "start",
815
- summary: "Start Supabase on this machine.",
816
- hint: "boots the Docker containers",
817
- scope: "machine",
818
- when: "instance-stopped",
819
- },
820
- {
821
- name: "connect",
822
- summary: "Run `supabase link` against a hosted project ref.",
823
- hint: "for driving the bare supabase CLI by hand",
824
- scope: "machine",
825
- },
826
- {
827
- name: "stop",
828
- summary: "Shut it down, freeing its containers.",
829
- hint: "your data survives",
830
- scope: "machine",
831
- // Not offered while nothing is running: "stop" against a stopped
832
- // stack is the one shape of question a menu should never ask.
833
- when: "instance-running",
834
- },
835
- {
836
- name: "restart",
837
- summary: "Stop it, then start it again.",
838
- // The reason this exists rather than being a footnote on `reset`:
839
- // `config.toml` is read at `supabase start`, so a reset replays
840
- // migrations into containers still holding the old settings.
841
- hint: "the only way to pick up config.toml — reset will not",
842
- scope: "machine",
843
- when: "instance-running",
844
- },
845
- // ── repo — files in the repo, no live connection ───────────────
846
- {
847
- name: "migration",
848
- summary: "Empty timestamped migration files, hand-authored.",
849
- scope: "repo",
850
- subcommands: [
851
- {
852
- name: "new",
853
- summary: "Create an empty timestamped migration file.",
854
- hint: "<timestamp>_<schema>_<description>.sql",
855
- options: [
856
- {
857
- flag: "--app",
858
- value: "<slug>",
859
- summary: "Whose schema. Asked for when absent.",
860
- },
861
- ],
862
- },
863
- ],
864
- },
865
- // ── endpoint — the session's database ──────────────────────────
866
- {
867
- name: "status",
868
- summary: "Report the session database's health, URLs and keys.",
869
- hint: "reads only",
870
- scope: "endpoint",
871
- options: [JSON_FLAG],
872
- },
873
- {
874
- name: "migrate",
875
- summary: "Apply new migrations to the database.",
876
- hint: "without erasing anything",
877
- scope: "endpoint",
878
- options: [YES],
879
- },
880
- {
881
- name: "reset",
882
- summary: "Rebuild the database: migrations, seeds, types, buckets.",
883
- hint: "⚠️ erases the database first",
884
- scope: "endpoint",
885
- options: [YES],
886
- },
887
- {
888
- name: "types",
889
- summary: "Regenerate database.types.ts, format it, rebuild the package.",
890
- hint: "after any schema change",
891
- scope: "endpoint",
892
- options: [],
893
- },
894
- {
895
- name: "seed",
896
- summary: "Seed storage buckets or the platform's role catalogue.",
897
- scope: "endpoint",
898
- subcommands: [
899
- {
900
- name: "buckets",
901
- summary: "Create the storage buckets config.toml declares.",
902
- options: [],
903
- },
904
- {
905
- name: "roles",
906
- summary: "Reconcile the platform's role catalogue.",
907
- options: [],
908
- },
909
- {
910
- name: "production",
911
- summary: "Apply supabase/seed/production/*.sql — roles and officers.",
912
- hint: "confirms before writing to a hosted database",
913
- options: [YES],
914
- },
915
- ],
916
- },
917
- {
918
- name: "introspect",
919
- summary: "Pull an app's live schema into its generated Drizzle files.",
920
- scope: "endpoint",
921
- options: [
922
- {
923
- flag: "--app",
924
- value: "<slug>",
925
- summary: "App whose schema to pull. Asked for when absent.",
926
- },
927
- ],
928
- },
929
- {
930
- name: "config",
931
- summary: "The Supabase config.toml, pushed to the session's project.",
932
- scope: "endpoint",
933
- subcommands: [
934
- {
935
- name: "push",
936
- summary: "Push config.toml to the session's hosted project.",
937
- hint: "hosted sessions only",
938
- options: [],
939
- },
940
- ],
941
- },
942
- // ── infra — hosted infrastructure, each naming its own connection
943
- {
944
- name: "planner",
945
- summary: "The migration_planner role the preflight tier may hold.",
946
- scope: "infra",
947
- subcommands: [
948
- {
949
- name: "status",
950
- summary: "Does the role exist, hold its two grants, and no more.",
951
- hint: "reads only — start here",
952
- options: [DB_URL, JSON_FLAG],
953
- },
954
- {
955
- name: "create",
956
- summary: "Mint the role, verify it live, write .env.preflight.",
957
- options: [DB_URL],
958
- },
959
- {
960
- name: "reset-password",
961
- summary: "Rotate the password. There is no retrieve.",
962
- options: [DB_URL, YES],
963
- },
964
- {
965
- name: "drop",
966
- summary: "Remove the role and blank the dead URL.",
967
- hint: "the recovery path",
968
- options: [DB_URL, YES],
969
- },
970
- ],
971
- },
972
- // ── unscoped — the escape hatch ─────────────────────────────────
973
- {
974
- name: "exec",
975
- summary: "Run the Supabase CLI. Everything after -- passes through.",
976
- hint: "the escape hatch",
977
- },
978
- ],
979
- },
980
- ],
981
- },
982
- {
983
- title: "Cloudflare",
984
- commands: [
985
- {
986
- name: "cf",
987
- summary: "Develop and build an app on the Workers runtime.",
988
- hint: "preview, typegen — deploys live in CI",
989
- subcommands: [
990
- {
991
- name: "preview",
992
- summary: "Build and serve an app on the Workers runtime.",
993
- options: [
994
- {
995
- flag: "--app",
996
- value: "<slug>",
997
- summary: "App to preview. Asked for when absent.",
998
- prompt: {
999
- kind: "select",
1000
- message: "Which app?",
1001
- choices: WORKER_APP_CHOICES,
1002
- },
1003
- },
1004
- {
1005
- flag: "--tier",
1006
- value: "<t>",
1007
- summary: "Preview against a tier's env. Defaults to development.",
1008
- // No prompt: the runtime resolver (`tier.ts`) asks this
1009
- // itself, conditionally, for both the wizard and a direct
1010
- // CLI invocation — a `prompt` here would ask it twice, once
1011
- // on this screen and once when the command actually runs.
1012
- },
1013
- YES,
1014
- ],
1015
- },
1016
- {
1017
- name: "typegen",
1018
- summary: "Regenerate cloudflare-env.d.ts from the wrangler config.",
1019
- options: [
1020
- {
1021
- flag: "--app",
1022
- value: "<slug>",
1023
- summary: "App to run typegen for. Asked for when absent.",
1024
- prompt: {
1025
- kind: "select",
1026
- message: "Which app?",
1027
- choices: WORKER_APP_CHOICES,
1028
- },
1029
- },
1030
- {
1031
- flag: "--check",
1032
- summary: "Check types only — do not write.",
1033
- prompt: {
1034
- kind: "confirm",
1035
- message: "Check only (do not write)?",
1036
- initial: true,
1037
- },
1038
- },
1039
- ],
1040
- },
1041
- {
1042
- name: "build",
1043
- summary: "Build an app's Worker bundle for a tier.",
1044
- options: [
1045
- {
1046
- flag: "--app",
1047
- value: "<slug>",
1048
- summary: "App to build. Asked for when absent.",
1049
- prompt: {
1050
- kind: "select",
1051
- message: "Which app?",
1052
- choices: WORKER_APP_CHOICES,
1053
- },
1054
- },
1055
- {
1056
- flag: "--tier",
1057
- value: "<t>",
1058
- summary: "Which deployment tier to build for.",
1059
- prompt: {
1060
- kind: "select",
1061
- message: "Which tier?",
1062
- choices: [
1063
- { value: "staging" },
1064
- { value: "production", hint: "⚠️ live bundle" },
1065
- ],
1066
- },
1067
- },
1068
- ],
1069
- },
1070
- {
1071
- name: "exec",
1072
- summary: "Run wrangler. Everything after -- passes through.",
1073
- hint: "the escape hatch",
1074
- },
1075
- ],
1076
- },
1077
- ],
1078
- },
1079
- ];
1080
- /**
1081
- * The contributor-facing navigation, grouped by the job somebody is doing.
1082
- *
1083
- * Command declarations stay close to their domain-specific option data above;
1084
- * this projection is the one place that decides how the root help and menu
1085
- * read. Paths do not change when a command moves between groups.
1086
- */
1087
- const declaredByName = new Map(DECLARED_GROUPS.flatMap((group) => group.commands).map((command) => [
1088
- command.name,
1089
- command,
1090
- ]));
1091
- function commands(...names) {
1092
- return names.map((name) => {
1093
- const command = declaredByName.get(name);
1094
- if (!command)
1095
- throw new Error(`Command "${name}" is not declared.`);
1096
- return command;
1097
- });
1098
- }
1099
- export const GROUPS = [
1100
- {
1101
- title: "Workspace",
1102
- commands: commands("setup", "oauth", "run", "gen", "docs"),
1103
- },
1104
- {
1105
- title: "Runtime & infrastructure",
1106
- commands: commands("db", "cf", "cron", "workflows"),
1107
- },
1108
- {
1109
- title: "Content & communications",
1110
- commands: commands("images", "emails"),
1111
- },
1112
- {
1113
- title: "Configuration & integrations",
1114
- commands: commands("env", "bw"),
1115
- },
1116
- {
1117
- title: "Moderation",
1118
- commands: commands("moderation", "grant-root"),
1119
- },
1120
- {
1121
- title: "Personas",
1122
- commands: commands("persona"),
1123
- },
1124
- {
1125
- title: "Environment",
1126
- commands: commands("doctor"),
1127
- },
1128
- {
1129
- title: "GitHub",
1130
- commands: commands("github"),
1131
- },
1132
- {
1133
- title: "CLI utilities",
1134
- commands: commands("completions"),
1135
- },
1136
- ];
1137
- // ── CI command tree ───────────────────────────────────────────────────────────
1138
- /**
1139
- * The commands the `devtools-ci` bin exposes.
1140
- *
1141
- * Kept separate from `GROUPS` because CI commands are never reached from the
1142
- * wizard and never rendered in `--help`. They live here so the docs build can
1143
- * render a CI reference page from the same declaration, and so the tests that
1144
- * guard the style guide can cover both trees without duplicating the pins.
1145
- */
1146
- export const CI_GROUPS = [
1147
- {
1148
- title: "Deploy",
1149
- commands: [
1150
- {
1151
- name: "deploy",
1152
- summary: "Deploy an app: token gate, per-app steps, upload.",
1153
- options: [TIER, DRY_RUN],
1154
- subcommands: [
1155
- // ── App orchestrators ────────────────────────────────────────────
1156
- {
1157
- name: "platform",
1158
- summary: "Deploy the platform app.",
1159
- options: [TIER, DRY_RUN],
1160
- },
1161
- {
1162
- name: "schedule-builder",
1163
- summary: "Deploy the schedule-builder app.",
1164
- options: [TIER, DRY_RUN],
1165
- },
1166
- {
1167
- name: "sandbox",
1168
- summary: "Deploy the sandbox app.",
1169
- options: [TIER, DRY_RUN],
1170
- },
1171
- // ── Step commands ────────────────────────────────────────────────
1172
- {
1173
- name: "write-env",
1174
- summary: "Compose .env.<DEPLOY_ENV> from the GitHub environment.",
1175
- options: [
1176
- {
1177
- flag: "--source",
1178
- value: "<manifest>",
1179
- summary: "Compose one manifest's slice instead of all.",
1180
- },
1181
- ],
1182
- },
1183
- {
1184
- name: "secrets-file",
1185
- summary: "Write the --secrets-file wrangler uploads with a Worker.",
1186
- options: [
1187
- {
1188
- flag: "--app",
1189
- value: "<app>",
1190
- summary: "Whose manifest declares the Worker's secrets.",
1191
- },
1192
- ],
1193
- },
1194
- {
1195
- name: "orphans",
1196
- summary: "Report Worker secrets nothing declares.",
1197
- options: [
1198
- {
1199
- flag: "--prune",
1200
- summary: "Delete them. production-apply only.",
1201
- },
1202
- ],
1203
- },
1204
- {
1205
- name: "preflight",
1206
- summary: "Classify the project: paused (skip) vs broken (fail).",
1207
- },
1208
- {
1209
- name: "require-token",
1210
- summary: "Refuse to deploy without CLOUDFLARE_API_TOKEN.",
1211
- },
1212
- {
1213
- name: "require-planner",
1214
- summary: "Refuse to plan unless DB_URL is the planner role.",
1215
- },
1216
- {
1217
- name: "plan",
1218
- summary: "Dry-run the migrations into the job summary.",
1219
- options: [
1220
- {
1221
- flag: "--label",
1222
- value: "<title>",
1223
- summary: "Heading for the summary section.",
1224
- },
1225
- ],
1226
- },
1227
- {
1228
- name: "migrate",
1229
- summary: "Apply the migrations to DB_URL.",
1230
- },
1231
- ],
1232
- },
1233
- ],
1234
- },
1235
- ];
1236
- // ── Lookup ───────────────────────────────────────────────────────────────────
1237
- /** Every top-level command, in group order. */
1238
- export const TOP_LEVEL = GROUPS.flatMap((group) => group.commands);
1239
- /** Every top-level CI command, in group order. */
1240
- export const CI_TOP_LEVEL = CI_GROUPS.flatMap((group) => group.commands);
1241
- /**
1242
- * Walks a path in the CI tree, returning `null` at the first miss.
1243
- *
1244
- * Identical to `findCommand` but over `CI_TOP_LEVEL` rather than `TOP_LEVEL`.
1245
- */
1246
- export function findCiCommand(path) {
1247
- let nodes = CI_TOP_LEVEL;
1248
- let found = null;
1249
- for (const name of path) {
1250
- const next = nodes.find((node) => node.name === name);
1251
- if (!next)
1252
- return null;
1253
- found = next;
1254
- nodes = next.subcommands ?? [];
1255
- }
1256
- return found;
1257
- }
1258
- /** The subcommand names under a CI path. */
1259
- export function subcommandCiNames(path) {
1260
- return (findCiCommand(path)?.subcommands ?? []).map((node) => node.name);
1261
- }
1262
- /**
1263
- * Walks a path like `["env", "pull"]`, returning `null` at the first miss.
1264
- *
1265
- * Callers use `null` to mean "not a command", which is the same answer the
1266
- * dispatcher gives, so an unknown name reads the same whichever notices first.
1267
- */
1268
- export function findCommand(path) {
1269
- let nodes = TOP_LEVEL;
1270
- let found = null;
1271
- for (const name of path) {
1272
- const next = nodes.find((node) => node.name === name);
1273
- if (!next)
1274
- return null;
1275
- found = next;
1276
- nodes = next.subcommands ?? [];
1277
- }
1278
- return found;
1279
- }
1280
- /** The group a top-level command sits in, for the wizard's first screen. */
1281
- export function groupOf(name) {
1282
- return GROUPS.find((group) => group.commands.some((command) => command.name === name));
1283
- }
1284
- /**
1285
- * The subcommand names under a path, in the order they are declared.
1286
- *
1287
- * This is what the dispatchers in `cli.ts` validate against. A subcommand the
1288
- * tree does not declare is refused by the CLI, and an interactive one it does
1289
- * declare is in the menu. There is one list, and this reads it.
1290
- */
1291
- export function subcommandNames(path) {
1292
- return (findCommand(path)?.subcommands ?? []).map((node) => node.name);
1293
- }
1294
- /** `pull, push, audit, init, example or reset`, for a refusal message. */
1295
- export function subcommandList(path) {
1296
- const names = subcommandNames(path);
1297
- if (names.length <= 1)
1298
- return names.join("");
1299
- return `${names.slice(0, -1).join(", ")} or ${names[names.length - 1]}`;
1300
- }
1301
- /**
1302
- * Every command path in the tree, deepest names included.
1303
- *
1304
- * Used by the coverage test, and by anything that wants to enumerate the CLI
1305
- * (the docs build's reference page is the intended second caller).
1306
- */
1307
- export function allPaths() {
1308
- const paths = [];
1309
- const visit = (nodes, prefix) => {
1310
- for (const node of nodes) {
1311
- const path = [...prefix, node.name];
1312
- paths.push(path);
1313
- visit(node.subcommands ?? [], path);
1314
- }
1315
- };
1316
- visit(TOP_LEVEL, []);
1317
- return paths;
1318
- }