Skip to main content

vulkro guard

In-loop security guard for AI coding agents. vulkro guard scans files as the agent writes them and feeds findings back so the agent regenerates until the file is clean.

Where vulkro mcp serve exposes the scanner as an MCP TOOL the agent may choose to call (discovery), vulkro guard wires the scanner into the agent's HOOK system so a scan runs on every file write or edit whether the agent asks for it or not (enforcement). The two compose: run mcp serve for on-demand scans and guard install to make scanning mandatory in the write loop.

Each check is a single-file scan with no network call and no token cost, so it fits inside the agent's edit loop.

Sub-commands

vulkro guard check <FILE> # scan one file, emit a verdict the agent consumes
vulkro guard install # wire the hook into an agent's config
vulkro guard uninstall # remove the hook the guard installed
vulkro guard status # show which agents are wired

guard check

Scan a single file fast and emit a verdict the agent loop can consume. Reuses the standard scanner (same result as vulkro scan), filtered to the one file. Findings at HIGH or CRITICAL severity BLOCK (the agent should regenerate the file); MEDIUM / LOW / INFO are reported but do not block, so the guard never traps the agent on a stylistic nit.

FlagDescription
--hook-format <FORMAT>Output / exit protocol to emit: generic (default), claude-code, or cursor.

The output and exit protocol depend on --hook-format:

  • generic (default): a structured JSON verdict on stdout, the regenerate instruction mirrored to stderr on a block. Exit 0 = clean, exit 2 = block. This intentionally differs from Vulkro's usual 0/1/2 contract: a hook needs a non-zero "stop" code on findings, and Claude Code reserves exit 1 for a non-blocking error, so the guard uses 2 to mean "block and regenerate".
  • claude-code: always exit 0; emits {"decision":"block","reason":...} on a block (fed back to the model) and an empty object when clean. The target file is read from the hook's stdin JSON (tool_input.file_path) when no path argument is given.
  • cursor: always exit 0; emits {"permission":"deny","agent_message":...} on a block or {"permission":"allow"} when clean. The target file is read from the hook's stdin JSON (file_path) when no path argument is given.

Any format also exits 2 on an operational error (file missing, scan failure) with an actionable message on stderr.

guard install

Idempotently wire vulkro guard check into an agent's hook config so it runs on every file write or edit. Merges into the existing config without clobbering unrelated keys; re-running does not duplicate. Prints the file it changed and how to undo.

FlagDescription
--agent <AGENT>Which agent to wire: claude-code (default), cursor, windsurf, or all.
--scope <SCOPE>user (all projects, the default) or project (this repo only, checked into version control).

Supported agents:

AgentConfigIntegration
claude-code~/.claude/settings.json or .claude/settings.jsonSolid: verified PostToolUse hook schema.
cursor~/.cursor/hooks.json or .cursor/hooks.jsonSolid: verified afterFileEdit hook schema.
windsurf~/.codeium/windsurf/hooks.json or .windsurf/hooks.jsonBest-effort: a generic-format wrapper until the schema stabilises.
all-Wire every supported agent at once.

Exit codes: 0 installed (or already present), 2 error (no home dir, unreadable / malformed config, write failure).

guard uninstall

Remove the guard hook that install added, idempotently. Removes only the Vulkro-managed entry; your own hooks and unrelated keys are preserved. Re-running after removal is a no-op.

guard status

Show which agents are wired to run vulkro guard check, the config file each uses, and whether the integration is solid or best-effort. Read-only.

Exit codes

  • check: 0 clean, 2 block (or operational error). See the protocol above; this differs from the usual contract on purpose.
  • install / uninstall / status: 0 success, 2 error.

Examples

# Make scanning mandatory in Claude Code, this project only.
vulkro guard install --agent claude-code --scope project

# See what is wired.
vulkro guard status

# Manual single-file check (generic protocol).
vulkro guard check src/handlers/users.ts

Command reference

Generated from vulkro help guard on vulkro 0.26.0. This block is the authoritative flag, usage, and exit-code reference for this command; the prose above is the friendly explanation. Do not edit this block by hand; run npm run docs:cli after a release.

Watch files as an AI coding agent writes them and feed findings back until they are clean.

In-loop security guard for AI coding agents: scan files as the agent writes them and feed findings back so the agent regenerates until the file is clean.

Where `vulkro mcp serve` exposes the scanner as an MCP TOOL the agent may choose to call (discovery), `vulkro guard` wires the scanner into the agent's HOOK system so a scan runs on every file write / edit whether the agent asks for it or not (enforcement). The two compose: run `mcp serve` for on-demand scans and `guard install` to make scanning mandatory in the write loop.

