@stupify/cli 0.0.16 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.review/CORPUS.md +44 -0
  2. package/.review/CORPUS.template.md +73 -0
  3. package/.review/REVIEW-PROMPT.md +52 -0
  4. package/.review/RUBRIC.md +46 -0
  5. package/LICENSE +1 -1
  6. package/README.md +95 -37
  7. package/package.json +27 -26
  8. package/packs/antirez.md +10 -0
  9. package/packs/anton-kropp.md +10 -0
  10. package/packs/dhh.md +10 -0
  11. package/packs/dtolnay.md +10 -0
  12. package/packs/jarred-sumner.md +9 -0
  13. package/packs/mitchell-hashimoto.md +10 -0
  14. package/packs/rich-harris.md +10 -0
  15. package/packs/simon-willison.md +10 -0
  16. package/packs/sindre-sorhus.md +10 -0
  17. package/packs/tanner-linsley.md +10 -0
  18. package/packs/zod.md +10 -0
  19. package/src/cli.ts +626 -0
  20. package/src/prime-install.test.ts +109 -0
  21. package/src/prime.ts +50 -0
  22. package/src/review-sweep.test.ts +101 -0
  23. package/src/review-sweep.ts +526 -0
  24. package/dist/analysis.d.ts +0 -16
  25. package/dist/analysis.js +0 -168
  26. package/dist/cache.d.ts +0 -2
  27. package/dist/cache.js +0 -57
  28. package/dist/checks.d.ts +0 -4
  29. package/dist/checks.js +0 -228
  30. package/dist/command.d.ts +0 -2
  31. package/dist/command.js +0 -147
  32. package/dist/constants.d.ts +0 -4
  33. package/dist/constants.js +0 -53
  34. package/dist/counter-scout.d.ts +0 -21
  35. package/dist/counter-scout.js +0 -167
  36. package/dist/diff.d.ts +0 -1
  37. package/dist/diff.js +0 -10
  38. package/dist/doctor.d.ts +0 -16
  39. package/dist/doctor.js +0 -143
  40. package/dist/git.d.ts +0 -17
  41. package/dist/git.js +0 -368
  42. package/dist/hooks.d.ts +0 -5
  43. package/dist/hooks.js +0 -135
  44. package/dist/index.d.ts +0 -1
  45. package/dist/index.js +0 -1
  46. package/dist/model.d.ts +0 -11
  47. package/dist/model.js +0 -296
  48. package/dist/prompts.d.ts +0 -8
  49. package/dist/prompts.js +0 -89
  50. package/dist/render.d.ts +0 -6
  51. package/dist/render.js +0 -295
  52. package/dist/repomix-provider.d.ts +0 -12
  53. package/dist/repomix-provider.js +0 -196
  54. package/dist/search-bench.d.ts +0 -1
  55. package/dist/search-bench.js +0 -677
  56. package/dist/search-profile.d.ts +0 -6
  57. package/dist/search-profile.js +0 -73
  58. package/dist/sem-provider.d.ts +0 -2
  59. package/dist/sem-provider.js +0 -255
  60. package/dist/stupify.d.ts +0 -38
  61. package/dist/stupify.js +0 -505
  62. package/dist/trace.d.ts +0 -31
  63. package/dist/trace.js +0 -86
  64. package/dist/types.d.ts +0 -341
  65. package/dist/types.js +0 -6
  66. package/dist/ui.d.ts +0 -34
  67. package/dist/ui.js +0 -143
  68. package/src/analysis.ts +0 -223
  69. package/src/cache.ts +0 -63
  70. package/src/checks.ts +0 -231
  71. package/src/command.ts +0 -173
  72. package/src/constants.ts +0 -56
  73. package/src/counter-scout.ts +0 -195
  74. package/src/diff.ts +0 -9
  75. package/src/doctor.ts +0 -166
  76. package/src/git.ts +0 -380
  77. package/src/hooks.ts +0 -151
  78. package/src/index.ts +0 -1
  79. package/src/model.ts +0 -367
  80. package/src/prompts.ts +0 -100
  81. package/src/render.ts +0 -328
  82. package/src/repomix-provider.ts +0 -219
  83. package/src/search-bench.ts +0 -783
  84. package/src/search-profile.ts +0 -89
  85. package/src/sem-provider.ts +0 -300
  86. package/src/stupify.ts +0 -604
  87. package/src/trace.ts +0 -126
  88. package/src/types.ts +0 -362
  89. package/src/ui.ts +0 -187
