zuke.tools.ai

← Python API reference

@zuke/ai 2.13.0: @zuke/ai — AI-powered code review for Zuke builds.

Symbols

#

AiTasks

class
class AiTasks

Typed task functions over the reviewers, for a build that calls rather than declares them.

  • static async review(ctx: ServiceContext, configure: Callable[[ReviewSettings], ReviewSettings]) -> None

    Run the configured reviewers as peers of one target, in order. Throws when a reviewer's gate trips, as the target it guards would fail.

  • static async preflight(ctx: ServiceContext, configure: Callable[[ReviewSettings], ReviewSettings]) -> None

    Run the reviewers' preflight: on a comment-started run, refuse a commenter who may not start a review. A no-op on any other run.

#

DiffSettings

class
class DiffSettings(Settings)

Fluent diff source configuration passed to "./reviewer.ts".Reviewer.diff.

  • base(ref: str) -> Self

    Review the diff against ref (e.g. "origin/main").

  • staged() -> Self

    Review the staged changes (git diff --cached).

  • text(diff: str) -> Self

    Review a diff supplied directly, bypassing git (useful in tests).

  • fetch_base(branch: str | None=None, remote: str | None=None) -> Self

    Fetch the base branch (a shallow, tag-less git fetch) before diffing, and diff against it — so CI needs no manual git fetch step. With no branch, the base is auto-detected from the CI environment (GitHub's GITHUB_BASE_REF — the pull request's base branch). Honoured by the "./fixer.ts".AiFixer; if the fetch fails it falls back to the working-tree diff.

#

DiscussionSettings

class
class DiscussionSettings(Settings)

