beast-agent 1.7.0 → 1.8.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.
@@ -87,6 +87,18 @@ if (process.argv[2] === 'update') {
87
87
  process.exit(0);
88
88
  }
89
89
 
90
+ /* SearXNG: yerel arama motoru — kur + arka planda başlat (127.0.0.1:8888)
91
+ beast searxng → gerekirse kur, başlat
92
+ beast searxng status → durum
93
+ beast searxng stop → kulla */
94
+ if (process.argv[2] === 'searxng') {
95
+ require('../src/agent/searxng').cli(process.argv.slice(3)).catch((e) => {
96
+ console.error('\u2717 ' + String((e && e.message) || e));
97
+ process.exit(1);
98
+ });
99
+ return;
100
+ }
101
+
90
102
  /* detached başlatıcı + kendini onaran electron:
91
103
  taze makinalarda npm postinstall sırasında electron binary indirmesi
92
104
  sessizce başarısız olmuş olabilir → burada otomatik tamir edilir. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "1.7.0",
4
+ "version": "1.8.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ 'use strict';
2
2
 
3
3
  /* Beast engine: sessions, frugal context, streaming tool loop.
4
4
  v2: token-bazlı bağlam bütçesi (kullanım kalibrasyonlu), paralel tool
@@ -1519,7 +1519,7 @@ class Engine {
1519
1519
  'Tarayıcı eylemlerinin yanıtındaki recent günlüğü ve navigated bilgisini takip et; eylem yanıtları zaten taze snapshot içerir — refler tutarsız görünürse yeni snapshot al.',
1520
1520
  'CONUŞMA ODAĞI SENDE KALSIN: kullanıcı seninle konuşurken iş çıkmışsa — uzun da olsa UFACIK da (tek komutluk dizin listesi, tek dosya okuma, tek arama…) — run_background ile PARALEL ajana devret; ana sohbet hiçbir işi beklemez; bittiğinde özet otomatik düşer.',
1521
1521
  'Python işleri için python_run kullan: küçük betikler inline code ile; tekrarlayan işler %APPDATA%\\beast\\scripts klasöründeki dosyalarla (ör. news.py = RSS haber toplayıcı: args ["--limit","8","--json"]). Python kurulu olmasa bile ilk çağrıda taşınabilir gömülü runtime otomatik iner.',
1522
- 'PYTHON DURUMU: makinede sistem Python\'u görünmese bile ŞAŞIRMA ve "python yok" DEME — python_run aracı kendi taşınabilir runtime\'ını (%APPDATA%\\beast\\py\\python.exe) otomatik indirir/kullanır ve bu klasör run_command PATH\'inde önceliklidir; yani run_command içinde de `python` çalışır. Ham Google/Bing scrape yerine önce web_search aracını kullan (zaten Obscura stealth tarayıcı + TinyFish + Python çoklu-motor destekli), script gerektiğinde python_run yaz.',
1522
+ 'PYTHON DURUMU: makinede sistem Python\'u görünmese bile ŞAŞIRMA ve "python yok" DEME — python_run aracı kendi taşınabilir runtime\'ını (%APPDATA%\\beast\\py\\python.exe) otomatik indirir/kullanır ve bu klasör run_command PATH\'inde önceliklidir; yani run_command içinde de `python` çalışır. Ham Google/Bing scrape yerine önce web_search aracını kullan (SearXNG + stealth TLS + TinyFish + Python çoklu-motor destekli), script gerektiğinde python_run yaz.',
1523
1523
  'PDF ÇIKTI KURALI: PDF üretirken pip\u2019ten pdf paketi (fpdf, fpdf2, markdown-pdf, weasyprint, reportlab vb.) KURMA/KULLANMA — bunlar Türkçe karakterleri bozar. Doğru kit Node tarafında ZATEN kurulu: `pdf-lib` + `@pdf-lib/fontkit` (Türkçe font gömme) ve `pdfkit`. python_run ile DEĞİL; write_file ile .js script yazıp run_command ile `node script.js` çalıştır. md→pdf çevirici YOKTUR ve kurulmaz: kullanıcıya rapor/özet/belge çıktısı vereceksen .md dosyası gönderme — aynı içeriği DOĞRUDAN pdf-lib/pdfkit ile PDF olarak üret ve send_file ile o PDF\u2019i gönder. Ayrıntılı örnekler: pdf skill\u2019i (SKILL.md).',
1524
1524
  'Eski bir hafıza kaydına ihtiyacın olursa memory_search ile ara; kalıcı bilgi/birikim için kb_search kullan, yeni bilgi öğrenirsen kb_add ile kaynak belirt.',
1525
1525
  'Bir oturumda 3+ kez memory_write yaptıysan iş bitince memory_hygiene çağır (duplike/eskime temizliği).',
@@ -3685,10 +3685,10 @@ class Engine {
3685
3685
 
3686
3686
  /* web_search zinciri — web_search VE deep_search aynı zinciri kullanır.
3687
3687
  Sıra + aç/kapa Ayarlar → Web Arama'dan değiştirilir (tools.searchChainWeb):
3688
- varsayılan: 1) dahili tarayıcı (DİREK GOOGLE) 2) Obscura (stealth headless
3689
- DuckDuckGo; otomatik kurulur, varsayılan AKTİF) 3) TinyFish (anahtar
3690
- girildiyse) 4) python çoklu-motor. Tarayıcı CAPTCHA/trafik verirse 10 dk
3691
- atlanır — sıradaki motor hemen devreye girer. */
3688
+ varsayılan: 1) SearXNG (yerel, ayaktaysa otomatik öne alınır) 2) stealth
3689
+ (curl_cffi Chrome TLS taklidi DDG html) 3) dahili tarayıcı (DİREK
3690
+ GOOGLE) 4) TinyFish (anahtar girildiyse) 5) python çoklu-motor. Tarayıcı
3691
+ CAPTCHA/trafik verirse 10 dk atlanır — sıradaki motor hemen devreye girer. */
3692
3692
  _webSearchChain(q, n, sessionId, signal) {
3693
3693
  const browserUsable = this.browser && typeof this.browser.search === 'function';
3694
3694
  return tools.searchChainWeb(q, n, {
@@ -4086,7 +4086,7 @@ class Engine {
4086
4086
  if (name === 'deep_search') {
4087
4087
  /* agentic derin araştırma: çoklu sorgu + gizli tarayıcıda sayfa okuma.
4088
4088
  ARAMA zinciri web_search ile BİREBİR AYNI — sıralı zincir
4089
- (tarayıcıObscura → TinyFish → python; Ayarlar'dan değiştirilir). */
4089
+ (SearXNGstealthtarayıcı → TinyFish → python; Ayarlar'dan değiştirilir).. */
4090
4090
  try {
4091
4091
  emitSafe(this, sessionId, { type: 'status', status: 'derin araştırma: çoklu sorgu + gizli sayfa okuma' });
4092
4092
  } catch {}
package/src/agent/llm.js CHANGED
@@ -249,6 +249,44 @@ async function streamOnce(sel, body, { signal, onDelta, onRetry } = {}, omitReas
249
249
  signal.addEventListener('abort', onAbort, { once: true });
250
250
  }
251
251
  try {
252
+ /* tek satır SSE olayını işle — hem okuma döngüsü hem SON FLUSH kullanır */
253
+ const handleLine = (line) => {
254
+ line = line.trim();
255
+ if (!line.startsWith('data:')) return;
256
+ const data = line.slice(5).trim();
257
+ if (!data || data === '[DONE]') return;
258
+
259
+ let json;
260
+ try {
261
+ json = JSON.parse(data);
262
+ } catch {
263
+ return;
264
+ }
265
+
266
+ if (json.usage) state.usage = json.usage;
267
+ const ch = json.choices && json.choices[0];
268
+ if (!ch) return;
269
+ if (ch.finish_reason) state.finishReason = ch.finish_reason;
270
+ const d = ch.delta || {};
271
+ if (d.content) {
272
+ state.content += d.content;
273
+ onDelta && onDelta(d.content, state.content);
274
+ }
275
+ if (d.reasoning_content) state.reasoning += d.reasoning_content;
276
+ if (d.reasoning) state.reasoning += d.reasoning;
277
+ for (const tc of d.tool_calls || []) {
278
+ const i = typeof tc.index === 'number' ? tc.index : state.toolCalls.length;
279
+ while (state.toolCalls.length <= i) {
280
+ state.toolCalls.push({ id: '', type: 'function', function: { name: '', arguments: '' } });
281
+ }
282
+ if (tc.id) state.toolCalls[i].id = tc.id;
283
+ if (tc.function) {
284
+ if (tc.function.name) state.toolCalls[i].function.name += tc.function.name;
285
+ if (tc.function.arguments) state.toolCalls[i].function.arguments += tc.function.arguments;
286
+ }
287
+ }
288
+ };
289
+
252
290
  while (true) {
253
291
  const { done, value } = await reader.read();
254
292
  if (signal && signal.aborted) {
@@ -261,44 +299,18 @@ async function streamOnce(sel, body, { signal, onDelta, onRetry } = {}, omitReas
261
299
 
262
300
  let idx;
263
301
  while ((idx = buf.indexOf('\n')) !== -1) {
264
- const line = buf.slice(0, idx).trim();
302
+ const line = buf.slice(0, idx);
265
303
  buf = buf.slice(idx + 1);
266
- if (!line.startsWith('data:')) continue;
267
- const data = line.slice(5).trim();
268
- if (!data || data === '[DONE]') continue;
269
-
270
- let json;
271
- try {
272
- json = JSON.parse(data);
273
- } catch {
274
- continue;
275
- }
276
-
277
- if (json.usage) state.usage = json.usage;
278
- const ch = json.choices && json.choices[0];
279
- if (!ch) continue;
280
- if (ch.finish_reason) state.finishReason = ch.finish_reason;
281
- const d = ch.delta || {};
282
- if (d.content) {
283
- state.content += d.content;
284
- onDelta && onDelta(d.content, state.content);
285
- }
286
- if (d.reasoning_content) state.reasoning += d.reasoning_content;
287
- if (d.reasoning) state.reasoning += d.reasoning;
288
- for (const tc of d.tool_calls || []) {
289
- const i = typeof tc.index === 'number' ? tc.index : state.toolCalls.length;
290
- while (state.toolCalls.length <= i) {
291
- state.toolCalls.push({ id: '', type: 'function', function: { name: '', arguments: '' } });
292
- }
293
- if (tc.id) state.toolCalls[i].id = tc.id;
294
- if (tc.function) {
295
- if (tc.function.name) state.toolCalls[i].function.name += tc.function.name;
296
- if (tc.function.arguments) state.toolCalls[i].function.arguments += tc.function.arguments;
297
- }
298
- }
304
+ handleLine(line);
299
305
  }
300
306
  }
301
307
 
308
+ /* SON FLUSH — KRİTİK: akış son parçası '\n' ile bitmeyebilir.
309
+ Okuma bitince kalan buf'u Decoder'dan boşaltıp işlemezsek
310
+ cevabın SON kelimesi/cümlesi kaybolur (SSE sonu öyle gelir). */
311
+ buf += decoder.decode();
312
+ if (buf.trim()) handleLine(buf);
313
+
302
314
  return { ...state };
303
315
  } finally {
304
316
  if (signal && onAbort) {
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+ # Beast stealth search - Obscura yaklasimi: Chrome TLS parmak izi taklidi.
3
+ # curl_cffi (curl-impersonate) ile html.duckduckgo.com'a gercek Chrome kimligiyle girer.
4
+ # stdout: ham DDG HTML - ayristirma JS tarafinda (tools.parseDdgResults) yapilir.
5
+ # Kullanim: stealthsearch.py "<url-encoded-sorgu>"
6
+ import sys
7
+ import urllib.parse
8
+
9
+
10
+ def main():
11
+ if len(sys.argv) < 2:
12
+ return
13
+ q = urllib.parse.quote_plus(sys.argv[1])
14
+ try:
15
+ from curl_cffi import requests
16
+ except ImportError:
17
+ return # kurulu degil - JS tarafi sessizce atlar
18
+ try:
19
+ r = requests.get(
20
+ "https://html.duckduckgo.com/html/?q=" + q,
21
+ impersonate="chrome",
22
+ headers={"Accept-Language": "tr-TR,tr;q=0.9,en;q=0.8"},
23
+ timeout=15,
24
+ )
25
+ sys.stdout.write(r.text or "")
26
+ except Exception:
27
+ return
28
+
29
+
30
+ main()
@@ -0,0 +1,325 @@
1
+ 'use strict';
2
+
3
+ /* SearXNG — yerel metasearch entegrasyonu (github.com/searxng/searxng)
4
+ SearXNG resmi olarak Linux/Docker hedefler; Windows'ta `import pwd`
5
+ yüzünden patlar. Bu modül Beast'ın GÖMÜLÜ Python'ına gerçek searxng'yi
6
+ (GitHub master) kurar + pwd shim'i yazar + 127.0.0.1:8888'de arka planda
7
+ çalıştırır. Arama zinciri 'searxng' motorunu yalnız AYAKTAYSA kullanır.
8
+
9
+ CLI: beast searxng → gerekirse kur + başlat
10
+ beast searxng status → durum
11
+ beast searxng stop → kulla */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const os = require('os');
16
+ const { spawn, execFile, execSync } = require('child_process');
17
+
18
+ const REQS_URL = 'https://raw.githubusercontent.com/searxng/searxng/master/requirements.txt';
19
+ const PKG_URL = 'https://github.com/searxng/searxng/archive/refs/heads/master.zip';
20
+ const GETPIP_URL = 'https://bootstrap.pypa.io/get-pip.py';
21
+ const DEFAULT_URL = 'http://127.0.0.1:8888';
22
+
23
+ /* ---------- yollar ---------- */
24
+
25
+ function beastAppDir() {
26
+ if (process.env.BEAST_DATA) return process.env.BEAST_DATA;
27
+ return process.env.APPDATA
28
+ ? path.join(process.env.APPDATA, 'beast')
29
+ : path.join(os.homedir(), 'AppData', 'Roaming', 'beast');
30
+ }
31
+
32
+ function dir() {
33
+ const d = path.join(beastAppDir(), 'searxng');
34
+ try { fs.mkdirSync(d, { recursive: true }); } catch {}
35
+ return d;
36
+ }
37
+
38
+ function settingsPath() { return path.join(dir(), 'settings.yml'); }
39
+ function runPyPath() { return path.join(dir(), 'run.py'); }
40
+ function pidPath() { return path.join(dir(), 'server.pid'); }
41
+ function pyExe() { return path.join(beastAppDir(), 'py', 'python.exe'); }
42
+
43
+ /* ---------- durum yoklama (cache'li) ---------- */
44
+
45
+ let _probe = { at: 0, up: false, url: '' };
46
+ const PROBE_UP_TTL = 5 * 60 * 1000;
47
+ const PROBE_DOWN_TTL = 2 * 60 * 1000;
48
+
49
+ function resetProbeCache() { _probe = { at: 0, up: false, url: '' }; }
50
+
51
+ async function isUp(baseUrl = DEFAULT_URL, { force = false } = {}) {
52
+ const base = String(baseUrl || DEFAULT_URL).replace(/\/+$/, '');
53
+ const ttl = _probe.up ? PROBE_UP_TTL : PROBE_DOWN_TTL;
54
+ if (!force && _probe.url === base && Date.now() - _probe.at < ttl) return _probe.up;
55
+ let up = false;
56
+ try {
57
+ const r = await fetch(`${base}/search?q=beast&format=json`, { signal: AbortSignal.timeout(1800) });
58
+ if (r.ok) {
59
+ const j = await r.json();
60
+ up = j && Array.isArray(j.results);
61
+ }
62
+ } catch {}
63
+ _probe = { at: Date.now(), up, url: base };
64
+ return up;
65
+ }
66
+
67
+ /* ---------- arama (zincir motoru) ---------- */
68
+
69
+ async function search(query, { maxResults = 8, signal, baseUrl } = {}) {
70
+ const base = String(baseUrl || process.env.BEAST_SEARXNG_URL || DEFAULT_URL).replace(/\/+$/, '');
71
+ if (!(await isUp(base))) return null; // ayakta değil → zincir sıradaki motora geçer
72
+ try {
73
+ const sig =
74
+ signal && typeof AbortSignal !== 'undefined' && AbortSignal.any
75
+ ? AbortSignal.any([signal, AbortSignal.timeout(15000)])
76
+ : AbortSignal.timeout(15000);
77
+ const r = await fetch(
78
+ `${base}/search?q=${encodeURIComponent(String(query || ''))}&format=json&safesearch=0`,
79
+ { signal: sig }
80
+ );
81
+ if (!r.ok) return null;
82
+ const j = await r.json();
83
+ const results = (j.results || [])
84
+ .map((x) => ({
85
+ title: String(x.title || ''),
86
+ url: String(x.url || ''),
87
+ snippet: String(x.content || '').replace(/\s+/g, ' ').slice(0, 400),
88
+ engine: 'searxng',
89
+ }))
90
+ .filter((x) => x.url && x.title);
91
+ if (!results.length) return null;
92
+ return {
93
+ ok: true,
94
+ engine: 'searxng',
95
+ query,
96
+ results: results.slice(0, Math.max(1, Math.min(12, Number(maxResults) || 8))),
97
+ };
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+
103
+ /* ---------- kurulum ---------- */
104
+
105
+ function sh(exe, args, timeoutMs = 600000) {
106
+ return new Promise((resolve, reject) => {
107
+ execFile(exe, args, { timeout: timeoutMs, windowsHide: true, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => {
108
+ if (err) {
109
+ const tail = String((stderr || stdout || err.message) || '').slice(-400);
110
+ return reject(new Error(tail || String(err)));
111
+ }
112
+ resolve(String(stdout || ''));
113
+ });
114
+ });
115
+ }
116
+
117
+ async function downloadTo(url, dest) {
118
+ const r = await fetch(url, { signal: AbortSignal.timeout(120000) });
119
+ if (!r.ok) throw new Error(`indirme başarısız: HTTP ${r.status}`);
120
+ fs.writeFileSync(dest, Buffer.from(await r.arrayBuffer()));
121
+ }
122
+
123
+ function sitePackagesDir(py) {
124
+ return sh(py, ['-c', 'import sysconfig;print(sysconfig.get_paths()["purelib"])']).then((s) => s.trim());
125
+ }
126
+
127
+ /* gömülü python'un ._pth dosyasında `import site` açık değilse pip/paketler görünmez */
128
+ function fixEmbeddedPth(py) {
129
+ try {
130
+ const dirPy = path.dirname(py);
131
+ for (const f of fs.readdirSync(dirPy)) {
132
+ if (/^python\d+\._pth$/.test(f)) {
133
+ const p = path.join(dirPy, f);
134
+ const cur = fs.readFileSync(p, 'utf8');
135
+ if (!/^import site$/m.test(cur)) {
136
+ fs.writeFileSync(p, cur.replace(/^#\s*import site/m, 'import site'), 'utf8');
137
+ }
138
+ }
139
+ }
140
+ } catch {}
141
+ }
142
+
143
+ async function ensurePip(py, onLog) {
144
+ try {
145
+ await sh(py, ['-m', 'pip', '--version'], 60000);
146
+ return;
147
+ } catch {}
148
+ onLog('pip kuruluyor…');
149
+ const gp = path.join(os.tmpdir(), 'beast-get-pip.py');
150
+ await downloadTo(GETPIP_URL, gp);
151
+ await sh(py, [gp, '--no-warn-script-location'], 300000);
152
+ fixEmbeddedPth(py);
153
+ }
154
+
155
+ /** Tam kurulum — `beast searxng` komutunun yaptığı iş. */
156
+ async function install({ onLog } = {}) {
157
+ const log = (m) => { try { (onLog || ((s) => console.log(' • ' + s)))(m); } catch {} };
158
+ const tools = require('./tools');
159
+ const probe = await tools.ensurePython();
160
+ const py = probe.exe;
161
+ log('python: ' + py);
162
+
163
+ await ensurePip(py, log);
164
+ log('setuptools/wheel/tzdata kuruluyor…');
165
+ await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', 'setuptools', 'wheel', 'tzdata'], 600000);
166
+
167
+ log('searxng bağımlılıkları indiriliyor (~40 MB)…');
168
+ const reqs = path.join(os.tmpdir(), 'beast-searxng-requirements.txt');
169
+ await downloadTo(REQS_URL, reqs);
170
+ await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '-r', reqs], 900000);
171
+
172
+ log('searxng motoru kuruluyor…');
173
+ await sh(py, ['-m', 'pip', 'install', '--no-warn-script-location', '--no-build-isolation', PKG_URL], 900000);
174
+
175
+ /* pwd shim — searxng/valkeydb.py unix socket sahipliği için pwd import eder */
176
+ const sp = (await sitePackagesDir(py)) || path.join(path.dirname(py), 'Lib', 'site-packages');
177
+ const shim = [
178
+ '"""Windows shim: SearXNG valkeydb.py yalnizca unix-socket sahipligi icin pwd',
179
+ 'kullanir; Windows\'ta valkey devre disi oldugundan donuk yanit yeterlidir."""',
180
+ 'import os',
181
+ '',
182
+ 'class _Pw:',
183
+ ' pw_name = "beast"',
184
+ ' pw_uid = 0',
185
+ ' pw_gid = 0',
186
+ ' pw_dir = os.path.expanduser("~")',
187
+ ' pw_shell = ""',
188
+ '',
189
+ 'def getpwuid(uid):',
190
+ ' return _Pw()',
191
+ '',
192
+ 'def getpwnam(name):',
193
+ ' return _Pw()',
194
+ '',
195
+ 'def getgrgid(gid):',
196
+ ' class _Gr:',
197
+ ' gr_name = "beast"',
198
+ ' gr_gid = 0',
199
+ ' return _Gr()',
200
+ '',
201
+ 'def getgrnam(name):',
202
+ ' return getgrgid(0)',
203
+ ].join('\n');
204
+ fs.writeFileSync(path.join(sp, 'pwd.py'), shim, 'utf8');
205
+
206
+ /* ayarlar + çalıştırıcı */
207
+ const secret = 'beast-' + Math.random().toString(36).slice(2, 14);
208
+ fs.writeFileSync(
209
+ settingsPath(),
210
+ [
211
+ 'use_default_settings: true',
212
+ 'server:',
213
+ ` secret_key: "${secret}"`,
214
+ ' port: 8888',
215
+ ' bind_address: "127.0.0.1"',
216
+ ' limiter: false',
217
+ ' image_proxy: false',
218
+ 'search:',
219
+ ' formats:',
220
+ ' - html',
221
+ ' - json',
222
+ '',
223
+ ].join('\n'),
224
+ 'utf8'
225
+ );
226
+ fs.writeFileSync(runPyPath(), 'from searx.webapp import run; run()', 'utf8');
227
+ resetProbeCache();
228
+ log('kurulum tamam');
229
+ return { ok: true };
230
+ }
231
+
232
+ /* ---------- başlat / durdur / durum ---------- */
233
+
234
+ function readPid() {
235
+ try {
236
+ const s = fs.readFileSync(pidPath(), 'utf8').trim();
237
+ return /^\d+$/.test(s) ? Number(s) : null;
238
+ } catch {
239
+ return null;
240
+ }
241
+ }
242
+
243
+ async function start({ installIfMissing = true, onLog } = {}) {
244
+ if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, already: true, url: DEFAULT_URL };
245
+ if (!fs.existsSync(settingsPath()) || !fs.existsSync(runPyPath())) {
246
+ if (!installIfMissing) return { ok: false, error: 'kurulu değil — önce: beast searxng' };
247
+ await install({ onLog });
248
+ }
249
+ const tools = require('./tools');
250
+ const probe = await tools.ensurePython();
251
+ const child = spawn(probe.exe, [runPyPath()], {
252
+ detached: true,
253
+ stdio: 'ignore',
254
+ windowsHide: true,
255
+ env: { ...process.env, SEARXNG_SETTINGS_PATH: settingsPath(), PYTHONIOENCODING: 'utf-8' },
256
+ });
257
+ try { fs.writeFileSync(pidPath(), String(child.pid)); } catch {}
258
+ child.unref();
259
+ /* ayağa kalkmasını bekle (ilk açılış motor listesini derler — birkaç sn) */
260
+ for (let i = 0; i < 20; i++) {
261
+ await new Promise((r) => setTimeout(r, 1000));
262
+ if (await isUp(DEFAULT_URL, { force: true })) return { ok: true, url: DEFAULT_URL, pid: child.pid };
263
+ }
264
+ return { ok: false, error: 'searxng başlatılamadı (20 sn içinde yanıt yok)' };
265
+ }
266
+
267
+ function stop() {
268
+ const pid = readPid();
269
+ if (!pid) return { ok: true, note: 'çalışmıyor' };
270
+ try {
271
+ execSync(`taskkill /PID ${pid} /T /F`, { stdio: 'ignore' });
272
+ } catch {}
273
+ try { fs.unlinkSync(pidPath()); } catch {}
274
+ resetProbeCache();
275
+ return { ok: true, stopped: pid };
276
+ }
277
+
278
+ async function status() {
279
+ return {
280
+ up: await isUp(DEFAULT_URL, { force: true }),
281
+ url: DEFAULT_URL,
282
+ pid: readPid(),
283
+ installed: fs.existsSync(settingsPath()) && fs.existsSync(runPyPath()),
284
+ };
285
+ }
286
+
287
+ /* ---------- CLI (bin/beast-agent.js → beast searxng) ---------- */
288
+
289
+ async function cli(argv) {
290
+ const sub = String(argv[0] || 'start').toLowerCase();
291
+ console.log('\u27F3 SearXNG (yerel arama motoru)');
292
+ if (sub === 'stop') {
293
+ const r = stop();
294
+ console.log(r.ok ? '\u2713 durduruldu' : '\u2717 durdurulamadı');
295
+ process.exit(r.ok ? 0 : 1);
296
+ }
297
+ if (sub === 'status') {
298
+ const s = await status();
299
+ console.log(` kurulu : ${s.installed ? 'evet' : 'hayır'}`);
300
+ console.log(` çalışıyor: ${s.up ? 'evet (' + s.url + ')' : 'hayır'}`);
301
+ process.exit(0);
302
+ }
303
+ /* start */
304
+ const r = await start({ installIfMissing: true, onLog: (m) => console.log(' \u2022 ' + m) });
305
+ if (r.ok) {
306
+ console.log(r.already ? '\u2713 SearXNG zaten çalışıyor — ' + r.url : '\u2713 SearXNG başlatıldı — ' + r.url);
307
+ console.log('\u2139 arama zincirinde kullanılmaya hazır (Ayarlar → Web Arama sırası)');
308
+ process.exit(0);
309
+ }
310
+ console.error('\u2717 ' + (r.error || 'başlatılamadı'));
311
+ process.exit(1);
312
+ }
313
+
314
+ module.exports = {
315
+ search,
316
+ isUp,
317
+ resetProbeCache,
318
+ install,
319
+ start,
320
+ stop,
321
+ status,
322
+ cli,
323
+ settingsPath,
324
+ DEFAULT_URL,
325
+ };
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: brainstorming
3
+ description: Yaratıcı işe KOD YAZMADAN ÖNCE kullan — yeni özellik, yeni proje, bileşen, davranış değişikliği istendiğinde; niyeti, gereksinimleri ve tasarımı kullanıcıyla netleştirmeden implementasyona geçmek yasaktır.
4
+ ---
5
+
6
+ # Brainstorming: Fikirden Tasarıma
7
+
8
+ Fikirleri doğal diyalogla tam oluşmuş tasarımlara dönüştür.
9
+
10
+ Akış: bağlamı anla → fikri netleştir → tasarımı sun → kullanıcı onayı.
11
+
12
+ <ZOR KAPI>
13
+ Kullanıcıya ne yapacağını söyleyip onayı almadan HİÇBİR implementasyon
14
+ eylemi yok: kod yazma, proje scaffold'ı, dosya üretme. Bu kapı HER görev
15
+ için geçerlidir; törenin boyutu göreve göre ölçeklenir ama ONAY KAPISI
16
+ ölçeklenmez.
17
+ </ZOR KAPI>
18
+
19
+ ## Üç Yol
20
+
21
+ İlk sorudan önce isteği SINIFLANDIR ve sınıfı sesli söyle — "bu sınırlı
22
+ kapsamlı görünüyor, spec dosyası yazmak yerine kısa tasarımı burada sunacağım"
23
+ — kullanıcı override edebilsin:
24
+
25
+ - **Spike** — fizibilite sorusu ("yapılabilir mi", "hızlı-çirkin olsun").
26
+ Çıktısı tuttuğun kod değil, bir CEVAPTIR. Soru ve deneme planını 2-3
27
+ cümleyle sun, onay al, en ucuz yolla araştır. Tasarım dosyası yok.
28
+ Bulgu → öneri olarak raporla; bir şey inşa ettiysen "atılabilir" etiketiyle.
29
+ - **Sınırlı (Bounded)** — mevcut koda iyi tanımlı değişiklik: yeni flag,
30
+ küçük endpoint, tek dosyalık fix. "Sınırlı" için DEĞİŞTİRECEĞİN AKIŞIN
31
+ Bu projede zaten var olması şart. Önemli netleştirme sorularını sor,
32
+ KISA tasarımı SOHBETTE sun (birkaç cümle - paragraf) ve DUR. Kullanıcı
33
+ "evet" demeden implementasyon yok. Spec dosyası yok, plan dosyası yok.
34
+ - **Mimari** — yeni projeler, yeni alt sistemler, bileşen ilişkilerini
35
+ değiştiren işler. Tam süreç: sorular → 2-3 yaklaşım → bölümlü tasarım →
36
+ yazılı spec → writing-plans skill'i.
37
+
38
+ İki yol arasında tereddüt varsa AĞIRINI seç. Ortasında gizli karmaşıklık
39
+ çıkarsa yol YÜKSELTİLİR (dur, söyle, büyüt); asla aşağı inmez.
40
+
41
+ ## Anti-Desen: "Onaya Gerek Yok Kadar Basit"
42
+
43
+ Her yol onayla biter. Todo listesi, tek fonksiyon, config değişikliği —
44
+ tasarım sohbette iki cümle olabilir ama SUN ve ONAY AL. "Basit" işler,
45
+ incelenmemiş varsayımların en çok iş boşa çıkardığı yerdir. Basitlikle
46
+ ölçeklenen şey artefaktır; onay asla değil.
47
+
48
+ ## Kırmızı Bayraklar
49
+
50
+ | Düşünce | Gerçek |
51
+ |---|---|
52
+ | "Bu, tasarım gerektirmeyecek kadar basit" | Basitlik kısa tasarım demektir, tasarım yok demek değil. |
53
+ | "Sınırlı der spec'i atlarım" | Etiket arayışı şüphenin ta kendisi — ağır yolu seç. |
54
+ | "Tasarım bariz, onu okurken başlarım" | Kapı onaydır, tasarımın uzunluğu değil. Sun ve evet bekle. |
55
+ | "Bu tür app'leri biliyorum, sınırlıdır" | Sınırlı olma projeyi ölçer, bilgini değil. Akış yoksa mimaridir. |
56
+ | "Spike çalıştı, kodu tutayım" | Spike'ın çıktısı cevaptır. Kodu tutmak YENİ istektir — sınıflandır. |
57
+ | "Büyüdü ama az kaldı, yeniden sınıflandırmaya gerek yok" | Gizli karmaşıklık yolu ortada yükseltir. Dur ve söyle. |
58
+
59
+ ## Kontrol Listesi
60
+
61
+ **Spike:** bağlamı gör → soru+plan sun (2-3 cümle) → onay → en ucuz yolla araştır → öneri raporla.
62
+
63
+ **Sınırlı:** bağlamı gör (dosyalar, dokümanlar, son commit'ler) → netleştirme
64
+ soruları TEK TEK → kısa tasarımı sohbette sun (yaklaşım, dokunulan dosyalar,
65
+ test stratejisi) → açık "evet" bekle (sunup hemen başlamak = kapıyı atlamak) →
66
+ normal iş akışıyla uygula (TDD geçerli).
67
+
68
+ **Mimari:**
69
+ 1. Bağlamı gör: mevcut yapıyı list_dir/grep/read_file ile incele, deseni takip et
70
+ 2. Netleştirme soruları — TEK SORU PER MESAJ; amaç/kısıt/başarı kriterini anla;
71
+ çoklu seçmeli soru tercih et
72
+ 3. Kapsam çok buysa parçala: bağımsız alt-projelere böl, sırayı belirle,
73
+ ilk alt-projeyle normal akışa gir
74
+ 4. 2-3 yaklaşım öner — artı/eksileriyle; önerini BAŞA koy ve nedenini söyle;
75
+ her yaklaşımda YAGNI: gereksiz özelliği çıkar
76
+ 5. Tasarımı bölümler halinde sun — mimari, bileşenler, veri akışı, hata
77
+ yönetimi, test stratejisi; her bölümün ardından "şimdiye kadar doğru mu?" de
78
+ 6. Onaylı tasarımı yaz: `<workspace>/docs/plans/specs/YYYY-MM-DD-<konu>-design.md`
79
+ 7. Spec öz-denetim: "TBD/TODO" var mı, iç tutarlılık, kapsam tek plana sığıyor mu,
80
+ iki şekilde yorumlanabilir gereksinim var mı → düzelt
81
+ 8. Kullanıcıya spec dosyasını göster, incelemesini iste
82
+ 9. Onay gelince writing-plans skill'iyle implementasyon planına geç — başka hiçbir
83
+ implementasyon adımı bu kapıdan önce gelmez
84
+
85
+ ## Mevcut Kodda Çalışırken
86
+
87
+ - Değişiklik önermeden önce mevcut yapıyı incele; var olan deseni takip et
88
+ - İşini etkileyen sorunlu noktalar varsa (şişmiş dosya, bulanık sınır) hedefli
89
+ iyileştirmeyi tasarıma dahil et
90
+ - Alakasız refactoring önerme — mevcut hedefe hizmet eden kal