Skip to content

Safe Project Initialization

agentguard init [PATH] prepares an existing repository for AgentGuard without running repository code, installing dependencies, or changing Git state. It is included in the production agentguard-evals==0.3.0 package:

python -m pip install "agentguard-evals==0.3.0"
agentguard --version

Start With A Dry Run

From the repository to initialize:

agentguard init --preset recommended --dry-run --ci github

Or provide the repository path from its parent directory:

agentguard init "./project with spaces" --preset strict --dry-run --ci github

The plan reports the resolved project root, detected project type, files that would be created or replaced, files already current, conflicts, the next local command, and the next CI step. Dry-run planning does not change project files.

After reviewing the plan, initialize the project:

agentguard init --preset recommended --ci github

Command Interface

agentguard init [PATH] [--dry-run] [--force]
                [--ci github | --no-ci]
                [--preset minimal|recommended|strict]
                [--test-command TEXT]

PATH defaults to the current directory. CI generation is opt-in; use --ci github to create the maintained workflow or --no-ci to state that no workflow should be generated. --test-command takes precedence over detected commands. --preset defaults to recommended; omitting it preserves the Phase 44A generated configuration. See CI policy presets for the exact effective settings and tradeoffs.

Generated Files

Path Purpose
agentguard.yaml Tracked strict-schema CI configuration using the selected policy preset.
.gitignore Preserves existing content and adds one .agentguard/ runtime-artifact entry when missing.
.github/workflows/agentguard.yml Optional pull-request gate generated by --ci github.

The tracked configuration is deliberately not placed under .agentguard/. That directory is reserved for generated reports, manifests, history, and other runtime artifacts.

A separate benchmark or suite is not needed for the first project check. CI mode evaluates the existing repository and its current Git diff:

agentguard ci --config agentguard.yaml

Python, Node.js, And Go Detection

Phase 44A recognizes common Python signals such as pyproject.toml, setup.py, setup.cfg, pytest configuration, conftest.py, and requirements files. It reads these as text only and never imports them or executes setup, hooks, package managers, test commands, or repository code during initialization.

When maintained pytest configuration is present, the initializer selects:

test_command: python -m pytest

For this conservatively detected case, the optional GitHub workflow also adds an explicit Python test-environment step. It always installs pytest rather than relying on runner-preinstalled packages. Safe root-level conventional requirements files (requirements.txt, requirements-test.txt, and requirements-dev.txt) are installed with pip --requirement, without installing the repository itself. A [project] table in pyproject.toml, a packaging [metadata] or [options] table in setup.cfg, or a regular root-level setup.py conservatively identifies an installable root project; the workflow then installs that checkout in editable mode so its declared runtime dependencies and source tree are available to its tests. A bare [build-system] table or pytest-only setup.cfg does not imply that the root is installable. These operations run later in GitHub Actions, not during initialization.

This is intentionally a small setup model, not universal Python environment inference. Unrecognized requirements*.txt names and symlinked, unreadable, or oversized dependency metadata require explicit customization instead of emitting a guessed installation step. Pytest-only roots without packaging or requirements metadata receive only the explicit pytest installation. An explicit --test-command takes precedence and suppresses all inferred project setup; add the reviewed setup required by that command to the workflow.

An explicit command is preserved as a single YAML string:

agentguard init --test-command "python -m pytest tests/unit"

AgentGuard later splits this value into an argument vector; it does not pass it through a shell. Quotes and shell metacharacters therefore do not become shell fragments. Review any explicit command before running the gate.

Node.js detection is deliberately narrower. The initializer reads only safe, root-level regular files. It parses at most 1 MiB of package.json as strict UTF-8 JSON, rejects duplicate keys and non-standard constants, and requires a top-level JSON object. Lockfiles are treated only as untrusted presence signals; their contents are never parsed or executed.

The only automatically selected Node.js command is the fixed native runner:

test_command: node --test

AgentGuard selects that fixed value only when all of these conditions hold:

  • package.json has an exact string value of node --test at scripts.test;
  • no workspaces field is present;
  • at most one of package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml, bun.lock, bun.lockb is present; and
  • when packageManager is present, it names npm, yarn, pnpm, or bun with a non-whitespace version/reference and agrees with the detected lockfile.

The script is used only as a conservative signal. Its contents are not copied into configuration, and initialization never invokes npm test, another package script, a lifecycle hook, a package manager, Node.js, a local binary, or repository code. Commands such as jest, vitest, npm test, yarn test, and values containing arguments or shell operators require an explicit --test-command; AgentGuard does not try to quote or rewrite them.

The following cases also require customization: mixed Python/Node.js roots, workspaces and monorepos, conflicting lockfiles, a packageManager mismatch, lockfile-only projects, and missing, unreadable, malformed, oversized, duplicate-key, non-object, or symlinked package.json metadata. Root-level signals are intentional; recursive package discovery could guess the wrong workspace. Symlinked detection inputs are ignored rather than followed.

An explicit --test-command still takes precedence for every recognized or ambiguous project. It is stored as one value and later split without a shell. That protects argument boundaries, but it does not make the selected executable or repository tests trusted.

Go detection is similarly conservative and reads only safe root-level regular files. go.mod, go.work, and go.sum are presence signals; their contents are untrusted. AgentGuard reads at most 1 MiB of go.mod as strict UTF-8 and requires exactly one supported simple, unquoted module directive. Missing, duplicate, or malformed module directives fail closed. AgentGuard deliberately does not parse or validate go, toolchain, require, replace, exclude, retract, or other remaining content; it treats those lines as opaque untrusted data and never copies them into generated files. It never invokes go, parses command output, follows symlinked metadata, or reads go.sum contents during initialization.

