AI-powered code review tool for GitHub Pull Requests - CLI and GitHub Action
npm install @probelabs/visor
---
Visor ships with a ready-to-run configuration at defaults/.visor.yaml, so you immediately get:
- A staged review pipeline (overview → security → performance → quality → style).
- Native GitHub integration: check runs, annotations, and PR comments out of the box.
- Built‑in code assistant: trigger via PR/issue comments (e.g., /visor how it works?).
- A manual release-notes generator for tagged release workflows.
- No magic: everything is config‑driven in .visor.yaml; prompts/context are visible and templatable.
- Built for scale: composable checks, tag-based profiles, and flexible extends for shared policies.
``yaml`.github/workflows/visor.yml
name: Visor
on:
pull_request: { types: [opened, synchronize] } # For fork PRs, see docs/GITHUB_CHECKS.md
issues: { types: [opened] }
issue_comment: { types: [created] }
permissions:
contents: read
pull-requests: write
issues: write
checks: write
jobs:
visor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: probelabs/visor@v1
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} # or ANTHROPIC/OPENAI
`yaml`
version: "1.0"
steps: # or 'checks' (legacy, both work identically)
security:
type: ai
schema: code-review
prompt: "Identify security issues in changed files"
tags: ["fast", "security"]
Tip: Pin releases for stability, e.g. uses: probelabs/visor@v1.uses: probelabs/visor@nightly
For latest changes, use . The @main ref is maintained for compatibility but may change frequently and is not recommended for production.
- Node.js 18+ (CI runs Node 20)
- When used as a GitHub Action: appropriate permissions/secrets (see Security Defaults)
- One‑off run
`bash`
npx -y @probelabs/visor@latest --check all --output table
`
- Project dev dependency
bash`
npm i -D @probelabs/visor
npx visor --check all --output json
Short cheatsheet for common tasks:
`bashValidate configuration before running checks
visor validate # Search for .visor.yaml in current directory
visor validate --config .visor.yaml # Validate specific config file
See full CLI options: docs/commands.md
Additional guides:
- debugging and local development (TUI, debug server): docs/debugging.md
- fail conditions: docs/fail-if.md
- forEach behavior and dependent propagation (including outputs_raw and history precedence): docs/foreach-dependency-propagation.md
- Failure routing and
on_finish (with outputs_raw in routing JS): docs/failure-routing.md
- timeouts and provider units: docs/timeouts.md
- execution limits (run caps for safety): docs/limits.md
- output formatting limits and truncation controls: docs/output-formatting.md
- live execution visualizer and control API: docs/debug-visualizer.md
- scheduler for cron jobs and dynamic schedules: docs/scheduler.md🧪 Integration Tests
Write and run integration tests for your Visor config in YAML. No network, built‑in GitHub fixtures, strict by default, and great CLI output.
- Getting started: docs/testing/getting-started.md
- DSL reference: docs/testing/dsl-reference.md
- Flows: docs/testing/flows.md
- Fixtures & mocks: docs/testing/fixtures-and-mocks.md
- Assertions: docs/testing/assertions.md
- Cookbook: docs/testing/cookbook.md
- CLI & reporters: docs/testing/cli.md
- CI integration: docs/testing/ci.md
- Troubleshooting: docs/testing/troubleshooting.md
Note: examples use descriptive step names (e.g.,
extract-facts, validate-fact) to illustrate patterns. These are not built‑ins; the test runner works with whatever steps you define in .visor.yaml.🧩 Core Concepts (1 minute)
- Check – unit of work (
security, performance).
- Schema – JSON shape checks return (e.g., code-review).
- Template – renders results (tables/markdown).
- Group – which comment a check is posted into.
- Provider – how a check runs (ai, mcp, http, http_client, command, log, github, claude-code).
- Dependencies – depends_on controls order; independents run in parallel.
- Tags – label checks (fast, local, comprehensive) and filter with --tags.
- Events – PRs, issues, /review comments, webhooks, or cron schedules.Beyond Code Review
Visor is a general SDLC automation framework:
- PR Reviews – security/perf/style findings with native annotations
- Issue Assistant –
/visor … for code Q&A and triage
- Release Notes – manual or tagged release workflows
- Scheduled Audits – cron‑driven checks against main
- Webhooks & HTTP – receive events, call APIs, and post results
- Policy‑as‑Code – schemas + templates for predictable, auditable outputsTable of Contents
- 90‑second Quick Start
- Requirements
- Installation
- CLI Usage
- Core Concepts (1 minute)
- Beyond Code Review
- Features
- When to pick Visor
- Developer Experience Playbook
- Tag-Based Check Filtering
- PR Comment Commands
- Suppressing Warnings
- Troubleshooting
- Security Defaults
- Performance & Cost Controls
- Observability
- AI Configuration
- Step Dependencies & Intelligent Execution
- Failure Routing (Auto-fix Loops)
- Claude Code Provider
- GitHub Provider
- AI Session Reuse
- Schema-Template System
- Enhanced Prompts
- SDK (Programmatic Usage)
- Debugging
- Advanced Configuration
- HTTP Integration & Scheduling
- Pluggable Architecture
- GitHub Action Reference
- Output Formats
- Contributing
- Further Reading
- License
✨ Features
- Native GitHub reviews: Check runs, inline annotations, and status reporting wired into PRs.
- Config‑first: One
.visor.yaml defines checks, prompts, schemas, and templates — no hidden logic.
- Structured outputs: JSON Schema validation drives deterministic rendering, annotations, and SARIF.
- Orchestrated pipelines: Dependencies, parallelism, and tag‑based profiles; run in Actions or any CI.
- Multi‑provider AI: Google Gemini, Anthropic Claude, OpenAI, AWS Bedrock — plus MCP tools, standalone MCP provider, and Claude Code SDK.
- Author permissions: Built-in functions to customize workflows based on contributor trust level (owner, member, collaborator, etc).
- Assistants & commands: /review to rerun checks, /visor … for Q&A, predictable comment groups.
- HTTP & schedules: Receive webhooks, call external APIs, and run cron‑scheduled audits and reports.
- Extensible providers: ai, mcp, http, http_client, log, command, github, claude-code, human-input, memory — or add your own.
- Security by default: GitHub App support, scoped tokens, remote‑extends allowlist, opt‑in network usage.
- Observability & control: JSON/SARIF outputs, fail‑fast and timeouts, parallelism and cost control.When to pick Visor
- You want native GitHub checks/annotations and config‑driven behavior
- You need structured outputs (schemas) and predictable templates
- You care about dependency‑aware execution and tag‑based profiles
- You want PR reviews + assistants + scheduled audits from one tool
- You prefer open‑source with no hidden rules
🧭 Developer Experience Playbook
Start with the defaults, iterate locally, and commit a shared
.visor.yaml for your team.Example:
`bash
npx -y @probelabs/visor@latest --check all --debug
`Learn more: docs/dev-playbook.md
🏷️ Tag-Based Check Filtering
Run subsets of checks (e.g.,
local, fast, security) and select them per environment with --tags/--exclude-tags.Example:
`yaml
steps:
security-quick:
type: ai
prompt: "Quick security scan"
tags: ["local", "fast", "security"]
`CLI:
`bash
visor --tags local,fast
`Learn more: docs/tag-filtering.md
💬 PR Comment Commands
Trigger reviews and assistant actions via comments on PRs/issues.
Examples:
`
/review
/review --check security
/visor how does caching work?
`Learn more: docs/commands.md
🔐 Author Permissions
Customize workflows based on PR author's permission level using built-in functions in JavaScript expressions:
`yaml
steps:
# Run security scan only for external contributors
security-scan:
type: command
exec: npm run security:full
if: "!hasMinPermission('MEMBER')" # Auto-approve PRs from collaborators
auto-approve:
type: command
exec: gh pr review --approve
if: "hasMinPermission('COLLABORATOR') && totalIssues === 0"
# Block sensitive file changes from non-members
protect-secrets:
type: command
exec: echo "Checking permissions..."
fail_if: "!isMember() && files.some(f => f.filename.startsWith('secrets/'))"
`Available functions:
-
hasMinPermission(level) - Check if author has >= permission level
- isOwner(), isMember(), isCollaborator(), isContributor(), isFirstTimer() - Boolean checksLearn more: docs/author-permissions.md
🔇 Suppressing Warnings
Suppress a specific issue by adding a nearby
visor-disable comment.Example (JS):
`js
const testPassword = "demo123"; // visor-disable
`Learn more: docs/suppressions.md
🛠️ Troubleshooting
If comments/annotations don’t appear, verify workflow permissions and run with
--debug.Example:
`bash
node dist/index.js --cli --check all --debug
`Run modes
- Default is CLI mode everywhere (no auto-detection).
- For GitHub-specific behavior (comments, checks), run with
--mode github-actions or set with: mode: github-actions when using the GitHub Action.Examples:
`bash
Local/CI CLI
npx -y @probelabs/visor@latest --config .visor.yaml --check all --output jsonGitHub Actions behavior from any shell/CI
npx -y @probelabs/visor@latest --mode github-actions --config .visor.yaml --check all
`GitHub Action usage:
`yaml
- uses: probelabs/visor@vX
with:
mode: github-actions
checks: all
output-format: json
`To force CLI mode inside a GitHub Action step, you can still use:
`yaml
env:
VISOR_MODE: cli
`Learn more: docs/troubleshooting.md
🔐 Security Defaults
Prefer a GitHub App for production, and restrict remote extends unless explicitly allowed.
Examples:
`bash
visor --no-remote-extends
visor --allowed-remote-patterns "https://raw.githubusercontent.com/myorg/"
`Learn more: docs/security.md
⚡ Performance & Cost Controls
Use tags for fast lanes and raise parallelism cautiously.
Example:
`bash
visor --tags local,fast --max-parallelism 5
`Learn more: docs/performance.md
👀 Observability
Use JSON for pipelines or SARIF for code scanning. To avoid any chance of logs mixing with the result stream, prefer the built‑in
--output-file.Examples:
`bash
visor --check security --output json --output-file visor-results.json
visor --check security --output sarif --output-file visor-results.sarif
`Learn more: docs/observability.md
🤖 AI Configuration
Set one provider key (Google/Anthropic/OpenAI/AWS Bedrock) via env.
Example (Action):
`yaml
- uses: probelabs/visor@v1
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
# Or for AWS Bedrock:
# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# AWS_REGION: us-east-1
`Learn more: docs/ai-configuration.md
📊 Step Dependencies & Intelligent Execution
Define
depends_on to enforce order; independent checks run in parallel.Example:
`yaml
steps:
security: { type: ai }
performance:{ type: ai, depends_on: [security] }
`Learn more: docs/dependencies.md. See also: forEach dependency propagation
Quick example (outputs_raw):
`yaml
version: "2.0"
checks:
list:
type: command
exec: echo '["a","b","c"]'
forEach: true summarize:
type: script
depends_on: [list]
content: |
const arr = outputs_raw['list'] || [];
return { total: arr.length };
branch-by-size:
type: script
depends_on: [list]
content: 'return true'
on_success:
goto_js: |
return (outputs_raw['list'] || []).length >= 3 ? 'after' : null;
after:
type: log
message: bulk mode reached
`🔄 Failure Routing (Auto-fix Loops)
Automatically remediate failures and re‑run steps using config‑driven routing:
- Per‑step
on_fail and on_success actions:
- retry with fixed/exponential backoff (+ deterministic jitter)
- run: remediation steps (single or list)
- goto: jump back to an ancestor step and continue forward
- goto_js / run_js: dynamic routing with safe, synchronous JS
- Loop safety:
- Global routing.max_loops per scope to prevent livelock
- Per‑step attempt counters; forEach items have isolated countersExample (retry + goto on failure):
`yaml
version: "2.0"
routing:
max_loops: 5
steps:
setup: { type: command, exec: "echo setup" }
build:
type: command
depends_on: [setup]
exec: |
test -f .ok || (echo first try fails >&2; touch .ok; exit 1)
echo ok
on_fail:
goto: setup
retry: { max: 1, backoff: { mode: exponential, delay_ms: 400 } }
`Example (on_success jump‑back once):
`yaml
steps:
unit: { type: command, exec: "echo unit" }
build:
type: command
depends_on: [unit]
exec: "echo build"
on_success:
run: [notify]
goto_js: |
// Jump back only on first success
return attempt === 1 ? 'unit' : null;
notify: { type: command, exec: "echo notify" }
`Learn more: docs/failure-routing.md
🤖 Claude Code Provider
Use the Claude Code SDK as a provider for deeper analysis.
Example:
`yaml
steps:
claude-review:
type: claude-code
prompt: "Analyze code complexity"
`Learn more: docs/claude-code.md
🔄 AI Session Reuse
Reuse conversation context between dependent AI checks for smarter follow‑ups.
Two modes available:
-
clone (default): Independent copy of history for parallel follow-ups
- append: Shared conversation thread for sequential multi-turn dialogueExample:
`yaml
steps:
security: { type: ai }
remediation:
type: ai
depends_on: [security]
reuse_ai_session: true # Clones history by default
verify:
type: ai
depends_on: [remediation]
reuse_ai_session: true
session_mode: append # Shares history for full conversation
`You can also reuse the same check’s session when it loops back to itself, using:
-
reuse_ai_session: "self" with session_mode: appendSee the standalone example at
examples/session-reuse-self.yaml and the detailed guide in docs/advanced-ai.md.Learn more: docs/advanced-ai.md
📋 Schema-Template System
Schemas validate outputs; templates render GitHub‑friendly comments.
Example:
`yaml
steps:
security:
type: ai
schema: code-review
prompt: "Return JSON matching code-review schema"
`Learn more: docs/schema-templates.md
🎯 Enhanced Prompts
Write prompts inline or in files; Liquid variables provide PR context.
Example:
`yaml
steps:
overview:
type: ai
prompt: ./prompts/overview.liquid
`Learn more: docs/liquid-templates.md
📦 SDK (Programmatic Usage)
Run Visor programmatically from Node.js without shelling out. The SDK is a thin façade over the existing engine.
Install:
`bash
npm i -D @probelabs/visor
`ESM Example:
`ts
import { loadConfig, runChecks } from '@probelabs/visor/sdk';const config = await loadConfig();
const result = await runChecks({
config,
checks: Object.keys(config.checks || {}),
output: { format: 'json' },
});
console.log('Total issues:', result.reviewSummary.issues?.length ?? 0);
`CommonJS Example:
`js
const { loadConfig, runChecks } = require('@probelabs/visor/sdk');
(async () => {
const config = await loadConfig();
const result = await runChecks({
config,
checks: Object.keys(config.checks || {}),
output: { format: 'json' }
});
console.log('Total issues:', result.reviewSummary.issues?.length ?? 0);
})();
`Key Functions:
-
loadConfig(configPath?: string) — Load Visor config
- resolveChecks(checkIds, config) — Expand check IDs with dependencies
- runChecks(options) — Run checks programmaticallyLearn more: docs/sdk.md
🔍 Debugging
Comprehensive debugging tools help troubleshoot configurations and data flows:
$3
`bash
Basic CLI run
visor --config .visor.yaml --check allTUI mode - interactive chat interface for human-input workflows
visor --tui --config ./examples/calculator-config.yamlDebug server - web UI for stepping through execution
visor --debug-server --debug-port 3456Combine options for full visibility
visor --tui --config .visor.yaml --debug
`TUI Mode Features:
- Chat-style interface with persistent input bar
- Press
Tab to switch between Chat and Logs tabs
- Re-run workflows by typing new messages after completion
- Press q to exit when done$3
Use
log() in JavaScript expressions:
`yaml
steps:
conditional-check:
if: |
log("Outputs:", outputs);
outputs["fetch-data"]?.status === "ready"
transform_js: |
// output is auto‑parsed JSON when possible; no JSON.parse needed
log("Raw data:", output);
output
`Use
json filter in Liquid templates:
`yaml
steps:
debug-check:
type: logger
message: |
Outputs: {{ outputs | json }}
PR: {{ pr | json }}
`Enable debug mode:
`bash
visor --check all --debug
`Learn more: docs/debugging.md
🔧 Advanced Configuration
Extend shared configs and override per‑repo settings.
Example:
`yaml
extends:
- default
- ./team-standards.yaml
`Learn more: docs/configuration.md
🌐 HTTP Integration & Scheduling
Receive webhooks, call APIs, and schedule checks.
Examples:
`yaml
http_server: { enabled: true, port: 8080 }
steps:
nightly: { type: ai, schedule: "0 2 *" }
`Learn more: docs/http.md
🔧 Pluggable Architecture
Mix providers (
ai, mcp, http, http_client, log, command, script, github, claude-code) or add your own.- Command Provider: Execute shell commands with templating and security - docs/command-provider.md
- Script Provider: Run JavaScript in a secure sandbox - docs/script.md
- MCP Provider: Call MCP tools directly via stdio, SSE, or HTTP transports - docs/mcp-provider.md
- MCP Tools for AI: Enhance AI providers with MCP context - docs/mcp.md
- Custom Providers: Build your own providers - docs/pluggable.md
🎯 GitHub Action Reference
Common inputs include
max-parallelism, fail-fast, and config-path.Example:
`yaml
- uses: probelabs/visor@v1
with:
max-parallelism: 5
`Learn more: docs/action-reference.md
📊 Output Formats
Emit
table, json, markdown, or sarif.Example:
`bash
visor --check security --output json
`Learn more: docs/output-formats.md
🤝 Contributing
Learn more: CONTRIBUTING.md
📚 Further Reading
- Failure conditions schema: docs/failure-conditions-schema.md
- Failure conditions implementation notes: docs/failure-conditions-implementation.md
- Recipes and practical examples: docs/recipes.md
- ForEach outputs and precedence (outputs vs outputs_raw vs history): docs/foreach-dependency-propagation.md
- Failure routing and on_finish aggregation (with outputs_raw in routing): docs/failure-routing.md
- Example config using outputs_raw: examples/outputs-raw-basic.yaml
📄 License
MIT License — see LICENSE
---
🧰 GitHub Provider
Use the native GitHub provider for safe labels and comments without invoking the
gh CLI.Example — apply overview‑derived labels to a PR:
`yaml
steps:
apply-overview-labels:
type: github
op: labels.add
values:
- "{{ outputs.overview.tags.label | default: '' | safe_label }}"
- "{{ outputs.overview.tags['review-effort'] | default: '' | prepend: 'review/effort:' | safe_label }}"
value_js: |
return values.filter(v => typeof v === 'string' && v.trim().length > 0);
``See docs: docs/github-ops.md
Visor ships a YAML‑native integration test runner so you can describe user flows, mocks, and assertions alongside your config.
- Start here: docs/testing/getting-started.md
- CLI details: docs/testing/cli.md
- Fixtures and mocks: docs/testing/fixtures-and-mocks.md
- Assertions reference: docs/testing/assertions.md
Example suite: defaults/.visor.tests.yaml