Undercut for OpenHands

OpenHands already speaks Agent Skills. Point it at a policy that stops overpaying.

Undercut is a free, MIT-licensed policy file in the same SKILL.md layout OpenHands' own docs specify — cheap tier first, escalate only on evidence, never on a vibe. Drop it in .agents/skills/ and OpenHands advertises it, then loads it in full when a task matches. See below for exactly how.

Install for OpenHands See the benchmarks
Works anywhere a SKILL.md is read
Claude Code Codex Cursor Copilot OpenCode OpenHands

How this maps onto OpenHands

Skills, not a bespoke config format — and no per-vendor model to assume.

Per OpenHands' current Skills Overview docs, OpenHands (formerly OpenDevin) implements the open Agent Skills specification and layers two of its own extensions on top: keyword triggers and file-path paths. A skill is a directory named for it, containing a required SKILL.md with frontmatter — name must match the directory, description tells the agent when it applies. OpenHands' documented locations are <project>/.agents/skills/<skill-name>/SKILL.md for that project's workspace, ~/.agents/skills/ for every conversation you run, and a public registry OpenHands maintains at OpenHands/extensions. The legacy .openhands/skills/ and .openhands/microagents/ directories — the old "microagent" naming from before the Agent Skills convergence — still work, but the docs say to use .agents/skills/ for anything new.

Loading is progressive by design: a skill's name and description are advertised to the model up front, and the full SKILL.md body is only injected when a task matches — keeping the initial prompt small rather than front-loading every skill in full. OpenHands' two extensions add deterministic paths around that: a triggers list injects the skill's full content the moment a keyword shows up in a user message (the skill stays callable by the model too), while a paths list turns the file into a rule that isn't advertised to the model at all and fires once per conversation the first time a matching file is read, edited, or created — and per the same docs, if a file declares both, paths wins. Separate from all of this, OpenHands also reads a repository-root AGENTS.md (plus recognizes CLAUDE.md/GEMINI.md) as always-on context loaded in full into the initial system prompt — the docs' own guidance is to keep that short and push anything specialized into an on-demand skill instead, which is exactly the shape Undercut ships in.

Undercut's tier names — cheap, standard, frontier, apex — stay generic on purpose, and that matters here specifically: OpenHands is bring-your-own-model, configurable against Anthropic, OpenAI, and dozens of other providers through LiteLLM, with no single house model to hard-code a mapping onto. models.md gives a rule of thumb instead — cheap resolves to the smallest/fastest model on your configured roster, apex to a single batched tie-break call on whichever model you'd trust most with the hardest 1% of the work. Nothing here sits between OpenHands and whichever LLM backend you've configured; it's a policy file OpenHands' own skill loader reads at dispatch time, no proxy, no network hop.

Install for OpenHands

Free. MIT. Nothing installs, nothing proxies your traffic.

Option 1 — project skill (recommended)

Clone or download undercutsh/firstpass, then copy skills/firstpass/ into OpenHands' documented .agents/skills/ location at your project root. Run this from your repo root once you've cloned the source alongside it.

$ mkdir -p .agents/skills && cp -r firstpass/skills/firstpass ./.agents/skills/firstpass

Option 2 — user skill, every project

Same directory shape, dropped under your home directory instead — OpenHands' docs list this as the user-scope location, applied to every conversation you run rather than one project workspace.

$ mkdir -p ~/.agents/skills && cp -r firstpass/skills/firstpass ~/.agents/skills/firstpass

No npx skills add path yet. skills.sh doesn't list OpenHands as a supported install target as of this writing — the manual copy above, straight into OpenHands' own documented .agents/skills/ path, is the confirmed way in for now.

Validate it yourself

  1. 1Read the published results — testing/README.md and the raw JSON in testing/results/
  2. 2Reproduce for free, no API key, no spend: node src/main.js --mock
  3. 3Start a task in OpenHands and check whether it names firstpass before starting mechanical work

Zero lock-in. It's a SKILL.md OpenHands' own skill loader reads — delete the directory and you're back to status quo.

How it works

The Undercut Rubric · v1

Six flags assign a tier. Three triggers are the only way up.

This is a named, versioned spec — not internal logic we could quietly change. When the rubric changes (a new flag, a different cap), the version number moves and the changelog says why.

Every unit of work gets scored against six flags, then assigned the cheapest tier that can plausibly handle it. It only moves up a tier when something objective proves it needs to — a failed check, a measured disagreement, an explicit uncertainty flag. It never moves up on a guess, and it never moves back down mid-task.

unit of work
6-flag rubric
0 flags
cheap~90% of units land here ✓
1–2
standard
3+/own
frontier
↑ escalate +1 tier only on: fail ×2 · two runs disagree · tagged "uncertain" residue only — never redoes the batch
See the rubric

Step 1 — Base tier, six-flag rubric

Unverifiable — can output be checked mechanically? (tests, schema, diff, grep)
Ambiguous — one right answer, or several defensible ones?
Blast — reversible? touches money, auth, user data, production, deletes?
Cross-cutting — one file/source, or reasoning across many?
Novel — pattern-following, or genuinely new design?
Format-strict — must output match an exact schema?

0 flags → cheap · 1–2 → standard · 3+ or any ownership/judgment call → frontier. Apex only when you can write one sentence stating why the marginal intelligence pays for the cost.

The override — single biggest lever

Cheap-to-verify ⇒ cheap-to-generate. If output can be checked mechanically, it starts at the lowest tier regardless of how hard it looks — verification catches failure. Only unverifiable work needs to start high.

Step 2 — Escalate exactly one tier, on evidence only

  • Verification fails twice at the current tier
  • Two cheap-tier runs disagree on ambiguous-flagged work
  • The worker tags an item "uncertain"

Residue-only handoff

The next tier up gets only the failed or uncertain items, plus verification notes. It resolves the residue — it never redoes the batch.

Hysteresis — guardrails

Never de-escalate mid-task. Max one retry per tier. Anything still unresolved goes to a single batched apex tie-break call. One hard file in a 700-file job doesn't drag the other 699 up a tier.

 unit of work
      |
  6-flag rubric
      |
  +---+----+
  | 0 flags|--> cheap
  | 1-2    |--> standard
  | 3+/own |--> frontier
  +---+----+
      | fail x2 / disagree / uncertain
      v
  escalate +1 tier
  (residue only)
      |
  still unresolved?
      v
  single batched
  apex tie-break

Why it holds up

Flags steer. Verification and escalation decide.

Even when the rubric flags are scored imperfectly, the escalator still routes work to the right tier almost every time — because a wrong flag only ever costs one extra cheap attempt, never a wrong answer or a big bill.

See the data

A stock dispatcher model reproduces the rubric flags imperfectly — Haiku hits 90% agreement, Sonnet 93%. The weakest flag (format-strict) is only 60%. None of that matters: under the shipped policy, both dispatchers still route 100% of units to the correct tier. A wrong flag changes the ladder cap, never the base tier — worst case, one extra cheap attempt.

Haiku dispatcher
90% flag agreement
100% tier-match
Sonnet dispatcher
93% flag agreement
100% tier-match

The rules are about the work, not the model.

Every rule is a property of the unit of work — verifiable, ambiguous, blast-radius — never a specific model's known weakness. Vendor cards go stale as models change; work properties don't. It's also hard-won — a format-strict rule that worked on Anthropic backfired on Gemini, which is exactly why the rubric no longer encodes per-vendor behavior.

Honest limits

What this doesn't do.

Not a proxy, not a gateway, doesn't auto-flag in production — OpenHands scores the flags itself (measured robust to getting them wrong, see above). It also doesn't pick a model for you: OpenHands is bring-your-own-model across many providers, so resolving each tier to a concrete model is still a choice you make in OpenHands' own LLM settings. Whether the CLI or OpenHands Cloud actually invokes a matching skill on a given task is model judgment, same as any other progressive-disclosure system — a description that doesn't clearly match the work won't get loaded. The directory paths, precedence rules, and trigger/path behavior described above come from OpenHands' own current Skills docs; we haven't independently re-tested every edge case end-to-end ourselves. Doesn't promise a dollar figure for your workload, and doesn't prove your number until you run it — every figure here is a reproducible observation from our tasks and graders, not a guarantee about yours. Full list of general limits, with the finance/procurement note and the paid-tier disclosure, is on the main page.

Undercut the top tier. Not the quality bar.

Nothing about this page expires. What does keep happening is the token bill, one session at a time, on whatever OpenHands is currently doing with mechanical work.

View on GitHub