For an otherwise unambiguous root module with no go.work, the initializer selects the fixed command:

test_command: go test ./...

The module path is only a conservative signal and is never copied into a command. Workspaces, mixed Python/Node.js/Go roots, go.sum without a supported go.mod, and missing, malformed, or unsupported module directives require customization, as do unreadable, oversized, NUL-containing, non-UTF-8, or symlinked metadata. Other go.mod content can still be invalid because the initializer does not implement Go's module-file parser. An explicit --test-command still takes precedence in these cases. The Go toolchain validates the complete module file only during later user-authorized execution.

Initialization itself never runs go test, go list, go env, go generate, other Go tooling, repository code, module downloads, scripts, or project binaries. The generated test command is intended for a later, reviewed AgentGuard run. During that later execution, go test ./... executes repository test code and the Go toolchain may download modules named by repository metadata. Review the module and test sources and configure dependency policy or an appropriate execution boundary before running it. AgentGuard CI performs post-execution validation; it does not contain hostile code.

The isolated test environment assigns Go build and module caches beneath the ignored .agentguard/cache/ directory, disables the per-user Go environment file, and disables automatic toolchain switching. This avoids dependence on a user home directory and prevents host Go cache state from being inherited. It does not prevent the selected Go runtime from downloading modules during the later test execution.

If no safe command can be inferred, the generated configuration contains a deterministic placeholder that prints an edit instruction and exits nonzero. Replace test_command before treating the configuration as a working CI gate. AgentGuard does not guess a command for unknown ecosystems.

Select A Validation Preset

The initializer supports three post-execution CI validation postures:

  • minimal keeps every check but uses wider limits for trusted local work.
  • recommended is the balanced default and preserves Phase 44A behavior.
  • strict tightens limits, makes scope and diff findings blocking, and enables supported built-in secret-content detectors.

Inspect definitions before initializing:

agentguard presets list
agentguard presets show strict
agentguard presets show strict --format yaml

These presets do not contain coding-agent or test-command execution. They omit Docker, command-policy, and filesystem-watcher settings because the current CI path does not enforce those controls. No untrusted-agent preset is exposed; contained execution is tracked separately in issue #157.

Overwrite And Idempotency Model

The initializer computes and validates the complete plan before writing:

  • Missing known targets are created atomically where practical.
  • Byte-identical targets are reported as already current and left untouched.
  • A non-identical target is a conflict, produces a nonzero exit status, and prevents all planned writes.
  • --force replaces only the three known targets listed above. It never deletes directories, recursively replaces content, or changes unrelated files.
  • Existing .gitignore content is retained and .agentguard/ is not duplicated.
  • Symlink targets and parent directories that escape the selected root are rejected, including with --force.
  • Selecting a different preset changes agentguard.yaml, so it conflicts by default and requires an intentional --force replacement. An identical workflow and .gitignore remain current.

The command never stages, commits, pushes, installs, or executes the generated configuration. Inspect the diff yourself:

git diff -- agentguard.yaml .gitignore .github/workflows/agentguard.yml

GitHub Actions Security

The optional workflow:

  • runs for pull_request, not pull_request_target;
  • grants only contents: read;
  • installs agentguard-evals==0.3.0 for a reproducible gate;
  • for a safely auto-detected pytest root, installs allowlisted requirements, installs an identified project editably when applicable, and explicitly installs pytest;
  • pins actions/checkout v5.0.1 and actions/setup-python v6.2.0 to maintained immutable full commit SHAs;
  • for a detected Node.js root, pins actions/setup-node v6.5.0 to its immutable full commit SHA and selects supported Node.js 24;
  • for a detected Go root, pins actions/setup-go v7.0.0 to its immutable full commit SHA, selects supported Go 1.26, and disables setup caching so setup does not inspect or restore module dependencies;
  • passes the pull request base SHA through a quoted environment variable;
  • uses no secrets, OIDC, long-lived credentials, or repository write access;
  • runs the real agentguard ci --config agentguard.yaml command and preserves its failing exit status.

Review the workflow, commit it on your branch, and open a pull request to run the first CI evaluation. The exact agentguard-evals==0.3.0 pin is available from production PyPI.

The generated workflow does not install Node.js dependencies. Dependency-free native tests can run immediately. If the selected test command needs installed packages, add a reviewed installation step yourself. Package-manager installs may download dependencies and execute lifecycle hooks on the GitHub runner; that later execution is outside agentguard init, and AgentGuard CI remains post-execution validation rather than hostile-code containment.

Python dependency installation is likewise later, user-authorized CI execution. Requirement processing and editable installation can download packages and execute package build backends or setup hooks from the checked-out repository. agentguard init only inspects bounded root-level regular-file metadata and writes the workflow; it never invokes pip, imports the project, or executes those hooks. Review dependency and build metadata before enabling the generated workflow. Projects needing constraints, lock tooling, non-editable installation, extras, multiple environments, or another setup sequence should replace the generated step explicitly.

For Go projects, setup prepares the supported runtime with dependency caching disabled; the generated workflow has no module installation or metadata-probe step. The later AgentGuard test step runs the configured go test ./...; that command can download modules and execute repository test code. Review go.mod, replacements, dependencies, and tests before enabling the gate.

Customize Or Remove

Edit agentguard.yaml to match the repository's source paths, test paths, expected diff size, and test command. Strict validation rejects unknown fields instead of silently weakening policy. See GitHub Actions for CI behavior, CI policy presets for preset details, and Architecture for the trust model.

To remove initialization manually, delete agentguard.yaml, optionally delete .github/workflows/agentguard.yml, and remove the .agentguard/ line from .gitignore if no other AgentGuard workflows use it. The initializer never performs removal.