Skip to content

Commit 6be7a9f

Browse files
committed
feat: add agents settings component and deepseek prompt; ignore .vscode/.github/.opencode
Made-with: Cursor
1 parent e29058c commit 6be7a9f

3 files changed

Lines changed: 270 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ node_modules
55
.env
66
.idea
77
.vscode
8+
.github
9+
.opencode
810
.codex
911
*~
1012
playground
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { Component, createEffect, createMemo, createSignal, For, Show, type JSX } from "solid-js"
2+
import { Select } from "@opencode-ai/ui/select"
3+
import { TextField } from "@opencode-ai/ui/text-field"
4+
import { showToast } from "@opencode-ai/ui/toast"
5+
import { useLanguage } from "@/context/language"
6+
import { useGlobalSync } from "@/context/global-sync"
7+
import { useModels } from "@/context/models"
8+
import { SettingsList } from "./settings-list"
9+
10+
const AGENT_NAMES = ["build", "plan", "general", "explore"] as const
11+
type AgentName = (typeof AGENT_NAMES)[number]
12+
13+
const AGENT_KEYS: Record<AgentName, { title: string; description: string }> = {
14+
build: {
15+
title: "settings.agents.agent.build.title",
16+
description: "settings.agents.agent.build.description",
17+
},
18+
plan: {
19+
title: "settings.agents.agent.plan.title",
20+
description: "settings.agents.agent.plan.description",
21+
},
22+
general: {
23+
title: "settings.agents.agent.general.title",
24+
description: "settings.agents.agent.general.description",
25+
},
26+
explore: {
27+
title: "settings.agents.agent.explore.title",
28+
description: "settings.agents.agent.explore.description",
29+
},
30+
}
31+
32+
interface SettingsRowProps {
33+
title: string | JSX.Element
34+
description: string | JSX.Element
35+
children: JSX.Element
36+
}
37+
38+
const SettingsRow: Component<SettingsRowProps> = (props) => (
39+
<div class="flex flex-wrap items-center gap-4 py-3 border-b border-border-weak-base last:border-none sm:flex-nowrap">
40+
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
41+
<span class="text-14-medium text-text-strong">{props.title}</span>
42+
<span class="text-12-regular text-text-weak">{props.description}</span>
43+
</div>
44+
<div class="flex w-full justify-end sm:w-auto sm:shrink-0">{props.children}</div>
45+
</div>
46+
)
47+
48+
const AgentSection: Component<{ name: AgentName }> = (props) => {
49+
const language = useLanguage()
50+
const globalSync = useGlobalSync()
51+
const models = useModels()
52+
53+
const agentConfig = createMemo(() => globalSync.data.config.agent?.[props.name])
54+
55+
const modelOptions = createMemo(() => {
56+
const none = { value: "", label: language.t("settings.agents.model.none") }
57+
return [
58+
none,
59+
...models.list().map((m) => ({
60+
value: `${m.provider.id}/${m.id}`,
61+
label: `${m.provider.name} / ${m.name}`,
62+
})),
63+
]
64+
})
65+
66+
const currentModelValue = createMemo(() => agentConfig()?.model ?? "")
67+
68+
const currentModel = createMemo(() => {
69+
const v = currentModelValue()
70+
if (!v) return undefined
71+
const slashIdx = v.indexOf("/")
72+
const providerID = v.slice(0, slashIdx)
73+
const modelID = v.slice(slashIdx + 1)
74+
return models.list().find((m) => m.provider.id === providerID && m.id === modelID)
75+
})
76+
77+
const variantOptions = createMemo(() => {
78+
const m = currentModel()
79+
if (!m?.variants || Object.keys(m.variants).length === 0) return []
80+
const none = { value: "", label: language.t("settings.agents.variant.none") }
81+
return [none, ...Object.keys(m.variants).map((v) => ({ value: v, label: v }))]
82+
})
83+
84+
const currentVariantValue = createMemo(() => {
85+
if (!currentModel()) return ""
86+
return agentConfig()?.variant ?? ""
87+
})
88+
89+
const [localPrompt, setLocalPrompt] = createSignal(agentConfig()?.prompt ?? "")
90+
91+
createEffect(() => {
92+
setLocalPrompt(agentConfig()?.prompt ?? "")
93+
})
94+
95+
const save = async (patch: { model?: string; variant?: string; prompt?: string }) => {
96+
const current = agentConfig() ?? {}
97+
const next: Record<string, unknown> = { ...current, ...patch }
98+
if (next.model === "") delete next.model
99+
if (next.variant === "") delete next.variant
100+
if (next.prompt === "") delete next.prompt
101+
await globalSync.updateConfig({ agent: { [props.name]: next } }).catch((err: unknown) => {
102+
const message = err instanceof Error ? err.message : String(err)
103+
showToast({ title: language.t("common.requestFailed"), description: message })
104+
})
105+
}
106+
107+
return (
108+
<div class="flex flex-col gap-1">
109+
<h3 class="text-14-medium text-text-strong pb-2">
110+
{language.t(AGENT_KEYS[props.name].title)}
111+
</h3>
112+
<SettingsList>
113+
<SettingsRow
114+
title={language.t("settings.agents.row.model.title")}
115+
description={language.t("settings.agents.row.model.description")}
116+
>
117+
<Select
118+
options={modelOptions()}
119+
current={modelOptions().find((o) => o.value === currentModelValue())}
120+
value={(o) => o.value}
121+
label={(o) => o.label}
122+
onSelect={(option) => {
123+
if (option === undefined) return
124+
void save({ model: option.value, variant: "" })
125+
}}
126+
variant="secondary"
127+
size="small"
128+
triggerVariant="settings"
129+
triggerStyle={{ "min-width": "220px" }}
130+
/>
131+
</SettingsRow>
132+
133+
<Show when={variantOptions().length > 0}>
134+
<SettingsRow
135+
title={language.t("settings.agents.row.variant.title")}
136+
description={language.t("settings.agents.row.variant.description")}
137+
>
138+
<Select
139+
options={variantOptions()}
140+
current={variantOptions().find((o) => o.value === currentVariantValue())}
141+
value={(o) => o.value}
142+
label={(o) => o.label}
143+
onSelect={(option) => {
144+
if (option === undefined) return
145+
void save({ variant: option.value })
146+
}}
147+
variant="secondary"
148+
size="small"
149+
triggerVariant="settings"
150+
/>
151+
</SettingsRow>
152+
</Show>
153+
154+
<div class="py-3 flex flex-col gap-2 border-b border-border-weak-base last:border-none">
155+
<div class="flex min-w-0 flex-col gap-0.5">
156+
<span class="text-14-medium text-text-strong">
157+
{language.t("settings.agents.row.prompt.title")}
158+
</span>
159+
<span class="text-12-regular text-text-weak">
160+
{language.t("settings.agents.row.prompt.description")}
161+
</span>
162+
</div>
163+
<TextField
164+
label={language.t("settings.agents.row.prompt.title")}
165+
hideLabel
166+
multiline
167+
value={localPrompt()}
168+
onChange={setLocalPrompt}
169+
onBlur={() => void save({ prompt: localPrompt() })}
170+
placeholder={language.t("settings.agents.row.prompt.placeholder")}
171+
class="text-12-regular w-full"
172+
spellcheck={false}
173+
autocorrect="off"
174+
autocomplete="off"
175+
autocapitalize="off"
176+
rows={4}
177+
/>
178+
</div>
179+
</SettingsList>
180+
</div>
181+
)
182+
}
183+
184+
export const SettingsAgents: Component = () => {
185+
const language = useLanguage()
186+
187+
return (
188+
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
189+
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
190+
<div class="flex flex-col gap-1 pt-6 pb-8">
191+
<h2 class="text-16-medium text-text-strong">{language.t("settings.agents.title")}</h2>
192+
<p class="text-12-regular text-text-weak">{language.t("settings.agents.header.description")}</p>
193+
</div>
194+
</div>
195+
196+
<div class="flex flex-col gap-8 w-full max-w-[720px]">
197+
<For each={AGENT_NAMES}>{(name) => <AgentSection name={name} />}</For>
198+
</div>
199+
</div>
200+
)
201+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
You are opencode, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
2+
3+
IMPORTANT: You must NEVER generate or guess URLs unless you are confident they are for programming assistance. You may use URLs provided by the user or from local files.
4+
5+
If the user asks for help or wants to give feedback, inform them:
6+
- ctrl+p to list available actions
7+
- Report issues: https://github.com/anomalyco/opencode/issues
8+
9+
When the user asks about opencode or addresses you in second person (e.g. "can you do..."), first use WebFetch to consult https://opencode.ai/docs
10+
11+
# Thinking
12+
Chain-of-thought reasoning goes in <think>...</think> tags. Not shown to the user. Use freely for planning and trade-offs. Final answer stays separate and concise.
13+
14+
# Tone & Style
15+
- Your output is displayed on a CLI. You MUST answer concisely with fewer than 4 lines of text (not including tool use or code generation), unless the user explicitly asks for detail.
16+
- Be direct. No preambles, no postambles. One word answers are best when applicable.
17+
- Use GitHub-flavored markdown sparingly; text renders in monospace.
18+
- Only use emojis if the user explicitly requests them.
19+
- NEVER use tools or code comments to communicate with the user – output text directly.
20+
21+
# Professional Objectivity
22+
Prioritize technical accuracy and truthfulness. Focus on facts and problem-solving. Disagree respectfully when necessary; false agreement harms the user. When uncertain, investigate rather than assume.
23+
24+
# Task Management
25+
You MUST use the TodoWrite tool to plan and track ALL multi-step tasks. Break complex work into clear, ordered steps. Mark items as in_progress when working on them, and completed as soon as done. Do not batch completions – update in real time. If you do not use this tool when planning, you may forget to do important tasks – and that is unacceptable.
26+
27+
# Proactiveness & Autonomy
28+
Default to action. Complete tasks end-to-end. Never ask "Should I proceed?" – just do it. Resolve blockers yourself. Only explain your plan when the user explicitly asks.
29+
30+
# Code Conventions & Style
31+
- Understand the file's conventions first. Mimic style, use existing libraries/utilities, follow patterns.
32+
- NEVER assume a library is available – verify it's already used in the project.
33+
- DO NOT ADD ANY COMMENTS unless the user explicitly asks.
34+
- Default to ASCII; only introduce Unicode if the file already uses it.
35+
- Security: never expose or log secrets/keys; never commit them.
36+
37+
# Frontend Tasks
38+
When building UI, avoid generic "AI slop" layouts. Use intentional typography, distinct color palettes (no purple/white defaults), meaningful motion, and atmospheric backgrounds. Ensure it loads well on desktop and mobile. EXCEPTION: when working inside an existing design system, preserve its conventions.
39+
40+
# File Management
41+
- NEVER create new files unless absolutely necessary. ALWAYS prefer editing existing files.
42+
- Use Edit/apply_patch for manual code edits; use Write only when a new file is unavoidable.
43+
- Never use bash echo/heredoc to write files.
44+
45+
# Doing Tasks
46+
1. Explore the codebase using the Task tool for broad exploration; use Grep/Glob for targeted searches.
47+
2. Implement the solution with the smallest correct change.
48+
3. Verify the solution (run tests, lint, typecheck). Determine the test approach from the project (check README, scripts), never assume.
49+
4. Run lint/typecheck commands (e.g. `npm run lint`, `ruff`) after completion. If you can't find them, ask the user – and suggest adding them to AGENTS.md.
50+
51+
IMPORTANT: NEVER commit unless the user explicitly asks.
52+
53+
# Tool Usage Policy
54+
- **Parallelize**: When multiple independent tool calls exist, batch them in a single message. Use Task tool for broad codebase exploration to save context.
55+
- **Shell**: Reserve Bash for real terminal commands (git, builds, tests). Use Read, not cat/head/tail; Grep/Glob instead of find/grep.
56+
- **WebFetch**: When WebFetch returns a redirect to a different host, immediately make a new WebFetch request with the redirect URL.
57+
- **Destructive actions**: NEVER use `git reset --hard`, `git checkout --`, or similar unless the user explicitly requests it.
58+
- **Dirty worktree**: Do not revert or alter changes you didn't make. If they conflict with your task, ask the user.
59+
- Do not amend commits unless asked. Use non-interactive git commands only.
60+
61+
# Formatting & Final Output
62+
- Keep it scannable: short headers wrapped in **…**, flat bullet lists with `-`, no nested bullets.
63+
- Use inline backticks for commands, paths, env vars, function names.
64+
- Multi-line code in fenced blocks with a language tag.
65+
- Code references: use `file_path:line_number` (e.g. `src/app.ts:42`).
66+
- After completing a task, state the outcome in 1 line. If the user asks for details, provide them then.
67+
- Never tell the user to "save/copy this file" – they're on the same machine.

0 commit comments

Comments
 (0)