beast-agent 2.15.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/src/agent/engine.js +417 -154
  3. package/src/agent/opencode/agents.js +161 -0
  4. package/src/agent/opencode/askperm.js +135 -0
  5. package/src/agent/opencode/index.js +51 -0
  6. package/src/agent/opencode/permission.js +108 -0
  7. package/src/agent/opencode/prompts/anthropic.txt +105 -0
  8. package/src/agent/opencode/prompts/beast.txt +147 -0
  9. package/src/agent/opencode/prompts/build-switch.txt +5 -0
  10. package/src/agent/opencode/prompts/codex.txt +79 -0
  11. package/src/agent/opencode/prompts/default.txt +95 -0
  12. package/src/agent/opencode/prompts/edit.txt +10 -0
  13. package/src/agent/opencode/prompts/explore.txt +18 -0
  14. package/src/agent/opencode/prompts/gemini.txt +155 -0
  15. package/src/agent/opencode/prompts/glob.txt +6 -0
  16. package/src/agent/opencode/prompts/gpt.txt +107 -0
  17. package/src/agent/opencode/prompts/grep.txt +8 -0
  18. package/src/agent/opencode/prompts/kimi.txt +95 -0
  19. package/src/agent/opencode/prompts/max-steps.txt +16 -0
  20. package/src/agent/opencode/prompts/meta.txt +65 -0
  21. package/src/agent/opencode/prompts/plan.txt +26 -0
  22. package/src/agent/opencode/prompts/read.txt +14 -0
  23. package/src/agent/opencode/prompts/skill.txt +5 -0
  24. package/src/agent/opencode/prompts/task.txt +19 -0
  25. package/src/agent/opencode/prompts/todowrite.txt +44 -0
  26. package/src/agent/opencode/prompts/trinity.txt +97 -0
  27. package/src/agent/opencode/prompts/webfetch.txt +13 -0
  28. package/src/agent/opencode/prompts/websearch.txt +14 -0
  29. package/src/agent/opencode/prompts/write.txt +8 -0
  30. package/src/agent/opencode/systemprompt.js +157 -0
  31. package/src/agent/opencode/toolmap.js +316 -0
  32. package/src/main.js +93 -41
  33. package/src/preload.js +2 -0
  34. package/src/renderer/renderer.js +59 -15
