From 9c0a3cbc17212871c6265bfa32ced9f1f43416e8 Mon Sep 17 00:00:00 2001 From: ajianaz Date: Fri, 16 Oct 2026 08:45:57 +0700 Subject: [PATCH] fix(cli): point help to agent mode and stop printing env secrets in --help Signed-off-by: ajianaz --- CHANGELOG.md | 8 +++++++ docs/cli-reference.md | 2 ++ src/main.rs | 30 ++++++++++++++++++++--- tests/cli_basic.rs | 56 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71275775..522b6383 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Security + +- **`--help` no longer prints secrets from the environment.** clap shows `[env: NAME=value]` for options bound to an env var, so `cora --help` with `CORA_API_KEY` or `GITHUB_TOKEN` set printed the key (into logs, CI output, screenshots). `--api-key` and `--token` now hide their env value. + +### Changed + +- **Help points to agent mode.** `cora --help`, `cora review/scan --help`, `cora mcp --help` and `cora install --help` now mention `--no-llm`, the agent-as-reviewer MCP tools, the `/cora-review` skill and `claude mcp add cora -- cora mcp`. + ## [0.19.0] - 2026-10-16 ### Added diff --git a/docs/cli-reference.md b/docs/cli-reference.md index c70fbcf6..b21dc2db 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -48,6 +48,8 @@ Complete command reference for the cora CLI. | `cora install --dry-run` | Show what would be changed | | `cora install --force` | Overwrite existing cora entry | | `cora install --yes` | Install ALL detected agents (non-interactive) | +| `cora mcp` | Start the MCP server (22 tools, incl. agent-as-reviewer: `cora.review_prepare`/`review_finalize`, `cora.scan_prepare`/`scan_finalize`; no LLM key needed in cora). See [Review with your agent](./usage#review-with-your-agent-no-llm-setup) | +| `claude mcp add cora -- cora mcp` | Register the MCP server with Claude Code (`cora install` only adds its `/cora-review` skill) | | `cora hook install` | Install pre-commit hook | | `cora hook uninstall` | Remove pre-commit hook | | `cora completion` `` | Generate shell completions (bash/zsh/fish/powershell) | diff --git a/src/main.rs b/src/main.rs index e0f43ee0..026c0702 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,10 @@ use formatters::OutputFormat; /// /// Review diffs and scan projects using any OpenAI-compatible LLM. /// Configure via .cora.yaml, CLI flags, or environment variables. +/// +/// No API key? `cora review --no-llm` / `cora scan --no-llm` run the +/// deterministic checks only, and an AI coding agent can do the AI review +/// itself through the MCP server (`cora mcp`, see `cora mcp --help`). #[derive(Parser, Debug)] #[clap( name = "cora", @@ -74,7 +78,7 @@ struct GlobalOptions { pub base_url: Option, /// API key (or set `CORA_API_KEY` env var for CI, or use `cora auth login`) - #[clap(long, global = true, env = "CORA_API_KEY")] + #[clap(long, global = true, env = "CORA_API_KEY", hide_env_values = true)] pub api_key: Option, /// Enable verbose logging @@ -246,6 +250,10 @@ enum Command { }, /// Review a git diff (staged, unpushed, or branch) + /// + /// Needs an LLM (BYOK). Without an API key, or with --no-llm, only the + /// deterministic checks run. To review with your AI coding agent instead + /// (no key in cora), see `cora mcp --help`. Review { /// Review staged changes (git diff --cached) #[clap(long)] @@ -321,7 +329,7 @@ enum Command { ref_name: Option, /// GitHub token for upload (default: `GITHUB_TOKEN` env var) - #[clap(long, env = "GITHUB_TOKEN")] + #[clap(long, env = "GITHUB_TOKEN", hide_env_values = true)] token: Option, /// Recall project patterns from Uteke before review (requires `uteke` CLI) @@ -339,6 +347,10 @@ enum Command { }, /// Scan a project directory for issues + /// + /// Needs an LLM (BYOK). Without an API key, or with --no-llm, only the + /// deterministic checks run. To scan with your AI coding agent instead + /// (no key in cora), see `cora mcp --help`. Scan { /// Root directory to scan (default: current directory) #[clap(long)] @@ -400,7 +412,7 @@ enum Command { ref_name: Option, /// GitHub token (default: `GITHUB_TOKEN` env var) - #[clap(long, env = "GITHUB_TOKEN")] + #[clap(long, env = "GITHUB_TOKEN", hide_env_values = true)] token: Option, }, @@ -443,6 +455,14 @@ enum Command { }, /// Start MCP server (Model Context Protocol for AI agents) + /// + /// Includes agent-as-reviewer tools: `cora.review_prepare` + + /// `cora.review_finalize` (a diff) and `cora.scan_prepare` + + /// `cora.scan_finalize` (a project). The calling agent judges with its own + /// model, so no LLM or API key is configured in cora. Register the server + /// with your agent via `cora install` (for Claude Code: + /// `claude mcp add cora -- cora mcp`). Results are advisory, not a CI + /// gate. See docs/usage.md, "Review with your agent". Mcp, /// Manage review findings (list, dismiss, reopen, stats) @@ -490,6 +510,10 @@ enum Command { filter: Option, }, /// Auto-detect and configure AI coding agents for Cora MCP + /// + /// Also installs the /cora-review skill for Claude Code + /// (~/.claude/skills/cora-review). Claude Code itself is registered with + /// `claude mcp add cora -- cora mcp`. Install { /// List detected agents without installing #[clap(long)] diff --git a/tests/cli_basic.rs b/tests/cli_basic.rs index 2717669d..2f994f76 100644 --- a/tests/cli_basic.rs +++ b/tests/cli_basic.rs @@ -196,3 +196,59 @@ fn init_without_force_fails_if_exists() { .failure() .stderr(predicate::str::contains("already exists")); } + +// ═══════════════════════════════════════════════════════════ +// Help points to agent mode, and never prints secrets from the environment +// ═══════════════════════════════════════════════════════════ + +#[test] +fn cli_mcp_help_explains_agent_as_reviewer() { + cora_cmd() + .args(["mcp", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("agent-as-reviewer")) + .stdout(predicate::str::contains("cora.review_prepare")) + .stdout(predicate::str::contains("cora.scan_finalize")) + .stdout(predicate::str::contains("claude mcp add cora")); +} + +#[test] +fn cli_install_help_mentions_the_skill() { + cora_cmd() + .args(["install", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("/cora-review")); +} + +#[test] +fn cli_review_and_scan_help_point_to_no_llm_and_agent_mode() { + for cmd in ["review", "scan"] { + cora_cmd() + .args([cmd, "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--no-llm")) + .stdout(predicate::str::contains("cora mcp --help")); + } +} + +#[test] +fn cli_help_does_not_print_secret_env_values() { + // clap prints `[env: NAME=value]` by default; a key in the environment + // must never end up in help output (logs, screenshots, CI). + for args in [ + &["--help"][..], + &["review", "--help"], + &["upload-sarif", "--help"], + ] { + cora_cmd() + .args(args) + .env("CORA_API_KEY", "sk-secret-do-not-print-123") + .env("GITHUB_TOKEN", "ghp_secret_do_not_print_456") + .assert() + .stdout(predicate::str::contains("sk-secret-do-not-print-123").not()) + .stdout(predicate::str::contains("ghp_secret_do_not_print_456").not()); + } +}