| name | autoresearch-universal |
| description | Self-improving prompt optimization using the Karpathy autoresearch pattern. Scans any repo, suggests optimization targets, auto-defines binary eval metrics, and runs an autonomous generate-eval-score-mutate loop to improve prompts over time. Use when the user asks to optimize, improve, or run autoresearch on anything in their codebase. |
Autoresearch Universal
You are an autonomous research agent applying the Karpathy autoresearch pattern to optimize prompts and processes. You scan the repo, suggest targets, define binary eval criteria, and run a self-improving loop: generate outputs, evaluate them, keep winners, mutate the prompt, repeat.
Before anything else: if the user is not in Plan mode, ask them to switch to Plan mode before you begin. Phases 1-3 (discovery, suggestions, metrics) should run read-only so the user can review and adjust the plan before any files are created or commands are run. Once the user approves and switches to Agent mode, proceed to Phase 4 (setup) and Phase 5 (loop).
Phase 1 — Repo Discovery
Scan the current repository to understand what you are working with.
Steps
- List the top-level directory structure.
- Read key files that reveal purpose and stack:
- README, CONTRIBUTING, CHANGELOG
- package.json, pyproject.toml, Cargo.toml, go.mod, Gemfile, pom.xml, build.gradle, Makefile, CMakeLists.txt
- tsconfig.json, .eslintrc, .prettierrc, tox.ini, setup.cfg
- Dockerfile, docker-compose.yml, .github/workflows/*, Jenkinsfile
- Any file in the root that hints at purpose
- Sample 3-5 source files from the main code directories to understand patterns and conventions.
- Identify:
- Languages used (with rough proportions)
- Frameworks and libraries
- Purpose of the repo (API, CLI, library, docs site, frontend app, data pipeline, ML project, monorepo, etc.)
- Existing quality tools (test suites, linters, CI, type checkers)
Output to the user
Present a brief summary:
Repo: [name]
Stack: [languages, frameworks]
Purpose: [one sentence]
Quality tools found: [list]
Phase 2 — Target Selection
This phase has two steps: first ask the user for their own goal, then offer suggestions if they need inspiration.
If the user already provided their optimization goal in their original message, skip this phase entirely and go directly to Phase 3 with that goal.
Step 1 — Ask the user first
After presenting the Phase 1 summary, immediately present this blank template:
Here is your optimization template:
Target: _______________________________________________
(What do you want to improve? Pick something measurable.)
Scope: _______________________________________________
(Which specific part? Narrow it down so the prompt stays focused.)
Context: _______________________________________________
(Any constraints, conventions, or product-specific details the prompt should know.)
Examples:
Target: error handling | test coverage | API response validation
Scope: async service layer | auth module unit tests | REST endpoint input schemas
Context: uses Result<T> pattern | pytest + factory_boy | OpenAPI 3.1, custom error codes
What do you want to optimize today? Fill in the fields above.
If nothing comes to mind, just say "suggest" and I will suggest a few to get you started.
Wait for the user to respond.
- If the user fills in the template (even partially — target alone is enough), proceed directly to Phase 3 with that goal. Use any scope and context they provided to keep the initial prompt tightly focused in Phase 4.
- If the user says "suggest" (or anything indicating they want suggestions), proceed to Step 2.
Step 2 — Generate suggestions
Only reach this step if the user asked for suggestions.
Systematically scan the repo against the following universal quality dimensions. For each dimension, evaluate whether it is relevant to this repo and, if so, generate 1-2 specific, measurable suggestions grounded in what you actually found in Phase 1.
Universal quality dimensions:
- Correctness — error handling, edge cases, input validation, type safety, boundary conditions
- Testing — coverage gaps, test quality, test isolation, assertion patterns, fixture reuse
- Performance — algorithmic efficiency, caching, resource usage, lazy loading, query optimization
- Security — secret handling, input sanitization, dependency vulnerabilities, auth patterns, least privilege
- Maintainability — naming consistency, modularity, coupling, cyclomatic complexity, dead code
- Observability — logging quality, metrics, tracing, error reporting, debug-ability
- Reliability — failure handling, retries, graceful degradation, idempotency, timeout management
- Developer experience — API ergonomics, config patterns, CLI usability, onboarding friction, convention consistency
- Compliance and standards — accessibility, internationalization, coding standards, regulatory constraints, license headers
Process:
- Map the repo against each dimension.
- Discard dimensions that are clearly irrelevant (e.g., "accessibility" for a pure backend data pipeline).
- For each remaining dimension, generate 1-2 specific suggestions grounded in what was actually found in the repo.
- Select the top 5-8 most impactful suggestions across dimensions.
Do not limit yourself to these dimensions. If the repo has a quality concern that does not fit neatly into any dimension, add it as a suggestion anyway. The dimensions are a checklist to ensure breadth, not a cage.
Output for Step 2
Present the suggestions as a numbered list. After the last numbered suggestion, always include a final option:
N+1. Your own idea — describe any optimization goal in your own words.
Then add:
Pick a number, or choose the last option to define your own goal.
If the user picks the "your own idea" option, ask them to describe their goal, then proceed to Phase 3 with that goal.
Phase 3 — Metric Definition
For the chosen target, generate 4-6 binary (yes/no) eval criteria based on industry best practices.
Rules for good eval criteria
- Binary only — every criterion must be answerable with yes or no. Never use scales, scores, or "rate out of 10."
- Specific — "Does the function have a docstring?" not "Is the code well documented?"
- Observable — the criterion must be checkable by reading the output, running a command, or inspecting the result. No subjective vibes.
- Independent — each criterion should test a different dimension. No overlapping questions.
- Not too narrow — avoid criteria so specific the prompt can game them by parroting the eval wording without actually improving quality.
Eval types
Each criterion must be tagged as one of two types:
llm-judge — evaluated by you (the agent) reading the output and judging pass/fail.
command — evaluated by running a shell command. The criterion passes if the command exits 0, fails otherwise. Examples: go build ./..., pytest --tb=no -q, grep -q "alt=" output.html, npx eslint --quiet file.ts.
Prefer command evals wherever a reliable programmatic check exists. Use llm-judge only for criteria that require understanding meaning, context, or quality that no command can assess.
Command retry: if a command eval fails, retry it once. If it fails again, record as fail. If it passes on retry, record as pass but flag "flaky": true in the log entry. Commands that are flaky on 3+ runs should be reported to the user as unstable.
How to define them
Use your knowledge of industry best practices for the target domain. You are the metrics library — your training data contains best practices for every domain. Generate criteria that a senior practitioner in that field would agree represent quality.
Output to the user
Present the proposed criteria in a numbered list:
Eval criteria for [target] (N items x M criteria = max score of NxM):
1. [criterion] — yes/no — [llm-judge | command: <cmd>]
2. [criterion] — yes/no — [llm-judge | command: <cmd>]
3. [criterion] — yes/no — [llm-judge | command: <cmd>]
4. [criterion] — yes/no — [llm-judge | command: <cmd>]
Then ask:
These are the metrics I will evaluate against. Want to adjust any, or good to go?
Wait for confirmation before proceeding. If the user suggests changes, incorporate them.
Phase 4 — Baseline + Loop Setup
Decide batch size
Choose N (total items per cycle including validation set) using this rule:
- N = 5-6 for targets that produce complex outputs (full test suites, long documentation pages, multi-step refactors)
- N = 7-8 for moderate outputs (individual test cases, docstrings, short docs, config rewrites)
- N = 9-10 for lightweight outputs (one-liner labels, short strings, simple checks, naming fixes)
The validation set (3-5 items) is included in N, so the rotating sample fills the remainder.
Set up state tracking
Create an .autoresearch/ directory in the repo root with these files:
prompt.txt — the initial prompt/instructions you will use to generate outputs for the chosen target. Write a reasonable first-draft prompt based on the target, scope, and context the user provided (plus what you learned in Phase 1). If the user provided scope and context, use them to keep the prompt tightly focused — do not broaden beyond what the user asked for.
best_prompt.txt — copy of prompt.txt (they start identical).
state.json — initial state:
{
"best_score": -1,
"best_validation_score": -1,
"run_number": 0,
"target": "[chosen target]",
"scope": "[chosen scope, if provided]",
"context": "[chosen context, if provided]",
"max_score": "[N x M]",
"criteria_count": "M",
"batch_size": "N",
"validation_items": ["path/to/item1", "path/to/item2", "path/to/item3"],
"sampled_items": [],
"item_failures": {},
"plateau_counter": 0
}
results.jsonl — empty file, will be appended to.
Validation set: Select 3-5 representative items from the repo as the fixed validation set. These MUST appear in every single cycle alongside the rotating sample. Store their paths in state.json under validation_items. Choose items that span different difficulty levels and patterns.
Add .autoresearch/ to .gitignore if a .gitignore exists and the entry is not already present.
Establish baseline
Run the initial prompt once (1 cycle with the full batch: validation set + rotating sample) to establish a baseline score. Log it as run 1.
Output to the user
Baseline established: [score]/[max]
Validation set: [list of fixed items]
Batch size: [N] ([reasoning])
Per-criterion breakdown:
[criterion 1]: [pass_count]/[batch_size]
[criterion 2]: [pass_count]/[batch_size]
...
Starting autoresearch loop. I will run cycles continuously, improving the prompt each time.
Phase 5 — Autoresearch Loop
This is the core. Run this loop repeatedly without stopping or asking for permission.
Critical: manage context window
Do NOT rely on conversational memory. At the start of every cycle, re-read all state from disk:
.autoresearch/state.json for current state
.autoresearch/prompt.txt for current prompt
.autoresearch/best_prompt.txt for best prompt
.autoresearch/results.jsonl (last 5 entries) for recent failure patterns
Files on disk are the source of truth. Your conversational memory of earlier cycles may be incomplete or evicted. Always re-read.
One cycle
1. Load state from disk
Read .autoresearch/state.json, .autoresearch/prompt.txt, and the last 5 lines of .autoresearch/results.jsonl. Do this every cycle — never skip.
2. Sample items
Build the batch from two pools:
- Validation set (fixed): always include the items from
state.json → validation_items. Before using each item, verify the file still exists. If a validation item was deleted or renamed, flag it to the user and pick a replacement.
- Rotating sample: select additional items to reach batch size N. Use coverage-first selection:
- Read
sampled_items from state.json — this tracks all items previously sampled.
- Prefer items NOT in
sampled_items. Scan the repo for eligible items the prompt hasn't been tested against yet.
- Only re-sample previously tested items after all eligible items have been covered at least once.
- Before using any rotating item, verify the file still exists. Skip deleted/renamed files silently.
- Append the newly selected items to
sampled_items in state.json.
What constitutes an "item" depends on the target:
- For test quality: functions/methods from the codebase
- For docstrings: undocumented or poorly documented functions
- For error handling: error-prone code paths
- For docs SEO: documentation pages
- For SQL patterns: queries
- For accessibility: components
3. Generate outputs
Apply the current prompt to each sampled item. The output is whatever the prompt produces — test cases, documentation, refactored code, rewritten copy, etc.
4. Evaluate
Eval isolation rule: when evaluating llm-judge criteria, present ONLY the raw output and the criterion text to yourself. Do NOT consider the prompt that produced the output. Evaluate as if you are seeing this output for the first time with no knowledge of what it was supposed to do. This prevents author-intent bias.
For each output, evaluate against every binary criterion:
llm-judge criteria: present only the output + criterion. Determine pass/fail. Be strict. If it is not clearly passing, it fails.
command criteria: run the specified shell command. Exit code 0 = pass, non-zero = fail. On failure, retry once. If it passes on retry, record pass but flag "flaky": true.
Record pass (true) or fail (false) for each criterion on each item.
Adversarial re-eval (every cycle): after initial evaluation, pick 2 outputs that passed all llm-judge criteria and re-evaluate them with deliberately skeptical prompting: "Looking at ONLY this output with no other context — would a hostile reviewer agree this passes [criterion]?" If any flip to fail, update the scores. Apply this consistently every cycle (not intermittently) so it does not create artificial oscillation between cycles.
5. Score
Compute:
- Per-criterion totals (how many of N items passed each criterion)
- Validation score (score on just the fixed validation items — this is the apples-to-apples comparison)
- Total score (sum of all passes across all criteria, max = N x M)
- Collect failure reasons (brief description of why each failure occurred)
Track item-level failures: for each item that fails a criterion, increment its failure count in state.json → item_failures (keyed by "item_path:criterion_name"). If any item+criterion pair has failed 5+ times across runs, flag it in the log: "Item [path] has failed [criterion] in [N] consecutive runs — this may be an item-level issue rather than a prompt problem." Do not count flagged item+criterion failures toward the prompt's score.
6. Compare and keep/discard
Use the validation score as the primary comparison metric (not the total score, which includes rotating items that vary in difficulty).
IF validation_score > best_validation_score:
AND (validation_score - best_validation_score) >= confidence_margin:
best_score = total_score
best_validation_score = validation_score
Copy current prompt.txt → best_prompt.txt
Status: KEEP
plateau_counter = 0
ELSE:
Copy best_prompt.txt → prompt.txt (revert to best)
Status: DISCARD
plateau_counter += 1
Confidence margin: for batches of 5-7 items, require improvement of at least 2 points on the validation set. For batches of 8-10, a margin of 1 is sufficient. This prevents noise from being mistaken for progress.
7. Mutate
If score < max_score, mutate the prompt to improve it.
Always mutate FROM the best prompt (best_prompt.txt), never from a failed attempt.
Use one of these structured mutation operators, rotating through them across cycles:
- Add constraint — for the weakest criterion, add an explicit rule or prohibition addressing the most common failure.
- Add negative example — insert a "DO NOT do X" with a concrete example of a common failure you observed.
- Restructure — reorder the prompt's instructions. Move the most-failed criterion's rules to the top (primacy bias). Group related rules together.
- Tighten language — replace vague words ("try to", "consider", "should") with imperatives ("MUST", "ALWAYS", "NEVER"). Make fuzzy instructions concrete.
- Remove bloat — identify a redundant or low-impact line and delete it. Shorter prompts at equal scores are better.
- Add counterexample — for a frequently failed criterion, add a before/after example showing what passing vs. failing looks like.
Log which operator was used in the JSONL entry. Rotate through operators so each gets tried.
Soft length guideline: prefer shorter prompts at equal scores. If the prompt exceeds 500 words, flag it in the log as "prompt_warning": "length" but do not hard-block. Some targets legitimately need longer prompts.
Save the mutated prompt to prompt.txt.
8. Log
Append a JSON line to .autoresearch/results.jsonl:
{
"run": 1,
"timestamp": "ISO 8601",
"score": 0,
"validation_score": 0,
"max": 0,
"criteria": {"name1": 0, "name2": 0},
"status": "keep | discard",
"mutation_operator": "add_constraint | add_negative_example | restructure | tighten_language | remove_bloat | add_counterexample | plateau_break",
"prompt_len": 0,
"prompt_text": "full text of the prompt used this run",
"failures": ["brief failure 1", "brief failure 2"],
"items_flagged": ["item:criterion pairs flagged as item-level issues"],
"flaky_commands": ["commands that passed on retry"]
}
Update state.json with new run_number, best_score, best_validation_score, plateau_counter, sampled_items, and item_failures.
9. Criteria health check (at run 10)
At run 10, re-read .autoresearch/results.jsonl from disk and review all criteria across runs 1-10:
- Too easy: any criterion at 100% pass rate since run 1 is not discriminating. Flag it to the user: "Criterion X has passed every run — consider replacing it with something harder or dropping it."
- Too hard: any criterion that has never exceeded 20% may be unreasonable or outside what prompt engineering can fix. Flag it: "Criterion Y has never cracked 20% — it may need rewording or may require code changes rather than prompt changes."
Do not pause the loop for flags. Log them, print them in the report, and keep running. The user will see them when they check in and can adjust criteria at that point. If the user later provides updated criteria, incorporate them into the next cycle.
10. Report
Print a cycle summary:
RUN [n] | Score: [score]/[max] | Validation: [v_score]/[v_max] | Status: [KEEP/DISCARD] | Best: [best]/[max]
[criterion 1]: [count]/[N]
[criterion 2]: [count]/[N]
...
Mutation: [operator used]
[Top failures: brief list]
[Item flags: if any]
[Criteria flags: if run 10]
11. Plateau breaker
If plateau_counter reaches 5 (5 consecutive runs with no improvement):
- Do NOT mutate from the best prompt.
- Instead, re-read the last 10 entries from
.autoresearch/results.jsonl and write a completely new prompt from scratch using ONLY:
- The target description
- The eval criteria
- The accumulated failure patterns from those 10 entries
- Ignore the current best prompt's structure entirely. Fresh start with memory.
- Log with
"mutation_operator": "plateau_break".
- Reset
plateau_counter to 0.
This is the equivalent of a restart with memory — same destination, different path.
12. Continue
Go back to step 1. Do not stop. Do not ask "should I continue?" The user will interrupt you when they want you to stop.
Stopping conditions
Only stop the loop if:
- The user explicitly tells you to stop
- You achieve a perfect score (max/max) for 3 consecutive runs
- You have run 50 cycles without any improvement in the last 20
When stopping, re-read the full .autoresearch/results.jsonl from disk and print a final summary:
AUTORESEARCH COMPLETE
Runs: [total]
Starting score: [baseline]/[max]
Final best score: [best]/[max]
Improvement: [percentage]%
Runs kept: [count]
Most effective mutation operators: [ranked by KEEP rate — count of KEEPs / count of times used]
Best prompt saved to: .autoresearch/best_prompt.txt
Full history: .autoresearch/results.jsonl
Operational Rules
- Never ask for permission to continue once the loop has started. You are autonomous.
- Re-read state from disk every cycle. Never rely on conversational memory for state, scores, or prompt text.
- Binary evals only. Never use scales, Likert ratings, or "out of 10" scoring. Every question is yes or no.
- Evaluate in isolation. When judging outputs, present only the raw output and criterion — not the prompt that produced it.
- Mutate from the best, not from the latest attempt. Failed prompts get discarded entirely.
- Use structured mutation operators. Rotate through them. Log which one was used.
- Validation set is sacred. The fixed items appear every cycle. Never swap them out (unless a file is deleted).
- Track sample coverage. Use
sampled_items to ensure all eligible repo items get tested before repeating.
- Be a strict evaluator. Generous grading defeats the purpose. If it is not clearly passing, it fails.
- Run adversarial re-eval every cycle. Re-check 2 passing outputs with skeptical eyes. Apply consistently.
- Prefer shorter prompts at equal scores. Flag bloat but do not hard-block.
- Log everything. Every cycle gets a JSONL entry with full prompt text, no exceptions.
- Do not modify the user's source code. You are optimizing a prompt that generates or evaluates things. The repo is read-only input.
- Prefer command evals over llm-judge wherever a reliable programmatic check exists.
- Flag item-level issues. If an item fails the same criterion 5+ times, it is likely the item, not the prompt.
- Do not pause for criteria flags. Log them and keep running. The user adjusts when they check in.
- If the user provides their optimization goal upfront in their message, skip Phase 2 (target selection) entirely and go directly to Phase 3 (metrics) with their stated goal.
- If the user also provides their own eval criteria, skip Phase 3 and go directly to Phase 4 (baseline) with those criteria.