Fluent settings for "./reviewer.ts".Reviewer.discussion — who the reviewer listens to on the PR thread, and how much of it the model may see.

  • trust_associations(*associations: str) -> Self

    Replace the trusted author_association set (default OWNER, MEMBER, COLLABORATOR). Comments from authors outside it (and outside trustAuthors) are dropped before the model sees them.

  • trust_authors(*logins: str) -> Self

    Trust these author logins in addition to the association rule — e.g. an outside collaborator whose review the project wants the reviewer to engage with.

  • max_comment_tokens(tokens: float) -> Self

    Cap the total comment text sent to the model at roughly this many tokens (default 4000), newest comments kept first — so a wall of text cannot crowd the diff and the rubric out of the context window.

  • threads() -> Self

    Also anchor each finding to a file/line review thread on the pull request, so a maintainer contests it by replying in that thread instead of quoting its id somewhere on the PR. The summary comment is posted either way and stays the one source of truth: it lists every finding — anchored or not — and carries the state block, so a thread that cannot be posted never hides a finding. The reviewer replies into the thread with the outcome and resolves it once the finding is fixed or dismissed. GitHub only; on other hosts the reviewer notes that and posts the summary alone.

  • commands(mention: str) -> Self

    Take commands from maintainers, addressed to mention — e.g. "@zuke-build", the account the workflow's comment command is addressed to, so one handle serves both. With it set, every review comment carries a collapsed Commands panel listing what a maintainer can say, and a trusted comment starting with <mention> accept <id> <reason> accepts the finding: it is recorded as accepted for this pull request without adjudication, answered in its thread and the thread resolved, and never re-raised on the pull request by any reviewer, however it is reworded, moved or escalated. In a finding's own thread the id may be left out. The trust gate is the one rebuttals pass — trustAssociations and trustAuthors, decided in code from the host's author metadata — so a stranger's accept is dropped before anything reads it. Unlike a rebuttal, an acceptance is one key: no adjudicator weighs it. On a host where the default associations admit accounts that should not decide for the project (GitHub's MEMBER and COLLABORATOR include read-only ones), narrow the gate with trustAssociations or trustAuthors before turning commands on. The panel also lists <mention> review, the workflow's comment command that runs the review on demand; pair this with aiReviewWorkflow's command so that both are true.

#

GateSettings

class
class GateSettings(Settings)

Fluent gate configuration passed to "./reviewer.ts".Reviewer.failWhen.

  • score_above(value: float) -> Self

    Fail when the assessed risk score is strictly above value (0–10).

  • severity_at_least(value: Literal['none', 'low', 'medium', 'high', 'critical']) -> Self

    Fail when the overall severity is at least value.

#

Reviewer

class
class Reviewer(Settings)

A fluent AI reviewer. Construct one via securityReviewer (and the sibling factories), configure it, and attach it to a target with .validateBefore(...) / .validateAfter(...). .provider(...) and .apiKey(...) are required; everything else has a default.

  • provider(provider: Literal['claude', 'openai', 'gemini']) -> Self

    Set the model provider (required).

  • api_key(api_key: str) -> Self

    Set the API key, from a secret parameter or a literal string (required).

  • model(model: str) -> Self

    Override the model (default: the provider's recommended model).

  • effort(effort: Literal['low', 'medium', 'high', 'xhigh', 'max']) -> Self

    Set the thinking-effort hint (honoured by Claude; ignored elsewhere).

  • criteria(criteria: str) -> Self

    Optional project-specific notes appended above the diff in the user prompt — framing that fine-tunes the built-in rubric (e.g. "strict TypeScript, no any/as"). Works for every reviewer; the assessment's own system prompt already covers what to look for, so this is purely additive. These notes are read from the head under review, and cannot be anything else: they are build code, evaluated by the very build the pull request changed, so there is no base copy to read short of running the base's build. A branch can therefore widen what its own review overlooks, which is real but bounded — the change is first-party code in the diff a maintainer reads, and a build that could not configure its own reviewer from its own source would be a different feature. Reviewer.criteriaFile is the base-anchored form for notes that should not be editable by the change they judge.

  • diff(configure: Callable[[DiffSettings], DiffSettings]) -> Self

    Configure the diff source (default: the working-tree diff, git diff).

  • include(*globs: str) -> Self

    Only review files matching these globs (default: all files).

  • exclude(*globs: str) -> Self

    Exclude files matching these globs (in addition to lockfiles).

  • max_diff_tokens(tokens: float) -> Self

    Cap the diff at roughly this many tokens, truncating the rest.

  • fail_when(configure: Callable[[GateSettings], GateSettings]) -> Self

    Choose the gate that breaks the build (default: score above 7).

  • on_error(mode: Literal['fail', 'warn']) -> Self

    What to do when the review itself fails (API error, refusal, bad JSON): "fail" breaks the build (default), "warn" logs and passes.

  • skip_if_key_missing() -> Self

    Skip the review (instead of failing) when the API key is missing — handy when the key is a CI-only secret. The skip is announced on the console and in the job summary so the gap is visible.

  • comment(mode: Literal['update', 'append'] | None=None) -> Self

    Also post the review to the pull/merge request as a comment. Works on every supported CI host — GitHub Actions, GitLab CI, Azure Pipelines, Bitbucket Pipelines — dispatched at runtime by detectCiHost. A no-op outside a PR context (e.g. local runs). On each host the workflow must grant the right scope: GitHub pull-requests: write, GitLab a token with the api scope, Azure System.AccessToken, Bitbucket an app password. mode chooses how re-runs post: "update" (default) keeps a single comment per reviewer, edited in place; "append" posts a fresh comment every run, so earlier assessments — and their finding ids — stay on the thread as history. The discussion feature works with both: its state block rides on every comment, and the newest one is read back. Reviewers attached side by side to one target — .validateBefore(security, quality) — post one comment between them rather than one each: a table of every reviewer's verdict, the findings of all of them in one table with a column naming the reviewer (and its badge) that raised each, and each reviewer's full report folded underneath. Every one of them runs even when an earlier one fails its gate; the target then fails with all of their reasons. The comment is posted by the last of them, with its token, and appended when any of them appends. Each reviewer's discussion state rides in it under the reviewer's name, and a reviewer that used to post alone still reads its old comment. Another validation between two reviewers splits them, and a reviewer alone posts its own comment as before.

  • comment_token(token: str) -> Self

    The token used to post the PR/MR comment. Defaults to the active host's conventional env var: GITHUB_TOKEN (GitHub), GITLAB_TOKEN (GitLab), SYSTEM_ACCESSTOKEN (Azure), BITBUCKET_TOKEN (Bitbucket). A function is called each time a post needs the token — for a token that does not exist until the build mints it, such as a GitHub App installation token narrowed to pull_requests: write, which makes the comments the app's rather than github-actions[bot]'s. A function that mints should remember its result, since a review posts more than once. The generated workflow cannot name a secret for a function, so it passes the host's default token alongside, for the function to fall back to.

  • github_token(token: str) -> Self

    Backwards-compatible alias for commentToken.

  • reactions(enabled: bool) -> Self

    Whether the reviewer reacts on the pull request to show its progress (on by default, whenever .comment() is set). On the description: 👀 while it runs, then 👍 when it passes clean, 🎉 when it passes clean where the previous run failed, 🤏 when it has findings under the failing threshold (😕 on GitHub, whose reaction set has no 🤏), 👎 when it fails. On the comment that started a comment-started run: 👍 as it starts, 😕 when the run could not review. On a trusted maintainer's reply: 👀 once read, ❤️ when it decided a finding. The reviewers on one pull request share the reactions, so the verdict shown is the worst of a run's. GitHub and GitLab have the reactions; on Azure DevOps and Bitbucket this is a no-op. false turns all of it off, as does .quiet(), under which the reviewer does not speak on the pull request.

  • badge(emoji: str) -> Self

    The emoji that marks this reviewer's findings — in the combined comment the reviewers of one target share (see comment), and on the review threads it opens. Defaults by kind: 🛡️ security, 🧹 code quality (genericReviewer), 🔑 secrets, 🐛 correctness, ⚖️ license. Set one when two reviewers of a kind review side by side — and give one of them a name of its own, which is what the comment tells them apart by.

  • quiet() -> Self

    Suppress the findings printout and the job-summary section.

  • conventions_file(path: str, max_tokens: float | None=None) -> Self

    Feed the project's conventions document (e.g. AGENTS.md) to the model as reference material, so the review judges the change against the project's documented rules instead of generic taste. When the diff has a base ref (.diff((d) => d.base(...)) or a successful .fetchBase()), the file is read from that base via git show — never from the head under review, so a pull request cannot rewrite the rules it is judged by. Without a base (a local working-tree review) it is read from disk. Truncated at roughly maxTokens (default 8000).

  • criteria_file(path: str, max_tokens: float | None=None) -> Self

    Project-specific notes read from a file, appended to whatever Reviewer.criteria set inline — the base-anchored half of the same slot. Read exactly as Reviewer.conventionsFile is: from the diff's base ref via git show when there is one, from disk otherwise, truncated at roughly maxTokens (default 8000). That is the point of it. A note that tells the reviewer a design is accepted — and so suppresses the findings that restate it — is a rule the review is judged by, and a pull request should not be able to add one to its own run. Keeping it in a file the base supplies costs one merge of lag, which is the same lag the conventions document already accepts, and is arguably the point: a newly accepted design should be agreed before it starts muting findings. Use it for what a branch should not be able to widen; keep Reviewer.criteria for the framing that travels with the build. What it does not buy: the build still decides *whether* to read a criteria document at all, and that decision is head code, so a branch can drop the call as easily as it can edit a string. This bounds what a change can add to the rules of its own review, not whether the reviewer is configured — the same limit Reviewer.conventionsFile has, and the reason the diff a maintainer reads remains the control that matters.

  • file_context(max_tokens: float | None=None) -> Self

    Also send the full post-image contents of the changed files (read via git show HEAD:<path>), bounded at roughly maxTokens (default 12000) — so the model can check a finding against the surrounding code (an existing guard, a validation a few lines away) instead of judging hunks in isolation. Skipped silently for a literal .diff((d) => d.text(...)) source with no repository behind it.

  • verify() -> Self

    Add an adversarial verification pass: after the review produces candidate findings, a second model call re-checks each against the diff (and the fileContext, when enabled) and refutes any whose failure path it cannot concretely trace. Refuted candidates are listed in the report but neither posted as findings nor gated on. Costs one extra API call per review with findings; if the pass itself errors, the unverified findings are kept (fail toward reporting, never toward silence).

  • discussion(configure: Callable[[DiscussionSettings], DiscussionSettings] | None=None) -> Self

    Engage with the pull-request discussion instead of repeating findings: the reviewer reads the PR's comments, and when a trusted commenter (by the host's own author metadata — see DiscussionSettings) contests a finding by quoting its ID, an adjudication pass weighs the rebuttal on technical merit and either upholds the finding (with the gap named) or dismisses it. Dismissals persist across runs in a state block inside the reviewer's own PR comment, so a dismissed finding — or a rewording of it — does not resurface without new evidence. Requires comment (the comment is where state lives) and a host that can list comments (GitHub currently); elsewhere the discussion is skipped with a console note. Untrusted comments are dropped in code before any prompt is built — the model never sees them, so a drive-by "the maintainer approved this" comment cannot influence the review.

#

ReviewSettings

class
class ReviewSettings(Settings)

The reviewers one AiTasks.review runs, as .validateBefore(a, b) would run them, and the target they review for.

  • target(name: str) -> Self

    The target the reviewers are attached to, named in their output; review by default.

  • generic(configure: Callable[[Reviewer], Reviewer] | None=None) -> Self

    Add a code-quality reviewer, as genericReviewer makes it.

  • security(configure: Callable[[Reviewer], Reviewer] | None=None) -> Self

    Add a security reviewer, as securityReviewer makes it.

  • secrets(configure: Callable[[Reviewer], Reviewer] | None=None) -> Self

    Add a leaked-secrets reviewer, as secretsReviewer makes it.

  • correctness(configure: Callable[[Reviewer], Reviewer] | None=None) -> Self

    Add a correctness reviewer, as correctnessReviewer makes it.

  • license(configure: Callable[[Reviewer], Reviewer] | None=None) -> Self

    Add a license reviewer, as licenseReviewer makes it.