feat: add Behavior settings page for global AGENTS.md (#1079)

* feat: add Behavior settings page for global AGENTS.md

- Add new 'Behavior' settings page to manage global system prompt
- Sync global behavior prompt to ~/.config/opencode/AGENTS.md
- Add GET/PUT /api/behavior/agents-md endpoints with 1MB size limit
- Add AbortController guards and parallel fetches in BehaviorPage
- Add i18n translations for en, es, ko, pt-BR, uk, zh-CN
- Add globalBehaviorPrompt to DesktopSettings type and sanitizer
- Add /api/behavior to express.json() body-parser whitelist
- Ensure atomic save: AGENTS.md written before settings updated

* fix: address Greptile review feedback

- Rename misleading 'trimmed' variable to 'value' in settings-helpers.js
- Add Content-Length guard for /api/behavior before 50mb JSON parser
- Use express.json({ limit: '1mb' }) for behavior endpoints
- Add actual translations for es, ko, pt-BR, uk, zh-CN locales

* fix(vscode): support behavior settings endpoint

* fix(behavior): wait for settings persistence

* fix(behavior): end agents file with newline

* fix(behavior): avoid duplicate textarea resize handles

* fix(behavior): clarify global rules copy

* fix(behavior): move rules note into tooltip

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
Shyamalan Kannan
2026-05-01 00:39:55 +03:00
committed by GitHub
co-authored by Bohdan Triapitsyn
parent d35ff8a2db
commit bcbf34b1d1
15 changed files with 361 additions and 1 deletions
@@ -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() };
}