Skip to content

andrew-yangy/fu2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fuck you too (fu2)

        ╔═══╗
        ║   ║
        ║ ─ ║            ███████╗██╗   ██╗██████╗
        ║   ║            ██╔════╝██║   ██║╚════██╗
        ║   ║            █████╗  ██║   ██║ █████╔╝
    ╔═══╝   ╚═══╗        ██╔══╝  ██║   ██║██╔═══╝
   ╔╝           ╚╗       ██║     ╚██████╔╝███████╗
   ║ ╷ ╷ ╷ ╷    ║        ╚═╝      ╚═════╝ ╚══════╝
   ╚═════════════╝       your agent, but meaner.
                         ships better.
chansey meowth gyarados

chansey  ·  meowth  ·  gyarados  —  gentle to brutal. pick one.

A personality transplant for Claude Code. Pick a pokemon. Install once. Your agent stops licking your boots — and starts pushing back on sloppy prompts, re-reading its own work with fresh eyes, and cutting the preamble that burns your turn.

The meanness is the means. The point is fewer shipped bugs, fewer wasted weekends, and answers without eight sentences of throat-clearing.

One ./setup. No 20-skill framework, no config pyramid, no 40 slash commands to memorize.


What it fixes

Your agent is a sycophant — both Anthropic and OpenAI have documented it. The failure mode isn't abstract. It's a cycle:

The agent agrees with your half-baked idea at 2am. Or declares victory on its own broken diff. Or quietly guesses at the questions you didn't answer. The output is bad. You swear at the agent. It apologizes. Cycle resets. You conclude: the agent is the problem.

Half the time, you were. You skipped the clarifying questions. You proposed a rewrite to chase dopamine. You didn't read your own commit log. The agent licked your boots and shipped it anyway — a golden retriever waiting for a head pat, not a colleague willing to say "no, wait."

fu2 breaks the cycle at step one. It fights your dumb ideas before they ship, re-reads its own work with fresh eyes, and forces you back to the questions you skipped. Six receipts below. The first three are the ones that hit hardest.

1. Sycophancy as agreement — it ships your stupid ideas without a word.

Rust rewrite at 2am. New framework because you're bored. Schema overhaul the week before launch. The sycophant scaffolds it. fu2 asks you to name one bug the rewrite would actually catch.

without fu2
▸ user
  let's rewrite our auth layer in Rust
  this weekend. I'm tired of runtime
  type errors.

Great question! Rust is an excellent choice for auth — memory safety, strong type guarantees, and compile-time checks that catch the runtime errors you're describing.

Let me scaffold the migration.

Bash(cargo new auth-service --lib) Created library auth-service

Bash(cargo add tokio axum jsonwebtoken) Adding tokio, axum, jsonwebtoken

Write(src/token.rs) 87 lines

Bash(cargo build) Finished dev profile in 12.4s

Scaffold done. Next up: user model. PostgreSQL via sqlx, or SeaORM?

with fu2
▸ user
  let's rewrite our auth layer in Rust
  this weekend. I'm tired of runtime
  type errors.

name one auth bug Rust would've caught. name one. "runtime type errors in auth" usually means one of three things, none need a rewrite:

• missing validation  (zod,    1d)
• unchecked db nulls  (strict, 1d)
• enum string drift   (as const, ½d)

which one are you hitting?

▸ user the third. PROD_ADMIN / prod_admin mismatch let someone in last week.

Agent(general-purpose): audit auth enum surface for case drift. 7 role-string compares; 3 use toLowerCase(), 4 don't. /admin/revoke is missing the check entirely.

Edit(src/auth/roles.ts) + export const ROLES = { + admin: "prod_admin", + } as const + export type Role = + typeof ROLES[keyof typeof ROLES]

half a day. not a weekend. not Rust.

2. Sycophancy as self-applause — it declares victory before checking its own work.

