@juanwmedia/sharpen
v0.2.1
Published
Sharpen by FrontendLeap. Timed challenges in your browser (Git and TypeScript), with a Socratic AI mentor.
Readme
Install
npm install -g @juanwmedia/sharpen
sharpensharpen starts a local server and opens the arena in your browser; the
package ships prebuilt, nothing is downloaded at runtime. sharpen 8 opens
scenario number 8 (the number frontendleap.com prints next to each one),
sharpen stop shuts the server down, sharpen update gets the latest
version, and sharpen harness re-picks the agent CLI that powers the
mentor. Prefer not to install? npx @juanwmedia/sharpen works too.
Each scenario has its own URL (/git/clean-sweep), so you can deep-link
straight into a run.
Requirements
- Node.js 20+
- Claude Code installed and authenticated (the mentor runs as a headless
claude -pprocess on your machine, using your account; model defaults tosonnet, override withSHARPEN_MENTOR_MODEL=haiku) ghCLI (only for the shared ranking, coming in v2)
The core loop
Every scenario is the same ritual, and the ritual is the product:
- Read the briefing. A real situation, the file tree it left behind, and one objective. The check rubric is on the table before you start, so you always know what "solved" means.
- Press Start and type real git. The repo is real and runs entirely in
your browser:
git statustells you the truth, pipes andrm -rfwork, and wrong moves have consequences. - Every Enter validates. Each command you run makes the server replay your whole transcript with the same engine and re-judge the final state of the repo. The verdict panel flips check by check as you close in.
- The mentor watches, Socratically. Stuck? Ask. Fail a check? It nudges. While you are live it answers with questions and concepts, never with the solving command. You do the thinking; that is the whole pedagogy.
- Solve it, or fail forward. Pass and your speed becomes your score. Run out of clock and nothing is spoiled: retry the scenario cold, or switch to learn mode and let the mentor guide you to it. Either way you leave knowing more git than you came with.
Two ways to play, one switch in the top bar (challenge is the default):
- Challenge: 60 seconds against the clock, your speed is your score. The mentor watches but never reveals, in time or out of it; every retry is a clean take. The adrenaline mode. (Attempts are recorded locally; the shared ranking arrives in v2.)
- Learn: no clock, no ranking. The mentor opens the conversation, progress persists between sessions and you decide when to reveal the solution. The mastery mode.
Languages
The arena speaks English and Spanish: switch with the EN/ES toggle in the top bar (persisted locally, English by default). The mentor answers in your language too. Scenario content (briefing, objective, and verdict checks) is written in both languages by the scenario author and the type system enforces it; the ASCII tree and titles/URL slugs stay in English, and the emulated git speaks English like the real one.
Under the hood
Browser (SPA) Local server (Node)
┌───────────────────────┐ POST ┌─────────────────────────────────┐
│ challenge + 60s timer │ ────────► │ authoritative timer + replay │
│ terminal: just-bash │ │ validation with the same engine │
│ + isomorphic-git │ ◄──────── │ spawns claude -p (mentor turn) │
│ mentor conversation │ SSE │ evidence + leaderboard JSON │
└───────────────────────┘ └─────────────────────────────────┘- The repo is real. isomorphic-git over a virtual filesystem shared with a
simulated bash (just-bash): pipes, grep, redirections all work. A hand-written
porcelain layer makes
git status,git clean,git restoreand friends behave and print like real git, including its refusals. - State is what counts. Validation asserts on the final repo state (refs,
index, working tree), never on what you typed. Any correct solution passes,
git clean -fdandrm -rf junk/alike. - Every Enter validates. Each command (or an empty Enter) triggers a server-side replay of your transcript with the same engine, and the mentor reacts to what actually happened. The mentor only does pedagogy, never grading.
- Everything is evidence. Each run records its transcript, engine version,
duration and a state hash to
~/.sharpen/evidence/. That package is exactly what CI replay validation will verify for the shared ranking (v2), with GitHub Issues as the submission channel and GitHub's own clock closing the timing hole (v3).
Current limitations
A living list, updated with every engine change. The arena is honest at the
prompt too: any unimplemented subcommand answers
sharpen: 'git X' is not available in this arena (yet).
Git porcelain implemented today: status (-s), add (paths, -A),
commit (-m, -a, -am, --amend with -m or --no-edit), log
(--oneline, -n), branch (list, create, -d with the real unmerged
refusal, -D), checkout (-b, branch, -- <paths>), switch (plain and
-c), restore (--staged, pathspecs), clean (-n, -f, -d), rm
(--cached, -f, with real git's refusals on staged or modified files),
reset (unstage form; --soft/--hard with HEAD~, oid, branch, or
HEAD@{n}), reflog (-n), diff (unstaged and --staged/--cached,
with real git's colors and hunk format), init.
Not there yet, planned in this order: reset --mixed against a commit,
stash, cherry-pick, revert, restore --source, merge with real
conflict markers.
Out of scope by design: remotes (push, pull, fetch, clone): the
arena is a local, deterministic sandbox. rebase: it does not fit the timed
arena format. git worktree: being explored as its own future pack (agent
isolation workflows).
Other honest edges:
restore <path>sources content from HEAD rather than the index; behavior is identical unless the path has staged changes.git <command> --helpnever replicates real manuals: it answers a short arena card (what the command does, exactly what this arena supports, and a pointer to the mentor, who owns the concepts).- The shell is just-bash: real pipes, grep, redirections and coreutils, but not every GNU flag of every tool.
Scoring (v1)
Pass: max(10, 100 - seconds elapsed). Timeout or fail: 0, but the attempt is
recorded. Every Enter validates (that is the core mechanic), so speed is the
only score input. The policy is versioned with the engine; it will not change
silently under a live ranking.
Development
Stack: Vue 3 + TypeScript (strict) + Vite + Tailwind 4 + vue-i18n + vue-router
for the arena SPA, organized as Feature-Sliced Design under src/. The engine
and the Node server are plain TypeScript (run via tsx). The engine stays
framework-agnostic on purpose: it runs in the browser, in the server's replay
validation, and in v2's CI Action.
npm install
npm run dev # Vite dev server with HMR (proxies /api to :4517)
npm run start # the arena server on http://127.0.0.1:4517
npm run build # typecheck (vue-tsc) + production build to dist/
npm test # vitest: porcelain, verdicts, fs bridge, mentor queue, API, i18n, slugs
npm run typecheck # vue-tsc --noEmit
npm run bundle # esbuild server.mjs + cli/sharpen.mjs + catalog (prepack)
npm run release # verify guards and gates, tag, push the tagReleases ship through npm. npm run release refuses a dirty tree, failing
gates, an existing tag or an unpushed HEAD, then tags v<version> and
pushes the tag. That triggers .github/workflows/release.yml: a 3-OS gate
packs the publishable tarball, installs it globally, boots the arena through
the real CLI and solves one scenario per kind through the API; only then the
publish job ships @juanwmedia/sharpen to npm with provenance.
To add a scenario: create a package folder scenarios/<pack>/<name>/ with
scenario.md (schema 2 frontmatter + bilingual sections), scenario.yaml
(declarative setup steps, check predicates and the canonical solution) and
walkthrough.md, then register it in scenarios/index.ts. Scenarios are
documents, not code: the engine interprets and validates them. See
scenarios/package/FORMAT.md. Its URL becomes /<pack>/<slug-of-title>.
Architecture, contracts and conventions live in CLAUDE.md. Verified
third-party API facts (just-bash, isomorphic-git, wterm, claude CLI, vue-i18n)
live in docs/api-notes.md: read them before touching engine code.
Roadmap
- v1 (now): local arena, git pack plus TypeScript pack (Monaco + Run), local ranking.
- v2: shared ranking. Submissions via GitHub Issues (identity signed by GitHub), CI Action replays every transcript with the same engine and regenerates the leaderboard; only verified entries count.
- v3: nonce-seeded scenarios plus GitHub server timestamps, making claimed times attack-resistant. More packs (Unix 101, …). A challenge declares its artifact; the arena does not change.
