pi-extensions GitHub

pi-review — semantic review of a change

Two on-demand tools for the part of review a linter cannot do. Both are advisory: they return text, and neither blocks a tool call. lint_change and verify_findings stay deferred until tool search loads them.

lint_change

Judges the change against the preferences AGENTS.md states as prose, because tsc, oxlint, oxfmt, knip and fallow already own everything shape decides.

Rule What it asks
overengineered Would a senior engineer call this more complicated than the problem requires?
unrequested_abstraction A new seam — module, interface, generic, registry — that nothing calls for
speculative_scaffolding Code for a requirement that does not exist yet
unnecessary_dependency A package or helper for something stdlib, platform, or installed already covers
scope_creep Changes the task did not ask for (needs task)
narrating_comment An added comment that restates the next line or narrates the edit instead of recording a reason or constraint
weak_tests Added tests that would pass with the change reverted
message_matches_diff The subject describes something the diff does not do (needs message)
single_concern More than one logical change (needs message)
commit_type_correct fix/refactor/feat does not match the kind of change

The subject's mechanical shape is checked in code, not asked: a model applying a regex is how a lint starts disagreeing with itself. A missing type, a capital letter after the colon, a trailing period and a BREAKING CHANGE footer with no ! are reported by conventionalProblems.

Thresholds live beside each rule in rules.mjs. A rule that needs a commit message, a task statement or test files is left out of the request rather than asked against null state — a question answered from nothing comes back as a confident "no".

The diff is cut at 45,000 characters (the state limit is 60,000) and the output says when that happened, because a silent cut reads as a clean judgment.

blast_radius

No model call. Finds every exported function, const or class the diff adds, removes or edits (a hunk's enclosing definition counts, so a body-only edit is caught), greps the tracked code for each name, and lists the caller files outside the diff. A symbol no test file mentions is called out. It names the callers; proving the change safe for them is still a run. Reads the diff with explicit a/ b/ prefixes because diff.mnemonicPrefix changes the headers it parses. Checks the first 25 definitions.

verify_findings

Review findings that quote their code are checkable by string comparison: if the quoted text is not in the diff, the finding is about code that is not there and no judgment is needed. The survivors are asked two questions each, in one request:

Output is strongest first, each line carrying support, severity and whether the quotation was found. Dropped findings are listed separately with the reason.

Configuration

Shared with pi-typesafe: TYPESAFE_API_KEY (or LORE_TYPESAFE_API_KEY, or typesafe.apiKey in the lore config), TYPESAFE_MODEL, TYPESAFE_BASE_URL, TYPESAFE_TIMEOUT_MS. See shared/systemone-config.mjs. Inside Pi the request uses classify() when a TypeSafe classifier is registered.

Tests

npm test — packages/review/test/*.test.mjs covers routing, the mechanical subject checks, quote extraction and the deterministic drop. The network is stubbed; scripts/jev-questions.test.mjs drives the question sets through the production validators.