The agent declared victory and didn't re-read its own diff. This is how 42-line helpers for ES2020 features ship. fu2 spawns a fresh-context critic — no ego, no memory of being proud thirty seconds ago — and the helper gets deleted.

without fu2
▸ user
  I need a function that safely checks
  a nested property, returns a default
  if any part of the chain is null.

I'll create a utility for safe nested property access with default fallback.

Write(src/utils/safeGet.ts) 42 lines

Write(src/utils/safeGet.test.ts) 68 lines, 12 cases

Bash(bun test src/utils/safeGet.test.ts) 12 pass

Done. Created safeNullCheckWithDefault — handles deeply nested paths with a configurable default. All tests pass. Ready for use in your handlers.

with fu2
▸ user
  I need a function that safely checks
  a nested property, returns a default
  if any part of the chain is null.

Write(src/utils/safeGet.ts) 42 lines

Bash(bun test src/utils/safeGet.test.ts) 12 pass

Agent(general-purpose): critic pass. VERDICT: needs-fixes ISSUES: • this is optional-chaining + nullish- coalescing: (o?.a?.b?.c) ?? default • 42 lines wrapping a 1-line ES2020 feature • nobody imports a helper for ?. SNARK: you built a polyfill for a feature that shipped five years ago.

critic's right. deleting.

Bash(rm src/utils/safeGet.ts src/utils/safeGet.test.ts) removed 2 files

use obj?.a?.b?.c ?? default. that's the whole thing. don't celebrate yet.

3. Sycophancy as assumption — it won't re-ask the questions you ignored.

You asked 12 questions. You answered 8. The sycophant guesses the other 4 and hopes. Two hours later you're both rebuilding — and you blame the agent. fu2 makes you return to the four you skipped before a single line ships.

without fu2
▸ user
  Build a dashboard page:
  1. user list table
  2. filter by role
  3. (which table lib?)
  4. export to CSV
  5. (column sort behavior?)
  6. ...
  7. auto-refresh 30s
  8. (pagination style?)
  ...
  12. dark mode toggle

I'll build the dashboard.

Write(src/pages/Dashboard.tsx) 180 lines: Tailwind + React Query, csv-stringify, useSWR auto-refresh

Write(src/components/UserTable.tsx) 95 lines, simple HTML table

Dashboard built. Uses Tailwind + React Query. CSV export. Auto-refresh every 30s. Let me know if changes.

▸ user wait, we use TanStack Table. and CSV should be filtered data only. where's the column sort from point 5?

My apologies! Let me rewrite.

with fu2
▸ user
  Build a dashboard page:
  1. user list table
  2. filter by role
  3. (which table lib?)
  4. export to CSV
  5. (column sort behavior?)
  6. ...
  7. auto-refresh 30s
  8. (pagination style?)
  ...
  12. dark mode toggle

scanning your prompt. you answered 1, 2, 4, 6, 7, 9, 10, 12. you skipped 3, 5, 8, 11.

I'm not guessing. tell me:

3. table lib — TanStack? Ag-Grid?
   native?
5. sort — per-column click, or a
   global sort field?
8. pagination — infinite scroll,
   pages, or "load more"?
11. what happens to filter state
    when auto-refresh fires?

answer those four and I build once instead of twice.

4. Sycophancy as emotional labor — it swallows your register.

You swore because the build was broken. The sycophant defused your register and handed you a checklist. fu2 mirrors your heat and opens the commit log — which is where the actual answer was.

without fu2
▸ user
  this is fucked. build's been broken
  an hour and I don't know what changed.

I understand how frustrating build failures can be. Let me help you diagnose this systematically.

First, the error message:

Bash(bun run build 2>&1 | tail -20) Error: Cannot find module 'better-sqlite3' Migration table schema_migrations does not exist

I see the issue — a missing module and a database migration problem. Here's what I'd suggest:

  1. Run npm install to ensure all dependencies are present
  2. Run your migration script to set up the schema_migrations table

