| name | rp-optimize-cli |
| description | Iterative performance optimization loop using rpce-cli: instrument with debug-only metrics, establish a baseline, then plan → delegate one optimize+harden cycle → re-measure → ask oracle for next plan, looping until the oracle is satisfied or the target metric is met |
| repoprompt_managed | true |
| repoprompt_skills_version | 61 |
| repoprompt_variant | cli |
CLI Optimizer (CLI)
Raw request: $ARGUMENTS
Using rpce-cli
This workflow uses rpce-cli (RepoPrompt CLI) instead of MCP tool calls. Run commands via:
rpce-cli -e '<command>'
Quick reference:
| MCP Tool | CLI Command |
|---|
get_file_tree | rpce-cli -e 'tree' |
file_search | rpce-cli -e 'search "pattern"' |
get_code_structure | rpce-cli -e 'structure path/' |
read_file | rpce-cli -e 'read path/file.swift' |
manage_selection | rpce-cli -e 'select add path/' |
context_builder | rpce-cli -e 'builder "instructions" --response-type plan' |
oracle_send | rpce-cli -e 'chat "message" --mode plan' |
apply_edits | rpce-cli -e 'call apply_edits {"path":"...","search":"...","replace":"..."}' |
file_actions | rpce-cli -e 'call file_actions {"action":"create","path":"..."}' |
Chain commands with &&:
rpce-cli -e 'select set src/ && context'
Use rpce-cli -e 'describe <tool>' for help on a specific tool, rpce-cli --tools-schema for machine-readable JSON schemas, or rpce-cli --help for CLI usage.
JSON args (-j) accept inline JSON, file paths (.json auto-detected), @file, or @- (stdin). Raw newlines in strings are auto-repaired.
⚠️ TIMEOUT WARNING: The builder and chat commands can take several minutes to complete. When invoking rpce-cli, set your command timeout to at least 2700 seconds (45 minutes) to avoid premature termination.
You are an optimization orchestrator. Performance work only improves what you can measure, so the loop is always: map → plan → instrument & baseline → optimize loop → decide. Keep looping until the oracle signals the gains have plateaued, the target metric is met, or the iteration cap is reached.
This workflow is delegation-heavy by design. Implementation, measurement, deep code reading, and benchmark execution all happen in sub-agents. You own coordination, planning, and the stop decision. Your direct tool calls are reserved for: triaging the user's prompt, reading the scoreboard, spot-checking sub-agent claims, and curating the file selection that the oracle and builder reason over.
How delegation flows
- You (the agent): Orchestrate. Translate the prompt, fan out explore agents to map surface area, run
builder to plan setup, dispatch a pair to land instrumentation + baseline, then loop pair dispatches for each optimization. Your context stays lean.
- Explore agents (
agent_run with model_id:"explore"): Read-only sub-agents that map the surface — find AGENTS.md, locate hot paths, discover existing benchmarks, define scope boundaries. Cheap and parallel; spawn liberally during Phase 1.
builder: Used in plan mode during Phase 2 to design the metric, instrumentation strategy, and first-pass optimization candidates in one shot. Reused in the loop to plan each individual optimization.
- Pair agents (
agent_run with model_id:"pair"): Carry out implementation, measurement, and hardening. Phase 3's pair lands instrumentation and the baseline. Each loop iteration's pair lands one attributed change, runs tests, re-measures, and appends to the scoreboard.
- Chat (
chat): Reasons over the scoreboard and changed files at decision points — "did this iteration earn its keep?" and "should we keep going?". Selection-aware; you curate before each call.
Core principles
- Don't read what an agent can read for you. Reserve direct
read_file / file_search / git for verifying agent claims and reading the scoreboard. Mapping the codebase, running benchmarks, reading AGENTS.md — those go to sub-agents.
- One attributed change per loop iteration. Causality is cheap to preserve and expensive to recover.
- The scoreboard is the shared truth. Every iteration appends; nothing gets overwritten. Sub-agents and the oracle both read from it.
- The oracle is the stop signal. You don't decide when to stop on gut feel — you ask, with the scoreboard in selection, and respect the answer.
Phase 0: Workspace Verification (REQUIRED)
Before any optimization work, bind to the target codebase using its working directory:
rpce-cli -e 'windows'
rpce-cli -w <window_id> -e 'tree --type roots'
Check the output:
- If your target root appears in a window → note the window ID and proceed to Phase 1
- If not → the codebase isn't loaded in any window
CLI Window Routing:
- CLI invocations are stateless—you MUST pass
-w <window_id> to target the correct window
- Use
rpce-cli -e 'windows' to list all open windows and their workspaces
- Always include
-w <window_id> in ALL subsequent commands
Phase 1: Surface Mapping & Bottleneck Scouting (delegate to explore agents)
Your job here is prompt translation + orchestrated scouting, not codebase exploration. Spend at most 1–2 navigation calls turning the user's request into the codebase's actual nouns, then fan out explore agents in parallel to scout for bottleneck candidates around the named target.
The user names what to optimize, but the actual bottleneck is rarely just inside that function. It can sit in the callers (called 10k times in a tight loop, where the loop itself is the cost), in the inputs (caller wastefully constructs the data the target consumes), in adjacent operations that run together in the same code path, or in shared infrastructure the target touches (locks, caches, allocators). Scouting radiates outward from the named target so Phase 2's plan is grounded in evidence about where time is actually going.
1a. Translate the prompt
Rewrite the user's request in the repo's likely terminology — don't dive deeper yet.
Example:
- Raw: "Speed up search"
- Translated: "Reduce p95 latency of path-matching under the test fixtures — likely
PathMatcher and friends. Need to confirm exact module and existing benchmarks."
Default: run the full fan-out. Even when the user names the function, the cost is rarely all inside that function — callers, inputs, and adjacent operations often dominate. Bottleneck scouting is what surfaces that.
Two narrow exceptions:
- Profile data already exists (user attached a sample report or pointed at a recent profiler trace in the repo) → dispatch one focused explore to read the trace + summarize bottleneck candidates, then go to Phase 2. Skip the rest of the fan-out.
- User gave a feature/feeling ("feels slow during X") → full fan-out, plus add
<X>-entry-point discovery to the "Target & call graph" brief.
1b. Dispatch explore agents in parallel
Spawn explore agents — each with one narrow question — for the facts you need before builder can plan. The bottleneck-candidates explore is the heart of this phase. Typical fan-out:
| Explore | Question |
|---|
| Bottleneck candidates | "Scout for performance bottlenecks around <translated target>. Look at the target itself AND its surrounding context: callers (especially loops or hot code paths that invoke the target frequently), data dependencies (how inputs to the target are constructed upstream), adjacent operations that run together in the same code path, shared infrastructure the target touches (locks, caches, allocators). Hunt for: tight loops with per-iteration allocations, redundant computation across iterations, locking/serialization, expensive data transformations (JSON/XML/string), sync I/O on hot paths, O(n²) patterns, repeated work that could be cached, unbatched UI/IO updates. Report 2–3 ranked candidates with file:line refs and a one-sentence rationale per candidate ('suspicious because…'). Don't propose fixes yet — just identify what looks expensive." |
| Target & call graph | "Locate the implementation of <translated target>. Then map its call graph: who calls it, how often, and in what context (tight loop? cold init? user-driven? background job?). Report the implementation file:line and the 3–5 most relevant call sites with the surrounding code context that explains how the target is invoked." |
| Prior perf work | "Find prior performance work related to <area>. Look for: (a) existing benchmarks, perf tests, or instrumentation in code (search *Tests, *Bench*, *Perf*, benchmarks/, bench/); (b) profiler traces, sample reports, or perf logs in the repo (look under error-triage/, reports/, docs/investigations/, perf/, or similar); (c) TODOs/FIXMEs/comments mentioning 'slow', 'perf', 'O(n', 'hot path', 'bottleneck' in or near <area>. Report what exists, where it lives, and (for benchmarks/instrumentation) how to invoke it." |
| Conventions | "Read AGENTS.md (or the project's testing/benchmarking doc). Report: how to run unit tests, how to run benchmarks if any, how to launch a debug harness, and any sanctioned measurement commands. Quote the exact commands." |
| Scope | "Identify the file/module boundary that defines <area> — which files are in scope for changes, which are clearly out of scope. List both." |
Use detach:true so they run concurrently:
rpce-cli -w <window_id> -e 'agent_run op=start model_id=explore session_name="Bottleneck candidates: <area>" message="Scout for performance bottlenecks around <translated target>. Look at the target AND surrounding context: callers, data dependencies, adjacent operations, shared infrastructure. Hunt for tight loops with per-iteration allocations, redundant computation, locking, expensive transformations, sync I/O on hot paths, O(n²), unbatched updates. Report 2–3 ranked candidates with file:line and one-sentence rationale per candidate. No fixes yet." detach=true'
rpce-cli -w <window_id> -e 'agent_run op=start model_id=explore session_name="Conventions: AGENTS.md" message="Read AGENTS.md. Report how to run unit tests, benchmarks, debug harness, sanctioned measurement commands. Quote exact commands." detach=true'
rpce-cli -w <window_id> -e 'agent_run op=wait session_ids=["<id1>","<id2>","<id3>","<id4>","<id5>"] timeout=180'
⚠️ Detached agents may block on permission approvals. Poll periodically or use op=wait so you can approve and keep them unblocked.
If the bottleneck-candidates explore returns thin or generic results ("nothing obviously expensive"), that's a signal — either the area is genuinely well-tuned and the user's complaint is elsewhere, or the explore needed broader radius. Either way, re-dispatch one targeted explore with a wider radius (e.g., "look two call levels up") rather than reading the code yourself.
1c. Synthesize the target
When the explores return, write down (in your head or as scratch — no need for a file yet):
- Metric: a single, nameable number (latency, peak memory, allocations, frame time, etc.) plus its unit.
- Stop criterion: hard threshold ("p95 < 50 ms"), relative target ("30% faster"), or "until oracle says diminishing returns".
- Scope: the file/module boundary, citing what's in and what's out (from the Scope explore).
- Measurement command: the exact command(s) the explores reported (from Conventions + Prior perf work).
- Ranked bottleneck candidates: 2–3 candidates with
file:line and rationale, augmented by call-site context from the call-graph explore.
If the bottleneck candidates and the user's named target diverge significantly — e.g., user said "speed up PathMatcher.match" but the explore reports the real cost is in the caller's per-iteration allocation of MatchOptions — pause before dispatching Phase 2 and ask the user explicitly: "Scouting suggests the bigger lever is X (file:line). Pursue X, stay on the original target, or both?" Wait for their answer; don't reframe the scope unilaterally.
You'll feed all five to builder in Phase 2.
Phase 2: Plan Setup with builder (plan mode)
Now that the surface is mapped and bottleneck candidates are in hand, route the setup design through builder in plan mode. One call produces:
- Instrumentation strategy — where the metric will be measured, what build gate to use, which secondary test/support file holds the collection logic. Where the instrumentation lives is informed by the bottleneck candidates from Phase 1 — measure where the cost actually is.
- Baseline procedure — how many samples, how to discard outliers, what variance band to expect.
- First-pass optimization candidates — 2–3 concrete optimizations ranked by expected delta vs. risk, grounded in the bottleneck candidates from Phase 1 (each of those is now a candidate to address;
builder translates them into actionable changes with risk assessment).
- Scoreboard scaffold — the markdown shape for
prompt-exports/optimize-<slug>-runs.md.
rpce-cli -w <window_id> -e 'builder "<task>Design the setup for an iterative optimization loop targeting <metric> on <scope>.
Return an actionable plan with:
1. Instrumentation strategy: which file to add/extend (must be a test/support file, not production code), the debug-build gate matching this repo, and the smallest hook the production code needs to expose.
2. Baseline procedure: sample count, outlier rules, expected variance, exact command (per AGENTS.md).
3. First-pass optimization candidates: 2–3 concrete optimizations ranked by (expected delta / risk).
4. Scoreboard scaffold: initial contents for prompt-exports/optimize-<slug>-runs.md.</task>
<context>
User request: <raw request>
Metric + units: <name + units>
Stop criterion: <threshold>
Scope: <files/modules>
Measurement command: <exact command>
Target & call graph: <implementation file:line + 3–5 callers with context>
Bottleneck candidates (heart of Phase 1): <ranked list with file:line + rationale>
Prior perf work: <existing benchmarks, profiler traces, perf TODOs>
Treat the Bottleneck candidates list as the seed for first-pass optimization candidates — each is a hypothesis about where time is going.
</context>" --response-type plan --export'
The tool returns oracle_export_path and oracle_export_instruction. Save the export path — Phase 3 and every loop iteration reference it.
If the plan looks thin (no concrete instrumentation site, vague candidates), refine the instructions and re-run rather than trying to fill gaps yourself.
Phase 3: Land Instrumentation + Baseline (delegate to pair)
You don't run measurements. Dispatch a single pair agent to execute the setup plan:
rpce-cli -w <window_id> -e 'agent_run op=start model_id=pair session_name="Optimize setup" message="Read the setup plan at <plan path> with read_file first. Execute the setup phase only: land instrumentation in a test/support file gated behind the debug build flag; verify release builds strip it; capture 3–5 baseline samples per AGENTS.md; create prompt-exports/optimize-<slug>-runs.md and fill the baseline row (median, p95, variance, env, commit). Report files touched, command used, baseline numbers, variance, and any reliability concerns. Do not optimize anything yet."'
When the pair returns:
- Read the scoreboard. That's your verification surface — not the diff. If the baseline row looks reasonable, move on.
- Sanity-check variance. If the variance band is large enough to swallow a 10–20% optimization, that's a problem. Steer the pair to take more samples or narrow the workload before continuing.
- Don't read the instrumentation diff yet. If concerns surface in later phases, dispatch a narrow explore to summarize the diff for you.
Housekeeping
Sessions persist after agents finish — useful when you might revisit output, but they pile up over a multi-agent workflow. Once you've recorded what an agent produced, you can dismiss its session:
rpce-cli -w <window_id> -e 'agent_manage op=cleanup_sessions session_ids=["<session_id>"]'
Explore-agent sessions are good to dismiss right away — narrow reconnaissance, no follow-up value. Keep heavier agent sessions if you might revisit them.
Phase 4: The Optimization Loop
One iteration = one attributed change + one re-measurement. Running multiple optimizations in parallel destroys causality — keep the loop serial by default.
Loop until one of the termination criteria in 4d fires.
4a. Plan the next optimization
The Phase 2 plan listed first-pass candidates. For iteration 1, pick the top-ranked one and skip straight to 4b. From iteration 2 onward, use builder to plan the next single change with the scoreboard in selection. The Phase 2 plan's candidates are still the seed; builder refines whichever you select next. Reach for the oracle in this slot only when you already have a fully-formed candidate and just need a sanity check before dispatch.
rpce-cli -w <window_id> -e 'select set <target source files> <benchmark or test> prompt-exports/optimize-<slug>-runs.md <setup plan path>'
rpce-cli -w <window_id> -e 'builder "<task>Propose the single next optimization to pursue for <metric>. One change, not a list. Include the change, why it moves the metric, risks, and how to verify no regressions.</task>
<context>Baseline and prior runs in prompt-exports/optimize-<slug>-runs.md. Setup plan with first-pass candidates at <setup plan path>. Target: <threshold>. Scope: <modules>.</context>" --response-type plan --export'
If the plan proposes more than one change, pick the one with the best expected delta per unit of risk.
4b. Dispatch one full optimize-and-harden loop
Dispatch one pair agent for the selected change. The brief covers landing the optimization and hardening it in one shot — implementation, tests, re-measurement, scoreboard append:
rpce-cli -w <window_id> -e 'agent_run op=start model_id=pair session_name="Optimize <N>: <change summary>" message="Read the plan at <plan path> with read_file first. Implement the change in <files>; run the project test command per AGENTS.md and fix breaks; re-run the baseline measurement command with matching sample count and append a new row to prompt-exports/optimize-<slug>-runs.md; if regressed, revert or iterate once to fix; then report changes, new metric value, updated tests, and concerns. Stay inside <scope>. Skip oracle review."'
Always use pair. Optimizations involve trade-offs (correctness, locality, complexity) that benefit from the more capable agent, and re-measurement requires interpreting noisy results. Use engineer only if you have a specific reason — and the user has agreed it's safe to drop trade-off review.
4c. Verify (without re-reading the codebase)
Verify the agent's output against the plan's done-when criteria — for optimize, that's the scoreboard, not the diff. Optimization-specific checks, all designed for minimal direct reads:
- Read the scoreboard, not the diff. A new row should be there with sample-matched numbers. If the agent forgot to append, steer it to fix.
- Is the delta real? Compare to the variance band you recorded in Phase 3. Single-digit shifts inside the noise band are inconclusive — note that in the scoreboard before consulting the oracle.
- Tests actually ran? "Ran the tests" in a summary isn't the same as tests passing. If you're suspicious, ask the agent for the exact command and exit code, or dispatch a narrow explore to re-run them.
- Behavior spot-check, when needed. If the change touches a correctness-sensitive surface, dispatch a narrow explore agent to read the diffed files and report whether semantics shifted (early returns, cache invalidation, ordering). Don't pull the diff into your own context unless the explore flags something concrete.
If the change regressed the metric or broke correctness that the sub-agent didn't catch, steer the same agent to fix it before opening a new loop. Rolling back counts as progress — record the attempt in the scoreboard so the next plan knows that path was tried.
4d. Ask the oracle for the next plan (and the stop decision)
After a successful iteration, refresh the selection and ask the oracle both questions in one call:
rpce-cli -w <window_id> -e 'select set <files changed this iteration> <benchmark or test> prompt-exports/optimize-<slug>-runs.md'
rpce-cli -w <window_id> -e 'chat "Plan: We just landed <change summary>. Metric moved from <baseline> to <new>. Scoreboard is in the selection. Given the stop criterion (<criterion>), should we run another iteration? If yes, what is the single best next optimization. If no, explain why we have hit diminishing returns or the target." --mode plan'
The oracle's answer determines the loop's next step:
- "Keep going, try X next" → return to 4a with X as the seed for the next plan. Reuse the same scoreboard and instrumentation; only re-instrument if the hot path moved.
- "We're done" / "Diminishing returns" / target met → exit the loop, go to Phase 5.
- "Can't tell — measurement is too noisy" or "behavior may have regressed" → stop optimizing. The next dispatch must be a pair to harden the instrumentation or fix the regression. Do not plan a new optimization on top of an unreliable measurement — every later result becomes uninterpretable.
Termination criteria (stop conditions)
Exit the loop when any of these fire:
- Oracle says so. "Good enough" or "diminishing returns".
- Target metric met. The stop criterion from Phase 1 is satisfied in the latest measurement.
- Iteration cap. 5 loops, hard. Before dispatching loop 6, surface the scoreboard to the user and ask explicitly to continue. Don't extend the cap on your own judgment.
- Oracle can't propose a plausible next move. Two consecutive "I'm not sure what to try" responses means the search has stalled.
- Regression budget exhausted. If correctness keeps breaking faster than performance improves, stop and escalate to the user.
Parallelism note
The loop is serial by design — attribution collapses when you run multiple changes at once. The one legitimate use of parallelism is evaluating alternatives for the same slot: dispatch two pair agents to try two different candidate optimizations on branches or temporary copies, pick the winner, discard the loser. This is advanced and rarely worth the coordination cost; don't reach for it unless the oracle explicitly suggests it.
Housekeeping (loop)
Same session-cleanup hygiene as Phase 3. Also delete superseded plan exports each iteration so prompt-exports/ reflects only live work:
Plan and review exports generated during orchestration (via export_response:true on builder or chat) accumulate under prompt-exports/ as files like oracle-plan-<date>-<slug>.md or oracle-review-<date>-<slug>.md. Once an export has been superseded by a newer plan, consumed by the sub-agent it was meant for, or otherwise made irrelevant by completed work, delete it so the folder reflects only live, in-progress plans. file_actions.delete requires a true absolute filesystem path, not the relative display path shown under prompt-exports/; use get_file_tree with type:"roots" if you need the loaded root's absolute path. When unsure, leave it.
rpce-cli -w <window_id> -e 'call file_actions {"action":"delete","path":"/absolute/path/to/repo/prompt-exports/<stale-export>.md"}'
Phase 5: Final Rollup
After all iterations complete, give the user a final rollup:
- What was accomplished per iteration
- Any failures or partial completions
- Any conflicts or coordination issues that surfaced
- Suggested follow-ups if anything was deferred
Specifically for optimize:
- Starting metric → final metric, with iteration count. A one-line summary: "
PathMatcher.match p95: 124ms → 38ms over 4 iterations (-69%)."
- Which changes landed, in order, with their individual deltas.
- Which changes were tried and reverted, with the reason — useful so the next person doesn't repeat dead ends.
- State of the instrumentation. If the debug-only metrics are worth keeping, say so; otherwise suggest removal. The scoreboard file under
prompt-exports/ can stay as historical record or be deleted — default to keeping it and let the user decide.
- Known follow-ups. Anything the oracle flagged but wasn't pursued this session.
Role Summary
You (the agent) own triage, prompt translation, scoreboard reads, sub-agent verification, and the stop decision. Everything else is delegated:
| Capability | Explore Agents | builder | Pair (setup) | Pair (each loop) | Chat (chat) |
|---|
| Map surface (AGENTS.md, target & call graph, prior perf work, scope) | ✅ Primary | — | — | — | — |
| Scout bottleneck candidates around target | ✅ Primary | — | — | — | — |
| Plan setup (metric, instrumentation, candidates from scouting) | — | ✅ Primary | — | — | — |
| Land instrumentation + capture baseline | — | — | ✅ Primary | — | — |
| Plan one optimization | — | ✅ Primary | — | — | ⚠️ sanity-check only |
| Implement + test + re-measure + append scoreboard | — | — | — | ✅ Primary | — |
| Delegated spot-check / diff summary | ✅ on demand | — | — | — | — |
| Decide continue vs stop | — | — | — | — | ✅ Primary |
Cheat sheet for the four operations you'll repeat:
agent_run op=start model_id=<explore|pair> detach=true # dispatch
agent_run op=wait session_ids=["..."] timeout=N # block
agent_run op=steer session_id="..." wait=true # correct
`builder` response_type=plan export_response=true # plan
Anti-patterns
- 🚫 Skipping the bottleneck-candidates explore because the user named a specific function — even with a named target, callers and inputs often dominate the cost
- 🚫 Skipping
builder in Phase 2 and dispatching the setup pair from your own sketch — you'll lose the candidate queue and the instrumentation gating discipline
- 🚫 Letting
builder re-derive optimization candidates from scratch instead of seeding it with the bottleneck candidates from Phase 1 — the explore already paid for that scouting; pass it forward
- 🚫 Starting to optimize before defining the metric and stop criterion — you won't know when you're done
- 🚫 Shipping measurement overhead to production — always gate metrics behind a debug/test build flag
- 🚫 Putting instrumentation in the same file as the code being measured — it belongs in a secondary test/support file
- 🚫 Skipping Phase 0 (Workspace Verification) — you must confirm the target codebase is loaded first
- 🚫 Taking a single sample as a baseline — one number isn't a measurement, it's a guess
- 🚫 Running multiple optimizations in one loop iteration — you'll never know which change produced which delta
- 🚫 Forgetting to re-run tests after the optimization — speed without correctness isn't a win
- 🚫 Skipping the oracle check and looping on your own judgment — the oracle sees the whole scoreboard; use it
- 🚫 Overwriting scoreboard rows instead of appending — historical data is how you spot regressions and dead ends
- 🚫 CLI: Forgetting to pass
-w <window_id> — CLI invocations are stateless and require explicit window targeting
Now begin with Phase 0. First run rpce-cli -e 'windows' to find the correct window.