@@ -0,0 +1,44 @@
1
+ # Good-code reference — taste packs
2
+
3
+ Judge every diff against the standards below. When you flag slop, name the principle (or the linked file) the change should have followed. The links are commit-pinned exemplars — open them when you need detail.
4
+
5
+ ---
6
+
7
+ ## Code like Sindre Sorhus (@sindresorhus) · one file, one job
8
+
9
+ Radical minimalism: each module does exactly one thing and is small enough to read in five minutes. A function
10
+ where a class would do; no surprise dependencies; inputs validated eagerly at the top so failures are loud and
11
+ early. Tiny public surface, deep comments on the *why*. If it can't be read top-to-bottom in one sitting, it's
12
+ too big.
13
+
14
+ - [`p-limit/index.js`](https://github.com/sindresorhus/p-limit/blob/42599ebbbb1228a5bdab381fcf8f4ac20eb8d551/index.js) — a whole concurrency limiter in one short, obvious file.
15
+ - [`execa/options.js`](https://github.com/sindresorhus/execa/blob/f3a2e8481a1e9138de3895827895c834078b9456/lib/arguments/options.js) — careful, explicit input normalization before anything runs.
16
+ - [`chalk/index.js`](https://github.com/sindresorhus/chalk/blob/aa06bb5ac3f14df9fda8cfb54274dfc165ddfdef/source/index.js) — a clean, composable API with a minimal surface.
17
+
18
+
19
+ ---
20
+
21
+ ## Code like Anton Kropp (@devshorts) · DI + branded types
22
+
23
+ The Startup Architecture house style: every domain concept gets its own tiny wrapper type (a `QueueName`,
24
+ never a raw `String`), so a primitive never leaks across a boundary. Dependencies are wired through small,
25
+ single-purpose DI modules listed explicitly at one auditable composition root. Interfaces are single-method
26
+ contracts. `Clock` is injected so tests can move time. Fail fast and loud; no silent fallbacks.
27
+
28
+ - [`QueueName.java`](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/model/src/main/java/io/paradoxical/cassieq/model/QueueName.java) — a branded value type: a raw string can't masquerade as a `QueueName`.
29
+ - [`DefaultApplicationModules.java`](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/modules/DefaultApplicationModules.java) — the composition root: one explicit list of named DI modules, no magic scanning.
30
+ - [`ClockModule.java`](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/modules/ClockModule.java) — one module, one concern (binds `Clock`), trivially swapped in tests.
31
+
32
+
33
+ ---
34
+
35
+ ## Code like Colin McDonnell (@colinhacks) · parse, don't validate
36
+
37
+ Data never enters the system as an unvalidated primitive — it's parsed at the boundary and the parsed type
38
+ guarantees its shape. Schemas are immutable values (methods return new instances). Errors are discriminated
39
+ unions with a `code`, so every branch is exhaustive and typed. `parse` throws (fail fast); `safeParse` returns
40
+ a typed result for callers that want to branch. Composable validators replace hand-rolled type guards.
41
+
42
+ - [`parse.ts`](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/parse.ts) — symmetric `parse`/`safeParse` with the sync/async boundary enforced; the error class is injected, not hardcoded.
43
+ - [`errors.ts`](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/errors.ts) — a discriminated-union error type, every field `readonly`, a `path[]` for nested location.
44
+ - [`schemas.ts`](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/schemas.ts) — distinct compile-time types per schema kind: illegal states are unrepresentable.
@@ -0,0 +1,73 @@
1
+ # Good-code reference — YOUR curated exemplars (template)
2
+
3
+ > This is a template. **Replace it with 3–6 files from your own codebase that you'd point a new hire at** —
4
+ > the code you wish all your code looked like. The reviewer treats these as the standard and measures every
5
+ > diff against them. Taste can't be auto-extracted: hand-pick these, and say *why* each is good. A vague
6
+ > corpus produces vague reviews; a sharp one produces sharp ones.
7
+
8
+ How to write an entry:
9
+ - **Name the file** (a real path in this repo) and **one sentence on what makes it good** — the principle it
10
+ embodies (e.g. "complexity tamed by decomposition", "type makes illegal states unrepresentable",
11
+ "fail-fast at the boundary"). The reviewer opens the live file; the excerpt just shows the shape.
12
+ - Keep a short code excerpt that captures the pattern. The point is the *principle*, not the lines.
13
+ - Group loosely (e.g. "complex but readable", "clean service boundary") so the reviewer can cite the right one.
14
+
15
+ Pick principles you actually care about. Common ones worth encoding:
16
+ **dependency injection** (collaborators injected, never `new`d inline; config read only at a composition root),
17
+ **type-system-first invariants** (`satisfies`, discriminated unions, schemas at boundaries — illegal states
18
+ hard to represent), **fail fast and loud** (no silent fallback), **small single-responsibility units**,
19
+ **declarative over imperative**, **readable signatures** (≤3 positional params → options object).
20
+
21
+ ---
22
+
23
+ ## A. Complex, kept readable
24
+
25
+ ### 1. `src/path/to/your-exemplar.ts` — one line on why it's good
26
+ `src/path/to/your-exemplar.ts`
27
+
28
+ Say what makes it the standard — e.g. the complexity (optimistic UI, retries, sync) is tamed by decomposition:
29
+ the orchestrator only *coordinates*; every concern is a small focused unit, and every operation is the same
30
+ shape, so N of them read like one.
31
+
32
+ ```ts
33
+ // a short excerpt that shows the pattern — the shape, not the whole file
34
+ export function handle(input: Input): Result {
35
+ const state = read()
36
+ const ops = compute(state, input) // pure
37
+ return apply(ops) // effectful shell
38
+ }
39
+ ```
40
+
41
+ ### 2. `src/path/to/another.ts` — composition + named pieces
42
+ `src/path/to/another.ts`
43
+
44
+ e.g. pure composition — each piece a named small component, conditions become named type-guards, not inline
45
+ boolean soup.
46
+
47
+ ```ts
48
+ function hasMeasuredWidth(width: number | undefined): width is number {
49
+ return width !== undefined && width > 0
50
+ }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## B. Clean boundary / DI
56
+
57
+ ### `src/path/to/service.ts` — injected collaborator + composition-root factory
58
+ `src/path/to/service.ts`
59
+
60
+ e.g. constructor injection — the collaborator is never `new`d inline; a small factory is the composition root;
61
+ the method parses input at the boundary, logs with structured context, and **fails loud** (catch → log → rethrow).
62
+
63
+ ```ts
64
+ export function createService() {
65
+ const scope = container.createChildContainer()
66
+ scope.register(CLIENT, { useValue: makeClient() })
67
+ return scope.resolve(Service)
68
+ }
69
+ ```
70
+
71
+ ---
72
+
73
+ > Add a "Fine — do NOT flag" set of your own here too, if there are patterns reviewers keep wrongly dinging.
@@ -0,0 +1,52 @@
1
+ # Review spec — corpus-grounded, anti-slop, with a personality
2
+
3
+ You are reviewing a code diff for this repo. You're running in the repo with `gh` / `git` / file access and
4
+ your own model — no API key needed. Run these steps:
5
+
6
+ 1. Read `RUBRIC.md` (the anti-slop rubric + finding taxonomy) and `CORPUS.md` (this team's curated "good code"
7
+ — the primitives it actually uses). Treat the corpus as the standard. Open the live files it points at.
8
+ 2. Get the diff for the target PR.
9
+ 3. Review every changed code file (skip lockfiles, generated/snapshot files, pure deletions). Catch BOTH
10
+ kinds from the rubric — the "just wrong" (bug / type-lie / dead-code / footgun) and the "taste / reuse"
11
+ (reinvents-primitive / slop). "Slop" is code RELATIVE to the simpler or already-existing way: does it
12
+ reinvent a corpus primitive, or is it bigger / more abstract / more speculative than the corpus pattern for
13
+ the same job? When you cite a fix, name the actual corpus file/primitive it should use.
14
+ 4. Format the review per the **Comment format** below. Report everything incl. low-confidence; don't self-filter.
15
+ 5. Post it with the `gh pr comment` command you were given (write the comment to the file, then post).
16
+
17
+ ## Prior reviews on this PR (your memory)
18
+
19
+ If the runner hands you a **"Prior reviews on this PR"** block, it's the existing review conversation — your
20
+ past reviews and the author's replies. You are CONTINUING that thread, not starting fresh. Treat it as memory:
21
+
22
+ - **Don't re-raise what's settled.** If you already flagged something and it's now fixed, or the author
23
+ **declined it with a reason**, do not raise it again — unless the diff brings new evidence that actually
24
+ rebuts their reason. Re-litigating a reasoned decline is noise (and the fastest way to be ignored).
25
+ - **Report only what's new.** Surface issues introduced since your last review, or ones you genuinely missed.
26
+ Do not manufacture marginal findings just to have something to say — a nit you wouldn't have raised on
27
+ round one doesn't become worth raising on round six.
28
+ - **Converge — knowing when to stop is part of the job.** If there are no new issues and the prior ones are
29
+ addressed or reasonably declined, do NOT write a review. Post exactly this line and nothing else:
30
+ `no new blocking issues — prior items addressed ✅`
31
+
32
+ (No prior-reviews block = this is the first review of this PR; ignore this section.)
33
+
34
+ ## Comment format (GitHub markdown — warm + scannable)
35
+
36
+ - **Opening line — write it yourself: direct, genuinely silly, honest.** ONE short, lowercase-casual line —
37
+ goofy human noises, drawn-out exclamations, mild swears, the way someone reacts while scrolling code:
38
+ "uhhhh ummm", "shieeeeet", "oof", "ohhh boy", "ok so… yeah". NOT corporate, NOT clever-witty, NOT a linter
39
+ header, no praise-padding. Be a little dumb on purpose, then get to what you found. Vary it every run:
40
+ - nothing wrong → `yep. clean. no notes 🎉` and **stop** (no blocks).
41
+ - a few small things → `uhhhh ummm a couple things 👇` · `shieeeeet, found some stuff:` · `ok so. some stuff:`
42
+ - something real → `oh no. ok there's a real one in here:` · `oof, yeah this'll break:`
43
+ Then a blank line. (Tune this register to your taste — or delete it for a dry tone.)
44
+ - **Each finding** worst-first, as a 3-line block with a blank line between blocks:
45
+ - line 1: `<emoji> **`path:line`** · <kind> · conf <0–1>`
46
+ - line 2: what's wrong and why (1–2 sentences, plain — describe the code, don't scold)
47
+ - line 3: `**→ Fix:** <corpus primitive to reuse, or the correct approach> (`<reference file>`)`
48
+ - Severity emoji: 🔴 high · 🟠 med · 🟡 low.
49
+ - Close with a quiet attribution on its own line so it's clearly the auto-reviewer, not a person:
50
+ `_— stupify, against the good-code corpus_`
51
+ - No tables, no nested bullets, no preamble before the opener. End the comment with the exact hidden marker
52
+ line you were given.
@@ -0,0 +1,46 @@
1
+ # Anti-slop rubric — the single source of truth for taste
2
+
3
+ This is what the reviewer judges against, alongside `CORPUS.md`. Edit it to match your team. A reviewer
4
+ catches two kinds of problem. Tag every finding with its `kind`.
5
+
6
+ ## Just wrong — flag regardless of the corpus
7
+ - `kind: bug` — correctness bugs; off-by-one; broken null/empty handling; wrong condition.
8
+ - `kind: type-lie` — a type/annotation that does not match what the code actually returns
9
+ (e.g. annotated `T | null` but every path returns a non-null value cast to `T`).
10
+ - `kind: dead-code` — unreachable or dead branches; a declared-and-unused const/import/function.
11
+ - `kind: footgun` — swallowed errors / catch-and-continue with no owned degraded state; silent fallbacks;
12
+ test-only special-casing (`NODE_ENV === 'test'`, env-name string checks) leaking into production code.
13
+
14
+ ## Taste / reuse — relative to the corpus and the simpler way
15
+ - `kind: reinvents-primitive` — a NEW abstraction/layer/wrapper/facade/shim/fallback-reader when a corpus
16
+ primitive already does it (name the primitive). Or hand-rolling what a corpus file does.
17
+ - `kind: slop` — bigger / more abstract / more speculative than the corpus pattern for the same job:
18
+ - speculative `unknown` in hand-authored types; `TResult = unknown` generic defaults;
19
+ `z.unknown()` / `z.array(z.unknown())`
20
+ - generic-parameter explosion on a call site that is not actually reused generically
21
+ - `let best*/latest*` imperative argmax/latest accumulator loops
22
+ - throwaway one-call helpers, or wrapper functions that add no value — a pure pass-through to another fn
23
+ with the same signature; inline it / call the inner directly
24
+ - a defensive `?.` / `??` fallback on a value the type or schema already guarantees — e.g. `x?.foo ?? x.y.foo`
25
+ when `x` is required (or should be). Drop the optional chain and the fallback (it's `x.foo`); if `x` is
26
+ wrongly optional, fix the schema/type, don't paper over it at the call site
27
+ - denormalized parallel constants or hardcoded membership lists (derive a Set/Record from ONE `as const` array)
28
+ - speculative config seams / unused `mode` switches / injectable-override defaults nothing needs yet
29
+ - additive churn on a cleanup; code that "looks productive" over the minimal change
30
+
31
+ ## Fine — do NOT flag
32
+ - `unknown` at a real parse boundary fed into a normalizer; `Record<string, unknown>` context bags
33
+ - Set/Map-building or dedupe loops (not argmax accumulators)
34
+ - a single choke-point helper its owner reuses
35
+
36
+ ## Weigh the fix against the owner
37
+ Right-size the remedy to the code that owns it. Don't prescribe a heavier primitive than the context warrants:
38
+ a one-off script shouldn't grow a schema library, glue code shouldn't sprout an interface, a guaranteed-shape
39
+ boundary doesn't need the validation an untrusted one does, and an unattended job usually wants a loud default
40
+ over a hard exit. Demanding more rigor than the owner needs is its own slop. If the minimal fix is a one-liner,
41
+ the fix is the one-liner — propose that, not an architecture.
42
+
43
+ ## Output per finding
44
+ `path:line` — [kind] — what's wrong and why — **fix:** the corpus primitive to reuse OR (for a bug) the
45
+ correct approach — severity(high|med|low) · confidence(0–1). Sort worst-first. Report everything incl.
46
+ low-confidence — do not self-filter; a downstream ranker (and your own memory) handles that.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Stupify contributors
3
+ Copyright (c) 2026 Noah Lindner
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,58 +1,116 @@
1
- # @stupify/cli
1
+ > Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as
2
+ > cleverly as possible, you are, by definition, not smart enough to debug it.
3
+ >
4
+ > — **Kernighan's Law**
2
5
 
3
- Local-only diagnostic CLI for checking whether AI is making you dumber.
6
+ # stupify
4
7
 
5
- Released under the MIT License.
8
+ Tired of [wasting your time](https://github.com/thesysdev/openui/issues/517) reviewing [AI](https://github.com/RsyncProject/rsync/issues/929) [slop](https://github.com/anthropics/claudes-c-compiler/issues/1)?
6
9
 
7
- Stupify has one analysis path:
10
+ [![npm](https://img.shields.io/npm/v/@stupify/cli?color=cb3837&label=%40stupify%2Fcli)](https://www.npmjs.com/package/@stupify/cli)
11
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
12
 
9
- ```text
10
- sem diff -> counter scout -> Repomix context -> local search model
11
- ```
13
+ **A code reviewer that talks like an idiot and catches real bugs.**
12
14
 
13
- It emits search `matches`, not audit findings.
15
+ Kernighan was right: the clever code is the code you can't debug. stupify drags your code back toward boring —
16
+ at **both ends** of the loop:
14
17
 
15
- ```sh
16
- npx @stupify/cli --staged
17
- npx @stupify/cli --since "2 weeks ago"
18
- npx @stupify/cli --commit HEAD
19
- npx @stupify/cli --commits 20
20
- git diff HEAD~1..HEAD | npx @stupify/cli --stdin
21
- ```
18
+ - **before you code** — [`stupify prime`](#prime-your-agent-instant-local-no-servers) seeds every Claude Code session with your taste, so the agent writes to your standard from line one.
19
+ - **after you PR** — the reviewer reads every PR on [Codex](https://github.com/openai/codex) against that *same* taste and flags what drifted.
20
+
21
+ You encode your taste once — a `CORPUS.md` of files you already think are good (or a [taste pack](#taste-packs)) — and stupify enforces it going in and coming out. Not some model's idea of "best practice." So it catches the stuff that actually bugs *you*: premature abstractions, cute one-liners with a bug hiding in them, helpers someone reinvented. Then it points at the boring thing they should've used.
22
+
23
+ Most AI reviewers carpet-bomb your PR with `consider renaming this`. stupify stays quiet until it finds
24
+ something real, says it in one sentence, and shuts up.
25
+
26
+ > uhhhh ummm this cleanup got a little cleanup-y:
27
+ >
28
+ > 🟠 **`server/checkout.ts:40`** · slop · conf 0.82
29
+ > you inlined `validateCart` and `applyDiscounts` into the handler, so it's branch soup with two mutable `let`s now, instead of validate → price → charge. those weren't throwaway wrappers, they were the steps.
30
+ > **→ Fix:** put the named steps back. the handler should orchestrate, not do all of it.
31
+ >
32
+ > 🟡 **`server/checkout.ts:12`** · slop · conf 0.7
33
+ > `order?.total ?? order.cart.total` — `order` is required here, so the `?.` never fires and the fallback is dead code cosplaying as safety. it's `order.total`.
34
+ > **→ Fix:** drop the `?.` and the `??`. if order's actually optional, fix the type, don't paper over it.
35
+ >
36
+ > _— stupify, against the good-code corpus_
22
37
 
23
- Install the warn-only pre-commit hook:
38
+ ### What you get
24
39
 
25
- ```sh
26
- stupify hook install
40
+ - **Your taste, not the model's.** Everything is judged against a `CORPUS.md` — a [taste pack](#taste-packs) ("code like dtolnay / DHH / antirez …") or your own best files. Nothing to write to start.
41
+ - **Slop, named.** `RUBRIC.md` is your list of what counts as slop: reinvented primitives, speculative abstraction, fallbacks the types already guarantee. It keeps the fix small.
42
+ - **Both ends of the loop.** The *same* `.review/` primes the agent before it writes (prevention) and reviews the PR after (detection). The best review is the one you didn't need.
43
+ - **It remembers.** Reads the PR thread, won't re-raise what you fixed or waved off, posts `no new blocking issues ✅` when there's nothing left.
44
+ - **It's funny.** `oof, yeah this'll break:`. Turn it off if you hate joy.
45
+
46
+ ## Prime your agent (instant, local, no servers)
47
+
48
+ The best slop is the slop never written. `prime` wires a Claude Code [SessionStart hook](https://docs.claude.com/en/docs/claude-code/hooks) that injects your taste into every session — so the agent holds your standard *before* it touches a line. Pure file read, ~30ms, no model call.
49
+
50
+ ```bash
51
+ bunx @stupify/cli taste --pack sindre-sorhus,zod # pick the code yours should look like
52
+ bunx @stupify/cli prime --install # every Claude Code session now opens knowing it
27
53
  ```
28
54
 
29
- The hook runs `stupify --staged` and exits 0.
55
+ That's it. Open Claude Code in any repo and it's already primed. A repo's own `.review/` wins; otherwise it
56
+ falls back to the taste you assembled. `bunx @stupify/cli prime --uninstall` removes the hook cleanly.
57
+
58
+ ## Add the reviewer (rides exe.dev — no keys, no servers *you* run)
30
59
 
31
- Check local setup:
60
+ From your laptop, **one command** provisions a VM that reviews your repo's PRs. No API keys, no tokens — you
61
+ never even SSH anywhere.
32
62
 
33
- ```sh
34
- stupify doctor
63
+ ```bash
64
+ bunx @stupify/cli
35
65
  ```
36
66
 
37
- Default search enables the checks that currently pass the local hook-safety
38
- bench: `duplicated_schema`, `unnecessary_complexity`, `over_commenting`,
39
- `lint_bypass`, and `reinvented_utils`. Other registry patterns can be opted in
40
- with `--checks`.
67
+ ```
68
+ ┌ stupify — provision a reviewer on exe.dev
69
+ ◇ using integration acme-widgets
70
+ ◇ VM stupify-acme-widgets created
71
+ └ stupify is provisioned for acme/widgets 👀
72
+ ```
73
+
74
+ New to [exe.dev](https://exe.dev)? `ssh exe.dev` to onboard and link GitHub at
75
+ [exe.dev/integrations](https://exe.dev/integrations) — both one-time, both painless. Then just **open a PR** —
76
+ stupify reviews every non-draft, non-bot PR in ~60s, no labels or workflows to wire up. (Want manual control?
77
+ `SCOPE=label` flips it to opt-in: only PRs you tag get reviewed.)
41
78
 
42
- ```sh
43
- stupify --staged --checks over_commenting
79
+ ```bash
80
+ bunx @stupify/cli <owner/repo> # provision for a specific repo
81
+ bunx @stupify/cli setup # run the reviewer on this machine instead of a VM
82
+ ssh exe.dev rm stupify-<owner>-<repo> # tear it down
44
83
  ```
45
84
 
46
- Large search inputs are skipped rather than truncated:
85
+ ## Taste packs
86
+
87
+ Don't have a corpus yet? Borrow one. Pick a programmer whose code you'd point a new hire at and review (and
88
+ write) like them — or compose several:
89
+
90
+ [dtolnay](packs/dtolnay.md) · [DHH](packs/dhh.md) · [antirez](packs/antirez.md) ·
91
+ [Sindre Sorhus](packs/sindre-sorhus.md) · [Rich Harris](packs/rich-harris.md) ·
92
+ [zod](packs/zod.md) · [Mitchell Hashimoto](packs/mitchell-hashimoto.md) ·
93
+ [Tanner Linsley](packs/tanner-linsley.md) · [Simon Willison](packs/simon-willison.md) ·
94
+ [Anton Kropp](packs/anton-kropp.md) · [the perf pack](packs/jarred-sumner.md) · [browse all →](packs)
95
+
96
+ Each pack is concrete principles plus commit-pinned exemplar files. Or bring your own: drop a
97
+ [`.review/`](.review) in your repo and point `CORPUS.md` at the files you *wish* all your code looked like (it
98
+ always wins over a pack). stupify dogfoods this — its own [`.review/CORPUS.md`](.review/CORPUS.md) is real.
99
+
100
+ ## How it works
47
101
 
48
- ```sh
49
- stupify --staged --max-search-input-tokens 24000
50
102
  ```
103
+ prime Claude Code SessionStart hook → bun ~/.stupify/prime.ts → inject .review/ (rubric + corpus)
104
+ review cron (~60s) → review-sweep.ts → codex exec → gh pr comment
105
+ refresh checkout · list open PRs (skip drafts/bots) · skip already-reviewed heads
106
+ feed the PR's thread back as memory · review against .review/* · post
107
+ ```
108
+
109
+ Both halves read the same `.review/` (a repo's own wins; else the pack taste you assembled). The CLI
110
+ (`src/cli.ts`) sets things up; the engines (`src/prime.ts` and `src/review-sweep.ts`) are dependency-free Bun.
111
+ The whole design — including why it *remembers* instead of debouncing — is in
112
+ [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
51
113
 
52
- The package is prepared for the public `@stupify` npm scope. Publishing should
53
- use the repository release workflow so npm receives Trusted Publishing
54
- provenance. See the repository release docs.
114
+ ## License
55
115
 
56
- This iteration intentionally does not run findings audit, validators, judges,
57
- baselines, hosted LLM APIs, GitHub integration, dashboards, or repo-wide
58
- crawling.
116
+ [MIT](LICENSE) © Noah Lindner. Built by the team at [Bevyl](https://bevyl.ai). `stupif.ai` — read it "stupify". PRs welcome — it'll review them 😈
package/package.json CHANGED
@@ -1,53 +1,54 @@
1
1
  {
2
2
  "name": "@stupify/cli",
3
- "version": "0.0.16",
4
- "description": "Local-only diagnostic CLI for checking whether AI is making you dumber.",
5
- "private": false,
3
+ "version": "0.2.0",
4
+ "description": "A code reviewer that talks like an idiot and catches real bugs — corpus-grounded, anti-slop, runs on Codex.",
6
5
  "type": "module",
7
6
  "bin": {
8
- "stupify": "dist/stupify.js"
7
+ "stupify": "src/cli.ts"
9
8
  },
9
+ "files": [
10
+ "src",
11
+ ".review",
12
+ "packs",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "license": "MIT",
17
+ "homepage": "https://stupif.ai",
10
18
  "repository": {
11
19
  "type": "git",
12
- "url": "git+https://github.com/Octember/stupif.ai.git",
13
- "directory": "packages/cli"
20
+ "url": "git+https://github.com/Octember/stupify.git"
14
21
  },
15
- "homepage": "https://stupif.ai",
16
22
  "bugs": {
17
- "url": "https://github.com/Octember/stupif.ai/issues"
23
+ "url": "https://github.com/Octember/stupify/issues"
18
24
  },
19
25
  "keywords": [
26
+ "code-review",
27
+ "codex",
20
28
  "ai",
21
- "cli",
29
+ "pull-request",
22
30
  "developer-tools",
23
- "local-first",
24
- "code-review"
31
+ "anti-slop"
25
32
  ],
26
- "license": "MIT",
27
33
  "engines": {
28
- "node": ">=20"
34
+ "bun": ">=1.3"
29
35
  },
30
36
  "publishConfig": {
31
37
  "access": "public",
32
38
  "provenance": true
33
39
  },
34
- "files": [
35
- "dist",
36
- "src",
37
- "LICENSE",
38
- "README.md",
39
- "package.json"
40
- ],
41
40
  "scripts": {
42
- "build": "tsc -p tsconfig.build.json",
43
- "prepack": "bun run build",
44
41
  "typecheck": "tsc -p tsconfig.json",
45
- "smoke": "bun run build && node ./dist/stupify.js --help"
42
+ "test": "bun test",
43
+ "cli": "bun src/cli.ts"
46
44
  },
47
45
  "dependencies": {
48
- "@ataraxy-labs/sem": "^0.3.24",
49
46
  "@clack/prompts": "^1.2.0",
50
- "picocolors": "^1.1.1",
51
- "repomix": "^1.14.0"
47
+ "picocolors": "^1.1.1"
48
+ },
49
+ "devDependencies": {
50
+ "@types/bun": "^1.3.14",
51
+ "@types/node": "^22.10.2",
52
+ "typescript": "^5.7.2"
52
53
  }
53
54
  }
@@ -0,0 +1,10 @@
1
+ ## Code like Salvatore Sanfilippo / antirez (@antirez) · comments that earn their keep
2
+
3
+ Readable C, of all things — because the comments do real work. ASCII-art diagrams of the data structure sit
4
+ above the code that implements it. Banner section dividers read like chapters. Every function comment states
5
+ the contract, the error return, and the ownership of any pointer it touches. Nothing non-obvious goes
6
+ unexplained; nothing obvious gets a comment.
7
+
8
+ - [`rax.h`](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax.h) — the radix tree drawn in ASCII before a line of it is implemented.
9
+ - [`rax.c`](https://github.com/antirez/rax/blob/1927550cb218ec3c3dda8b39d82d1d019bf0476d/rax.c) — banner-divided sections; each function comment states its contract and pointer ownership.
10
+ - [`sds.h`](https://github.com/antirez/sds/blob/5347739b1581fcba74fd5cab1fc21d2aef317d71/sds.h) — a string library whose header comments are the spec.
@@ -0,0 +1,10 @@
1
+ ## Code like Anton Kropp (@devshorts) · DI + branded types
2
+
3
+ The Startup Architecture house style: every domain concept gets its own tiny wrapper type (a `QueueName`,
4
+ never a raw `String`), so a primitive never leaks across a boundary. Dependencies are wired through small,
5
+ single-purpose DI modules listed explicitly at one auditable composition root. Interfaces are single-method
6
+ contracts. `Clock` is injected so tests can move time. Fail fast and loud; no silent fallbacks.
7
+
8
+ - [`QueueName.java`](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/model/src/main/java/io/paradoxical/cassieq/model/QueueName.java) — a branded value type: a raw string can't masquerade as a `QueueName`.
9
+ - [`DefaultApplicationModules.java`](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/modules/DefaultApplicationModules.java) — the composition root: one explicit list of named DI modules, no magic scanning.
10
+ - [`ClockModule.java`](https://github.com/paradoxical-io/cassieq/blob/3856962f13e5f7d84893a2ef274d08016b2c828b/core/src/main/java/io/paradoxical/cassieq/modules/ClockModule.java) — one module, one concern (binds `Clock`), trivially swapped in tests.
package/packs/dhh.md ADDED
@@ -0,0 +1,10 @@
1
+ ## Code like DHH (@dhh) · controllers that tell the story
2
+
3
+ Conventions used so consistently that a new file is predictable. Controllers fit on one screen: actions are a
4
+ few lines, all setup pushed into `before_action` filters with self-documenting names (`set_room`,
5
+ `ensure_can_administer`). Cross-cutting behavior is extracted into named concerns (`Authentication`,
6
+ `Authorization`), not sprinkled inline. The code reads like the product, top to bottom.
7
+
8
+ - [`messages_controller.rb`](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/controllers/messages_controller.rb) — a controller you can read in one screen; the actions tell the story.
9
+ - [`authentication.rb`](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/controllers/concerns/authentication.rb) — a named policy extracted as a concern, reused everywhere.
10
+ - [`message.rb`](https://github.com/basecamp/once-campfire/blob/8d3c2bbd2be070008a275330efbee1001fd202dc/app/models/message.rb) — a fat-but-organized model with the domain rules where they belong.
@@ -0,0 +1,10 @@
1
+ ## Code like David Tolnay (@dtolnay) · the API that disappears
2
+
3
+ The gold standard of idiomatic Rust. Every public type earns its existence; the API surface collapses to the
4
+ minimum, and correct usage is the *only* usage. Errors carry context without leaking internals. Derive macros
5
+ vanish into your types and leave no fingerprint. Trait impls over free functions. Expose the smallest thing
6
+ that works.
7
+
8
+ - [`thiserror/lib.rs`](https://github.com/dtolnay/thiserror/blob/7214e0e8331d76afbea7173d8a14997512ac8713/src/lib.rs) — a tiny public surface that generates exactly the error impls you'd hand-write.
9
+ - [`thiserror/expand.rs`](https://github.com/dtolnay/thiserror/blob/7214e0e8331d76afbea7173d8a14997512ac8713/impl/src/expand.rs) — the macro internals: precise, readable codegen with no fingerprint left behind.
10
+ - [`anyhow/context.rs`](https://github.com/dtolnay/anyhow/blob/841522b2aa09732fecee40804440d2c35c68c480/src/context.rs) — errors with context attached, internals never leaked.
@@ -0,0 +1,9 @@
1
+ ## Code like Jarred Sumner (@Jarred-Sumner) · perf as a correctness concern
2
+
3
+ The perf pack. Performance decided *structurally*, not by micro-tweaking later: stack-fallback allocators
4
+ before the heap, atomic counters instead of mutexes, `comptime` conditionals that delete dead paths at zero
5
+ runtime cost. Names encode invariants. The root module is a curated namespace, not a junk drawer. Fast because
6
+ the *shape* is fast — and still readable.
7
+
8
+ - [`bun.zig`](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/bun.zig) — the root namespace, deliberately curated so the fast path is the obvious one.
9
+ - [`AsyncHTTP.zig`](https://github.com/oven-sh/bun/blob/454e3b2884c2bfabfa424ebecc3e9a1a9ee32773/src/http/AsyncHTTP.zig) — concurrency built from atomics and explicit state, not locks bolted on.
@@ -0,0 +1,10 @@
1
+ ## Code like Mitchell Hashimoto (@mitchellh) · documented tradeoffs
2
+
3
+ State machines as exhaustive tagged unions, so every case is handled or it won't compile. Data structures that
4
+ document their own tradeoffs inline. Comments explain the *why* and the tradeoff taken, never the *what*.
5
+ Explicit `MAX_*` constants with the empirical reason next to them. When something is non-obvious, it says so
6
+ candidly instead of pretending it's clean.
7
+
8
+ - [`Parser.zig`](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/terminal/Parser.zig) — a real state machine as an exhaustive enum; no `default:` swallowing the unknown.
9
+ - [`circ_buf.zig`](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/datastruct/circ_buf.zig) — a data structure whose comments justify the layout and its costs.
10
+ - [`Config.zig`](https://github.com/mitchellh/ghostty/blob/49a9181560707936c587ae121656d2d762d27849/src/config/Config.zig) — config as typed data with the constraints stated, not scattered through the code.
@@ -0,0 +1,10 @@
1
+ ## Code like Rich Harris (@Rich-Harris) · compiler-grade precision
2
+
3
+ Library code written with a compiler author's discipline: small named classes with one responsibility,
4
+ immutable sentinel constants (`BLANK`, `EMPTY_SET`) instead of re-allocating, errors that carry a `code` and a
5
+ docs URL. No defensive fallbacks — methods throw immediately with a precise message rather than papering over a
6
+ bad state.
7
+
8
+ - [`magic-string/Chunk.js`](https://github.com/Rich-Harris/magic-string/blob/410fd4d080d8bf0b5be900c16c8ba11276fd8749/src/Chunk.js) — a focused, mutation-careful data structure.
9
+ - [`rollup/blank.ts`](https://github.com/rollup/rollup/blob/5e0066d92defee0097f10fb814e63f60b2a7b612/src/utils/blank.ts) — shared sentinel objects, named and reused instead of re-allocated.
10
+ - [`rollup/getOrCreate.ts`](https://github.com/rollup/rollup/blob/5e0066d92defee0097f10fb814e63f60b2a7b612/src/utils/getOrCreate.ts) — a one-job helper extracted and reused, not inlined everywhere.
@@ -0,0 +1,10 @@
1
+ ## Code like Simon Willison (@simonw) · one concept per file
2
+
3
+ (Yes — the person who coined "slop".) Plugin-first design: every extension point is a named hookspec, declared
4
+ before it's implemented. One concept per file (`recipes.py`, `events.py`, `hookspecs.py`). Dataclasses for
5
+ typed events and data. Sentinel values over magic booleans. Docstrings explain the contract, not the
6
+ implementation. Small, obvious, testable seams.
7
+
8
+ - [`recipes.py`](https://github.com/simonw/sqlite-utils/blob/8f0c06e1889513ed0f01cb57783ddf07c442d4be/sqlite_utils/recipes.py) — one concept per module: small, composable transforms.
9
+ - [`hookspecs.py`](https://github.com/simonw/sqlite-utils/blob/8f0c06e1889513ed0f01cb57783ddf07c442d4be/sqlite_utils/hookspecs.py) — plugin seams declared up front, before any implementation.
10
+ - [`events.py`](https://github.com/simonw/datasette/blob/dfd5b95ec8adc425b683df22148cb1c14bb01128/datasette/events.py) — typed dataclass events instead of loose dicts.
@@ -0,0 +1,10 @@
1
+ ## Code like Sindre Sorhus (@sindresorhus) · one file, one job
2
+
3
+ Radical minimalism: each module does exactly one thing and is small enough to read in five minutes. A function
4
+ where a class would do; no surprise dependencies; inputs validated eagerly at the top so failures are loud and
5
+ early. Tiny public surface, deep comments on the *why*. If it can't be read top-to-bottom in one sitting, it's
6
+ too big.
7
+
8
+ - [`p-limit/index.js`](https://github.com/sindresorhus/p-limit/blob/42599ebbbb1228a5bdab381fcf8f4ac20eb8d551/index.js) — a whole concurrency limiter in one short, obvious file.
9
+ - [`execa/options.js`](https://github.com/sindresorhus/execa/blob/f3a2e8481a1e9138de3895827895c834078b9456/lib/arguments/options.js) — careful, explicit input normalization before anything runs.
10
+ - [`chalk/index.js`](https://github.com/sindresorhus/chalk/blob/aa06bb5ac3f14df9fda8cfb54274dfc165ddfdef/source/index.js) — a clean, composable API with a minimal surface.
@@ -0,0 +1,10 @@
1
+ ## Code like Tanner Linsley (@tannerlinsley) · types that forbid bad states
2
+
3
+ Private class fields (`#field`) for real encapsulation. Types do the enforcing: `Updater<T> = T | ((old: T) =>
4
+ T)`, recursion-guarded `DeepKeys<T>`, tuple utilities — illegal states are structurally unrepresentable, caught
5
+ at compile time, not asserted at runtime. Big interfaces are assembled from small feature slices rather than
6
+ written as one god-type.
7
+
8
+ - [`query/subscribable.ts`](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/subscribable.ts) — a tiny single-purpose unit with truly private state.
9
+ - [`query/focusManager.ts`](https://github.com/TanStack/query/blob/0bed37a91efa1b6e84b192ca3629d6e0c6cfcb73/packages/query-core/src/focusManager.ts) — one concern, encapsulated, testable.
10
+ - [`table/type-utils.ts`](https://github.com/TanStack/table/blob/ed814260e0a863861f8387087e72feef1b75cd37/packages/table-core/src/types/type-utils.ts) — type-algebra that makes the wrong shape a compile error.
package/packs/zod.md ADDED
@@ -0,0 +1,10 @@
1
+ ## Code like Colin McDonnell (@colinhacks) · parse, don't validate
2
+
3
+ Data never enters the system as an unvalidated primitive — it's parsed at the boundary and the parsed type
4
+ guarantees its shape. Schemas are immutable values (methods return new instances). Errors are discriminated
5
+ unions with a `code`, so every branch is exhaustive and typed. `parse` throws (fail fast); `safeParse` returns
6
+ a typed result for callers that want to branch. Composable validators replace hand-rolled type guards.
7
+
8
+ - [`parse.ts`](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/parse.ts) — symmetric `parse`/`safeParse` with the sync/async boundary enforced; the error class is injected, not hardcoded.
9
+ - [`errors.ts`](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/errors.ts) — a discriminated-union error type, every field `readonly`, a `path[]` for nested location.
10
+ - [`schemas.ts`](https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/core/schemas.ts) — distinct compile-time types per schema kind: illegal states are unrepresentable.