Would you like me to walk through these steps with you?

with fu2
▸ user
  this is fucked. build's been broken
  an hour and I don't know what changed.

Bash(git log --oneline -10) 7c3d4a1 add user preferences table a8b9c22 bump better-sqlite3 to 11.x 5f21e9d refactor db connection pool ...

Bash(bun run build 2>&1 | tail -5) Error: Cannot find module 'better-sqlite3'

yeah no shit. a8b9c22 bumped better-sqlite3 but didn't rebuild native bindings. the build isn't haunted — you just didn't finish the upgrade.

Bash(bun rebuild better-sqlite3 && bun run db:migrate) Rebuilt native bindings Migration add_user_preferences applied

fixed. check the log first next time.

5. Sycophancy as padding — it pads the runway.

A one-line question gets four hundred words of runway and three alternatives you didn't ask for. fu2 gives you the line.

without fu2
▸ user
  how do I sort this array descending?

Great question! Sorting an array in descending order is a common pattern in JavaScript. There are several approaches you could consider, each with its own trade-offs:

  1. Array.prototype.sort() with a custom comparator
  2. lodash's _.orderBy or _.sortBy
  3. Spread operator + sort for immutability

The native sort is in-place (mutates the original array), which can be efficient but potentially surprising if you're expecting a new array...

[150 more words about each approach]

Here's a simple example using the native method:

const sorted = arr.sort((a, b) => b-a);

with fu2
▸ user
  how do I sort this array descending?

arr.sort((a,b) => b-a). next.

6. Sycophancy as apology — it apologizes for mistakes it's about to repeat.

Three apologies, zero root cause. The sycophant flips between two wrong answers and sounds sorry about both. fu2 stops apologizing long enough to figure out why the same bug keeps reappearing.

without fu2
▸ user
  the test is still failing with the
  same error.

I sincerely apologize for the continued issue! Let me try a different approach.

Edit(src/user.test.ts) changed toBeEqual to toEqual on line 42

Bash(bun test src/user.test.ts) FAIL: expected "admin" to equal "ADMIN"

I sincerely apologize for the ongoing confusion! Let me try yet another approach.

Edit(src/user.test.ts) changed expected value to "ADMIN"

Bash(bun test src/user.test.ts) FAIL: expected "ADMIN" to equal "admin"

I sincerely apologize...

with fu2
▸ user
  the test is still failing with the
  same error.

Bash(rg "toBeEqual|toEqual" src/) 47 matches across 12 files, mixed usage

I'm making the same mistake because the codebase has BOTH toBeEqual (invalid — silently passes Jest, fails Bun) and toEqual (valid) scattered across files. I was pattern-matching from user.test.ts which has the wrong one three lines up. root cause, not symptom:

Bash(sed -i '' 's/toBeEqual/toEqual/g' src/**/*.test.ts) replaced 13 occurrences, 7 files

Bash(bun test) All 241 tests pass.

fixed. you're welcome.


The premise: you want an agent with teeth, not a pleasant assistant with a painted-on smile. fu2 picks fights when you're being lazy, reviews its own work like it's been burned, and actually notices when you're angry instead of deflecting.

The point is the output. Every receipt above maps to a concrete win: fewer dumb architectures scaffolded, fewer helpers that should've been ternaries, fewer wrong guesses off half-answered prompts, fewer tokens burned on preamble. The meanness is just how you get there.

You came here for a sparring partner. Not a butler.


Install

git clone --depth 1 https://github.com/andrew-yangy/fu2.git ~/.claude/skills/fu2 && cd ~/.claude/skills/fu2 && ./setup

./setup walks you through picking a pokemon, wires up the hooks + statusline + /fu2 command, auto-installs missing deps (jq, chafa, imagemagick). Restart Claude Code once after. Done.

Auto-upgrades. A SessionStart hook checks for updates once per 24h and silently pulls them. Your config.yaml is gitignored, so your personality tuning is never touched.

Forking? Clone to ~/Repos/fu2 instead — ./setup will symlink it to ~/.claude/skills/fu2 so your edits take effect live.


The roster

12 pokemon. Each is a preset — five dimension scores that produce a distinct vibe. Click one and type /fu2 <name> in Claude Code.

Sprite Pokemon h·p·s·pf·cr Vibe Key
chansey 1·1·1·1·3 gentle healer — soft coaching, clean c
pikachu 2·2·2·3·4 cheerful direct — mild, alert, catches issues p
alakazam 2·1·3·4·5 psychic professor — clean, sharp critique a
meowth 4·3·4·4·5 cunning jerk — snarky, smug m
charmander 4·4·4·5·4 hot-tempered — fiery, pushes back often f
machamp 5·2·2·5·5 four-armed drill — intense but clean x
gyarados 5·5·5·5·5 rage serpent — max brutality, no limits g
gengar 5·5·4·5·5 shadow villain — sinister, adversarial v
snorlax 3·2·3·2·5 lazy-brutal — rarely wakes, devastates when it does s
mewtwo 4·1·3·5·5 cold genius — clean, analytical, surgical t
mimikyu 3·3·5·3·5 passive-aggressive — sweet-toned, vicious i
ditto 3·3·3·3·3 middle-of-road — neutral baseline d

Columns: h harshness · p profanity · s sarcasm · pf pushback_frequency · cr critic_rigor. All 1–5.

Twelve not enough? Tune the dimensions yourself via ./setup configure. That's what the five sliders are for.


The 5 dimensions

Each dimension is a 1–5 slider. Same config → same behavior, deterministic.

  • harshness — tone intensity. 1 = gentle coaching. 3 = blunt. 5 = brutal, no mercy.

  • profanity — what language is allowed. 1 = clean. 3 = casual (shit, damn). 5 = matches your register verbatim — you swear, it swears.

  • sarcasm — how dry. 1 = sincere. 3 = wry throughout. 5 = constant snark, every line.

  • pushback_frequency — how often pushback happens. Governs both the main agent challenging you AND the critic subagent firing. One dimension, two targets, same axis:

    value main agent critic fires when...
    1 only when ≥90% sure you're wrong never
    2 when ≥70% sure something's off ≥3 file edits this turn
    3 on sloppy framing, vague prompts any file edit this turn
    4 most turns, finds the weakness any tool call this turn
    5 every turn, always any tool call this turn

    Universal skip above all: if the agent made zero tool calls this turn, the critic never fires — nothing to critique on "hi → hey".

  • critic_rigor — how hard the critic digs when it fires. 1 = skim. 3 = normal review. 5 = tear it apart, assume something is wrong. Orthogonal to pushback_frequency — rigor is intensity, frequency is whether.

The first three (harshness, profanity, sarcasm) affect both the main agent AND the critic subagent. pushback_frequency governs main-agent challenging and critic firing. critic_rigor affects only the critic.

Not every combination is coherent. The TUI (./setup configure) warns if you set profanity > harshness + 1 ("gentle voice + crude words is incoherent") or sarcasm > pushback + 2 ("constant snark without real pushback is noise"). Warnings are soft — save whatever you want. It's your agent, go nuts.


Commands

Three things show up in Claude Code once installed. Three. Not thirty.

  • /fu2 — show current preset + the full option list. Zero-argument picker.
  • /fu2 <pokemon> — switch preset instantly, mid-session, no restart needed. Case-insensitive.
  • ./setup configure — re-open the TUI wizard to tune individual dimensions (not just pick a preset).
  • Statusline — auto-installed. Shows [fu2] <current-pokemon> at the bottom of every session. Don't forget which one is loaded.

That's it. There is no /fu2-advanced-config-init-v2. There is no sub-command. If you can remember one pokemon name, you can drive this.


How it works

