- Python 97.2%
- Just 1.2%
- Shell 0.9%
- Dockerfile 0.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
/ pr-review (push) Has been skipped
/ pr-review (release) Has been skipped
/ gitleaks (push) Successful in 24s
/ gitleaks (release) Successful in 27s
/ checks (push) Successful in 1m55s
/ publish-container (push) Has been skipped
/ checks (release) Successful in 1m49s
/ deploy-docs (release) Has been skipped
/ deploy-docs (push) Successful in 1m1s
/ publish-container (release) Successful in 4m25s
|
||
| .forgejo/workflows | ||
| docs | ||
| src/wuming | ||
| tests | ||
| tools | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| .python-version | ||
| action.yml | ||
| AGENTS.md | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| Containerfile | ||
| Improvements.md | ||
| justfile | ||
| mkdocs.yml | ||
| noxfile.py | ||
| pylock.toml | ||
| pyproject.toml | ||
| README.md | ||
| Release-Notes.md | ||
| Release-Notes.md.template | ||
| run-wuming.fish | ||
| uv.lock | ||
| wuming.pub | ||
WuMing (无名) — AI PR Review for Forgejo
WuMing (无名, wúmíng) — Chinese for "nameless". A reviewer with no identity of its own: it speaks only through the code it examines.
Fetches a PR diff from the Forgejo/Codeberg API, routes changed files to specialised
AI agents, and posts inline review comments back to the PR. Works as a
Woodpecker CI plugin (image: step) and as a native
Forgejo/GitHub Actions action (uses: step). No Python to install in consumer repos.
Status
Available as a container image at registry.marvin8.zone/marvin8/wuming. Implements the
Forgejo client, diff parser, multi-agent router (code, config, docs, shell, security),
DeepSeek, Anthropic, and Ollama backends, comment deduplication, PR size gate, and
OWASP Top 10 security review. Not yet production-hardened — use with caution and
report issues on the issue tracker.
Verifying images
Images are signed with cosign v2 using a managed
key; the public key lives in this repository as wuming.pub. Every
image also carries a signed CycloneDX SBOM attestation.
With the cosign CLI
curl -O https://forge.marvin8.zone/marvin8/wuming/raw/branch/main/wuming.pub
cosign verify --key wuming.pub --insecure-ignore-tlog registry.marvin8.zone/marvin8/wuming:latest
cosign verify-attestation --key wuming.pub --insecure-ignore-tlog --type cyclonedx registry.marvin8.zone/marvin8/wuming:latest
With podman (pull-time enforcement)
Podman (via containers/image) verifies signatures at pull time, so podman run,
quadlets, and CI runners using podman are all covered by two config files plus
the public key. Rootless podman reads them from ~/.config/containers/, root
podman (e.g. a runner using the docker-compatible socket) from /etc/containers/.
# containers/registries.d/cosign.yaml — enable reading cosign attachments
docker:
registry.marvin8.zone:
use-sigstore-attachments: true
// containers/policy.json — add under "transports" → "docker"
// (keep "default" permissive; scope the rule to this registry only)
"registry.marvin8.zone/marvin8/wuming": [
{
"type": "sigstoreSigned",
"keyPath": "/etc/pki/containers/wuming.pub",
"signedIdentity": { "type": "matchRepository" }
}
]
matchRepository is required: cosign signatures only carry repository identity.
Unsigned or wrongly-signed images are then refused at pull time.
Images are signed with cosign v2-format signatures (
sha256-<digest>.sigtags). cosign v3 stores signatures as OCI referrers, which podman cannot yet verify.
Quick start
# .woodpecker/ai-review.yaml in your repo
when:
event: pull_request
steps:
- name: ai-review
image: registry.marvin8.zone/marvin8/wuming:latest
environment:
WUMING_FORGEJO_TOKEN:
from_secret: codeberg_token
WUMING_DEEPSEEK_KEY:
from_secret: deepseek_api_key
WUMING_AGENTS: code,config
To use the Anthropic backend instead, swap the API key and set the backend:
WUMING_ANTHROPIC_KEY:
from_secret: anthropic_api_key
WUMING_BACKEND: anthropic
Forgejo/GitHub Actions
Forgejo Actions (Codeberg / self-hosted Forgejo):
# .forgejo/workflows/ai-review.yml
on:
pull_request:
jobs:
ai-review:
runs-on: codeberg-tiny
steps:
- uses: https://forge.marvin8.zone/marvin8/wuming@main
with:
forgejo_token: ${{ secrets.WUMING_FORGEJO_TOKEN }}
deepseek_key: ${{ secrets.WUMING_DEEPSEEK_KEY }}
agents: code,config
uses: https://forge.marvin8.zone/marvin8/wuming@<ref> builds the Containerfile
from that ref on your runner — @main runs the latest commit, and pinning
@v0.8.0 runs that release. Your runner needs a container builder (Docker or
Podman) and access to pull the base images.
GitHub Actions (github.com): use uses: docker://registry.marvin8.zone/marvin8/wuming:latest instead of uses: https://forge.marvin8.zone/marvin8/wuming@main — WuMing is hosted on forge.marvin8.zone and has no GitHub mirror. Also replace the with: block with an env: block using WUMING_* variable names (e.g. WUMING_FORGEJO_TOKEN, WUMING_DEEPSEEK_KEY) — when GitHub Actions uses docker://, it does not process action.yml, so with: keys are not mapped to WUMING_* environment variables.
See the Forgejo/GitHub Actions guide for all inputs.
Configuration
| Variable | Default | Description |
|---|---|---|
WUMING_FORGEJO_TOKEN |
— | Forgejo API token (required) |
WUMING_FORGEJO_URL |
https://forge.marvin8.zone |
Forgejo instance base URL |
WUMING_DEEPSEEK_KEY |
— | DeepSeek API key |
WUMING_AGENTS |
code |
Comma-separated agent list; valid names: code, config, docs, performance, security, shell, tests; use name:model to set per-agent model (model names are backend-specific, e.g. code:deepseek-v4-flash,docs) |
WUMING_MAX_TOKENS |
2048 |
Max output tokens per backend call; auto-raised to 16384 for all DeepSeek and Moonshot (Kimi) calls |
WUMING_SKIP_PATHS |
— | Additional glob patterns to skip |
WUMING_BACKEND |
deepseek |
deepseek | anthropic | ollama | moonshot |
WUMING_OLLAMA_URL |
— | Ollama base URL (required if backend=ollama) |
WUMING_ANTHROPIC_KEY |
— | Anthropic API key (required if backend=anthropic) |
WUMING_MOONSHOT_KEY |
— | Moonshot (Kimi) API key (required if backend=moonshot) |
WUMING_PROGRESS |
— | true/1 force on; false/0 force off; when unset or empty, auto-detects: enabled when stdout is a TTY and CI env var is absent |
WUMING_MAX_DIFF_LINES |
0 |
Maximum raw diff size in lines. When the diff exceeds this value and it is greater than 0, WuMing posts a single plain PR comment explaining the PR is too large to review automatically, then exits without posting inline comments. Set to 0 to disable. |
WUMING_MAX_FILE_LINES |
0 |
Maximum file size (in lines) for full-file context fetching. When greater than 0, WuMing fetches the complete content of each changed file from Forgejo and includes it in the agent's context, giving the model visibility beyond the diff hunks. Files exceeding this line count are excluded. Set to 0 (the default) to disable. |
WUMING_DRY_RUN |
false |
When true, skips all Forgejo write calls (no comments posted) and logs the would-be comment bodies to stdout instead. All read operations still run. |
WUMING_VERBOSE |
false |
When true, sets the wuming logger to DEBUG level. Each backend logs the full LLM request payload (including diff text and file contents) and raw response before parsing. Treat these logs as sensitive — they may contain repository source code. |
WUMING_SUMMARY_COMMENT |
false |
When true, posts a plain PR comment after the review with a per-agent severity count table. Posted even when no new inline comments are found. Skipped when the diff-too-large early exit fires. |
Agents
| Agent | Files | Focus |
|---|---|---|
code |
*.py, *.go, *.ts, … |
Correctness, logic bugs |
config |
*.toml, *.yaml, *.json |
Schema, secret hygiene |
docs |
*.md, *.rst |
Clarity, accuracy |
performance |
*.py, *.go, *.ts, *.sql, … |
N+1 queries, O(n²) loops, I/O in loops, missing indexes |
security |
code + config + shell files | OWASP Top 10 security vulnerabilities |
shell |
*.sh, Dockerfile, CI files |
Shell safety, image hygiene |
tests |
*.py, *.go, *.ts, … |
Assertion quality, global state teardown, coverage gaps |
Handling large PRs
WuMing has two mechanisms to stay within token and API limits when a PR is unusually large.
Size gate (WUMING_MAX_DIFF_LINES): if the total raw diff line count exceeds this
value, WuMing posts a single plain PR comment explaining that the PR is too large to
review automatically, then exits without posting any inline comments. Set to 0
(the default) to disable the gate entirely.
Hunk size cap: individual diff hunks longer than 300 lines are replaced with a
single stub line showing the line count, e.g.
[... 450 lines truncated — too large for review ...]. The hunk header (filename and
position) is preserved so the agent still knows which file and region was affected.
Woodpecker CI variables injected automatically
| Variable | Description |
|---|---|
CI_REPO_OWNER |
Repository owner |
CI_REPO_NAME |
Repository name |
CI_COMMIT_PULL_REQUEST |
PR number (absent on non-PR events) |
CI_COMMIT_SHA |
Head commit SHA |
About
This project was developed with the assistance of Claude Code. All code has been reviewed by a human before merging. WuMing also reviews its own pull requests — a small demonstration of the workflow it enables.