diff --git a/packages/ui/src/components/sections/behavior/BehaviorPage.tsx b/packages/ui/src/components/sections/behavior/BehaviorPage.tsx new file mode 100644 index 00000000..bdeae59d --- /dev/null +++ b/packages/ui/src/components/sections/behavior/BehaviorPage.tsx @@ -0,0 +1,183 @@ +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; +import { toast } from '@/components/ui'; +import { useI18n } from '@/lib/i18n'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; +import { RiInformationLine } from '@remixicon/react'; + +const AGENTS_MD_PATH = '~/.config/opencode/AGENTS.md'; + +const readApiError = async (response: Response, fallback: string) => { + const data = await response.json().catch(() => null) as { error?: unknown } | null; + return typeof data?.error === 'string' && data.error.trim() ? data.error : fallback; +}; + +const normalizeAgentsMdContent = (content: string) => { + return content.length > 0 && !content.endsWith('\n') ? `${content}\n` : content; +}; + +const saveBehaviorSetting = async (globalBehaviorPrompt: string, fallbackError: string) => { + const response = await fetch('/api/config/settings', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify({ globalBehaviorPrompt }), + }); + + if (!response.ok) { + throw new Error(await readApiError(response, fallbackError)); + } +}; + +export const BehaviorPage: React.FC = () => { + const { t } = useI18n(); + const [prompt, setPrompt] = React.useState(''); + const [isLoading, setIsLoading] = React.useState(true); + const [isSaving, setIsSaving] = React.useState(false); + const [initialPrompt, setInitialPrompt] = React.useState(''); + + React.useEffect(() => { + const abort = new AbortController(); + + const load = async () => { + try { + const [settingsRes, agentsMdRes] = await Promise.all([ + fetch('/api/config/settings', { + method: 'GET', + headers: { Accept: 'application/json' }, + signal: abort.signal, + }), + fetch('/api/behavior/agents-md', { + method: 'GET', + headers: { Accept: 'application/json' }, + signal: abort.signal, + }), + ]); + + let settingsPrompt = ''; + if (settingsRes.ok) { + const data = await settingsRes.json(); + if (typeof data.globalBehaviorPrompt === 'string') { + settingsPrompt = data.globalBehaviorPrompt; + } + } + + if (!settingsPrompt.trim() && agentsMdRes.ok) { + const agentsData = await agentsMdRes.json(); + if (typeof agentsData.content === 'string') { + settingsPrompt = agentsData.content; + } + } + + setPrompt(settingsPrompt); + setInitialPrompt(settingsPrompt); + } catch (error) { + if ((error as Error).name !== 'AbortError') { + console.warn('Failed to load behavior settings:', error); + } + } finally { + setIsLoading(false); + } + }; + + void load(); + return () => abort.abort(); + }, []); + + const isDirty = prompt !== initialPrompt; + + const handleSave = async () => { + setIsSaving(true); + try { + const content = normalizeAgentsMdContent(prompt); + const response = await fetch('/api/behavior/agents-md', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify({ content }), + }); + + if (!response.ok) { + throw new Error(await readApiError(response, t('settings.behavior.page.toast.saveFailed'))); + } + + await saveBehaviorSetting(content, t('settings.behavior.page.toast.saveFailed')); + + setPrompt(content); + setInitialPrompt(content); + toast.success(t('settings.behavior.page.toast.saved')); + } catch (error) { + console.error('Failed to save behavior:', error); + const message = error instanceof Error ? error.message : t('settings.behavior.page.toast.saveFailed'); + toast.error(message); + } finally { + setIsSaving(false); + } + }; + + return ( + + + + + {t('settings.behavior.page.title')} + + + + + + + + {t('settings.behavior.page.section.systemPrompt')} + + + + + + + + + {t('settings.behavior.page.warning.title')} + + + {t('settings.behavior.page.warning.description', { path: AGENTS_MD_PATH })} + + + + + + + + + setPrompt(e.target.value)} + placeholder={t('settings.behavior.page.field.systemPromptPlaceholder')} + rows={12} + disabled={isLoading} + outerClassName="min-h-[160px] max-h-[70vh]" + className="w-full font-mono typography-meta bg-transparent" + /> + + + + + + {isSaving ? t('settings.common.actions.saving') : t('settings.common.actions.saveChanges')} + + + + + ); +}; diff --git a/packages/ui/src/components/views/SettingsView.tsx b/packages/ui/src/components/views/SettingsView.tsx index 6b1be7ff..23fed5a4 100644 --- a/packages/ui/src/components/views/SettingsView.tsx +++ b/packages/ui/src/components/views/SettingsView.tsx @@ -30,11 +30,13 @@ import { RiRestartLine, RiServerLine, RiSlashCommands2, + RiBrainLine, } from '@remixicon/react'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { ErrorBoundary } from '@/components/ui/ErrorBoundary'; import { AgentsSidebar } from '@/components/sections/agents/AgentsSidebar'; import { AgentsPage } from '@/components/sections/agents/AgentsPage'; +import { BehaviorPage } from '@/components/sections/behavior/BehaviorPage'; import { CommandsSidebar } from '@/components/sections/commands/CommandsSidebar'; import { CommandsPage } from '@/components/sections/commands/CommandsPage'; import { McpSidebar } from '@/components/sections/mcp/McpSidebar'; @@ -93,6 +95,7 @@ const pageOrder: SettingsPageSlug[] = [ 'projects', 'remote-instances', 'agents', + 'behavior', 'commands', 'mcp', 'providers', @@ -140,6 +143,8 @@ export function getSettingsNavIcon(slug: SettingsPageSlug): React.ComponentType< return RiCloudLine; case 'agents': return RiAiAgentLine; + case 'behavior': + return RiBrainLine; case 'commands': return RiSlashCommands2; case 'mcp': @@ -395,6 +400,8 @@ export const SettingsView: React.FC = ({ onClose, forceMobile return t('settings.page.usage.title'); case 'agents': return t('settings.page.agents.title'); + case 'behavior': + return t('settings.page.behavior.title'); case 'commands': return t('settings.page.commands.title'); case 'mcp': @@ -478,6 +485,8 @@ export const SettingsView: React.FC = ({ onClose, forceMobile return ; case 'agents': return ; + case 'behavior': + return ; case 'commands': return ; case 'mcp': diff --git a/packages/ui/src/lib/desktop.ts b/packages/ui/src/lib/desktop.ts index 52bcfb48..8224a584 100644 --- a/packages/ui/src/lib/desktop.ts +++ b/packages/ui/src/lib/desktop.ts @@ -158,6 +158,9 @@ export type DesktopSettings = { skillCatalogs?: SkillCatalogConfig[]; // Opt-in to send anonymous usage reports for update checks (default: true) reportUsage?: boolean; + + // Global behavior prompt — synced to ~/.config/opencode/AGENTS.md + globalBehaviorPrompt?: string; }; type TauriGlobal = { diff --git a/packages/ui/src/lib/i18n/messages/en.settings.ts b/packages/ui/src/lib/i18n/messages/en.settings.ts index 65c16b2b..28e4f3ed 100644 --- a/packages/ui/src/lib/i18n/messages/en.settings.ts +++ b/packages/ui/src/lib/i18n/messages/en.settings.ts @@ -337,6 +337,14 @@ export const settingsDict = { 'settings.agents.page.permissions.addCustomRule': 'Add Custom Rule', 'settings.agents.page.permissions.permissionPlaceholder': 'Permission...', 'settings.agents.page.permissions.patternPlaceholder': 'Pattern (e.g. *)', + 'settings.page.behavior.title': 'Behavior', + 'settings.behavior.page.title': 'Behavior', + 'settings.behavior.page.warning.title': 'Global rules are combined with project rules', + 'settings.behavior.page.warning.description': 'Changes made here update {path}. OpenCode also includes any project-level AGENTS.md rules when they exist.', + 'settings.behavior.page.section.systemPrompt': 'Global AGENTS.md', + 'settings.behavior.page.field.systemPromptPlaceholder': 'You are a helpful AI assistant...\n\nUse this space to define absolute rules for how the AI should behave across all sessions and providers.', + 'settings.behavior.page.toast.saved': 'Behavior saved successfully', + 'settings.behavior.page.toast.saveFailed': 'Failed to save behavior', 'settings.commands.sidebar.title': 'Commands', 'settings.commands.sidebar.total': 'Total {count}', 'settings.commands.sidebar.empty.title': 'No commands configured', diff --git a/packages/ui/src/lib/i18n/messages/es.settings.ts b/packages/ui/src/lib/i18n/messages/es.settings.ts index b959325b..0f49d54a 100644 --- a/packages/ui/src/lib/i18n/messages/es.settings.ts +++ b/packages/ui/src/lib/i18n/messages/es.settings.ts @@ -337,6 +337,14 @@ export const settingsDict = { "settings.agents.page.permissions.addCustomRule": "Añadir regla personalizada", "settings.agents.page.permissions.permissionPlaceholder": "Permiso...", "settings.agents.page.permissions.patternPlaceholder": "Patrón (ej. *)", + "settings.page.behavior.title": "Comportamiento", + "settings.behavior.page.title": "Comportamiento", + "settings.behavior.page.warning.title": "Las reglas globales se combinan con las reglas del proyecto", + "settings.behavior.page.warning.description": "Los cambios realizados aquí actualizan {path}. OpenCode también incluye cualquier AGENTS.md a nivel de proyecto cuando existe.", + "settings.behavior.page.section.systemPrompt": "AGENTS.md global", + "settings.behavior.page.field.systemPromptPlaceholder": "Eres un asistente de IA útil...\n\nUsa este espacio para definir reglas absolutas sobre cómo la IA debe comportarse en todas las sesiones y proveedores.", + "settings.behavior.page.toast.saved": "Comportamiento guardado correctamente", + "settings.behavior.page.toast.saveFailed": "Error al guardar el comportamiento", "settings.commands.sidebar.title": "Comandos", "settings.commands.sidebar.total": "Total {count}", "settings.commands.sidebar.empty.title": "No hay comandos configurados", diff --git a/packages/ui/src/lib/i18n/messages/ko.settings.ts b/packages/ui/src/lib/i18n/messages/ko.settings.ts index 72a40b92..b0aaff42 100644 --- a/packages/ui/src/lib/i18n/messages/ko.settings.ts +++ b/packages/ui/src/lib/i18n/messages/ko.settings.ts @@ -337,6 +337,14 @@ export const settingsDict = { 'settings.agents.page.permissions.addCustomRule': '사용자 정의 규칙 추가', 'settings.agents.page.permissions.permissionPlaceholder': '권한...', 'settings.agents.page.permissions.patternPlaceholder': '패턴(예: *)', + 'settings.page.behavior.title': '동작', + 'settings.behavior.page.title': '동작', + 'settings.behavior.page.warning.title': '전역 규칙은 프로젝트 규칙과 함께 사용됩니다', + 'settings.behavior.page.warning.description': '여기에서 변경한 내용은 {path}를 업데이트합니다. 프로젝트 수준의 AGENTS.md가 있으면 OpenCode도 함께 포함합니다.', + 'settings.behavior.page.section.systemPrompt': '전역 AGENTS.md', + 'settings.behavior.page.field.systemPromptPlaceholder': '당신은 유용한 AI 어시스턴트입니다...\n\n모든 세션과 공급자에서 AI가 어떻게 행동해야 하는지에 대한 절대적인 규칙을 정의할 수 있습니다.', + 'settings.behavior.page.toast.saved': '동작이 성공적으로 저장되었습니다', + 'settings.behavior.page.toast.saveFailed': '동작 저장에 실패했습니다', 'settings.commands.sidebar.title': '명령어', 'settings.commands.sidebar.total': '총 {count}개', 'settings.commands.sidebar.empty.title': '설정된 명령어가 없습니다', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts index cc79b5c5..7ec835ea 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts @@ -337,6 +337,14 @@ export const settingsDict = { "settings.agents.page.permissions.addCustomRule": "Adicionar regra personalizada", "settings.agents.page.permissions.permissionPlaceholder": "Permissão...", "settings.agents.page.permissions.patternPlaceholder": "Padrão (ex.: *)", + "settings.page.behavior.title": "Comportamento", + "settings.behavior.page.title": "Comportamento", + "settings.behavior.page.warning.title": "Regras globais são combinadas com regras do projeto", + "settings.behavior.page.warning.description": "As alterações feitas aqui atualizam {path}. O OpenCode também inclui qualquer AGENTS.md a nível de projeto quando existir.", + "settings.behavior.page.section.systemPrompt": "AGENTS.md global", + "settings.behavior.page.field.systemPromptPlaceholder": "Você é um assistente de IA útil...\n\nUse este espaço para definir regras absolutas sobre como a IA deve se comportar em todas as sessões e provedores.", + "settings.behavior.page.toast.saved": "Comportamento salvo com sucesso", + "settings.behavior.page.toast.saveFailed": "Falha ao salvar o comportamento", "settings.commands.sidebar.title": "Comandos", "settings.commands.sidebar.total": "Total {count}", "settings.commands.sidebar.empty.title": "Não há comandos configurados", diff --git a/packages/ui/src/lib/i18n/messages/uk.settings.ts b/packages/ui/src/lib/i18n/messages/uk.settings.ts index 9f39d7dc..1254a6b7 100644 --- a/packages/ui/src/lib/i18n/messages/uk.settings.ts +++ b/packages/ui/src/lib/i18n/messages/uk.settings.ts @@ -337,6 +337,14 @@ export const settingsDict = { "settings.agents.page.permissions.addCustomRule": "Додати спеціальне правило", "settings.agents.page.permissions.permissionPlaceholder": "Дозвіл...", "settings.agents.page.permissions.patternPlaceholder": "Шаблон (наприклад, *)", + "settings.page.behavior.title": "Поведінка", + "settings.behavior.page.title": "Поведінка", + "settings.behavior.page.warning.title": "Глобальні правила поєднуються з правилами проєкту", + "settings.behavior.page.warning.description": "Зміни тут оновлюють {path}. OpenCode також додає правила з AGENTS.md на рівні проєкту, якщо вони існують.", + "settings.behavior.page.section.systemPrompt": "Глобальний AGENTS.md", + "settings.behavior.page.field.systemPromptPlaceholder": "Ви — корисний AI-асистент...\n\nВикористовуйте цей простір для визначення абсолютних правил поведінки AI для всіх сесій та провайдерів.", + "settings.behavior.page.toast.saved": "Поведінку збережено", + "settings.behavior.page.toast.saveFailed": "Не вдалося зберегти поведінку", "settings.commands.sidebar.title": "Команди", "settings.commands.sidebar.total": "Усього {count}", "settings.commands.sidebar.empty.title": "Команди не налаштовано", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts index 5f9494ba..431bcfc0 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts @@ -337,6 +337,14 @@ export const settingsDict = { 'settings.agents.page.permissions.addCustomRule': '添加自定义规则', 'settings.agents.page.permissions.permissionPlaceholder': '权限...', 'settings.agents.page.permissions.patternPlaceholder': '模式(例如 *)', + 'settings.page.behavior.title': '行为', + 'settings.behavior.page.title': '行为', + 'settings.behavior.page.warning.title': '全局规则会与项目规则合并使用', + 'settings.behavior.page.warning.description': '此处所做的更改会更新 {path}。当项目级 AGENTS.md 存在时,OpenCode 也会包含它。', + 'settings.behavior.page.section.systemPrompt': '全局 AGENTS.md', + 'settings.behavior.page.field.systemPromptPlaceholder': '你是一个有帮助的 AI 助手...\n\n使用此空间定义 AI 在所有会话和提供商中的绝对行为规则。', + 'settings.behavior.page.toast.saved': '行为保存成功', + 'settings.behavior.page.toast.saveFailed': '行为保存失败', 'settings.commands.sidebar.title': '命令', 'settings.commands.sidebar.total': '总计 {count}', 'settings.commands.sidebar.empty.title': '未配置命令', diff --git a/packages/ui/src/lib/settings/metadata.ts b/packages/ui/src/lib/settings/metadata.ts index 91e7c631..7ae93bc9 100644 --- a/packages/ui/src/lib/settings/metadata.ts +++ b/packages/ui/src/lib/settings/metadata.ts @@ -7,6 +7,7 @@ export type SettingsPageSlug = | 'providers' | 'usage' | 'agents' + | 'behavior' | 'commands' | 'mcp' | 'skills.installed' @@ -103,6 +104,13 @@ export const SETTINGS_PAGE_METADATA: readonly SettingsPageMeta[] = [ kind: 'split', keywords: ['agent', 'agents', 'prompts', 'tools', 'permissions'], }, + { + slug: 'behavior', + title: 'Behavior', + group: 'opencode', + kind: 'single', + keywords: ['behavior', 'agents.md', 'system prompt', 'global rules', 'instructions', 'override'], + }, { slug: 'commands', title: 'Commands', diff --git a/packages/vscode/src/bridge-config-runtime.ts b/packages/vscode/src/bridge-config-runtime.ts index 2c3d44d6..aa6cfdbd 100644 --- a/packages/vscode/src/bridge-config-runtime.ts +++ b/packages/vscode/src/bridge-config-runtime.ts @@ -1,4 +1,6 @@ import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as os from 'os'; import * as path from 'path'; import { createAgent, @@ -55,6 +57,9 @@ type ConfigRuntimeDeps = { clientReloadDelayMs: number; }; +const AGENTS_MD_PATH = path.join(os.homedir(), '.config', 'opencode', 'AGENTS.md'); +const MAX_BEHAVIOR_PROMPT_SIZE = 1024 * 1024; + const resolveWorkingDirectory = (ctx: BridgeContext | undefined, directory?: string): string | undefined => ( (typeof directory === 'string' && directory.trim()) ? directory.trim() @@ -144,6 +149,30 @@ export async function handleConfigBridgeMessage( return { id, type, success: true, data: updated }; } + case 'api:behavior/agents-md:get': { + try { + const content = await fs.promises.readFile(AGENTS_MD_PATH, 'utf8'); + return { id, type, success: true, data: { content, exists: true } }; + } catch (error) { + if ((error as NodeJS.ErrnoException)?.code === 'ENOENT') { + return { id, type, success: true, data: { content: '', exists: false } }; + } + throw error; + } + } + + case 'api:behavior/agents-md:save': { + const request = (payload || {}) as { content?: unknown }; + const content = typeof request.content === 'string' ? request.content : ''; + if (content.length > MAX_BEHAVIOR_PROMPT_SIZE) { + return { id, type, success: false, error: `Content exceeds maximum size of ${MAX_BEHAVIOR_PROMPT_SIZE} bytes` }; + } + await fs.promises.mkdir(path.dirname(AGENTS_MD_PATH), { recursive: true }); + await fs.promises.writeFile(AGENTS_MD_PATH, content, 'utf8'); + await ctx?.manager?.restart(); + return { id, type, success: true, data: { success: true } }; + } + case 'api:magic-prompts:get': { return { id, type, success: true, data: deps.readMagicPromptOverrides() }; } diff --git a/packages/vscode/webview/main.tsx b/packages/vscode/webview/main.tsx index 9a5a537a..e5a3cd47 100644 --- a/packages/vscode/webview/main.tsx +++ b/packages/vscode/webview/main.tsx @@ -843,6 +843,18 @@ const handleLocalApiRequest = async (url: URL, init?: RequestInit) => { return new Response(JSON.stringify(updated), { status: 200, headers: { 'Content-Type': 'application/json' } }); } + if (normalizedPathname === '/api/behavior/agents-md') { + if (method === 'GET') { + const data = await sendBridgeMessage('api:behavior/agents-md:get'); + return new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } }); + } + if (method === 'PUT') { + const body = init?.body ? JSON.parse(init.body as string) : {}; + const data = await sendBridgeMessage('api:behavior/agents-md:save', body); + return new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } }); + } + } + if (pathname === '/api/magic-prompts') { if (method === 'GET') { const data = await sendBridgeMessage('api:magic-prompts:get'); diff --git a/packages/web/server/lib/opencode/core-routes.js b/packages/web/server/lib/opencode/core-routes.js index 28998e94..9c2fba75 100644 --- a/packages/web/server/lib/opencode/core-routes.js +++ b/packages/web/server/lib/opencode/core-routes.js @@ -454,7 +454,13 @@ export const registerCommonRequestMiddleware = (app, dependencies) => { const { express } = dependencies; app.use((req, res, next) => { - if ( + if (req.path.startsWith('/api/behavior')) { + const contentLength = parseInt(req.headers['content-length'] || '0', 10); + if (contentLength > 1024 * 1024) { + return res.status(413).json({ error: 'Content exceeds maximum size of 1048576 bytes' }); + } + express.json({ limit: '1mb' })(req, res, next); + } else if ( req.path.startsWith('/api/config/agents') || req.path.startsWith('/api/config/commands') || req.path.startsWith('/api/config/mcp') || diff --git a/packages/web/server/lib/opencode/routes.js b/packages/web/server/lib/opencode/routes.js index 9aa26d3c..3e812015 100644 --- a/packages/web/server/lib/opencode/routes.js +++ b/packages/web/server/lib/opencode/routes.js @@ -1,4 +1,7 @@ import { createProjectIdFromPath } from '../projects/project-id.js'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; export const registerOpenCodeRoutes = (app, dependencies) => { const { @@ -295,4 +298,55 @@ export const registerOpenCodeRoutes = (app, dependencies) => { return res.status(500).json({ error: error.message || 'Failed to update working directory' }); } }); + + // Behavior / Global AGENTS.md endpoints + const AGENTS_MD_PATH = path.join(os.homedir(), '.config', 'opencode', 'AGENTS.md'); + const MAX_BEHAVIOR_PROMPT_SIZE = 1024 * 1024; // 1 MB + + app.get('/api/behavior/agents-md', async (_req, res) => { + try { + try { + await fs.promises.access(AGENTS_MD_PATH); + } catch { + return res.json({ content: '', exists: false }); + } + const content = await fs.promises.readFile(AGENTS_MD_PATH, 'utf8'); + return res.json({ content, exists: true }); + } catch (error) { + console.error('Failed to read AGENTS.md:', error); + return res.status(500).json({ error: 'Failed to read AGENTS.md' }); + } + }); + + app.put('/api/behavior/agents-md', async (req, res) => { + try { + const content = typeof req.body?.content === 'string' ? req.body.content : ''; + + if (content.length > MAX_BEHAVIOR_PROMPT_SIZE) { + return res.status(413).json({ error: `Content exceeds maximum size of ${MAX_BEHAVIOR_PROMPT_SIZE} bytes` }); + } + + // Ensure parent directory exists + const parentDir = path.dirname(AGENTS_MD_PATH); + try { + await fs.promises.access(parentDir); + } catch { + await fs.promises.mkdir(parentDir, { recursive: true }); + } + + await fs.promises.writeFile(AGENTS_MD_PATH, content, 'utf8'); + + // Refresh OpenCode so it picks up the new AGENTS.md without a full restart + try { + await refreshOpenCodeAfterConfigChange('global behavior (AGENTS.md) updated'); + } catch { + // Non-fatal: file was written successfully + } + + return res.json({ success: true }); + } catch (error) { + console.error('Failed to write AGENTS.md:', error); + return res.status(500).json({ error: error.message || 'Failed to write AGENTS.md' }); + } + }); }; diff --git a/packages/web/server/lib/opencode/settings-helpers.js b/packages/web/server/lib/opencode/settings-helpers.js index d6a5584a..43799318 100644 --- a/packages/web/server/lib/opencode/settings-helpers.js +++ b/packages/web/server/lib/opencode/settings-helpers.js @@ -555,6 +555,14 @@ export const createSettingsHelpers = (dependencies) => { result.reportUsage = candidate.reportUsage; } + // Global behavior prompt — synced to ~/.config/opencode/AGENTS.md + if (typeof candidate.globalBehaviorPrompt === 'string') { + const value = candidate.globalBehaviorPrompt; + if (value.length <= 1024 * 1024) { + result.globalBehaviorPrompt = value; + } + } + return result; };
+ {t('settings.behavior.page.warning.title')} +
+ {t('settings.behavior.page.warning.description', { path: AGENTS_MD_PATH })} +