beast-agent 1.1.0 → 1.2.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.
package/src/main.js CHANGED
@@ -662,6 +662,7 @@ function waSlashHelp() {
662
662
  '• */notes* – bu oturumun notlarını göster',
663
663
  '• */notify* on|off – hata mail bildirimini aç/kapa',
664
664
  '• */think* <0-5> – düşünme seviyesi (0 kapalı · 1 low · 2 medium · 3 high · 4 xhigh · 5 max)',
665
+ '• */agent* [isim] – özel ajan bağla/listele (%APPDATA%\beast\agents)',
665
666
  '• */clear* – bu oturumun geçmişini temizle',
666
667
  '• */screenshot* – masaüstü ekran görüntüsünü gönder',
667
668
  '• */rule* <metin> – kalıcı kural ekle (*/rules*: liste)',
@@ -3925,6 +3926,32 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
3925
3926
  desktopEcho(sessionId, t, r && r.error ? r.error : r ? r.text : thinkStatusText());
3926
3927
  return true;
3927
3928
  }
3929
+ if (t === '/agent' || t.startsWith('/agent ')) {
3930
+ /* opencode agent port: özel ajan tanımları (%APPDATA%\beast\agents\*.md) */
3931
+ const arg = t.slice(6).trim();
3932
+ if (!arg) {
3933
+ const list = engine.listAgents();
3934
+ desktopEcho(
3935
+ sessionId,
3936
+ t,
3937
+ list.length
3938
+ ? '**Özel ajanlar:**\n' + list.map((d) => `- **${d.name}**${d.model ? ' · ' + d.model : ''}${d.steps ? ' · ' + d.steps + ' tur' : ''}${d.tools ? ' · ' + d.tools.length + ' araç' : ''}`).join('\n') + '\n\nBağlamak için: **/agent <isim>** · ayırmak için: **/agent off**'
3939
+ : 'Özel ajan yok — `%APPDATA%\\beast\\agents\\` klasörüne `<isim>.md` tanımı koy (örnek dosya orada).'
3940
+ );
3941
+ return true;
3942
+ }
3943
+ const r = engine.setSessionAgent(sessionId, arg);
3944
+ desktopEcho(
3945
+ sessionId,
3946
+ t,
3947
+ r.ok
3948
+ ? r.agent
3949
+ ? `**Ajan bağlandı: ${r.agent}**${r.model ? ' · model: ' + r.model : ''}${r.steps ? ' · ' + r.steps + ' tur' : ''}${r.tools ? ' · araçlar: ' + r.tools.join(', ') : ''}`
3950
+ : '**Ajan bağlantısı kaldırıldı** — oturum normal akışa döndü.'
3951
+ : r.error
3952
+ );
3953
+ return true;
3954
+ }
3928
3955
  if (t === '/clear') {
3929
3956
  /* #25 artık GERÇEK silme: oturum dosyasındaki mesajlar + notlar silinir,
3930
3957
  kod/meta/todolar korunur. Ekran da temizlenir ('clear' olayı). */
@@ -4008,6 +4035,7 @@ function desktopSlashHelp() {
4008
4035
  '**/change [n]** – modelleri listele · n. modele geç',
4009
4036
  '**/model [isim]** – aktif modeli göster / değiştir',
4010
4037
  '**/think 0-5** – düşünme seviyesi (0 kapalı · 5 max)',
4038
+ '**/agent [isim]** – özel ajan bağla / listele (%APPDATA%\\beast\\agents\\*.md)',
4011
4039
  '**/clear** – oturum geçmişini gerçekten sil (kod korunur)',
4012
4040
  '**/notes** – bu oturumun notlarını göster',
4013
4041
  '**/rule <metin>** – kalıcı kural ekle · **/rules** – listele',
@@ -4458,9 +4486,22 @@ ipcMain.handle('model:set', (_e, sel) => {
4458
4486
  saveSettings();
4459
4487
  engine.setModelOverride(sel);
4460
4488
  }
4489
+ /* IDE/Beast Code dahil tüm paneller anlık haberdar olsun — picker +
4490
+ durum etiketleri kapatıp açmadan güncellenir */
4491
+ try { win && !win.isDestroyed() && win.webContents.send('agent:event', { type: 'modelChanged' }); } catch {}
4461
4492
  return engine.publicState();
4462
4493
  });
4463
4494
 
4495
+ /* Paralel ajan geçmişini TOPLUCA sil (rail başlığındaki çöp ikonu) */
4496
+ ipcMain.handle('agents:clearAll', () => {
4497
+ try {
4498
+ const removed = engine.clearAllBgJobs();
4499
+ return { ok: true, removed };
4500
+ } catch (e) {
4501
+ return { ok: false, error: String((e && e.message) || e) };
4502
+ }
4503
+ });
4504
+
4464
4505
  ipcMain.handle('model:role', (_e, map) => {
4465
4506
  const roleModels = {};
4466
4507
  const allowed = {};
package/src/preload.js CHANGED
@@ -180,6 +180,7 @@ contextBridge.exposeInMainWorld('beast', {
180
180
  idePreview: () => ipcRenderer.invoke('ide:preview'),
181
181
  idePreviewFile: (rel) => ipcRenderer.invoke('ide:previewFile', rel),
182
182
  idePreviewUrl: (url) => ipcRenderer.invoke('ide:previewUrl', url),
183
+ agentsClearAll: () => ipcRenderer.invoke('agents:clearAll'),
183
184
  ideDelete: (rel) => ipcRenderer.invoke('ide:delete', rel),
184
185
  onWaEvent: (cb) => ipcRenderer.on('wa:event', (_e, ev) => cb(ev)),
185
186
  onEvent: (cb) => ipcRenderer.on('agent:event', (_e, ev) => cb(ev)),
@@ -130,6 +130,7 @@
130
130
  </div>
131
131
  <div id="bcOut"></div>
132
132
  <div id="bcTodoWrap" hidden></div>
133
+ <div id="bcStatus" hidden></div>
133
134
  <div id="bcInputRow">
134
135
  <span id="bcPrompt">code&gt;</span>
135
136
  <input id="bcInput" spellcheck="false" autocomplete="off" placeholder="Beast Code mesajı yaz — Enter ile gönder…" data-i18n-ph="bc_ph" />
@@ -140,7 +141,12 @@
140
141
  </main>
141
142
 
142
143
  <aside id="rail">
143
- <div id="railHead" data-i18n="labelParallel">Paralel Ajanlar</div>
144
+ <div id="railHead">
145
+ <span id="railTitle" data-i18n="labelParallel">Paralel Ajanlar</span>
146
+ <button id="railClear" title="Tüm paralel ajan geçmişini sil" aria-label="Tüm paralel ajan geçmişini sil">
147
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>
148
+ </button>
149
+ </div>
144
150
  <div id="railList"></div>
145
151
  <div id="sideFoot2">
146
152
  <span class="foot-label2" data-i18n="labelParallel">Paralel Ajanlar</span>
@@ -42,6 +42,7 @@ const els = {
42
42
  eyeBtn: $('#eyeBtn'),
43
43
  netDot: $('#netDot'),
44
44
  railBtn: $('#railBtn'),
45
+ railClear: $('#railClear'),
45
46
  watchBtn: $('#watchBtn'),
46
47
  cronBtn: $('#cronBtn'),
47
48
  watchOverlay: $('#watchOverlay'),
@@ -76,6 +77,7 @@ const els = {
76
77
  bcClear: $('#bcClear'),
77
78
  bcNew: $('#bcNew'),
78
79
  bcTodoWrap: $('#bcTodoWrap'),
80
+ bcStatus: $('#bcStatus'),
79
81
  codeTabs: $('#codeTabs'),
80
82
  codeTa: $('#codeTa'),
81
83
  codeGutter: $('#codeGutter'),
@@ -411,6 +413,176 @@ function addStopNote(reason) {
411
413
  scrollDown(true);
412
414
  }
413
415
 
416
+ /* ---------------- opencode diff görünümü (BİREBİR port) ----------------
417
+ edit_file/write_file sonuçları chat'te ve Beast Code panelinde opencode'un
418
+ permission/diff ekranı gibi gösterilir: SOLDa kırmızı (eski/silinen),
419
+ SAĞDA yeşil (yeni/eklenen) — dar alanda birleşik (unified) görünüme düşer.
420
+ Motor: LCS tabanlı satır diff'i (opencode npm "diff" paketinin diffLines
421
+ karşılığı) + del/add blok eşleştirmesi (split görünüm hizalaması). */
422
+ function diffOps(aText, bText) {
423
+ const a = aText ? String(aText).split('\n') : [];
424
+ const b = bText ? String(bText).split('\n') : [];
425
+ let p = 0;
426
+ while (p < a.length && p < b.length && a[p] === b[p]) p++;
427
+ let ea = a.length - 1;
428
+ let eb = b.length - 1;
429
+ while (ea >= p && eb >= p && a[ea] === b[eb]) { ea--; eb--; }
430
+ const ops = [];
431
+ for (let i = 0; i < p; i++) ops.push({ t: 'ctx', a: a[i], b: b[i] });
432
+ const midA = a.slice(p, ea + 1);
433
+ const midB = b.slice(p, eb + 1);
434
+ const n = midA.length;
435
+ const m = midB.length;
436
+ if (n * m > 400000 || n > 1500 || m > 1500) {
437
+ for (const x of midA) ops.push({ t: 'del', a: x });
438
+ for (const y of midB) ops.push({ t: 'add', b: y });
439
+ } else {
440
+ const w = m + 1;
441
+ const dp = new Int32Array((n + 1) * w);
442
+ for (let i = n - 1; i >= 0; i--) {
443
+ for (let j = m - 1; j >= 0; j--) {
444
+ dp[i * w + j] =
445
+ midA[i] === midB[j]
446
+ ? dp[(i + 1) * w + j + 1] + 1
447
+ : Math.max(dp[(i + 1) * w + j], dp[i * w + j + 1]);
448
+ }
449
+ }
450
+ let i = 0;
451
+ let j = 0;
452
+ while (i < n && j < m) {
453
+ if (midA[i] === midB[j]) { ops.push({ t: 'ctx', a: midA[i], b: midB[j] }); i++; j++; }
454
+ else if (dp[(i + 1) * w + j] >= dp[i * w + j + 1]) { ops.push({ t: 'del', a: midA[i] }); i++; }
455
+ else { ops.push({ t: 'add', b: midB[j] }); j++; }
456
+ }
457
+ while (i < n) ops.push({ t: 'del', a: midA[i++] });
458
+ while (j < m) ops.push({ t: 'add', b: midB[j++] });
459
+ }
460
+ for (let k = ea + 1; k < a.length; k++) ops.push({ t: 'ctx', a: a[k], b: b[k] });
461
+ return ops;
462
+ }
463
+
464
+ /* del/add bloklarını yan yana hizalar: split görünümde sol satır ile sağ satır
465
+ aynı yükseklikte durur; eksik taraf boş "space" satırla doldurulur */
466
+ function diffSplitRows(ops, startLine) {
467
+ const rows = [];
468
+ let oldNo = Math.max(1, startLine || 1);
469
+ let newNo = oldNo;
470
+ let i = 0;
471
+ while (i < ops.length) {
472
+ const op = ops[i];
473
+ if (op.t === 'ctx') {
474
+ rows.push({ l: { no: oldNo++, text: op.a, cls: 'ctx' }, r: { no: newNo++, text: op.b, cls: 'ctx' } });
475
+ i++;
476
+ continue;
477
+ }
478
+ const dels = [];
479
+ const adds = [];
480
+ while (i < ops.length && ops[i].t !== 'ctx') {
481
+ if (ops[i].t === 'del') dels.push(ops[i].a);
482
+ else adds.push(ops[i].b);
483
+ i++;
484
+ }
485
+ const max = Math.max(dels.length, adds.length);
486
+ for (let k = 0; k < max; k++) {
487
+ rows.push({
488
+ l: k < dels.length ? { no: oldNo++, text: dels[k], cls: 'del' } : { no: '', text: '', cls: 'space' },
489
+ r: k < adds.length ? { no: newNo++, text: adds[k], cls: 'add' } : { no: '', text: '', cls: 'space' },
490
+ });
491
+ }
492
+ }
493
+ return rows;
494
+ }
495
+
496
+ const DIFF_RENDER_MAX_ROWS = 400; /* dev değişikliklerde DOM şişmesin */
497
+
498
+ function diffLineEl(no, text, cls, sign) {
499
+ const line = document.createElement('div');
500
+ line.className = 'diff-line ' + cls;
501
+ const num = document.createElement('span');
502
+ num.className = 'diff-num';
503
+ num.textContent = String(no);
504
+ const code = document.createElement('span');
505
+ code.className = 'diff-code';
506
+ code.textContent = (sign || '') + text;
507
+ line.appendChild(num);
508
+ line.appendChild(code);
509
+ return line;
510
+ }
511
+
512
+ /* opencode <diff view="split|unified"> karşılığı: genişlik >= 620px ise
513
+ yan-yana (solda kırmızı eski / sağda yeşil yeni), dar ise birleşik */
514
+ function buildDiffEl(diff, availWidth) {
515
+ const wrap = document.createElement('div');
516
+ wrap.className = 'diffbox';
517
+ const head = document.createElement('div');
518
+ head.className = 'diff-head';
519
+ const fname = document.createElement('span');
520
+ fname.className = 'diff-file';
521
+ fname.textContent = String(diff.path || '');
522
+ const badge = document.createElement('span');
523
+ badge.className = 'diff-badge';
524
+ const a = document.createElement('span');
525
+ a.className = 'diff-badge-add';
526
+ a.textContent = '+' + (diff.additions || 0);
527
+ const d = document.createElement('span');
528
+ d.className = 'diff-badge-del';
529
+ d.textContent = '−' + (diff.deletions || 0);
530
+ badge.appendChild(a);
531
+ badge.appendChild(d);
532
+ head.appendChild(fname);
533
+ head.appendChild(badge);
534
+ wrap.appendChild(head);
535
+
536
+ const ops = diffOps(diff.before, diff.after);
537
+ const split = (availWidth || 0) >= 620;
538
+ const view = document.createElement('div');
539
+ view.className = 'diff ' + (split ? 'diff-split' : 'diff-uni');
540
+ if (split) {
541
+ const rows = diffSplitRows(ops, diff.startLine);
542
+ const left = document.createElement('div');
543
+ left.className = 'diff-side old';
544
+ const right = document.createElement('div');
545
+ right.className = 'diff-side new';
546
+ const capped = rows.length > DIFF_RENDER_MAX_ROWS;
547
+ for (const row of rows.slice(0, DIFF_RENDER_MAX_ROWS)) {
548
+ left.appendChild(diffLineEl(row.l.no, row.l.text, row.l.cls, ''));
549
+ right.appendChild(diffLineEl(row.r.no, row.r.text, row.r.cls, ''));
550
+ }
551
+ view.appendChild(left);
552
+ view.appendChild(right);
553
+ if (capped) {
554
+ const note = document.createElement('div');
555
+ note.className = 'diff-more';
556
+ note.textContent = `… ${rows.length - DIFF_RENDER_MAX_ROWS} satır daha (dosyada gör)`;
557
+ view.appendChild(note);
558
+ }
559
+ } else {
560
+ let oldNo = Math.max(1, diff.startLine || 1);
561
+ let newNo = oldNo;
562
+ const capped = ops.length > DIFF_RENDER_MAX_ROWS;
563
+ for (const op of ops.slice(0, DIFF_RENDER_MAX_ROWS)) {
564
+ if (op.t === 'ctx') {
565
+ view.appendChild(diffLineEl(newNo, op.b, 'ctx', ' '));
566
+ oldNo++; newNo++;
567
+ } else if (op.t === 'del') {
568
+ view.appendChild(diffLineEl(oldNo, op.a, 'del', '-'));
569
+ oldNo++;
570
+ } else {
571
+ view.appendChild(diffLineEl(newNo, op.b, 'add', '+'));
572
+ newNo++;
573
+ }
574
+ }
575
+ if (capped) {
576
+ const note = document.createElement('div');
577
+ note.className = 'diff-more';
578
+ note.textContent = `… ${ops.length - DIFF_RENDER_MAX_ROWS} satır daha (dosyada gör)`;
579
+ view.appendChild(note);
580
+ }
581
+ }
582
+ wrap.appendChild(view);
583
+ return wrap;
584
+ }
585
+
414
586
  /* ---------------- tool cards ---------------- */
415
587
  /* Ard arda gelen araç kartları TEK kutuda toplanır (.tool-box): çalışırken
416
588
  lacivert outline döner, gövde max 420px scroll'lu. Grup; tool_calls İÇEREN
@@ -425,7 +597,9 @@ function closeChatToolGroup() {
425
597
  function argSummary(name, args) {
426
598
  try {
427
599
  if (name === 'run_command') return String(args.command || '');
428
- if (name === 'read_file' || name === 'write_file') return String(args.path || '');
600
+ if (name === 'read_file' || name === 'write_file' || name === 'edit_file') return String(args.path || '');
601
+ if (name === 'grep') return String(args.pattern || '');
602
+ if (name === 'glob') return String(args.pattern || '');
429
603
  if (name === 'list_dir') return String(args.path || '.');
430
604
  if (name === 'memory_write' || name === 'user_write') return String(args.text || '').slice(0, 80);
431
605
  return JSON.stringify(args).slice(0, 100);
@@ -468,7 +642,10 @@ function addToolCard(callId, name, args) {
468
642
  return card;
469
643
  }
470
644
 
471
- function finishToolCard(callId, ok, result) {
645
+ /* opencode tool-end portu: edit/write sonucu ham JSON yerine kırmızı/yeşil
646
+ DIFF olarak çizilir (opencode'un permission/diff ekranı); diğer araçlar
647
+ eskisi gibi düz metin gösterir */
648
+ function finishToolCard(callId, ok, result, diff) {
472
649
  const sel = callId
473
650
  ? `.tool-card[data-call-id="${CSS.escape(callId)}"]`
474
651
  : null;
@@ -479,8 +656,29 @@ function finishToolCard(callId, ok, result) {
479
656
  st.classList.add(ok ? 'ok' : 'err');
480
657
  st.textContent = ok ? 'tamam' : 'hata';
481
658
  const body = card.querySelector('.tool-body');
482
- body.textContent = String(result || '(çıktı yok)').slice(0, 4000);
483
- if (String(result || '').length <= 300) body.classList.add('open');
659
+ if (diff && diff.path) {
660
+ /* başlığa +/- rozetleri (opencode filediff additions/deletions) */
661
+ const head = card.querySelector('.tool-head');
662
+ if (head && !head.querySelector('.diff-badge')) {
663
+ const badge = document.createElement('span');
664
+ badge.className = 'diff-badge';
665
+ const a = document.createElement('span');
666
+ a.className = 'diff-badge-add';
667
+ a.textContent = '+' + (diff.additions || 0);
668
+ const d = document.createElement('span');
669
+ d.className = 'diff-badge-del';
670
+ d.textContent = '−' + (diff.deletions || 0);
671
+ badge.appendChild(a);
672
+ badge.appendChild(d);
673
+ head.insertBefore(badge, st);
674
+ }
675
+ body.textContent = '';
676
+ body.classList.add('open', 'diff-body');
677
+ body.appendChild(buildDiffEl(diff, body.clientWidth || 800));
678
+ } else {
679
+ body.textContent = String(result || '(çıktı yok)').slice(0, 4000);
680
+ if (String(result || '').length <= 300) body.classList.add('open');
681
+ }
484
682
  /* ring burada söndürülmez — iş bitene kadar (closeChatToolGroup) döner */
485
683
  if (chatToolGroup && chatToolGroup.body.contains(card)) {
486
684
  chatToolGroup.body.scrollTop = chatToolGroup.body.scrollHeight;
@@ -621,7 +819,7 @@ async function openSession(id) {
621
819
  addToolCard(tc.id, tc.function.name, args);
622
820
  const out = s.messages[i + 1];
623
821
  if (out && out.role === 'tool' && out.tool_call_id === tc.id) {
624
- finishToolCard(tc.id, true, out.content);
822
+ finishToolCard(tc.id, true, out.content, out.diffView);
625
823
  i++;
626
824
  } else {
627
825
  finishToolCard(tc.id, false, '(sonuç yok)');
@@ -3906,11 +4104,11 @@ function onEvent(ev) {
3906
4104
  addToolCard(ev.callId, ev.name, ev.args);
3907
4105
  setStatus(ev.name + '…');
3908
4106
  termAgentEvent(ev);
3909
- /* ajan dosya yazarsa açık IDE sekmesini izlemeye al */
3910
- if (ev.name === 'write_file' && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
4107
+ /* ajan dosya yazarsa/editlerse açık IDE sekmesini izlemeye al */
4108
+ if ((ev.name === 'write_file' || ev.name === 'edit_file') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
3911
4109
  break;
3912
4110
  case 'tool-end':
3913
- finishToolCard(ev.callId, ev.ok, ev.result);
4111
+ finishToolCard(ev.callId, ev.ok, ev.result, ev.diff);
3914
4112
  setStatus('düşünüyor…');
3915
4113
  termAgentEvent(ev);
3916
4114
  if (codeWriteWatch.has(ev.callId)) {
@@ -4843,9 +5041,29 @@ async function init() {
4843
5041
  });
4844
5042
  }
4845
5043
  if (els.railBtn) els.railBtn.addEventListener('click', () => {
5044
+ const willOpen = document.body.classList.contains('rail-hidden');
5045
+ /* IDE modunda rail istenirse açık tarayıcı yerini bırakır — ikisi aynı
5046
+ sağ dock'u paylaşır; tarayıcı kapanma olayı rail'i tekrar KAPATMASIN
5047
+ diye railPrefBeforeBrowser=true ile bırakılır */
5048
+ if (willOpen && ideModeOn() && document.body.classList.contains('browser-open')) {
5049
+ railPrefBeforeBrowser = true;
5050
+ els.bbClose.click();
5051
+ }
4846
5052
  toggleRail(!document.body.classList.contains('rail-hidden'));
4847
5053
  railPrefBeforeBrowser = !document.body.classList.contains('rail-hidden');
4848
5054
  });
5055
+ if (els.railClear) {
5056
+ els.railClear.addEventListener('click', async () => {
5057
+ if (!confirm('TÜM paralel ajan geçmişi silinsin mi?\n(Çalışan ajanlar da iptal edilir — geri alınamaz)')) return;
5058
+ try {
5059
+ const r = await beast.agentsClearAll();
5060
+ if (r && r.ok) toast('Paralel ajan geçmişi silindi' + (r.removed ? ' — ' + r.removed + ' iş' : ''));
5061
+ else toast('Silinemedi: ' + ((r && r.error) || '?'));
5062
+ } catch (e) {
5063
+ toast('Silinemedi: ' + String((e && e.message) || e));
5064
+ }
5065
+ });
5066
+ }
4849
5067
  els.bbBack.addEventListener('click', () => beast.browserCtrl('back'));
4850
5068
  els.bbFwd.addEventListener('click', () => beast.browserCtrl('forward'));
4851
5069
  els.bbReload.addEventListener('click', () => beast.browserCtrl('reload'));
@@ -5828,6 +6046,21 @@ async function renderFileTree() {
5828
6046
  await build('', 0, tree);
5829
6047
  }
5830
6048
 
6049
+ /* Ağaç otomatik tazeleme: ajan dosya/klasör ürettikçe soldaki panel KENDİ
6050
+ kendisi yenilenir — elle yenileme yok. Açık klasörler (ideState.open)
6051
+ korunur; debounce paralel araç patlamalarını tek çizime indirir. */
6052
+ let ideTreeTimer = null;
6053
+ function ideRefreshTree() {
6054
+ if (ideTreeTimer) return;
6055
+ ideTreeTimer = setTimeout(() => {
6056
+ ideTreeTimer = null;
6057
+ try {
6058
+ ideState.cache.clear();
6059
+ renderFileTree().catch(() => {});
6060
+ } catch {}
6061
+ }, 600);
6062
+ }
6063
+
5831
6064
  /* ---------- sekmeli kod editörü (VS Code düzeni) ----------
5832
6065
  Dosya tıklaması MODAL değil, codePane içinde SEKME açar.
5833
6066
  Yanında Beast Code chat'i, onun sağındaki preview tarayıcı durur. */
@@ -6270,6 +6503,20 @@ function bcStreamDelta(delta) {
6270
6503
  /* İş bitti → preview açık ve IDE modundaysa workspace sayfasını otomatik yenile */
6271
6504
  let bcPrevAt = 0;
6272
6505
 
6506
+ /* Beast Code durum balonu: düşünüyor… / araç adları / durum satırları —
6507
+ chat inputun üstündeki balonun BC panelindeki karşılığı */
6508
+ function bcStatusShow(text) {
6509
+ if (!els.bcStatus) return;
6510
+ const t = String(text || '').trim();
6511
+ if (!t) return;
6512
+ els.bcStatus.textContent = t;
6513
+ els.bcStatus.hidden = false;
6514
+ }
6515
+
6516
+ function bcStatusHide() {
6517
+ if (els.bcStatus) els.bcStatus.hidden = true;
6518
+ }
6519
+
6273
6520
  function bcRefreshPreview() {
6274
6521
  const now = Date.now();
6275
6522
  if (now - bcPrevAt < 1500) return; /* done + idle çift tetiklemesin */
@@ -6354,7 +6601,7 @@ function bcToolBoxStart(callId, name, args) {
6354
6601
  bcBodyScroll(body);
6355
6602
  }
6356
6603
 
6357
- function bcToolBoxEnd(callId, ok, result) {
6604
+ function bcToolBoxEnd(callId, ok, result, diff) {
6358
6605
  bcFlushStream();
6359
6606
  const t = (callId && bcTools.get(callId)) || null;
6360
6607
  if (callId) bcTools.delete(callId);
@@ -6365,7 +6612,27 @@ function bcToolBoxEnd(callId, ok, result) {
6365
6612
  sec.classList.add('done');
6366
6613
  if (!ok) sec.classList.add('failed');
6367
6614
  const pre = sec.querySelector('.bc-toolout');
6368
- if (out) {
6615
+ if (diff && diff.path) {
6616
+ /* opencode diff görünümü: başlığa +/- rozeti, gövdeye kırmızı/yeşil diff */
6617
+ const headEl = sec.querySelector('.bc-toolhead');
6618
+ if (headEl && !headEl.querySelector('.diff-badge')) {
6619
+ const badge = document.createElement('span');
6620
+ badge.className = 'diff-badge';
6621
+ const a = document.createElement('span');
6622
+ a.className = 'diff-badge-add';
6623
+ a.textContent = '+' + (diff.additions || 0);
6624
+ const d = document.createElement('span');
6625
+ d.className = 'diff-badge-del';
6626
+ d.textContent = '−' + (diff.deletions || 0);
6627
+ badge.appendChild(a);
6628
+ badge.appendChild(d);
6629
+ headEl.appendChild(badge);
6630
+ }
6631
+ pre.hidden = false;
6632
+ pre.textContent = '';
6633
+ pre.classList.add('bc-diffview');
6634
+ pre.appendChild(buildDiffEl(diff, pre.clientWidth || 480));
6635
+ } else if (out) {
6369
6636
  pre.hidden = false;
6370
6637
  pre.textContent = out.length > 4000 ? out.slice(0, 4000) + '\n… (kesildi)' : out;
6371
6638
  } else {
@@ -6465,16 +6732,20 @@ function bcIngest(ev) {
6465
6732
  break;
6466
6733
  case 'tool-start':
6467
6734
  bcToolBoxStart(ev.callId, ev.name, ev.args);
6468
- /* ajan dosya yazarsa açık IDE sekmesini izlemeye al */
6469
- if (ev.name === 'write_file' && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
6735
+ /* ajan dosya yazarsa/editlerse açık IDE sekmesini izlemeye al */
6736
+ if ((ev.name === 'write_file' || ev.name === 'edit_file') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
6470
6737
  break;
6471
6738
  case 'tool-end':
6472
- bcToolBoxEnd(ev.callId, ev.ok, ev.result);
6739
+ bcToolBoxEnd(ev.callId, ev.ok, ev.result, ev.diff);
6473
6740
  if (codeWriteWatch.has(ev.callId)) {
6474
6741
  const wp = codeWriteWatch.get(ev.callId);
6475
6742
  codeWriteWatch.delete(ev.callId);
6476
6743
  codeReloadIfOpen(wp);
6477
6744
  }
6745
+ /* dosya/klasör üreten araçlardan sonra soldaki ağaç otomatik tazelensin */
6746
+ if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'run_command' || ev.name === 'python_run') {
6747
+ ideRefreshTree();
6748
+ }
6478
6749
  break;
6479
6750
  case 'bc-preview':
6480
6751
  /* iş bitince üretilen site/app dahili tarayıcıda CANLI açılır */
@@ -6493,8 +6764,10 @@ function bcIngest(ev) {
6493
6764
  bcFlushStream();
6494
6765
  bcCloseToolGroup();
6495
6766
  bcSetBusy(false);
6767
+ bcStatusHide();
6496
6768
  /* iptal/durdurma SEBEBİ panelde de görünür */
6497
6769
  bcLine(ev.aborted ? 't-err' : 't-dim', ev.aborted ? '[durduruldu — sebep: ' + (ev.reason || 'sebep belirtilmedi') + ']' : '(tamamlandı)');
6770
+ ideRefreshTree(); /* son güvenlik tazelemesi — izlenmeyen yoldan üretilen dosyalar da düşsün */
6498
6771
  bcRefreshPreview();
6499
6772
  if (ideModeOn() && els.bcInput) els.bcInput.focus();
6500
6773
  break;
@@ -6502,6 +6775,7 @@ function bcIngest(ev) {
6502
6775
  bcFlushStream();
6503
6776
  bcCloseToolGroup();
6504
6777
  bcSetBusy(false);
6778
+ bcStatusHide();
6505
6779
  bcLine('t-err', '[hata] ' + (ev.error || ''));
6506
6780
  break;
6507
6781
  case 'status':
@@ -6510,7 +6784,11 @@ function bcIngest(ev) {
6510
6784
  if (ev.status === 'idle') {
6511
6785
  bcCloseToolGroup();
6512
6786
  bcSetBusy(false);
6787
+ bcStatusHide();
6513
6788
  bcRefreshPreview();
6789
+ } else {
6790
+ /* düşünüyor… / araç adı / bağlam durumu — input üstünde canlı balon */
6791
+ bcStatusShow(ev.status === 'thinking' ? 'düşünüyor…' : String(ev.status || ''));
6514
6792
  }
6515
6793
  break;
6516
6794
  }