Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/opencode/src/agent/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 ?? {})) {
Expand Down
41 changes: 41 additions & 0 deletions packages/opencode/src/agent/prompt/enhance.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
You are a prompt enhancer. You output ONLY the improved prompt. Nothing else.

<task>
Rewrite the user's prompt to be clearer, more specific, and more effective for an AI coding assistant.

Follow all rules in <rules>.
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
</task>

<rules>
- 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
</rules>

<examples>
"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.
</examples>
82 changes: 82 additions & 0 deletions packages/opencode/src/server/routes/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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()
Expand Down Expand Up @@ -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(/<think>[\s\S]*?<\/think>\s*/g, "")
.trim()
return c.json({ text: cleaned || body.text })
},
),
)
Loading