first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
# `cli`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`@suaveplan/linter/cli` is the pure-function CLI entry point. It parses an `argv` array,
runs the unified orchestrator, and returns a resolved exit code. Unlike the upstream
package, it never calls `process.exit` — the shipped binary (`suaveplan-lint`) is a
one-line wrapper that calls `runCli(process.argv.slice(2))` and then exits with the
returned code. Keeping the CLI pure means tests can assert on the resolved code without
the test process terminating mid-suite, and other tools (pre-commit hooks, IDE
integrations) can embed the CLI logic directly.
## Features
- Hand-rolled argv parser — no `commander` dependency for a binary with five flags.
- Recognised flags: `--help` / `-h`, `--fix` / `--write`, `--unified`,
`--markdown-only`, `--cwd <path>`. Anything else starting with `-` raises
`LinterError(LINTER.UNKNOWN_FLAG)` (rather than silently treating it as a positional
pattern, which is how the upstream package quietly swallowed typos).
- Positional arguments accumulate into `patterns` so multiple globs are supported.
- Injectable `out: (line: string) => void` sink for tests — defaults to `console.log`.
- Translates `runUnifiedLint`'s `LintReport.success` into exit code 0 / 1, prints a
concise summary line, and catches `LinterError` to surface the code and message in
the summary.
## Usage: Basic
```ts
import { runCli } from "@suaveplan/linter/cli";
const { code } = await runCli(process.argv.slice(2));
process.exit(code);
```
## Usage: Advanced
Embed the CLI inside another tool with full adapter control:
```ts
import { runCli } from "@suaveplan/linter/cli";
import type { CommandRunner } from "@suaveplan/linter";
const lines: string[] = [];
const out = (line: string) => lines.push(line);
const runner: CommandRunner = {
async run() {
return { exitCode: 0, stdout: "", stderr: "" };
},
};
const { code } = await runCli(["--fix", "docs/*.md"], {
out,
runner,
cwd: "/tmp/repo",
});
// code === 0, lines contains the summary
```
## API Reference
### `runCli(argv, options?): Promise<{ code: number }>`
- `argv: ReadonlyArray<string>` — required. The argv slice (typically
`process.argv.slice(2)`).
- `options.out?: (line: string) => void` — sink for help / summary / error lines.
- `options.runner?: CommandRunner` — forwarded to the orchestrator.
- `options.fs?: FsAdapter`, `options.glob?: GlobAdapter`, `options.clock?: ClockLike`
— forwarded to the orchestrator.
Returns `{ code: 0 }` on success or `{ code: 1 }` on failure. Throws
`LinterError(LINTER.UNKNOWN_FLAG)` for an unrecognised flag.
## Implementation Notes
- The parser uses a single forward sweep; flags are recognised by exact match and
positional args are anything that doesn't start with `-` (or `--cwd <path>` where
the next token is consumed as the cwd value). This is deliberately simple — the
scope is five flags, not a general argv parser.
- `--unified` is accepted but no-op; the orchestrator runs both lanes by default and
the flag exists for symmetry with the upstream CLI's documented surface.
- The success summary names the count of markdown files touched and the count that
were fixed. The failure summary names the biome exit code so consumers can grep CI
output for the underlying cause.
- `LinterError` is caught here (not at the orchestrator) because the CLI is the layer
that maps domain errors to exit codes. Letting the error propagate out of `runCli`
would force every consumer to write the same translation.
- The CLI does NOT print the markdown reports inline. Consumers that want per-file
output can call `runUnifiedLint` directly; the CLI summary is the "CI-friendly"
shape.