diff --git a/README.md b/README.md index e2cb55e..5c627ba 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,57 @@ -
- CodeBoarding Logo +# CodeBoarding Review - # CodeBoarding Visual Architecture Review +Review system design on every pull request, not just the diff. - Visual system-design review for pull requests. CodeBoarding analyzes the architecture before and after a change, then comments on the PR with an inline Mermaid diagram showing what changed. -
+CodeBoarding analyzes your architecture before and after a change, then comments on the PR with an inline Mermaid diagram of what changed — added, modified, and deleted components and the relationships between them. It runs the [CodeBoarding](https://github.com/CodeBoarding/CodeBoarding) engine in CI: static analysis combined with LLM reasoning. -## What It Does +[CodeBoarding](https://github.com/CodeBoarding/CodeBoarding) · [Website](https://codeboarding.org) · [Explore examples](https://codeboarding.org/diagrams) · [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) · [Discord](https://discord.gg/T5zHTJYFuy) + +[![JavaScript](https://img.shields.io/badge/JavaScript-222222?style=flat-square&logo=javascript&logoColor=F7DF1E)](https://developer.mozilla.org/en-US/docs/Web/JavaScript) +[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) +[![Java](https://img.shields.io/badge/Java-E76F00?style=flat-square&logo=openjdk&logoColor=white)](https://www.java.com/) +[![Python](https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/) +[![Go](https://img.shields.io/badge/Go-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev/) +[![PHP](https://img.shields.io/badge/PHP-777BB4?style=flat-square&logo=php&logoColor=white)](https://www.php.net/) +[![Rust](https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white)](https://www.rust-lang.org/) +[![C#](https://custom-icon-badges.demolab.com/badge/C%23-512BD4.svg?style=flat-square&logo=cshrp&logoColor=white)](https://learn.microsoft.com/en-us/dotnet/csharp/) + +## What it does - Builds or reuses a baseline architecture analysis for the PR base. - Runs incremental analysis on the PR head, then diffs components and relationships. -- Posts a sticky PR comment with an inline Mermaid map — 🟩 added · 🟨 modified · 🟥 deleted (dashed), for both nodes and edges. +- Posts a sticky PR comment with an inline Mermaid map. Green is added, yellow is modified, red (dashed) is deleted, for both nodes and edges. A PR comment looks like this: ```mermaid graph LR - Gateway["API Gateway"] - Auth["Auth Service"] - Cache["Cache"] - Gateway -- "routes to" --> Auth - Auth -- "reads/writes" --> Cache + Orchestration_Workflow_Manager["Orchestration & Workflow Manager"] + Incremental_Analysis_Controller["Incremental Analysis Controller"] + Static_Analysis_Engine["Static Analysis Engine"] + Agentic_Intelligence_Core["Agentic Intelligence Core"] + Health_Quality_Monitor["Health & Quality Monitor"] + Rendering_Output_Engine["Rendering & Output Engine"] + Persistence_Provider_Infrastructure["Persistence & Provider Infrastructure"] + Orchestration_Workflow_Manager -- "triggers change detection" --> Incremental_Analysis_Controller + Incremental_Analysis_Controller -- "passes filtered file sets" --> Static_Analysis_Engine + Static_Analysis_Engine -- "provides CFGs and symbol tables" --> Agentic_Intelligence_Core + Static_Analysis_Engine -- "supplies structural metrics" --> Health_Quality_Monitor + Agentic_Intelligence_Core -- "delivers summaries and diagrams" --> Rendering_Output_Engine + Health_Quality_Monitor -- "provides health reports" --> Rendering_Output_Engine + Persistence_Provider_Infrastructure -- "supplies LLM clients" --> Agentic_Intelligence_Core + Orchestration_Workflow_Manager -- "persists pipeline state" --> Persistence_Provider_Infrastructure classDef added fill:#1f883d,stroke:#0b5d23,color:#fff; classDef modified fill:#bf8700,stroke:#7d4e00,color:#fff; classDef deleted fill:#cf222e,stroke:#82071e,color:#fff,stroke-dasharray:5 3; - class Cache added; - class Auth modified; - class Gateway deleted; - linkStyle 0 stroke:#cf222e,stroke-width:2px,stroke-dasharray:5 3; - linkStyle 1 stroke:#1f883d,stroke-width:2px; + class Health_Quality_Monitor added; + class Static_Analysis_Engine,Agentic_Intelligence_Core modified; + class Persistence_Provider_Infrastructure deleted; + linkStyle 3,5 stroke:#1f883d,stroke-width:2px; + linkStyle 2 stroke:#bf8700,stroke-width:2px; + linkStyle 6,7 stroke:#cf222e,stroke-width:2px,stroke-dasharray:5 3; ``` -## Usage +## Quick start Create `.github/workflows/codeboarding.yml`: @@ -40,7 +60,7 @@ name: CodeBoarding review on: pull_request: - # Generate ONCE, when the PR becomes reviewable — not on every push, so you + # Generate once, when the PR becomes reviewable, not on every push, so you # don't spend an LLM job per commit. Use [opened] for strictly creation-only, # or add `synchronize` to re-run on each push. Refresh anytime with /codeboarding. types: [opened, reopened, ready_for_review] @@ -70,15 +90,15 @@ jobs: llm_api_key: ${{ secrets.OPENROUTER_API_KEY }} ``` -Add the API key as a repository secret (**Settings → Secrets and variables → Actions**): +Add the API key as a [repository secret](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets) (Settings → Secrets and variables → Actions): ```text OPENROUTER_API_KEY = sk-or-... ``` -That's the only required setup — it's passed via `llm_api_key` above. (For local runs with `scripts/run_local.sh`, export `OPENROUTER_API_KEY` as an env var instead.) +That is the only required setup, passed via `llm_api_key` above. For local runs with `scripts/run_local.sh`, export `OPENROUTER_API_KEY` as an environment variable instead. -**Models are optional.** Omit `agent_model` / `parsing_model` to use the engine's default for your provider, or pin them — inline or from a repository **variable** (a model name isn't a secret, so use `vars.`, not `secrets.`): +Models are optional. Omit `agent_model` and `parsing_model` to use the engine's default for your provider, or pin them inline or from a repository variable (a model name is not a secret, so use `vars.`, not `secrets.`): ```yaml with: @@ -87,11 +107,9 @@ That's the only required setup — it's passed via `llm_api_key` above. (For loc parsing_model: google/gemini-3-flash-preview # optional ``` -**Model format (OpenRouter):** a bare OpenRouter slug (e.g. `anthropic/claude-sonnet-4`) — exactly one `/`, **no `openrouter/` prefix** (that's the LiteLLM form; the action rejects it early). Other providers use their own native model ids. - ## Bring your own LLM provider -OpenRouter is the default, but you can use any provider the engine supports — set `llm_provider` and pass that provider's key: +OpenRouter is the default, but you can use any provider the engine supports. Set `llm_provider` and pass that provider's key: ```yaml with: @@ -99,13 +117,13 @@ OpenRouter is the default, but you can use any provider the engine supports — llm_api_key: ${{ secrets.ANTHROPIC_API_KEY }} ``` -`llm_provider: ` hands your key to the engine as `_API_KEY`, and the engine auto-selects that provider. Set **exactly one** key per run. +`llm_provider: ` hands your key to the engine as `_API_KEY`, and the engine auto-selects that provider. Set exactly one key per run. -
Supported providers +
Supported providers -| `llm_provider` | env var the engine reads | +| `llm_provider` | Environment variable the engine reads | |---|---| -| `openrouter` *(default)* | `OPENROUTER_API_KEY` | +| `openrouter` (default) | `OPENROUTER_API_KEY` | | `openai` | `OPENAI_API_KEY` | | `anthropic` | `ANTHROPIC_API_KEY` | | `google` | `GOOGLE_API_KEY` | @@ -116,31 +134,31 @@ OpenRouter is the default, but you can use any provider the engine supports — | `aws_bedrock` | `AWS_BEARER_TOKEN_BEDROCK` | | `ollama` | `OLLAMA_BASE_URL` | -This table mirrors the engine and may lag it — the source of truth is the engine's provider registry ([`agents/llm_config.py`](https://github.com/CodeBoarding/CodeBoarding/blob/main/agents/llm_config.py)). Any provider it adds that follows the `_API_KEY` convention works here with no action change. +This table mirrors the engine and may lag it. The source of truth is the engine's provider registry, [`agents/llm_config.py`](https://github.com/CodeBoarding/CodeBoarding/blob/main/agents/llm_config.py). Any provider it adds that follows the `_API_KEY` convention works here with no action change.
## When it runs -- **PR opened / reopened / marked ready** — generated once (per the `on:` triggers above). It does **not** re-run on every push, so you never spend an LLM job per commit; the comment reflects that point until refreshed. -- **`/codeboarding` comment** — a trusted collaborator (`OWNER`/`MEMBER`/`COLLABORATOR`) regenerates the diagram against the **current** PR head, even if one already exists. It re-runs and updates the same comment in place (the action reacts with 👀). Change the keyword via `trigger_command`. +- On a PR being opened, reopened, or marked ready for review, the diagram is generated once (per the `on:` triggers above). It does not re-run on every push, so you never spend an LLM job per commit; the comment reflects that point until refreshed. +- On a `/codeboarding` comment, a trusted collaborator (`OWNER`, `MEMBER`, or `COLLABORATOR`) regenerates the diagram against the current PR head, even if one already exists. It re-runs and updates the same comment in place. Change the keyword via `trigger_command`. -The command needs the `issue_comment` trigger and runs from your **default branch** (GitHub's rule), so it only works once the workflow is merged there. On-demand runs on fork PRs are refused, so fork code is never analyzed with your secrets. +The command needs the `issue_comment` trigger and runs from your default branch (a GitHub rule), so it only works once the workflow is merged there. On-demand runs on fork PRs are refused, so fork code is never analyzed with your secrets. ## Inputs | Input | Default | Description | |---|---|---| | `llm_api_key` | required | Your LLM provider API key (see `llm_provider`). | -| `llm_provider` | `openrouter` | Provider for the key — mapped to `_API_KEY` (e.g. `anthropic`, `openai`, `google`). | -| `github_token` | `${{ github.token }}` | Token used to post/update the PR comment. | +| `llm_provider` | `openrouter` | Provider for the key, mapped to `_API_KEY` (e.g. `anthropic`, `openai`, `google`). | +| `github_token` | `${{ github.token }}` | Token used to post or update the PR comment. | | `engine_ref` | `v0.12.0` | CodeBoarding engine ref. Pin for reproducibility. | | `depth_level` | `1` | Analysis depth, 1 to 3. Higher is slower and richer. | | `render_depth` | `1` | Display depth for the PR diagram. Keep `1` for a clean top-level view. | | `diagram_direction` | `LR` | Mermaid direction: `LR`, `TD`, `TB`, `RL`, or `BT`. | | `changed_only` | `false` | Render only changed components and incident edges. | -| `agent_model` | engine default | Analysis model. Bare OpenRouter slug (e.g. `anthropic/claude-sonnet-4`); empty = engine's per-provider default. | -| `parsing_model` | engine default | Parsing model. Bare OpenRouter slug; empty = engine's per-provider default. | +| `agent_model` | `google/gemini-3-flash-preview` | Analysis model. OpenRouter default shown; other providers use their own engine default. | +| `parsing_model` | `google/gemini-3.1-flash-lite-preview` | Parsing model. OpenRouter default shown; other providers use their own engine default. | | `comment_header` | `Architecture review` | Heading for the PR comment. | | `trigger_command` | `/codeboarding` | Slash command for trusted on-demand runs. | | `cta_base_url` | empty | Optional click-proxy base URL for editor and extension links. | @@ -160,7 +178,7 @@ The command needs the `issue_comment` trigger and runs from your **default branc - Do not use `pull_request_target` for this action. It can expose secrets to PR-head code. - GitHub renders Mermaid in strict mode, so node click-through links are not supported in the PR diagram. -## Local Testing +## Local testing Fast path, no LLM calls: diff --git a/action.yml b/action.yml index 24bcf5f..a142d58 100644 --- a/action.yml +++ b/action.yml @@ -27,11 +27,11 @@ inputs: required: false default: '1' agent_model: - description: 'Analysis model (AGENT_MODEL env var). A bare OpenRouter slug, e.g. anthropic/claude-sonnet-4. Empty (default) uses the engine''s own per-provider default.' + description: 'Analysis model (AGENT_MODEL env var). A bare OpenRouter slug. Defaults to google/gemini-3-flash-preview on OpenRouter; for other providers, empty uses the engine''s per-provider default.' required: false default: '' parsing_model: - description: 'Parsing model (PARSING_MODEL env var). A bare OpenRouter slug. Empty (default) uses the engine''s own per-provider default.' + description: 'Parsing model (PARSING_MODEL env var). A bare OpenRouter slug. Defaults to google/gemini-3.1-flash-lite-preview on OpenRouter; for other providers, empty uses the engine''s per-provider default.' required: false default: '' comment_header: @@ -155,6 +155,21 @@ runs: gh api -X POST "repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \ -f content=eyes >/dev/null 2>&1 || true + - name: Post in-progress comment + if: steps.guard.outputs.skip != 'true' + continue-on-error: true + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: codeboarding-architecture-diff + number: ${{ steps.guard.outputs.pr_number }} + message: | + ### ${{ inputs.comment_header }} · analyzing… + + ⏳ CodeBoarding is analyzing the architecture changes in this PR. This usually takes a few minutes. + + codeboarding-action · run ${{ github.run_id }} + GITHUB_TOKEN: ${{ inputs.github_token }} + - name: Checkout CodeBoarding engine if: steps.guard.outputs.skip != 'true' uses: actions/checkout@v4 @@ -282,6 +297,10 @@ runs: echo "Provider: $PROVIDER -> $PROVIDER_ENV; key length: ${#KEY}" if [ "$PROVIDER" = "openrouter" ]; then + # Default models on OpenRouter when the user didn't pin one (cheap Gemini). + # Other providers fall through to the engine's own per-provider default. + AGENT_MODEL="${AGENT_MODEL:-google/gemini-3-flash-preview}" + PARSING_MODEL="${PARSING_MODEL:-google/gemini-3.1-flash-lite-preview}" # OpenRouter-only checks. The litellm 'openrouter/...' model prefix 400s # the engine's native OpenRouter call; other providers use native ids. for M in "$AGENT_MODEL" "$PARSING_MODEL"; do diff --git a/scripts/run_local.sh b/scripts/run_local.sh index b575c02..34cb2b3 100755 --- a/scripts/run_local.sh +++ b/scripts/run_local.sh @@ -65,8 +65,8 @@ run_engine() { PROJECT_ROOT="$ENGINE" \ DIAGRAM_DEPTH_LEVEL="$DEPTH" \ CACHING_DOCUMENTATION="false" \ - ENABLE_MONITORING="false" \ - OPENROUTER_API_KEY="${OPENROUTER_API_KEY:-}" + ENABLE_MONITORING="false" + # OPENROUTER_API_KEY is inherited from the environment (full mode requires it). # Pass the model only when set; empty -> engine's own valid per-provider default. if [ -n "$AGENT_MODEL" ]; then export AGENT_MODEL; fi if [ -n "$PARSING_MODEL" ]; then export PARSING_MODEL; fi @@ -78,8 +78,9 @@ if [ -n "$BASE_JSON" ] && [ -n "$HEAD_JSON" ]; then BASE_ANALYSIS="$BASE_JSON" HEAD_ANALYSIS="$HEAD_JSON" else - [ -n "$REPO" ] && [ -n "$BASE_REF" ] && [ -n "$HEAD_REF" ] || { - echo "Need either --base-json/--head-json, or --repo/--base/--head." >&2; exit 2; } + if [ -z "$REPO" ] || [ -z "$BASE_REF" ] || [ -z "$HEAD_REF" ]; then + echo "Need either --base-json/--head-json, or --repo/--base/--head." >&2; exit 2 + fi [ -d "$ENGINE" ] || { echo "Engine not found at $ENGINE (set --engine or \$ENGINE)." >&2; exit 2; } [ -n "${OPENROUTER_API_KEY:-}" ] || { echo "Export OPENROUTER_API_KEY for the full pipeline." >&2; exit 2; } REPO="$(cd "$REPO" && pwd)"