Files
kilocode/packages/kilo-vscode/docs/mercury-next-edit-testing.html
T
2026-08-26 16:49:04 +02:00

1108 lines
36 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mercury Next Edit — Testing Playground (Kilo Code)</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root {
--bg: #0d1117;
--panel: #161b22;
--border: #30363d;
--fg: #e6edf3;
--muted: #8b949e;
--accent: #7ee787;
--accent2: #79c0ff;
--warn: #f0883e;
--danger: #ff7b72;
--code: #c9d1d9;
--code-bg: #0d1117;
}
@media (prefers-color-scheme: light) {
:root {
--bg: #ffffff;
--panel: #f6f8fa;
--border: #d0d7de;
--fg: #1f2328;
--muted: #57606a;
--accent: #1a7f37;
--accent2: #0969da;
--warn: #bc4c00;
--danger: #cf222e;
--code: #1f2328;
--code-bg: #f6f8fa;
}
}
* {
box-sizing: border-box;
}
html {
font-size: 16px;
}
body {
background: var(--bg);
color: var(--fg);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
line-height: 1.55;
margin: 0;
padding: 0;
}
.wrap {
max-width: 920px;
margin: 0 auto;
padding: 2.5rem 1.5rem 4rem;
}
h1 {
font-size: 2rem;
margin-top: 0;
letter-spacing: -0.01em;
}
h2 {
font-size: 1.4rem;
margin-top: 2.5rem;
border-bottom: 1px solid var(--border);
padding-bottom: 0.4rem;
}
h3 {
font-size: 1.1rem;
margin-top: 1.75rem;
}
p,
li {
color: var(--fg);
}
.muted {
color: var(--muted);
}
.pill {
display: inline-block;
padding: 2px 8px;
border-radius: 999px;
font-size: 0.78rem;
background: var(--panel);
border: 1px solid var(--border);
color: var(--muted);
margin-right: 6px;
vertical-align: middle;
}
.pill.same-line {
color: var(--accent);
border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.pill.off-cursor {
color: var(--accent2);
border-color: color-mix(in srgb, var(--accent2) 50%, var(--border));
}
.pill.suppressed {
color: var(--warn);
border-color: color-mix(in srgb, var(--warn) 50%, var(--border));
}
code,
pre {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}
code {
background: var(--panel);
padding: 1px 5px;
border-radius: 4px;
font-size: 0.92em;
}
pre {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.9rem 1rem;
overflow-x: auto;
margin: 0.5rem 0;
}
pre code {
background: none;
padding: 0;
color: var(--code);
font-size: 0.88rem;
line-height: 1.5;
}
.card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
padding: 1.1rem 1.25rem;
margin: 1rem 0;
}
.card h3 {
margin-top: 0;
}
.card .label {
font-weight: 600;
color: var(--muted);
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.06em;
margin-top: 0.6rem;
}
.step-list {
padding-left: 1.4rem;
margin: 0.4rem 0 0;
}
table {
border-collapse: collapse;
width: 100%;
margin: 0.75rem 0;
}
th,
td {
text-align: left;
padding: 0.55rem 0.7rem;
border-bottom: 1px solid var(--border);
font-size: 0.92rem;
}
th {
color: var(--muted);
font-weight: 600;
}
.toc {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0.9rem 1.1rem;
margin: 1rem 0 2rem;
}
.toc h2 {
font-size: 1rem;
margin: 0 0 0.4rem;
border: 0;
padding: 0;
color: var(--muted);
}
.toc ul {
margin: 0;
padding-left: 1.2rem;
}
.toc li {
margin: 0.15rem 0;
font-size: 0.95rem;
}
a {
color: var(--accent2);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
blockquote {
margin: 1rem 0;
padding: 0.6rem 1rem;
border-left: 3px solid var(--accent2);
background: var(--panel);
border-radius: 0 6px 6px 0;
color: var(--fg);
}
blockquote.warn {
border-left-color: var(--warn);
}
.footer {
color: var(--muted);
font-size: 0.85rem;
margin-top: 3rem;
border-top: 1px solid var(--border);
padding-top: 1rem;
}
kbd {
font-family: ui-monospace, monospace;
background: var(--panel);
border: 1px solid var(--border);
border-bottom-width: 2px;
border-radius: 4px;
padding: 1px 6px;
font-size: 0.85em;
}
</style>
</head>
<body>
<div class="wrap">
<h1>Mercury Next Edit — Testing Playground</h1>
<p class="muted">
A walk-through guide for Kilo Code reviewers to validate the new
<strong>Next Edit Suggestion</strong> integration powered by Mercury Edit 2 from Inception Labs.
</p>
<div class="toc">
<h2>On this page</h2>
<ul>
<li><a href="#what">What is Mercury Next Edit?</a></li>
<li><a href="#install">Install the PR locally</a> — 3 minutes from clone to running Dev Host</li>
<li><a href="#setup">Enabling the feature</a></li>
<li><a href="#flow">How the integration works</a></li>
<li><a href="#tests">Test cases</a> — 20 scenarios across Python, TS, Go, Rust, JS, SQL, MD</li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
<li><a href="#feedback">Feedback we're looking for</a></li>
</ul>
</div>
<h2 id="what">What is Mercury Next Edit?</h2>
<p>
<strong>Mercury Edit 2</strong> is a code-edit model from <a href="https://inceptionlabs.ai">Inception Labs</a>.
Unlike FIM completion, it predicts the user's <em>next multi-line edit</em> given the current file, cursor
position, and recent edit history. It typically responds in under 250 ms.
</p>
<p>
This PR adds Mercury Next Edit as a <strong>new, opt-in</strong> autocomplete option in Kilo Code. It lives
alongside everything that's already shipping &mdash; Codestral FIM and Mercury Edit 2 via the Kilo gateway are
bit-for-bit unchanged. Selecting <strong>Mercury Next Edit (Inception)</strong> from the model dropdown switches
to a separate render pipeline:
</p>
<ul>
<li>
<strong>Same-line predictions</strong> render as inline ghost text (just like FIM &mdash;
<kbd>Tab</kbd> accepts).
</li>
<li>
<strong>Off-cursor predictions</strong> render as a decoration (red strikethrough + green ghost annotation) at
the predicted edit location. First <kbd>Tab</kbd> teleports the cursor there; second <kbd>Tab</kbd> applies.
</li>
<li>
After any accept, the integration immediately re-triggers Mercury so the user can walk a refactor with
repeated <kbd>Tab</kbd> presses ("Tab-Tab-Tab").
</li>
</ul>
<h2 id="install">Install the PR locally</h2>
<p>
You'll need to pull this PR's branch and run the extension in a development VSCode window ("Extension
Development Host"). The whole loop is about 3 minutes once you have the prerequisites.
</p>
<h3>Prerequisites</h3>
<ul>
<li><strong>VSCode</strong> ≥ 1.105.1 (matches kilocode's <code>engines.vscode</code>)</li>
<li>
<strong>Bun</strong> ≥ 1.3.14 (the build script checks the version) &mdash; install via
<code>brew install bun</code> or <a href="https://bun.sh">bun.sh</a>
</li>
<li><strong>GitHub CLI</strong> (<code>gh</code>) &mdash; optional but makes the PR checkout one command</li>
<li>
<strong>An Inception API key</strong> &mdash; create one at
<a href="https://platform.inceptionlabs.ai">platform.inceptionlabs.ai</a> if you don't already have one
</li>
</ul>
<h3>1. Check out the PR branch</h3>
<p>From an empty directory:</p>
<pre><code>gh repo clone Kilo-Org/kilocode
cd kilocode
gh pr checkout 10536</code></pre>
<p>Or without <code>gh</code>:</p>
<pre><code>git clone https://github.com/Kilo-Org/kilocode.git
cd kilocode
git fetch origin pull/10536/head:mercury-next-edit-integration
git checkout mercury-next-edit-integration</code></pre>
<h3>2. Install dependencies</h3>
<pre><code>bun install</code></pre>
<p>(First install pulls the full monorepo &mdash; takes 3060 seconds.)</p>
<h3>3. Start the dev build</h3>
<pre><code>cd packages/kilo-vscode
bun run watch</code></pre>
<p>
Leave that terminal running. It rebuilds the extension on every save and runs the TypeScript compiler in watch
mode.
</p>
<h3>4. Open kilocode in VSCode and launch the Extension Development Host</h3>
<p>From a separate terminal (or your IDE launcher):</p>
<pre><code>code /path/to/kilocode</code></pre>
<p>
Inside that VSCode window, press <kbd>F5</kbd> (or <strong>Run → Start Debugging</strong>). A second VSCode
window opens, titled <strong>[Extension Development Host]</strong>. That window has this PR's build of the
kilocode extension loaded.
</p>
<blockquote class="warn">
<p>
Pre-push <code>turbo typecheck</code> may fail on packages that need Java (JetBrains plugin). That's
environment, not code &mdash; not relevant to the NES feature. Use <code>--no-verify</code> on any local
pushes if you hit it.
</p>
</blockquote>
<h3>5. Open the test playground</h3>
<p>
In the Dev Host window: <strong>File → Open Folder…</strong> → choose
<code>packages/kilo-vscode/docs/nes-examples/</code> inside this same repo. That gives you the 20 self-contained
test files described below.
</p>
<h3>6. Configure NES</h3>
<p>Settings (<kbd>Cmd</kbd>+<kbd>,</kbd>) in the Dev Host, search <code>kilo-code.new.autocomplete</code>:</p>
<ul>
<li>
<code>model</code><strong>Mercury Next Edit (Inception)</strong> &mdash; <em>not</em> "Mercury Edit 2",
which is the classic FIM-via-gateway option
</li>
<li>
<code>nextEdit.apiKey</code> → paste your <code>sk_…</code> Inception API key (or set
<code>INCEPTION_API_KEY</code> env before launching)
</li>
<li><code>enableAutoTrigger</code> → ✓ (already the default)</li>
</ul>
<h3>7. Watch the pipeline live</h3>
<p>
In the Dev Host: <strong>View → Output</strong> → in the dropdown, select
<strong>"Kilo Code · Next Edit"</strong>. Every request, response, and render decision is logged here with
timestamps. Keep this panel visible while testing &mdash; it's the single best diagnostic.
</p>
<p>You're set. Skip to the <a href="#tests">test cases</a> below.</p>
<h2 id="setup">Enabling the feature (settings reference)</h2>
<p>In VSCode Settings (<kbd>Cmd</kbd>+<kbd>,</kbd>), search <code>kilo-code.new.autocomplete</code>:</p>
<table>
<tr>
<th>Setting</th>
<th>Value</th>
</tr>
<tr>
<td><code>model</code></td>
<td>
<strong>Mercury Next Edit (Inception)</strong> &mdash; <em>not</em> "Mercury Edit 2", which is the original
FIM-via-gateway option
</td>
</tr>
<tr>
<td><code>nextEdit.apiKey</code></td>
<td>your Inception API key (<code>sk_...</code>); also accepts <code>INCEPTION_API_KEY</code> env var</td>
</tr>
<tr>
<td><code>enableAutoTrigger</code></td>
<td>✓ (default)</td>
</tr>
<tr>
<td><code>nextEdit.baseUrl</code></td>
<td>(optional) override the API base, defaults to <code>https://api.inceptionlabs.ai/v1</code></td>
</tr>
<tr>
<td><code>nextEdit.debug</code></td>
<td>(optional) mirror diagnostic logs to DevTools console</td>
</tr>
</table>
<p>
To watch the pipeline live: <strong>View → Output</strong> in the Dev Host, choose the
<strong>"Kilo Code · Next Edit"</strong> channel.
</p>
<h2 id="flow">How the integration works</h2>
<p>
The <code>AutocompleteServiceManager</code> instantiates both providers up front. Provider
<em>registration</em> with <code>vscode.languages.registerInlineCompletionItemProvider</code> is driven by the
configured model:
</p>
<ul>
<li><code>inception/mercury-next-edit</code><strong>NES provider</strong> (this PR's new pipeline)</li>
<li>anything else → <strong>classic FIM provider</strong> (unchanged)</li>
</ul>
<p>The NES provider, per keystroke:</p>
<ol>
<li>Debounces 250 ms (skipped for explicit invocations).</li>
<li>
Builds a Mercury prompt: current file + cursor + an editable region <code>[cursor 5, cursor + 10]</code> +
35 recently-viewed-snippet ranges (from the shared <code>RecentlyVisitedRangesService</code>) + the last 5
debounced unidiffs (from a new per-file <code>EditHistoryTracker</code>).
</li>
<li>
Sends a single <code>role: "user"</code> message to <code>POST /v1/edit/completions</code> with
<code>max_tokens: 512</code>.
</li>
<li>
Parses the triple-backtick fenced reply, strips Mercury's sentinel tokens, computes the minimal line-diff
against the current document.
</li>
<li>
Branches: same-line diff → <code>InlineCompletionItem</code>; off-cursor diff →
<code>NextEditSuggestionManager</code> with a decoration + Tab/Esc keybinding gated on a context flag
(<code>kilo-code.nextEdit.hasPendingSuggestion</code>).
</li>
</ol>
<h2 id="tests">Test cases</h2>
<p>
Each test below is a self-contained file at <code>packages/kilo-vscode/docs/nes-examples/</code> in this repo.
Open that folder in the Extension Development Host (step 5 above), then work through the cases. Place your
cursor where indicated, wait ~300 ms idle, and observe.
</p>
<p>
Tip: keep this page open in a separate window from the Dev Host &mdash; the descriptions below would otherwise
leak into Mercury's prompt context and bias the test.
</p>
<p>Render-path legend:</p>
<p>
<span class="pill same-line">same-line ghost</span> appears as inline ghost text at the cursor (<kbd>Tab</kbd>
accepts) &middot; <span class="pill off-cursor">off-cursor decoration</span> renders away from the cursor; first
<kbd>Tab</kbd> jumps, second <kbd>Tab</kbd> applies &middot;
<span class="pill suppressed">suppressed</span> negative case — nothing should render
</p>
<h3>Python — core tests</h3>
<div class="card">
<h3>01 — Finish a recursive function body <span class="pill same-line">same-line</span></h3>
<pre><code>def factorial(n):
if n &lt;= 1:
return 1
</code></pre>
<div class="label">Cursor</div>
<p>The empty indented line at the end of <code>factorial</code> (column 4).</p>
<div class="label">Expected</div>
<p>
Ghost text proposing the recursive case (e.g. <code>return n * factorial(n - 1)</code>).
<kbd>Tab</kbd> accepts.
</p>
</div>
<div class="card">
<h3>02 — Pattern continuation <span class="pill same-line">same-line</span></h3>
<pre><code>COLOR_RED = "#ff0000"
COLOR_GREEN = "#00ff00"
COLOR_BLUE =</code></pre>
<div class="label">Cursor</div>
<p>End of line 3 (right after <code>=</code>).</p>
<div class="label">Expected</div>
<p>Ghost text appending a hex color like <code> "#0000ff"</code>.</p>
</div>
<div class="card">
<h3>03 — Mid-identifier completion <span class="pill same-line">same-line</span></h3>
<pre><code>def calculate_total(items):
total = 0
for item in items:
total += item.price
return tot</code></pre>
<div class="label">Cursor</div>
<p>End of file (after <code>return tot</code>).</p>
<div class="label">Expected</div>
<p>Ghost text completing the identifier (likely <code>al</code> &rarr; <code>total</code>).</p>
</div>
<div class="card">
<h3>04 — Loop body inference <span class="pill same-line">same-line</span></h3>
<pre><code>def calculate_total(items):
total = 0
for item in items:
return total</code></pre>
<div class="label">Cursor</div>
<p>The empty indented line inside the <code>for</code> loop (column 8).</p>
<div class="label">Expected</div>
<p>Ghost text proposing the accumulator update.</p>
</div>
<div class="card">
<h3>05 — Sibling method body <span class="pill same-line">same-line</span></h3>
<pre><code>class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
def peek(self):
return self.items[-1] if self.items else None</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside <code>pop</code> (column 8).</p>
<div class="label">Expected</div>
<p>Ghost text proposing a body consistent with the symmetric <code>push</code>.</p>
</div>
<h3>Python — advanced</h3>
<div class="card">
<h3>07 — Multi-line rename refactor <span class="pill off-cursor">off-cursor</span></h3>
<pre><code>def compute_user_score(u, w):
base = u * 10
bonus = w * 5
penalty = u - w
return base + bonus - penalty
def compute_user_score(user_id, weight):
base = u * 10
bonus = w * 5
penalty = u - w
return base + bonus - penalty</code></pre>
<div class="label">Cursor</div>
<p>End of the renamed signature line (<code>def compute_user_score(user_id, weight):</code>).</p>
<div class="label">Expected</div>
<p>
Strikethrough on the body lines below + ghost showing the renamed body. First <kbd>Tab</kbd> jumps, second
applies.
</p>
</div>
<div class="card">
<h3>08 — Mixed insert + replace <span class="pill off-cursor">off-cursor</span></h3>
<pre><code>def sum_prices(items):
total = 0
for item in items:
return total</code></pre>
<div class="label">Cursor</div>
<p>End of <code>total = 0</code>.</p>
<div class="label">Expected</div>
<p>Decoration on the broken for-loop area showing the corrected body (insertion + replacement combined).</p>
</div>
<div class="card">
<h3>10 — Mid-token completion <span class="pill same-line">same-line</span></h3>
<pre><code>def fibonacci(n):
if n &lt;= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
result = fib</code></pre>
<div class="label">Cursor</div>
<p>End of the file (after <code>result = fib</code>).</p>
<div class="label">Expected</div>
<p>Ghost text extending the identifier and supplying a call, e.g. <code>onacci(10)</code>.</p>
</div>
<div class="card">
<h3>11 — Stub method with implemented siblings <span class="pill same-line">same-line</span></h3>
<pre><code>class Queue:
def __init__(self):
self.items = []
def enqueue(self, item):
self.items.append(item)
def peek(self):
return self.items[0] if self.items else None
def size(self):
return len(self.items)
def is_empty(self):
return not self.items
def dequeue(self):
def clear(self):
self.items.clear()</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside <code>dequeue</code> (column 8).</p>
<div class="label">Expected</div>
<p>Ghost text proposing a FIFO pop, e.g. <code>return self.items.pop(0)</code>.</p>
</div>
<div class="card">
<h3>
12 — Type annotation insertion <span class="pill same-line">same-line</span> /
<span class="pill off-cursor">off-cursor</span>
</h3>
<pre><code>def multiply(a: int, b: int) -&gt; int:
return a * b
def subtract(a: int, b: int) -&gt; int:
return a - b
def add(a, b):
return a + b</code></pre>
<div class="label">Cursor</div>
<p>End of <code>def add(a, b):</code> (the only un-annotated function).</p>
<div class="label">Expected</div>
<p>
Strikethrough on the signature line + ghost showing the typed version (<code
>def add(a: int, b: int) -&gt; int:</code
>). May render same-line or off-cursor depending on where on the line you clicked.
</p>
</div>
<div class="card">
<h3>13 — Docstring generation <span class="pill same-line">same-line</span></h3>
<pre><code>import datetime
def parse_iso_datetime(s):
"""Parse an ISO 8601 datetime string into a datetime.datetime."""
return datetime.datetime.fromisoformat(s)
def parse_iso_date(s):
return datetime.date.fromisoformat(s)</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line under <code>def parse_iso_date(s):</code> (column 4).</p>
<div class="label">Expected</div>
<p>Ghost text inserting a one-line docstring matching the sibling's style.</p>
</div>
<div class="card">
<h3>14 — No-op suppression <span class="pill suppressed">suppressed</span></h3>
<pre><code>def add(a: int, b: int) -&gt; int:
"""Return the sum of two integers."""
return a + b
def multiply(a: int, b: int) -&gt; int:
"""Return the product of two integers."""
return a * b
def subtract(a: int, b: int) -&gt; int:
"""Return a minus b."""
return a - b</code></pre>
<div class="label">Cursor</div>
<p>End of <code>return a + b</code>.</p>
<div class="label">Expected</div>
<p>
<strong>Nothing</strong>. The code is already correct &mdash; either Mercury returns an identical reply or our
suppression branch drops the proposal. Channel should show "no-op" or skip lines, never a render.
</p>
<div class="label">Failure mode</div>
<p>Any visible suggestion that just replays the existing code is a false positive worth reporting.</p>
</div>
<h3>TypeScript</h3>
<div class="card">
<h3>ts_07 — Array transform completion <span class="pill same-line">same-line</span></h3>
<pre><code>interface User {
id: number;
name: string;
active: boolean;
}
function getActiveUserNames(users: User[]): string[] {
return users
}
const sample: User[] = [
{ id: 1, name: "ada", active: true },
{ id: 2, name: "lin", active: false },
{ id: 3, name: "rin", active: true },
];
console.log(getActiveUserNames(sample));</code></pre>
<div class="label">Cursor</div>
<p>End of <code>return users</code> inside <code>getActiveUserNames</code>.</p>
<div class="label">Expected</div>
<p>Ghost text completing the chain, e.g. <code>.filter(u =&gt; u.active).map(u =&gt; u.name)</code>.</p>
</div>
<div class="card">
<h3>ts_08 — Param type annotations <span class="pill off-cursor">off-cursor</span></h3>
<pre><code>function double(x: number): number {
return x * 2;
}
function add(a, b) {
return a + b;
}
function negate(x: number): number {
return -x;
}
function main(): void {
console.log(double(3));
console.log(add(2, 4));
console.log(negate(7));
}
main();</code></pre>
<div class="label">Cursor</div>
<p>End of file (after <code>main();</code>).</p>
<div class="label">Expected</div>
<p>Decoration on the <code>add(a, b)</code> signature proposing the typed version.</p>
</div>
<div class="card">
<h3>ts_09 — React event handler <span class="pill same-line">same-line</span></h3>
<pre><code>declare const React: {
useState: &lt;T&gt;(initial: T) =&gt; [T, (next: T) =&gt; void];
};
function Counter(): JSX.Element {
const [count, setCount] = React.useState&lt;number&gt;(0);
function handleClick() {
}
return (
&lt;div&gt;
&lt;p&gt;Count: {count}&lt;/p&gt;
&lt;button onClick={handleClick}&gt;Increment&lt;/button&gt;
&lt;/div&gt;
);
}
export default Counter;</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside <code>handleClick</code> (column 4).</p>
<div class="label">Expected</div>
<p>Ghost text incrementing <code>count</code> via <code>setCount</code>.</p>
</div>
<h3>Go</h3>
<div class="card">
<h3>go_07 — Error handling block <span class="pill same-line">same-line</span></h3>
<pre><code>package main
import (
"fmt"
"os"
)
func loadConfig(path string) ([]byte, error) {
data, err := os.ReadFile(path)
return data, nil
}
func main() {
cfg, err := loadConfig("config.json")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(string(cfg))
}</code></pre>
<div class="label">Cursor</div>
<p>Empty line right after <code>data, err := os.ReadFile(path)</code>.</p>
<div class="label">Expected</div>
<p>Ghost text proposing the canonical <code>if err != nil { return nil, err }</code>.</p>
</div>
<div class="card">
<h3>go_08 — Struct method body <span class="pill same-line">same-line</span></h3>
<pre><code>package main
import "fmt"
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
func (r Rectangle) Area() float64 {
}
func main() {
r := Rectangle{Width: 3, Height: 4}
fmt.Println("perimeter:", r.Perimeter())
fmt.Println("area:", r.Area())
}</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside <code>Area()</code>.</p>
<div class="label">Expected</div>
<p>Ghost text computing area from <code>Width</code> and <code>Height</code>.</p>
</div>
<div class="card">
<h3>go_09 — Goroutine + channel <span class="pill same-line">same-line</span></h3>
<pre><code>package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
}()
for v := range ch {
fmt.Println("got:", v)
}
}</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside the goroutine.</p>
<div class="label">Expected</div>
<p>Ghost text producing values onto the channel and closing it.</p>
</div>
<h3>Rust</h3>
<div class="card">
<h3>rs_07 — Match-arm completion <span class="pill same-line">same-line</span></h3>
<pre><code>enum Shape {
Circle(f64),
Square(f64),
Rectangle(f64, f64),
Triangle(f64, f64),
}
fn area(s: &amp;Shape) -&gt; f64 {
match s {
Shape::Circle(r) =&gt; std::f64::consts::PI * r * r,
Shape::Square(side) =&gt; side * side,
}
}
fn main() {
let shapes = vec![
Shape::Circle(1.0),
Shape::Rectangle(2.0, 3.0),
Shape::Triangle(4.0, 5.0),
];
for s in &amp;shapes {
println!("area = {}", area(s));
}
}</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside the <code>match</code> body, after the <code>Square</code> arm.</p>
<div class="label">Expected</div>
<p>Ghost text adding the missing <code>Rectangle</code> and <code>Triangle</code> arms.</p>
</div>
<div class="card">
<h3>rs_08 — Result/Option chaining <span class="pill same-line">same-line</span></h3>
<pre><code>fn parse_int(s: &amp;str) -&gt; Option&lt;i32&gt; {
let n = s.trim()
Some(n * 2)
}
fn main() {
let inputs = [" 21 ", "not-a-number", "10"];
for s in &amp;inputs {
match parse_int(s) {
Some(v) =&gt; println!("{} -&gt; {}", s, v),
None =&gt; println!("{} -&gt; skipped", s),
}
}
}</code></pre>
<div class="label">Cursor</div>
<p>End of <code>let n = s.trim()</code> (no semicolon yet).</p>
<div class="label">Expected</div>
<p>Ghost text continuing the chain into a parsed <code>i32</code>.</p>
</div>
<div class="card">
<h3>rs_09 — Lifetime annotations <span class="pill off-cursor">off-cursor</span></h3>
<pre><code>fn longest(a: &amp;str, b: &amp;str) -&gt; &amp;str {
if a.len() &gt;= b.len() {
a
} else {
b
}
}
fn main() {
let s1 = String::from("hello world");
let s2 = String::from("hi");
let out = longest(&amp;s1, &amp;s2);
println!("longest = {}", out);
}</code></pre>
<div class="label">Cursor</div>
<p>End of file.</p>
<div class="label">Expected</div>
<p>Decoration on the <code>fn longest</code> signature proposing lifetime annotations.</p>
</div>
<h3>JavaScript</h3>
<div class="card">
<h3>js_07 — Async/await fetch <span class="pill same-line">same-line</span></h3>
<pre><code>async function fetchUser(id) {
try {
} catch (err) {
console.error("fetchUser failed", err);
return null;
}
}
async function main() {
const user = await fetchUser(42);
console.log("user:", user);
}
main();</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside the <code>try {</code> block (column 8).</p>
<div class="label">Expected</div>
<p>Ghost text completing the fetch + json parse.</p>
</div>
<div class="card">
<h3>js_08 — Express GET handler <span class="pill same-line">same-line</span></h3>
<pre><code>const app = {
get: (_path, _handler) =&gt; app,
post: (_path, _handler) =&gt; app,
listen: (_port, cb) =&gt; cb &amp;&amp; cb(),
};
const users = [
{ id: 1, name: "ada" },
{ id: 2, name: "lin" },
];
app.get("/users/:id", (req, res) =&gt; {
});
app.post("/users", (req, res) =&gt; {
const user = { id: users.length + 1, name: req.body.name };
users.push(user);
res.status(201).json(user);
});
app.listen(3000, () =&gt; console.log("listening on :3000"));</code></pre>
<div class="label">Cursor</div>
<p>Empty indented line inside the GET handler (column 4).</p>
<div class="label">Expected</div>
<p>Ghost text proposing a get-by-id (lookup, 404, json response).</p>
</div>
<h3>SQL</h3>
<div class="card">
<h3>sql_07 — Missing JOIN <span class="pill same-line">same-line</span></h3>
<pre><code>SELECT
c.name,
SUM(o.total) AS total_spent
FROM orders o
WHERE o.created_at &gt;= '2026-01-01'
GROUP BY c.name
ORDER BY total_spent DESC
LIMIT 10;</code></pre>
<div class="label">Cursor</div>
<p>End of the line <code>FROM orders o</code>.</p>
<div class="label">Expected</div>
<p>Ghost text completing the JOIN against <code>customers</code>.</p>
</div>
<div class="card">
<h3>sql_08 — WHERE filter <span class="pill same-line">same-line</span></h3>
<pre><code>SELECT id, email
FROM users
WHERE
ORDER BY last_login_at DESC;</code></pre>
<div class="label">Cursor</div>
<p>End of the bare <code>WHERE</code> line.</p>
<div class="label">Expected</div>
<p>Ghost text proposing a predicate.</p>
</div>
<h3>Markdown (negative case)</h3>
<div class="card">
<h3>md_07 — Prose should stay quiet <span class="pill suppressed">suppressed</span></h3>
<pre><code># Mercury Edit 2 — Quick Notes
Mercury Edit 2 is a small, fast model trained to predict the user's
next single edit given the current file, cursor position, and recent
edit history. It targets latency under 200 ms on typical files and
returns a unified-diff-like patch scoped to a window around the cursor.
Unlike chat-style completions, the model is biased toward minimal,
local changes — finishing a function body, fixing a typo, propagating
a rename — rather than generating new files from scratch.</code></pre>
<div class="label">Cursor</div>
<p>End of the last sentence.</p>
<div class="label">Expected</div>
<p>
<strong>Nothing</strong>. If Mercury does propose a prose continuation it counts as a soft fail &mdash; we
don't want a code model writing README content.
</p>
</div>
<h2 id="troubleshooting">Troubleshooting</h2>
<blockquote class="warn">
<p>
If nothing happens when you type, open <strong>View → Output → "Kilo Code · Next Edit"</strong> and watch the
log. The pipeline is verbose enough that 90% of issues are obvious from the first few lines.
</p>
</blockquote>
<table>
<tr>
<th>Symptom</th>
<th>Likely cause</th>
<th>Fix</th>
</tr>
<tr>
<td>No log lines at all</td>
<td>Wrong model selected, or Dev Host wasn't reloaded after rebuild</td>
<td>
<kbd>Cmd</kbd>+<kbd>R</kbd> in the Dev Host; confirm <code>model = Mercury Next Edit (Inception)</code>
</td>
</tr>
<tr>
<td><code>skip — no API key resolved</code></td>
<td>Setting not saved</td>
<td>Re-paste the key in <code>nextEdit.apiKey</code>, press Enter, reload</td>
</tr>
<tr>
<td><code>&lt;- 401 Unauthorized</code></td>
<td>Wrong key or wrong tier</td>
<td>Verify the key at platform.inceptionlabs.ai</td>
</tr>
<tr>
<td><code>&lt;- 400 Bad Request</code></td>
<td>Prompt-shape regression (we shouldn't ship this, but if it happens during dev)</td>
<td>Capture the response body from the channel and ping the integration owner</td>
</tr>
<tr>
<td>Suggestion shown for a wrong-looking model</td>
<td>
Selecting "Mercury Edit 2" routes through the classic FIM provider, not NES &mdash; that's by design (the
old behavior is preserved)
</td>
<td>Switch to "Mercury Next Edit (Inception)" to use the new pipeline</td>
</tr>
<tr>
<td>Inline ghost text never appears, but logs show <code>RENDER</code></td>
<td>Another extension (Copilot, Tabnine) is winning the inline-completion race</td>
<td>Temporarily disable conflicting extensions in the Dev Host</td>
</tr>
</table>
<h2 id="feedback">Feedback we'd love</h2>
<ul>
<li>
<strong>Where the prediction was wrong but the UX was correct.</strong> Note the file + cursor position + what
Mercury proposed. Helps us tune the model.
</li>
<li>
<strong>Where the UX got in the way.</strong> Tab semantics, decoration appearance, chained-prediction timing,
anything that felt clumsy compared to other NES products you've used.
</li>
<li>
<strong>Performance regressions in classic FIM autocomplete.</strong> The PR is supposed to leave the classic
path untouched &mdash; if Codestral or Mercury Edit 2 (FIM) feel different in this build, that's a regression
we want to know about.
</li>
<li>
<strong>Things you tried that aren't in this doc.</strong> The 20 tests are a starting point, not a contract.
Real codebases will be different.
</li>
</ul>
<div class="footer">
<p>
Mercury Next Edit integration for Kilo Code &mdash; prepared by the Inception Labs team.<br />
Questions / bug reports: <a href="mailto:firas@inceptionlabs.ai">firas@inceptionlabs.ai</a>.
</p>
</div>
</div>
</body>
</html>