Fully offline and low-latency by design: each check is a single-file scan with no network call and no token cost, so it fits inside the agent's edit loop.

Sub-actions: * `check <FILE>` scan one file, emit a verdict the agent consumes. * `install` wire the hook into an agent's config. * `uninstall` remove the hook the guard installed. * `status` show which agents are wired.

Exit codes: see each sub-action's `--help`. `check` uses a hook exit/stdout protocol that differs from vulkro's usual 0/1/2 contract (a hook needs a non-zero "block" signal on findings); `install` / `uninstall` / `status` use 0 = success, 2 = error.

Usage: vulkro guard [OPTIONS] <COMMAND>

Commands:
check Scan a single file FAST and emit a verdict the agent loop can consume. Reuses the standard scanner (same result as `vulkro scan`), filtered to the one file, with no network call
install Idempotently wire `vulkro guard check` into an agent's hook config so it runs on every file write / edit. Merges into the existing config without clobbering unrelated keys; re-running does not duplicate. Prints the file it changed and how to undo
uninstall Remove the guard hook that `install` added, idempotently. Removes only the vulkro-managed entry; the user's own hooks and unrelated keys are preserved. Re-running after removal is a no-op
status Show which agents are wired to run `vulkro guard check`, the config file each uses, and whether the integration is solid or best-effort. Read-only
help Print this message or the help of the given subcommand(s)

