Skip to content

CLI Reference

Braden Seaborn edited this page Aug 26, 2026 · 1 revision

CLI Reference

Complete reference for the ste100 command. Everything here also works as python ste_lint.py from a source checkout, or python -m ste100 -- see Installation.

Synopsis

ste100 [PATH ...] [--format {text,json}] [--profile PROFILE]
       [--config CONFIG] [--preset PRESET] [--root ROOT] [--version]
       [--fix] [--explain RULE_ID] [--baseline PATH] [--stats]
       [--today YYYY-MM-DD]

With no arguments, ste100 lints every .md and .csv file under the current directory (minus never_lint paths -- see Configuration), using the resolved config's default preset.

Positional: paths

ste100                     # whole project tree
ste100 docs/                # everything under docs/
ste100 docs/ README.md      # a directory plus one specific file

A file named explicitly on the command line gets linted even when it matches a never_lint entry. Only automatic directory discovery honors never_lint.

--format {text,json}

Default: text. Reach for --format json when a script, CI dashboard, or another tool needs to consume findings as data, not as text for a person to read in a terminal.

ste100 --format json docs/ > report.json

The JSON object shape, verified by running the tool:

{
  "schema_version": 1,
  "run_at": "2026-08-25T14:02:32Z",
  "summary": {
    "files": 1,
    "errors": 12,
    "warnings": 3,
    "review": 9,
    "smell_density": 5.0,
    "ari_grade": 10.96,
    "passive_ratio": 0.6667,
    "budget_violations": 1
  },
  "findings": [
    {
      "file": "sample.md",
      "line": 3,
      "column": 21,
      "rule": "STE-T1-SUB-0104",
      "test": "T1",
      "severity": "error",
      "message": "Replaceable: 'utilize' -> 'use'.",
      "excerpt": "...perator should utilize the correct pr...",
      "suggestion": "use",
      "source": "vale_redhat.simple_words"
    }
  ]
}

suggestion is present only on findings that carry a concrete replacement, which today means T1 findings. The four summary metrics are documented under Reading the output below.

--profile PROFILE

Overrides profile detection and forces every targeted file to use the named profile, regardless of path glob or a first-line <!-- lint-profile: NAME --> comment. Use this for a one-off check against a stricter or looser rule set than the file's detected profile applies by default.

# Check a design doc as if it were formal requirements text
ste100 --profile spec docs/design/notes.md

See Configuration for the full profile-detection order and what each shipped profile does.

--config CONFIG

Path to a specific JSON config file. This is the highest-priority way to choose configuration -- it overrides --preset and any project-local ste100.json found by directory walk.

ste100 --config path/to/my-config.json docs/

Reach for this when you keep a config that does not live at the project root, or run the same config against unrelated repositories from one script.

--preset PRESET

Selects one of the presets that ship inside the package: ste100 --preset default or ste100 --preset veistra. See Configuration for what each preset is for.

ste100 --preset default docs/

Reach for this to use a shipped preset without writing a config file, or to pin CI to a known preset even if a project-local ste100.json later gets added by mistake.

--root ROOT

Sets the project root used to compute each finding's reported path. Default: the current working directory. path_globs in profile detection are also matched against paths measured from this root.

ste100 --root /repo docs/guide.md

Verified: running from a different directory with --root run pointed at the directory containing sample.md reports the finding as sample.md, not run/sample.md. Use this when invoking the linter from outside the tree being linted -- for example, a build script that runs from a shared tools directory.

--version

Prints the installed version and exits.

$ ste100 --version
ste100 0.1.0

--fix

Rewrites Markdown files in place, applying only unambiguous T1 substitutions. See The --fix safety envelope below for what "unambiguous" means here. --fix does not change the exit-code rule -- findings left after fixing still decide the exit code.

ste100 --fix docs/

Reach for this after a T1 review pass, to apply the easy replacements in bulk, then re-run without --fix to review what is left.

--explain RULE_ID

Prints what a rule ID means and the rule's source, then exits -- no files are linted. Useful when a finding's message alone is not enough context, or when deciding whether to override a rule's severity.

$ ste100 --explain STE-T1-SUB-0104
STE-T1-SUB-0104: {
  "pattern": "utilize",
  "suggestion": "use",
  "alts": [
    "use"
  ],
  "source": "vale_redhat.simple_words",
  "id": "STE-T1-SUB-0104"
}
$ ste100 --explain STE-T3-HDG-0106
STE-T3-HDG-0106: {
  "id": "STE-T3-HDG-0106",
  "pattern": "should"
}
$ ste100 --explain STE-BUD-0001
STE-BUD-0001: Budget: sentence exceeds the profile's word budget (spec §9).

An unrecognized rule ID prints Unknown rule id: <RULE_ID> and exits 2, the same "tool failure" exit code as a bad config path -- an unresolvable rule ID is an input error, not a normal empty result.

--baseline PATH

Points at a JSON report from an earlier --format json run whose findings are suppressed on this run. This is how an existing codebase adopts the linter without fixing years of prose first. See Baselines in Configuration for the full adoption workflow and the exact suppression semantics (occurrence-count matching, not presence matching; line numbers excluded from the match key).

