feat(cli): add ci command for managing CI#955
Conversation
`install` bundles CI setup with skills, hooks, and the package script, so there was no focused way to set up, tune, or upgrade React Doctor in CI on its own. `react-doctor ci <install|upgrade|config>` is that home. - `ci install` detects the provider (GitHub Actions or GitLab CI), scaffolds a pull-request-scanning workflow, bakes a gate from the flags, and can open a PR with `--pr`. - `ci config` walks the gate / scope / reporting settings (interactively or via flags) with a plain-language recap, editing in place only when the file round-trips to what React Doctor generates so a hand-edited workflow is never clobbered. - `ci upgrade` bumps the action to its current floating major. GitHub Actions is fully supported; GitLab CI is a gate-only scaffold. Reuses the existing workflow template, PR-opening flow, branch detection, and gate guards; `install`'s behavior is unchanged. Telemetry: ci.scaffolded / ci.configured / ci.upgraded (anonymized). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit: |
- detect: check the git remote host before the generic on-disk CI files, so a GitHub repo carrying a stray/legacy `.gitlab-ci.yml` is no longer misread as GitLab. - gitlab: parse the gate off React Doctor's own scan line, not file-wide, so a merged pipeline running other jobs with `--blocking`/`--scope` can't be mistaken for the gate (mirrors the GitHub provider's anchored parse). - ci install: when a GitHub workflow already exists, say the gate flags weren't applied and point at `ci config`, instead of silently ignoring them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A `#` comment that mentions `millionco/react-doctor@` (GitHub) or `react-doctor … --blocking` (GitLab) could be read as the action step, so the real `with:` block / scan flags were skipped and `ci config` fell back to the advisory defaults. Anchor the GitHub parse to a `- uses:` line and the GitLab parse to a `- ` script item (stripping any trailing comment) so only the real step is read. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ffolds `ci config` previously rebuilt the whole workflow and wrote only when the file was byte-identical to a React Doctor scaffold, so a workflow with extra steps/jobs/comments, other action inputs, or generated by another version got a paste snippet instead of an edit. Now the GitHub provider edits the workflow via the `yaml` AST: it finds the React Doctor step (matching `millionco/react-doctor@…`, any ref/SHA pin) and sets only the managed gate keys on its `with:` map, deleting keys that return to the action default. Comments, the action ref, and other inputs (directory / project / node-version / version) are preserved. A pristine scaffold still takes the clean template-rebuild path; only a file with no React Doctor step (or unparseable YAML) is refused. The GitLab provider splices the gate flags on its scan line in place. `yaml` is added as a dependency and inlined into the bundle, so end users get no extra install. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two GitLab gaps from the in-place editor: - `ci config` treated any existing `.gitlab-ci.yml` as a React Doctor workflow. A file with no scan job reported the advisory defaults as "current" and blamed a failed edit on "customization". Add a `containsReactDoctor` provider check so `ci config` says the file has no React Doctor job and points at `ci install`. - The GitLab gate splice only replaced existing `--blocking`/`--scope` tokens, so a flag the user had removed was silently skipped while the recap claimed it applied. Upsert the flags (add when missing) and anchor the scan line on the `react-doctor@` npx spec so a flagless line is still found. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ention `isScanLine` matched any `- …react-doctor@…` script item, so a setup step like `- npm install react-doctor@latest` could be read or edited as the scan command. Match an actual run instead — a runner (`npx`/`pnpm dlx`/`yarn dlx`/`bunx`) or a bare `react-doctor` command — so install steps and comments are skipped and the real scan line is found. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`ci upgrade` claimed the workflow "already uses @v2" whenever nothing changed, including exact-tag and SHA-pinned refs that deliberately skip the floating `@v1`→`@v2` bump. Report that there's no floating `@v1` ref to upgrade instead of asserting a version it may not be on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… block scripts - GitHub: `ci config` / `ci upgrade` now find the React Doctor action step in any `.github/workflows/*.yml` (preferring the canonical `react-doctor.yml`), not just `react-doctor.yml`, so a step added to an existing CI workflow is managed in place. `ci install` treats the action existing anywhere as "already configured" and won't add a duplicate workflow. - GitLab: the scan-line matcher now matches the React Doctor run whether it's a `- npx react-doctor@…` item or a command inside a multiline `script: - |` block scalar, while still excluding the `react-doctor:` job key, comments, and `npm install react-doctor` steps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e58e01f. Configure here.
| .option("--color", "force colored output") | ||
| .option("--no-color", "disable colored output (also honors NO_COLOR)") | ||
| .addHelpText("after", renderCiHelpEpilog) | ||
| .action((_options, command) => ciInstallAction(command.optsWithGlobals())); |
There was a problem hiding this comment.
Root scan flags affect CI
Medium Severity
ci install and ci config merge options with command.optsWithGlobals(), sharing --blocking and --scope with the default scan command. Flags given before the ci subcommand (e.g. react-doctor --scope full ci install or a shell alias that always passes scan flags) are applied to the CI gate, not only to local scans, which can silently bake the wrong blocking level or PR scope into the workflow.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit e58e01f. Configure here.


Why
react-doctor installbundles CI setup together with skills, hooks, and the package script, so there was no focused way to set up, tune, or upgrade React Doctor in CI on its own. This addsreact-doctor ci <install|upgrade|config>as the dedicated home for CI, while leavinginstallunchanged.ci configedits any workflow that contains the React Doctor action — surgically, in place, preserving your other steps, jobs, inputs, and comments — rather than only a pristine scaffold.Usage
Set up CI in one command — auto-detects the provider (GitHub Actions or GitLab CI), scaffolds an advisory workflow that scans every PR:
Tune the gate later — interactively, or with flags for scripts/CI:
It edits your real workflow in place. Given a hand-customized file:
ci config --blocking error --scope full --no-commenttouches only the gate, preserving the rest:Bake the gate at install time, or open a PR with the change:
What changed
cicommand group (commands/ci.ts, wired incli/index.ts) withinstall,config,upgrade, a help epilog, and flag-strip routing.cli/utils/ci/):githubActionsProvider(full support) +gitlabCiProvider(gate-only scaffold), auto-detected from the git remote host then on-disk CI files,--providerto override.ci configedits in place. GitHub uses theyamlAST to set only the managed gate keys (blocking/scope/comment/review-comments/commit-status) on the React Doctor step'swith:map — preserving comments, the action ref (incl. SHA pins), and other inputs (directory/project/node-version/version); keys that return to the action default are removed. A pristine scaffold still gets the clean template rebuild; only a file with no React Doctor step is refused. GitLab splices the gate flags on its scan line.yamlis added and inlined into the bundle (no extra user install).ci.scaffolded/ci.configured/ci.upgraded(anonymized: provider id, mode, gate enums, booleans).installunchanged; its help points toci. Noaction.yml/ JSON-report /schemaVersionchange. Minor changeset.Test plan
pnpm --filter react-doctor run typecheck·pnpm lint·pnpm format:check— cleanpnpm --filter react-doctor exec vp test run tests/ci-providers.test.ts tests/manage-ci.test.ts tests/strip-unknown-cli-flags.test.ts tests/install-github-workflow.test.ts tests/install-react-doctor.test.ts tests/open-workflow-pull-request.test.ts— 113 passing, incl. surgical edit preserving extra steps/inputs/comments, no-with:insertion, remove-on-default, flow-stylewith: {…}, no-step → snippet, GitLab splice in a larger pipeline,--baseadd/remove, provider detection, flag routingreact-doctorsuite green in isolation (the only workspace failures are pre-existing: the.env*-gitignore-trap tests incore, adeslop-jsfixture, and flaky e2ecli-and-outputunder parallel load)yamlis bundled intodist/cli.js, and smoke-tested the built bin end to end (ci install,ci configon a customized workflow, GitLab scaffold)🤖 Generated with Claude Code
Note
Medium Risk
The CLI writes and rewrites real CI config files (workflows and
.gitlab-ci.yml); mistakes in YAML editing could break pipelines, though surgical edits and broad tests reduce that risk.Overview
Adds
react-doctor ciwithinstall,config, andupgradeas the dedicated way to set up and tune React Doctor in CI (the bundledinstallflow is unchanged).ci installauto-detects GitHub Actions or GitLab CI, scaffolds an advisory PR-scan workflow, accepts gate flags (--blocking,--scope, PR reporting toggles), and optional--prto open a setup PR. GitHub gets full workflow + action inputs; GitLab is a merge-request job scaffold only (blocking/scope on the CLI).ci configreads the current gate from disk, applies flags or interactive prompts, then edits workflows in place—GitHub viayamlAST on the React Doctor step’swith:map (preserving other steps, pins, and custom inputs); GitLab by splicing flags on the scan line. Customized files get surgical edits; missing React Doctor wiring falls back to a paste snippet.ci upgradebumps a floating@v1GitHub Action ref to@v2. Provider resolution uses existing workflow,originremote, then on-disk CI files. README CI steps now point atci install;yamlis bundled; telemetry addsci.scaffolded/ci.configured/ci.upgraded.Reviewed by Cursor Bugbot for commit e58e01f. Bugbot is set up for automated code reviews on this repo. Configure here.