@expo/code-review-cli 0.2.3 → 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.
Files changed (43) hide show
  1. package/README.md +183 -6
  2. package/build/cli.js +24 -17
  3. package/build/commands/ci.js +427 -28
  4. package/build/commands/dismiss.js +16 -16
  5. package/build/commands/doctor.js +172 -32
  6. package/build/commands/init.js +244 -34
  7. package/build/commands/review.js +124 -30
  8. package/build/commands/verify-config.js +214 -0
  9. package/build/config/load.js +155 -52
  10. package/build/config/routing.js +122 -0
  11. package/build/config/schema.js +127 -8
  12. package/build/core/auth.js +101 -38
  13. package/build/core/coordinator.js +5 -5
  14. package/build/core/diff.js +19 -19
  15. package/build/core/exec.js +10 -10
  16. package/build/core/log.js +3 -3
  17. package/build/core/noise.js +52 -52
  18. package/build/core/opencode.js +98 -44
  19. package/build/core/prompts.js +157 -148
  20. package/build/core/render.js +202 -48
  21. package/build/core/review.js +187 -81
  22. package/build/core/router.js +10 -10
  23. package/build/core/schema.js +26 -12
  24. package/build/core/step-summary.js +18 -0
  25. package/build/core/suppress.js +7 -7
  26. package/build/core/tools.js +9 -9
  27. package/build/core/util.js +2 -2
  28. package/build/core/verify.js +25 -25
  29. package/build/reporters/github.js +103 -51
  30. package/build/reporters/terminal.js +19 -19
  31. package/build/sources/github-pr.js +21 -21
  32. package/build/sources/local-git.js +20 -20
  33. package/build/sources/source.js +35 -1
  34. package/package.json +6 -1
  35. package/templates/agents/security.md +5 -0
  36. package/templates/command.yml +164 -0
  37. package/templates/config.jsonc +10 -0
  38. package/templates/coordinator.md +5 -3
  39. package/templates/dismiss.yml +110 -0
  40. package/templates/routing.jsonc +27 -0
  41. package/templates/scope-config.jsonc +25 -0
  42. package/templates/shared.md +12 -0
  43. package/templates/workflow.yml +58 -23
@@ -17,36 +17,30 @@ concurrency:
17
17
  jobs:
18
18
  review:
19
19
  runs-on: ubuntu-latest
20
- # Opt-in per PR: only run when the `ai-review` label is present. Remove this
21
- # line to review every PR automatically.
22
- if: contains(join(github.event.pull_request.labels.*.name, ','), 'ai-review')
20
+ env:
21
+ # Version of the published engine used for BOTH the guard and the review, so
22
+ # the guard that clears a config is the same engine that then reads it. Override
23
+ # with repo variable ECR_VERSION; pin to a specific version to freeze it.
24
+ ECR_VERSION: ${{ vars.ECR_VERSION || 'latest' }}
25
+ # Trigger policy lives in .expo-code-review/config.jsonc (review.trigger); `ecr ci`
26
+ # self-gates on it (and honors the ai-review:skip label). This coarse gate just
27
+ # avoids spinning up a runner for a PR that explicitly opted out. Uses the array
28
+ # form of contains() for an EXACT label match ("ai-review:skip" is not "ai-review").
29
+ # Prefer to gate entirely here instead? Set config trigger to "label" and replace
30
+ # the line below with, e.g.:
31
+ # if: contains(github.event.pull_request.labels.*.name, 'ai-review')
32
+ if: ${{ !contains(github.event.pull_request.labels.*.name, 'ai-review:skip') }}
23
33
  # Backstop so a stalled review fails fast instead of hanging.
24
34
  timeout-minutes: 60
25
35
  # A reviewer failure must never fail the PR's checks.
26
36
  continue-on-error: true
27
37
  steps:
28
- - uses: actions/checkout@v5
38
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
29
39
  with:
30
40
  # Shallow is enough — the reviewer gets the diff from the API (`gh`).
31
41
  fetch-depth: 1
32
42
 
33
- # SECURITY: this workflow checks out the PR's code, including
34
- # .expo-code-review/config.jsonc, whose auth.tokenEnv names the env var the CLI
35
- # forwards as the model credential. Refuse to run unless it's the expected value
36
- # (below / repo var ECR_EXPECTED_TOKEN_ENV) so a PR can't repoint it at another
37
- # secret in the runner. Keep this in sync with auth.tokenEnv in config.jsonc.
38
- - name: Guard config.jsonc tokenEnv
39
- env:
40
- EXPECTED: ${{ vars.ECR_EXPECTED_TOKEN_ENV || 'ANTHROPIC_OAUTH_API_KEY' }}
41
- run: |
42
- values=$(grep -oE '"tokenEnv"[[:space:]]*:[[:space:]]*"[A-Za-z0-9_]+"' .expo-code-review/config.jsonc | sed -E 's/.*"([A-Za-z0-9_]+)"$/\1/')
43
- count=$(printf '%s\n' "$values" | grep -c .)
44
- if [ "$count" != "1" ] || [ "$values" != "$EXPECTED" ]; then
45
- echo "::error::.expo-code-review/config.jsonc auth.tokenEnv must be \"$EXPECTED\" (found: \"${values:-none}\"). Refusing to run so a PR can't redirect which secret is forwarded to the model provider."
46
- exit 1
47
- fi
48
-
49
- - uses: actions/setup-node@v5
43
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
50
44
  with:
51
45
  node-version: 24
52
46
  # The reviewer runs via npx and never installs with a package manager, so
@@ -54,16 +48,57 @@ jobs:
54
48
  # to save an empty cache and error).
55
49
  package-manager-cache: false
56
50
 
51
+ # SECURITY: this workflow checks out the PR's code, including every
52
+ # .expo-code-review/config.jsonc + routing.jsonc, whose auth.tokenEnv names the
53
+ # env var the CLI forwards as the model credential. The canonical guard ships
54
+ # with the CLI: `ecr verify-config` sweeps every config (root + routing + all
55
+ # scopes, referenced or not) with the engine's real JSONC parser and refuses
56
+ # unless tokenEnv appears exactly once, in a ROOT-owned file, equal to the
57
+ # expected value (repo var ECR_EXPECTED_TOKEN_ENV) — so a PR can't repoint it at
58
+ # another runner secret, sneak in a JSON-escaped key, or stage an unreferenced
59
+ # scope config with its own auth. This is layer 2; layer 1 is the runtime
60
+ # ECR_EXPECTED_TOKEN_ENV lock in `ecr ci` itself, so guard/loader drift fails safe.
61
+ #
62
+ # This step MUST run BEFORE `ecr ci` (before any PR code is built or loaded).
63
+ # Only setup-node (runtime install) precedes it; running the PUBLISHED package
64
+ # via npx is safe pre-review because npx fetches @expo/code-review-cli@$ECR_VERSION
65
+ # from the registry — it never builds or executes the PR's code.
66
+ - name: Guard config tokenEnv (root + routing + all scopes)
67
+ env:
68
+ ECR_EXPECTED_TOKEN_ENV: ${{ vars.ECR_EXPECTED_TOKEN_ENV || 'ANTHROPIC_OAUTH_API_KEY' }}
69
+ run: npx --yes -p "@expo/code-review-cli@$ECR_VERSION" ecr verify-config
70
+
57
71
  - name: Run AI review
58
72
  # npx installs @expo/code-review-cli and its bundled `opencode` binary and
59
- # puts them on PATH for this process. Pin @latest to a version to freeze it.
60
- run: npx --yes -p "@expo/code-review-cli@latest" ecr ci
73
+ # puts them on PATH for this process the SAME $ECR_VERSION the guard cleared.
74
+ run: npx --yes -p "@expo/code-review-cli@$ECR_VERSION" ecr ci
61
75
  continue-on-error: true
62
76
  env:
63
77
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78
+ # Layer-1 auth lock: the CLI refuses to run when the tokenEnv it would
79
+ # honor (root config.jsonc, or routing.jsonc defaults.auth) differs from
80
+ # this — it catches what the guard step above can't. Keep it in sync
81
+ # with the guard.
82
+ ECR_EXPECTED_TOKEN_ENV: ${{ vars.ECR_EXPECTED_TOKEN_ENV || 'ANTHROPIC_OAUTH_API_KEY' }}
64
83
  # Claude Pro/Max OAuth token (from `claude setup-token`) — the env var
65
84
  # named by auth.tokenEnv in config.jsonc. Store it as a repo secret.
66
85
  # (For an API key instead, set auth.mode "api-key" and pass that key here.)
67
86
  ANTHROPIC_OAUTH_API_KEY: ${{ secrets.ANTHROPIC_OAUTH_API_KEY }}
68
87
  # Optional: override the model for every agent (uses your OpenCode login).
69
88
  REVIEWER_MODEL: ${{ vars.REVIEWER_MODEL }}
89
+
90
+ # Observability: the per-run log (token/cache/cost totals + per-pass timing +
91
+ # coverage notes) is written under .expo-code-review/.runs/ but git-ignored, so
92
+ # in CI it is otherwise ephemeral — gone when the runner is torn down. Upload it
93
+ # as an artifact so a reviewer run can be inspected after the fact (why a finding
94
+ # did/didn't surface, cache-reuse, spend). always() so it is captured even when
95
+ # the review step timed out or errored; if-no-files-found: ignore because a run
96
+ # that failed before writing the log (or a no-op skip) legitimately has no file.
97
+ - name: Upload review run log
98
+ if: always()
99
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
100
+ with:
101
+ name: review-run-log-pr${{ github.event.pull_request.number }}
102
+ path: .expo-code-review/.runs/reviews.jsonl
103
+ if-no-files-found: ignore
104
+ retention-days: 14