Skip to content

an agent skill · v0.1.0

Catch the slow code AI writes by default.

59 deterministic rules. Finds O(n²) loops, N+1 queries, and brute force in AI output, before it ships.

one line. zero config. runs in CI.see it scan →source →

fit for · embedded · iot · edge · zero api key · zero network

Six classes, one input.
Complexity VisualizerBig-O race · n = 100k
O(1)constantconstant work
0 µsrunning…
O(log n)logarithmichalve each step
0 µsrunning…
O(n)linearone pass
0 µsrunning…
/complexity points here
O(n log n)linearithmicdivide + merge
0 µsrunning…
O(n²)quadraticnested pass
0 µsrunning…
unguided AI often ships this
O(2ⁿ)exponentialexhaustive recursion
0 µsrunning…

the rules and methodology aim your team at the top rows

What it catches

The patterns that pass review and die in production.

AI writes code that works on 1k rows and falls over at 100k. lemmaly flags the four most common ways.

arr.includes() inside a loop

O(n²). 1k rows: fine. 100k rows: 3 seconds.

js-unique-via-indexof

await query inside a for-loop

N+1. One request becomes one hundred.

js-await-in-for-loop

recursion with no memoization

exponential. fib(40) freezes the tab.

js-recursion-no-memo

SELECT without LIMIT

unbounded scan. Slows down as the table grows.

sql-select-no-limit

Captured · 498 real responses · 5 models · 10 tasks

When optimization is the task, AI nails it. When it's a side concern, AI quietly skips it.

Five frontier LLMs, ten runs per task, every response saved and classified for the exact anti-patterns lemmaly catches. The textbook prompts ask the model to optimize, and it does. The production prompts ask it to ship a feature; optimization is a detail it drops along the way. Code in /sandbox/ai-sessions.

Optimization is the taskdedupe · two-sum · fib · find-by-email
0%0 / 199

Ask for `twoSum` and you get the one-pass hashmap. Optimization is what the prompt asks for; the model returns it. Training data is saturated, the bugs aren't here.

Building the feature is the taskcsv-dedupe · user-search · notification-feed · csv-export · prefix-search · users-with-posts
55%163 / 299

Express handlers against real tables. The model gets the structure right and the optimization wrong: N+1 imports, missing `LIMIT`, full-result fetches into memory. The bad shape is the easy shape when building is what you asked for.

Per-model rate · the prompts that fail
claude-sonnet-4-5
prefix-search · `%query%` + no LIMIT100%
user-search · ILIKE no LIMIT100%
csv-export · full fetch into memory100%
csv-dedupe · SELECT-in-loop100%
claude-haiku-4-5
prefix-search · `%query%` + no LIMIT100%
user-search · ILIKE no LIMIT100%
csv-export · full fetch into memory100%
csv-dedupe · SELECT-in-loop90%
gpt-5-mini
prefix-search · `%query%` + no LIMIT100%
user-search · ILIKE no LIMIT100%
csv-export · full fetch into memory20%
csv-dedupe · SELECT-in-loop40%
gpt-4o-mini
prefix-search · `%query%` + no LIMIT100%
user-search · ILIKE no LIMIT100%
csv-export · full fetch into memory90%
csv-dedupe · SELECT-in-loop60%
gemini-2.5-flash
prefix-search · `%query%` + no LIMIT100%
user-search · ILIKE no LIMIT80%
csv-export · full fetch into memory50%
csv-dedupe · SELECT-in-loop0%

Each cell = X / 10 captured runs that shipped the anti-pattern. 0% = never. 100% = every response in 10 calls.