Options:
--offline
Hard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). Blocks the CVE feed, the license heartbeat, the update check, webhooks, and any cloud AI endpoint; a loopback model (http://127.0.0.1, http://localhost) is still allowed. Equivalent to exporting VULKRO_OFFLINE=1, and the flag wins when both are set

-h, --help
Print help (see a summary with '-h')

$ vulkro guard check --help
------------------------------------------------------------
Scan a single file FAST and emit a verdict the agent loop can consume. Reuses the standard scanner (same result as `vulkro scan`), filtered to the one file, with no network call.

The file to scan comes from the `<FILE>` argument, or (when a `--hook-format` is set and no argument is given) from the path carried in the agent hook's stdin JSON (`tool_input.file_path` for Claude Code, `file_path` for Cursor).

Verdict: findings at HIGH or CRITICAL severity BLOCK (the agent should regenerate the file); MEDIUM / LOW / INFO are reported but do not block, so the guard never traps the agent on a stylistic nit.

Output / exit protocol depends on `--hook-format`: * `generic` (default): JSON verdict on stdout, regenerate instruction on stderr when blocked. Exit 0 = clean, exit 2 = block. NOTE: this differs from vulkro's usual 0/1/2 contract on purpose. A hook needs a non-zero "stop" code on findings, and Claude Code reserves exit 1 for a non-blocking error, so the guard uses 2 to mean "block and regenerate". * `claude-code`: always exit 0; `{"decision":"block","reason":...}` on a block (fed back to the model), empty object when clean. * `cursor`: always exit 0; `{"permission":"deny","agent_message": ...}` on a block, `{"permission":"allow"}` when clean.

Exit 2 in `generic` is the block signal (above); any format also exits 2 on an operational error (file missing, scan failure) with an actionable message on stderr.

Usage: vulkro guard check [OPTIONS] [FILE]

Arguments:
[FILE]
Path to the file to scan. Omit it only when piping agent hook JSON on stdin together with `--hook-format`

Options:
--hook-format <HOOK_FORMAT>
Output / exit protocol to emit. Defaults to `generic`

Possible values:
- generic: Agent-agnostic. A structured JSON verdict on stdout, the regenerate instruction mirrored to stderr on a block, and exit 2 = block / exit 0 = clean. Use this from a shell wrapper or any hook system not listed below. Exit 2 (not 1) signals "block" because Claude Code reserves exit 1 for a non-blocking error
- claude-code: Claude Code `PostToolUse` protocol. Always exit 0 (Claude Code only parses hook JSON on exit 0); on a block, emit `{"decision":"block","reason":...}` which Claude feeds back to the model so it regenerates. The target file is read from the hook's stdin JSON (`tool_input.file_path`) when no path argument is given
- cursor: Cursor `afterFileEdit` protocol. Always exit 0; emit `{"permission":"deny","agent_message":...}` on a block or `{"permission":"allow"}` when clean. The target file is read from the hook's stdin JSON (`file_path`) when no path argument is given

[default: generic]

--offline
Hard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). Blocks the CVE feed, the license heartbeat, the update check, webhooks, and any cloud AI endpoint; a loopback model (http://127.0.0.1, http://localhost) is still allowed. Equivalent to exporting VULKRO_OFFLINE=1, and the flag wins when both are set

-h, --help
Print help (see a summary with '-h')

$ vulkro guard install --help
------------------------------------------------------------
Idempotently wire `vulkro guard check` into an agent's hook config so it runs on every file write / edit. Merges into the existing config without clobbering unrelated keys; re-running does not duplicate. Prints the file it changed and how to undo.

Composes with `vulkro mcp serve`: the MCP server makes the scanner discoverable as a tool, the hook makes scanning mandatory.

Exit codes: 0 = installed (or already present), 2 = error (no home dir, unreadable / malformed config, write failure).

Usage: vulkro guard install [OPTIONS]

Options:
--agent <AGENT>
Which agent to wire. Defaults to `claude-code` (the most solid integration). `all` wires every supported agent

Possible values:
- claude-code: Claude Code (`~/.claude/settings.json` or `.claude/settings.json`). Solid: verified PostToolUse hook schema
- cursor: Cursor (`~/.cursor/hooks.json` or `.cursor/hooks.json`). Solid: verified afterFileEdit hook schema
- windsurf: Windsurf (`~/.codeium/windsurf/hooks.json` or `.windsurf/hooks.json`). Best-effort: a generic-format wrapper until the schema stabilises
- all: Wire every supported agent at once

[default: claude-code]

--offline
Hard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). Blocks the CVE feed, the license heartbeat, the update check, webhooks, and any cloud AI endpoint; a loopback model (http://127.0.0.1, http://localhost) is still allowed. Equivalent to exporting VULKRO_OFFLINE=1, and the flag wins when both are set

--scope <SCOPE>
Config scope: `user` (all projects, the default) or `project` (this repo only, checked into version control)

Possible values:
- user: The user's home config: applies to every project. Default
- project: The current project's config (`.claude/` or `.cursor/` in this repo): applies to this repo only and can be checked into version control

[default: user]

-h, --help
Print help (see a summary with '-h')

$ vulkro guard uninstall --help
------------------------------------------------------------
Remove the guard hook that `install` added, idempotently. Removes only the vulkro-managed entry; the user's own hooks and unrelated keys are preserved. Re-running after removal is a no-op.

Exit codes: 0 = removed (or nothing to remove), 2 = error.

Usage: vulkro guard uninstall [OPTIONS]

Options:
--agent <AGENT>
Which agent to unwire. Defaults to `claude-code`. `all` unwires every supported agent

Possible values:
- claude-code: Claude Code (`~/.claude/settings.json` or `.claude/settings.json`). Solid: verified PostToolUse hook schema
- cursor: Cursor (`~/.cursor/hooks.json` or `.cursor/hooks.json`). Solid: verified afterFileEdit hook schema
- windsurf: Windsurf (`~/.codeium/windsurf/hooks.json` or `.windsurf/hooks.json`). Best-effort: a generic-format wrapper until the schema stabilises
- all: Wire every supported agent at once

[default: claude-code]

--offline
Hard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). Blocks the CVE feed, the license heartbeat, the update check, webhooks, and any cloud AI endpoint; a loopback model (http://127.0.0.1, http://localhost) is still allowed. Equivalent to exporting VULKRO_OFFLINE=1, and the flag wins when both are set

--scope <SCOPE>
Config scope to unwire. Defaults to `user`

Possible values:
- user: The user's home config: applies to every project. Default
- project: The current project's config (`.claude/` or `.cursor/` in this repo): applies to this repo only and can be checked into version control

[default: user]

-h, --help
Print help (see a summary with '-h')

$ vulkro guard status --help
------------------------------------------------------------
Show which agents are wired to run `vulkro guard check`, the config file each uses, and whether the integration is solid or best-effort. Read-only.

Exit code: 0 always (status is informational).

Usage: vulkro guard status [OPTIONS]

Options:
--offline
Hard-disable every outbound network call for this run (sets VULKRO_OFFLINE=1). Blocks the CVE feed, the license heartbeat, the update check, webhooks, and any cloud AI endpoint; a loopback model (http://127.0.0.1, http://localhost) is still allowed. Equivalent to exporting VULKRO_OFFLINE=1, and the flag wins when both are set

--scope <SCOPE>
Config scope to report. Defaults to `user`

Possible values:
- user: The user's home config: applies to every project. Default
- project: The current project's config (`.claude/` or `.cursor/` in this repo): applies to this repo only and can be checked into version control

[default: user]

-h, --help
Print help (see a summary with '-h')