Skip to content

fix(uncheck): review the whole package: safer hooks, tsc selection like tsc, faster runs, clearer docs - #12

Merged
dinwwwh merged 19 commits into
mainfrom
claude/uncheck-review-improvements
Sep 24, 2026
Merged

dinwwwh merged 19 commits into
mainfrom
claude/uncheck-review-improvements

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

A full review of the uncheck package, with every finding reproduced before it was fixed. uncheck staged no longer loses or mis-stages work in the git situations it used to break on, prepare stops writing into hooks it should not touch, agent hooks stop damaging settings and always check the right folder, and tsc runs exactly the projects tsc itself would check. The README is rewritten to be shorter and task-first.

Breaking changes

  • Windsurf is no longer a hooks install agent: its hook can neither show the report nor send Cascade back.
  • Generated hooks run npx --no, bunx --no-install and yarn run --silent (-T in Yarn 2+ where needed), so a missing install fails instead of downloading uncheck. Re-running prepare or hooks install upgrades old lines in place.
  • hooks run without --cwd checks from the top of the git repository; installs below it write --dir=<folder>.
  • sherif only reports in uncheck staged, since its fixes touch other manifests and the lockfile.
  • Globs match dot files, and exclusions on their own exclude from everything.
  • Peer floors: oxlint >=1.60, sherif >=1.10. Node >=22.20 is now the whole requirement.

Fixes

  • staged:
    • Unstaged changes come back after a closed terminal, or an edit saved while the checks run.
    • CRLF files, package .gitattributes, core.safecrlf, sparse checkouts, symlinks, type changes and post-checkout hooks are handled.
    • Merge commits check only their own files, and conflict messages name only the files that conflicted.
  • prepare:
    • Never writes into a global or system core.hooksPath, non-shell, binary or unreadable hooks, or folder names sh would misread.
    • Every package keeps its line under parallel installs.
    • The line runs after the hook's environment setup and before its commands.
  • agent hooks:
    • Broken configs are refused, and only uncheck's own stop entry is updated.
    • A timeout you set is kept; new entries get 600 s.
    • Copilot's Claude-format payload blocks, lingering failures surface as a warning, and stopped Cursor turns are left alone.
  • tsc:
    • Solution layouts, config-only commits, dependents of a changed library and diamond extends are selected like tsc does.
    • tsc -p runs with --noEmit.
  • everywhere:
    • Windows command-line limits and Yarn PnP work.
    • A killed tool fails its check instead of crashing the run, and docs-only commits pass.
    • File names starting with ! or - are checked, colours follow Node's rules, and platform errors print cleanly.
    • --wizard and --log-level, which did nothing, are gone.

Performance

  • The entry enables Node's compile cache, so each start compiles less. The bundle stays unminified, so crash traces keep readable names for bug reports.
  • Standalone tsc projects are checked up to four at a time, with output kept in order.
  • staged spawns fewer git processes and runs independent ones together; file filtering on large projects is several times faster.

Docs

  • The README is reorganised around what a reader wants to do (check a project, check some files, run it before every commit or after every agent turn), with every example run against the CLI. It is about a quarter shorter.
  • The licence badge no longer links to a missing file.

Testing

  • 118 tests, up from 66, including real git commit runs through the hook, signal handling, concurrent installs, old git and Yarn PnP.
  • The built bundle was run on Node 22, 24 and 26.

…anager commands

- globs match dot files (picomatch) and exclusions on their own apply to everything
- git file names are taken literally in staged and hooks run; names starting with ! or - reach tools intact
- a run given files that no check handles passes in staged and hooks run
- tools killed or failing to spawn fail their check instead of crashing the run
- argv batches fit Windows' command line; Yarn PnP installs resolve tools
- hooks never download: npx --no, bunx --no-install; yarn run --silent keeps agent JSON clean
- sherif only reports in staged, since its fixes reach beyond the commit
- colours follow Node's rules; SIGHUP puts unstaged changes back; platform errors print cleanly
- minified bundle with a compile-cached entry; dead --wizard and --log-level flags hidden
… once

tsc -p no longer writes JavaScript or declarations during a check, and the
standalone projects of a monorepo are checked up to four at a time with their
output kept in plan order.
…t paths, argv batches and killed tools

Also lets the middleapi tsconfig preset accept the .ts imports Node's type
stripping needs.
…taged changes back intact

- A staged symlink is no longer checked or fixed through to the file it points
  to (outside the repository, untracked or unstaged); a symlink turned into a
  regular file is now checked.
- During a merge, only files that differ from the side merged in are checked
  and fixed, so merge commits no longer carry fixes to the other side's work.
- With --fix only the files the fixes changed are staged, so a merge or squash
  bringing in files outside a sparse checkout can be committed, and a
  conflicting fix is still undone there.
- A partially staged file stored with CRLF under text=auto or core.autocrlf is
  no longer reported as a conflicting fix.
- Run from a package folder, the restored file keeps the line endings and
  encoding its .gitattributes set.
- Setting unstaged changes aside and undoing fixes no longer runs, or fails
  with, the post-checkout hook.
…repository and every package

- A core.hooksPath from the global or system git config is left alone and reported,
  so one install no longer adds this repository's line to the hook every repository runs
- Packages of a workspace that prepare at the same time all keep their line
- In a hook that already has commands, the line runs before them, so their failure
  still blocks the commit and an `exec` or `exit` no longer skips it
- A hook in another language (Node, Python, Ruby) is left alone and prepare says
  which line it should run, instead of breaking every commit
- A package folder whose name sh would expand inside double quotes gets no line
  rather than a broken or ever-growing one
- A hooks folder only counts as the husky 9 or Vite+ dispatcher when it is their `_` folder
- The folder a line enters comes from git itself, so it no longer depends on how
  the path to the package was spelled
