You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit fd9ad7b
Browse filesBrowse the repository at this point in the historyBrowse files
* test(uncheck): harden the test harness against machine leaks and false greens
* fix(uncheck): keep node_modules and links leaving the project out of file lists
* fix(uncheck): skip pre-commit package lines with nothing staged or no folder, place hook lines around husky 4 and quoted setup
* fix(uncheck): address review of C1
* fix(uncheck): address review of B1
* fix(uncheck): address review of A1
* fix(uncheck): report repositories git refuses in prepare, resolve a symlinked --cwd, test the hook lock deterministically
* fix(uncheck): typecheck deletions, never pass a broken setup, keep report-only sherif read-only
* fix(uncheck): address review of A2
* fix(uncheck): select the projects tsc itself would check
- use the nearest tsconfig.json above a folder that has none, scoped to the folder's files
- add the projects of workspace packages that depend on a selected one
- check reference graphs whose projects would emit next to their sources with tsc -p --noEmit
- pass config paths starting with - or @ to tsc with ./
- leave unreachable tsconfig paths to tsc instead of aborting the run
- skip shared bases named tsconfig.json, select the extenders of a deleted base
* docs(uncheck): the presets need oxfmt 0.43+, the first to take a boolean sortImports
* fix(uncheck): address review of A3
- build the clean projects that -p checked ones reference first, so -p reads fresh declarations
- look for a tsconfig.json above the folder only inside its git repository
- test workspace dependents reached through references and peer or optional dependencies
* fix(uncheck): agent hook keeps sherif report-only, remembers its own block and checks the agent's project
* fix(uncheck): address review of A4
* fix(uncheck): staged stages what the fixers produced, keeps later edits and ends with the verdict
* test(uncheck): align merged expectations: deletions run tsc, physical --cwd, verdict last
* fix(uncheck): staged keeps edits saved during a conflicted run, survives a dying commit index and names what it skipped
* fix(uncheck): prepare lifts the lines v0.0.3 appended after an exec, reads setup without comments, keeps lines in blocks
* fix(uncheck): tsc builds git-ignored in-place projects with -b, checks solutions in the -p fallback, uses the config above a folder without its own, and names a missing typescript
- R5: a references member that emits beside its sources stays in tsc -b when git ignores its would-be JavaScript
- R8: an affected solution without sources in a -p graph is checked with -p, so a missing reference fails with TS6053
- R6: the config above cwd is used whenever cwd has no tsconfig.json of its own, next to the nested ones
- R7: workspace dependents also come from the packages owning the given files, before the not-covered exit
- R9: a deleted base extended by package name selects its extenders
- R11/R17: no tsconfig.json fails even for unrelated files; a missing typescript reads 'not installed'
- R19: README scopes deleted or moved files to the hooks
* fix(uncheck): address review of S
* fix(uncheck): address review of P
* fix(uncheck): leave out links into node_modules, isolate tests from /etc/gitattributes and ancestor ignore files, wait for parallel prepares
* fix(uncheck): agent hook re-arms its block once the checks pass, follows links, reads Copilot's sessionId and reports a repository git refuses
* docs(uncheck): links into node_modules, a repeated send-back, refused repositories in every command
* refactor(uncheck): simplify the review changes and plan tsc faster
- the runner skips a check that plans no commands, replacing the guards in
oxlint and oxfmt and the evenIfRequired flag; literal runs imply the
fixes stay within the given files
- shared helpers replace copies (fileKind, resolveFolders, isOutside,
logLines, a batched git listing, one copyBack); rawDiff always ends with --
- the agent hook finds its repository with one git call and looks it up once
when the agent works in its project folder
- tsc reads each config and its extends chain once per run and asks git once
which projects build in place, so planning a 300-package monorepo drops
from about 2-5 s to under 0.2 s with the same plans
- tests share their repeated constants and assertion helpers
Format issues found in above 2 files. Run without `--check` to fix.
54
54
✘ oxfmt failed 65ms
55
55
▶ tsc -p tsconfig.json --noEmit
@@ -83,9 +83,9 @@ npx uncheck src '!src/generated' # a directory, minus a part of it
83
83
npx uncheck '!**/*.gen.ts'# everything except some files
84
84
```
85
85
86
-
uncheck turns your paths into one file list that every tool gets, so they never disagree about what a path means. Directories and globs match the files git knows about (tracked, or new and not ignored), dot files included. A path that exists is never read as a glob, so `'app/[id]/page.tsx'` and `'app/(marketing)/**'` just work. A path that matches nothing fails the run, unless you pass `--no-error-on-unmatched-pattern`.
86
+
uncheck turns your paths into one file list that every tool gets, so they never disagree about what a path means. Directories and globs match the files git knows about (tracked, or new and not ignored), dot files included, but never links that point outside the directory uncheck runs in or into `node_modules`. Without paths, each tool finds the files itself, and oxlint checks and fixes those links too: pass `.` to leave them out. A path that exists is never read as a glob, so `'app/[id]/page.tsx'` and `'app/(marketing)/**'` just work. An exclusion glob also leaves out the folders it matches, as in `.gitignore`: `'!**/generated'`. A path that matches nothing fails the run, unless you pass `--no-error-on-unmatched-pattern`.
87
87
88
-
tsc then checks only the projects that include one of the files, and sherif runs only when a `package.json` or `pnpm-workspace.yaml` is among them.
88
+
tsc then checks only the projects that include one of the files, and the projects that depend on those. sherif runs only when a `package.json` or `pnpm-workspace.yaml` is among the files.
89
89
90
90
## Run it before every commit
91
91
@@ -101,28 +101,32 @@ Add a `prepare` script, so every clone sets up the hook on install, and run it o
101
101
102
102
Every commit then runs `uncheck staged --fix`: it checks the staged files, fixes what oxlint and oxfmt can, and stages those fixes. A failing check blocks the commit, and `git commit --no-verify` skips the hook. No lint-staged or simple-git-hooks needed.
103
103
104
-
|`prepare` flag | Effect |
104
+
To change the hook, put flags in the `prepare` script and run it again, such as `"prepare": "uncheck prepare --pre-commit --only=oxlint --only=oxfmt"`. Every install runs the script, so a flag passed only by hand is undone by the next install.
|`--no-fix`| The hook only checks and never changes your files |
107
109
|`--allow-empty`| The hook lets a commit through when the fixes undo every staged change |
108
-
|`--only`, `--skip`, `--require`|Written into the hook command|
110
+
|`--only`, `--skip`, `--require`|Pick the checks the hook runs|
109
111
110
-
Run `prepare` again with other flags to change the hook. What it guarantees:
112
+
What the hook guarantees:
111
113
112
-
-**You commit what was checked.** After `git add -p`, the unstaged part of a file is set aside while the checks run and put back afterwards, even after Ctrl-C.
114
+
-**Each staged file is checked as you staged it.** After `git add -p`, the unstaged part of a file is set aside while the checks run and put back afterwards, even after Ctrl-C.
113
115
-**Nothing is lost.** If a fix clashes with your unstaged changes, every fix is undone and the commit stops. Stage the whole file, or stash the rest, and commit again.
114
-
-**Only fixes to staged files are staged.** During a merge, only files that differ from the branch being merged in are checked.
116
+
-**Only fixes to staged files are staged.**The fixes are staged once oxlint and oxfmt finish, so an edit you save while tsc runs stays unstaged. During a merge, only files that differ from the branch being merged in are checked.
115
117
-**No empty commits.** If the fixes undo every staged change, the commit fails, unless you pass `--allow-empty`.
116
118
117
119
Good to know:
118
120
119
-
- tsc checks whole projects, so it can report errors in files you did not stage. `--only=oxlint --only=oxfmt` keeps the hook inside the commit.
121
+
- tsc checks whole projects as they are on disk, so it can report errors in files you did not stage, and pass thanks to a file you forgot to `git add`. `--only=oxlint --only=oxfmt` keeps the hook inside the commit.
122
+
- A hook line checks the staged files in its folder and below, so a line at the root already covers every package. A package's own line adds its checks, with its flags, on top.
120
123
- sherif only reports in the hook, since its fixes reach beyond the commit. Run `npx uncheck --fix` for them.
121
124
- A commit no selected check covers, such as a README change with `--only=tsc`, passes. Add `--require=tsc` to make it fail.
122
125
- An existing hook is kept: uncheck adds one line after its setup (comments, `source`, `export`, variables) and before its commands. With husky 9 or Vite+, it writes the `pre-commit` file they run.
123
-
- uncheck writes nothing, and says why, outside a git repository, when `core.hooksPath` comes from your global or system git config, or when the existing hook is not a shell script. Your install keeps working.
126
+
- uncheck writes nothing, and says why, outside a git repository, when git refuses the repository, when `core.hooksPath` comes from your global or system git config, or when the existing hook is not a shell script. Your install keeps working.
124
127
- The hook runs uncheck through your package manager (`pnpm exec`, `yarn run --silent`, `bunx --no-install` or `npx --no`), so a missing install fails instead of downloading uncheck.
125
128
- Yarn 2+ does not run `prepare`. Use `postinstall` instead, and in a package you publish, turn it off while packing, for example with `"prepack": "pinst --disable"` and `"postpack": "pinst --enable"`.
129
+
- Installs that leave out devDependencies (`npm ci --omit=dev`, `NODE_ENV=production`, `bun install --production`, `yarn workspaces focus --production`) still run `prepare` or `postinstall`, but without uncheck. Append `|| exit 0` so they pass: `"prepare": "uncheck prepare --pre-commit || exit 0"`.
126
130
127
131
## Run it after every agent turn
128
132
@@ -138,33 +142,37 @@ npx uncheck hooks install # or pick them from a list
Whenever the agent finishes a turn, the hook runs `uncheck hooks run --fix`. It checks the files changed since the last commit, fixes what it can, and when problems remain, sends the agent back to fix them. That happens at most once per turn, so an agent that cannot fix something is never stuck in a loop.
145
+
Whenever the agent finishes a turn, the hook runs `uncheck hooks run --fix`. It checks the files changed since the last commit, fixes what oxlint and oxfmt can, and when problems remain, sends the agent back to fix them. It sends the agent back again only after the checks have passed in between, whatever other hooks do, so an agent that cannot fix something is never stuck in a loop.
142
146
147
+
-**sherif only reports** here, since its fixes reach beyond the agent's change: they move versions in other packages and run your install. Run `npx uncheck --fix` for them.
143
148
-**Too slow?** Leave the typecheck to CI: `npx uncheck hooks install claude --only=oxlint --only=oxfmt`. Install again to change the flags.
149
+
-**Outside git** and before the first commit, the hook checks the whole folder, and oxlint also fixes the files that links in it point to. In a repository git refuses, such as one another user owns, the hook checks nothing and shows git's message.
150
+
-**Installed from a package?** The hook finds uncheck only where your package manager can run it, so add uncheck to the workspace root too when the agent may work outside that package.
144
151
-**Your config is kept.** Other hooks and settings stay, and installing again only updates uncheck's entry. Comments in the file are lost when it is rewritten.
145
152
-**Avoid double runs.** Cursor and Copilot CLI also run the hooks in `.claude/settings.json`, so add `cursor` or `copilot` next to `claude` only where they do not read that file.
146
153
-**Copilot** reads `.github/hooks` only at the top of the repository, so install `copilot` from there.
147
154
148
155
## Monorepos
149
156
150
-
Run uncheck from the workspace root, the folder whose `package.json` has `workspaces` or that has a `pnpm-workspace.yaml`, to check the whole monorepo.
157
+
Run uncheck from the workspace root, the folder whose `package.json` has `workspaces` or whose `pnpm-workspace.yaml` lists `packages`, to check the whole monorepo.
151
158
152
-
**sherif** checks the workspace as a whole, so it only runs at the root. Configure it in the `sherif` field of the root `package.json`, [as sherif documents](https://github.com/QuiiBz/sherif). With `--fix`, mismatched versions move to the highest one (unless you set `select`), and your install runs afterwards (unless you set `"noInstall": true`). When `CI` is set, sherif only reports.
159
+
**sherif** checks the workspace as a whole, so it only runs at the root. Configure it in the `sherif` field of the root `package.json`, [as sherif documents](https://github.com/QuiiBz/sherif). With `--fix`, mismatched versions move to the highest one (unless you set `select`), and your install runs afterwards (unless you set `"noInstall": true`). When `CI` is set, sherif only reports. Leave out `"fix": true`, since uncheck decides when sherif fixes: a run that only reports fails while it is set.
153
160
154
161
**TypeScript.** uncheck finds every `tsconfig.json` and follows their `references`:
155
162
156
-
- Projects linked by `references` are built with one `tsc -b`, which writes what your configs ask for, such as declarations.
157
-
- Every other project is checked with `tsc -p --noEmit`, a few at a time.
158
-
- When only some files are checked, tsc runs just the projects that include them, and the projects that reference those. A changed tsconfig selects every project that extends it.
163
+
- Projects linked by `references` are built with one `tsc -b`, which writes what your configs ask for, such as declarations. A project that sets none of `noEmit`, `emitDeclarationOnly`, `outDir` and `outFile` gets JavaScript next to its sources. When git ignores that JavaScript, the project builds in place on purpose and stays in `tsc -b`. Otherwise, as with Vite's `tsconfig.node.json`, it and the projects that reference it are checked with `tsc -p --noEmit` instead, after `tsc -b` builds the projects they reference. These cannot import each other: tsc reports TS6305.
164
+
- Every other project is checked with `tsc -p --noEmit`, a few at a time. A `tsconfig.json` that other configs extend and that includes no files is a shared base, not a project.
165
+
- When only some files are checked, tsc runs just the projects that include them, the projects that reference those, and the projects of the packages that depend on the files' packages. A changed tsconfig selects every project that extends it. In the hooks, a deleted or moved file also selects the projects that included or extended it.
166
+
- In a folder without its own `tsconfig.json`, such as a package that shares the root one, uncheck also uses the nearest one above it in the same git repository, when it includes files of that folder.
159
167
160
-
**Hooks.** Each package that runs `uncheck prepare --pre-commit` gets its own line in the one pre-commit hook, with its own flags:
168
+
**Hooks.** Each package that runs `uncheck prepare --pre-commit` gets its own line in the one pre-commit hook, with its own flags. A package's line runs only when the commit changes files in that package:
161
169
162
170
```sh
163
171
#!/bin/sh
164
172
# Written by `uncheck prepare`, run it again to change the command.
The presets need oxlint 1.70+, oxfmt 0.41+ and TypeScript 5.6+. The tsconfig presets load no runtime types, so name yours: `"types": ["node"]` for Node.js, or `"lib": ["ES2022", "DOM", "DOM.Iterable"]` for browsers.
210
+
The presets need oxlint 1.70+, oxfmt 0.43+ and TypeScript 5.6+. The tsconfig presets load no runtime types, so name yours: `"types": ["node"]` for Node.js, or `"lib": ["ES2022", "DOM", "DOM.Iterable"]` for browsers.
203
211
204
212
## Troubleshooting
205
213
206
214
**A path looks like a command, a flag or an exclusion.** Start it with `./`: `./staged`, `./-draft.ts`, `'./!notes.ts'`.
207
215
208
216
**`uncheck dist` says "No files match".** git ignores that folder, so it holds no project files. You can still name an ignored file directly.
209
217
218
+
**git refuses the repository ("detected dubious ownership").** git does not trust a repository another user owns, such as a checkout mounted into a container, so `prepare` writes no hook and `uncheck staged` and the agent hook check nothing. Run the `safe.directory` command `git status` prints there, then try again.
219
+
210
220
**A commit stops with "An earlier run left the unstaged versions of your files in …".** A pre-commit run was killed before it could put your unstaged changes back. Copy what your files are missing from the folder the message names, delete the folder, and commit again.
0 commit comments