Zum Inhalt springen

Analyze a solution or project with the CodeCharter CLI

Full reference for codecharter analyze, options, diff-only and baseline modes, platform profiles, and exit codes.

codecharter analyze [path] [options]

[path] is the path to a .sln, .slnx, or .csproj file. When omitted, or when it points at an existing directory, that directory (the working directory when omitted) is searched recursively (skipping bin, obj, .git, and node_modules) and the shallowest .sln/.slnx is analyzed, falling back to a .csproj when no solution exists. If several candidates are found the shallowest, alphabetically first one is used and the others are listed; pass a path to a specific file to override. If nothing is found, analyze stops with exit code 2.

Examples

# Auto-discover the solution under the current directory
codecharter analyze

# Default run against an explicit solution, output to console
codecharter analyze MySolution.sln

# JSON output to a file
codecharter analyze MySolution.sln --output json --output-file findings.json

# Only errors as build blockers, GitHub annotations for PR inline comments
codecharter analyze MySolution.sln --output github-annotations --fail-on error

# Add a rule directory to the sources the config declares
codecharter analyze MySolution.sln --rules ./shared-rules

# Run only the named directories, ignoring the config's sources
codecharter analyze MySolution.sln --rules ./shared-rules --rules-only

# Diff-only: report and gate on lines changed against the base branch
codecharter analyze MySolution.sln --git-ref origin/main..HEAD --fail-on error

# Diff-only from a pre-computed diff (or stdin with '-')
git diff origin/main..HEAD | codecharter analyze MySolution.sln --diff -

Options

Option Default Description
--rules <dir> not set Directory containing .ccr or .cgr rule files, relative to the current directory. Repeatable. Runs alongside the sources .codecharter/config.yml declares. See Rule sources.
--rules-only off Run only the --rules directories and ignore the profiles: and rules: the config declares. Needs at least one --rules.
--output <format> console Output format: console, json, sarif, github-annotations. Repeatable with format:path for multiple outputs in one run. See Output Formats.
--output-file <path> stdout Write output to a file instead of stdout. Only valid for the single-output form; cannot be combined with format:path.
--severity <level> info Minimum severity shown in output: info, warn, error.
--fail-on <level> not set Exit code 1 for findings at or above this severity: info, warn, error. Without this flag, any finding shown at the current --severity threshold causes exit code 1. Pass --fail-on error to gate only on errors.
--no-color off Disable ANSI color codes in console output.
--verbose off Detailed diagnostic logs.
--progress off Live progress on stderr while analyzing. Shows i/N plus a percentage per project and per rule; the solution-load phase shows a spinner only (no measurable sub-progress). In a terminal it updates a single spinner line; when stderr is redirected (as in CI) it prints throttled progress lines. Can be combined with --verbose.
--workspace <dir> not set Run as if started in this workspace, like git -C. A file anchors its directory. Global: every workspace-bound command takes it. See Choosing the workspace.
--workspace-root <path> not set Path prefix to strip from file paths. Useful for github-annotations.
--diff <path\|-> not set Diff-only mode: report and gate on only the lines added or modified by this unified diff. Pass a diff file path, or - to read the diff from stdin. Mutually exclusive with --git-ref.
--git-ref <range> not set Diff-only mode: report and gate on only the lines changed in this git ref range (e.g. origin/main..HEAD); runs git diff <range> --unified=0. A two-dot range is diffed from the merge base, an explicit three-dot range is used as typed, and a bare ref or SHA keeps git's meaning of "working tree against that commit". Mutually exclusive with --diff.
--diff-context off With --diff/--git-ref, also report findings in the touched files whose line is not among the changed lines — a class-level finding such as a newly introduced God-class, for example. They join the reported findings and the --fail-on gate. Without the flag they are only counted on stderr.
--write-baseline <file> not set Run the analysis and write the current findings' fingerprints to this baseline file, then exit 0 without reporting findings or gating. See Baseline gate.
--baseline <file> not set Suppress findings recorded in this baseline file so output and --fail-on apply only to new findings. A missing file analyzes everything (with a warning). See Baseline gate.
--telemetry off Opt-in: after the run, send one anonymous usage event (tool name, latency bucket, per-rule finding counts, hashed workspace id) to the CodeCharter endpoint. Off by default; no source, file paths, or code are ever sent. Best-effort — never affects the run.
--telemetry-endpoint <url> portal Override the telemetry ingestion endpoint.
--license <path> not set Path to the license file, overriding the default search locations. Every run passes the license check. See License File.

