Repository navigation
feat(typos-format): report at commit time through a shared git-hook installer - #7547
Conversation
… the typos-format entry lib/git-hooks/install.mjs implements the HookManager port (detect, list, plan, copy, decline, verify) with lefthook, husky and plain adapters; lib/git-hooks/check-lib.sh gives check scripts the staged and worktree file sets, changed-line ranges, the push range, the checks.skipmissing key and agent detection. Both are vendored into the ten check plugins. The prerequisites probe runs verify --quiet when a plugin ships git-hooks/entries.json. typos-format gains its pre-commit entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
…git-hooks setup action The Stop row runs the pre-commit typos check over the uncommitted changes behind typos_format_end_of_turn (default false), honors stop_hook_active, and costs only the node launcher with the option off. Setup gains apply install-git-hooks [scope=local|shared], which runs the installer's plan, applies its actions and leaves every decisions[] item to the agent; check reports verify's drift states and missing tools. The per-edit PostToolUse row stays as the pilot control. Changelog fragments cover every plugin whose vendored copies changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
entries.json gains an optional assets list: plugin-relative files copied beside the installed script and hashed into its stamp, so a changed asset reports stale and an edited installed one reports modified. typos-format lists config/default-typos.toml and passes it with -c, matching the per-edit hook. The stamp test now checks literal hashes of fixed bytes. Adds the Stop row's option-off spawn ratchet, and bumps user-experience, which is not in fragment mode, for its prerequisites.mjs copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
…cal hooks dirs check-lib gains checks_refuse_partly_staged, which a rewriting check calls before it rewrites: each staged path with unstaged changes gets the contract line and the call returns 1. install.mjs no longer installs at local scope into a hooks dir that core.hooksPath hands to another tool (husky's .husky/_ or a foreign one): plan returns a hooks-path-owned decision and copy writes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
Per the pilot verdict, the per-edit PostToolUse report row is removed: findings arrive from the git pre-commit entry, or at turn end when typos_format_end_of_turn is on (still off by default). typos-format.sh keeps only the Stop and --reset-digests paths; the write mode and the typos_format_write_changes and typos_format_lint_gitignored options go with the per-edit path, and the row tests now pin the remaining row set. README, setup skill and its evals say findings arrive at commit time. Also lists checks_refuse_partly_staged in the check-lib index and snapshots the whole .husky/_ tree in the owned-hooks-dir test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
….md mirrors The generated catalog in docs/ must change with it, which is outside this branch's scope; the new description goes in with the catalog regeneration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
…e reporting The SessionStart compact|clear --reset-digests row and its branch go: no per-edit finding is written now, so there is nothing to re-send. The manifest description says findings arrive at commit time, the catalog is regenerated from it, the hook-observability and hook-precision conventions no longer cite typos-format's removed per-edit hook, and the pipefail baseline drops the rewritten test file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
Present-tense references to typos-format's per-edit hook, write mode and lint_gitignored option are reworded in the setup evals, markdown-format's README, plugin-quality's packet-seal comment and evidence-packet reference, the typos-check comments, and the hook-budget and hook-precision conventions (now eight format and lint hooks). typos-format emits no hook telemetry now, so its hook-telemetry registration and data schema go. The new test files unset GIT_CONFIG as the fixture-isolation check requires, and the end-of-turn row's missing-typos skip names why it is silent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
The measured-rows table still said typos-format runs per edit "today"; this branch removes that hook, so the row now records the per-edit cost as the baseline the pre-commit entry replaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
A cloned repository is untrusted. copy now refuses, with status and plan decision unsafe-path, when a file it would write or a directory on the way to it is a symlink, and writes through a temp file and rename. Repository files are read only as regular files of at most 1 MiB, never through a final symlink, so a link to /dev/zero no longer exhausts memory in verify. Only the exact dispatcher counts as ours; a hook that merely carries the sentinel is a modified decision that copy overwrites only with --force. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
…p required jq The pre-commit entry skipped a file that was staged and then deleted from the work tree; it now checks the index copy, keeping the repository's typos excludes through a scratch mirror of its config files, and still skips a staged symlink. No remaining row needs jq, so it is declared optional for the plugin, and the setup and check skills and the README stop probing or listing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @kyle-sexton's task in 5m 2s —— View job Security review in progress
|
|
Claude finished @kyle-sexton's task in 8m 35s —— View job Code review complete
I focused on the hand-written logic — The symlink/unsafe-path handling, the staged-then-deleted-file fix, the partly-staged checks, and the 🟡 Stale cross-reference left behind in
This is no longer true: typos-format has no write/rewrite path left at all (not just outside No other findings met the bar for this lane. Nothing here is a correctness bug in the shipped installer, check library, or hook scripts. claude-lane-summary: lane=claude-review sha=095fde024d7a950f3ccecc306a85b9d62376cd31 run=38924358258 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 095fde024d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
claude-review has reviewed this pull request through d2fdb67; a later push is reviewed from there. |
…taging - typos-check: a typos exit other than 0 or 2 (a config it cannot read) now blocks instead of passing with no findings. - typos-check: a partly staged or work-tree-deleted file is checked as a mirrored file at its own path, so nested typos configs govern it. - install.mjs: a shared copy whose git add fails reports stage-failed and exits 1; a plain marker counts as wired only while it names its entry's script. - Register the lib/git-hooks clusters in the cross-plugin source registry, fence the typos fixtures in the typos-format tests, and drop the stale typos-format escape from the evidence-packet reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
|
Claude finished @kyle-sexton's task in 4m 32s —— View job Code review complete
This incremental slice is a tightly scoped fix-up of the three review findings raised against
All five fixes read correctly, match their stated contracts in No findings to report for this incremental slice. claude-lane-summary: lane=claude-review sha=63d57e7e8ed3ef983c57a49e5e3c5fbf8ae1509d run=38926179168 |
|
Claude finished @kyle-sexton's task in 3m 48s —— View job Security review complete
This PR was already through one round of review (Codex + Prior findings, verified fixed at
All four fixes carry regression tests added in the same commits ( Fresh pass over the current head: the symlink defenses in No new security findings to report. claude-lane-summary: lane=claude-security-review sha=63d57e7e8ed3ef983c57a49e5e3c5fbf8ae1509d run=38926179446 |
|
claude-security-review has reviewed this pull request through d2fdb67; a later push is reviewed from there. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz
|
Claude finished @kyle-sexton's task in 28s —— View job Security review complete
This lane's scope for this pass was narrowed to exactly one file changed since the last review at PG_WORK="$(mktemp -d)"
+ trap 'rm -rf "$PG_WORK"' EXIT
mkdir -p "$PG_WORK/sysbin" "$PG_WORK/cwd"This adds an No security findings for this incremental slice. claude-lane-summary: lane=claude-security-review sha=d2fdb679257e484a7ba11538481c23d7bf40b19b run=38927390232 |
|
Claude finished @kyle-sexton's task in 52s —— View job Code review complete
This incremental slice only adds one line to the probe-gate block in claude-code-plugins/plugins/typos-format/hooks/typos-format.test.sh Lines 88 to 89 in d2fdb67 PG_WORK="$(mktemp -d)"
+ trap 'rm -rf "$PG_WORK"' EXIT
mkdir -p "$PG_WORK/sysbin" "$PG_WORK/cwd"This is the only No correctness or maintainability issues found in this slice. claude-lane-summary: lane=claude-review sha=d2fdb679257e484a7ba11538481c23d7bf40b19b run=38927390291 |

Refs: #7613
PR B of the check-placement rollout; it implements the git-hook registration contract that #7544 records. #7613 holds two review findings deferred to later phases (foreign shared entries before
hooks-path; pre-push blob reads).Summary
Plugins had no way to put their checks in a git hook, so report-only findings could only reach the model per edit. This PR ships the shared installer and check library every check plugin vendors, and moves typos-format, the pilot plugin, from a per-edit report to a pre-commit entry on changed lines, with an opt-in end-of-turn check. A three-arm pilot (30 headless runs) found no regression and set the end-of-turn mode to off by default.
Fix
lib/git-hooks/install.mjs(detect, list, plan, copy, decline, verify; lefthook, husky and plain adapters) andlib/git-hooks/check-lib.sh(staged files, changed lines, push range,checks.skipmissing, agent detection, partly staged refusal), vendored per ADR 0019. Scripts only do mechanical steps; edits to user-owned hook-manager config come back as decisions the setup agent applies.copyrefuses a target that is or passes through a symlink (unsafe-path), writes by temp file and rename, reads repository files through one bounded, no-follow, regular-file-only reader, and treats only the exact dispatcher as its own (any other hook carrying the sentinel ismodified).typos-checkwith the bundled config as an asset (a file staged then deleted from the work tree is read from the index);typos_format_end_of_turnStop row, default off; setup gainsinstall-git-hooksand a drift check incheck;jqis no longer required.prerequisites.mjsin 69 plugins (68 fragments, user-experience bumped to 0.1.8).Verification
bash lib/git-hooks/install.test.sh(runsnode --test lib/git-hooks/*.test.mjs; the plan'snode --test lib/git-hooks/form does not resolve a directory on Node 24),check-lib.test.sh,typos-check.test.sh,typos-format.test.sh, end-of-turn: all pass; real lefthook 2.2.1, husky 9.1.7 and plain-hook commits block on a new-line typo and pass an untouched one.scripts/check-affected.sh --testspasses on the head;sync-shared-copies.sh --checkand--check-bump origin/main,check-fixture-git-isolation.sh,check-silent-skips.sh,check-pipefail-grep-q.sh,generate-catalog.mjs --check,ratchet.py check: all pass.actionlint-check.test.shCase 9 fails on main too, because the session'sCLAUDE_PLUGIN_DATA/CLAUDE_CODE_SESSION_IDleak into the test; it passes with them unset. Windows host timing not measured.Related
🤖 Generated with Claude Code
https://claude.ai/code/session_011Wn4wZmSz8fVYJLNgFG8Zz