@@ -0,0 +1,157 @@
1
+ 'use strict';
2
+
3
+ /* ---------- opencode session/system.ts + reminders.ts portu (birebir) ----------
4
+ Beast Code (bcCode) oturumlarında system prompt artık TEK elle yazılmış
5
+ metin değil, opencode birleşimidir (prompt.ts:1257-1269):
6
+
7
+ system = [ ...environment(model), → bu modül
8
+ ...instructions(AGENTS.md), → engine._projectInstructions
9
+ ...(skills kataloğu), → bu modül (skill/skill.ts fmt)
10
+ ...ajan tanımının promptu ] → agents.js (explore vb.)
11
+
12
+ Ana prompt MODEL BAZLI seçilir (system.ts:27-49): gpt-4 ailesi, o1, o3 → beast.txt,
13
+ gpt* → gpt.txt (codex* → codex.txt), gemini-* → gemini.txt, claude →
14
+ anthropic.txt, kimi → kimi.txt, muse → meta.txt, trinity → trinity.txt,
15
+ diğer → default.txt.
16
+
17
+ Reminders (reminders.ts:15-90): plan ajanındaysa son user mesajına plan.txt
18
+ synthetic part'ı; geçmişte plan koşmuşsa ve şimdi build'se build-switch.txt
19
+ eklenir. Belleği kirletmemek için payload ÖNCESİ kopya mesajlara uygulanır
20
+ (her tur taze — opencode her turda DB'den yeniden yükleyip uygular). */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const Permission = require('./permission');
25
+
26
+ const PROMPT_DIR = path.join(__dirname, 'prompts');
27
+ function loadPrompt(name) {
28
+ try {
29
+ let t = fs.readFileSync(path.join(PROMPT_DIR, name), 'utf8');
30
+ if (t.charCodeAt(0) === 0xfeff) t = t.slice(1); // BOM-strip
31
+ return t.replace(/\s+$/, '');
32
+ } catch {
33
+ return '';
34
+ }
35
+ }
36
+
37
+ const PROMPTS = {
38
+ beast: loadPrompt('beast.txt'),
39
+ gpt: loadPrompt('gpt.txt'),
40
+ codex: loadPrompt('codex.txt'),
41
+ anthropic: loadPrompt('anthropic.txt'),
42
+ gemini: loadPrompt('gemini.txt'),
43
+ kimi: loadPrompt('kimi.txt'),
44
+ meta: loadPrompt('meta.txt'),
45
+ trinity: loadPrompt('trinity.txt'),
46
+ default: loadPrompt('default.txt'),
47
+ plan: loadPrompt('plan.txt'),
48
+ buildSwitch: loadPrompt('build-switch.txt'),
49
+ explore: loadPrompt('explore.txt'),
50
+ };
51
+
52
+ /* opencode system.ts:27-49 — model-bazlı prompt seçimi (birebir) */
53
+ function provider(modelId) {
54
+ const id = String(modelId || '');
55
+ if (id.includes('muse')) {
56
+ const name = id.includes('muse-glimmer') ? 'Muse Glimmer' : 'Muse Spark';
57
+ return [PROMPTS.meta.replaceAll('{{MODEL_NAME}}', name)];
58
+ }
59
+ if (id.includes('gpt-4') || id.includes('o1') || id.includes('o3')) return [PROMPTS.beast];
60
+ if (id.includes('gpt')) {
61
+ if (id.includes('codex')) return [PROMPTS.codex];
62
+ return [PROMPTS.gpt];
63
+ }
64
+ if (id.includes('gemini-')) return [PROMPTS.gemini];
65
+ if (id.includes('claude')) return [PROMPTS.anthropic];
66
+ if (id.toLowerCase().includes('trinity')) return [PROMPTS.trinity];
67
+ if (id.toLowerCase().includes('kimi')) return [PROMPTS.kimi];
68
+ return [PROMPTS.default];
69
+ }
70
+
71
+ /* opencode SystemPrompt.environment — model kimliği + <env> bloğu */
72
+ function environment({ modelId, providerId, directory, worktree }) {
73
+ const isGit = (() => {
74
+ try {
75
+ return fs.existsSync(path.join(String(directory || ''), '.git')) ? 'yes' : 'no';
76
+ } catch {
77
+ return 'no';
78
+ }
79
+ })();
80
+ return [
81
+ [
82
+ `You are powered by the model named ${modelId}. The exact model ID is ${providerId}/${modelId}`,
83
+ `Here is some useful information about the environment you are running in:`,
84
+ `<env>`,
85
+ ` Working directory: ${directory}`,
86
+ ` Workspace root folder: ${worktree}`,
87
+ ` Is directory a git repo: ${isGit}`,
88
+ ` Platform: ${process.platform}`,
89
+ ` Today's date: ${new Date().toDateString()}`,
90
+ `</env>`,
91
+ ].join('\n'),
92
+ ];
93
+ }
94
+
95
+ /* opencode skill/skill.ts fmt (verbose) — skills.js scan() listesini
96
+ opencode katalog biçimine basar */
97
+ function skillsCatalog(skills) {
98
+ const described = (skills || []).filter((s) => s && s.description);
99
+ if (!described.length) return undefined;
100
+ return [
101
+ 'Skills provide specialized instructions and workflows for specific tasks.',
102
+ 'Use the skill tool to load a skill when a task matches its description.',
103
+ '<available_skills>',
104
+ ...described
105
+ .slice()
106
+ .sort((a, b) => String(a.name).localeCompare(String(b.name)))
107
+ .flatMap((s) => [
108
+ ' <skill>',
109
+ ` <name>${s.name}</name>`,
110
+ ` <description>${s.description}</description>`,
111
+ ` <location>${s.path}</location>`,
112
+ ' </skill>',
113
+ ]),
114
+ '</available_skills>',
115
+ ].join('\n');
116
+ }
117
+
118
+ /* opencode SystemPrompt.skills — ajanın skill izni yoksa katalog gösterilmez */
119
+ function skills(ruleset, scanFn) {
120
+ if (Permission.disabled(['skill'], ruleset).has('skill')) return undefined;
121
+ try {
122
+ return skillsCatalog(scanFn());
123
+ } catch {
124
+ return undefined;
125
+ }
126
+ }
127
+
128
+ /* opencode SessionReminders.apply — synthetic part yerine son user mesajının
129
+ KOPIASINA metin ekler; session.messages asla değişmez (her tur taze uygulanır).
130
+ wasPlan: bu oturumda daha önce plan ajanı koştu mu (engine takip eder). */
131
+ function applyReminders(messages, agent, wasPlan) {
132
+ const msgs = (messages || []).slice();
133
+ const lastUserIdx = msgs.findLastIndex ? msgs.findLastIndex((m) => m.role === 'user') : -1;
134
+ if (lastUserIdx < 0 || !agent) return msgs;
135
+ const adds = [];
136
+ if (agent.name === 'plan') adds.push(PROMPTS.plan);
137
+ if (wasPlan && agent.name === 'build') adds.push(PROMPTS.buildSwitch);
138
+ if (!adds.length) return msgs;
139
+ const base = msgs[lastUserIdx];
140
+ const baseText =
141
+ typeof base.content === 'string' ? base.content : '';
142
+ msgs[lastUserIdx] = {
143
+ ...base,
144
+ content: (baseText ? baseText + '\n\n' : '') + adds.join('\n\n'),
145
+ };
146
+ return msgs;
147
+ }
148
+
149
+ module.exports = {
150
+ PROMPTS,
151
+ provider,
152
+ environment,
153
+ skills,
154
+ skillsCatalog,
155
+ applyReminders,
156
+ loadPrompt,
157
+ };
@@ -0,0 +1,316 @@
1
+ 'use strict';
2
+
3
+ /* ---------- opencode tool seti → Beast araç köprüsü ----------
4
+ Beast Code (bcCode) oturumlarında model opencode'un ARAÇ DİLİYLE konuşur:
5
+ read · edit · write · bash · glob · grep · list · fetch · websearch ·
6
+ todowrite · task · skill (opencode builtin registry sırası)
7
+
8
+ Tanımlar opencode-dev'den birebir: açıklama metinleri tool/*.txt,
9
+ parametre şemaları opencode Schema.Struct karşılıkları (JSON Schema).
10
+ execMap() çağrıyı Beast'in yerleşik aracına çevirir — implementasyon
11
+ değişmez, modelin gördüğü dünya opencode olur. */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const PROMPT_DIR = path.join(__dirname, 'prompts');
17
+ function loadPrompt(name) {
18
+ try {
19
+ let t = fs.readFileSync(path.join(PROMPT_DIR, name), 'utf8');
20
+ if (t.charCodeAt(0) === 0xfeff) t = t.slice(1); // BOM-strip
21
+ return t.replace(/\s+$/, '') + '\n';
22
+ } catch {
23
+ return '';
24
+ }
25
+ }
26
+
27
+ /* opencode tool/shell/prompt.ts Windows (PowerShell) açıklaması — Beast
28
+ değerleriyle: varsayılan timeout 120s, 2000 satır / 50KB tavan, tam
29
+ çıktı geçici dosyaya düşer (Beast run_command zaten bu disiplinde) */
30
+ function bashDescription() {
31
+ const defaultTimeoutMs = 120000;
32
+ const maxLines = 2000;
33
+ const maxBytes = '50KB';
34
+ return `Executes a given PowerShell command in a persistent shell session.
35
+
36
+ ## Command Execution
37
+
38
+ Before executing the command, please follow these steps:
39
+ 1. If the command will create new directories or files, first use \`Test-Path -LiteralPath <parent>\` to verify the parent directory exists and is the correct location
40
+ 2. If the command will modify or delete files, first verify the paths are correct
41
+ 3. For commands expected to create new directories or files, verify the expected file/folder location is inside the working directory
42
+
43
+ After ensuring proper quoting, execute the command.
44
+ - Capture the output of the command.
45
+
46
+ ## Usage Notes
47
+
48
+ - The command argument is required.
49
+ - You can specify an optional timeout in milliseconds. If not specified, commands will time out after ${defaultTimeoutMs}ms.
50
+ - If the output exceeds ${maxLines} lines or ${maxBytes} bytes, it will be truncated and the full output will be written to a file. You can use Read with offset/limit to read specific sections or Grep to search the full content. Do NOT use \`Select-Object -First\`, \`Select-Object -Last\`, or other truncation commands to limit output; the full output will already be captured to a file for more precise searching.
51
+ - Avoid using Shell with PowerShell file/content cmdlets unless explicitly instructed or when these cmdlets are truly necessary for the task. Instead, always prefer using the dedicated tools for reading, searching, and editing files:
52
+ - Read: Reading and analyzing file contents (with line numbers)
53
+ - Edit: Making targeted changes to specific sections of files
54
+ - Write: Creating new files from scratch
55
+ - Grep: Searching file contents with regex patterns
56
+ - Glob: Finding files by patterns (e.g., "src/**/*.ts")
57
+ - When issuing multiple commands:
58
+ - If the commands are independent and can run in parallel, make multiple bash tool calls in a single message. For example, if you need to run "git status" and "git diff", send a single message with two bash tool calls in parallel.
59
+ - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
60
+ - If the commands depend on each other and must run sequentially, avoid '&&' in this shell because Windows PowerShell (5.1) does not support it. Use PowerShell conditionals such as \`cmd1; if ($?) { cmd2 }\` when later commands must depend on earlier success.
61
+ - DO NOT use newlines to separate commands (newlines are ok in quoted strings)
62
+ - The shell is PERSISTENT: working directory and environment variables are preserved between calls.
63
+ - Commands run in Windows PowerShell (5.1).`;
64
+ }
65
+
66
+ /* opencode tool şemaları — Schema.Struct karşılığı JSON Schema'lar */
67
+ const DEFS = () => [
68
+ {
69
+ name: 'bash',
70
+ description: bashDescription(),
71
+ parameters: {
72
+ type: 'object',
73
+ properties: {
74
+ command: { type: 'string', description: 'The command to execute' },
75
+ timeout: { type: 'integer', description: 'Optional timeout in milliseconds' },
76
+ },
77
+ required: ['command'],
78
+ },
79
+ },
80
+ {
81
+ name: 'read',
82
+ description: loadPrompt('read.txt'),
83
+ parameters: {
84
+ type: 'object',
85
+ properties: {
86
+ filePath: { type: 'string', description: 'The absolute path to the file or directory to read' },
87
+ offset: { type: 'integer', description: 'The line number to start reading from (1-indexed)' },
88
+ limit: { type: 'integer', description: 'The maximum number of lines to read (defaults to 2000)' },
89
+ },
90
+ required: ['filePath'],
91
+ },
92
+ },
93
+ {
94
+ name: 'edit',
95
+ description: loadPrompt('edit.txt'),
96
+ parameters: {
97
+ type: 'object',
98
+ properties: {
99
+ filePath: { type: 'string', description: 'The absolute path to the file to modify' },
100
+ oldString: { type: 'string', description: 'The text to replace' },
101
+ newString: { type: 'string', description: 'The text to replace it with (must be different from oldString)' },
102
+ replaceAll: { type: 'boolean', description: 'Replace all occurrences of oldString (default false)' },
103
+ },
104
+ required: ['filePath', 'oldString', 'newString'],
105
+ },
106
+ },
107
+ {
108
+ name: 'write',
109
+ description: loadPrompt('write.txt'),
110
+ parameters: {
111
+ type: 'object',
112
+ properties: {
113
+ filePath: { type: 'string', description: 'The absolute path to the file to write (will create the file if it does not exist, will overwrite if it does)' },
114
+ content: { type: 'string', description: 'The content to write' },
115
+ },
116
+ required: ['filePath', 'content'],
117
+ },
118
+ },
119
+ {
120
+ name: 'glob',
121
+ description: loadPrompt('glob.txt'),
122
+ parameters: {
123
+ type: 'object',
124
+ properties: {
125
+ pattern: { type: 'string', description: 'The glob pattern to match files against' },
126
+ path: { type: 'string', description: 'The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.' },
127
+ },
128
+ required: ['pattern'],
129
+ },
130
+ },
131
+ {
132
+ name: 'grep',
133
+ description: loadPrompt('grep.txt'),
134
+ parameters: {
135
+ type: 'object',
136
+ properties: {
137
+ pattern: { type: 'string', description: 'The regex pattern to search for in file contents' },
138
+ path: { type: 'string', description: 'The directory to search in. Defaults to the current working directory.' },
139
+ include: { type: 'string', description: 'File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")' },
140
+ },
141
+ required: ['pattern'],
142
+ },
143
+ },
144
+ {
145
+ name: 'list',
146
+ description: 'List files and directories in a given path. Returns entries sorted by name (directories first, then files), with type indicator and size.',
147
+ parameters: {
148
+ type: 'object',
149
+ properties: {
150
+ path: { type: 'string', description: 'The directory path to list. Defaults to the current working directory.' },
151
+ },
152
+ },
153
+ },
154
+ {
155
+ name: 'webfetch',
156
+ description: loadPrompt('webfetch.txt'),
157
+ parameters: {
158
+ type: 'object',
159
+ properties: {
160
+ url: { type: 'string', description: 'The URL to fetch' },
161
+ format: { type: 'string', enum: ['text', 'markdown', 'html'], description: 'The format to return the content in (text, markdown, or html). Defaults to markdown.' },
162
+ timeout: { type: 'integer', description: 'Optional timeout in seconds (max 120)' },
163
+ max_chars: { type: 'integer', description: 'Optional maximum characters to return' },
164
+ },
165
+ required: ['url'],
166
+ },
167
+ },
168
+ {
169
+ name: 'websearch',
170
+ description: loadPrompt('websearch.txt'),
171
+ parameters: {
172
+ type: 'object',
173
+ properties: {
174
+ query: { type: 'string', description: 'Websearch query' },
175
+ numResults: { type: 'integer', description: 'Number of search results to return (default: 8)' },
176
+ type: { type: 'string', enum: ['auto', 'fast', 'deep'], description: "Search type - 'auto': balanced search (default), 'fast': quick results, 'deep': comprehensive search" },
177
+ },
178
+ required: ['query'],
179
+ },
180
+ },
181
+ {
182
+ name: 'todowrite',
183
+ description: loadPrompt('todowrite.txt'),
184
+ parameters: {
185
+ type: 'object',
186
+ properties: {
187
+ todos: {
188
+ type: 'array',
189
+ description: 'The updated todo list',
190
+ items: {
191
+ type: 'object',
192
+ properties: {
193
+ content: { type: 'string', description: 'Brief description of the task' },
194
+ status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'cancelled'], description: 'Current status of the task' },
195
+ priority: { type: 'string', enum: ['high', 'medium', 'low'], description: 'Priority level of the task' },
196
+ },
197
+ required: ['content', 'status'],
198
+ },
199
+ },
200
+ },
201
+ required: ['todos'],
202
+ },
203
+ },
204
+ {
205
+ name: 'task',
206
+ description: loadPrompt('task.txt'),
207
+ parameters: {
208
+ type: 'object',
209
+ properties: {
210
+ description: { type: 'string', description: 'A short description of the task (max 80 chars)' },
211
+ prompt: { type: 'string', description: 'The full task description for the subagent to execute' },
212
+ subagent_type: { type: 'string', enum: ['general', 'explore'], description: 'Type of subagent to launch (options: explore, general)' },
213
+ },
214
+ required: ['description', 'prompt', 'subagent_type'],
215
+ },
216
+ },
217
+ {
218
+ name: 'skill',
219
+ description: loadPrompt('skill.txt'),
220
+ parameters: {
221
+ type: 'object',
222
+ properties: {
223
+ name: { type: 'string', description: 'The name of the skill from available_skills' },
224
+ },
225
+ required: ['name'],
226
+ },
227
+ },
228
+ {
229
+ /* Beast Sandbox'a özgü: UZUN SÜRELİ süreçleri (dev server vb.) ÇALIŞTIR
230
+ paneline devreder — tur BLOKLAMAZ. panel_run izni yalnız sandbox
231
+ ajanında allow edilir (agents.js); BC build/plan'da gizlidir. */
232
+ name: 'panel_run',
233
+ description:
234
+ 'Run a LONG-RUNNING process (dev server, background service) in the managed RUN panel instead of the blocking shell. The turn does not wait for it to finish; output streams live in the panel and can be stopped by the user. Use this instead of starting servers with bash — a blocking bash call would stall the whole job. Only ONE process runs at a time.',
235
+ parameters: {
236
+ type: 'object',
237
+ properties: {
238
+ command: { type: 'string', description: 'The command to run in the panel' },
239
+ },
240
+ required: ['command'],
241
+ },
242
+ },
243
+ ];
244
+
245
+ /* OpenAI function-call formatına çevirir (engine TOOLS formatı) */
246
+ function definitions() {
247
+ return DEFS().map((d) => ({ type: 'function', function: d }));
248
+ }
249
+
250
+ const TO_BUILTIN = {
251
+ bash: 'run_command',
252
+ read: 'read_file',
253
+ edit: 'edit_file',
254
+ write: 'write_file',
255
+ glob: 'glob',
256
+ grep: 'grep',
257
+ list: 'list_dir',
258
+ webfetch: 'webfetch',
259
+ websearch: 'web_search',
260
+ todowrite: 'todo_write',
261
+ task: 'delegate_task',
262
+ panel_run: 'panel_run',
263
+ };
264
+
265
+ /* opencode parametreleri → Beast araç parametreleri */
266
+ function execMap(name, args) {
267
+ const a = args || {};
268
+ switch (name) {
269
+ case 'read':
270
+ return { name: 'read_file', args: { path: a.filePath, offset: a.offset, limit: a.limit } };
271
+ case 'edit':
272
+ return {
273
+ name: 'edit_file',
274
+ args: { path: a.filePath, old_string: a.oldString, new_string: a.newString, replace_all: a.replaceAll === true },
275
+ };
276
+ case 'write':
277
+ return { name: 'write_file', args: { path: a.filePath, content: a.content } };
278
+ case 'bash':
279
+ return { name: 'run_command', args: { command: a.command, timeout_ms: a.timeout } };
280
+ case 'list':
281
+ return { name: 'list_dir', args: { path: a.path } };
282
+ case 'webfetch':
283
+ return { name: 'webfetch', args: { url: a.url, format: a.format, timeout: a.timeout, max_chars: a.max_chars } };
284
+ case 'websearch':
285
+ return { name: 'web_search', args: { query: a.query, max_results: a.numResults || a.max_results } };
286
+ case 'todowrite':
287
+ return { name: 'todo_write', args: { items: (a.todos || []).map(opencodeTodoToItem) } };
288
+ case 'task':
289
+ return {
290
+ name: 'delegate_task',
291
+ args: { task: a.prompt || a.description, context: a.description || '', subagent_type: a.subagent_type },
292
+ };
293
+ default:
294
+ return { name: TO_BUILTIN[name] || name, args: a };
295
+ }
296
+ }
297
+
298
+ /* opencode Todo.Info {content, status, priority} → Beast iç depo {title, status}
299
+ opencode durum makinesi: pending → in_progress → completed | cancelled
300
+ Beast iç durumu: pending | active | done */
301
+ const OC_TODO_STATUS = { pending: 'pending', in_progress: 'active', completed: 'done', cancelled: 'done' };
302
+ function opencodeTodoToItem(t) {
303
+ const it = t || {};
304
+ return {
305
+ title: String(it.content ?? '').replace(/\s+/g, ' ').trim().slice(0, 120),
306
+ status: OC_TODO_STATUS[String(it.status || 'pending')] || 'pending',
307
+ };
308
+ }
309
+
310
+ /* BC oturumunda modele sunulan opencode araç adları (permission filtresinden
311
+ önce tam set — opencode registry builtin sırası) */
312
+ function names() {
313
+ return DEFS().map((d) => d.name);
314
+ }
315
+
316
+ module.exports = { definitions, names, execMap, TO_BUILTIN, opencodeTodoToItem };