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:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
d35ff8a2db
commit
bcbf34b1d1
@@ -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') ||
|
||||
|
||||
@@ -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' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user