@expo/code-review-cli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -6
- package/build/cli.js +24 -17
- package/build/commands/ci.js +406 -43
- package/build/commands/dismiss.js +16 -16
- package/build/commands/doctor.js +173 -26
- package/build/commands/init.js +244 -34
- package/build/commands/review.js +118 -30
- package/build/commands/verify-config.js +214 -0
- package/build/config/load.js +154 -52
- package/build/config/routing.js +122 -0
- package/build/config/schema.js +116 -12
- package/build/core/auth.js +32 -29
- package/build/core/coordinator.js +5 -5
- package/build/core/diff.js +19 -19
- package/build/core/exec.js +10 -10
- package/build/core/log.js +3 -3
- package/build/core/noise.js +52 -52
- package/build/core/opencode.js +44 -44
- package/build/core/prompts.js +157 -148
- package/build/core/render.js +202 -48
- package/build/core/review.js +147 -85
- package/build/core/router.js +10 -10
- package/build/core/schema.js +26 -12
- package/build/core/step-summary.js +18 -0
- package/build/core/suppress.js +7 -7
- package/build/core/tools.js +9 -9
- package/build/core/util.js +2 -2
- package/build/core/verify.js +25 -25
- package/build/reporters/github.js +103 -51
- package/build/reporters/terminal.js +19 -19
- package/build/sources/github-pr.js +21 -21
- package/build/sources/local-git.js +20 -20
- package/build/sources/source.js +35 -1
- package/package.json +6 -1
- package/templates/agents/security.md +5 -0
- package/templates/command.yml +164 -0
- package/templates/coordinator.md +5 -3
- package/templates/dismiss.yml +110 -0
- package/templates/routing.jsonc +27 -0
- package/templates/scope-config.jsonc +25 -0
- package/templates/shared.md +12 -0
- package/templates/workflow.yml +50 -20
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ Options (most to least common):
|
|
|
74
74
|
| --- | --- |
|
|
75
75
|
| `--pr <n>` | Review GitHub PR #n by number (diff fetched via `gh`, no checkout); not combinable with `--base`/`--head`/`--staged`. |
|
|
76
76
|
| `--post` | With `--pr`, also post the result as the PR comment (needs `gh` auth). Omit to preview only; re-run with `--post` to publish. |
|
|
77
|
-
| `--staged` | Review only staged changes. |
|
|
77
|
+
| `--staged` | Review only staged changes (index vs HEAD; not combinable with `--base`/`--head`). |
|
|
78
78
|
| `--base <ref>` | Base ref to diff against (default: merge-base with the default branch). |
|
|
79
79
|
| `--head <ref>` | Head ref to diff (default: working tree, incl. uncommitted changes). |
|
|
80
80
|
| `--agents <a,b>` | Run only these agents (comma-separated ids); default: all. |
|
|
@@ -101,14 +101,132 @@ is a ready example to adapt.
|
|
|
101
101
|
| Command | What it does |
|
|
102
102
|
| --- | --- |
|
|
103
103
|
| `ecr init [--no-workflow] [--force]` | Scaffold `.expo-code-review/` (config, agents, prompts) + a CI workflow. |
|
|
104
|
+
| `ecr init --monorepo` | …and add a `routing.jsonc` routing manifest (one default scope). |
|
|
105
|
+
| `ecr init --scope <dir>` | Scaffold a per-team scope under `<dir>` and register it in the manifest. |
|
|
104
106
|
| `ecr review [options]` | Review local changes and print an advisory review (default command). |
|
|
107
|
+
| `ecr review --scope <name>` | Review only one routing scope over just that scope's changed files. |
|
|
105
108
|
| `ecr ci` | Review the current GitHub PR and post/update a comment. For GitHub Actions. |
|
|
106
|
-
| `ecr doctor` | Check environment, config, and
|
|
109
|
+
| `ecr doctor [--list-scopes]` | Check environment, config, credentials, and (with a manifest) scopes. |
|
|
110
|
+
|
|
111
|
+
Extra flags for monorepos: `review`/`ci` `--config-dir <dir>` (load config from an
|
|
112
|
+
alternate dir; also `ECR_CONFIG_DIR`), `ci --scopes a,b` (limit the fan-out to
|
|
113
|
+
named scopes), `ci --comment single|per-scope` (override the manifest).
|
|
107
114
|
|
|
108
115
|
(When developing this repo itself, use `bun run src/cli.ts <command>`.)
|
|
109
116
|
|
|
110
117
|
---
|
|
111
118
|
|
|
119
|
+
## Monorepos (routing manifest)
|
|
120
|
+
|
|
121
|
+
A monorepo can route different subtrees to different reviewer rosters from a single
|
|
122
|
+
infra-owned manifest. There is still **one workflow, one `ecr ci` process** per PR:
|
|
123
|
+
it reads the changed files once, assigns each to exactly one scope, reviews each
|
|
124
|
+
active scope over only its files, and renders one comment. Because it is a single
|
|
125
|
+
writer in a single process there is no comment/check race and no locking.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
your-monorepo/
|
|
129
|
+
.expo-code-review/
|
|
130
|
+
routing.jsonc # the manifest — infra-owned, ordered scope list + locked defaults
|
|
131
|
+
config.jsonc # the default/root scope; the ONLY place auth/tokenEnv lives
|
|
132
|
+
shared.md coordinator.md agents/
|
|
133
|
+
server/
|
|
134
|
+
www/.expo-code-review/{config.jsonc(NO auth),coordinator.md,agents/} # www team
|
|
135
|
+
website/.expo-code-review/{config.jsonc(NO auth),coordinator.md,agents/} # website team
|
|
136
|
+
.github/workflows/expo-code-review.yml # unchanged shape: one workflow, one `ecr ci`
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```jsonc
|
|
140
|
+
// .expo-code-review/routing.jsonc
|
|
141
|
+
{
|
|
142
|
+
// Central guardrails every scope inherits and CANNOT override.
|
|
143
|
+
"defaults": {
|
|
144
|
+
// The ONLY place auth/tokenEnv is honored (besides the root config.jsonc).
|
|
145
|
+
"auth": { "mode": "oauth", "provider": "anthropic", "tokenEnv": "ANTHROPIC_OAUTH_API_KEY" },
|
|
146
|
+
"enforceAgents": ["security"], // always runs on every scope, roster or not
|
|
147
|
+
"commentTag": "expo-ai-code-reviewer" // per-scope markers derive from this
|
|
148
|
+
},
|
|
149
|
+
"comment": "single", // "single" = one aggregated comment (default) | "per-scope"
|
|
150
|
+
// Ordered; the LAST matching scope wins per changed file (CODEOWNERS discipline).
|
|
151
|
+
"scopes": [
|
|
152
|
+
{ "name": "default", "paths": ["**/*"], "config": "." },
|
|
153
|
+
{ "name": "server-www", "paths": ["server/www/**"], "config": "server/www" },
|
|
154
|
+
{ "name": "server-website", "paths": ["server/website/**"], "config": "server/website" }
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```jsonc
|
|
160
|
+
// server/www/.expo-code-review/config.jsonc (the www team owns this)
|
|
161
|
+
{
|
|
162
|
+
// NO "auth" block — locked centrally; a tokenEnv here is rejected by loader + CI guard.
|
|
163
|
+
"model": "anthropic/claude-sonnet-5",
|
|
164
|
+
"policy": { "includeSuggestions": false },
|
|
165
|
+
"noise": { "additionalIgnores": ["server/www/**/__generated__/**"] }
|
|
166
|
+
// shared.md, coordinator.md, agents/*.md live beside this file — the www team's roster.
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **Path glob dialect** — `**` crosses `/`, `*` matches within a segment. Keep a
|
|
171
|
+
`**/*` catch-all scope so no changed file goes unreviewed (`ecr doctor` flags a
|
|
172
|
+
coverage gap otherwise). Scopes are ordered and the **last** match wins, so put
|
|
173
|
+
broad scopes first and specific ones after (CODEOWNERS/Renovate discipline).
|
|
174
|
+
- **Comment modes** — `single` posts one aggregated comment (a scope summary table
|
|
175
|
+
+ a collapsed `<details>` per scope) under the existing marker; `per-scope` posts
|
|
176
|
+
one cleanly-namespaced comment per scope. A scope with zero matched files gets its
|
|
177
|
+
stale comment deleted.
|
|
178
|
+
- **Scoped flags** — `ecr ci --scopes a,b` limits the fan-out; `ecr ci --comment
|
|
179
|
+
single|per-scope` overrides the manifest; `ecr review --scope <name>` runs one
|
|
180
|
+
scope locally; `ecr review`/`ecr ci --config-dir <dir>` (or `ECR_CONFIG_DIR`)
|
|
181
|
+
load config from an alternate directory; `ecr doctor --list-scopes` prints the
|
|
182
|
+
scope table. **`--config-dir` designates an alternate ROOT config dir: both
|
|
183
|
+
`config.jsonc` and `routing.jsonc` are read from it, so the root config and its
|
|
184
|
+
manifest always travel together. Scope `config` paths stay repo-root-relative —
|
|
185
|
+
the override swaps the root config/manifest against the *real* scope tree, it
|
|
186
|
+
does not relocate the scopes themselves.**
|
|
187
|
+
- **Passes budget** — `defaults`-level `budget` bounds total review time:
|
|
188
|
+
`totalPassesMinutes` (default 32) is split across active scopes (which run
|
|
189
|
+
sequentially in one `ecr ci`), clamped up to `minScopeMinutes` (default 5) so a
|
|
190
|
+
single scope still gets a workable window. When enough scopes are active that the
|
|
191
|
+
floor would overshoot the total, `ecr ci` keeps the floor but warns, and `ecr
|
|
192
|
+
doctor` flags the worst case (`scopes × floor` vs total) — raise the workflow
|
|
193
|
+
`timeout-minutes` or trim scopes.
|
|
194
|
+
- **Adoption is incremental** — with no `routing.jsonc`, behavior is exactly as
|
|
195
|
+
before (single config). Add the manifest with just a default scope → still one
|
|
196
|
+
comment, identical behavior. Land per-team scope dirs one at a time; everything
|
|
197
|
+
else keeps hitting the default scope.
|
|
198
|
+
|
|
199
|
+
### Security
|
|
200
|
+
|
|
201
|
+
- **auth is locked to the root.** `tokenEnv` (which env var becomes the model
|
|
202
|
+
credential) is honored in exactly one place: the root `config.jsonc` or
|
|
203
|
+
`routing.jsonc` `defaults.auth`. A scope config declaring `auth`/`breakGlass`
|
|
204
|
+
**fails to parse** (Zod-level rejection), and the CI guard step independently
|
|
205
|
+
sweeps every `.expo-code-review/config.jsonc`/`routing.jsonc` repo-wide and refuses
|
|
206
|
+
to run unless `tokenEnv` appears exactly once, in a root-owned file, equal to
|
|
207
|
+
`ECR_EXPECTED_TOKEN_ENV`. A routing manifest can never widen exposure — globs only
|
|
208
|
+
choose *which roster* reviews a file, never *which secret* is sent.
|
|
209
|
+
- **enforceAgents can't be weakened.** Agents listed in `defaults.enforceAgents`
|
|
210
|
+
(e.g. `security`) are injected into every scope with `alwaysRun`, taken from the
|
|
211
|
+
root roster — a scope defining a same-id agent gets the root one, so a team can't
|
|
212
|
+
shadow the enforced reviewer with a weaker version on its own subtree.
|
|
213
|
+
- **Config comes from the checked-out ref (documented tradeoff).** The scaffolded
|
|
214
|
+
auto workflow checks out the PR **merge ref**, so a PR *can* edit rosters, prompts,
|
|
215
|
+
and routing globs for its own advisory review — that only steers what the
|
|
216
|
+
comment-only reviewer says about that PR. What a PR can **never** do is touch
|
|
217
|
+
credentials: `auth` is locked by the scope-schema rejection, the CLI's runtime
|
|
218
|
+
`ECR_EXPECTED_TOKEN_ENV` check, and the guard step above, all three of which run
|
|
219
|
+
against whatever ref is checked out. A `/review`-command workflow that checks out
|
|
220
|
+
only the trusted base ref (see eas-cli's) closes the prompt-tampering vector too;
|
|
221
|
+
resolving config from the base ref on the auto path as well is on the
|
|
222
|
+
[roadmap](./ROADMAP.md).
|
|
223
|
+
|
|
224
|
+
Ownership is enforced with CODEOWNERS: `/.expo-code-review/routing.jsonc @your-infra`
|
|
225
|
+
(the single authoritative router) and `/server/www/.expo-code-review/ @your-www-team`
|
|
226
|
+
(each team owns only its own scope dir). Rerouting globs is gated behind infra review.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
112
230
|
<details>
|
|
113
231
|
<summary><b>How it works</b></summary>
|
|
114
232
|
|
|
@@ -135,6 +253,49 @@ and applies Anthropic prompt caching automatically.
|
|
|
135
253
|
|
|
136
254
|
</details>
|
|
137
255
|
|
|
256
|
+
<details>
|
|
257
|
+
<summary><b>Tokens, cost & prompt caching</b></summary>
|
|
258
|
+
|
|
259
|
+
Every run reports what it spent and how much of it was served from the prompt
|
|
260
|
+
cache, in three places:
|
|
261
|
+
|
|
262
|
+
- **Job log (stderr)** — one line at the end of the run:
|
|
263
|
+
`Token usage — input …, output …, cache read …, cache write … (cost $…)`.
|
|
264
|
+
- **GitHub Actions step summary** — a per-pass table (one row per agent, plus the
|
|
265
|
+
cross-cutting pass, coordinator, and verifier), the run's cache hit rate, and
|
|
266
|
+
the exact comment that was posted. The PR comment is updated in place on every
|
|
267
|
+
run, so the step summary is where past runs' comments remain readable.
|
|
268
|
+
- **`.expo-code-review/.runs/reviews.jsonl`** — one JSON line per run (uploaded as
|
|
269
|
+
a CI artifact) with the same totals plus per-pass `agentTokens`, the raw
|
|
270
|
+
per-agent findings, coverage notes, and what the verifier dropped.
|
|
271
|
+
|
|
272
|
+
**How the caching works.** Anthropic prompt caching is a *prefix match*: the
|
|
273
|
+
provider caches the rendered prompt up to a marker, and any byte change anywhere
|
|
274
|
+
in that prefix invalidates everything after it. The reviewer is laid out so the
|
|
275
|
+
prefix is stable — the system prompt (`shared.md` + the agent's own `.md`) is
|
|
276
|
+
byte-identical for every chunk an agent reviews, while the volatile parts (the
|
|
277
|
+
diff, file lists, PR metadata) travel in the user message *after* the prefix and
|
|
278
|
+
never touch it. The first call per agent **writes** the cache (`cache write`,
|
|
279
|
+
billed ~1.25× the input price); every later chunk in the run **reads** it
|
|
280
|
+
(`cache read`, ~0.1× the input price). Cache entries live ~5 minutes and are
|
|
281
|
+
refreshed on use, which comfortably covers a run's concurrent calls.
|
|
282
|
+
|
|
283
|
+
**Reading the numbers.** Hit rate = `cache read / (cache read + input)` — the
|
|
284
|
+
share of prompt tokens served from cache instead of being reprocessed at full
|
|
285
|
+
price. Multi-chunk reviews should show a high rate; single-chunk reviews mostly
|
|
286
|
+
show writes (there is nothing to re-read within the run).
|
|
287
|
+
|
|
288
|
+
**Keeping hits high:**
|
|
289
|
+
|
|
290
|
+
- Keep `shared.md` and `agents/*.md` stable. Any edit writes a new prefix — one
|
|
291
|
+
extra cache write per agent on the next run, then it is warm again. Never put
|
|
292
|
+
varying text (dates, PR numbers) into prompt files.
|
|
293
|
+
- Very short prompts may show `cache read 0`: prompts below the model's minimum
|
|
294
|
+
cacheable size (~1–4K tokens depending on the model) are silently not cached.
|
|
295
|
+
That is expected, not a bug.
|
|
296
|
+
|
|
297
|
+
</details>
|
|
298
|
+
|
|
138
299
|
<details>
|
|
139
300
|
<summary><b>Configuration — <code>.expo-code-review/</code></b></summary>
|
|
140
301
|
|
|
@@ -176,6 +337,9 @@ per-repo `noise.additionalIgnores`.
|
|
|
176
337
|
"policy": { "includeSuggestions": false }, // suppress suggestion-severity findings
|
|
177
338
|
"chunk": { "maxChangedLines": 1000, "maxFiles": 20, "concurrency": 6 },
|
|
178
339
|
"noise": { "additionalIgnores": ["packages/*/build/**"] },
|
|
340
|
+
"review": { "trigger": "all", // which PRs `ecr ci` reviews: "all"
|
|
341
|
+
"label": "ai-review", // (default, except ai-review:skip) or
|
|
342
|
+
"skipLabel": "ai-review:skip" }, // "label" (only labeled PRs)
|
|
179
343
|
"breakGlass": { "marker": "/skip-review" }, // PR body marker that skips the review
|
|
180
344
|
"commentTag": "expo-ai-code-reviewer", // hidden tag used to find/update the comment
|
|
181
345
|
"auth": { "mode": "oauth", "provider": "anthropic",
|
|
@@ -223,6 +387,14 @@ change which model reviewed your code. Use an explicit override instead.
|
|
|
223
387
|
un-reducible pass reports a coverage gap — and it is always reported, never silent.
|
|
224
388
|
- **Parse failures are retried** (same session, then once in a bounded fresh
|
|
225
389
|
session) — separate from the timeout path.
|
|
390
|
+
- **Transient API errors are retried** (bounded backoff on 429/5xx/network) —
|
|
391
|
+
distinct from both the timeout path (abandon) and the parse path; a one-off blip
|
|
392
|
+
no longer drops an entire pass.
|
|
393
|
+
- **Auth failures surface once, and fail fast** — `ecr` checks the configured
|
|
394
|
+
provider's credential at startup and stops with one clear message if it's missing
|
|
395
|
+
(rather than failing every pass); a credential rejected mid-run (401/403)
|
|
396
|
+
collapses into a single actionable coverage note pointing at
|
|
397
|
+
`auth.tokenEnv`/`REVIEWER_MODEL`.
|
|
226
398
|
- **A failed run never reads as "Approve"** — all passes fail → "could not
|
|
227
399
|
complete"; some fail → never a clean approve, and coverage-reduced.
|
|
228
400
|
- **The coordinator can't sink the run** — if consolidation fails, findings are
|
|
@@ -243,9 +415,12 @@ line: **comments = one-shot actions, labels = persistent configuration.**
|
|
|
243
415
|
- **command workflow** — one-shot `/review` comments (maintainers): `/review`
|
|
244
416
|
(router picks agents), `/review all`, `/review correctness security`. Never
|
|
245
417
|
changes configuration.
|
|
246
|
-
- **auto workflow** — continuous review
|
|
247
|
-
|
|
248
|
-
|
|
418
|
+
- **auto workflow** — continuous review. **Which PRs get reviewed is set in
|
|
419
|
+
`config.jsonc` → `review.trigger`**: `"all"` (default) reviews every PR except
|
|
420
|
+
those labeled `ai-review:skip`; `"label"` reviews only PRs labeled `ai-review`
|
|
421
|
+
(or `ai-review:<agent>` to scope agents). `ecr ci` self-gates on this policy;
|
|
422
|
+
the workflow's `if:` is an optional coarse gate layered on top. `ai-review:skip`
|
|
423
|
+
always wins and is write-gated, so a contributor can't opt their own PR out.
|
|
249
424
|
- **dismiss workflow** — `/dismiss <id> [… -- reason]` / `/undismiss <id>`
|
|
250
425
|
(maintainers). Each finding shows a short `` `id:…` ``. Dismissal is a **display
|
|
251
426
|
filter only** — the reviewer still analyzes everything, and a `critical`/`secrets`
|
|
@@ -263,7 +438,9 @@ as the published package via `npx`, so no PR-controlled code is built.
|
|
|
263
438
|
Each run appends a JSON line to `.expo-code-review/.runs/reviews.jsonl` with the
|
|
264
439
|
inputs, decision, finding count, duration, per-agent cost, and aggregate token
|
|
265
440
|
usage (incl. prompt-cache read/write counts) — for auditing and measuring
|
|
266
|
-
cost/latency/cache reuse over time.
|
|
441
|
+
cost/latency/cache reuse over time. The same totals are printed as a one-line
|
|
442
|
+
summary to the terminal / CI job log at the end of each run, so cache reuse is
|
|
443
|
+
visible even in CI (where the run log is ephemeral).
|
|
267
444
|
|
|
268
445
|
</details>
|
|
269
446
|
|
package/build/cli.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { ciCommand } from
|
|
3
|
-
import { dismissCommand } from
|
|
4
|
-
import { doctorCommand } from
|
|
5
|
-
import { initCommand } from
|
|
6
|
-
import { reviewCommand } from
|
|
2
|
+
import { ciCommand } from "./commands/ci.js";
|
|
3
|
+
import { dismissCommand } from "./commands/dismiss.js";
|
|
4
|
+
import { doctorCommand } from "./commands/doctor.js";
|
|
5
|
+
import { initCommand } from "./commands/init.js";
|
|
6
|
+
import { reviewCommand } from "./commands/review.js";
|
|
7
|
+
import { verifyConfigCommand } from "./commands/verify-config.js";
|
|
7
8
|
const USAGE = `expo-code-review (ecr) — config-driven AI code reviewer
|
|
8
9
|
|
|
9
10
|
Usage:
|
|
@@ -11,41 +12,47 @@ Usage:
|
|
|
11
12
|
ecr ci Review the current PR and post a comment (GitHub Actions).
|
|
12
13
|
ecr dismiss --pr <n> <id...> Hide a finding on a PR (see \`ecr dismiss --help\`).
|
|
13
14
|
ecr undismiss --pr <n> <id...> Restore a dismissed finding.
|
|
14
|
-
ecr init [--
|
|
15
|
-
ecr doctor
|
|
15
|
+
ecr init [--monorepo] [--scope <dir>] Scaffold .expo-code-review/ in this repo.
|
|
16
|
+
ecr doctor [--list-scopes] Check environment, config, credentials, and scopes.
|
|
17
|
+
ecr verify-config [--expected <env>] [--json] Refuse to run if a config could redirect the credential (CI guard).
|
|
16
18
|
|
|
17
19
|
Agents live in each repo under .expo-code-review/. This CLI is the engine.
|
|
20
|
+
|
|
21
|
+
Monorepos: .expo-code-review/routing.jsonc routes paths to per-team scopes (see README).
|
|
18
22
|
`;
|
|
19
23
|
async function main() {
|
|
20
24
|
const [, , sub, ...rest] = process.argv;
|
|
21
|
-
if (sub ===
|
|
25
|
+
if (sub === "-h" || sub === "--help" || sub === "help") {
|
|
22
26
|
process.stdout.write(USAGE);
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
25
29
|
// No subcommand (or a leading flag) defaults to `review`.
|
|
26
|
-
if (!sub || sub.startsWith(
|
|
30
|
+
if (!sub || sub.startsWith("-")) {
|
|
27
31
|
await reviewCommand(process.argv.slice(2));
|
|
28
32
|
return;
|
|
29
33
|
}
|
|
30
34
|
switch (sub) {
|
|
31
|
-
case
|
|
35
|
+
case "review":
|
|
32
36
|
await reviewCommand(rest);
|
|
33
37
|
break;
|
|
34
|
-
case
|
|
38
|
+
case "ci":
|
|
35
39
|
await ciCommand(rest);
|
|
36
40
|
break;
|
|
37
|
-
case
|
|
38
|
-
await dismissCommand(rest,
|
|
41
|
+
case "dismiss":
|
|
42
|
+
await dismissCommand(rest, "add");
|
|
39
43
|
break;
|
|
40
|
-
case
|
|
41
|
-
await dismissCommand(rest,
|
|
44
|
+
case "undismiss":
|
|
45
|
+
await dismissCommand(rest, "remove");
|
|
42
46
|
break;
|
|
43
|
-
case
|
|
47
|
+
case "init":
|
|
44
48
|
await initCommand(rest);
|
|
45
49
|
break;
|
|
46
|
-
case
|
|
50
|
+
case "doctor":
|
|
47
51
|
await doctorCommand(rest);
|
|
48
52
|
break;
|
|
53
|
+
case "verify-config":
|
|
54
|
+
await verifyConfigCommand(rest);
|
|
55
|
+
break;
|
|
49
56
|
default:
|
|
50
57
|
process.stderr.write(`Unknown command: ${sub}\n\n${USAGE}`);
|
|
51
58
|
process.exitCode = 2;
|