ste100 --format json docs/ > .ste100-baseline.json
ste100 --baseline .ste100-baseline.json docs/

--stats

Also reports review-tier findings, hidden by default because they are advisory, not defects. Does not affect the exit code -- review findings never fail a run.

ste100 --stats docs/guide.md

Reach for this while developing a config or profile, or when doing a deeper editorial pass beyond what CI enforces.

--today YYYY-MM-DD

Overrides "today" for CSV review-date staleness checks (csv_integrity tests that compare a review_by column against the current date). Useful for reproducible test fixtures and CI runs that need deterministic output regardless of when they execute.

ste100 --today 2026-01-01 registry/decisions.csv

Exit codes

Code Meaning
0 Lint ran cleanly; no error-tier findings.
1 Lint ran cleanly; one or more error-tier findings exist.
2 The tool itself failed -- bad config, missing preset, unreadable file, an internal crash, or nothing matched the given paths.

Distinguishing 1 from 2 matters in CI. Both codes are nonzero, but they signal two opposite problems:

  • Exit 1 -- the documentation needs work; a failing pipeline is correct here.
  • Exit 2 -- the linter did not run: a misconfigured --config path, a typo in --preset, or a permissions problem. The pipeline failure has no bearing on the docs.

A CI script that treats both exit codes the same way hides a tool failure inside what reads as an ordinary lint failure. A person then spends time editing prose the linter never checked.

Verified failure modes, each confirmed to exit 2:

$ ste100 --preset nope docs/
tool failure loading config/data: unknown preset 'nope'; available: default, veistra

$ ste100 --config missing.json docs/
tool failure loading config/data: config file not found: missing.json

$ ste100 empty-directory/
No files to lint.

Reading the output

A single text-format finding line:

sample.md:3:21 ERROR T1 STE-T1-SUB-0104 -- Replaceable: 'utilize' -> 'use'.
    ...perator should utilize the correct pr...
Field Value here Meaning
file sample.md Path measured from --root (or cwd); uses /.
line:column 3:21 1-indexed position of the finding in the source file.
Severity ERROR One of ERROR, WARNING, REVIEW -- see Configuration's severity precedence for how a rule lands in a tier.
Test T1 Which of the six tests (or structural) produced the finding.
Rule ID STE-T1-SUB-0104 Stable identifier; look it up with --explain.
Message Replaceable: 'utilize' -> 'use'. Human-readable description, including the fix text where one exists.
Excerpt (second line, indented) Surrounding text, truncated with ...; makes the finding legible without opening the file.

The run summary line above the findings:

ste100: 1 files, 12 errors, 3 warnings, 9 review
smell_density=5.0 ari_grade=10.96 passive_ratio=0.6667 budget_violations=1
  • smell_density -- findings per 100 words across the run. A single scale-independent number for comparing writing quality across files or runs of differing length.
  • ari_grade -- the Automated Readability Index for the linted corpus, a US grade-level estimate computed from characters-per-word and words-per-sentence. Lower is simpler prose.
  • passive_ratio -- the fraction of sentences flagged as passive voice (0.6667 above means two of three sentences).
  • budget_violations -- count of sentence/paragraph/whole-file length budgets exceeded.

review-tier findings are counted in the summary (9 review above) even when --stats is not passed to print them. The summary reflects the full run.

The --fix safety envelope

--fix is conservative by design. Verified against src/ste100/fixer.py and by running --fix against test fixtures:

  • Only T1 (Replaceable) findings are ever auto-fixed. No other test applies a substitution.
  • Only when the rule has a single candidate replacement. 146 of the 424 substitution rules list more than one alts entry, for example absentnone / not here. When a rule has more than one, --fix reports the finding and leaves the text unchanged -- picking between choices needs human judgement.
  • Never deletes text. 14 rules carry an empty suggestion, meaning: remove this phrase (be advised, in the process of, inter alia, and 11 others). Deleting words changes the grammar of the surrounding sentence -- the fixer's source comment gives a worked example of the corruption this used to cause ("the operator selects the type of report""the operator selects the of report"). These 14 are reported and never auto-applied.
  • Never touches a line containing inline code, a link, or other masked spans, and skips fenced code blocks. Verified: a file containing `utilize` inside a code span was linted (the finding was still reported), but the backtick-quoted text stayed byte-for-byte unchanged after --fix.
  • Preserves the file's line endings. The fixer writes back with newline="\n" set explicitly, not Python's newline-translation default. --fix cannot change a file's line endings when it substitutes a word.

A verified before/after, run against a fixture with an ordinary case, a code span, and a curly-quote substitution:

# before
The operator will utilize the tool.
Run `utilize` in code, which must not change.
The operator didn't utilize the manual and/or the guide.

# after --fix
The operator will use the tool.
Run `utilize` in code, which must not change.
The operator didn't use the manual … or … or both the guide.

The code-span utilize is untouched; the two plain-text instances are fixed, including the non-ASCII curly apostrophe and ellipsis characters (see the UTF-8 note in Installation).

Related pages

Clone this wiki locally