- Lines older versions wrote with plain npx or yarn are upgraded in place, and
  hand-written lines that chain or soften the command stay as they are
…ry and reach every agent

- `hooks install` refuses a config that is not valid JSON(C) or not an object, naming the file and leaving every file as it was, instead of rewriting it with settings lost
- Reinstalling updates only uncheck's own hook entry, keeping keys added to it; permission rules, notes and chained commands that merely mention `uncheck hooks run` stay as they are, and a Stop hook is still added next to them
- Hook entries carry a 600 s timeout, so Copilot, VS Code and CodeBuddy no longer kill a typecheck at their 30 s or 60 s default; existing installs are upgraded on the next install
- The hook checks the top of the repository whichever directory the agent `cd`'d into; installed below the top it carries `--dir=<path>` and checks that directory
- Copilot is sent back through its Claude-format entry too, not only warned
- When checks still fail after the one retry, Claude Code and CodeBuddy show the user a warning instead of ending the turn silently
- A change no selected check covers passes the hook, and a deleted file whose name looks like a pattern no longer checks its neighbours
- Windsurf is no longer offered: its hook event can neither show the report nor send Cascade back
…ees it

- staged and given files select referenced configs of any name, so a Vite,
  Nx or Angular solution layout (tsconfig.app.json) is no longer skipped
- a changed tsconfig, or a base it extends, selects the projects it applies
  to, also when the base is a workspace package linked into node_modules
- a selected project that others reference builds the roots depending on it
  with tsc -b, so a library change that breaks its dependents fails the
  commit; unrelated roots stay out
- project inputs match tsc: a base shared by two extends branches applies in
  both, declarationDir is excluded, `*` skips .min.js, extensions are case
  sensitive and an explicit node_modules include counts
- backslashes in extends and references read as separators, and references
  keep ${configDir} as written, like tsc
- a reference cycle anywhere fails runs on given files too
- globs read [!x] as POSIX negation and parentheses literally, as before picomatch
- staged: a CRLF file under text=auto is undone rather than turned to LF, fixes outside a
  sparse checkout are staged, core.safecrlf no longer blocks, messages name only what applies
- prepare: the line goes after the hook's PATH and environment setup, git before 2.26 still
  refuses a global core.hooksPath, an unreadable or binary hook is never overwritten
- hooks: only the stop event's entry is uncheck's, a timeout the user set is kept, a stale
  --dir is reported, Yarn 2+ root installs run the root binary, Copilot is refused below the
  top, Cursor turns the user stopped are left alone
- tsc: dependents found without a quadratic walk, backslashes in include/exclude, smaller
  cycle search
- Yarn PnP resolves tools from every ancestor workspace
- globs use minimatch with dot matching, the engine path.matchesGlob already used, instead of
  patching picomatch back to its semantics
- shared helpers for the rev-parse location, reading a file that may not exist, platform error
  messages and batched git calls; the Yarn 2+ -T rule lives in detectExec
- staged runs its independent git calls together, writes a tree only when it sets changes
  aside, and merges files concurrently; hooks run lists changed files concurrently
- existence and node_modules filters no longer cost a fiber or an array per file
- tsc resolves real paths only when a config is among the given files
- tests share the Stop payload, the CLI path and the side-branch setup
- staged: an edit saved to a partially staged file while the checks run is merged back again;
  only a CRLF blob git keeps as it is refuses the merge
- a newline in the repository's path no longer shifts the prefix staged, prepare and install use
- prepare puts its line before a setup line that also runs a command or continues onto the next,
  and the old-git scope check reads included config files
- hooks install in a Yarn 2+ package without its own uncheck runs the root's
- a linked node_modules stays out of the project files; globs drop `.` inside braces again
- Yarn PnP falls back to node_modules for folders its resolver does not cover
Adds tests for an edit saved during the checks, a put-back that fails, a first commit with
fixes, a repository path with a newline, other tools' Stop hooks and --require/--skip in
hook commands, Yarn 2+ package installs, setup lines that run a command or continue, included
git config on old git, tsc -b before parallel tsc -p, Yarn PnP resolution, linked node_modules,
braces with `.`, and the CLI's error output and global flags. The fixture now refuses to write
into a symlinked tool, which would change the shared pnpm store.
@pullfrog

pullfrog Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Run failed. View the logs →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

@pkg-pr-new

pkg-pr-new Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/uncheck@12

commit: 3f7831c

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

They are different tools and will grow apart, so each keeps its own file as on main.
A stack trace with real names is what users can report back.
path.matchesGlob, the reason for skipping 23.x and early 24.x, is no longer used.
Organised by what a reader wants to do: check a project, check some files, run it before every
commit, run it after every agent turn, then monorepos, presets and troubleshooting. Every
example was run against the CLI. Also fixes the licence badge, which linked to a missing file.
@dinwwwh dinwwwh changed the title fix(uncheck): review the whole package: safer hooks, tsc selection like tsc, smaller and faster fix(uncheck): review the whole package: safer hooks, tsc selection like tsc, faster runs, clearer docs Sep 24, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one minor documentation fix inline.

Reviewed changes

Re-reviewed the README rewrite pushed after the prior head (2491be9 → 40c86a9); the documentation's claims were cross-checked against the current implementation.

  • Rewritten packages/uncheck/README.md around what a reader wants to do — install, check a project, check some files, run before every commit, run after every agent turn, monorepos, presets, troubleshooting.
  • Fixed the licence badge to link to LICENCE, the file that exists.
  • Consolidated the duplicated usage, agent and tsconfig blocks into tables, and documented the uncheck/tsconfig/middleapi and /lib exports.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

Comment thread packages/uncheck/README.md Outdated
@dinwwwh
dinwwwh merged commit 8bc75fb into main Sep 24, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant