Skip to content

fix(cli): CLI flag, run-folder and inspect edges, and two GitHub Action edges - #264

Merged
dgesteves merged 4 commits into
mainfrom
fix/os2-59-cli-polish
Oct 30, 2026
Merged

dgesteves merged 4 commits into
mainfrom
fix/os2-59-cli-polish

Conversation

@dgesteves

@dgesteves dgesteves commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Part of the OS2-59 polish bundle: the CLI and GitHub Action area. The message and ESLint area is #265, the package and contributor area #266. None of them share a file.

What was wrong

  • check --max-warnings=-1 (ESLint's "no limit") failed a clean file with Too many warnings (0, max -1)., and --max-warnings= from an unset variable was read as 0: Number('') is 0 and passes Number.isInteger.
  • Every command accepted every option: check . --out report.json wrote no file and exited as if it had, and check . --explain and inspect --max-warnings 0 were ignored.
  • check danger-zone.tsx run from examples/shadcn-demo/app/settings (no config, components.json or package.json there) took that folder as the root, found no design system and printed No problems in 1 file. with exit 0. From the demo's root the same file has errors. The Claude Code hook and the MCP server already walk up to the project.
  • inspect did not say that color tokens were missing (only check did, after its findings), and the root check --require-design-system error did not say which project lacked what.
  • Ctrl+D at one of init's questions printed an AbortError: Aborted with Ctrl+D stack trace.
  • check --format github titled annotations no-hardcoded-color, the Action onsystem: no-hardcoded-color.
  • The Action's parseDiff read any line starting with +++ as a file header, so an added line ++ counter; (shown as +++ counter;) reset the file and its later hunks were dropped: findings there were filed as "not reported".
  • In a container: job, git refuses the workspace as another user's ("dubious ownership"), and the Action failed every pull request with advice to check out with fetch-depth: 0.

What changed

  • .changeset/cli-and-action-edges.md is a minor bump and opens with an Upgrading note: an option of another command, an empty --max-warnings= and a run from a folder without a project marker can each fail a CI run that passed before, and it says what to do for each.
  • src/cli.ts
    • --max-warnings takes a whole number; a negative one is no limit. Anything else (empty, 1.5, 0x10, many) exits 2 with one line. --help writes it --max-warnings=-1, as --max-warnings -1 is refused by Node's parser as ambiguous.
    • The options --help gives to some commands only (check:, inspect:, export:, init:, serve: in its text) exit 2 on any other command: check: --out is an option of export only. Run onsystem --help for usage. serve instead says serve: -y is an option of init only, so it is ignored. on stderr and starts, so a stale flag in an MCP client's args does not leave the client with a broken server. The table is next to the parser, and test/cli-docs.test.ts runs every option against every command --help does not give it to, so the table and --help cannot drift apart.
    • check, inspect, export and init, run from a folder with none of the project markers, use the nearest folder above with one as --root (relative, so the commands the hints print still work from there). Not when --root, --config, --components, --tokens or --docs is given, not when the folder has a baseline, not when it has a components/ui or src/components/ui folder the project above would not read (it is not the project's src or app folder, and the project has no components.json or config: a design system with no package.json below a stray one), not from the home folder, never the home folder or the filesystem root (compared as findProjectRoot compares them, without case on Windows), and never for serve. I chose the nearest marker rather than findProjectRoot's workspace root, so a run from apps/web/app uses apps/web, the same root and baseline as a run from apps/web.
    • When it walked up, check says so once: Checking the project in ../.., first in pretty output and on stderr with --format json, sarif or github, so machine output still parses (not with --quiet-without-design-system, the hook's flag). The single-project notice names it (No design system found in ../.. (…)), and at a monorepo root the notices and the --require-design-system error name projects relative to the working directory, as the files are named: from tools/scripts the root's own files were said to be in ..
    • inspect prints No color tokens found: check will not run no-hardcoded-color. See …/configuration (or the same for missing components) after the tokens. At a monorepo root, each project gets a missing line. The root --require-design-system error adds ; no color tokens in apps/admin, apps/web and packages/ui. The new lines are not under Warnings, so the corpus's inspect counts do not move.
    • An AbortError from one of init's questions (Ctrl+D or Ctrl+C) prints init: cancelled; nothing after that question was done. on a line of its own, and exits 130. Other commands keep the generic handling.
  • src/init.ts: run from a folder below the project, the baseline line names ../../onsystem.baseline.json, as the config line already named ../../onsystem.config.json.
  • src/workspace.ts: samePath is exported for the walk-up.
  • src/lint/format.ts: --format github titles annotations onsystem: <rule>, as action/check.mjs does.
  • action/check.mjs
    • parseDiff counts each hunk's removed and added lines from its header and reads a +++ line as a header only when the hunk is done.
    • Its git calls get -c safe.directory=<dir> for each folder from the working directory up to GITHUB_WORKSPACE, one of which is the repository top. Nothing is added outside Actions, or for a folder outside the workspace. When git still refuses, the error suggests adding safe.directory instead of fetch-depth: 0.
    • The CLI it runs gets the same entries as GIT_CONFIG_COUNT / GIT_CONFIG_KEY_<n> / GIT_CONFIG_VALUE_<n>, after any the job already set, so the CLI's git check-ignore reads a container job's checkout and git-ignored files are left out there too.
    • When the CLI exits 2, the error annotation gives the first stderr line that is not Checking the project in …, so from a working-directory with no project marker it still says why check could not run.
  • docs/cli.md: the --root row and a paragraph on the walk-up, the exit-2 list (an option of another command, an empty --max-warnings), --max-warnings=-1, and what serve does with another command's option.

How it was tested

New tests fail on main and pass here:

  • test/cli-edges.test.ts: --max-warnings=-1 on a clean file and on 11 warnings; '', ' ', many, 1.5 and 0x10; options of other commands (check --out writes no file); serve given -y and --format twice warns once for each and answers listTools over an in-memory transport; Ctrl+D through a real readline terminal interface at init's first question (exit 130, message on its own line, no config written); check danger-zone.tsx from app/settings reports the same 11 findings as from the demo's root, with Checking the project in ../.. on stderr for JSON and first on stdout for pretty, and not from the root or with --quiet-without-design-system; --root ., a baseline or a components/ui / src/components/ui folder keeps the folder despite a stray package.json above; the demo laid out as a Next.js src project and as a React Router app project (components.json and tsconfig paths) reports the same findings from src or app, and from the settings folder, as from the root (with the folder kept as the root, the built CLI gave 4 errors and 3 warnings from src, and 1 error with "No color tokens found" from app, against 8 errors and 3 warnings); run from inside the home folder or from it, the walk never reaches it or goes above it (with the home folder spelled in capitals on Windows); the notices say in ../.. from a subfolder of one project and from tools/scripts of a monorepo; init -y from app/settings prints Wrote ../../onsystem.config.json. and Baseline: 1 finding in 1 file → ../../onsystem.baseline.json; inspect naming missing color tokens.
  • test/cli-docs.test.ts: every option against every command --help does not give it to: exit 2 with the error, or for serve exit 0 with the warning.
  • test/action.test.ts: a diff with +++ counter;, --- removed and \ No newline at end of file inside hunks; configEnv numbering after an existing GIT_CONFIG_COUNT; and an end-to-end Action run on a repository git takes for another user's (GIT_TEST_ASSUME_DIFFERENT_OWNER=1, with no system or global git config), inside and outside GITHUB_WORKSPACE. The stand-in CLI now leaves out what git check-ignore reports, as the CLI does, and in that run a file in .gitignore and one in a core.excludesFile set through GIT_CONFIG_COUNT=1 are both left out: without the variables, or with them numbered from 0, they are checked. From a working-directory with no project marker, the stand-in, like the CLI, says Checking the project in ../.. before the reason it exits 2, and the annotation gives the reason.
  • Updated: test/cli.test.ts (annotation title), test/workspace.test.ts (the missing line and the root error).

Gate on this branch, up to date with main: pnpm install --frozen-lockfile --prefer-offline, pnpm format:check, pnpm lint, pnpm typecheck, pnpm test (112 files passed and 1 skipped; 1,459 tests passed, 3 skipped), pnpm build and pnpm smoke all pass.

I also ran the built CLI from examples/shadcn-demo/app/settings: check danger-zone.tsx prints Checking the project in ../.. then the findings (exit 1), check danger-zone.tsx --max-warnings -1 gives Node's "argument is ambiguous" message (exit 2), and onsystem -y --format json --no-watch prints the two serve: lines and ready on stdio. The Action's run() with the built CLI, from a working-directory with no marker under a repository package.json, annotates onsystem check exited with 2: check --require-design-system: No design system found in ../.. (…); before this fix it annotated Checking the project in ../...

Left for later (outside this bundle's files)

  • docs/troubleshooting.md: inspect now prints "No color tokens found", and the root --require-design-system case.
  • src/lint/sarif.ts: helpUri still points to /rules#id, not /rules/<id>.
  • docs/ci.md: say that --format github shows at most 10 annotations of each kind per step, and point to the Action's job summary.
  • init: a warning when the token files it found hold no colors.

Roadmap item: OS2-59 (os-bugs-edges/os-cli-flag-polish, os-arch/os-error-message-polish (h), os-adopt-monorepo/polish-cli-ci-nits (a, f), os-adopt-monorepo/tokens-missing-not-surfaced, os-bugs-edges/os-parsediff-plusplus-line, os-bugs-edges/os-action-git-refuses-checkout).

…on edges

- `--max-warnings=-1` is no limit, as in ESLint; an empty or non-integer
  value exits 2 instead of being read as 0.
- An option `--help` gives to other commands exits 2 instead of being
  ignored (`check . --out report.json` wrote no file). The drift test
  runs every such pair against `--help`.
- From a folder with no config, components.json, package.json or
  project.json (`app/settings`), check, inspect, export and init work on
  the nearest folder above with one, as the Claude Code hook does.
- `inspect` says when color tokens or components are missing, and the
  root `--require-design-system` error names what each project lacks.
- Ctrl+D or Ctrl+C at an `init` question says it cancelled (exit 130)
  instead of printing an AbortError stack.
- `--format github` titles annotations `onsystem: <rule>`, as the
  Action does.
- The Action counts each hunk's lines, so an added `++ ` line is not
  read as a file header, and passes the workspace folders as git
  safe.directory, so a container job's checkout is read.

Roadmap item OS2-59.
…res other commands' options

- The changeset is minor, with an Upgrading note: an option of another
  command, an empty --max-warnings= and a run from a subfolder can each
  fail a CI run that passed before.
- check names the project it walked up to once ("Checking the project
  in ../.."), on stderr with --format json, sarif or github. The
  single-project notice names it too, and at a monorepo root the
  notices name projects relative to the working directory, not "in .".
- The walk-up compares with the home folder as findProjectRoot does
  (case-insensitive on Windows), does not start above it when run from
  it, and keeps a folder with components/ui or src/components/ui.
- serve says on stderr that it ignores an option of another command and
  starts, so a stale flag in an MCP client's args does not break it.
- The cancel message is init's only, on a line of its own.
- The Action passes its safe.directory entries to the CLI it runs as
  GIT_CONFIG_* variables, after any already set, so the CLI's
  git check-ignore reads a container job's checkout.
- --help and docs/cli.md write --max-warnings=-1; docs/cli.md covers the
  walk-up, the new exit-2 cases and serve. init names the baseline
  relative to the working directory, as it names the config.
…s why check could not run

A run from src in a src layout, or from app in a React Router one, kept that folder as the
root because it holds components/ui, and lost findings: 4 errors and 3 warnings from src, and
1 error with no color tokens from app, against 8 errors and 3 warnings from the root. The
folder's own components/ui now keeps it as the root only when the project above would not read
them: it is not the project's src or app folder, and the project has no components.json or
config. A design system with no package.json below a stray one is still its own root.

The Action's error annotation took "Checking the project in ../.." for the reason the CLI
exited 2, when working-directory has no project marker. It now takes the first other line.
@dgesteves
dgesteves merged commit 808a231 into main Oct 30, 2026
19 checks passed
@dgesteves
dgesteves deleted the fix/os2-59-cli-polish branch October 30, 2026 19:32
@dgesteves-release dgesteves-release Bot mentioned this pull request Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant