Achiever
Turn everyday coding activity into Steam-style achievements, right inside VS Code.
What it does
Achiever silently tracks things you already do — typing, saving, deleting files, debugging, running terminal commands — and unlocks tiered achievements as you cross milestones. Every unlock fires a toast-style popup with a sound, and your full collection lives in the Achiever view in the Activity Bar, with a progress bar on every badge showing exactly how far you are to the next tier (e.g. 500 / 1,000 keystrokes to Silver).
Tiers
Every achievement progresses through five tiers, each with its own color theme:
| Tier |
Numeral |
Color |
| Bronze |
I |
#CD7F32 |
| Silver |
II |
#C0C0C0 |
| Gold |
III |
#FFD700 |
| Diamond |
IV |
#5DE0E6 |
| Platinum |
V |
#C9A7FF |
Achievements
Typing & Editing — Keyboard Warrior (keystrokes), Prolific Author (lines written), Serial Saver (file saves)
Time & Sessions — In The Zone (active editor time), Creature of Habit (daily streak), Marathon Coder (longest single session), Night Owl (active 10pm–4am), Early Bird (active 4am–7am), Weekend Warrior (distinct weekend days coded), Livin' like Larry (active Friday 2pm–6pm)
File Operations — Architect (files created), Marie Kondo (files deleted), Identity Crisis (files renamed)
Editor Actions — Clicky Clicky (mouse-driven selection changes, the closest VS Code lets an extension get to "clicks"), Terminal Velocity (terminal commands run, via shell integration), Bug Hunter (debug sessions started)
Editing Behavior — Ctrl+Z Enjoyer (undo count), Multi-Cursor Madness (3+ simultaneous cursors), Polyglot (distinct languages edited), Copy-Paste Wizard (large single-shot pastes), Deleter (characters deleted one backspace at a time), Jarvis, Delete That (characters deleted in bulk — selections, word-deletes), Emoji Coder (emoji characters inserted), Vibe Coder (code that appeared without you typing it — see below)
Debugging & Quality — Exterminator (a file's errors/warnings cleared to zero), Red to Green (debug sessions completed without an unhandled exception)
Customization & Exploration — Theme Hopper (color theme switches), Tab Hoarder (most tabs open at once), Indentation Rebel (tab size/tabs-vs-spaces changes), Tinkerer (any VS Code setting changed), Split Personality (most editor groups open side-by-side), Magnifying Glass (zoom/font size changes)
Project Scope — Nomad (distinct workspace folders opened)
All thresholds and titles live in src/achievements.ts — add a new entry there (and a matching StatKey in src/types.ts + tracking logic in src/statsTracker.ts) to add a new achievement.
Art & sound
- Per-achievement, per-tier artwork goes in
media/achievements/<achievementId>/<tier>.png, where <tier> is one of neutral (locked/not-yet-earned), bronze, silver, gold, diamond, platinum. The achievement IDs are the id field in src/achievements.ts (e.g. keystrokes, nightOwl, polyglot...).
- src/achievementImages.ts resolves the right file at runtime and falls back to
media/coming-soon.svg for any achievement/tier that doesn't have art yet — so art can be dropped in incrementally with no code changes.
media/sounds/achievement-unlock.wav is a silent placeholder (0.3s of silence, valid WAV header) — replace it with a real sound effect of the same filename and it'll just work.
Profile view, and why it's built store-agnostic
achiever.openProfile renders src/profileHtml.ts via src/profilePanel.ts, fed by two pure functions that take plain {stats, unlockedTiers} data rather than a live StateStore:
- src/sidebarData.ts's
buildViewModelsFromRaw(stats, unlocked) — the achievement list/progress logic, also used by the sidebar and detail panel. buildAchievementViewModels(store) is just a thin wrapper around it now.
- src/profileData.ts's
buildProfileSummary(stats, unlocked) — the header stats (score = sum of unlocked tier levels across all achievements, 0-165; tier breakdown; highest tier; etc.).
This is deliberate groundwork for a planned opt-in leaderboard: viewing someone else's profile will mean feeding these exact same functions remote JSON instead of store.getStats()/store.getUnlockedTiers(), with no duplicated rendering or scoring logic. No leaderboard code exists yet - this is just the local profile view, designed so that feature slots in later without a rewrite.
Known limitations
- Popup isn't a true floating overlay. VS Code's extension API has no floating/toast surface that supports custom HTML, images, and audio — only
showInformationMessage (text + small icon only) does that natively. To get the Steam-style look with art, tier color, and sound, the popup is a small auto-closing Webview panel that briefly opens beside your editor and disposes itself after ~4.5s. It will nudge your layout slightly; this was a deliberate tradeoff (see popupManager.ts).
- "Clicks" tracks mouse-driven selection changes, not raw OS-level mouse clicks — VS Code doesn't expose global click events to extensions.
- "Keystrokes" counts characters inserted into documents, which also picks up autocomplete/snippet/paste insertions, not just literal key presses — there's no lower-level keydown API available to extensions. Undo/redo insertions are excluded from this count (undo has its own achievement instead).
- Terminal Velocity requires VS Code's shell integration (
onDidStartTerminalShellExecution, stable since ~1.93) to be active in the integrated terminal; if shell integration isn't available, this counter just won't increment.
- Red to Green detects unhandled exceptions by listening for the Debug Adapter Protocol's
stopped event with reason exception. This is how most debuggers (Node, Python, etc.) report it, but isn't guaranteed for every third-party debug extension — some may never flag a session as "had an exception" even if one occurred.
- Polyglot only counts documents opened from disk (
scheme === "file"), so virtual documents (settings UI, diffs, output panels) don't inflate your language count.
- Vibe Coder detects "content that appeared without you typing it" by checking, for any 2+ line insertion, whether it landed at your live cursor position in the active editor. If it didn't (or the file wasn't even the focused editor), it's a candidate. This reliably catches AI agents (Claude Code, Copilot agent mode, Codex, etc.) writing to your files, but it's a heuristic, not a verified "AI did this" — see
statsTracker.ts/handleTextChange for the exact logic. Known tradeoffs:
- Git suppression, and why it's deferred: a
git pull/checkout/merge/rebase can also drop multi-line changes into a file away from your cursor. We watch the built-in Git extension's API for HEAD.commit changes to tell git apart from AI — but the Git extension detects and reports that change on its own debounced schedule, independent of (and not guaranteed to be faster than) the core file-watcher event that tells us a file changed. So instead of judging a candidate the instant it happens, we hold it for 5 seconds, then check whether a git HEAD change occurred within 15 seconds either side of it. Only if none did does it actually count as a Vibe Coder hit. This adds a small delay to the popup but avoids the race where a slow-to-report git operation gets blamed on AI.
git stash pop/apply doesn't move HEAD, so that one specific action can still slip through as a rare false positive — accepted as a known gap rather than adding a noisier signal to catch it.
- If a user has the built-in Git extension disabled entirely, this suppression doesn't run for them and git operations will register as Vibe Coder hits. Disabling that extension is rare enough that this is an accepted gap.
- Multi-line insertions excluded by this check (i.e. genuinely external edits) are also excluded from the Keyboard Warrior keystroke count immediately — that part doesn't wait on the git verdict, since it's true either way that the human didn't type it.
- Copy-Paste Wizard and Emoji Coder only look at insertions that do land at your cursor (so Vibe Coder candidates, whether ultimately blamed on AI or suppressed as git, never double-count as a "paste" or "emoji" too). Copy-Paste Wizard's threshold is single-shot insertions of 15+ characters, which will also catch large autocomplete/snippet acceptances — there's no way to distinguish those from a real clipboard paste.
Commands
- Achiever: Show Achievements — reveals the Activity Bar view.
- Achiever: Open Profile — a full-page profile: a summary header (score, achievements unlocked, highest tier, active time, daily streak, a tier breakdown) above the full achievement list. See "Profile view" below for why it's built the way it is.
- Achiever: Test Achievement Popup — fires a sample Gold-tier popup immediately, so you can check the sound/visuals without grinding 50,000 keystrokes.
- Achiever: Reset All Progress — wipes all stats and unlocks (asks for confirmation).
Settings
achiever.soundEnabled (default true)
achiever.popupEnabled (default true)
Running it
npm install
npm run compile
Then press F5 in VS Code (with this folder open) to launch an Extension Development Host with Achiever loaded. Run Achiever: Test Achievement Popup from the Command Palette to preview the popup immediately.
Stats persist in VS Code's global state, so they accumulate across every workspace you open — they are not tied to a single project.