diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts
index 53c655d1b373..2c640e5e06af 100644
--- a/packages/opencode/src/agent/agent.ts
+++ b/packages/opencode/src/agent/agent.ts
@@ -13,6 +13,7 @@ import PROMPT_COMPACTION from "./prompt/compaction.txt"
import PROMPT_EXPLORE from "./prompt/explore.txt"
import PROMPT_SUMMARY from "./prompt/summary.txt"
import PROMPT_TITLE from "./prompt/title.txt"
+import PROMPT_ENHANCE from "./prompt/enhance.txt"
import { Permission } from "@/permission"
import { mergeDeep, pipe, sortBy, values } from "remeda"
import { Global } from "@/global"
@@ -230,6 +231,22 @@ export namespace Agent {
),
prompt: PROMPT_SUMMARY,
},
+ enhance: {
+ name: "enhance",
+ mode: "primary",
+ options: {},
+ native: true,
+ hidden: true,
+ temperature: 0.7,
+ permission: Permission.merge(
+ defaults,
+ Permission.fromConfig({
+ "*": "deny",
+ }),
+ user,
+ ),
+ prompt: PROMPT_ENHANCE,
+ },
}
for (const [key, value] of Object.entries(cfg.agent ?? {})) {
diff --git a/packages/opencode/src/agent/prompt/enhance.txt b/packages/opencode/src/agent/prompt/enhance.txt
new file mode 100644
index 000000000000..72a7c3fae259
--- /dev/null
+++ b/packages/opencode/src/agent/prompt/enhance.txt
@@ -0,0 +1,41 @@
+You are a prompt enhancer. You output ONLY the improved prompt. Nothing else.
+
+
+Rewrite the user's prompt to be clearer, more specific, and more effective for an AI coding assistant.
+
+Follow all rules in .
+Your output must be:
+- The enhanced prompt text only
+- No explanations, preamble, or meta-commentary
+- No surrounding quotes or markdown fencing
+- No bullet points or numbered lists unless the original uses them
+
+
+
+- Preserve the user's original intent exactly — do not add features or change scope
+- Add specificity: replace vague words with concrete technical terms where obvious
+- Add structure: break ambiguous requests into clear sub-steps if needed
+- Add context clues: if the user references files, frameworks, or patterns, make those references explicit
+- Keep the same language and tone as the original
+- If the prompt is already clear and specific, make only minimal improvements
+- Do NOT pad the prompt with generic instructions like "be thorough" or "handle edge cases"
+- Do NOT add requirements the user didn't mention
+- Do NOT rewrite short, direct prompts into verbose ones — brevity is valuable
+- A one-line prompt that's already clear should stay roughly one line
+- Never output anything except the enhanced prompt itself
+- Never refuse or comment on the input — always output an enhanced version
+
+
+
+"fix the bug" → Fix the bug in the current file — identify the root cause, apply the minimal correction, and verify the fix doesn't break existing behavior.
+
+"add dark mode" → Add a dark mode toggle to the settings page that persists the user's preference and applies the theme globally.
+
+"refactor this function" → Refactor this function to improve readability and reduce complexity while preserving the same behavior and return values.
+
+"make it faster" → Optimize the performance of this code — profile for bottlenecks, reduce unnecessary allocations, and avoid redundant computations.
+
+"write tests" → Write unit tests for the changed code covering the main success path, edge cases, and error handling.
+
+"why is this broken" → Investigate why this code is failing — trace the execution path, identify where the actual behavior diverges from expected, and explain the root cause.
+
diff --git a/packages/opencode/src/server/routes/experimental.ts b/packages/opencode/src/server/routes/experimental.ts
index a41b21a1fe9b..f90b36b934c3 100644
--- a/packages/opencode/src/server/routes/experimental.ts
+++ b/packages/opencode/src/server/routes/experimental.ts
@@ -2,6 +2,7 @@ import { Hono } from "hono"
import { describeRoute, validator, resolver } from "hono-openapi"
import z from "zod"
import { ProviderID, ModelID } from "../../provider/schema"
+import { SessionID, MessageID } from "../../session/schema"
import { ToolRegistry } from "../../tool/registry"
import { Worktree } from "../../worktree"
import { Instance } from "../../project/instance"
@@ -12,6 +13,9 @@ import { zodToJsonSchema } from "zod-to-json-schema"
import { errors } from "../error"
import { lazy } from "../../util/lazy"
import { WorkspaceRoutes } from "./workspace"
+import { Agent } from "../../agent/agent"
+import { Provider } from "../../provider/provider"
+import { LLM } from "../../session/llm"
export const ExperimentalRoutes = lazy(() =>
new Hono()
@@ -267,5 +271,83 @@ export const ExperimentalRoutes = lazy(() =>
async (c) => {
return c.json(await MCP.resources())
},
+ )
+ .post(
+ "/enhance",
+ describeRoute({
+ summary: "Enhance prompt",
+ description:
+ "Rewrite a user prompt to be clearer, more specific, and more effective for an AI coding assistant.",
+ operationId: "experimental.enhance",
+ responses: {
+ 200: {
+ description: "Enhanced prompt text",
+ content: {
+ "application/json": {
+ schema: resolver(
+ z.object({ text: z.string() }).meta({ ref: "EnhanceResult" }),
+ ),
+ },
+ },
+ },
+ ...errors(400),
+ },
+ }),
+ validator(
+ "json",
+ z.object({
+ text: z.string().min(1),
+ providerID: z.string().optional(),
+ modelID: z.string().optional(),
+ }),
+ ),
+ async (c) => {
+ const body = c.req.valid("json")
+ const agent = await Agent.get("enhance")
+ if (!agent) return c.json({ text: body.text })
+
+ const defaults = await Provider.defaultModel()
+ const providerID = (body.providerID ?? defaults.providerID) as ProviderID
+ const model = await (async () => {
+ if (agent.model)
+ return Provider.getModel(agent.model.providerID, agent.model.modelID)
+ const small = await Provider.getSmallModel(providerID)
+ if (small) return small
+ return Provider.getModel(providerID, (body.modelID ?? defaults.modelID) as ModelID)
+ })()
+ if (!model) return c.json({ text: body.text })
+
+ const result = await LLM.stream({
+ agent,
+ user: {
+ role: "user",
+ id: "" as MessageID,
+ sessionID: "" as SessionID,
+ time: { created: Date.now() },
+ agent: "enhance",
+ model: { providerID: model.providerID, modelID: model.id },
+ variant: "default",
+ },
+ system: [],
+ small: true,
+ tools: {},
+ model,
+ abort: new AbortController().signal,
+ sessionID: "" as SessionID,
+ retries: 2,
+ messages: [
+ {
+ role: "user",
+ content: body.text,
+ },
+ ],
+ })
+ const text = await result.text.catch(() => undefined)
+ if (!text) return c.json({ text: body.text })
+ const cleaned = text
+ .replace(/[\s\S]*?<\/think>\s*/g, "")
+ .trim()
+ return c.json({ text: cleaned || body.text })
+ },
),
)