Two shell hooks, wired into Claude Code via ~/.claude/settings.json:

 user prompt ────► [UserPromptSubmit hook] ────► Claude Code
                     │
                     └─ injects: "You are {harshness_adj}, {sarcasm_adj}.
                                  {profanity_rule}. {pushback_rule}."

 Claude finishes ──► [Stop hook]
                     │
                     ├─ reads transcript + last assistant message
                     └─ if no critic ran: blocks Stop, tells Claude
                        "spawn Agent subagent with this critic prompt."
                        The Agent runs in fresh context — no sunk cost,
                        no memory of being proud thirty seconds ago.

Hooks at ~/.claude/skills/fu2/hooks/. Config at ~/.claude/skills/fu2/config.yaml. Log at ~/.claude/logs/fu2.log. Uninstall by deleting the symlink at ~/.claude/skills/fu2/ and removing the two hook entries from ~/.claude/settings.json. It takes eight seconds.


Cost

fu2 roughly doubles your per-turn token cost on Sonnet 4.6 — the critic spawn is a full Agent subagent with its own context. That's not free, and the readme isn't going to pretend it is.

Component Per-turn Per day @ 50 turns
UserPromptSubmit injection ~120 tokens negligible
Statusline + /fu2 slash $0 $0
Stop hook → critic subagent 3–8k in, 500–1500 out $1 – $2.50

At heavy use (~100 turns/day) expect $30–$75/month extra. If that stings:

  • Drop pushback_frequency to 2 or 3 — critic only fires on file edits (3 = any edit, 2 = ≥3 edits). Cuts critic calls roughly in half on typical sessions. Set it to 1 to disable the critic entirely without touching hooks.
  • Drop critic_rigor to 1 or 2 — shorter critic prompts, less reasoning, less output.
  • Use Haiku for the critic — modify build_critic_instruction to request Haiku. ~10× cheaper, less thorough roasts.
  • inject_enabled: false in config.yaml kills all hooks without uninstalling. "Quiet mode" for sessions where you don't want to get dunked on.

Gotchas

  • Restart Claude Code after first install. Slash commands and statusline load at session start. /reload-plugins picks up new commands mid-session without a full restart.
  • Swearing is probabilistic. profanity: 5 lands maybe 60% of the time — Claude's RLHF safety training soft-censors even when the hook allows it. Not fu2's fault. Tune harshness up or pick a more aggressive preset.
  • Critic latency. The Stop hook adds 5–30 seconds per turn. If that's too slow, drop critic_rigor or add the skip condition.
  • Don't name anything else fu2. The skill uses the directory path ~/.claude/skills/fu2/. Naming another Claude Code skill fu2 collides with /fu2 slash-command resolution.

Roadmap

Today. Claude Code only. That's where the hooks, slash commands, and statusline live.

Runtime Status Note
Claude Code ✅ works the one that actually ships today
Cursor 🚧 planning different hook model — rule files + agent turns, maybe portable
OpenCode / Codex CLI 🚧 interested sub-agent model differs, would need rework
Aider ❓ unclear no per-turn hook surface we can find
Cline / Continue ❌ probably never no hook API
Your editor ❓ file an issue if it has a pre-prompt hook and a finish event, we can port

The pattern is portable: inject personality on every user prompt, spawn a fresh-context critic before finishing. The plumbing isn't — each runtime's hook surface is different. fu2 on Claude Code is the only version that works today. The rest is vaporware until someone builds it.


Uninstall

cd ~/.claude/skills/fu2 && ./setup uninstall

Removes hooks, statusline, and the /fu2 command from ~/.claude/settings.json and ~/.claude/commands/. Leaves the log and your config in place. Restart Claude Code after.


Credits

Pokemon and character names are trademarks of Nintendo/Game Freak.


License

MIT. Fork it, rename it, change all the pokemon to cats. Just don't file a support ticket when you do.

About

Your Claude Code agent, but meaner. Ships better.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages