Repository navigation
Upgrading from 0.4: passes remembered by 0.4 are asked once more, because
passed.json now records the score and grader of each pass. grip grade no longer
takes --passing-score; the pass mark comes from the configuration. The Keep A Grip
commands moved under grip platform, and their old names still work for now.
Added
retry_questionsoption:same(the default) keeps reusing a diff's questions and
board on a retry;newforgets them when a quiz, a whiteboard defense orgrip grade
fails, so the next try asks new ones. Grading feedback now says where to look again
and what kind of point is missing, instead of naming the rubric point, so a failed
quiz can no longer be passed by pasting its feedback back.[[paths]]tables in.grip.toml(or[[tool.grip.paths]]inpyproject.toml) set
apassing_score, adifficultyorskip = truefor the files that match their
matchglobs. A diff takes the strictest pass mark and the hardest difficulty of the
files it touches, and a diff made only of skipped files is not quizzed.- A GitHub Action (
uses: guilyx/grip@...): on each pull request it writes five
questions about the diff since the merge base and posts them as one comment, updated on
every push, for the author or a reviewer to answer in review. Nothing is graded in CI.
export-board: truealso uploads the whiteboard as a workflow artifact and links it. - Diffs with nothing to explain are skipped, with the reason printed: only renames or mode
changes, only whitespace (a re-indent still counts), only paths in the newskip_paths
option (e.g.["docs/**", "*.md"]), or fewer added and removed lines than the new
min_changed_linesoption (off by default).grip check, the agent push gate and
grip statusagree with the hook;grip quiz --forcequizzes such a diff anyway. - The model reads the messages of the commits being quizzed (pushes,
--unpushed,
--branch,--range), up to 4 KB, as delimited data next to the diff, so questions can
ask why and grading can weigh what the developer said they meant. Rewording a commit gets
new questions.include_commit_messages = falsesends the diff alone. grip appeal <question> "<why my answer was right>"has one question of the last graded
quiz graded again by the same provider, with the justification as data. It never lowers
a grade, allows one appeal per question per quiz, is saved in the report and the history
and shown bygrip last, and records a pass when the new score reaches the pass mark.- grip measures what a quiz costs. Each graded quiz records the wall time of every model
request and, where the provider reports it (the Anthropic and OpenAI-compatible APIs,
not the coding-agent CLIs), the tokens used; the result ends with
took 9.8 s · 3,140 tokens,grip lastshows it per request,grip statusaverages the
last 20 quizzes (an appealed quiz counts once), andgrip gradeandgrip last --json
include it. Agrip appealrequest is added to its quiz as anappealcall. The FAQ
explains how to turn tokens into a cost with your provider's prices.
Changed
- An empty diff in
grip quiz,grip whiteboardandgrip asksays what there is
instead and what to run, e.g. "nothing staged. 3 untracked files and 1 modified file:
git addthem first; 2 unpushed commits: trygrip quiz --unpushedor--branch".
grip askadds the same as data in ahintfield. The "Try it" line at the end of
grip initsuggests a command that has something to quiz in that repository. grip initwritesprovider = "auto"instead of this machine's coding agent, so the
committed.grip.tomlworks for teammates without that tool. It prints whatauto
means on this machine, listsautofirst in its menu, and writes a concrete provider
only when you pick one. It now also asks for the difficulty (defaultnormal) and says
"wrote AGENTS.md for any coding agent that reads it" instead of "for AGENTS.md
readers".--provider autois accepted on every command.- A missing API key for the
anthropicoropenaiprovider is reported before the
quiz starts, instead of after the "reading your diff" spinner. The message lists the
fixes: set the key variable (api_key_env,ANTHROPIC_API_KEYby default), use a
coding agent installed on the machine, or try--provider fake. Theopenaiprovider
no longer asks for a key whenbase_urlpoints at a server of your own. - The quiz header names what is being quizzed in words:
3 commits on feat/x → origin
for a push,main..HEADas typed for--range,2 unpushed commits on mainfor
--unpushed, andeverything since the first commitinstead of the empty tree's hash
for a first push. When the diff is overmax_diff_bytesit also says how much the model
saw:grip saw the first 200 KB of a 790 KB change (max_diff_bytes). grip --helplists the core commands first (init, quiz, whiteboard, status, skip,
resume, last), groups the rest, and ends with examples. The Keep A Grip commands moved
undergrip platform(grip platform login,solve,submit, …); the old top-level
names still work and print a one-line note that they moved.hook,agent-hook,
statusline,askandgradeare no longer listed in the help but work as before.grip configshows where each value comes from (default,pyproject.toml,
.grip.toml,env GRIP_…or a flag) and which configuration files it read, and takes
--provider,--model,--passing-scoreand--difficultyto preview a run with
those flags.grip statusshows a short summary instead of the full table: provider,
pass mark, difficulty and hook mode.- Docs: new pages on cost and privacy per provider, rolling grip out to a team, removing
it, and a glossary. The FAQ, honest numbers and home page no longer say that every run
asks new questions or thatanthropicis the default provider, the README says the
offlinefakegrader only checks answer length, and the Mitchell Hashimoto quote links
to his post. - Whiteboard page: handing in with questions unanswered asks first ("2 unanswered will
score 0"); answers show a counter against the 4,000-character limit; the open question
lights up the box or arrow it is pinned to, and graded arrows take their colour; an
answered pin gets a tick instead of turning black; a score below the pass mark comes
with the command to run again and a Copy button. Interview mode says "Score interview"
and shows the ticks at full contrast after scoring. Exports show the questions without
an answer box. On a phone, a tap within 22px of a pin opens it, the question scrolls
into view, and the legend no longer scrolls away with the drawing.
Fixed
--range main...HEAD(three dots) no longer fails withbad revision '.HEAD'. It diffs
from the merge base, as git does, ingrip quiz,ask,checkandwhiteboard.
main..andmain...run up toHEAD, and a malformed range is rejected with the
expected form.- Git failures are one plain line instead of git's command line and stderr: "not inside a
git repository (run grip from your project)", "this repository has no commits yet",
"unknown revision 'nope'".GRIP_DEBUG=1also prints the full git command and output.
grip statusworks in a repository with no commits yet. grip quizwithout an interactive terminal (grip quiz </dev/null, a script) exits 2
and points togrip ask/grip gradeorgrip whiteboard, instead of skipping with
exit 0. Hooks still skip unlessrequire_ttyis set, and the notice no longer reads
"grip: grip needs".- A pasted answer that ends with an empty line ends there, instead of waiting for one
more Enter. Empty lines between pasted paragraphs are still kept. grip checkwithout a scope flag checks what the installed hooks quiz (the unpushed
commits for the default pre-push hook) instead of always the staged changes, which said
okwhile the push would block. It says what it checked
(grip: ok (unpushed commits)), names a--rangeas typed instead of calling it "the
staged changes", and writes "1 file". Two scope flags at once (--branch --unpushed,--rangewith
--staged) are a usage error ingrip quiz,ask,checkandwhiteboardinstead of
the last one silently winning.- The installed hook runs grip by the absolute path of the grip that ran
grip install,
then falls back toPATH, so a push from an IDE or GUI client whosePATHlacks grip is
quizzed instead of skipped with "grip: not installed". When that path is gone, the hook
says so and suggestsgrip installagain, andgrip statusshowsgrip missing.
Re-runninggrip install --appendrefreshes the appended block. - CLI paper cuts: Ctrl-C exits 130 with
grip: stopped, nothing recorded.instead of
Aborted!and exit 1;grip forgetsaysno remembered passeswhen there were none;
pause times are shown in local time with the time zone instead of UTC; a change where
every file is binary is skipped instead of quizzed;grip status,installand
uninstallshow hook paths relative to the repository root (.git/hooks/pre-push);
grip grade --helpno longer lists--freshand--difficulty, and grading uses the
difficultygrip askwrote the questions at; a retry that reuses cached questions no
longer shows a "reading your diff" spinner. - A remembered pass can no longer be faked.
.git/grip/passed.jsonnow stores each pass's
score, pass mark, provider and model, and the hooks,grip checkand the agent push
gate only accept it if the score reaches the current pass mark and, unlessfakeis
the configured provider, it was not graded byfake.grip gradeno longer takes
--passing-score, andgrip statussays when a pass does not count and why. Passes
recorded by older versions are asked again once. - The Claude Code push gate honours
GRIP_SKIP=1 git pushtyped in the command, as its
message promised; before, it only read its own environment and denied the push anyway.
The/gripskill no longer tells the agent where the rubrics are stored, every skill
and the agent rules say not to read.git/grip/, and the docs say plainly that the
rubrics are on disk there and that an agent with file access could read them. - Whiteboard answers are no longer lost when grip stops. The open page pings grip every
minute, so a long think no longer hits the 20-minute idle timeout; answers are kept as
a draft in the tab and survive a reload; and if grip is gone at hand-in, the page says
"grip stopped: your answers are below, copy them" with a Copy answers button. - The whiteboard works from the keyboard and with a screen reader. Boxes, pins and
buttons show a focus ring, focus no longer drops to the top of the page when it
redraws, arrow keys move between pins,1to5open a question and Esc goes back,
and a skip link jumps to the questions. Pins say whether they are answered or how they
scored, the score is announced, and the panel no longer re-reads itself on every click. - The whiteboard is readable on real changes. The board is drawn at full size or larger in
a window you can drag, scroll and zoom (Ctrl + scroll, pinch, or the buttons; Fit
shows it all), and takes the height of the screen on a desktop. Box labels wrap onto two
lines and boxes grow to fit, arrows bow around boxes instead of running through them,
edge labels sit on a patch of board and move apart instead of overlapping, and changed
boxes are tinted. The page now loads its fonts (Archivo, Instrument Sans, JetBrains
Mono), which ship with grip under the SIL Open Font License; an export embeds them.
Install
pipx install grip-hook==0.5.0 # or: uv tool install grip-hook==0.5.0