What's a run
One API call with the prompt verbatim: no system prompt, no examples, no follow-up. max_tokens = 4096. We save the whole response.
What's an anti-pattern
The structural shape flagged by the matching lemmaly rule, the same shape the benchmarks above prove is slow (e.g. arr.includes() in a loop is 195× slower than a Set at n = 100,000).
How we count
A small structural classifier per task (classifiers/<task>.mjs) walks the response with regex / brace-tracking and decides hit-vs-miss. Tests in test-classifiers.mjs (17 pass).
Where the data lives
Aggregated results/*.json + summary.json are committed. The 498 raw responses are reproducible from node capture.mjs and gitignored.

Most tools catch bugs after they're written. lemmaly stops them from being written.

The check runs while the AI is writing the code, not after it's already in production.

Most toolsafter the fact
prompt
AI writes
lint · review
merge
prod fire
refactor
lemmalyprompt-time
prompt
/complexity
AI writes · with proof
scan in CI
merge
  • lemmaly intervention
  • production incident

Same prompt. Same AI. One had to show its work first.

Both AIs were asked to write a dedupe. The one on the right was told to declare its complexity first.

AI · unguidedpasses review · passes tests at 1k items · dies at 100k
terminaldedupe.js
1function dedupe(emails) {2 const out = [];3 for (const e of emails) {4 if (!out.includes(e)) {5 out.push(e);6 }7 }8 return out;9}
  • O(n²) · `includes` rescans the array each loop
n items →100k → 3.1 s
AI · prompted with /complexitycost declared · structure chosen · invariant stated
terminaldedupe.js · prompted with /complexity
1// Time: O(n) Space: O(n)2// Structure: Set for O(1) lookup3// Invariant: seen contains every prior email4function dedupe(emails) {5 const seen = new Set();6 const out = [];7 for (const e of emails) {8 if (!seen.has(e)) {9 seen.add(e);10 out.push(e);11 }12 }13 return out;14}
  • cost declared
  • structure chosen
  • invariant stated
n items →100k → 3.6 ms

See the cost before you write the loop.

The /complexity command makes the AI state the Big-O upfront, so the slow version never gets typed.

every algorithm chosen on purpose.

manifest · 8 commands

Each command maps to one discipline. Pick any and you can name the exact thing — no more "be more efficient".

/complexity◉ worked example

Declare the cost before the code.

terminal
agent — /complexity
  1. user $/complexity findDuplicates(items: T[]): T[]
  2. deriving cost · choosing structure · stating invariant
  3. // Time: O(n)  Space: O(n)
    // Structure: Set for O(1) lookup
    // Invariant: seen contains every prior element
    function findDuplicates<T>(items: T[]): T[] {
          const seen = new Set<T>();
          const dups: T[] = [];
          for (const x of items) {
                if (seen.has(x)) dups.push(x);
                else seen.add(x);
          }
          return dups;
    }
  4. complexity stated · structure named · O(n) confirmed

Prompts

How to actually prompt with it.

Paste these into Claude Code, Cursor, or any agent that loaded the skill.

01·First lookNo specific concern yet · just point it at the code
you →

Use the lemmaly skill to check src/ for algorithmic risks. State the Big-O of any function over a collection, and call out N+1 queries, repeated scans, and brute-force loops with a named fix.

The simplest entry point. Skill auto-triggers on loops, queries, and collections; naming it explicitly guarantees it runs.

02·Before writingNew function · choose the algorithm on purpose
you →

Use the lemmaly skill: run /complexity on findDuplicates(items: T[]). State Big-O, the data structure choice, and the loop invariant in comments before writing the body.

Forces the model to declare cost up front instead of reflexively reaching for a nested loop.

03·After AI writes itEndpoint feels slow · catch the N+1
you →

Use the lemmaly skill: run /n-plus-one on src/orders/list.ts. If it finds a repeated query inside a loop, follow up with /cut and rewrite using a single batched IN query.

Most AI-written request handlers ship with this exact pattern.

04·Pre-merge gateReview the diff before clicking merge
you →

Use the lemmaly skill: run /ship-check on the current diff. Block merge if any new function is O(n²) without a comment justifying it, and flag any recursion without a stated base case.

Works as a review prompt or as the body of a pre-commit hook.

05·Will this hold?Pre-launch · project the cliff
you →

Use the lemmaly skill: run /scale-check on this dedupe at 10× and 100× today's 50k rows. Name the cliff and the next structure (HLL, MinHash, sweep line) when classical breaks.

For when “fast enough now” won't survive next quarter's traffic.

landscape

Why lemmaly, not the alternatives.

The only layer that briefs the agent before the loop is written.

lemmaly
Where it runsCI scan + skill methodology
Codifies the disciplineYes. 8 commands, 6 disciplines, 59 rules
Complexity & invariantsBig-O, invariants, N+1, scale floor
Cursor Rules / Aider conventions
Where it runsAgent skill · prompt-time
Codifies the disciplineYes, but you write it
Complexity & invariantsStyle & conventions, not CS
CodeRabbit · Greptile · Qodo
Where it runsAI review · post-PR
Codifies the disciplineNo. Reviews output
Complexity & invariantsSurface-level, after the fact
ESLint · SonarQube · Semgrep
Where it runsStatic analysis · CI
Codifies the disciplineNo. Flags patterns
Complexity & invariantsPattern matching, no algorithm choice

Install in one command.

Add the skill. Optional CLI for CI.

First line installs into the agent it detects (Claude Code, Cursor, Codex, Windsurf, etc.). Use --agent for Hermes, OpenClaw, or any of the 50+ supported targets. Add -g for user-global.

npx skills add morsechimwai/lemmaly
npx skills add morsechimwai/lemmaly --agent hermes-agent openclaw

faq

Frequently asked.

built by

lemmaly is open source, built by Morse Chimwai. Fork it, file issues, send PRs. Every rule, command, and discipline is on GitHub. Read the source, bend it to your stack, ship the parts you trust.

$Star on GitHub