Choosing the workspace

Every command that works on a workspace (analyze, graph, coverage, baseline, config, restore, update, req and the others) accepts the global option --workspace <dir>. The command then runs as if it had been started in that directory, the way git -C <dir> does: config discovery, the lockfile, relative paths and --rules directories all resolve inside the workspace, and your shell's directory is left alone. A path to a file anchors the file's directory; a path that does not exist is a usage error.

When a command also takes a path of its own, the explicit values are combined, never silently overridden:

  • A relative path is resolved inside the workspace, so codecharter analyze src/App.sln --workspace ../worktree analyzes ../worktree/src/App.sln.
  • An absolute path inside the workspace is used as given.
  • An absolute path outside the workspace is a conflict: the command stops with a usage error that names both values, before it does any work. Drop one of the two, or pass the path relative to the workspace.

Without --workspace, every command uses the current directory, as before. Agents working in a git worktree should pass --workspace (or the MCP tools' anchor, see Workspace anchor) so they never touch the main checkout by accident.

Diff-only mode

--diff and --git-ref restrict the run to findings on added or modified lines. The whole solution is still loaded and analyzed (rules need the full type model), but both the reported findings and the --fail-on gate are scoped to the diff — so a pull request fails only on issues it introduced. Diff paths are resolved against the git repository root, so a solution in a subfolder works. Without a path argument, the run analyses the solution that contains every project the diff touches, the same unit the MCP analyze_diff tool picks, so a repository with several solutions side by side needs no path for a diff run. When the diff touches no analyzed line, the run reports nothing and exits 0.

--git-ref diffs a two-dot range such as origin/main..HEAD from the merge base of the two commits — git's three-dot form — so commits that landed on the base branch since you branched off are not counted as your changes. Write the three-dot form explicitly and it is used as typed; pass a bare ref or SHA and it keeps git's own meaning of "working tree against that commit". coverage --git-ref takes the same range syntax.

Some findings are attributed to a line your diff never touched even though your change caused them — a class that only became a God-class once you added a method, for example. Diff mode counts these on stderr instead of hiding them; add --diff-context to report them and include them in the --fail-on gate.

Baseline gate

--baseline reports and gates only on findings that are not in an accepted baseline, so existing findings are tolerated while new ones fail the build — see the Baseline gate guide. It composes with diff mode (both filters apply).

Rule sources

A run evaluates exactly the rule sources that are declared, in this order:

  1. The platform profiles under profiles: in .codecharter/config.yml, in config order (the file is found by walking up from the analysed file). See Using platform profiles.
  2. The rule directories under rules: in the same file, resolved against the repository root. A path that leaves the repository is rejected.
  3. Every --rules <dir>, resolved against the current directory.

The sources add up. When two sources define the same rule id, the later one wins and the run names every shadowed id. --rules-only drops the first two groups and runs only the --rules directories, which is how --rules behaved before 1.6.4.

Nothing is picked up implicitly: a rules folder that exists but is not declared is never read (the run names it and the rules: entry that would declare it), and the sample rules shipped with the CLI are not loaded unless you name them. A run with no rule source at all stops with exit code 2 and says how to declare one: codecharter config init, then codecharter config add profiles codeguard/csharp-all@^1.2 or codecharter config add rules <dir>. A declared directory that is missing or holds no rules is reported by name.

The Rules: line on stderr lists every source with the rules it contributed after shadowing, and names the shadowed ones separately, for example Rules: 245 from codeguard/csharp-all@1.2.0 (2 shadowed), 7 from .codecharter/rules. The counts add up to the rules the run composed.

Run context and reach

Every run states what it actually checked. The JSON report carries it as run, the SARIF report as runs[0].properties.run, and every MCP analyze_* and graph_* result as run, in the same shape:

  • analysisUnit, analysisUnitKind (solution or project), configPath and lockfilePath. Every path in the run context is relative to the repository root (the directory holding .codecharter/config.yml) with forward slashes, for example src/App.sln and .codecharter/config.yml. A path outside the repository, or any path in a run without a config, is written as its file name only. The run context never contains an absolute path, because JSON and SARIF reports are uploaded to code scanning and kept as CI artifacts. Console and stderr output may still show absolute paths.
  • ruleSources: one entry per declared source with kind, identity (a profile's resolved slug@version), declaredIdentity (as written in the config or on the command line), configuredCount (what the lockfile pins for a profile, the number of rule files for a rules: directory, null when unknown), resolvedCount, shadowedCount, isResolved and, for a source that contributed nothing, unresolvedReason.
  • reach: configured, resolved and evaluated rule counts, unresolvedSources, drift (each disagreement between the config's profiles and the lockfile), coreLibraryUnresolvedProjects, projectLoadFailures (each project of the analysis unit that failed to load, with the cause MSBuild reported), isInconclusive and inconclusiveReasons.

A run is inconclusive when any of these reason codes applies:

Code Meaning Next step
no-rule-source No rule source is configured. Declare one, see Rule sources.
nothing-evaluated Not one rule was evaluated. Check the rule sources and the skipped-rule messages.
resolved-short-of-configured A profile resolved fewer rules than its lockfile entry pins, or a rules: directory loaded fewer rules than it holds rule files. codecharter restore, or fix the named rule files
resolved-beyond-configured A profile resolved more rules than its lockfile entry pins. codecharter update, then codecharter restore
source-unresolved A declared source contributed no rules. Fix the named source.
lockfile-missing Profiles are declared, but there is no lockfile. codecharter update
config-lock-drift The config's profiles disagree with the lockfile the run used. codecharter update
core-library-unresolved A project could not resolve its own core library, so its facts may be wrong. dotnet restore
project-load-failed A project of the analysis unit failed to load, so its code is not part of the run. Findings for the other projects are still reported. Fix the cause named in projectLoadFailures

An inconclusive run exits with code 2, whatever it found, and prints one line per reason on stderr:

Inconclusive run: 1 rules evaluated, 1 resolved. This is NOT a clean result.
  [core-library-unresolved] 1 project(s) could not resolve their own core library reference (Tiny), so every fact extracted from them may be wrong; run 'dotnet restore'.

Suppressions

The ignore and exclude sections of .codecharter/config.yml, and // codecharter-disable / // codecharter-disable-next-line comments in source files, suppress findings before output and gating, so suppressed findings neither appear in the results nor affect the exit code. See Suppressions.

Exit Codes

  • 0: Run successful, zero findings at or above the active threshold.
  • 1: One or more findings at or above the threshold. Without --fail-on, any finding at or above the --severity threshold triggers exit 1.
  • 2: Usage or configuration error (for example invalid flag combinations, a missing diff file, no rule source configured, a --rules directory that does not exist, or a corrupt baseline file), or an inconclusive run (see Run context and reach). An inconclusive run exits 2 even when it found violations.
  • 3: Analysis failed, for example because the solution or a project could not be loaded.
  • 6: License error (missing, expired, or not valid for this CLI version).

With platform profiles configured, the pre-run check can also exit 2 (lockfile missing), 3 (profile cache incomplete and portal unreachable), or 4 (lockfile out of date). More at Exit Codes.