Use Python 3.10+ and an installer with patched certificate trust: pip >=26.2.2 or uv >=0.13.4. Keep the operating system’s CA store updated as well. The Python security advisory explains why updating only system certificates does not repair upstream pip’s vendored certifi bundle.
Create and activate a virtual environment. On Linux/macOS:
python3 -m venv .venv
source .venv/bin/activate
On Windows PowerShell:
py -m venv .venv
.venv\Scripts\Activate.ps1
Each new environment can seed an older pip through ensurepip. Before any
dependency downloads, run this command with the activated environment’s Python:
python -m pip --isolated --disable-pip-version-check install --no-index --no-deps --no-cache-dir --require-hashes "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"
python -m pip --version
python -m pip install agent-code-guard
The advisory supplies this exact wheel and SHA-256. Even when the seed pip’s TLS
roots are stale, the locally specified hash authenticates the bootstrap bytes
before installation. The command disables index lookup, dependency resolution,
cache reuse, and pip’s update check. Do not replace it with an unpinned
pip install --upgrade pip or disable TLS verification. Subsequent dependency
downloads use the patched pip. For Python.org macOS installations, also update
the separate certifi package used by that installation, as the advisory directs.
Install Agent Code Guard as an isolated CLI tool with pipx only after repairing the installer pipx actually uses:
pipx install agent-code-guard
For pipx’s pip backend, locate its shared environment using pipx environment
and run the pinned bootstrap above with that environment’s Python before
pipx install, pipx upgrade-shared, or any other download. On Linux/macOS its
interpreter is normally <PIPX_SHARED_LIBS>/bin/python; on Windows it is
<PIPX_SHARED_LIBS>\Scripts\python.exe. If the shared environment does not yet
exist, use the virtual-environment route above rather than bootstrapping pipx
through an older downloader. Updating a project’s or system’s pip does not
update pipx’s shared pip. See how pipx works.
For pipx’s uv backend or a direct uv tool install, first verify uv --version
reports >=0.13.4. Obtain a patched uv through an updated OS package manager or
the pinned-pip environment above; an older uv’s bundled roots must not be used
to fetch its own replacement. Then:
uv tool install agent-code-guard
For source or repository development installs, bootstrap pip in the active virtual environment as above, then run one of the following from a checkout:
python -m pip install .
python -m pip install -e .
The checkout runner at skills/code-guard/scripts/code_guard.py exists for
repository and skill compatibility; it is not the primary installed command.
The public command is code-guard. With no paths, it defaults to ., but
normal development should select current work explicitly.
Report the installed distribution identity without configuration, scope, Git, provider, skill, or guard work:
$ code-guard --version
agent-code-guard <version>
For machine-readable output:
$ code-guard --version --json
{
"distribution": "agent-code-guard",
"version": "<version>"
}
The value is read from installed metadata for the canonical
agent-code-guard distribution. --version may be combined only with
--json. Both successful version forms exit 0; incompatible arguments or
unavailable metadata exit 3. Human errors use standard error and JSON errors
use an error object on standard output. If distribution metadata is
unavailable, the error is
installed distribution metadata is unavailable for agent-code-guard.
Inspect the active installation and its immediate local capabilities without running project analysis:
code-guard doctor
code-guard doctor --json
The human and JSON reports cover the active distribution, Python process,
invoked entry point, bundled skill, current-directory configuration and Git
context, and all supported parser providers. A healthy report exits 0; a
completed unhealthy report exits 1; invocation or internal failures that
prevent a report exit 3. Completed reports use standard output only.
doctor is reserved only as the exact first token. Analyze a file or directory
with that name through a qualified spelling such as ./doctor, .\doctor, or
an absolute path. Doctor is read-only: it does not analyze source, repair or
install dependencies, export skills, modify configuration or Git state, access
the network, or persist diagnostics.
Diagnostic output includes resolved launcher, interpreter, skill, Git, and configuration paths. Treat those paths and other environment details as potentially sensitive before sharing a report.
code-guard . --changed-only
This selects staged, unstaged, and untracked files known to the current Git worktree, then intersects them with positional file or directory bounds.
code-guard . --staged
This selects only index changes as source candidates. Analysis still uses working-tree configuration and baselines, including unstaged policy edits.
code-guard . --base-ref origin/main --ci
This selects added, copied, modified, or renamed files from the merge-base
comparison <ref>...HEAD. The ref must exist and resolve in the environment;
fetch the intended base before running in shallow or isolated CI checkouts.
Only one of --changed-only, --staged, and --base-ref may be used at a
time. Positional paths bound the Git-selected candidates; they do not add files
outside that selection.
Every Git comparison also runs the non-configurable policyRelaxation guard.
It compares persistent effective policy after production defaults and
normalization, using the same retained Git authority as source selection:
| Mode | Trusted policy | Current policy |
|---|---|---|
--changed-only |
Resolved HEAD | Actual working tree |
--staged |
Resolved HEAD | Actual working tree, including unstaged policy |
--base-ref REF |
Runner-selected merge-base object | Actual working tree |
Changed/staged repositories with no HEAD use built-in defaults and empty baselines as the trusted state. Base-ref still requires valid commit authority. Missing owned artifacts mean defaults/empty allowances. Invalid authority, malformed current/historical policy, unsupported baseline versions, and unsafe historical symlink/object/ancestor shapes are tool errors, never policy PASS.
The four compared artifacts are the actual active configuration and:
.agent-tools/code-guard.loc-baseline.json;.agent-tools/code-guard.markdown-baseline.json;.agent-tools/code-guard.callable-review-baseline.json.Automatic config discovery stays relative to the invocation directory; baselines stay at the Git root. Repository-owned explicit config aliases map to their canonical repository path. External configs and repository policy symlink traversal cannot establish trusted history and fail closed in Git modes. Plain explicit/audit analysis omits this inapplicable guard.
The guard reviews disabling any configured guard, increasing active REVIEW or
LOC thresholds, changing LOC ratchetAt from review to fail, reducing
counted blank/comment evidence, removing normalized LOC extensions, adding
normalized exclusion/exemption declarations, and adding/increasing any of the
three baseline allowances. Lowering/removing allowances, reason-only edits,
and neutral default serialization/reordering stay quiet. Transient CLI choices
are excluded from persistent policy comparison.
Exclusions use declaration sets, without symbolic glob reasoning: even a
redundant added declaration may REVIEW. Ordered LOC override match sets retain
last-match-wins semantics. Unchanged normalized topology permits threshold
comparison; a changed topology produces one conservative
overrideTopologyChanged reason. These findings describe what changed and do
not claim that arbitrary effective path coverage was proven weaker.
REVIEW is one aggregate with totalReasons, sorted reasonCounts and
artifactCounts, at most 20 reasons, and omittedReasons. Reasons sort by
artifact, logical field, path/selector, and code. Each retains artifact,
field, reasonCode, before, after, and explanatory message, with a
path or exact callable selector where appropriate. Topology before/after
summaries retain entry counts and SHA-256 fingerprints instead of unbounded
match lists. Human, full/debug JSON, and compact JSON share this cap. REVIEW
requires policyRelaxation guidance; PASS has no required policy. --ci
retains visible REVIEW and exits 0; independent FAIL/INCOMPLETE dominates.
Policy comparison does not alter source counts. An empty selected-source scope
can REVIEW a policy change, even when positional bounds, scope.exclude, or
LOC exclusions hide every source or policy file from ordinary analysis.
Legitimate authorized policy relaxation can be retained; do not reverse it
solely to clear REVIEW or weaken another control to silence the finding.
Residual trust boundaries remain: transient CLI thresholds/counting/exclusions; choosing a base that omits prior weakening; changing/removing Code Guard or its CI invocation; and switching invocation/config outside the active compared artifact. This guard does not enforce CI integrity or infer authorization.
Without Git, pass the exact files owned by the current change:
code-guard src/Foo.py src/Bar.ts docs/guide.md
Git selectors require a Git repository and fail rather than falling back to a recursive audit. Do not create a manifest merely to provide scope.
code-guard .
A directory without a Git selector is a recursive audit. In a Git repository,
directory discovery respects Git tracking and ignore rules. Outside Git, it
walks the directory while pruning .git, node_modules, bin, and obj.
Changed work and a full audit are intentionally different operations.
Recursive discovery does not follow symlinks. An explicitly supplied file symlink expresses caller intent and is inspected at its resolved target. An explicit directory symlink is rejected rather than traversed recursively.
Missing explicit paths, incompatible selectors, unavailable base refs, invalid scope, and Git selectors outside a repository are tool errors. Git-derived paths that no longer exist are ignored.
Established repositories may adopt LOC policy without accepting further growth.
The default guards.loc.ratchetAt: "fail" records files above effective
failAt. Use "review" when existing files above effective warnAt may remain
but must not grow. Create the
canonical source-controlled ratchet over an intentionally bounded scope:
code-guard src/legacy --create-loc-baseline
git add .agent-tools/code-guard.loc-baseline.json
git diff --cached
The file is always
<analysis-root>/.agent-tools/code-guard.loc-baseline.json, where the root is
the enclosing Git top-level or, outside Git, the resolved invocation directory.
Normal analysis reads it automatically and never writes it. Under fail, a file
at its allowance, or reduced while still above failAt, becomes grandfathered
REVIEW. Under review, a file within its allowance keeps ordinary REVIEW
between warnAt and failAt, and is grandfathered only above failAt. Growth
above either allowance is FAIL. At or below the selected policy threshold,
the entry is reported as no longer needed.
After reducing or deleting legacy code, explicitly lower and prune entries:
code-guard src/legacy --update-loc-baseline
Update bounds come only from the positional paths. Update lowers existing allowances, removes entries that are missing, excluded, inapplicable, or no longer above the selected policy threshold, and leaves entries outside the bounds unchanged. It never adds a newly oversized path or raises an allowance; any attempted growth aborts the entire update. A rename is an old-path deletion plus a new, ungrandfathered destination—Git history is not consulted. Manual JSON edits are ordinary source-control changes and require review.
This ratchet differs from LOC exclusions, threshold overrides, and
allowedLargeFiles: exclusions skip LOC evaluation, overrides change ordinary
thresholds, and allowedLargeFiles is a reasoned static exemption without a
size-regression check. A ratchet entry may not overlap allowedLargeFiles.
This workflow is only for adopting established legacy repositories. New projects, including Agent Code Guard itself, should meet policy directly and must not create a LOC ratchet baseline.
Do not switch between fail and review, raise thresholds, add exclusions or
exemptions, or edit allowances merely to silence a growth failure. Those are
source-controlled policy changes and require their own substantive justification.
After reviewing an oversized, cohesive Markdown document, explicitly record its accepted physical-line count over a deliberately bounded scope:
code-guard docs/architecture.md --create-markdown-baseline
git add .agent-tools/code-guard.markdown-baseline.json
git diff --cached
This creates <analysis-root>/.agent-tools/code-guard.markdown-baseline.json.
The analysis root is the enclosing Git top-level or, outside Git, the resolved
invocation directory. Creation requires an enabled markdownDocumentSize guard
and records only selected .md files strictly above its effective reviewAt.
An existing baseline is never overwritten by create. Review and commit the
baseline as the explicit acceptance decision; it is not part of ordinary setup.
Normal analysis reads the baseline automatically without writing it. With the default 800-line threshold and an accepted allowance of 845 document lines:
| Current document | Document-size result |
|---|---|
| Same path, 845 lines | PASS, within allowance |
| Same path, 840 lines | PASS, within allowance |
| Same path, 846 lines | REVIEW, allowance exceeded |
| New or renamed path, 845 lines | REVIEW, no allowance |
| Any document at or below 800 lines | PASS, no allowance needed |
This ratchet applies only to markdownDocumentSize. It does not change the
threshold, exclude Markdown from analysis, or suppress markdownSectionSize.
An accepted document can still have section REVIEW findings. Growth remains
REVIEW (exit 1, or 0 with --ci), not FAIL.
After shortening or deleting documents, explicitly lower or prune entries:
code-guard docs --update-markdown-baseline
Update requires an existing baseline and an enabled document guard. It lowers existing allowances within positional bounds, removes deleted or scope-excluded paths and entries at or below the effective threshold, and preserves entries outside those bounds. It never adds an entry or increases an allowance. Growth in a retained entry aborts the entire update without changing the baseline. A rename is a deletion plus an unaccepted new path. Manual baseline changes require source-control review; do not rebaseline merely to hide growth.
Both write modes accept paths, --config, and repeated --scope-exclude.
They reject other baseline modes, LOC counting/threshold options, Git selectors,
JSON/CI analysis options, diagnostics, version reporting, and skill management.
Writes do not run normal analysis. Successful writes exit 0; invalid inputs
or unsafe baselines produce tool errors (exit 3, stderr, no completed report).
Normal analysis and creation do not grant allowances through an explicitly
supplied file symlink; updates reject symlink bounds. Baseline storage and
entries must not traverse symlinks, and baseline-enabled scopes must stay
inside the analysis root.
Human output shows the accepted allowance and within, exceeded, or
no longer needed. Full/debug JSON retains document measurements and the
unchanged thresholds.reviewAt; entries matched by path add baselineLines
and ratchetStatus (within, exceeded, or notNeeded). Unmatched findings
retain their original shape. Compact JSON omits accepted PASS findings like
other passes. Accepted documents do not require the document-size policy;
independent section findings still require their own policy.
See the persisted schema.
After a human authorizes accepting a current callable REVIEW, record exactly one finding in exactly one explicit physical file:
code-guard offenders_patterns.py \
--accept-callable-review cyclomaticComplexity python offenders_patterns._auth \
--reason "Reviewed cohesive authentication-format catalog; re-review on growth."
git add .agent-tools/code-guard.callable-review-baseline.json
git diff --cached
The selector is guard + embedded language + exact lexical identity, as shown
in full JSON. Canonical guards are callableSize, nesting, and
cyclomaticComplexity; the existing complexity result/policy ID is still
complexity, which is rejected as a baseline/CLI alias. Each invocation targets
one current REVIEW, requires a non-empty reason, and records its exact current
measurement. Directories, multiple files, PASS/nonexistent targets, duplicate
lexical identities, and existing allowances are rejected. A second explicitly
reviewed callable can be added with another invocation; there is no bulk mode.
Acceptance cannot replace or raise an existing ceiling, including after growth.
Normal analysis automatically reads the baseline without writing it. The threshold remains the general policy for unreviewed code; the accepted value is the reviewed ceiling for one callable/guard. For complexity threshold 15 and accepted value 17:
| Current complexity | Result |
|---|---|
| 17 or 16 | PASS, withinAllowance |
| 18 | REVIEW, grown |
| 15 or less | PASS, notNeeded; removable by maintenance |
| Unaccepted callable at 16 | REVIEW under the unchanged threshold |
Only a unique current physical path + language + lexical identity receives the allowance. Inserting unrelated lines above a named callable preserves the match. Renames/moves become unaccepted identities. Anonymous callbacks whose existing lexical names contain coordinates can become stale when moved and require re-review. Different embedded languages in Vue remain separate; two identical names in the same language fail closed. Remove an ambiguous entry through an explicitly reviewed source-control edit before accepting a unique current identity. No fuzzy matching or new parser behavior rescues old entries.
Explicit maintenance operates within positional file/directory bounds:
code-guard src --update-callable-review-baseline
code-guard src --update-callable-review-baseline --prune-stale-callable-reviews
Maintenance requires an existing baseline. It lowers a ceiling to the current measurement while still above the ordinary threshold, removes allowances no longer needed, and never adds/increases one. Growth aborts the entire proposal without partial writes. Stale entries remain until pruning is explicitly requested; directory bounds allow pruning entries for deleted files. Disabled guards and existing excluded/unselected/inapplicable files are preserved. Unchanged maintenance preserves file bytes and timestamps. Reasons survive lowering. Normal analysis reports stale identities only in completed analyzed files; maintenance additionally reports missing paths within its bounds.
Both commands require explicit positional scope and current filesystem state.
They accept --config and repeated --scope-exclude, reject other write modes,
LOC counting/threshold options, --changed-only, --staged, --base-ref,
--ci, JSON modes, diagnostics, version reporting, and skill management.
Known unavailable syntax/provider evidence blocks writes. Success exits 0;
invalid/unsafe/ambiguous targets exit 3 on stderr without a completed report.
Baseline storage and entries may not traverse symlinks or escape the analysis
root. Write bounds reject symlinks; ordinary explicit-file symlink analysis
cannot gain an allowance unless the physical file is also reached directly.
Full/debug matched findings add allowedMeasured, ratchetStatus
(withinAllowance, grown, notNeeded), and reason, retaining ordinary
thresholds.reviewAt, measurement, range, language, and guard-native details.
Compact JSON omits accepted PASS findings. Human output stays quiet for those
passes and shows current versus accepted measurement on growth. Stale entries
add concise human diagnostics and callableReviewBaseline.diagnostics in all
JSON modes. Growth routes the existing guard policy; accepted passes do not.
Independent REVIEW/FAIL and INCOMPLETE evidence and exit codes remain intact.
An allowance records a human-reviewed outcome, not proof of ideal structure. Agents must not create or raise one merely to silence a finding. Do not split coherent callables mechanically, obscure control flow to reduce measured complexity/nesting, or change thresholds/exclusions to avoid re-review. Genuine reductions remain visible without automatic baseline mutation. Review and commit acceptance decisions. See the schema.
PASS means no special action and exits 0.REVIEW means inspect findings and apply judgment; normal invocation exits 1.FAIL blocks normal completion and exits 2.INCOMPLETE retains independent completed evidence when known per-file
syntax/provider evidence is unavailable and exits 3.3 without a completed report.--ci changes a REVIEW-only result to exit 0. It does not suppress FAIL,
INCOMPLETE, or tool errors. REVIEW is not automatic refactoring, and metrics
must never be gamed.
Human output is the default and emphasizes actionable findings. Every completed analysis starts with the aggregate state and exact scope counts:
PASS: 3 selected; 2 analyzed; 1 inapplicable; 0 excluded.
The labels do not pluralize. An empty valid selection with no policy relaxation is
PASS: 0 selected; 0 analyzed; 0 inapplicable; 0 excluded. and exits 0.
Existing finding and required-policy lines follow this summary unchanged.
The counts have these meanings:
selected: files remaining after discovery, bounds, normalization,
deduplication, absent Git-derived entries, and all-guard exclusions;analyzed: selected files applicable to at least one enabled guard;inapplicable: selected files applicable to no enabled guard;excluded: existing normalized files removed specifically by
scope.exclude or --scope-exclude.Therefore analyzed + inapplicable == selected, and an excluded file belongs
to none of the other sets. All values are non-negative integers. Git-ignored
files never discovered, paths outside positional bounds, and absent Git-derived
files are not counted as exclusions.
Add --json for the stable, compatible full machine-readable output, including
all passing and actionable findings. Completed PASS, REVIEW, and FAIL
results include exactly one top-level summary alongside the existing overall,
requiredPolicies, and guards values:
"scope": {
"selected": 3,
"analyzed": 2,
"inapplicable": 1,
"excluded": 0
}
Counts do not change aggregate state, findings, required policies, or exit
codes. Tool errors retain their existing human or JSON error form and do not
include a successful scope object.
When a known per-file syntax or provider failure occurs, the headline is
INCOMPLETE, followed by ordered unavailable context and the incomplete syntax
guard identifiers before ordinary findings. JSON uses overall: "incomplete",
adds the authoritative completed aggregate as completedOverall, and includes
ordered top-level unavailable records containing path, embedded language,
kind, and the exact provider message. scope.unavailable overlaps
analyzed, so analyzed + inapplicable == selected remains true. Every guard
adds complete only on incomplete runs; incomplete syntax guards also add
ordered unavailablePaths. Guard states, findings, and requiredPolicies
continue to describe only completed evidence.
Choose a completed-analysis serialization mode explicitly when needed:
code-guard . --json --json-mode compact
code-guard . --json --json-mode debug
compact is intended for routine agent checks. It preserves overall, the
complete scope, requiredPolicies, every guard and guard state, and existing
guard and retained-finding ordering. It omits each finding whose normalized
state is pass and retains unchanged findings whose state is review or
fail. This includes omitting LOC exemptions: their native status is exempt,
but their normalized state is pass. debug is an explicit name for the full
output and is byte-for-byte identical to bare --json for the same completed
analysis.
For incomplete output, full and debug retain identical unavailable records;
compact filters only ordinary passing findings and also retains those records
unchanged. All three JSON modes, human output, normal invocation, and --ci
exit 3.
Both named modes require --json and apply only to completed analysis output.
They do not change analysis, scope, policies, ordering, aggregate or guard
states, exit codes, or error shapes and channels. Version JSON supports only
bare --version --json; skill-management modes are also incompatible with JSON
analysis options. Values are exact and case-sensitive. There is no detail mode.
requiredPolicies lists the policy identifiers needed for actionable findings.
An agent should load only those referenced policies, preserve project intent,
and decide whether a REVIEW warrants meaningful improvement. A passing result
has no required policy work.
After a meaningful editing turn in Git, the normal structured agent command is:
code-guard . --changed-only --json --json-mode compact
Outside Git, pass the exact edited paths instead. An optional, user-authorized
platform hook may add --ci; this changes REVIEW’s process exit from 1 to
0 without hiding its findings or changing FAIL and tool-error exits.
Agent Code Guard does not install hooks.
See the human and agent workflow for the repeated manual and hook-assisted loop. Installed distributions also carry a version-matched skill payload; skill distribution documents discovery, export, and platform activation.
Performance changes can be measured against the fixed Wayfarer workload with
tools/benchmark_wayfarer.py. The caller supplies a disposable checkout at
commit 679ddae9717bf78681a2cfbf794f687127b23b5d, its exact project config, and
an output directory outside that checkout:
python tools/benchmark_wayfarer.py --wayfarer-path C:\bench\Wayfarer --config-path C:\bench\wayfarer-code-guard.config.json --output-directory C:\bench\results\after --installation-mode "editable wheel from issue 122 branch"
The script validates the source commit, records Python and Code Guard versions, the configuration hash, exact commands, three fresh sequential warm-process samples and medians for LOC-only, syntax-only, and normal six-guard scans, plus a normal-run cProfile file. It requires successful complete Git-status checks before and after the run, and fails if verification fails or analysis creates repository metadata. It rejects output paths equal to or beneath the target, never clones or writes to the target checkout, and is intentionally not a CI test. Run the same script and installation mode against the before and after revisions, retaining both result directories for comparison.