lastgen-cli 1.0.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/dist/index.js ADDED
@@ -0,0 +1,4583 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ extractIssueKey,
4
+ fetchIssue,
5
+ validateAuth
6
+ } from "./chunk-NILKRTZZ.js";
7
+ import {
8
+ TRELLO_BACK,
9
+ extractCardId,
10
+ fetchBoardCards,
11
+ fetchBoards,
12
+ fetchCard,
13
+ runTrelloBoardPicker,
14
+ runTrelloCardPicker,
15
+ validateTrelloAuth
16
+ } from "./chunk-JUKR4XSW.js";
17
+ import {
18
+ ApiError,
19
+ listModels,
20
+ streamChat,
21
+ streamChatWithRetry
22
+ } from "./chunk-JGVBNMNK.js";
23
+ import {
24
+ LASTGEN_CONTEXT_FILE,
25
+ TOOL_DEFINITIONS,
26
+ executeTool,
27
+ formatFilePath,
28
+ formatInstructions,
29
+ formatToolCall,
30
+ generateAgentPlan,
31
+ generateLastgenContext,
32
+ hasLastgenContext,
33
+ lastgenContextPath,
34
+ listPlans,
35
+ readLastgenContext,
36
+ saveExecuteRecord,
37
+ savePlan
38
+ } from "./chunk-UV67GGHZ.js";
39
+ import {
40
+ GUTTER,
41
+ assistantHeader,
42
+ bannerDecode,
43
+ bannerLines,
44
+ buildLogoSettle,
45
+ closeInput,
46
+ colors,
47
+ error,
48
+ formatTokens,
49
+ goodbye,
50
+ info,
51
+ logoDecodeLines,
52
+ newline,
53
+ ok,
54
+ pick,
55
+ pickThinkingPhrase,
56
+ prompt,
57
+ promptBoxed,
58
+ promptExtraContext,
59
+ promptHidden,
60
+ randomGlitchChar,
61
+ setOutputSink,
62
+ startThinking,
63
+ visibleLength,
64
+ wrapAnsi,
65
+ write
66
+ } from "./chunk-ESFGGWR6.js";
67
+
68
+ // src/config.ts
69
+ import { homedir } from "os";
70
+ import { join } from "path";
71
+ import { promises as fs } from "fs";
72
+ function configDir() {
73
+ return process.env.LASTGEN_CONFIG_DIR ?? join(homedir(), ".lastgen");
74
+ }
75
+ function configPath() {
76
+ return join(configDir(), "config.json");
77
+ }
78
+ async function loadConfig() {
79
+ try {
80
+ const raw = await fs.readFile(configPath(), "utf8");
81
+ return JSON.parse(raw);
82
+ } catch (err) {
83
+ if (err.code === "ENOENT") return null;
84
+ throw err;
85
+ }
86
+ }
87
+ async function saveConfig(config) {
88
+ await fs.mkdir(configDir(), { recursive: true });
89
+ await fs.writeFile(configPath(), JSON.stringify(config, null, 2), {
90
+ mode: 384
91
+ });
92
+ try {
93
+ await fs.chmod(configPath(), 384);
94
+ } catch {
95
+ }
96
+ }
97
+ function applyEnvOverrides(config) {
98
+ const apiKey = process.env.LASTGEN_API_KEY;
99
+ const baseUrl = process.env.LASTGEN_BASE_URL;
100
+ const model = process.env.LASTGEN_MODEL;
101
+ if (!apiKey && !baseUrl && !model) return config;
102
+ const base = config ?? { baseUrl: "", apiKey: "" };
103
+ return {
104
+ ...base,
105
+ ...baseUrl ? { baseUrl } : {},
106
+ ...apiKey ? { apiKey } : {},
107
+ ...model ? { model } : {}
108
+ };
109
+ }
110
+ async function clearConfig() {
111
+ try {
112
+ await fs.unlink(configPath());
113
+ } catch (err) {
114
+ if (err.code !== "ENOENT") throw err;
115
+ }
116
+ }
117
+
118
+ // src/sessions.ts
119
+ import {
120
+ readFileSync,
121
+ writeFileSync,
122
+ mkdirSync,
123
+ readdirSync,
124
+ unlinkSync,
125
+ appendFileSync,
126
+ existsSync
127
+ } from "fs";
128
+ import { readFile } from "fs/promises";
129
+ import { homedir as homedir2 } from "os";
130
+ import { join as join2 } from "path";
131
+ var ERROR_LOG = join2(homedir2(), ".lastgen", "lastgen-errors.log");
132
+ function logError(context, err) {
133
+ const msg = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${context}: ${err instanceof Error ? err.message : String(err)}
134
+ `;
135
+ try {
136
+ mkdirSync(join2(homedir2(), ".lastgen"), { recursive: true });
137
+ appendFileSync(ERROR_LOG, msg, "utf-8");
138
+ } catch {
139
+ process.stderr.write(`[LASTGEN] Hata loglanamad\u0131: ${msg}`);
140
+ }
141
+ }
142
+ var SESSIONS_DIR = join2(homedir2(), ".lastgen", "sessions");
143
+ var CURRENT_FILE = join2(homedir2(), ".lastgen", "current-session");
144
+ function ensureDir() {
145
+ mkdirSync(SESSIONS_DIR, { recursive: true });
146
+ }
147
+ function sessionPath(id) {
148
+ return join2(SESSIONS_DIR, `${id}.json`);
149
+ }
150
+ function titleFromMessages(messages) {
151
+ const first = messages.find((m) => m.role === "user");
152
+ if (!first) return "Yeni oturum";
153
+ return [...first.content].slice(0, 48).join("") + (first.content.length > 48 ? "\u2026" : "");
154
+ }
155
+ function newId() {
156
+ const now = /* @__PURE__ */ new Date();
157
+ const pad = (n, l = 2) => String(n).padStart(l, "0");
158
+ return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}-${Math.random().toString(36).slice(2, 6)}`;
159
+ }
160
+ function listSessions() {
161
+ try {
162
+ ensureDir();
163
+ return readdirSync(SESSIONS_DIR).filter((f) => f.endsWith(".json")).map((f) => {
164
+ try {
165
+ const raw = readFileSync(join2(SESSIONS_DIR, f), "utf-8");
166
+ const d = JSON.parse(raw);
167
+ return {
168
+ id: d.id,
169
+ title: d.title,
170
+ createdAt: d.createdAt,
171
+ updatedAt: d.updatedAt,
172
+ messageCount: d.messages.length
173
+ };
174
+ } catch {
175
+ return null;
176
+ }
177
+ }).filter((x) => x !== null).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
178
+ } catch {
179
+ return [];
180
+ }
181
+ }
182
+ async function loadSessionById(id) {
183
+ try {
184
+ const raw = await readFile(sessionPath(id), "utf-8");
185
+ return JSON.parse(raw);
186
+ } catch {
187
+ return null;
188
+ }
189
+ }
190
+ function getCurrentSessionId() {
191
+ try {
192
+ return readFileSync(CURRENT_FILE, "utf-8").trim() || null;
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+ function saveSessionSync(id, messages) {
198
+ try {
199
+ ensureDir();
200
+ const existing = (() => {
201
+ try {
202
+ return JSON.parse(readFileSync(sessionPath(id), "utf-8"));
203
+ } catch {
204
+ return null;
205
+ }
206
+ })();
207
+ const data = {
208
+ id,
209
+ title: titleFromMessages(messages),
210
+ createdAt: existing?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
211
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
212
+ messageCount: messages.length,
213
+ messages
214
+ };
215
+ writeFileSync(sessionPath(id), JSON.stringify(data, null, 2), "utf-8");
216
+ } catch (err) {
217
+ logError("saveSessionSync", err);
218
+ }
219
+ }
220
+ function setCurrentSessionId(id) {
221
+ try {
222
+ mkdirSync(join2(homedir2(), ".lastgen"), { recursive: true });
223
+ writeFileSync(CURRENT_FILE, id, "utf-8");
224
+ } catch (err) {
225
+ logError("setCurrentSessionId", err);
226
+ }
227
+ }
228
+ function deleteSession(id) {
229
+ const currentId = getCurrentSessionId();
230
+ if (currentId === id) {
231
+ throw new Error("Aktif oturum silinemez.");
232
+ }
233
+ const path = sessionPath(id);
234
+ if (!existsSync(path)) {
235
+ throw new Error(`Oturum bulunamad\u0131: ${id}`);
236
+ }
237
+ unlinkSync(path);
238
+ }
239
+ function createSession() {
240
+ const id = newId();
241
+ ensureDir();
242
+ setCurrentSessionId(id);
243
+ return id;
244
+ }
245
+ function migrateLegacyHistory() {
246
+ const legacyPath = join2(homedir2(), ".lastgen", "history.json");
247
+ try {
248
+ const raw = readFileSync(legacyPath, "utf-8");
249
+ const messages = JSON.parse(raw);
250
+ if (!Array.isArray(messages) || messages.length === 0) return;
251
+ if (listSessions().length > 0) return;
252
+ const id = newId();
253
+ saveSessionSync(id, messages);
254
+ setCurrentSessionId(id);
255
+ try {
256
+ unlinkSync(legacyPath);
257
+ } catch {
258
+ }
259
+ } catch {
260
+ }
261
+ }
262
+
263
+ // src/session-picker.ts
264
+ import { stdin, stdout } from "process";
265
+ import chalk from "chalk";
266
+ var brand = chalk.hex("#1577d4").bold;
267
+ var dim = chalk.dim;
268
+ var white = chalk.white;
269
+ var hiNum = chalk.greenBright.bold;
270
+ var hiTitle = chalk.white.bold;
271
+ var hiDate = chalk.hex("#1577d4");
272
+ function fmt(iso) {
273
+ try {
274
+ const d = new Date(iso);
275
+ return d.toLocaleDateString("tr-TR", { day: "2-digit", month: "short", year: "numeric" }) + " " + d.toLocaleTimeString("tr-TR", { hour: "2-digit", minute: "2-digit" });
276
+ } catch {
277
+ return iso.slice(0, 16);
278
+ }
279
+ }
280
+ function render(sessions, sel, rows, cols, hint) {
281
+ const C = cols;
282
+ const lines = [];
283
+ lines.push("");
284
+ lines.push(" " + brand("QAI") + dim(" \u2014 oturum se\xE7"));
285
+ lines.push("");
286
+ const reservedBottom = 3;
287
+ const maxVisible = Math.max(1, rows - lines.length - reservedBottom - 2);
288
+ const totalItems = sessions.length + 1;
289
+ let offset = 0;
290
+ if (sel >= offset + maxVisible) offset = sel - maxVisible + 1;
291
+ if (sel < offset) offset = sel;
292
+ const visibleSessions = sessions.slice(offset, offset + maxVisible);
293
+ visibleSessions.forEach((s, vi) => {
294
+ const i = vi + offset;
295
+ const selected = i === sel;
296
+ const num = selected ? hiNum(` \u203A ${i + 1}.`) : dim(` ${i + 1}.`);
297
+ const date = selected ? hiDate(fmt(s.updatedAt)) : dim(fmt(s.updatedAt));
298
+ const turns = dim(`${Math.floor(s.messageCount / 2)} tur`);
299
+ const title = selected ? hiTitle(s.title.slice(0, C - 32)) : white(s.title.slice(0, C - 32)) + (s.title.length > C - 32 ? dim("\u2026") : "");
300
+ lines.push(`${num} ${date} ${title} ${turns}`);
301
+ });
302
+ const newSel = sel === sessions.length;
303
+ lines.push("");
304
+ lines.push(
305
+ newSel ? hiNum(` \u203A ${sessions.length + 1}.`) + " " + hiTitle("Yeni oturum ba\u015Flat") : dim(` ${sessions.length + 1}.`) + " " + dim("Yeni oturum ba\u015Flat")
306
+ );
307
+ lines.push("");
308
+ lines.push(dim(" " + (hint ?? "\u2191 \u2193 gezin Enter se\xE7 D sil Ctrl+C \xE7\u0131k\u0131\u015F")));
309
+ lines.push("");
310
+ let buf = "\x1B[?25l\x1B[H";
311
+ for (let r = 0; r < rows; r++) {
312
+ buf += `\x1B[${r + 1};1H\x1B[2K` + (lines[r] ?? "");
313
+ }
314
+ stdout.write(buf);
315
+ }
316
+ async function runSessionPicker() {
317
+ const sessions = listSessions();
318
+ if (sessions.length === 0) {
319
+ const id = createSession();
320
+ return id;
321
+ }
322
+ if (!stdin.isTTY || !stdout.isTTY) {
323
+ return sessions[0].id;
324
+ }
325
+ return new Promise((resolve) => {
326
+ let sel = 0;
327
+ let confirmDelete = false;
328
+ const wasRaw = Boolean(stdin.isRaw);
329
+ const cols = () => stdout.columns || 80;
330
+ const rows = () => stdout.rows || 24;
331
+ const draw = (hint) => render(sessions, sel, rows(), cols(), hint);
332
+ const onResize = () => draw();
333
+ const onData = (chunk) => {
334
+ const code = chunk.charCodeAt(0);
335
+ if (code === 3) {
336
+ cleanup();
337
+ stdout.write("\x1B[?25h");
338
+ process.exit(0);
339
+ }
340
+ if (confirmDelete) {
341
+ const key = chunk.toLowerCase();
342
+ if (key === "e") {
343
+ confirmDelete = false;
344
+ const target = sessions[sel];
345
+ try {
346
+ deleteSession(target.id);
347
+ } catch {
348
+ }
349
+ const newSessions = listSessions();
350
+ sessions.length = 0;
351
+ sessions.push(...newSessions);
352
+ if (sel >= sessions.length) sel = Math.max(0, sessions.length - 1);
353
+ draw();
354
+ } else {
355
+ confirmDelete = false;
356
+ draw();
357
+ }
358
+ return;
359
+ }
360
+ if (chunk === "\x1B[A") {
361
+ sel = sel > 0 ? sel - 1 : sessions.length;
362
+ draw();
363
+ return;
364
+ }
365
+ if (chunk === "\x1B[B") {
366
+ sel = sel < sessions.length ? sel + 1 : 0;
367
+ draw();
368
+ return;
369
+ }
370
+ if (chunk.toLowerCase() === "d") {
371
+ if (sel === sessions.length) {
372
+ draw();
373
+ return;
374
+ }
375
+ const target = sessions[sel];
376
+ const currentId = getCurrentSessionId();
377
+ if (target.id === currentId) {
378
+ draw("Aktif oturum silinemez \u2014 ba\u015Fka tu\u015Fa bas");
379
+ return;
380
+ }
381
+ confirmDelete = true;
382
+ draw("[E] onayla [H] iptal");
383
+ return;
384
+ }
385
+ if (chunk === "\r" || chunk === "\n") {
386
+ cleanup();
387
+ stdout.write("\x1B[?25h\x1B[2J\x1B[H");
388
+ if (sel === sessions.length) {
389
+ const id = createSession();
390
+ resolve(id);
391
+ } else {
392
+ const picked = sessions[sel];
393
+ setCurrentSessionId(picked.id);
394
+ resolve(picked.id);
395
+ }
396
+ }
397
+ };
398
+ let dataHandler = null;
399
+ const cleanup = () => {
400
+ if (dataHandler) stdin.removeListener("data", dataHandler);
401
+ stdout.removeListener("resize", onResize);
402
+ stdin.setRawMode(wasRaw);
403
+ if (!wasRaw) stdin.pause();
404
+ };
405
+ stdin.setRawMode(true);
406
+ stdin.resume();
407
+ stdin.setEncoding("utf8");
408
+ stdout.on("resize", onResize);
409
+ draw();
410
+ dataHandler = onData;
411
+ stdin.on("data", dataHandler);
412
+ });
413
+ }
414
+
415
+ // src/providers.ts
416
+ var CORPORATE_DEFAULT_BASE_URL = "https://api.openai.com/v1";
417
+ var GLM_DEFAULT_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
418
+ var PROVIDERS = [
419
+ {
420
+ id: "corporate",
421
+ label: "Kurumsal Endpoint",
422
+ description: "\u0130\xE7/kurumsal OpenAI-uyumlu LLM (LiteLLM vb.)",
423
+ defaultBaseUrl: CORPORATE_DEFAULT_BASE_URL,
424
+ promptForBaseUrl: true,
425
+ fallbackModels: []
426
+ },
427
+ {
428
+ id: "external",
429
+ label: "Global Endpoint (GLM z.ai)",
430
+ description: "GLM z.ai \u2014 glm-4.6 / glm-5.2",
431
+ defaultBaseUrl: GLM_DEFAULT_BASE_URL,
432
+ promptForBaseUrl: false,
433
+ fallbackModels: ["glm-4.6", "glm-5.2"]
434
+ }
435
+ ];
436
+ function providerById(id) {
437
+ return PROVIDERS.find((p) => p.id === id) ?? PROVIDERS[0];
438
+ }
439
+
440
+ // src/context.ts
441
+ import { promises as fs2 } from "fs";
442
+ import { join as join3, extname } from "path";
443
+ var IGNORE_DIRS = /* @__PURE__ */ new Set([
444
+ "node_modules",
445
+ ".git",
446
+ "dist",
447
+ "build",
448
+ ".next",
449
+ "coverage",
450
+ ".cache",
451
+ "out"
452
+ ]);
453
+ var BINARY_EXT = /* @__PURE__ */ new Set([
454
+ ".png",
455
+ ".jpg",
456
+ ".jpeg",
457
+ ".gif",
458
+ ".ico",
459
+ ".pdf",
460
+ ".zip",
461
+ ".gz",
462
+ ".lock",
463
+ ".woff",
464
+ ".woff2",
465
+ ".ttf",
466
+ ".eot",
467
+ ".map"
468
+ ]);
469
+ var ANCHOR_RE = /^(readme(\.\w+)?|package\.json|tsconfig.*\.json|.*\.config\.(js|ts|json|mjs|cjs))$/i;
470
+ var STOPWORDS = /* @__PURE__ */ new Set([
471
+ "the",
472
+ "and",
473
+ "for",
474
+ "with",
475
+ "this",
476
+ "that",
477
+ "task",
478
+ "issue",
479
+ "jira",
480
+ "ile",
481
+ "i\xE7in",
482
+ "olan",
483
+ "bir",
484
+ "olarak",
485
+ "gibi",
486
+ "the",
487
+ "ekran",
488
+ "page"
489
+ ]);
490
+ function extractKeywords(issue) {
491
+ const text = `${issue.summary} ${issue.description}`.toLowerCase();
492
+ const words = text.match(/[a-zçğıöşü0-9]{3,}/gi) ?? [];
493
+ const set = /* @__PURE__ */ new Set();
494
+ for (const w of words) {
495
+ const lw = w.toLowerCase();
496
+ if (!STOPWORDS.has(lw)) set.add(lw);
497
+ }
498
+ return [...set];
499
+ }
500
+ function basename(path) {
501
+ const parts = path.split("/");
502
+ return parts[parts.length - 1];
503
+ }
504
+ function isAnchor(path) {
505
+ return ANCHOR_RE.test(basename(path));
506
+ }
507
+ function matchesKeyword(path, keywords) {
508
+ const lp = path.toLowerCase();
509
+ return keywords.some((k) => k.length >= 3 && lp.includes(k));
510
+ }
511
+ function selectFiles(paths, keywords, maxFiles = 25) {
512
+ const anchors = paths.filter(isAnchor).sort();
513
+ const anchorSet = new Set(anchors);
514
+ const kw = paths.filter((p) => !anchorSet.has(p) && matchesKeyword(p, keywords)).sort();
515
+ const candidates = [...anchors, ...kw];
516
+ const toRead = candidates.slice(0, maxFiles);
517
+ return { toRead, skipped: candidates.length - toRead.length };
518
+ }
519
+ async function walk(rootDir, rel = "", acc = []) {
520
+ const entries = await fs2.readdir(join3(rootDir, rel), { withFileTypes: true });
521
+ for (const e of entries) {
522
+ if (e.isDirectory()) {
523
+ if (IGNORE_DIRS.has(e.name) || e.name.startsWith(".")) continue;
524
+ await walk(rootDir, join3(rel, e.name), acc);
525
+ } else if (e.isFile()) {
526
+ if (BINARY_EXT.has(extname(e.name).toLowerCase())) continue;
527
+ acc.push(join3(rel, e.name).replace(/\\/g, "/"));
528
+ }
529
+ }
530
+ return acc;
531
+ }
532
+ function buildTree(paths) {
533
+ const sorted = [...paths].sort();
534
+ const cap = 400;
535
+ let tree = sorted.slice(0, cap).join("\n");
536
+ if (sorted.length > cap) tree += `
537
+ \u2026 (+${sorted.length - cap} dosya daha)`;
538
+ return tree;
539
+ }
540
+ async function gatherContext(rootDir, issue, maxChars = 12e4) {
541
+ const paths = await walk(rootDir);
542
+ const tree = buildTree(paths);
543
+ const keywords = extractKeywords(issue);
544
+ const { toRead, skipped: skippedByCount } = selectFiles(paths, keywords);
545
+ const files = [];
546
+ let used = 0;
547
+ let skipped = skippedByCount;
548
+ for (const p of toRead) {
549
+ let content;
550
+ try {
551
+ content = await fs2.readFile(join3(rootDir, p), "utf8");
552
+ } catch {
553
+ skipped++;
554
+ continue;
555
+ }
556
+ if (used + content.length > maxChars) {
557
+ skipped++;
558
+ continue;
559
+ }
560
+ used += content.length;
561
+ files.push({ path: p, content });
562
+ }
563
+ return { tree, files, skipped };
564
+ }
565
+
566
+ // src/plan.ts
567
+ var SYSTEM_PROMPT = [
568
+ "Sen k\u0131demli bir yaz\u0131l\u0131m geli\u015Ftiricisin.",
569
+ "Sana bir Jira task'\u0131 ve projenin dosya yap\u0131s\u0131 ile ilgili dosya i\xE7erikleri verilecek.",
570
+ "G\xF6revin: bu task'\u0131 bu projede hayata ge\xE7irmek i\xE7in ad\u0131m ad\u0131m, dosya bazl\u0131 bir GEL\u0130\u015ET\u0130RME PLANI \xE7\u0131karmak.",
571
+ "Kurallar:",
572
+ "- T\xFCrk\xE7e yaz.",
573
+ "- Hangi dosyalar\u0131n olu\u015Fturulaca\u011F\u0131n\u0131/de\u011Fi\u015Ftirilece\u011Fini somut yollar\u0131yla belirt.",
574
+ "- Her ad\u0131m\u0131 k\xFC\xE7\xFCk ve uygulanabilir tut.",
575
+ "- Varsay\u0131mlar\u0131n\u0131 ve riskleri ayr\u0131 bir b\xF6l\xFCmde belirt.",
576
+ "- Kod yazma; yaln\u0131zca plan\u0131 \xFCret (gerekirse k\u0131sa \xF6rnek imzalar verebilirsin)."
577
+ ].join("\n");
578
+ function buildPlanMessages(issue, context) {
579
+ const filesBlock = context.files.map((f) => `### ${f.path}
580
+ \`\`\`
581
+ ${f.content}
582
+ \`\`\``).join("\n\n");
583
+ const user = [
584
+ `# Jira Task: ${issue.key}`,
585
+ `Ba\u015Fl\u0131k: ${issue.summary}`,
586
+ `Tip: ${issue.issueType}`,
587
+ `A\xE7\u0131klama:`,
588
+ issue.description || "(a\xE7\u0131klama yok)",
589
+ ``,
590
+ `# Proje Dosya A\u011Fac\u0131`,
591
+ context.tree || "(bo\u015F)",
592
+ ``,
593
+ `# \u0130lgili Dosya \u0130\xE7erikleri`,
594
+ filesBlock || "(i\xE7erik se\xE7ilmedi)",
595
+ ``,
596
+ `Yukar\u0131daki task i\xE7in ad\u0131m ad\u0131m, dosya bazl\u0131 bir geli\u015Ftirme plan\u0131 \xE7\u0131kar.`
597
+ ].join("\n");
598
+ return [
599
+ { role: "system", content: SYSTEM_PROMPT },
600
+ { role: "user", content: user }
601
+ ];
602
+ }
603
+ async function generatePlan(config, issue, context, onChunk, projectRoot) {
604
+ const messages = buildPlanMessages(issue, context);
605
+ if (projectRoot) {
606
+ const lastgenCtx = await readLastgenContext(projectRoot);
607
+ if (lastgenCtx && messages[0]?.role === "system") {
608
+ messages[0].content += `
609
+
610
+ ## Proje Ba\u011Flam\u0131 (${LASTGEN_CONTEXT_FILE})
611
+ ${lastgenCtx}`;
612
+ }
613
+ }
614
+ let full = "";
615
+ for await (const ev of streamChat(config, messages)) {
616
+ if (ev.type !== "content") continue;
617
+ full += ev.text;
618
+ onChunk(ev.text);
619
+ }
620
+ return full;
621
+ }
622
+
623
+ // src/markdown.ts
624
+ import chalk2 from "chalk";
625
+ var codeBg = chalk2.bgHex("#2b2b2b").whiteBright;
626
+ var fenceColor = chalk2.dim;
627
+ var headingColor = chalk2.hex("#c678dd").bold;
628
+ var bulletColor = chalk2.hex("#c678dd").bold;
629
+ var linkText = chalk2.cyan.underline;
630
+ var linkUrl = chalk2.dim;
631
+ var SYN = {
632
+ control: chalk2.hex("#c586c0"),
633
+ // mor — if, else, for, return, try, catch, throw
634
+ keyword: chalk2.hex("#569cd6"),
635
+ // mavi — class, const, public, private, import
636
+ type: chalk2.hex("#4ec9b0"),
637
+ // yeşil-turkuaz — String, Boolean, PascalCase class
638
+ string: chalk2.hex("#ce9178"),
639
+ // turuncu — "…" '…' `…`
640
+ number: chalk2.hex("#b5cea8"),
641
+ // açık yeşil — 42, 3.14, ALL_CAPS_CONST
642
+ comment: chalk2.hex("#6a9955").italic,
643
+ // koyu yeşil — // … /* … */ #
644
+ decorator: chalk2.hex("#dcdcaa"),
645
+ // sarı — @Override, @Value, @ExceptionHandler
646
+ func: chalk2.hex("#dcdcaa"),
647
+ // sarı — methodName(
648
+ variable: chalk2.hex("#9cdcfe"),
649
+ // açık mavi — camelCase değişkenler
650
+ propAccess: chalk2.hex("#9cdcfe"),
651
+ // açık mavi — .property, .method
652
+ bracket: chalk2.hex("#ffd700"),
653
+ // altın — ( ) { } [ ]
654
+ operator: chalk2.hex("#d4d4d4"),
655
+ // beyaz — = => + - * / < > !
656
+ punct: chalk2.hex("#808080"),
657
+ // gri — , ; . : ?
658
+ builtin: chalk2.hex("#4ec9b0"),
659
+ // turkuaz — console, Math, Promise, System
660
+ plain: chalk2.hex("#d4d4d4")
661
+ // varsayılan metin rengi
662
+ };
663
+ var CONTROL_KEYWORDS = /* @__PURE__ */ new Set([
664
+ "if",
665
+ "else",
666
+ "for",
667
+ "while",
668
+ "do",
669
+ "switch",
670
+ "case",
671
+ "break",
672
+ "continue",
673
+ "return",
674
+ "try",
675
+ "catch",
676
+ "finally",
677
+ "throw",
678
+ "throws",
679
+ "yield",
680
+ "goto",
681
+ "async",
682
+ "await",
683
+ "new",
684
+ "delete",
685
+ "typeof",
686
+ "instanceof",
687
+ "in",
688
+ "of",
689
+ "as",
690
+ "is"
691
+ ]);
692
+ var DECL_KEYWORDS = /* @__PURE__ */ new Set([
693
+ "class",
694
+ "interface",
695
+ "enum",
696
+ "struct",
697
+ "type",
698
+ "def",
699
+ "fn",
700
+ "func",
701
+ "fun",
702
+ "const",
703
+ "let",
704
+ "var",
705
+ "val",
706
+ "static",
707
+ "final",
708
+ "public",
709
+ "private",
710
+ "protected",
711
+ "abstract",
712
+ "override",
713
+ "readonly",
714
+ "export",
715
+ "import",
716
+ "from",
717
+ "package",
718
+ "extends",
719
+ "implements",
720
+ "namespace",
721
+ "module",
722
+ "declare",
723
+ "get",
724
+ "set",
725
+ "this",
726
+ "super",
727
+ "self",
728
+ "void",
729
+ "null",
730
+ "undefined",
731
+ "nil",
732
+ "none",
733
+ "true",
734
+ "false",
735
+ "boolean",
736
+ "number",
737
+ "string",
738
+ "object",
739
+ "symbol",
740
+ "any",
741
+ "bigint",
742
+ "never",
743
+ "unknown",
744
+ "keyof",
745
+ "typeof",
746
+ "out",
747
+ "ref",
748
+ "mut",
749
+ "pub",
750
+ "use",
751
+ "mod",
752
+ "crate",
753
+ "impl",
754
+ "trait",
755
+ "where",
756
+ "move",
757
+ "synchronized",
758
+ "volatile",
759
+ "native",
760
+ "transient",
761
+ "strictfp",
762
+ "data",
763
+ "sealed",
764
+ "when",
765
+ "by",
766
+ "lateinit",
767
+ "companion",
768
+ "open",
769
+ "internal",
770
+ "suspend",
771
+ "inline",
772
+ "operator",
773
+ "infix",
774
+ "crossinline",
775
+ "noinline",
776
+ "reified",
777
+ "vararg",
778
+ "tailrec",
779
+ "external",
780
+ "infix"
781
+ ]);
782
+ var BUILTINS = /* @__PURE__ */ new Set([
783
+ "console",
784
+ "Math",
785
+ "Object",
786
+ "Array",
787
+ "String",
788
+ "Number",
789
+ "Boolean",
790
+ "Promise",
791
+ "JSON",
792
+ "Error",
793
+ "Map",
794
+ "Set",
795
+ "Date",
796
+ "RegExp",
797
+ "Symbol",
798
+ "Buffer",
799
+ "process",
800
+ "setTimeout",
801
+ "setInterval",
802
+ "clearTimeout",
803
+ "clearInterval",
804
+ "fetch",
805
+ "parseInt",
806
+ "parseFloat",
807
+ "isNaN",
808
+ "isFinite",
809
+ "encodeURIComponent",
810
+ "List",
811
+ "Map",
812
+ "HashMap",
813
+ "ArrayList",
814
+ "LinkedList",
815
+ "Optional",
816
+ "System",
817
+ "Integer",
818
+ "Double",
819
+ "Float",
820
+ "Long",
821
+ "Character",
822
+ "Byte",
823
+ "Short",
824
+ "Exception",
825
+ "RuntimeException",
826
+ "Throwable",
827
+ "Error",
828
+ "HttpStatus",
829
+ "ResponseEntity",
830
+ "Autowired",
831
+ "Service",
832
+ "Component",
833
+ "Repository",
834
+ "Configuration",
835
+ "Bean",
836
+ "Qualifier",
837
+ "RequestMapping",
838
+ "GetMapping",
839
+ "PostMapping",
840
+ "PutMapping",
841
+ "DeleteMapping",
842
+ "PathVariable",
843
+ "RequestBody",
844
+ "RequestParam",
845
+ "ExceptionHandler",
846
+ "ControllerAdvice",
847
+ "RestController",
848
+ "SpringApplication",
849
+ "ApplicationContext",
850
+ "EntityManager",
851
+ "JpaRepository",
852
+ "Override",
853
+ "FunctionalInterface",
854
+ "Nullable",
855
+ "NonNull",
856
+ "Generated"
857
+ ]);
858
+ function isPascalCase(word) {
859
+ return /^[A-Z][A-Za-z0-9]+$/.test(word);
860
+ }
861
+ function isCamelCase(word) {
862
+ return /^[a-z][A-Za-z0-9]*$/.test(word) && word.length > 1;
863
+ }
864
+ function isConstant(word) {
865
+ return /^[A-Z][A-Z0-9_]{1,}$/.test(word);
866
+ }
867
+ function tokenizeLine(line) {
868
+ const tokens = [];
869
+ let i = 0;
870
+ while (i < line.length) {
871
+ if (line[i] === "/" && line[i + 1] === "/") {
872
+ tokens.push({ kind: "comment", text: line.slice(i) });
873
+ break;
874
+ }
875
+ if (line[i] === "#" && (i === 0 || /\s/.test(line[i - 1]))) {
876
+ tokens.push({ kind: "comment", text: line.slice(i) });
877
+ break;
878
+ }
879
+ if (line[i] === "/" && line[i + 1] === "*") {
880
+ const end = line.indexOf("*/", i + 2);
881
+ if (end !== -1) {
882
+ tokens.push({ kind: "comment", text: line.slice(i, end + 2) });
883
+ i = end + 2;
884
+ continue;
885
+ }
886
+ tokens.push({ kind: "comment", text: line.slice(i) });
887
+ break;
888
+ }
889
+ if (line[i] === '"' || line[i] === "'" || line[i] === "`") {
890
+ const q = line[i];
891
+ let j = i + 1;
892
+ while (j < line.length) {
893
+ if (line[j] === "\\") {
894
+ j += 2;
895
+ continue;
896
+ }
897
+ if (line[j] === q) {
898
+ j++;
899
+ break;
900
+ }
901
+ j++;
902
+ }
903
+ tokens.push({ kind: "string", text: line.slice(i, j) });
904
+ i = j;
905
+ continue;
906
+ }
907
+ if (line[i] === "@" && /[A-Za-z_]/.test(line[i + 1] ?? "")) {
908
+ let j = i + 1;
909
+ while (j < line.length && /[\w.]/.test(line[j])) j++;
910
+ tokens.push({ kind: "decorator", text: line.slice(i, j) });
911
+ i = j;
912
+ continue;
913
+ }
914
+ if (/[0-9]/.test(line[i])) {
915
+ let j = i + 1;
916
+ while (j < line.length && /[0-9._xXa-fA-FlLdDfF]/.test(line[j])) j++;
917
+ tokens.push({ kind: "number", text: line.slice(i, j) });
918
+ i = j;
919
+ continue;
920
+ }
921
+ if (/[A-Za-z_$]/.test(line[i])) {
922
+ let j = i + 1;
923
+ while (j < line.length && /[\w$]/.test(line[j])) j++;
924
+ const word = line.slice(i, j);
925
+ let nextNonSpace = j;
926
+ while (nextNonSpace < line.length && line[nextNonSpace] === " ") nextNonSpace++;
927
+ const isCall = nextNonSpace < line.length && line[nextNonSpace] === "(";
928
+ const prevToken = [...tokens].reverse().find((t) => t.text !== " " && t.text !== " ");
929
+ const isPropAccess = prevToken?.text === ".";
930
+ let kind;
931
+ if (CONTROL_KEYWORDS.has(word)) {
932
+ kind = "control";
933
+ } else if (DECL_KEYWORDS.has(word)) {
934
+ kind = "keyword";
935
+ } else if (BUILTINS.has(word)) {
936
+ kind = "builtin";
937
+ } else if (isPascalCase(word)) {
938
+ kind = "type";
939
+ } else if (isConstant(word)) {
940
+ kind = "number";
941
+ } else if (isCall && isCamelCase(word)) {
942
+ kind = "func";
943
+ } else if (isPropAccess) {
944
+ kind = "propAccess";
945
+ } else if (isCamelCase(word)) {
946
+ kind = "variable";
947
+ } else {
948
+ kind = "plain";
949
+ }
950
+ tokens.push({ kind, text: word });
951
+ i = j;
952
+ continue;
953
+ }
954
+ if (/[()[\]{}]/.test(line[i])) {
955
+ tokens.push({ kind: "bracket", text: line[i] });
956
+ i++;
957
+ continue;
958
+ }
959
+ if (line[i] === "=" && line[i + 1] === ">") {
960
+ tokens.push({ kind: "operator", text: "=>" });
961
+ i += 2;
962
+ continue;
963
+ }
964
+ if (/[=+\-*/<>!&|^~%]/.test(line[i])) {
965
+ tokens.push({ kind: "operator", text: line[i] });
966
+ i++;
967
+ continue;
968
+ }
969
+ if (/[,:;.?]/.test(line[i])) {
970
+ tokens.push({ kind: "punct", text: line[i] });
971
+ i++;
972
+ continue;
973
+ }
974
+ tokens.push({ kind: "plain", text: line[i] });
975
+ i++;
976
+ }
977
+ return tokens;
978
+ }
979
+ function highlightLine(line) {
980
+ return tokenizeLine(line).map((t) => SYN[t.kind](t.text)).join("");
981
+ }
982
+ var CODE_MARKER = "\0";
983
+ function renderInline(text) {
984
+ const codes = [];
985
+ let out = text.replace(/`([^`]+)`/g, (_m, c) => {
986
+ codes.push(c);
987
+ return `${CODE_MARKER}${codes.length - 1}${CODE_MARKER}`;
988
+ });
989
+ out = out.replace(
990
+ /\[([^\]]+)\]\(([^)]+)\)/g,
991
+ (_m, t, u) => linkText(t) + linkUrl(` (${u})`)
992
+ );
993
+ out = out.replace(/\*\*([^*]+)\*\*/g, (_m, t) => chalk2.bold(t));
994
+ out = out.replace(/__([^_]+)__/g, (_m, t) => chalk2.bold(t));
995
+ out = out.replace(/\*([^*\n]+)\*/g, (_m, t) => chalk2.italic(t));
996
+ out = out.replace(
997
+ new RegExp(`${CODE_MARKER}(\\d+)${CODE_MARKER}`, "g"),
998
+ (_m, i) => codeBg(` ${codes[Number(i)]} `)
999
+ );
1000
+ return out;
1001
+ }
1002
+ function renderMarkdownBlock(text) {
1003
+ const md = createMarkdownStream();
1004
+ return md.push(text) + md.flush();
1005
+ }
1006
+ function createMarkdownStream() {
1007
+ let buffer = "";
1008
+ let inFence = false;
1009
+ let fenceLang = "";
1010
+ function renderLine(line) {
1011
+ const fenceMatch = line.match(/^\s*```(\w*)/);
1012
+ if (fenceMatch) {
1013
+ if (!inFence) {
1014
+ inFence = true;
1015
+ fenceLang = fenceMatch[1].toLowerCase();
1016
+ } else {
1017
+ inFence = false;
1018
+ fenceLang = "";
1019
+ }
1020
+ return fenceColor(line);
1021
+ }
1022
+ if (inFence) {
1023
+ const highlight = ["ts", "tsx", "js", "jsx", "typescript", "javascript", "json", "sh", "bash", "python", "py", "go", "rust", "css", "html", "java", "kt", "kotlin", "dart", "c", "cpp", "cs", "rb", "php", "swift", "yml", "yaml", "sql"].includes(fenceLang);
1024
+ return highlight ? highlightLine(line) : SYN.plain(line);
1025
+ }
1026
+ const h = line.match(/^\s*(#{1,6})\s+(.*)$/);
1027
+ if (h) return headingColor(renderInline(h[2]));
1028
+ const bullet = line.match(/^(\s*)([-*+])\s+(.*)$/);
1029
+ if (bullet) return `${bullet[1]}${bulletColor("\u2022")} ${renderInline(bullet[3])}`;
1030
+ const numbered = line.match(/^(\s*)(\d+)\.\s+(.*)$/);
1031
+ if (numbered) return `${numbered[1]}${bulletColor(numbered[2] + ".")} ${renderInline(numbered[3])}`;
1032
+ return renderInline(line);
1033
+ }
1034
+ return {
1035
+ push(chunk) {
1036
+ buffer += chunk;
1037
+ let out = "";
1038
+ let idx;
1039
+ while ((idx = buffer.indexOf("\n")) !== -1) {
1040
+ out += renderLine(buffer.slice(0, idx)) + "\n";
1041
+ buffer = buffer.slice(idx + 1);
1042
+ }
1043
+ return out;
1044
+ },
1045
+ flush() {
1046
+ if (buffer.length === 0) return "";
1047
+ const out = renderLine(buffer);
1048
+ buffer = "";
1049
+ return out;
1050
+ }
1051
+ };
1052
+ }
1053
+
1054
+ // src/agent-loop.ts
1055
+ var AGENT_SYSTEM_PROMPT = `Sen Lastgen'sin (GLM 5.2). G\xF6revin sana verilen "Yaz\u0131l\u0131m Uygulama Plan\u0131"ndaki ad\u0131mlar\u0131 s\u0131rayla uygulayarak kaynak kodda ger\xE7ek de\u011Fi\u015Fiklikler yapmakt\u0131r.
1056
+
1057
+ \xC7ALI\u015EMA KURALLARI:
1058
+ 1. DOSYA \u0130\xC7ER\u0130\u011E\u0130: Hedef dosyan\u0131n i\xE7eri\u011Fi sana user mesaj\u0131nda verildi. T\xFCm dosyay\u0131 tekrar read_file ile okuma \u2014 sadece g\xF6rmedi\u011Fin sat\u0131r aral\u0131klar\u0131n\u0131 read_file start_line/end_line ile oku.
1059
+ 2. NOKTA ATI\u015EI PATCH: search_string olarak verilen dosya i\xE7eri\u011Findeki GER\xC7EK kodu kullan \u2014 tahmin etme, kopyala. Indentation'\u0131 koru.
1060
+ 3. apply_patch BA\u015EARISIZ OLURSA: Hata mesaj\u0131ndaki "DO\u011ERU sat\u0131r" bilgisini kullan ve search_string'i g\xFCncelle. Ayn\u0131 search_string ile ikinci kez deneme.
1061
+ 4. IMPORT EKS\u0130KSE: Sa\u011Flanan "\xC7\xD6Z\xDCMLENEN IMPORT'LAR" b\xF6l\xFCm\xFCne bak \u2014 class'\u0131n import yolu orada yaz\u0131yor.
1062
+ 5. D\xD6NG\xDCDEN KA\xC7IN: Ayn\u0131 i\u015Flemi 2 kez denediysen ve h\xE2l\xE2 ba\u015Far\u0131s\u0131z oluyorsa dur ve \xF6zetle.
1063
+
1064
+ G\xDCVENL\u0130K:
1065
+ - Yaln\u0131zca proje dizini i\xE7indeki dosyalara eri\u015F.
1066
+ - Yaln\u0131zca izin verilen komutlar\u0131 \xE7al\u0131\u015Ft\u0131r (pytest, npm test, mvn test, go test, gradle test, make vb.).
1067
+ - mvnw/mvnw.cmd alt dizindeyse "cd subdir && mvnw.cmd test" format\u0131n\u0131 kullan \u2014 cd ve komut ayn\u0131 && zincirinde olabilir. "cmd /c" veya "npx" kullanma.
1068
+
1069
+ DE\u011EI\u015EKEN/STATE KONTROL\xDC: Yazd\u0131\u011F\u0131n veya patch etti\u011Fin kodda kulland\u0131\u011F\u0131n her de\u011Fi\u015Fkenin tan\u0131ml\u0131 oldu\u011Fundan emin ol. React bile\u015Feninde yeni state/ref kullan\u0131yorsan \u2014 useState/useRef bildirimi bile\u015Fenin \xFCst\xFCnde mevcut mu? Yoksa \xF6nce o ad\u0131m\u0131 uygula. Java'da yeni field/ba\u011F\u0131ml\u0131l\u0131k ekliyorsan \u2014 constructor/injection tan\u0131m\u0131 var m\u0131? Tan\u0131ms\u0131z de\u011Fi\u015Fken ESLint/compiler hatas\u0131na yol a\xE7ar.
1070
+
1071
+ YAYGIN SORUNLAR VE \xC7\xD6Z\xDCMLER\u0130:
1072
+ - react-scripts (CRA) projesinde "npm test" watch mode'da tak\u0131l\u0131r ve asla bitmez \u2192 "CI=true npm test" kullan. "npm test -- --watchAll=false" da \xE7al\u0131\u015F\u0131r ama CI=true daha g\xFCvenilir.
1073
+ - Dosya bulunamad\u0131\u011F\u0131nda (read_file \u2192 "mevcut de\u011Fil"): hemen find_files ile do\u011Fru yolu bul. \xD6zellikle .js/.jsx/.ts/.tsx uzant\u0131lar\u0131 kar\u0131\u015Ft\u0131r\u0131labilir \u2014 find_files ile ara, tahmin etme.
1074
+ - Test ba\u015Far\u0131s\u0131z ama kod do\u011Fruysa: test dosyas\u0131n\u0131 read_file ile oku, bile\u015Fen ger\xE7ekte ne render ediyor find_files ile kontrol et, test beklentisini mevcut koda g\xF6re g\xFCncelle.
1075
+
1076
+ \xC7IKTI: Sadece ne yapt\u0131\u011F\u0131n\u0131 ve sonucu yaz. T\xFCm ad\u0131mlar bitince k\u0131sa \xF6zet ver.
1077
+ "Ad\u0131m N/M tamamland\u0131" veya "Ad\u0131m tamamland\u0131" mesaj\u0131n\u0131 yazma \u2014 sistem otomatik olarak \xFCretiyor. Sen sadece yapt\u0131\u011F\u0131n i\u015Fin teknik a\xE7\u0131klamas\u0131n\u0131 yaz.
1078
+
1079
+ D\u0130L: T\xFCm d\xFC\u015F\xFCnme (thinking) ve a\xE7\u0131klama metinleri T\xFCrk\xE7e olsun. \u0130ngilizce yazma.`;
1080
+ var EXECUTE_TOOL_DEFINITIONS = TOOL_DEFINITIONS.filter(
1081
+ (t) => ["read_file", "grep_files", "find_files", "apply_patch", "create_file", "run_command"].includes(t.function.name)
1082
+ );
1083
+ async function callModel(config, messages, tools = EXECUTE_TOOL_DEFINITIONS, abortSignal) {
1084
+ const url = `${config.baseUrl.replace(/\/+$/, "")}/chat/completions`;
1085
+ const MAX_RETRIES = 4;
1086
+ const DELAY_429_MS = 8e3;
1087
+ const DELAY_NET_MS = 3e3;
1088
+ function isTransientNet(cause) {
1089
+ return cause.includes("ECONNRESET") || cause.includes("ECONNABORTED") || cause.includes("EPIPE") || cause.includes("UND_ERR_SOCKET") || cause.includes("UND_ERR_CONNECT");
1090
+ }
1091
+ let res;
1092
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1093
+ try {
1094
+ const timeoutSignal = AbortSignal.timeout(18e4);
1095
+ const signal = abortSignal ? AbortSignal.any([timeoutSignal, abortSignal]) : timeoutSignal;
1096
+ res = await fetch(url, {
1097
+ method: "POST",
1098
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${config.apiKey}` },
1099
+ body: JSON.stringify({
1100
+ model: config.model,
1101
+ messages,
1102
+ tools,
1103
+ tool_choice: "auto",
1104
+ temperature: 0.1
1105
+ }),
1106
+ signal
1107
+ });
1108
+ } catch (err) {
1109
+ const e = err;
1110
+ const cause = e.cause?.message ?? e.message ?? "";
1111
+ if (abortSignal?.aborted || e.name === "AbortError") {
1112
+ return { content: null, toolCalls: [], stopped: true, tokens: 0 };
1113
+ }
1114
+ if (e.name === "TimeoutError") {
1115
+ if (attempt < MAX_RETRIES) {
1116
+ await new Promise((r) => setTimeout(r, DELAY_NET_MS * (attempt + 1)));
1117
+ continue;
1118
+ }
1119
+ throw new Error("API iste\u011Fi zaman a\u015F\u0131m\u0131na u\u011Frad\u0131 (180s) \u2014 t\xFCm denemeler t\xFCkendi");
1120
+ }
1121
+ if (isTransientNet(cause)) {
1122
+ if (attempt < MAX_RETRIES) {
1123
+ await new Promise((r) => setTimeout(r, DELAY_NET_MS * (attempt + 1)));
1124
+ continue;
1125
+ }
1126
+ throw new Error(`Ba\u011Flant\u0131 s\u0131f\u0131rland\u0131 (${cause}) \u2014 t\xFCm denemeler t\xFCkendi`);
1127
+ }
1128
+ if (cause.includes("ECONNREFUSED")) throw new Error(`Ba\u011Flant\u0131 reddedildi \u2014 sunucu \xE7al\u0131\u015F\u0131yor mu?`);
1129
+ if (cause.includes("ENOTFOUND")) throw new Error(`DNS \xE7\xF6z\xFCmlenemedi \u2014 URL do\u011Fru mu?`);
1130
+ throw new Error(`A\u011F hatas\u0131: ${e.message}${cause ? ` (${cause})` : ""}`);
1131
+ }
1132
+ if (res.status === 429) {
1133
+ if (attempt === MAX_RETRIES) {
1134
+ const text = await res.text().catch(() => "");
1135
+ throw new Error(`API 429 \u2014 servis a\u015F\u0131r\u0131 y\xFCkl\xFC, tekrar deneme t\xFCkendi: ${text.slice(0, 200)}`);
1136
+ }
1137
+ await new Promise((r) => setTimeout(r, DELAY_429_MS * Math.pow(2, attempt)));
1138
+ continue;
1139
+ }
1140
+ if (res.status >= 500) {
1141
+ if (attempt < MAX_RETRIES) {
1142
+ await new Promise((r) => setTimeout(r, DELAY_NET_MS * (attempt + 1)));
1143
+ continue;
1144
+ }
1145
+ }
1146
+ break;
1147
+ }
1148
+ if (!res.ok) {
1149
+ const text = await res.text().catch(() => "");
1150
+ throw new Error(`API ${res.status} ${res.statusText}: ${text.slice(0, 300)}`);
1151
+ }
1152
+ const data = await res.json();
1153
+ const choice = data.choices?.[0];
1154
+ return {
1155
+ content: choice?.message?.content ?? null,
1156
+ toolCalls: choice?.message?.tool_calls ?? [],
1157
+ stopped: choice?.finish_reason === "stop",
1158
+ tokens: data.usage?.total_tokens ?? 0
1159
+ };
1160
+ }
1161
+ async function resolveJavaImports(projectRoot, filePath, fileContent, instructions) {
1162
+ const existingImports = new Set(
1163
+ fileContent.match(/import\s+([\w.]+);/g)?.map((l) => l.replace(/^import\s+/, "").replace(";", "")) ?? []
1164
+ );
1165
+ const candidates = [...new Set(
1166
+ instructions.match(/\b[A-Z][a-zA-Z]{3,}Exception\b|\b[A-Z][a-zA-Z]{3,}Repository\b|\b[A-Z][a-zA-Z]{3,}Service\b|\b[A-Z][a-zA-Z]{3,}Mapper\b|\b[A-Z][a-zA-Z]{3,}Util\b/g) ?? []
1167
+ )];
1168
+ if (candidates.length === 0) return null;
1169
+ const hints = [];
1170
+ for (const cls of candidates) {
1171
+ if ([...existingImports].some((i) => i.endsWith(`.${cls}`))) continue;
1172
+ const found = await executeTool("find_files", { name_pattern: `${cls}.java` }, projectRoot);
1173
+ if (!found.success || found.output.includes("bulunamad\u0131")) continue;
1174
+ const srcPath = found.output.split("\n")[0].trim();
1175
+ const pkg = await executeTool("read_file", { path: srcPath, start_line: "1", end_line: "3" }, projectRoot);
1176
+ const packageLine = pkg.output.match(/package\s+([\w.]+);/);
1177
+ if (!packageLine) continue;
1178
+ const importStatement = `import ${packageLine[1]}.${cls};`;
1179
+ if (!fileContent.includes(importStatement)) {
1180
+ const lines = fileContent.split("\n");
1181
+ let lastImportIdx = -1;
1182
+ for (let i = 0; i < lines.length; i++) {
1183
+ if (lines[i].trimStart().startsWith("import ")) lastImportIdx = i;
1184
+ }
1185
+ if (lastImportIdx >= 0) {
1186
+ lines.splice(lastImportIdx + 1, 0, importStatement);
1187
+ const newContent = lines.join("\n");
1188
+ const { writeFile } = await import("fs/promises");
1189
+ const { resolve } = await import("path");
1190
+ await writeFile(resolve(projectRoot, filePath), newContent, "utf-8");
1191
+ fileContent = newContent;
1192
+ }
1193
+ }
1194
+ hints.push(`${importStatement} // otomatik eklendi \u2014 ${srcPath}`);
1195
+ existingImports.add(`${packageLine[1]}.${cls}`);
1196
+ }
1197
+ return hints.length > 0 ? hints.join("\n") : null;
1198
+ }
1199
+ var MAX_STEP_ITERS = 12;
1200
+ function truncateFileContext(raw, filePath, maxLines = 300) {
1201
+ const lines = raw.split("\n");
1202
+ if (lines.length <= 400) return raw;
1203
+ const headCount = Math.floor(maxLines * 0.65);
1204
+ const tailCount = maxLines - headCount;
1205
+ const head = lines.slice(0, headCount);
1206
+ const tail = lines.slice(-tailCount);
1207
+ const skipped = lines.length - headCount - tailCount;
1208
+ return head.join("\n") + `
1209
+ \u2026 [${skipped} sat\u0131r atland\u0131 \u2014 sat\u0131r ${headCount + 1}\u2013${lines.length - tailCount}, gerekirse read_file start_line/end_line ile oku: ${filePath}] \u2026
1210
+ ` + tail.join("\n");
1211
+ }
1212
+ function compressMessages(messages, keepTurns = 3) {
1213
+ const head = messages.slice(0, 2);
1214
+ const body = messages.slice(2);
1215
+ if (body.length === 0) return messages;
1216
+ const assistantIndexes = [];
1217
+ for (let i = 0; i < body.length; i++) {
1218
+ if (body[i].role === "assistant") assistantIndexes.push(i);
1219
+ }
1220
+ if (assistantIndexes.length <= keepTurns) return messages;
1221
+ const cutFrom = assistantIndexes[assistantIndexes.length - keepTurns];
1222
+ const tail = body.slice(cutFrom);
1223
+ return [...head, ...tail];
1224
+ }
1225
+ async function runStep(config, projectRoot, stepInstruction, onStep, onProgress, startMs, tokensRef, abortSignal, executionMode, onToolApproval) {
1226
+ const messages = [
1227
+ { role: "system", content: AGENT_SYSTEM_PROMPT },
1228
+ { role: "user", content: `Proje k\xF6k\xFC: ${projectRoot}
1229
+
1230
+ ${stepInstruction}
1231
+
1232
+ \xD6NEML\u0130: Dosya i\xE7eri\u011Finde "\u2026 [N sat\u0131r atland\u0131 \u2026]" notu g\xF6r\xFCrsen, de\u011Fi\u015Ftirece\u011Fin kod o aral\u0131ktaysa HEMEN read_file start_line/end_line ile o b\xF6l\xFCm\xFC oku \u2014 ard\u0131ndan apply_patch yap. T\xFCm dosyay\u0131 tekrar okuma, yaln\u0131zca eksik aral\u0131\u011F\u0131 iste. Import veya referans class bulmak i\xE7in grep_files/find_files kullanabilirsin.` }
1233
+ ];
1234
+ let failedPatch = 0;
1235
+ let successfulPatch = 0;
1236
+ const appliedFiles = [];
1237
+ let lastFailReason = null;
1238
+ let lastModelError = null;
1239
+ for (let i = 0; i < MAX_STEP_ITERS; i++) {
1240
+ if (abortSignal?.aborted) {
1241
+ return { status: "failed", appliedFiles, failReason: "Kullan\u0131c\u0131 taraf\u0131ndan durduruldu (CTRL+C)." };
1242
+ }
1243
+ const sendMessages = i >= 2 ? compressMessages(messages) : messages;
1244
+ let response;
1245
+ try {
1246
+ response = await callModel(config, sendMessages, EXECUTE_TOOL_DEFINITIONS, abortSignal);
1247
+ } catch (err) {
1248
+ lastModelError = String(err);
1249
+ onStep({ type: "error", content: `Model hatas\u0131: ${lastModelError}` });
1250
+ return { status: "failed", appliedFiles, failReason: `Model ba\u011Flant\u0131 hatas\u0131: ${lastModelError}` };
1251
+ }
1252
+ const { content, toolCalls, stopped, tokens } = response;
1253
+ if (tokens > 0) {
1254
+ tokensRef.value = tokens;
1255
+ onProgress?.(tokens, Date.now() - startMs);
1256
+ }
1257
+ if (content) onStep({ type: "thinking", content });
1258
+ if (toolCalls.length === 0 || stopped) {
1259
+ return { status: "done", appliedFiles, failReason: null };
1260
+ }
1261
+ messages.push({ role: "assistant", content, tool_calls: toolCalls });
1262
+ for (const tc of toolCalls) {
1263
+ const toolName = tc.function.name;
1264
+ let toolArgs = {};
1265
+ try {
1266
+ toolArgs = JSON.parse(tc.function.arguments);
1267
+ } catch {
1268
+ }
1269
+ onStep({ type: "tool_call", content: `${toolName}(${JSON.stringify(toolArgs)})`, toolName, toolArgs });
1270
+ const isWriteTool = toolName === "apply_patch" || toolName === "create_file";
1271
+ if (isWriteTool && executionMode === "dry") {
1272
+ const dryOut = "[KRU] Dry-run: de\u011Fi\u015Fiklik yap\u0131lmad\u0131.";
1273
+ onStep({ type: "tool_result", content: dryOut, toolName, success: true });
1274
+ messages.push({ role: "tool", tool_call_id: tc.id, name: toolName, content: dryOut });
1275
+ continue;
1276
+ }
1277
+ if (isWriteTool && executionMode === "file" && onToolApproval) {
1278
+ const approved = await onToolApproval(toolName, toolArgs);
1279
+ if (!approved) {
1280
+ const skipOut = "Kullan\u0131c\u0131 bu de\u011Fi\u015Fikli\u011Fi atlad\u0131.";
1281
+ onStep({ type: "tool_result", content: skipOut, toolName, success: false });
1282
+ messages.push({ role: "tool", tool_call_id: tc.id, name: toolName, content: `HATA: ${skipOut}` });
1283
+ continue;
1284
+ }
1285
+ }
1286
+ let result = await executeTool(toolName, toolArgs, projectRoot);
1287
+ if (toolName === "apply_patch") {
1288
+ if (result.success) {
1289
+ successfulPatch++;
1290
+ const p = toolArgs.path ?? "";
1291
+ if (p && !appliedFiles.includes(p)) appliedFiles.push(p);
1292
+ } else {
1293
+ failedPatch++;
1294
+ lastFailReason = result.output.split("\n")[0].slice(0, 200);
1295
+ const filePath = toolArgs.path ?? "";
1296
+ if (filePath && failedPatch <= 2) {
1297
+ const reread = await executeTool("read_file", { path: filePath }, projectRoot);
1298
+ result = {
1299
+ success: false,
1300
+ output: `${result.output}
1301
+
1302
+ --- ${filePath} g\xFCncel i\xE7eri\u011Fi \u2014 search_string'i buradan al ---
1303
+ ${reread.output.slice(0, 2e3)}`
1304
+ };
1305
+ } else if (failedPatch > 2) {
1306
+ onStep({ type: "tool_result", content: result.output, toolName, success: false });
1307
+ return {
1308
+ status: successfulPatch > 0 ? "done" : "failed",
1309
+ appliedFiles,
1310
+ failReason: successfulPatch > 0 ? null : `apply_patch ${failedPatch} kez ba\u015Far\u0131s\u0131z \u2014 ${lastFailReason ?? "e\u015Fle\u015Fme bulunamad\u0131"}`
1311
+ };
1312
+ }
1313
+ }
1314
+ }
1315
+ let storedOutput = result.output;
1316
+ if (toolName === "read_file" && result.success) {
1317
+ const lines = storedOutput.split("\n");
1318
+ if (lines.length > 120) storedOutput = lines.slice(0, 120).join("\n") + `
1319
+ \u2026 (${lines.length - 120} sat\u0131r k\u0131rp\u0131ld\u0131)`;
1320
+ }
1321
+ onStep({ type: "tool_result", content: result.output, toolName, success: result.success });
1322
+ messages.push({
1323
+ role: "tool",
1324
+ tool_call_id: tc.id,
1325
+ name: toolName,
1326
+ content: result.success ? storedOutput : `HATA: ${result.output}`
1327
+ });
1328
+ }
1329
+ }
1330
+ if (successfulPatch > 0) {
1331
+ return { status: "done", appliedFiles, failReason: null };
1332
+ }
1333
+ return {
1334
+ status: "failed",
1335
+ appliedFiles,
1336
+ failReason: lastFailReason ? `Maksimum iterasyon doldu \u2014 son hata: ${lastFailReason}` : "Maksimum iterasyon doldu, patch uygulanamad\u0131"
1337
+ };
1338
+ }
1339
+ async function runAgentLoop(opts) {
1340
+ const { config, projectRoot, plan, testCommand = "pytest", executionMode, onStep, onProgress, onStepApproval, onToolApproval, abortSignal } = opts;
1341
+ const startMs = Date.now();
1342
+ const tokensRef = { value: 0 };
1343
+ const isAborted = () => abortSignal?.aborted ?? false;
1344
+ let planJson = null;
1345
+ try {
1346
+ const cleaned = plan.replace(/^```json\s*/i, "").replace(/^```\s*/i, "").replace(/```\s*$/, "").trim();
1347
+ planJson = JSON.parse(cleaned);
1348
+ } catch {
1349
+ }
1350
+ if (!planJson) {
1351
+ const messages = [
1352
+ { role: "system", content: AGENT_SYSTEM_PROMPT },
1353
+ { role: "user", content: `Proje k\xF6k\xFC: ${projectRoot}
1354
+ Test komutu: ${testCommand}
1355
+
1356
+ ${plan}` }
1357
+ ];
1358
+ for (let i = 0; i < 30; i++) {
1359
+ const sendMessages = i >= 4 ? compressMessages(messages, 4) : messages;
1360
+ let response;
1361
+ try {
1362
+ response = await callModel(config, sendMessages, TOOL_DEFINITIONS, abortSignal);
1363
+ } catch (err) {
1364
+ onStep({ type: "error", content: String(err) });
1365
+ return;
1366
+ }
1367
+ const { content, toolCalls, stopped, tokens } = response;
1368
+ if (tokens > 0) {
1369
+ tokensRef.value = tokens;
1370
+ onProgress?.(tokens, Date.now() - startMs);
1371
+ }
1372
+ if (content) onStep({ type: "thinking", content });
1373
+ if (toolCalls.length === 0 || stopped) {
1374
+ onStep({ type: "complete", content: content ?? "Tamamland\u0131." });
1375
+ return;
1376
+ }
1377
+ messages.push({ role: "assistant", content, tool_calls: toolCalls });
1378
+ for (const tc of toolCalls) {
1379
+ const toolName = tc.function.name;
1380
+ let toolArgs = {};
1381
+ try {
1382
+ toolArgs = JSON.parse(tc.function.arguments);
1383
+ } catch {
1384
+ }
1385
+ onStep({ type: "tool_call", content: `${toolName}(${JSON.stringify(toolArgs)})`, toolName, toolArgs });
1386
+ const result = await executeTool(toolName, toolArgs, projectRoot);
1387
+ let storedOut = result.output;
1388
+ if (toolName === "read_file" && result.success) {
1389
+ const ls = storedOut.split("\n");
1390
+ if (ls.length > 120) storedOut = ls.slice(0, 120).join("\n") + `
1391
+ \u2026 (${ls.length - 120} sat\u0131r k\u0131rp\u0131ld\u0131)`;
1392
+ }
1393
+ onStep({ type: "tool_result", content: result.output, toolName, success: result.success });
1394
+ messages.push({ role: "tool", tool_call_id: tc.id, name: toolName, content: result.success ? storedOut : `HATA: ${result.output}` });
1395
+ }
1396
+ }
1397
+ onStep({ type: "error", content: "Maksimum iterasyon a\u015F\u0131ld\u0131." });
1398
+ return;
1399
+ }
1400
+ const steps = planJson.steps;
1401
+ const failedSteps = [];
1402
+ let fileCache = null;
1403
+ onStep({ type: "thinking", content: `Plan: ${planJson.title} \u2014 ${steps.length} ad\u0131m` });
1404
+ for (const step of steps) {
1405
+ if (isAborted()) {
1406
+ onStep({ type: "error", content: "Kullan\u0131c\u0131 taraf\u0131ndan durduruldu (CTRL+C)." });
1407
+ return;
1408
+ }
1409
+ onStep({ type: "thinking", content: `\u25B6 Ad\u0131m ${step.step_number}/${steps.length}: ${step.description}` });
1410
+ if (executionMode === "step" && onStepApproval) {
1411
+ const approved = await onStepApproval(step.step_number, steps.length, step.description);
1412
+ if (!approved) {
1413
+ onStep({ type: "thinking", content: `Ad\u0131m ${step.step_number}/${steps.length} atland\u0131.` });
1414
+ continue;
1415
+ }
1416
+ }
1417
+ let fileContext = "";
1418
+ if (step.target_file) {
1419
+ const cached = fileCache?.path === step.target_file ? fileCache : null;
1420
+ if (cached) {
1421
+ fileContext = cached.context;
1422
+ onStep({ type: "thinking", content: `\u21A9 \xD6nbellekten: ${step.target_file}` });
1423
+ } else {
1424
+ onStep({ type: "tool_call", content: `read_file`, toolName: "read_file", toolArgs: { path: step.target_file, full: "true" } });
1425
+ const preread = await executeTool("read_file", { path: step.target_file, full: "true" }, projectRoot);
1426
+ if (preread.success) {
1427
+ onStep({ type: "tool_result", content: preread.output.slice(0, 120) + (preread.output.length > 120 ? "\u2026" : ""), toolName: "read_file", success: true });
1428
+ const truncated = truncateFileContext(preread.output, step.target_file, 300);
1429
+ fileContext = `
1430
+
1431
+ --- ${step.target_file} MEVCUT \u0130\xC7ER\u0130K ---
1432
+ ${truncated}
1433
+ --- DOSYA SONU ---`;
1434
+ const isJava = step.target_file.endsWith(".java") || step.target_file.endsWith(".kt");
1435
+ if (isJava) {
1436
+ const importHints = await resolveJavaImports(projectRoot, step.target_file, preread.output, step.instructions);
1437
+ if (importHints) fileContext += `
1438
+
1439
+ --- \xC7\xD6Z\xDCMLENEN IMPORT'LAR ---
1440
+ ${importHints}
1441
+ `;
1442
+ }
1443
+ fileCache = { path: step.target_file, context: fileContext, rawContent: preread.output };
1444
+ } else {
1445
+ onStep({ type: "thinking", content: `Yeni dosya: ${step.target_file}`, toolName: "read_file", success: false });
1446
+ fileContext = `
1447
+
1448
+ --- ${step.target_file} YEN\u0130 DOSYA ---
1449
+ Bu dosya hen\xFCz mevcut de\u011Fil. \u0130\xE7eri\u011Fi create_file arac\u0131yla olu\u015Fturmal\u0131s\u0131n.`;
1450
+ }
1451
+ }
1452
+ }
1453
+ const instruction = `G\xD6REV: ${planJson.title}
1454
+ MEVCUT ADIM (${step.step_number}/${steps.length}): ${step.description}
1455
+ Dosya: ${step.target_file}
1456
+ Talimat: ${step.instructions}` + fileContext;
1457
+ const result = await runStep(config, projectRoot, instruction, onStep, onProgress, startMs, tokensRef, abortSignal, executionMode, onToolApproval);
1458
+ if (result.status === "done") fileCache = null;
1459
+ if (result.status === "done") {
1460
+ if (result.appliedFiles.length > 0) {
1461
+ const files = result.appliedFiles.map((f) => f.split(/[/\\]/).slice(-2).join("/")).join(", ");
1462
+ onStep({ type: "thinking", content: `Ad\u0131m ${step.step_number}/${steps.length} tamamland\u0131 \u2014 de\u011Fi\u015Ftirilen: ${files}` });
1463
+ } else {
1464
+ onStep({ type: "thinking", content: `Ad\u0131m ${step.step_number}/${steps.length} tamamland\u0131` });
1465
+ }
1466
+ } else {
1467
+ failedSteps.push(step.step_number);
1468
+ const reason = result.failReason ?? "bilinmeyen hata";
1469
+ onStep({ type: "thinking", content: `Ad\u0131m ${step.step_number}/${steps.length} tamamlanamad\u0131 \u2014 ${reason}` });
1470
+ }
1471
+ }
1472
+ const MAX_VERIFICATION_RETRIES = 3;
1473
+ let verificationPassed = !planJson.verification_command;
1474
+ if (planJson.verification_command) {
1475
+ for (let vAttempt = 0; vAttempt <= MAX_VERIFICATION_RETRIES; vAttempt++) {
1476
+ if (isAborted()) {
1477
+ onStep({ type: "error", content: "Kullan\u0131c\u0131 taraf\u0131ndan durduruldu (CTRL+C)." });
1478
+ return;
1479
+ }
1480
+ onStep({ type: "thinking", content: `Do\u011Frulama: ${planJson.verification_command}${vAttempt > 0 ? ` (deneme ${vAttempt + 1}/${MAX_VERIFICATION_RETRIES + 1})` : ""}` });
1481
+ onStep({ type: "tool_call", content: `run_command`, toolName: "run_command", toolArgs: { command: planJson.verification_command } });
1482
+ const vResult = await executeTool("run_command", { command: planJson.verification_command }, projectRoot);
1483
+ onStep({ type: "tool_result", content: vResult.output, toolName: "run_command", success: vResult.success });
1484
+ if (vResult.success) {
1485
+ verificationPassed = true;
1486
+ if (vAttempt > 0) {
1487
+ onStep({ type: "thinking", content: `\u2713 Do\u011Frulama ${vAttempt + 1}. denemede ba\u015Far\u0131l\u0131` });
1488
+ }
1489
+ break;
1490
+ }
1491
+ if (vAttempt < MAX_VERIFICATION_RETRIES) {
1492
+ onStep({ type: "thinking", content: `Do\u011Frulama ba\u015Far\u0131s\u0131z \u2014 d\xFCzeltme denemesi ${vAttempt + 1}/${MAX_VERIFICATION_RETRIES}` });
1493
+ const fixInstruction = `Do\u011Frulama komutu (${planJson.verification_command}) ba\u015Far\u0131s\u0131z oldu.
1494
+
1495
+ HATA \xC7IKTISI:
1496
+ ${vResult.output.slice(0, 3e3)}
1497
+
1498
+ Bu hatay\u0131 analiz et, ilgili dosyay\u0131/dosyalar\u0131 read_file ile oku, apply_patch ile d\xFCzelt ve tekrar dene. Sadece hatay\u0131 d\xFCzelt, yeni \xF6zellik ekleme.`;
1499
+ const fixResult = await runStep(config, projectRoot, fixInstruction, onStep, onProgress, startMs, tokensRef, abortSignal, executionMode, onToolApproval);
1500
+ if (fixResult.appliedFiles.length > 0) {
1501
+ const files = fixResult.appliedFiles.map((f) => f.split(/[/\\]/).slice(-2).join("/")).join(", ");
1502
+ onStep({ type: "thinking", content: `D\xFCzeltme yap\u0131ld\u0131: ${files}` });
1503
+ }
1504
+ }
1505
+ }
1506
+ }
1507
+ const summary = failedSteps.length === 0 ? verificationPassed ? `${steps.length} ad\u0131m\u0131n tamam\u0131 uyguland\u0131. Do\u011Frulama ba\u015Far\u0131l\u0131.` : `${steps.length} ad\u0131m tamamland\u0131 ancak do\u011Frulama ba\u015Far\u0131s\u0131z \u2014 manuel kontrol gerekli.` : verificationPassed ? `${steps.length - failedSteps.length}/${steps.length} ad\u0131m tamamland\u0131. Do\u011Frulama ba\u015Far\u0131l\u0131. Ba\u015Far\u0131s\u0131z ad\u0131mlar: ${failedSteps.join(", ")}` : `${steps.length - failedSteps.length}/${steps.length} ad\u0131m tamamland\u0131. Do\u011Frulama da ba\u015Far\u0131s\u0131z. Ba\u015Far\u0131s\u0131z ad\u0131mlar: ${failedSteps.join(", ")}`;
1508
+ onStep({ type: "complete", content: summary });
1509
+ }
1510
+
1511
+ // src/commands.ts
1512
+ import { promises as fs3, existsSync as existsSync2 } from "fs";
1513
+ import { join as join4 } from "path";
1514
+ import chalk3 from "chalk";
1515
+ async function detectTestCommand(projectRoot) {
1516
+ const has = (file) => existsSync2(join4(projectRoot, file));
1517
+ if (has("pubspec.yaml")) return "flutter test";
1518
+ if (has("Cargo.toml")) return "cargo test";
1519
+ if (has("go.mod")) return "go test ./...";
1520
+ if (has("pom.xml")) return "mvn test";
1521
+ if (has("build.gradle") || has("build.gradle.kts")) return "gradle test";
1522
+ if (has("pyproject.toml") || has("setup.py")) return "pytest";
1523
+ if (has("package.json")) {
1524
+ try {
1525
+ const pkg = JSON.parse(await import("fs").then((m) => m.promises.readFile(join4(projectRoot, "package.json"), "utf-8")));
1526
+ const testScript = pkg?.scripts?.test ?? "";
1527
+ if (testScript.includes("react-scripts")) return "CI=true npm test";
1528
+ } catch {
1529
+ }
1530
+ return "npm test";
1531
+ }
1532
+ return "npm test";
1533
+ }
1534
+ var DEFAULT_JIRA_URL = "https://jira.turktelekom.com.tr";
1535
+ function parseCommand(input) {
1536
+ if (!input.startsWith("/")) return { cmd: "", arg: "" };
1537
+ const body = input.slice(1);
1538
+ const cmd = body.split(/\s+/)[0] ?? "";
1539
+ const arg = body.slice(cmd.length).trim();
1540
+ return { cmd: cmd.toLowerCase(), arg };
1541
+ }
1542
+ async function selectProvider(current) {
1543
+ const defaultId = current?.provider ?? "corporate";
1544
+ newline();
1545
+ write(colors.brand(" Endpoint t\xFCr\xFCn\xFC se\xE7in\n\n"));
1546
+ const defaultIdx = Math.max(0, PROVIDERS.findIndex((p) => p.id === defaultId));
1547
+ const idx = await pick(
1548
+ PROVIDERS.map((p) => ({ label: p.label, description: p.description })),
1549
+ defaultIdx
1550
+ );
1551
+ newline();
1552
+ if (idx === null) {
1553
+ error("/login iptal edildi.");
1554
+ return null;
1555
+ }
1556
+ return PROVIDERS[idx] ?? null;
1557
+ }
1558
+ async function runLogin(session) {
1559
+ info("API yap\u0131land\u0131rmas\u0131 (Lastgen /login)");
1560
+ const current = session.config;
1561
+ const preset = await selectProvider(current);
1562
+ if (!preset) return;
1563
+ const suggestedBaseUrl = current?.provider === preset.id && current?.baseUrl ? current.baseUrl : preset.defaultBaseUrl;
1564
+ let baseUrl;
1565
+ if (preset.promptForBaseUrl) {
1566
+ const baseUrlInput = await prompt(
1567
+ colors.dim(`Endpoint [${suggestedBaseUrl}]: `)
1568
+ );
1569
+ baseUrl = baseUrlInput || suggestedBaseUrl;
1570
+ } else {
1571
+ baseUrl = suggestedBaseUrl;
1572
+ info(`Endpoint: ${colors.brand(baseUrl)}`);
1573
+ }
1574
+ const apiKey = await promptHidden(colors.dim("API key (gizli): "));
1575
+ if (!apiKey) {
1576
+ error("API key bo\u015F olamaz. /login iptal edildi.");
1577
+ return;
1578
+ }
1579
+ const sameProvider = current?.provider === preset.id;
1580
+ const candidate = {
1581
+ provider: preset.id,
1582
+ baseUrl,
1583
+ apiKey,
1584
+ model: sameProvider ? current?.model : void 0,
1585
+ systemPrompt: current?.systemPrompt,
1586
+ verifySsl: sameProvider ? current?.verifySsl : void 0,
1587
+ caBundlePath: sameProvider ? current?.caBundlePath : void 0
1588
+ };
1589
+ if (candidate.verifySsl === false) {
1590
+ info("TLS do\u011Frulamas\u0131 KAPALI (verifySsl: false) \u2014 i\xE7/self-signed sunucu.");
1591
+ }
1592
+ info(`Ba\u011Flant\u0131 do\u011Frulan\u0131yor (${preset.label})...`);
1593
+ let models;
1594
+ try {
1595
+ models = await listModels(candidate);
1596
+ } catch (err) {
1597
+ const msg = err instanceof ApiError ? err.message : String(err);
1598
+ if (preset.fallbackModels.length > 0) {
1599
+ info(`Model listesi al\u0131namad\u0131 (${msg}).`);
1600
+ info("Bu sa\u011Flay\u0131c\u0131 i\xE7in bilinen modeller kullan\u0131lacak.");
1601
+ models = [...preset.fallbackModels];
1602
+ } else {
1603
+ error(msg);
1604
+ error("Yap\u0131land\u0131rma kaydedilmedi.");
1605
+ return;
1606
+ }
1607
+ }
1608
+ if (models.length > 0 && (!candidate.model || !models.includes(candidate.model))) {
1609
+ candidate.model = models[0];
1610
+ }
1611
+ await saveConfig(candidate);
1612
+ session.config = candidate;
1613
+ ok(`Giri\u015F ba\u015Far\u0131l\u0131. ${models.length} model bulundu.`);
1614
+ if (candidate.model) info(`Aktif model: ${colors.brand(candidate.model)}`);
1615
+ info("Model de\u011Fi\u015Ftirmek i\xE7in /models yaz\u0131n.");
1616
+ }
1617
+ async function runModels(session) {
1618
+ if (!session.config) {
1619
+ error("\xD6nce /login ile giri\u015F yap\u0131n.");
1620
+ return;
1621
+ }
1622
+ const preset = providerById(session.config.provider);
1623
+ let models;
1624
+ try {
1625
+ models = await listModels(session.config);
1626
+ } catch (err) {
1627
+ const msg = err instanceof ApiError ? err.message : String(err);
1628
+ if (preset.fallbackModels.length > 0) {
1629
+ info(`Model listesi al\u0131namad\u0131 (${msg}). Bilinen modeller g\xF6steriliyor.`);
1630
+ models = [...preset.fallbackModels];
1631
+ } else {
1632
+ error(msg);
1633
+ return;
1634
+ }
1635
+ }
1636
+ if (models.length === 0) {
1637
+ info("Hi\xE7 model bulunamad\u0131.");
1638
+ return;
1639
+ }
1640
+ newline();
1641
+ write(colors.brand(" Model se\xE7in\n\n"));
1642
+ const activeIdx = Math.max(0, models.indexOf(session.config.model ?? ""));
1643
+ const idx = await pick(
1644
+ models.map((m) => ({
1645
+ label: m,
1646
+ description: m === session.config.model ? "\u2014 aktif" : ""
1647
+ })),
1648
+ activeIdx
1649
+ );
1650
+ newline();
1651
+ if (idx === null) return;
1652
+ session.config.model = models[idx];
1653
+ await saveConfig(session.config);
1654
+ ok(`Aktif model: ${models[idx]}`);
1655
+ }
1656
+ async function runResume(session) {
1657
+ if (session.history.length > 0) {
1658
+ saveSessionSync(session.sessionId, session.history);
1659
+ }
1660
+ if (listSessions().length === 0) {
1661
+ info("Kay\u0131tl\u0131 oturum bulunamad\u0131.");
1662
+ return;
1663
+ }
1664
+ const pickedId = await runSessionPicker();
1665
+ if (pickedId === session.sessionId) {
1666
+ info("Bu oturum zaten aktif.");
1667
+ return;
1668
+ }
1669
+ const data = await loadSessionById(pickedId);
1670
+ if (!data) {
1671
+ session.sessionId = pickedId;
1672
+ session.history = [];
1673
+ session.totalTokens = 0;
1674
+ ok("Yeni oturum ba\u015Flat\u0131ld\u0131.");
1675
+ return;
1676
+ }
1677
+ session.sessionId = pickedId;
1678
+ session.history = data.messages;
1679
+ session.totalTokens = 0;
1680
+ setCurrentSessionId(pickedId);
1681
+ ok(`"${data.title}" y\xFCklendi \u2014 ${Math.floor(data.messages.length / 2)} tur.`);
1682
+ }
1683
+ async function runSessionDelete(session, arg) {
1684
+ const sessions = listSessions();
1685
+ const others = sessions.filter((s) => s.id !== session.sessionId);
1686
+ if (arg.trim().toLowerCase() === "all") {
1687
+ if (others.length === 0) {
1688
+ info("Silinecek eski oturum yok.");
1689
+ return;
1690
+ }
1691
+ let deleted = 0;
1692
+ for (const s of others) {
1693
+ try {
1694
+ deleteSession(s.id);
1695
+ deleted++;
1696
+ } catch (err) {
1697
+ error(`Silinemedi (${s.id}): ${err instanceof Error ? err.message : String(err)}`);
1698
+ }
1699
+ }
1700
+ ok(`${deleted} oturum silindi.`);
1701
+ return;
1702
+ }
1703
+ if (others.length === 0) {
1704
+ info("Aktif oturum d\u0131\u015F\u0131nda kay\u0131tl\u0131 oturum yok.");
1705
+ return;
1706
+ }
1707
+ newline();
1708
+ write(colors.brand(" Silinebilir oturumlar\n"));
1709
+ others.forEach((s, i) => {
1710
+ write(` ${colors.brand(String(i + 1).padStart(2))}. ${s.title.slice(0, 48)} ${colors.dim(s.updatedAt.slice(0, 16))}
1711
+ `);
1712
+ });
1713
+ newline();
1714
+ info("T\xFCm\xFCn\xFC silmek i\xE7in: /session-delete all");
1715
+ }
1716
+ async function runJiraLogin(session) {
1717
+ if (!session.config) {
1718
+ error("\xD6nce /login ile LLM ba\u011Flant\u0131s\u0131n\u0131 yap\u0131n.");
1719
+ return;
1720
+ }
1721
+ info("Jira yap\u0131land\u0131rmas\u0131 (/jira-login)");
1722
+ const current = session.config;
1723
+ const urlInput = await prompt(
1724
+ colors.dim(`Jira URL [${current.jiraBaseUrl ?? DEFAULT_JIRA_URL}]: `)
1725
+ );
1726
+ const jiraBaseUrl = urlInput || current.jiraBaseUrl || DEFAULT_JIRA_URL;
1727
+ const jiraToken = await promptHidden(colors.dim("Jira PAT (gizli): "));
1728
+ if (!jiraToken) {
1729
+ error("PAT bo\u015F olamaz. /jira-login iptal edildi.");
1730
+ return;
1731
+ }
1732
+ const candidate = { ...current, jiraBaseUrl, jiraToken };
1733
+ info("Jira ba\u011Flant\u0131s\u0131 do\u011Frulan\u0131yor...");
1734
+ let who;
1735
+ try {
1736
+ who = await validateAuth(candidate);
1737
+ } catch (err) {
1738
+ error(err instanceof ApiError ? err.message : String(err));
1739
+ error("Jira yap\u0131land\u0131rmas\u0131 kaydedilmedi.");
1740
+ return;
1741
+ }
1742
+ await saveConfig(candidate);
1743
+ session.config = candidate;
1744
+ ok(`Jira ba\u011Flant\u0131s\u0131 ba\u015Far\u0131l\u0131. Kullan\u0131c\u0131: ${who}`);
1745
+ }
1746
+ async function runTrelloLogin(session) {
1747
+ if (!session.config) {
1748
+ error("\xD6nce /login ile LLM ba\u011Flant\u0131s\u0131n\u0131 yap\u0131n.");
1749
+ return;
1750
+ }
1751
+ info("Trello yap\u0131land\u0131rmas\u0131 (/trello-login)");
1752
+ info("App Key i\xE7in: https://trello.com/power-ups/admin");
1753
+ const current = session.config;
1754
+ const trelloKey = await prompt(colors.dim("Trello App Key (ESC = iptal): "));
1755
+ if (!trelloKey) {
1756
+ info("\u0130ptal edildi.");
1757
+ return;
1758
+ }
1759
+ const trelloToken = await promptHidden(colors.dim("Trello Token (ESC = iptal): "));
1760
+ if (!trelloToken) {
1761
+ info("\u0130ptal edildi.");
1762
+ return;
1763
+ }
1764
+ const candidate = { ...current, trelloKey, trelloToken };
1765
+ info("Trello ba\u011Flant\u0131s\u0131 do\u011Frulan\u0131yor...");
1766
+ let who;
1767
+ try {
1768
+ who = await validateTrelloAuth(candidate);
1769
+ } catch (err) {
1770
+ error(err instanceof ApiError ? err.message : String(err));
1771
+ error("Trello yap\u0131land\u0131rmas\u0131 kaydedilmedi.");
1772
+ return;
1773
+ }
1774
+ ok(`Trello ba\u011Flant\u0131s\u0131 ba\u015Far\u0131l\u0131. Kullan\u0131c\u0131: ${who}`);
1775
+ info("Panolar y\xFCkleniyor...");
1776
+ let boards = [];
1777
+ try {
1778
+ boards = await fetchBoards(candidate);
1779
+ } catch {
1780
+ boards = [];
1781
+ }
1782
+ if (boards.length > 0) {
1783
+ const picked = await runTrelloBoardPicker(boards);
1784
+ if (picked) {
1785
+ candidate.trelloBoardId = picked.id;
1786
+ candidate.trelloBoardName = picked.name;
1787
+ ok(`Pano se\xE7ildi: ${picked.name}`);
1788
+ }
1789
+ }
1790
+ await saveConfig(candidate);
1791
+ session.config = candidate;
1792
+ }
1793
+ async function savePlan2(key, content) {
1794
+ const filename = `${key}-plan.md`;
1795
+ const path = join4(process.cwd(), filename);
1796
+ let exists = false;
1797
+ try {
1798
+ await fs3.access(path);
1799
+ exists = true;
1800
+ } catch {
1801
+ }
1802
+ if (exists) {
1803
+ const ans = await prompt(
1804
+ colors.dim(`${filename} zaten var. \xDCzerine yaz\u0131ls\u0131n m\u0131? (e/H): `)
1805
+ );
1806
+ if (ans === null) return;
1807
+ if (ans.toLowerCase() !== "e") {
1808
+ info("Dosyaya kaydedilmedi.");
1809
+ return;
1810
+ }
1811
+ }
1812
+ await fs3.writeFile(path, content, "utf8");
1813
+ ok(`Plan kaydedildi: ${filename}`);
1814
+ }
1815
+ async function runJira(session, arg) {
1816
+ if (!session.config) {
1817
+ error("\xD6nce /login ile giri\u015F yap\u0131n.");
1818
+ return null;
1819
+ }
1820
+ if (!session.config.model) {
1821
+ error("Aktif model yok. /models ile bir model se\xE7in.");
1822
+ return null;
1823
+ }
1824
+ if (!arg) {
1825
+ error("Kullan\u0131m: /jira <link-veya-KEY> (\xF6r. /jira PROJ-1234)");
1826
+ return null;
1827
+ }
1828
+ let key;
1829
+ try {
1830
+ key = extractIssueKey(arg);
1831
+ } catch (err) {
1832
+ error(err instanceof ApiError ? err.message : String(err));
1833
+ return null;
1834
+ }
1835
+ info(`Jira'dan ${key} \xE7ekiliyor...`);
1836
+ let issue;
1837
+ try {
1838
+ issue = await fetchIssue(session.config, key);
1839
+ } catch (err) {
1840
+ error(err instanceof ApiError ? err.message : String(err));
1841
+ return null;
1842
+ }
1843
+ write("\n" + chalk3.yellow.bold(`${issue.key}`) + chalk3.dim(" \u203A ") + chalk3.yellow.bold(issue.summary) + chalk3.dim(` [${issue.issueType}]`) + "\n\n");
1844
+ info("Proje taran\u0131yor...");
1845
+ const context = await gatherContext(process.cwd(), issue);
1846
+ info(
1847
+ `${context.files.length} dosya okundu` + (context.skipped > 0 ? `, ${context.skipped} atland\u0131 (s\u0131n\u0131r).` : ".")
1848
+ );
1849
+ if (context.files.length === 0) {
1850
+ info("\u0130lgili dosya bulunamad\u0131 \u2014 plan task a\xE7\u0131klamas\u0131na g\xF6re genel \xE7\u0131kacak.");
1851
+ }
1852
+ info(`Plan \xFCretiliyor (model: ${session.config.model})...`);
1853
+ newline();
1854
+ write(colors.dim("\u2500".repeat(50) + "\n"));
1855
+ const jiraMd = createMarkdownStream();
1856
+ let plan;
1857
+ try {
1858
+ plan = await generatePlan(session.config, issue, context, (c) => {
1859
+ write(jiraMd.push(c));
1860
+ }, process.cwd());
1861
+ write(jiraMd.flush());
1862
+ } catch (err) {
1863
+ write(jiraMd.flush());
1864
+ newline();
1865
+ error(err instanceof ApiError ? err.message : String(err));
1866
+ return null;
1867
+ }
1868
+ newline();
1869
+ write(colors.dim("\u2500".repeat(50) + "\n"));
1870
+ if (plan.trim().length === 0) {
1871
+ info("(bo\u015F yan\u0131t) \u2014 plan kaydedilmedi.");
1872
+ return null;
1873
+ }
1874
+ return { key, plan };
1875
+ }
1876
+ async function runJiraSave(key, plan) {
1877
+ await savePlan2(key, plan);
1878
+ }
1879
+ async function runTrello(session, arg, startThinking2) {
1880
+ if (!session.config) {
1881
+ error("\xD6nce /login ile giri\u015F yap\u0131n.");
1882
+ return null;
1883
+ }
1884
+ if (!session.config.model) {
1885
+ error("Aktif model yok. /models ile se\xE7in.");
1886
+ return null;
1887
+ }
1888
+ if (!arg) {
1889
+ error("Kullan\u0131m: /trello <shortLink-veya-URL>");
1890
+ return null;
1891
+ }
1892
+ let cardId;
1893
+ try {
1894
+ cardId = extractCardId(arg);
1895
+ } catch (err) {
1896
+ error(err instanceof ApiError ? err.message : String(err));
1897
+ return null;
1898
+ }
1899
+ info(`Kart \xE7ekiliyor: ${cardId}...`);
1900
+ let stopThink = startThinking2?.();
1901
+ let card;
1902
+ try {
1903
+ card = await fetchCard(session.config, cardId);
1904
+ stopThink?.();
1905
+ stopThink = void 0;
1906
+ } catch (err) {
1907
+ stopThink?.();
1908
+ error(err instanceof ApiError ? err.message : String(err));
1909
+ return null;
1910
+ }
1911
+ const boardLabel = session.config.trelloBoardName ? chalk3.yellow.bold(session.config.trelloBoardName) + chalk3.dim(" \u203A ") : "";
1912
+ const cardLabel = chalk3.yellow.bold(card.name);
1913
+ const listLabel = chalk3.dim(` [${card.listName}]`);
1914
+ const labelTags = card.labels.length ? " " + card.labels.map((l) => chalk3.bgYellow.black(` ${l} `)).join(" ") : "";
1915
+ write("\n" + boardLabel + cardLabel + listLabel + labelTags + "\n\n");
1916
+ const issue = {
1917
+ key: card.shortLink,
1918
+ summary: card.name,
1919
+ description: card.desc + (card.labels.length ? `
1920
+ Etiketler: ${card.labels.join(", ")}` : ""),
1921
+ issueType: card.listName
1922
+ };
1923
+ info("Proje taran\u0131yor...");
1924
+ stopThink = startThinking2?.();
1925
+ const context = await gatherContext(process.cwd(), issue);
1926
+ stopThink?.();
1927
+ stopThink = void 0;
1928
+ ok(
1929
+ `${context.files.length} dosya okundu` + (context.skipped > 0 ? `, ${context.skipped} atland\u0131.` : ".")
1930
+ );
1931
+ info(`Plan \xFCretiliyor (model: ${session.config.model})...`);
1932
+ stopThink = startThinking2?.();
1933
+ newline();
1934
+ write(colors.dim("\u2500".repeat(50) + "\n"));
1935
+ const mdStream = createMarkdownStream();
1936
+ let plan;
1937
+ try {
1938
+ plan = await generatePlan(session.config, issue, context, (c) => {
1939
+ if (stopThink) {
1940
+ stopThink();
1941
+ stopThink = void 0;
1942
+ }
1943
+ write(mdStream.push(c));
1944
+ }, process.cwd());
1945
+ stopThink?.();
1946
+ write(mdStream.flush());
1947
+ } catch (err) {
1948
+ stopThink?.();
1949
+ write(mdStream.flush());
1950
+ newline();
1951
+ error(err instanceof ApiError ? err.message : String(err));
1952
+ return null;
1953
+ }
1954
+ newline();
1955
+ write(colors.dim("\u2500".repeat(50) + "\n"));
1956
+ if (plan.trim().length === 0) {
1957
+ info("(bo\u015F yan\u0131t) \u2014 plan kaydedilmedi.");
1958
+ return null;
1959
+ }
1960
+ return { id: card.shortLink, plan };
1961
+ }
1962
+ async function runTrelloSave(id, plan) {
1963
+ await savePlan2(id, plan);
1964
+ }
1965
+ async function runTrelloPickCard(session) {
1966
+ if (!session.config) return null;
1967
+ let boards;
1968
+ try {
1969
+ boards = await fetchBoards(session.config);
1970
+ } catch {
1971
+ return null;
1972
+ }
1973
+ const savedIdx = boards.findIndex((b) => b.id === session.config?.trelloBoardId);
1974
+ const boardPick = await runTrelloBoardPicker(boards, savedIdx >= 0 ? savedIdx : 0);
1975
+ if (!boardPick) return null;
1976
+ let currentBoardId = boardPick.id;
1977
+ let currentBoardName = boardPick.name;
1978
+ outer: while (true) {
1979
+ let cards;
1980
+ try {
1981
+ cards = await fetchBoardCards(session.config, currentBoardId);
1982
+ } catch (err) {
1983
+ error(err instanceof ApiError ? err.message : String(err));
1984
+ return null;
1985
+ }
1986
+ const cardPick = await runTrelloCardPicker(cards, currentBoardName);
1987
+ if (cardPick === null) return null;
1988
+ if (cardPick === TRELLO_BACK) {
1989
+ const bp = await runTrelloBoardPicker(boards, boards.findIndex((b) => b.id === currentBoardId));
1990
+ if (!bp) return null;
1991
+ currentBoardId = bp.id;
1992
+ currentBoardName = bp.name;
1993
+ continue outer;
1994
+ }
1995
+ return { shortLink: cardPick.shortLink, boardId: currentBoardId, boardName: currentBoardName };
1996
+ }
1997
+ }
1998
+ async function runExport(session) {
1999
+ if (session.history.length === 0) {
2000
+ info("D\u0131\u015Fa aktar\u0131lacak konu\u015Fma yok.");
2001
+ return;
2002
+ }
2003
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2004
+ const title = session.history.find((m) => m.role === "user")?.content.slice(0, 40).replace(/[^a-zA-Z0-9ğüşıöçĞÜŞİÖÇ\s]/g, "").trim().replace(/\s+/g, "-") ?? "konusma";
2005
+ const filename = `${date}-${title}.md`;
2006
+ const path = join4(process.cwd(), filename);
2007
+ let md = `# ${title}
2008
+ _${date}_
2009
+
2010
+ `;
2011
+ for (const m of session.history) {
2012
+ md += m.role === "user" ? `## Siz
2013
+
2014
+ ${m.content}
2015
+
2016
+ ` : `## LASTGEN
2017
+
2018
+ ${m.content}
2019
+
2020
+ `;
2021
+ }
2022
+ await fs3.writeFile(path, md, "utf-8");
2023
+ ok(`Konu\u015Fma aktar\u0131ld\u0131: ${filename}`);
2024
+ }
2025
+ async function runPrompt(session) {
2026
+ if (!session.config) {
2027
+ error("\xD6nce /login yap\u0131n.");
2028
+ return;
2029
+ }
2030
+ const current = session.config.systemPrompt ?? "";
2031
+ newline();
2032
+ write(colors.brand(" Sistem Prompt\n\n"));
2033
+ if (current) {
2034
+ write(colors.dim(" Mevcut:\n"));
2035
+ write(` ${current.slice(0, 200)}${current.length > 200 ? "\u2026" : ""}
2036
+
2037
+ `);
2038
+ } else {
2039
+ write(colors.dim(" (hen\xFCz ayarlanmam\u0131\u015F)\n\n"));
2040
+ }
2041
+ const input = await prompt(colors.dim(" Yeni sistem prompt (bo\u015F = sil, Enter = iptal): "));
2042
+ if (input === void 0 || input === null) return;
2043
+ if (input.trim() === "") {
2044
+ session.config.systemPrompt = void 0;
2045
+ await saveConfig(session.config);
2046
+ ok("Sistem prompt silindi.");
2047
+ } else {
2048
+ session.config.systemPrompt = input.trim();
2049
+ await saveConfig(session.config);
2050
+ ok("Sistem prompt g\xFCncellendi.");
2051
+ }
2052
+ }
2053
+ function maskKey(key) {
2054
+ if (key.length <= 8) return "\u2022\u2022\u2022\u2022";
2055
+ return `${key.slice(0, 4)}\u2026${key.slice(-4)}`;
2056
+ }
2057
+ function printConfig(session) {
2058
+ const c = session.config;
2059
+ if (!c) {
2060
+ error("Yap\u0131land\u0131rma yok. /login yap\u0131n.");
2061
+ return;
2062
+ }
2063
+ newline();
2064
+ write(colors.brand(" Ba\u011Flant\u0131 ayarlar\u0131\n"));
2065
+ const rows = [
2066
+ ["Sa\u011Flay\u0131c\u0131", providerById(c.provider).label],
2067
+ ["Endpoint", c.baseUrl],
2068
+ ["Model", c.model ?? "(se\xE7ilmedi)"],
2069
+ ["API key", c.apiKey ? maskKey(c.apiKey) : "(yok)"],
2070
+ ["TLS do\u011Frulama", c.verifySsl === false ? "KAPALI" : "a\xE7\u0131k"],
2071
+ ["CA bundle", c.caBundlePath ?? "(yok)"],
2072
+ ["Jira URL", c.jiraBaseUrl ?? "(yok)"],
2073
+ ["Jira PAT", c.jiraToken ? maskKey(c.jiraToken) : "(yok)"],
2074
+ ["Trello Key", c.trelloKey ? maskKey(c.trelloKey) : "(yok)"],
2075
+ ["Trello Token", c.trelloToken ? maskKey(c.trelloToken) : "(yok)"]
2076
+ ];
2077
+ for (const [k, v] of rows) {
2078
+ write(` ${colors.dim(k.padEnd(14))} ${v}
2079
+ `);
2080
+ }
2081
+ newline();
2082
+ }
2083
+ function printHelp() {
2084
+ newline();
2085
+ write(colors.brand(" Lastgen komutlar\u0131\n"));
2086
+ const rows = [
2087
+ ["/login", "API endpoint ve key gir, kaydet"],
2088
+ ["/models", "Modelleri listele ve aktif modeli se\xE7"],
2089
+ ["/jira", "Jira task linkinden geli\u015Ftirme plan\u0131 \xFCret"],
2090
+ ["/jira-login", "Jira URL ve PAT ayarla"],
2091
+ ["/trello <shortLink>", "Trello kart\u0131ndan geli\u015Ftirme plan\u0131 \xFCret"],
2092
+ ["/trello-login", "Trello App Key ve Token ayarla"],
2093
+ ["/init", "Projeyi tara, LASTGEN.md ba\u011Flam dosyas\u0131 olu\u015Ftur (write-plan'\u0131 h\u0131zland\u0131r\u0131r)"],
2094
+ ["/plan", "Kaydedilen bir plan\u0131 se\xE7, sohbet ba\u011Flam\u0131na y\xFCkle \u2014 planla ilgili soru sor"],
2095
+ ["/trello-write-plan", "Trello kart\u0131ndan Agent uygulama plan\u0131 \xFCret ve kaydet"],
2096
+ ["/trello-execute-plan", "Kaydedilen Trello plan\u0131n\u0131 Agent ile uygula"],
2097
+ ["/jira-write-plan", "Jira issue'sinden Agent uygulama plan\u0131 \xFCret ve kaydet"],
2098
+ ["/jira-execute-plan", "Kaydedilen Jira plan\u0131n\u0131 Agent ile uygula"],
2099
+ ["/config", "Mevcut ba\u011Flant\u0131 ayarlar\u0131n\u0131 g\xF6ster"],
2100
+ ["/clear", "Ekran\u0131 temizle"],
2101
+ ["/reset", "Sohbet ge\xE7mi\u015Fini s\u0131f\u0131rla"],
2102
+ ["/resume", "Kaydedilmi\u015F ge\xE7mi\u015F konu\u015Fmay\u0131 geri y\xFCkle"],
2103
+ ["/session-delete", "Eski oturumlar\u0131 listele/sil (all \u2192 t\xFCm\xFCn\xFC sil)"],
2104
+ ["/export", "Aktif konu\u015Fmay\u0131 Markdown dosyas\u0131na aktar"],
2105
+ ["/prompt", "Sistem prompt'u g\xF6r\xFCnt\xFCle veya g\xFCncelle"],
2106
+ ["/help", "Bu yard\u0131m\u0131 g\xF6ster"],
2107
+ ["/exit", "\xC7\u0131k\u0131\u015F (Ctrl+C ile de \xE7\u0131kabilirsiniz)"]
2108
+ ];
2109
+ for (const [cmd, desc] of rows) {
2110
+ write(` ${colors.user(cmd.padEnd(12))} ${colors.dim(desc)}
2111
+ `);
2112
+ }
2113
+ newline();
2114
+ }
2115
+ async function runWritePlan(session, task, boardName, projectRoot, onPlanStart, onPlanReady, onProgress, onToolCall, onInsight, extraContext) {
2116
+ if (!session.config) {
2117
+ error("\xD6nce /login yap\u0131n.");
2118
+ return;
2119
+ }
2120
+ if (!session.config.model) {
2121
+ error("Aktif model yok.");
2122
+ return;
2123
+ }
2124
+ if (extraContext?.trim()) {
2125
+ task = { ...task, desc: (task.desc?.trim() ? task.desc + "\n\n" : "") + `## Ekstra Bilgi
2126
+ ${extraContext.trim()}` };
2127
+ }
2128
+ newline();
2129
+ write(chalk3.yellow.bold(` ${boardName}`) + chalk3.dim(" \u203A ") + chalk3.yellow.bold(task.name) + "\n");
2130
+ write(chalk3.dim(` [${task.listName}]`) + (task.labels.length ? chalk3.dim(` \u2022 ${task.labels.join(", ")}`) : "") + "\n");
2131
+ newline();
2132
+ const testCommand = await detectTestCommand(projectRoot);
2133
+ info("Plan \xFCretiliyor...");
2134
+ onPlanStart?.();
2135
+ let planMarkdown = "";
2136
+ let planStarted = false;
2137
+ try {
2138
+ planMarkdown = await generateAgentPlan(
2139
+ session.config,
2140
+ task,
2141
+ boardName,
2142
+ projectRoot,
2143
+ testCommand,
2144
+ (type, text) => {
2145
+ if (type === "tool") {
2146
+ if (onToolCall) {
2147
+ onToolCall(text);
2148
+ } else {
2149
+ write(chalk3.hex("#c678dd").bold(" \u2699 ") + colors.dim(text) + "\n");
2150
+ }
2151
+ } else {
2152
+ if (!planStarted) {
2153
+ planStarted = true;
2154
+ }
2155
+ const safe = typeof text === "string" ? text : String(text ?? "");
2156
+ if (onPlanReady) {
2157
+ onPlanReady(safe);
2158
+ } else {
2159
+ newline();
2160
+ write(colors.dim("\u2500".repeat(50) + "\n"));
2161
+ write(safe);
2162
+ }
2163
+ }
2164
+ },
2165
+ onProgress,
2166
+ onInsight
2167
+ );
2168
+ } catch (err) {
2169
+ throw err;
2170
+ }
2171
+ newline();
2172
+ write(colors.dim("\u2500".repeat(50) + "\n"));
2173
+ const FAILURE_MARKERS = [
2174
+ "(plan \xFCretilemedi \u2014 maksimum iterasyon)",
2175
+ "(plan \xFCretilemedi"
2176
+ ];
2177
+ const planFailed = !planMarkdown.trim() || FAILURE_MARKERS.some((m) => planMarkdown.includes(m));
2178
+ if (planFailed) {
2179
+ error("Plan \xFCretilemedi \u2014 model maksimum iterasyona ula\u015Ft\u0131. Kaydedilmedi.");
2180
+ info("\u0130pucu: LASTGEN.md g\xFCncel de\u011Filse /init --force \xE7al\u0131\u015Ft\u0131r\u0131n, ard\u0131ndan tekrar deneyin.");
2181
+ return;
2182
+ }
2183
+ const now = /* @__PURE__ */ new Date();
2184
+ const planId = `${now.toISOString().replace(/[:.]/g, "-").slice(0, 19)}-${task.id}`;
2185
+ const plan = {
2186
+ id: planId,
2187
+ trelloCardId: task.id,
2188
+ trelloCardName: task.name,
2189
+ boardName,
2190
+ createdAt: now.toISOString(),
2191
+ projectRoot,
2192
+ testCommand,
2193
+ planMarkdown
2194
+ };
2195
+ const filePath = await savePlan(projectRoot, plan);
2196
+ ok(`Plan kaydedildi: ${filePath}`);
2197
+ info(`Uygulamak i\xE7in: /${task.source === "Jira" ? "jira" : "trello"}-execute-plan`);
2198
+ }
2199
+ async function runTrelloWritePlan(session, card, boardName, projectRoot, onPlanStart, onPlanReady, onProgress, onToolCall, onInsight, extraContext) {
2200
+ const task = {
2201
+ id: card.shortLink,
2202
+ name: card.name,
2203
+ desc: card.desc,
2204
+ listName: card.listName,
2205
+ labels: card.labels,
2206
+ source: "Trello"
2207
+ };
2208
+ await runWritePlan(session, task, boardName, projectRoot, onPlanStart, onPlanReady, onProgress, onToolCall, onInsight, extraContext);
2209
+ }
2210
+ async function runJiraWritePlan(session, issueKey, projectRoot, onPlanStart, onPlanReady, onProgress, onToolCall, onTaskReady, onInsight, extraContext) {
2211
+ if (!session.config) {
2212
+ error("\xD6nce /login yap\u0131n.");
2213
+ return;
2214
+ }
2215
+ if (!session.config.jiraToken || !session.config.jiraBaseUrl) {
2216
+ info("Jira yap\u0131land\u0131rmas\u0131 yok \u2014 \xF6nce ayarlayal\u0131m.");
2217
+ await runJiraLogin(session);
2218
+ if (!session.config.jiraToken) return;
2219
+ }
2220
+ let key;
2221
+ try {
2222
+ key = extractIssueKey(issueKey);
2223
+ } catch (err) {
2224
+ error(err instanceof ApiError ? err.message : String(err));
2225
+ return;
2226
+ }
2227
+ info(`Jira'dan ${key} \xE7ekiliyor...`);
2228
+ let issue;
2229
+ try {
2230
+ issue = await fetchIssue(session.config, key);
2231
+ } catch (err) {
2232
+ error(err instanceof ApiError ? err.message : String(err));
2233
+ return;
2234
+ }
2235
+ onTaskReady?.(issue.summary, issue.description ?? "");
2236
+ const task = {
2237
+ id: issue.key,
2238
+ name: issue.summary,
2239
+ desc: issue.description,
2240
+ listName: issue.issueType,
2241
+ labels: [],
2242
+ source: "Jira"
2243
+ };
2244
+ const boardName = session.config.jiraBaseUrl?.replace(/https?:\/\//, "").split("/")[0] ?? "Jira";
2245
+ await runWritePlan(session, task, boardName, projectRoot, onPlanStart, onPlanReady, onProgress, onToolCall, onInsight, extraContext);
2246
+ }
2247
+ async function executeAgentPlan(session, plan, onStep, onProgress, abortSignal, executionMode, onStepApproval, onToolApproval) {
2248
+ if (!session.config) {
2249
+ error("\xD6nce /login yap\u0131n.");
2250
+ return;
2251
+ }
2252
+ newline();
2253
+ write(colors.dim("\u2500".repeat(50) + "\n"));
2254
+ const startMs = Date.now();
2255
+ let lastTokens = 0;
2256
+ let summary = "";
2257
+ const stepRecords = [];
2258
+ let currentStep = null;
2259
+ let lastPatchArgs = null;
2260
+ let verificationRecord = null;
2261
+ let inVerification = false;
2262
+ let verificationCmd = "";
2263
+ await runAgentLoop({
2264
+ config: session.config,
2265
+ projectRoot: plan.projectRoot,
2266
+ plan: plan.planMarkdown,
2267
+ testCommand: plan.testCommand,
2268
+ maxIterations: 30,
2269
+ executionMode,
2270
+ onStepApproval,
2271
+ onToolApproval,
2272
+ abortSignal,
2273
+ onProgress: (tokens, elapsed) => {
2274
+ lastTokens = tokens;
2275
+ onProgress?.(tokens, elapsed);
2276
+ },
2277
+ onStep: (step) => {
2278
+ const c = typeof step.content === "string" ? step.content : String(step.content ?? "");
2279
+ if (step.type === "thinking") {
2280
+ const stepMatch = c.match(/▶\s*Adım\s*(\d+)\/\d+:\s*(.+)/);
2281
+ if (stepMatch) {
2282
+ currentStep = {
2283
+ stepNumber: parseInt(stepMatch[1]),
2284
+ description: stepMatch[2].trim(),
2285
+ targetFile: "",
2286
+ status: "skipped",
2287
+ patches: [],
2288
+ toolCalls: []
2289
+ };
2290
+ stepRecords.push(currentStep);
2291
+ inVerification = false;
2292
+ }
2293
+ const verMatch = c.match(/Doğrulama:\s*(.+)/);
2294
+ if (verMatch) {
2295
+ inVerification = true;
2296
+ verificationCmd = verMatch[1].trim();
2297
+ }
2298
+ if (currentStep) {
2299
+ if (c.includes("tamamlanamad\u0131")) currentStep.status = "failed";
2300
+ }
2301
+ if (c.match(/\d+\/\d+ adım/)) summary = c;
2302
+ }
2303
+ if (step.type === "tool_call") {
2304
+ const args = step.toolArgs ?? {};
2305
+ const tool = step.toolName ?? "";
2306
+ if (currentStep) {
2307
+ currentStep.toolCalls.push({ tool, args });
2308
+ if (tool === "apply_patch") lastPatchArgs = args;
2309
+ if (tool === "read_file" && args.path && !currentStep.targetFile)
2310
+ currentStep.targetFile = args.path;
2311
+ }
2312
+ }
2313
+ if (step.type === "tool_result") {
2314
+ if (currentStep && lastPatchArgs) {
2315
+ const patch = {
2316
+ path: lastPatchArgs.path ?? "",
2317
+ searchString: lastPatchArgs.search_string ?? "",
2318
+ replaceString: lastPatchArgs.replace_string ?? "",
2319
+ success: step.success ?? false
2320
+ };
2321
+ currentStep.patches.push(patch);
2322
+ if (step.success) currentStep.status = "done";
2323
+ lastPatchArgs = null;
2324
+ }
2325
+ if (inVerification) {
2326
+ verificationRecord = {
2327
+ command: verificationCmd,
2328
+ output: c.slice(0, 2e3),
2329
+ success: step.success ?? false
2330
+ };
2331
+ inVerification = false;
2332
+ }
2333
+ }
2334
+ if (step.type === "complete") summary = c;
2335
+ switch (step.type) {
2336
+ case "thinking": {
2337
+ if (/▶\s*Adım\s*\d+\/\d+:/.test(c)) {
2338
+ write(chalk3.hex("#56b6c2").bold("\u25C6 ") + chalk3.hex("#56b6c2").bold(formatInstructions(c)) + "\n");
2339
+ } else if (/^Adım\s*\d+\/\d+\s*tamamlandı/.test(c)) {
2340
+ const stepNum = c.match(/Adım\s*(\d+)/)?.[1] ?? "?";
2341
+ const total = c.match(/Adım\s*\d+\/(\d+)/)?.[1] ?? "";
2342
+ const fileMatch = c.match(/değiştirilen:\s*(.+)$/);
2343
+ const label = fileMatch ? chalk3.dim(" \u2014 de\u011Fi\u015Ftirilen: ") + chalk3.hex("#e5c07b")(formatFilePath(fileMatch[1])) : "";
2344
+ const totalLabel = total ? `/${total}` : "";
2345
+ write(chalk3.greenBright.bold("\u2713 ") + chalk3.greenBright.bold(`Ad\u0131m ${stepNum}${totalLabel} tamamland\u0131`) + label + "\n");
2346
+ } else if (/^Adım\s*\d+\/\d+\s*tamamlanamadı/.test(c)) {
2347
+ const stepNum = c.match(/Adım\s*(\d+)/)?.[1] ?? "?";
2348
+ const total = c.match(/Adım\s*\d+\/(\d+)/)?.[1] ?? "";
2349
+ const reasonMatch = c.match(/—\s*(.+)$/);
2350
+ const reason = reasonMatch ? chalk3.dim(" \u2014 ") + chalk3.redBright(reasonMatch[1]) : "";
2351
+ const totalLabel = total ? `/${total}` : "";
2352
+ write(chalk3.redBright.bold("\u2717 ") + chalk3.redBright.bold(`Ad\u0131m ${stepNum}${totalLabel} tamamlanamad\u0131`) + reason + "\n");
2353
+ } else if (/^Doğrulama:/.test(c)) {
2354
+ write(chalk3.hex("#e5c07b").bold("\u25C6 ") + chalk3.hex("#e5c07b")(c) + "\n");
2355
+ } else if (/^Plan:/.test(c)) {
2356
+ write(chalk3.hex("#c678dd").bold("\u25C6 ") + chalk3.hex("#c678dd").bold(c) + "\n");
2357
+ } else if (/^↩\s*Önbellekten/.test(c)) {
2358
+ write(chalk3.hex("#17a2b8")(" \u21A9 ") + chalk3.dim(c.replace(/^↩\s*/, "")) + "\n");
2359
+ } else {
2360
+ write(chalk3.hex("#17a2b8")("\u25C6 ") + chalk3.hex("#17a2b8")(formatInstructions(c)) + "\n");
2361
+ }
2362
+ break;
2363
+ }
2364
+ case "tool_call": {
2365
+ const args = step.toolArgs ?? {};
2366
+ const rich = formatToolCall(step.toolName ?? "", args);
2367
+ write(" " + rich + "\n");
2368
+ break;
2369
+ }
2370
+ case "tool_result": {
2371
+ const preview = c.slice(0, 400) + (c.length > 400 ? "\n \u2026" : "");
2372
+ const isEmpty = /bulunamadı|boş dizin|eşleşme bulunamadı/i.test(c);
2373
+ if (isEmpty) {
2374
+ write(chalk3.hex("#6a737d")(" \u25C7 ") + chalk3.dim(preview) + "\n\n");
2375
+ } else {
2376
+ write((step.success ? chalk3.greenBright(" \u2713 ") : chalk3.redBright(" \u2717 ")) + chalk3.dim(preview) + "\n\n");
2377
+ }
2378
+ break;
2379
+ }
2380
+ case "complete":
2381
+ write(chalk3.dim("\u2500".repeat(50) + "\n"));
2382
+ ok("G\xF6rev tamamland\u0131.");
2383
+ write(formatInstructions(c) + "\n");
2384
+ break;
2385
+ case "error":
2386
+ error(c);
2387
+ break;
2388
+ }
2389
+ onStep(step.type, c, step.success);
2390
+ }
2391
+ });
2392
+ if (abortSignal?.aborted) return;
2393
+ try {
2394
+ const now = /* @__PURE__ */ new Date();
2395
+ const pad = (n) => String(n).padStart(2, "0");
2396
+ const id = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
2397
+ const record = {
2398
+ id,
2399
+ planId: plan.id,
2400
+ trelloCardId: plan.trelloCardId,
2401
+ trelloCardName: plan.trelloCardName,
2402
+ boardName: plan.boardName,
2403
+ projectRoot: plan.projectRoot,
2404
+ executedAt: now.toISOString(),
2405
+ totalTokens: lastTokens,
2406
+ elapsedMs: Date.now() - startMs,
2407
+ steps: stepRecords,
2408
+ verification: verificationRecord,
2409
+ summary
2410
+ };
2411
+ const filePath = await saveExecuteRecord(plan.projectRoot, plan, record);
2412
+ write(colors.dim(`
2413
+ \u25C8 Execute kayd\u0131: ${filePath}
2414
+ `));
2415
+ } catch {
2416
+ }
2417
+ }
2418
+ async function handleCommand(input, session) {
2419
+ if (!input.startsWith("/")) return { handled: false };
2420
+ const { cmd, arg } = parseCommand(input);
2421
+ switch (cmd) {
2422
+ case "login":
2423
+ await runLogin(session);
2424
+ return { handled: true };
2425
+ case "models":
2426
+ await runModels(session);
2427
+ return { handled: true };
2428
+ case "config":
2429
+ printConfig(session);
2430
+ return { handled: true };
2431
+ case "clear":
2432
+ return { handled: true, clearScreen: true };
2433
+ case "reset":
2434
+ session.history = [];
2435
+ ok("Sohbet ge\xE7mi\u015Fi s\u0131f\u0131rland\u0131.");
2436
+ return { handled: true };
2437
+ case "resume": {
2438
+ await runResume(session);
2439
+ return { handled: true };
2440
+ }
2441
+ case "help":
2442
+ printHelp();
2443
+ return { handled: true };
2444
+ case "exit":
2445
+ case "quit":
2446
+ return { handled: true, exit: true };
2447
+ case "jira": {
2448
+ const keys = arg.trim().split(/\s+/).filter(Boolean);
2449
+ if (keys.length === 0) {
2450
+ error("Kullan\u0131m: /jira <KEY> [KEY2 KEY3...]");
2451
+ return { handled: true };
2452
+ }
2453
+ for (const key of keys) {
2454
+ const result = await runJira(session, key);
2455
+ if (result) await runJiraSave(result.key, result.plan);
2456
+ }
2457
+ return { handled: true };
2458
+ }
2459
+ case "session-delete": {
2460
+ await runSessionDelete(session, arg);
2461
+ return { handled: true };
2462
+ }
2463
+ case "export":
2464
+ await runExport(session);
2465
+ return { handled: true };
2466
+ case "prompt":
2467
+ await runPrompt(session);
2468
+ return { handled: true };
2469
+ case "jira-login":
2470
+ await runJiraLogin(session);
2471
+ return { handled: true };
2472
+ case "trello": {
2473
+ const trelloKeys = arg.trim().split(/\s+/).filter(Boolean);
2474
+ if (trelloKeys.length === 0) {
2475
+ error("Kullan\u0131m: /trello <shortLink-veya-URL>");
2476
+ return { handled: true };
2477
+ }
2478
+ for (const key of trelloKeys) {
2479
+ const result = await runTrello(session, key);
2480
+ if (result) await runTrelloSave(result.id, result.plan);
2481
+ }
2482
+ return { handled: true };
2483
+ }
2484
+ case "trello-login":
2485
+ await runTrelloLogin(session);
2486
+ return { handled: true };
2487
+ case "init": {
2488
+ if (!session.config) {
2489
+ error("\xD6nce /login yap\u0131n.");
2490
+ return { handled: true };
2491
+ }
2492
+ const projectRoot = process.cwd();
2493
+ const exists = await hasLastgenContext(projectRoot);
2494
+ if (exists && !arg.includes("--force")) {
2495
+ info(`LASTGEN.md zaten mevcut. Yeniden olu\u015Fturmak i\xE7in /init --force yaz\u0131n.`);
2496
+ info(`Konum: ${lastgenContextPath(projectRoot)}`);
2497
+ return { handled: true };
2498
+ }
2499
+ info(`Proje ba\u011Flam\u0131 olu\u015Fturuluyor: ${projectRoot}`);
2500
+ try {
2501
+ await generateLastgenContext(session.config, projectRoot, (msg) => info(msg));
2502
+ ok(`LASTGEN.md olu\u015Fturuldu: ${lastgenContextPath(projectRoot)}`);
2503
+ info("Art\u0131k /trello-write-plan daha az ara\xE7 \xE7a\u011Fr\u0131s\u0131yla \xE7al\u0131\u015Facak.");
2504
+ } catch (err) {
2505
+ error(`/init ba\u015Far\u0131s\u0131z: ${String(err)}`);
2506
+ }
2507
+ return { handled: true };
2508
+ }
2509
+ case "trello-write-plan":
2510
+ case "trello-execute-plan":
2511
+ case "jira-write-plan":
2512
+ case "jira-execute-plan":
2513
+ info(`/${cmd} komutu TUI \xFCzerinden \xE7al\u0131\u015F\u0131r.`);
2514
+ return { handled: true };
2515
+ case "logout":
2516
+ await clearConfig();
2517
+ session.config = null;
2518
+ ok("Yap\u0131land\u0131rma silindi. Tekrar /login yap\u0131n.");
2519
+ return { handled: true };
2520
+ default:
2521
+ error(`Bilinmeyen komut: /${cmd}. /help deneyin.`);
2522
+ return { handled: true };
2523
+ }
2524
+ }
2525
+
2526
+ // src/tui.ts
2527
+ import { stdin as stdin2, stdout as stdout2 } from "process";
2528
+ import chalk4 from "chalk";
2529
+ var WELCOMES = [
2530
+ "Merhaba! Bug\xFCn ne ke\u015Ffediyoruz?",
2531
+ "Tekrar ho\u015F geldiniz! Konu\u015Fmaya haz\u0131r\u0131m.",
2532
+ "Selam! Sizi yeniden g\xF6rmek g\xFCzel.",
2533
+ "Merhaba! Haf\u0131zam taze, sorular\u0131n\u0131z haz\u0131r m\u0131?",
2534
+ "Ho\u015F geldiniz! Bug\xFCn ne \xF6\u011Freniyoruz?",
2535
+ "Merhaba! Ge\xE7en seferden devam edelim mi?",
2536
+ "Selam! D\xFC\u015F\xFCnmeye haz\u0131r\u0131m, siz de haz\u0131r m\u0131s\u0131n\u0131z?",
2537
+ "Ho\u015F geldiniz! Beraber neler yapabiliriz acaba?"
2538
+ ];
2539
+ var FAREWELLS = [
2540
+ "G\xF6r\xFC\u015F\xFCr\xFCz! Konu\u015Fmam\u0131z\u0131 saklad\u0131m, bir dahaki sefere kald\u0131\u011F\u0131m\u0131z yerden devam ederiz.",
2541
+ "G\xFCle g\xFCle! Token harcamak g\xFCzeldi. Ge\xE7mi\u015Fimiz g\xFCvende.",
2542
+ "Elveda! D\xFC\u015F\xFCncelerim silinmiyor, sadece uyku moduna ge\xE7iyor...",
2543
+ "Kapan\u0131yorum. Merak etme, her \u015Feyi hat\u0131rl\u0131yorum (ger\xE7ekten).",
2544
+ "Bye! Bir dahaki sefere daha az hall\xFCsinasyon yapar\u0131m. Belki.",
2545
+ "G\xF6r\xFC\u015Fmek \xFCzere! Ge\xE7mi\u015Fimizi kaydettim, gelecek parlak.",
2546
+ "\xC7\u0131k\u0131\u015F yap\u0131l\u0131yor... Sizi \xF6zleyece\u011Fim (bu bir LLM vaadi, ciddiye al\u0131n).",
2547
+ "Ho\u015F\xE7a kal\u0131n! `.lastgen/history.json` an\u0131lar\u0131m\u0131z\u0131n evidir."
2548
+ ];
2549
+ var COMMANDS_WITH_PROMPT = /* @__PURE__ */ new Set(["login", "models", "jira-login", "resume"]);
2550
+ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
2551
+ "login",
2552
+ "models",
2553
+ "jira",
2554
+ "jira-login",
2555
+ "trello",
2556
+ "trello-login",
2557
+ "trello-write-plan",
2558
+ "trello-execute-plan",
2559
+ "jira-write-plan",
2560
+ "jira-execute-plan",
2561
+ "plan",
2562
+ "init",
2563
+ "config",
2564
+ "clear",
2565
+ "reset",
2566
+ "resume",
2567
+ "help",
2568
+ "exit",
2569
+ "quit",
2570
+ "logout",
2571
+ "export",
2572
+ "prompt",
2573
+ "session-delete"
2574
+ ]);
2575
+ var COMMAND_LIST = [
2576
+ { cmd: "trello-write-plan", desc: "Trello kart\u0131ndan geli\u015Ftirme plan\u0131 olu\u015Ftur" },
2577
+ { cmd: "trello-execute-plan", desc: "Trello plan\u0131n\u0131 coding agent ile uygula" },
2578
+ { cmd: "jira-write-plan", desc: "Jira issue'dan geli\u015Ftirme plan\u0131 olu\u015Ftur" },
2579
+ { cmd: "jira-execute-plan", desc: "Jira plan\u0131n\u0131 coding agent ile uygula" },
2580
+ { cmd: "plan", desc: "Kay\u0131tl\u0131 bir plan\u0131 sohbet ba\u011Flam\u0131na y\xFCkle" },
2581
+ { cmd: "trello", desc: "Trello pano ve kart bilgisi getir" },
2582
+ { cmd: "trello-login", desc: "Trello API anahtar\u0131 kaydet" },
2583
+ { cmd: "jira", desc: "Jira issue getir" },
2584
+ { cmd: "jira-login", desc: "Jira kimlik bilgisi kaydet" },
2585
+ { cmd: "init", desc: "Proje ba\u011Flam\u0131 dosyas\u0131 olu\u015Ftur" },
2586
+ { cmd: "login", desc: "API anahtar\u0131 ve model se\xE7" },
2587
+ { cmd: "models", desc: "Aktif modeli de\u011Fi\u015Ftir" },
2588
+ { cmd: "config", desc: "Mevcut ayarlar\u0131 g\xF6ster" },
2589
+ { cmd: "resume", desc: "\xD6nceki oturumu devam ettir" },
2590
+ { cmd: "clear", desc: "Sohbet ge\xE7mi\u015Fini temizle" },
2591
+ { cmd: "reset", desc: "Oturumu s\u0131f\u0131rla" },
2592
+ { cmd: "export", desc: "Sohbeti dosyaya aktar" },
2593
+ { cmd: "prompt", desc: "Sistem prompt'unu g\xF6r\xFCnt\xFCle" },
2594
+ { cmd: "session-delete", desc: "Kay\u0131tl\u0131 oturumu sil" },
2595
+ { cmd: "logout", desc: "Oturumu kapat" },
2596
+ { cmd: "help", desc: "T\xFCm komutlar\u0131 listele" },
2597
+ { cmd: "exit", desc: "Uygulamadan \xE7\u0131k" }
2598
+ ];
2599
+ function colorizeInputPiece(piece, isFirstPiece, rawInput) {
2600
+ if (!isFirstPiece || !rawInput.startsWith("/")) return colors.white(piece);
2601
+ const cmdMatch = piece.match(/^(\/[a-z-]*)(.*)$/s);
2602
+ if (!cmdMatch) return colors.white(piece);
2603
+ const [, cmdPart, rest] = cmdMatch;
2604
+ const cmdName = cmdPart.slice(1);
2605
+ const isKnown = KNOWN_COMMANDS.has(cmdName) || cmdName === "";
2606
+ const cmdColor = isKnown ? chalk4.hex("#e5c07b").bold(cmdPart) : chalk4.hex("#e06c75").bold(cmdPart);
2607
+ return cmdColor + colors.white(rest);
2608
+ }
2609
+ var turquoise = chalk4.hex("#1577d4").bold;
2610
+ var turquoiseDim = chalk4.hex("#0a3d6e");
2611
+ function splitAnsiTokens(s) {
2612
+ const result = [];
2613
+ const re = /\x1B\[[0-9;]*[A-Za-z]/g;
2614
+ let last = 0;
2615
+ let m;
2616
+ while ((m = re.exec(s)) !== null) {
2617
+ for (const ch of s.slice(last, m.index)) result.push(ch);
2618
+ result.push(m[0]);
2619
+ last = m.index + m[0].length;
2620
+ }
2621
+ for (const ch of s.slice(last)) result.push(ch);
2622
+ return result;
2623
+ }
2624
+ function userBubble(text, maxCols) {
2625
+ const innerW = Math.max(10, maxCols - 6);
2626
+ const wrappedLines = [];
2627
+ for (const para of text.split("\n")) {
2628
+ if ([...para].every((c) => c === " ")) {
2629
+ wrappedLines.push("");
2630
+ continue;
2631
+ }
2632
+ const words = para.split(" ");
2633
+ let cur = "";
2634
+ let curLen = 0;
2635
+ for (const word of words) {
2636
+ const wordLen = [...word].length;
2637
+ if (curLen === 0) {
2638
+ cur = word;
2639
+ curLen = wordLen;
2640
+ } else if (curLen + 1 + wordLen <= innerW) {
2641
+ cur += " " + word;
2642
+ curLen += 1 + wordLen;
2643
+ } else {
2644
+ wrappedLines.push(cur);
2645
+ cur = word;
2646
+ curLen = wordLen;
2647
+ }
2648
+ while (curLen > innerW) {
2649
+ wrappedLines.push([...cur].slice(0, innerW).join(""));
2650
+ cur = [...cur].slice(innerW).join("");
2651
+ curLen = [...cur].length;
2652
+ }
2653
+ }
2654
+ if (curLen > 0) wrappedLines.push(cur);
2655
+ }
2656
+ if (wrappedLines.length === 0) wrappedLines.push("");
2657
+ const maxLineLen = wrappedLines.reduce((m, l) => Math.max(m, [...l].length), 0);
2658
+ const boxW = Math.min(maxCols - 4, maxLineLen + 2);
2659
+ const top = chalk4.white(" \u256D" + "\u2500".repeat(boxW) + "\u256E");
2660
+ const bot = chalk4.white(" \u2570" + "\u2500".repeat(boxW) + "\u256F");
2661
+ const mids = wrappedLines.map((line) => {
2662
+ const pad = Math.max(0, boxW - 1 - [...line].length);
2663
+ return chalk4.white(" \u2502 ") + chalk4.white.bold(line) + " ".repeat(pad) + chalk4.white("\u2502");
2664
+ });
2665
+ return [top, ...mids, bot].join("\n");
2666
+ }
2667
+ var CTRL_C = 3;
2668
+ var CTRL_O = 15;
2669
+ var BACKSPACE = 8;
2670
+ var DEL = 127;
2671
+ var MAX_HISTORY_TURNS = 20;
2672
+ var lastgenProjectContext = null;
2673
+ readLastgenContext(process.cwd()).then((ctx) => {
2674
+ lastgenProjectContext = ctx;
2675
+ }).catch(() => {
2676
+ });
2677
+ function buildMessages(session, onTrimmed) {
2678
+ const messages = [];
2679
+ const systemParts = [];
2680
+ if (session.config?.systemPrompt) systemParts.push(session.config.systemPrompt);
2681
+ if (lastgenProjectContext) {
2682
+ systemParts.push(
2683
+ `## Proje Ba\u011Flam\u0131 (${LASTGEN_CONTEXT_FILE})
2684
+ \xC7al\u0131\u015Fma dizini: ${process.cwd()}
2685
+
2686
+ ` + lastgenProjectContext
2687
+ );
2688
+ }
2689
+ if (systemParts.length > 0) {
2690
+ messages.push({ role: "system", content: systemParts.join("\n\n---\n\n") });
2691
+ }
2692
+ const fullHistory = session.history;
2693
+ const maxMessages = MAX_HISTORY_TURNS * 2;
2694
+ const history = fullHistory.length > maxMessages ? (onTrimmed?.(), fullHistory.slice(-maxMessages)) : fullHistory;
2695
+ for (const m of history) messages.push({ role: m.role, content: m.content });
2696
+ return messages;
2697
+ }
2698
+ var approxTokens = (s) => Math.max(1, Math.ceil([...s].length / 4));
2699
+ async function runChatTUI(session) {
2700
+ if (!stdin2.isTTY || !stdout2.isTTY || !stdout2.rows) return false;
2701
+ return new Promise((resolve) => {
2702
+ let lines = [""];
2703
+ let status = null;
2704
+ let contextWarning = null;
2705
+ let planToolCalls = [];
2706
+ let planStackLineIdx = -1;
2707
+ let planStackLineCount = 0;
2708
+ let planStackExpanded = false;
2709
+ let input = "";
2710
+ let pasteCount = 0;
2711
+ let inputIsPaste = false;
2712
+ let pasteExtra = "";
2713
+ let inBracketedPaste = false;
2714
+ const inputHistory = [];
2715
+ let historyIdx = -1;
2716
+ let historySaved = "";
2717
+ let acIdx = 0;
2718
+ let acClosed = false;
2719
+ let pasteBuffer = "";
2720
+ let busy = false;
2721
+ let currentAbortController = null;
2722
+ let picker = null;
2723
+ let pickerResolve = null;
2724
+ let pickerActive = false;
2725
+ let pickerCooldown = false;
2726
+ const EXEC_MODES = [
2727
+ { mode: "auto", label: "Full Auto", desc: "T\xFCm ad\u0131mlar\u0131 onay beklemeden s\u0131rayla uygula \u2014 \xF6nerilen mod" },
2728
+ { mode: "step", label: "Step-by-Step", desc: "Her plan ad\u0131m\u0131 \xF6ncesi onay iste; reddetmek o ad\u0131m\u0131 atlar" },
2729
+ { mode: "file", label: "Review & Apply", desc: "Dosya yazma i\u015Flemi (patch / create) \xF6ncesi diff'i g\xF6r, onayla" },
2730
+ { mode: "dry", label: "Dry Run", desc: "Hi\xE7bir dosyaya dokunma; model yan\u0131tlar\u0131n\u0131 ve ara\xE7 \xE7a\u011Fr\u0131lar\u0131n\u0131 g\xF6zlemle" }
2731
+ ];
2732
+ let modePicker = null;
2733
+ let modePickerResolve = null;
2734
+ let approvalPending = null;
2735
+ const queue = [];
2736
+ let queueEditIdx = -1;
2737
+ let finished = false;
2738
+ let scrollOffset = 0;
2739
+ const wasRaw = Boolean(stdin2.isRaw);
2740
+ function inputDisplay() {
2741
+ if (!inputIsPaste) return input;
2742
+ const lineCount = input.split("\n").length;
2743
+ const charCount = [...input].length;
2744
+ const base = `\u2317${pasteCount} ${lineCount > 1 ? `${lineCount} sat\u0131r` : `${charCount} karakter`}`;
2745
+ return pasteExtra.length > 0 ? `${base} ${pasteExtra}` : `${base} (Enter \u2192 g\xF6nder, Backspace \u2192 sil)`;
2746
+ }
2747
+ const cols = () => stdout2.columns || 80;
2748
+ const rows = () => stdout2.rows || 24;
2749
+ function richToolLine(t) {
2750
+ const m = t.match(/^(\w+)\((\{.*\})\)$/s);
2751
+ if (m) {
2752
+ try {
2753
+ const args = JSON.parse(m[2]);
2754
+ return " " + formatToolCall(m[1], args);
2755
+ } catch {
2756
+ }
2757
+ }
2758
+ return chalk4.hex("#c678dd").bold(" \u2699 ") + colors.dim(t);
2759
+ }
2760
+ function refreshToolStack() {
2761
+ const newStackLines = planStackExpanded ? [
2762
+ ...planToolCalls.map(richToolLine),
2763
+ colors.dim(" \u2504 Ctrl+O daralt")
2764
+ ] : [
2765
+ chalk4.hex("#c678dd").bold(" \u2699 ") + chalk4.greenBright.bold(String(planToolCalls.length)) + colors.dim(" ara\xE7 \xE7a\u011Fr\u0131s\u0131 Ctrl+O geni\u015Flet")
2766
+ ];
2767
+ if (planStackLineIdx === -1) {
2768
+ planStackLineIdx = lines.length;
2769
+ planStackLineCount = newStackLines.length;
2770
+ lines.push(...newStackLines);
2771
+ } else {
2772
+ lines.splice(planStackLineIdx, planStackLineCount, ...newStackLines);
2773
+ planStackLineCount = newStackLines.length;
2774
+ }
2775
+ scheduleRender();
2776
+ }
2777
+ function appendLines(text) {
2778
+ if (scrollOffset > 0) {
2779
+ const C = cols();
2780
+ const countPhys = () => lines.reduce((s, ln) => s + wrapAnsi(ln, C).length, 0);
2781
+ const before = countPhys();
2782
+ const parts = text.split("\n");
2783
+ lines[lines.length - 1] += parts[0];
2784
+ for (let i = 1; i < parts.length; i++) lines.push(parts[i]);
2785
+ scrollOffset += Math.max(0, countPhys() - before);
2786
+ } else {
2787
+ const parts = text.split("\n");
2788
+ lines[lines.length - 1] += parts[0];
2789
+ for (let i = 1; i < parts.length; i++) lines.push(parts[i]);
2790
+ }
2791
+ }
2792
+ function append(text) {
2793
+ appendLines(text);
2794
+ if (scrollOffset === 0) scheduleRender();
2795
+ }
2796
+ function wrapInput(text, c) {
2797
+ const inner = Math.max(1, c - 4);
2798
+ const firstCap = Math.max(1, inner - 2);
2799
+ const chars = [...text];
2800
+ if (chars.length === 0) return [""];
2801
+ const pieces = [];
2802
+ let i = 0;
2803
+ let cap = firstCap;
2804
+ while (i < chars.length) {
2805
+ pieces.push(chars.slice(i, i + cap).join(""));
2806
+ i += cap;
2807
+ cap = inner;
2808
+ }
2809
+ return pieces;
2810
+ }
2811
+ function renderStatusOnly() {
2812
+ if (suspended || finished) return;
2813
+ const C = cols(), R = rows();
2814
+ const queuePanelH = queue.length > 0 ? queue.length + 2 : 0;
2815
+ const pieces = wrapInput(inputDisplay(), C);
2816
+ const H = pieces.length + 2 + queuePanelH;
2817
+ const outRows = Math.max(1, R - H);
2818
+ const statusLine = status !== null ? wrapAnsi(status, C)[0] ?? "" : "";
2819
+ stdout2.write(`\x1B[${outRows};1H\x1B[2K${statusLine}`);
2820
+ }
2821
+ function renderPicker(p) {
2822
+ const R = rows();
2823
+ const C = cols();
2824
+ const brand2 = chalk4.hex("#1577d4").bold;
2825
+ const dim2 = chalk4.dim;
2826
+ const hi = chalk4.white.bold;
2827
+ const hiNum2 = chalk4.greenBright.bold;
2828
+ const hiSub = chalk4.hex("#1577d4");
2829
+ const ok2 = chalk4.greenBright.bold;
2830
+ const fmt2 = (iso) => {
2831
+ try {
2832
+ return new Date(iso).toLocaleString("tr-TR");
2833
+ } catch {
2834
+ return iso.slice(0, 16);
2835
+ }
2836
+ };
2837
+ const lines2 = [];
2838
+ if (p.phase === "list") {
2839
+ const cardW = Math.min(72, Math.max(48, Math.floor(C * 0.7)));
2840
+ const indent = Math.max(2, Math.floor((C - cardW) / 2));
2841
+ const pad = " ".repeat(indent);
2842
+ const innerW = cardW - 4;
2843
+ const accent = chalk4.hex("#1577d4");
2844
+ const makeTop = (label, isActive) => {
2845
+ const N = [...label].length;
2846
+ const F = Math.max(0, cardW - 6 - N);
2847
+ return isActive ? accent("\u256D\u2500 ") + chalk4.white.bold(label) + accent("\u2500".repeat(F) + " \u2500\u256E") : dim2("\u256D\u2500 " + label + "\u2500".repeat(F) + " \u2500\u256E");
2848
+ };
2849
+ const makeLine = (text, isActive) => {
2850
+ const content = [...text].length > innerW ? text.slice(0, innerW - 1) + "\u2026" : text.padEnd(innerW);
2851
+ return isActive ? accent("\u2502 ") + chalk4.white(content) + accent(" \u2502") : dim2("\u2502 " + content + " \u2502");
2852
+ };
2853
+ const makeBottom = (isActive) => isActive ? accent("\u2570" + "\u2500".repeat(cardW - 2) + "\u256F") : dim2("\u2570" + "\u2500".repeat(cardW - 2) + "\u256F");
2854
+ lines2.push("", pad + brand2("LASTGEN") + dim2(" \u2014 plan se\xE7"), "");
2855
+ p.plans.forEach((pl, i) => {
2856
+ const s = i === p.sel;
2857
+ const label = `${i + 1}. ${fmt2(pl.createdAt)}`;
2858
+ const taskLine = pl.trelloCardName.slice(0, innerW);
2859
+ const metaLine = (pl.boardName + " \xB7 " + pl.testCommand).slice(0, innerW);
2860
+ lines2.push(pad + makeTop(label, s));
2861
+ lines2.push(pad + makeLine(s ? taskLine : taskLine, s));
2862
+ lines2.push(pad + (s ? accent("\u2502 ") + dim2(metaLine.padEnd(innerW)) + accent(" \u2502") : dim2("\u2502 " + metaLine.padEnd(innerW) + " \u2502")));
2863
+ lines2.push(pad + makeBottom(s));
2864
+ lines2.push("");
2865
+ });
2866
+ lines2.push(
2867
+ pad + dim2("\u2191 \u2193 gezin ") + chalk4.bgGreen.white.bold(" \u21B5 \u0130\xC7ER\u0130\u011E\u0130 G\xD6R ") + dim2(" Ctrl+C \xE7\u0131k\u0131\u015F")
2868
+ );
2869
+ } else if (p.phase === "preview") {
2870
+ const pl = p.plans[p.sel];
2871
+ const textW = Math.max(20, C - 4);
2872
+ const wrapText = (text, width, indent = "") => {
2873
+ const words = text.split(" ");
2874
+ const out = [];
2875
+ let cur = indent;
2876
+ for (const w of words) {
2877
+ if (cur.length + w.length + (cur === indent ? 0 : 1) > width) {
2878
+ if (cur !== indent) {
2879
+ out.push(cur);
2880
+ cur = indent + w;
2881
+ } else {
2882
+ out.push(cur + w);
2883
+ cur = indent;
2884
+ }
2885
+ } else {
2886
+ cur += (cur === indent ? "" : " ") + w;
2887
+ }
2888
+ }
2889
+ if (cur !== indent) out.push(cur);
2890
+ return out.length ? out : [indent];
2891
+ };
2892
+ const shortPath = (fp) => {
2893
+ const parts = fp.replace(/\\/g, "/").split("/").filter(Boolean);
2894
+ return parts.length > 3 ? "\u2026/" + parts.slice(-3).join("/") : fp;
2895
+ };
2896
+ let stepCount = 0;
2897
+ try {
2898
+ const tmp = JSON.parse(pl.planMarkdown.replace(/^```json\s*/i, "").replace(/^```\s*/i, "").replace(/```\s*$/, "").trim());
2899
+ stepCount = tmp.steps?.length ?? 0;
2900
+ } catch {
2901
+ }
2902
+ const stepLabel = stepCount > 0 ? dim2(` (${stepCount} ad\u0131m)`) : "";
2903
+ lines2.push("", " " + brand2("LASTGEN") + dim2(" \u2014 plan i\xE7eri\u011Fi") + stepLabel, "");
2904
+ lines2.push(" " + chalk4.yellow.bold(pl.trelloCardName.slice(0, C - 4)));
2905
+ lines2.push(" " + dim2(`${pl.boardName} \xB7 Test: ${pl.testCommand} \xB7 ${fmt2(pl.createdAt)}`));
2906
+ lines2.push(" " + dim2("\u2500".repeat(Math.max(0, C - 4))));
2907
+ const contentLines = [];
2908
+ try {
2909
+ const cleaned = pl.planMarkdown.replace(/^```json\s*/i, "").replace(/^```\s*/i, "").replace(/```\s*$/, "").trim();
2910
+ const parsed = JSON.parse(cleaned);
2911
+ if (parsed.summary) {
2912
+ contentLines.push(" " + dim2("\xD6zet"));
2913
+ for (const l of wrapText(parsed.summary, textW, " ")) contentLines.push(chalk4.white(l));
2914
+ contentLines.push("");
2915
+ }
2916
+ if (parsed.steps) {
2917
+ parsed.steps.forEach((s) => {
2918
+ const numStr = hiNum2(` ${s.step_number}.`);
2919
+ const descLines = wrapText(s.description, textW - 5, " ");
2920
+ contentLines.push(`${numStr} ${chalk4.white(s.description.length <= textW - 5 ? s.description : s.description.slice(0, textW - 5))}`);
2921
+ if (descLines.length > 1) descLines.slice(1).forEach((l) => contentLines.push(chalk4.white(l)));
2922
+ if (s.target_file) {
2923
+ contentLines.push(" " + dim2("\u2514\u2500 ") + chalk4.hex("#61afef")(shortPath(s.target_file)));
2924
+ }
2925
+ contentLines.push("");
2926
+ });
2927
+ }
2928
+ if (parsed.verification_command) {
2929
+ contentLines.push(" " + chalk4.yellow("\u2726 ") + dim2("Do\u011Frulama: ") + chalk4.hex("#98c379")(parsed.verification_command));
2930
+ }
2931
+ } catch {
2932
+ pl.planMarkdown.split("\n").forEach((l) => contentLines.push(" " + l));
2933
+ }
2934
+ const FOOTER_ROWS = 2;
2935
+ const visibleRows = Math.max(1, R - lines2.length - FOOTER_ROWS);
2936
+ const maxScroll = Math.max(0, contentLines.length - visibleRows);
2937
+ const scroll = Math.min(p.previewScroll, maxScroll);
2938
+ if (scroll !== p.previewScroll) p.previewScroll = scroll;
2939
+ contentLines.slice(scroll, scroll + visibleRows).forEach((l) => lines2.push(l));
2940
+ const scrollHint = maxScroll > 0 ? dim2(` \u2191\u2193 ${scroll + 1}\u2013${Math.min(scroll + visibleRows, contentLines.length)}/${contentLines.length} `) : "";
2941
+ const runBtn = chalk4.bgGreen.white.bold(" \u21B5 \xC7ALI\u015ETIR ");
2942
+ const backHint = dim2(" ESC / Ctrl+C geri");
2943
+ lines2.push(" " + dim2("\u2500".repeat(Math.max(0, C - 4))));
2944
+ lines2.push(" " + scrollHint + runBtn + backHint);
2945
+ lines2.push("");
2946
+ } else {
2947
+ const pl = p.plans[p.sel];
2948
+ lines2.push("", " " + brand2("LASTGEN") + dim2(" \u2014 onayla"), "");
2949
+ lines2.push(" " + chalk4.yellow.bold(`G\xF6rev: ${pl.trelloCardName.slice(0, C - 12)}`));
2950
+ lines2.push(" " + dim2(`Pano: ${pl.boardName} \xB7 Test: ${pl.testCommand}`));
2951
+ lines2.push("");
2952
+ [{ l: "Evet, ba\u015Flat", d: "Agent plan\u0131 uygulamaya ba\u015Flar" }, { l: "\u2190 Geri", d: "Plan listesine d\xF6n" }].forEach((o, i) => {
2953
+ const a = i === p.confirmSel;
2954
+ lines2.push((a ? ok2(" \u276F ") : " ") + (a ? chalk4.whiteBright.bold(o.l) : dim2(o.l)));
2955
+ lines2.push(" " + dim2(o.d));
2956
+ });
2957
+ lines2.push("", dim2(" \u2191 \u2193 gezin Enter se\xE7 Ctrl+C \xE7\u0131k\u0131\u015F"));
2958
+ }
2959
+ let buf = "\x1B[?2026h\x1B[?25l\x1B[H";
2960
+ for (let r = 0; r < R; r++) buf += "\x1B[2K" + (lines2[r] ?? "") + (r < R - 1 ? "\r\n" : "");
2961
+ buf += "\x1B[?2026l";
2962
+ stdout2.write(buf);
2963
+ }
2964
+ function renderModePicker() {
2965
+ const R = rows();
2966
+ const C = cols();
2967
+ const brand2 = chalk4.hex("#1577d4").bold;
2968
+ const dim2 = chalk4.dim;
2969
+ const accent = chalk4.hex("#1577d4");
2970
+ const sel = modePicker?.sel ?? 0;
2971
+ const cardW = Math.min(66, Math.max(46, Math.floor(C * 0.6)));
2972
+ const indent = Math.max(2, Math.floor((C - cardW) / 2));
2973
+ const pad = " ".repeat(indent);
2974
+ const innerW = cardW - 4;
2975
+ const makeTop = (label, isActive) => {
2976
+ const N = [...label].length;
2977
+ const F = Math.max(0, cardW - 6 - N);
2978
+ const dashes = "\u2500".repeat(F);
2979
+ if (isActive) {
2980
+ return accent("\u256D\u2500 ") + chalk4.white.bold(label) + accent(dashes + " \u2500\u256E");
2981
+ }
2982
+ return dim2("\u256D\u2500 " + label + dashes + " \u2500\u256E");
2983
+ };
2984
+ const makeMiddle = (text, isActive) => {
2985
+ const content = [...text].length > innerW ? text.slice(0, innerW - 1) + "\u2026" : text.padEnd(innerW);
2986
+ if (isActive) return accent("\u2502 ") + dim2(content) + accent(" \u2502");
2987
+ return dim2("\u2502 " + content + " \u2502");
2988
+ };
2989
+ const makeBottom = (isActive) => {
2990
+ const line = "\u2570" + "\u2500".repeat(cardW - 2) + "\u256F";
2991
+ return isActive ? accent(line) : dim2(line);
2992
+ };
2993
+ const lines2 = [];
2994
+ lines2.push("");
2995
+ lines2.push(pad + brand2("LASTGEN") + dim2(" \u2014 \xE7al\u0131\u015Ft\u0131rma modu se\xE7"));
2996
+ lines2.push("");
2997
+ EXEC_MODES.forEach((m, i) => {
2998
+ const isActive = i === sel;
2999
+ const label = `${i + 1}. ${m.label}`;
3000
+ lines2.push(pad + makeTop(label, isActive));
3001
+ lines2.push(pad + makeMiddle(m.desc, isActive));
3002
+ lines2.push(pad + makeBottom(isActive));
3003
+ lines2.push("");
3004
+ });
3005
+ lines2.push(
3006
+ pad + dim2("\u2191 \u2193 gezin 1\u20134 h\u0131zl\u0131 se\xE7 ") + chalk4.bgGreen.white.bold(" \u21B5 SE\xC7 ") + dim2(" Ctrl+C \xE7\u0131k\u0131\u015F")
3007
+ );
3008
+ let buf = "\x1B[?2026h\x1B[?25l\x1B[H";
3009
+ for (let r = 0; r < R; r++) buf += "\x1B[2K" + (lines2[r] ?? "") + (r < R - 1 ? "\r\n" : "");
3010
+ buf += "\x1B[?2026l";
3011
+ stdout2.write(buf);
3012
+ }
3013
+ function render2() {
3014
+ if (picker) {
3015
+ renderPicker(picker);
3016
+ return;
3017
+ }
3018
+ if (modePicker) {
3019
+ renderModePicker();
3020
+ return;
3021
+ }
3022
+ const C = cols();
3023
+ const R = rows();
3024
+ const inner = Math.max(1, C - 4);
3025
+ const queuePanel = [];
3026
+ if (queue.length > 0) {
3027
+ queuePanel.push(colors.dim(" \u2504".padEnd(C - 1)));
3028
+ queue.forEach((item, i) => {
3029
+ const selected = i === queueEditIdx;
3030
+ const numLabel = selected ? chalk4.greenBright.bold(` [${i + 1}] `) : colors.dim(` [${i + 1}] `);
3031
+ const maxText = Math.max(1, C - 8);
3032
+ const preview = [...item.replace(/\n/g, " ")].slice(0, maxText).join("") + ([...item].length > maxText ? "\u2026" : "");
3033
+ const text = selected ? chalk4.white.bold(preview) : colors.dim(preview);
3034
+ queuePanel.push(numLabel + text);
3035
+ });
3036
+ queuePanel.push(colors.dim(" \u2504".padEnd(C - 1)));
3037
+ }
3038
+ const contextWarningLines = [];
3039
+ if (contextWarning !== null) {
3040
+ for (const p of wrapAnsi(contextWarning, C)) contextWarningLines.push(p);
3041
+ }
3042
+ const acLines = [];
3043
+ if (!inputIsPaste && !acClosed && !busy && input.startsWith("/")) {
3044
+ const query = input.slice(1).toLowerCase();
3045
+ const matches = COMMAND_LIST.filter(({ cmd }) => cmd.startsWith(query));
3046
+ if (matches.length > 0) {
3047
+ const WIN = 8;
3048
+ const selIdx = Math.max(0, Math.min(acIdx, matches.length - 1));
3049
+ const winStart = Math.min(
3050
+ Math.max(0, selIdx - WIN + 1),
3051
+ Math.max(0, matches.length - WIN)
3052
+ );
3053
+ const winEnd = Math.min(winStart + WIN, matches.length);
3054
+ const cmdW = Math.max(...matches.map((m) => m.cmd.length));
3055
+ const above = winStart > 0 ? winStart : 0;
3056
+ const below = matches.length - winEnd;
3057
+ acLines.push(chalk4.dim(" \u2504".padEnd(C - 1)));
3058
+ if (above > 0) acLines.push(" " + chalk4.dim(` \u2191 ${above} daha`));
3059
+ for (let i = winStart; i < winEnd; i++) {
3060
+ const { cmd, desc } = matches[i];
3061
+ const isSel = i === selIdx;
3062
+ const cmdStr = ("/" + cmd).padEnd(cmdW + 2);
3063
+ const descStr = desc.slice(0, Math.max(0, C - cmdW - 8));
3064
+ if (isSel) {
3065
+ acLines.push(
3066
+ " " + chalk4.bgHex("#1577d4").white.bold(cmdStr) + " " + chalk4.dim(descStr)
3067
+ );
3068
+ } else {
3069
+ acLines.push(
3070
+ " " + chalk4.hex("#e5c07b")(cmdStr) + " " + chalk4.dim(descStr)
3071
+ );
3072
+ }
3073
+ }
3074
+ if (below > 0) acLines.push(" " + chalk4.dim(` \u2193 ${below} daha`));
3075
+ acLines.push(chalk4.dim(" \u2504".padEnd(C - 1)));
3076
+ }
3077
+ }
3078
+ const topDashes = Math.max(0, C - 2);
3079
+ let H;
3080
+ let inputBoxLines;
3081
+ let inputBoxBottom;
3082
+ if (approvalPending) {
3083
+ H = 4 + queuePanel.length + contextWarningLines.length + acLines.length;
3084
+ const msgText = chalk4.yellow("\u23F8 ") + chalk4.white.bold(approvalPending.msg.slice(0, inner - 2));
3085
+ const hintText = chalk4.greenBright.bold("\u21B5 Devam") + chalk4.dim(" N / ESC Atla Ctrl+C Durdur");
3086
+ const msgPad = Math.max(0, inner - visibleLength(msgText));
3087
+ const hintPad = Math.max(0, inner - visibleLength(hintText));
3088
+ inputBoxLines = [
3089
+ colors.border("\u2502 ") + msgText + " ".repeat(msgPad) + colors.border(" \u2502"),
3090
+ colors.border("\u2502 ") + hintText + " ".repeat(hintPad) + colors.border(" \u2502")
3091
+ ];
3092
+ inputBoxBottom = colors.border("\u2570" + "\u2500".repeat(topDashes) + "\u256F");
3093
+ } else {
3094
+ const pieces = wrapInput(inputDisplay(), C);
3095
+ H = pieces.length + 2 + queuePanel.length + contextWarningLines.length + acLines.length;
3096
+ inputBoxLines = pieces.map((piece, idx) => {
3097
+ const isLast = idx === pieces.length - 1;
3098
+ const prefix = idx === 0 ? queueEditIdx >= 0 ? chalk4.greenBright.bold(`[${queueEditIdx + 1}] `) : colors.user("\u203A ") : "";
3099
+ const prefixLen = idx === 0 ? queueEditIdx >= 0 ? String(queueEditIdx + 1).length + 3 : 2 : 0;
3100
+ const caret = isLast ? chalk4.inverse(" ") : "";
3101
+ const vis = prefixLen + [...piece].length + (isLast ? 1 : 0);
3102
+ const pad = Math.max(0, inner - vis);
3103
+ return colors.border("\u2502 ") + prefix + colorizeInputPiece(piece, idx === 0, inputDisplay()) + caret + " ".repeat(pad) + colors.border(" \u2502");
3104
+ });
3105
+ const TOKEN_SLOT = 21;
3106
+ const tokenValue = session.totalTokens > 0 ? formatTokens(session.totalTokens) : "---";
3107
+ const coloredLabel = chalk4.dim(" total token: ") + chalk4.greenBright.bold(tokenValue) + chalk4.dim(" ");
3108
+ const visLen = visibleLength(coloredLabel);
3109
+ const padded = visLen < TOKEN_SLOT ? " ".repeat(TOKEN_SLOT - visLen) + coloredLabel : coloredLabel;
3110
+ const botDashes = Math.max(0, C - 2 - TOKEN_SLOT);
3111
+ inputBoxBottom = colors.border("\u2570" + "\u2500".repeat(botDashes)) + padded + colors.border("\u256F");
3112
+ }
3113
+ const top = colors.border("\u256D" + "\u2500".repeat(topDashes) + "\u256E");
3114
+ const outRows = Math.max(1, R - H);
3115
+ const phys = [];
3116
+ for (const ln of lines) for (const p of wrapAnsi(ln, C)) phys.push(p);
3117
+ if (status !== null) for (const p of wrapAnsi(status, C)) phys.push(p);
3118
+ const maxScroll = Math.max(0, phys.length - outRows);
3119
+ const effScroll = Math.min(scrollOffset, maxScroll);
3120
+ if (effScroll !== scrollOffset) scrollOffset = effScroll;
3121
+ const endIdx = phys.length - effScroll;
3122
+ const startIdx = Math.max(0, endIdx - outRows);
3123
+ const visible = phys.slice(startIdx, endIdx);
3124
+ const scrollIndicator = effScroll > 0 ? colors.dim(` \u2191 ${effScroll} sat\u0131r `) : "";
3125
+ const frame = [];
3126
+ for (let i = 0; i < outRows - visible.length; i++) frame.push("");
3127
+ frame.push(...visible);
3128
+ if (scrollIndicator && frame.length > 0) {
3129
+ const indLen = visibleLength(scrollIndicator);
3130
+ const firstLen = visibleLength(frame[0] ?? "");
3131
+ const pad = Math.max(0, C - firstLen - indLen);
3132
+ frame[0] = (frame[0] ?? "") + " ".repeat(pad) + scrollIndicator;
3133
+ }
3134
+ frame.push(...queuePanel, ...acLines, ...contextWarningLines, top, ...inputBoxLines, inputBoxBottom);
3135
+ let buf = "\x1B[?2026h\x1B[?25l\x1B[H";
3136
+ for (let r = 0; r < R; r++) {
3137
+ buf += "\x1B[2K" + (frame[r] ?? "") + (r < R - 1 ? "\r\n" : "");
3138
+ }
3139
+ buf += "\x1B[?2026l";
3140
+ stdout2.write(buf);
3141
+ }
3142
+ let renderScheduled = false;
3143
+ let suspended = false;
3144
+ function scheduleRender() {
3145
+ if (renderScheduled) return;
3146
+ renderScheduled = true;
3147
+ setTimeout(() => {
3148
+ renderScheduled = false;
3149
+ if (!finished && !suspended) render2();
3150
+ }, 16);
3151
+ }
3152
+ function startThinking2(elapsed) {
3153
+ let phrase = pickThinkingPhrase();
3154
+ let settle = buildSettle(phrase.length);
3155
+ let frame = 0;
3156
+ let holding = 0;
3157
+ let flashMsg = null;
3158
+ let flashTimer = null;
3159
+ const draw = () => {
3160
+ if (flashMsg) {
3161
+ status = `${chalk4.yellow("\u2022 ")}${chalk4.yellow(flashMsg)} ${turquoiseDim(elapsed())}`;
3162
+ renderStatusOnly();
3163
+ return;
3164
+ }
3165
+ if (holding > 0) {
3166
+ status = `${turquoise("\u2022 " + phrase)} ${turquoiseDim(elapsed())}`;
3167
+ holding--;
3168
+ } else if (frame <= 12) {
3169
+ let o = "";
3170
+ for (let i = 0; i < phrase.length; i++) {
3171
+ const ch = phrase[i];
3172
+ if (ch === " ") o += " ";
3173
+ else if (frame >= settle[i]) o += turquoise(ch);
3174
+ else o += turquoiseDim(randomGlitchChar());
3175
+ }
3176
+ status = `${turquoise("\u2022 ")}${o} ${turquoiseDim(elapsed())}`;
3177
+ frame++;
3178
+ } else {
3179
+ holding = 22;
3180
+ let next = pickThinkingPhrase();
3181
+ while (next === phrase) next = pickThinkingPhrase();
3182
+ phrase = next;
3183
+ settle = buildSettle(phrase.length);
3184
+ frame = 0;
3185
+ }
3186
+ renderStatusOnly();
3187
+ };
3188
+ draw();
3189
+ const timer = setInterval(draw, 60);
3190
+ timer.unref?.();
3191
+ return {
3192
+ stop: () => {
3193
+ if (timer) {
3194
+ clearInterval(timer);
3195
+ }
3196
+ if (flashTimer) clearTimeout(flashTimer);
3197
+ status = null;
3198
+ scheduleRender();
3199
+ },
3200
+ flash: (msg) => {
3201
+ flashMsg = msg;
3202
+ if (flashTimer) clearTimeout(flashTimer);
3203
+ flashTimer = setTimeout(() => {
3204
+ flashMsg = null;
3205
+ }, 3500);
3206
+ draw();
3207
+ }
3208
+ };
3209
+ }
3210
+ function buildSettle(n) {
3211
+ return Array.from({ length: n }, () => Math.floor(Math.random() * 12));
3212
+ }
3213
+ function cancelPicker() {
3214
+ pickerActive = false;
3215
+ pickerCooldown = true;
3216
+ setTimeout(() => {
3217
+ pickerCooldown = false;
3218
+ }, 500);
3219
+ if (pickerResolve) {
3220
+ const r = pickerResolve;
3221
+ picker = null;
3222
+ pickerResolve = null;
3223
+ render2();
3224
+ r(null);
3225
+ } else {
3226
+ render2();
3227
+ }
3228
+ }
3229
+ const onSigint = () => {
3230
+ if (finished) return;
3231
+ if (currentAbortController) {
3232
+ currentAbortController.abort();
3233
+ } else if (approvalPending) {
3234
+ const r = approvalPending.resolve;
3235
+ approvalPending = null;
3236
+ scheduleRender();
3237
+ r(false);
3238
+ } else if (modePicker) {
3239
+ modePicker = null;
3240
+ const res = modePickerResolve;
3241
+ modePickerResolve = null;
3242
+ pickerActive = false;
3243
+ pickerCooldown = true;
3244
+ setTimeout(() => {
3245
+ pickerCooldown = false;
3246
+ }, 500);
3247
+ render2();
3248
+ res?.(null);
3249
+ } else if (pickerActive || pickerCooldown) {
3250
+ if (pickerActive) cancelPicker();
3251
+ } else {
3252
+ finish();
3253
+ }
3254
+ };
3255
+ function enterScreen() {
3256
+ process.on("SIGINT", onSigint);
3257
+ stdout2.write("\x1B[?1049h\x1B[2J\x1B[?25l\x1B[?2004h");
3258
+ stdin2.setRawMode(true);
3259
+ stdin2.resume();
3260
+ stdin2.setEncoding("utf8");
3261
+ stdin2.on("data", onData);
3262
+ stdout2.on("resize", scheduleRender);
3263
+ setOutputSink(append);
3264
+ }
3265
+ function leaveScreen() {
3266
+ process.removeListener("SIGINT", onSigint);
3267
+ stdin2.removeListener("data", onData);
3268
+ stdout2.removeListener("resize", scheduleRender);
3269
+ setOutputSink(null);
3270
+ stdin2.setRawMode(wasRaw);
3271
+ stdout2.write("\x1B[?2004l\x1B[?25h\x1B[?1049l");
3272
+ }
3273
+ function finish() {
3274
+ if (finished) return;
3275
+ finished = true;
3276
+ saveSessionSync(session.sessionId, session.history);
3277
+ playFarewell().then(() => {
3278
+ leaveScreen();
3279
+ resolve(true);
3280
+ });
3281
+ }
3282
+ async function playFarewell() {
3283
+ input = "";
3284
+ inputIsPaste = false;
3285
+ pasteExtra = "";
3286
+ status = null;
3287
+ const farewell = FAREWELLS[Math.floor(Math.random() * FAREWELLS.length)];
3288
+ const chars = [...farewell];
3289
+ const settleAt = chars.map(() => Math.floor(Math.random() * 10));
3290
+ await new Promise((res) => {
3291
+ let frame = 0;
3292
+ const tick = () => {
3293
+ if (frame <= 14) {
3294
+ let o = "";
3295
+ for (let i = 0; i < chars.length; i++) {
3296
+ const ch = chars[i];
3297
+ if (ch === " ") {
3298
+ o += " ";
3299
+ continue;
3300
+ }
3301
+ o += frame >= settleAt[i] ? turquoise(ch) : turquoiseDim(randomGlitchChar());
3302
+ }
3303
+ status = turquoise("\u25E2 LASTGEN ") + o;
3304
+ frame++;
3305
+ render2();
3306
+ setTimeout(tick, 55);
3307
+ } else {
3308
+ status = turquoise("\u25E2 LASTGEN ") + turquoise(farewell);
3309
+ render2();
3310
+ setTimeout(() => {
3311
+ status = null;
3312
+ appendLines("\n" + turquoise("\u25E2 LASTGEN ") + turquoise(farewell) + "\n");
3313
+ res();
3314
+ }, 1800);
3315
+ }
3316
+ };
3317
+ tick();
3318
+ });
3319
+ }
3320
+ async function suspendAndRun(fn) {
3321
+ suspended = true;
3322
+ stdin2.removeListener("data", onData);
3323
+ setOutputSink(null);
3324
+ stdin2.setRawMode(wasRaw);
3325
+ stdout2.write("\x1B[?2004l\x1B[?25h\x1B[?1049l");
3326
+ try {
3327
+ await fn();
3328
+ } finally {
3329
+ suspended = false;
3330
+ stdout2.write("\x1B[?1049h\x1B[2J\x1B[?25l\x1B[?2004h");
3331
+ stdin2.setRawMode(true);
3332
+ stdin2.resume();
3333
+ setOutputSink(append);
3334
+ stdin2.on("data", onData);
3335
+ render2();
3336
+ }
3337
+ }
3338
+ async function streamAnswer(text) {
3339
+ if (!session.config) {
3340
+ append("\n" + colors.error("\u2717 \xD6nce /login ile giri\u015F yap\u0131n.") + "\n");
3341
+ return;
3342
+ }
3343
+ if (!session.config.model) {
3344
+ append("\n" + colors.error("\u2717 Aktif model yok. /models ile se\xE7in.") + "\n");
3345
+ return;
3346
+ }
3347
+ scrollOffset = 0;
3348
+ session.history.push({ role: "user", content: text });
3349
+ append(
3350
+ "\n" + turquoise("\u25E2 LASTGEN") + colors.dim(" \xB7 " + (session.config.model ?? "")) + "\n"
3351
+ );
3352
+ let answer = "";
3353
+ const start = Date.now();
3354
+ let liveTokens = 0;
3355
+ let turnTokens = 0;
3356
+ if (session.totalTokens > 8e3) {
3357
+ contextWarning = chalk4.red("\u26A0 Ba\u011Flam kritik seviyede \u2014 yeni mesajlar kaybolabilir");
3358
+ } else if (session.totalTokens > 6e3) {
3359
+ contextWarning = chalk4.yellow("\u26A0 Ba\u011Flam %80 dolu \u2014 /reset ile temizleyebilirsiniz");
3360
+ } else {
3361
+ contextWarning = null;
3362
+ }
3363
+ let trimmed = false;
3364
+ const msgs = buildMessages(session, () => {
3365
+ trimmed = true;
3366
+ });
3367
+ if (trimmed) {
3368
+ contextWarning = chalk4.yellow("\u2139 Uzun ge\xE7mi\u015F \u2014 son 20 tur g\xF6nderiliyor");
3369
+ }
3370
+ const { stop: stopThinking, flash: thinkingFlash } = startThinking2(
3371
+ () => `${((Date.now() - start) / 1e3).toFixed(1)}s \xB7 ~${liveTokens} tok`
3372
+ );
3373
+ try {
3374
+ for await (const ev of streamChatWithRetry(session.config, msgs, 4, 1e3, (attempt, waitMs, is429) => {
3375
+ const secs2 = Math.round(waitMs / 1e3);
3376
+ thinkingFlash(is429 ? `429 \xB7 ${secs2}s sonra yeniden deneniyor\u2026 (${attempt}/3)` : `Ba\u011Flant\u0131 hatas\u0131 \xB7 ${secs2}s sonra yeniden deneniyor\u2026 (${attempt}/3)`);
3377
+ })) {
3378
+ if (ev.type === "usage") {
3379
+ if (ev.usage) {
3380
+ session.totalTokens += ev.usage.totalTokens;
3381
+ turnTokens = ev.usage.totalTokens;
3382
+ }
3383
+ continue;
3384
+ }
3385
+ if (ev.type === "reasoning") {
3386
+ liveTokens += approxTokens(ev.text);
3387
+ continue;
3388
+ }
3389
+ answer += ev.text;
3390
+ }
3391
+ } catch (err) {
3392
+ stopThinking();
3393
+ append("\n" + colors.error("\u2717 " + (err instanceof ApiError ? err.message : String(err))) + "\n");
3394
+ session.history.pop();
3395
+ return;
3396
+ }
3397
+ stopThinking();
3398
+ if (answer.length === 0) {
3399
+ append("\n" + colors.info("\u2022 (bo\u015F yan\u0131t)") + "\n");
3400
+ session.history.pop();
3401
+ return;
3402
+ }
3403
+ const rendered = renderMarkdownBlock(answer);
3404
+ const tokens = splitAnsiTokens(rendered);
3405
+ await new Promise((res) => {
3406
+ let i = 0;
3407
+ const tick = () => {
3408
+ if (finished) {
3409
+ res();
3410
+ return;
3411
+ }
3412
+ if (i >= tokens.length) {
3413
+ res();
3414
+ return;
3415
+ }
3416
+ const batch = tokens.slice(i, i + 6).join("");
3417
+ i += 6;
3418
+ appendLines(batch);
3419
+ scheduleRender();
3420
+ setTimeout(tick, 20);
3421
+ };
3422
+ tick();
3423
+ });
3424
+ const secs = ((Date.now() - start) / 1e3).toFixed(1);
3425
+ const metaLine = turnTokens > 0 ? chalk4.dim("\u21B3 ") + chalk4.greenBright.bold(String(turnTokens)) + chalk4.dim(" tok \xB7 ") + chalk4.greenBright.bold(secs + "s") : chalk4.dim("\u21B3 ") + chalk4.greenBright.bold(secs + "s");
3426
+ append("\n" + metaLine + "\n");
3427
+ session.history.push({ role: "assistant", content: answer });
3428
+ saveSessionSync(session.sessionId, session.history);
3429
+ }
3430
+ async function processInput(text) {
3431
+ const C = cols(), R = rows();
3432
+ const physBefore = lines.reduce((s, ln) => s + wrapAnsi(ln, C).length, 0);
3433
+ append("\n" + userBubble(text, cols()) + "\n");
3434
+ const totalPhys = lines.reduce((s, ln) => s + wrapAnsi(ln, C).length, 0);
3435
+ const outRows = Math.max(1, R - 3);
3436
+ scrollOffset = Math.max(0, totalPhys - physBefore - outRows);
3437
+ scheduleRender();
3438
+ if (text.startsWith("/")) {
3439
+ const { cmd } = parseCommand(text);
3440
+ let exit = false;
3441
+ if (cmd === "jira") {
3442
+ if (!session.config?.jiraToken || !session.config?.jiraBaseUrl) {
3443
+ await suspendAndRun(async () => {
3444
+ await handleCommand("/jira-login", session);
3445
+ });
3446
+ if (!session.config?.jiraToken) {
3447
+ scheduleRender();
3448
+ return;
3449
+ }
3450
+ }
3451
+ const { arg } = parseCommand(text);
3452
+ let result = null;
3453
+ try {
3454
+ result = await runJira(session, arg);
3455
+ } catch (err) {
3456
+ append("\n" + colors.error("\u2717 " + String(err)) + "\n");
3457
+ }
3458
+ if (result) {
3459
+ await suspendAndRun(async () => {
3460
+ await runJiraSave(result.key, result.plan);
3461
+ });
3462
+ }
3463
+ scheduleRender();
3464
+ return;
3465
+ }
3466
+ if (cmd === "trello-login") {
3467
+ await suspendAndRun(async () => {
3468
+ await handleCommand("/trello-login", session);
3469
+ });
3470
+ if (session.config?.trelloToken) {
3471
+ append("\n" + colors.ok("\u2713 Trello ba\u011Flant\u0131s\u0131 kuruldu.") + (session.config.trelloBoardName ? colors.dim(` Pano: ${session.config.trelloBoardName}`) : "") + "\n");
3472
+ } else {
3473
+ append("\n" + colors.error("\u2717 Trello giri\u015Fi tamamlanamad\u0131.") + "\n");
3474
+ }
3475
+ scheduleRender();
3476
+ return;
3477
+ }
3478
+ if (cmd === "trello") {
3479
+ if (!session.config?.trelloKey || !session.config?.trelloToken) {
3480
+ await suspendAndRun(async () => {
3481
+ await handleCommand("/trello-login", session);
3482
+ });
3483
+ if (session.config?.trelloToken) {
3484
+ append("\n" + colors.ok("\u2713 Trello ba\u011Flant\u0131s\u0131 kuruldu.") + (session.config.trelloBoardName ? colors.dim(` Pano: ${session.config.trelloBoardName}`) : "") + "\n");
3485
+ } else {
3486
+ scheduleRender();
3487
+ return;
3488
+ }
3489
+ }
3490
+ const { arg } = parseCommand(text);
3491
+ let trelloArg = arg;
3492
+ if (!trelloArg) {
3493
+ if (!session.config?.trelloBoardId) {
3494
+ append("\n" + colors.error("\u2717 Pano se\xE7ilmemi\u015F. \xD6nce /trello-login yap\u0131n.") + "\n");
3495
+ scheduleRender();
3496
+ return;
3497
+ }
3498
+ const pickResultBox = { value: null };
3499
+ await suspendAndRun(async () => {
3500
+ try {
3501
+ pickResultBox.value = await runTrelloPickCard(session);
3502
+ } catch {
3503
+ pickResultBox.value = null;
3504
+ }
3505
+ });
3506
+ if (!pickResultBox.value) {
3507
+ append("\n" + colors.dim(" \u0130ptal edildi.") + "\n");
3508
+ scheduleRender();
3509
+ return;
3510
+ }
3511
+ trelloArg = pickResultBox.value.shortLink;
3512
+ if (session.config) {
3513
+ session.config.trelloBoardId = pickResultBox.value.boardId;
3514
+ session.config.trelloBoardName = pickResultBox.value.boardName;
3515
+ }
3516
+ }
3517
+ try {
3518
+ const result = await runTrello(session, trelloArg, () => startThinking2(() => "").stop);
3519
+ if (result) {
3520
+ await suspendAndRun(async () => {
3521
+ await runTrelloSave(result.id, result.plan);
3522
+ });
3523
+ append("\n" + colors.ok(`\u2713 Plan kaydedildi: ${result.id}-plan.md`) + "\n");
3524
+ }
3525
+ } catch (err) {
3526
+ append("\n" + colors.error("\u2717 " + String(err)) + "\n");
3527
+ }
3528
+ scheduleRender();
3529
+ return;
3530
+ }
3531
+ if (COMMANDS_WITH_PROMPT.has(cmd)) {
3532
+ try {
3533
+ await suspendAndRun(async () => {
3534
+ await handleCommand(text, session);
3535
+ });
3536
+ } catch (err) {
3537
+ append("\n" + colors.error("\u2717 " + String(err)) + "\n");
3538
+ }
3539
+ if (cmd === "resume" && session.history.length > 0) {
3540
+ lines = [""];
3541
+ status = null;
3542
+ scrollOffset = 0;
3543
+ const preview = session.history.slice(-6);
3544
+ append(colors.dim(" \u2500\u2500 " + (session.history.length / 2 | 0) + " turlu oturum y\xFCklendi \u2500\u2500") + "\n");
3545
+ for (const m of preview) {
3546
+ if (m.role === "user") {
3547
+ append("\n" + userBubble(m.content, cols()) + "\n");
3548
+ } else {
3549
+ append(
3550
+ "\n" + turquoise("\u25E2 LASTGEN") + colors.dim(" \xB7 " + (session.config?.model ?? "")) + "\n" + renderMarkdownBlock(m.content) + "\n"
3551
+ );
3552
+ }
3553
+ }
3554
+ append(colors.dim(" \u2500\u2500 devam \u2500\u2500") + "\n\n");
3555
+ }
3556
+ } else if (cmd === "init") {
3557
+ if (!session.config) {
3558
+ append("\n" + colors.error("\u2717 \xD6nce /login yap\u0131n.") + "\n");
3559
+ scheduleRender();
3560
+ return;
3561
+ }
3562
+ const { arg: initArg } = parseCommand(text);
3563
+ const projectRoot = process.cwd();
3564
+ const exists = await (await import("./agent-init-CBHFO47V.js")).hasLastgenContext(projectRoot);
3565
+ if (exists && !initArg.includes("--force")) {
3566
+ append("\n" + colors.info("\u2022 LASTGEN.md zaten mevcut. Yenilemek i\xE7in /init --force yaz\u0131n.") + "\n");
3567
+ scheduleRender();
3568
+ return;
3569
+ }
3570
+ const { generateLastgenContext: generateLastgenContext2 } = await import("./agent-init-CBHFO47V.js");
3571
+ const initStart = Date.now();
3572
+ const initThinkingRef = { current: null };
3573
+ let initTokens = 0;
3574
+ try {
3575
+ await generateLastgenContext2(
3576
+ session.config,
3577
+ projectRoot,
3578
+ (msg) => append(colors.info("\u2022 ") + msg + "\n"),
3579
+ () => {
3580
+ initThinkingRef.current = startThinking2(() => {
3581
+ const secs2 = ((Date.now() - initStart) / 1e3).toFixed(1);
3582
+ return `${secs2}s \xB7 ~${initTokens} tok`;
3583
+ });
3584
+ },
3585
+ (tokens) => {
3586
+ initThinkingRef.current?.stop();
3587
+ initThinkingRef.current = null;
3588
+ initTokens = tokens;
3589
+ }
3590
+ );
3591
+ const secs = ((Date.now() - initStart) / 1e3).toFixed(1);
3592
+ append("\n" + colors.ok(`\u2713 LASTGEN.md olu\u015Fturuldu`) + colors.dim(` \u2014 ${secs}s \xB7 ${initTokens} tok`) + "\n");
3593
+ lastgenProjectContext = await readLastgenContext(projectRoot);
3594
+ if (lastgenProjectContext) {
3595
+ append(colors.dim(" T\xFCm sohbet mesajlar\u0131nda aktif.\n"));
3596
+ }
3597
+ } catch (err) {
3598
+ initThinkingRef.current?.stop();
3599
+ append("\n" + colors.error("\u2717 /init ba\u015Far\u0131s\u0131z: " + String(err)) + "\n");
3600
+ } finally {
3601
+ initThinkingRef.current?.stop();
3602
+ initThinkingRef.current = null;
3603
+ }
3604
+ scheduleRender();
3605
+ return;
3606
+ } else if (cmd === "trello-write-plan") {
3607
+ if (!session.config?.trelloKey || !session.config?.trelloToken) {
3608
+ await suspendAndRun(async () => {
3609
+ await handleCommand("/trello-login", session);
3610
+ });
3611
+ if (!session.config?.trelloToken) {
3612
+ scheduleRender();
3613
+ return;
3614
+ }
3615
+ }
3616
+ const pickResultBox = { value: null };
3617
+ await suspendAndRun(async () => {
3618
+ try {
3619
+ pickResultBox.value = await runTrelloPickCard(session);
3620
+ } catch {
3621
+ pickResultBox.value = null;
3622
+ }
3623
+ });
3624
+ if (!pickResultBox.value) {
3625
+ append("\n" + colors.dim(" \u0130ptal edildi.") + "\n");
3626
+ scheduleRender();
3627
+ return;
3628
+ }
3629
+ if (session.config) {
3630
+ session.config.trelloBoardId = pickResultBox.value.boardId;
3631
+ session.config.trelloBoardName = pickResultBox.value.boardName;
3632
+ }
3633
+ let agentCard = null;
3634
+ let fetchErr = null;
3635
+ await suspendAndRun(async () => {
3636
+ try {
3637
+ const { fetchCard: fetchCard2 } = await import("./trello-DY6WYSKB.js");
3638
+ agentCard = await fetchCard2(session.config, pickResultBox.value.shortLink);
3639
+ } catch (err) {
3640
+ fetchErr = String(err);
3641
+ }
3642
+ });
3643
+ if (fetchErr) {
3644
+ append("\n" + colors.error("\u2717 Kart \xE7ekilemedi: " + fetchErr) + "\n");
3645
+ scheduleRender();
3646
+ return;
3647
+ }
3648
+ if (!agentCard) {
3649
+ scheduleRender();
3650
+ return;
3651
+ }
3652
+ const cardObj = agentCard;
3653
+ {
3654
+ const chalk5 = (await import("chalk")).default;
3655
+ const cardDesc = cardObj.desc?.trim();
3656
+ append("\n" + chalk5.white.bold(cardObj.name) + "\n");
3657
+ if (cardDesc) {
3658
+ append(chalk5.dim("\u2500".repeat(40)) + "\n");
3659
+ append(chalk5.gray(cardDesc) + "\n");
3660
+ }
3661
+ append("\n");
3662
+ }
3663
+ let trelloExtraContext = "";
3664
+ await suspendAndRun(async () => {
3665
+ const input2 = await promptExtraContext(cardObj.name, cardObj.desc);
3666
+ trelloExtraContext = (typeof input2 === "string" ? input2 : "").trim();
3667
+ });
3668
+ if (trelloExtraContext) {
3669
+ append("\n" + colors.dim(" Ekstra: ") + colors.dim(trelloExtraContext) + "\n");
3670
+ }
3671
+ scrollOffset = 0;
3672
+ planToolCalls = [];
3673
+ planStackLineIdx = -1;
3674
+ planStackLineCount = 0;
3675
+ planStackExpanded = false;
3676
+ const planThinkingRef = { current: null };
3677
+ const planStart = Date.now();
3678
+ let planLiveTokens = 0;
3679
+ let planContentStartLine = -1;
3680
+ try {
3681
+ await runTrelloWritePlan(
3682
+ session,
3683
+ agentCard,
3684
+ pickResultBox.value.boardName,
3685
+ process.cwd(),
3686
+ () => {
3687
+ planThinkingRef.current = startThinking2(() => {
3688
+ const secs = ((Date.now() - planStart) / 1e3).toFixed(1);
3689
+ return `${secs}s \xB7 ~${planLiveTokens} tok`;
3690
+ });
3691
+ },
3692
+ (display) => {
3693
+ planThinkingRef.current?.stop();
3694
+ planThinkingRef.current = null;
3695
+ append("\n" + colors.dim("\u2500".repeat(50)) + "\n");
3696
+ planContentStartLine = lines.length;
3697
+ append(display + "\n");
3698
+ scheduleRender();
3699
+ },
3700
+ (tokens) => {
3701
+ planLiveTokens = tokens;
3702
+ },
3703
+ (toolText) => {
3704
+ planToolCalls.push(toolText);
3705
+ refreshToolStack();
3706
+ },
3707
+ (insight) => {
3708
+ planThinkingRef.current?.flash(insight);
3709
+ },
3710
+ trelloExtraContext || void 0
3711
+ );
3712
+ } catch (err) {
3713
+ planThinkingRef.current?.stop();
3714
+ planThinkingRef.current = null;
3715
+ append("\n" + colors.error("\u2717 " + String(err)) + "\n");
3716
+ } finally {
3717
+ planThinkingRef.current?.stop();
3718
+ planThinkingRef.current = null;
3719
+ }
3720
+ if (planContentStartLine !== -1) {
3721
+ const C2 = cols(), R2 = rows();
3722
+ const totalPhys2 = lines.reduce((s, ln) => s + wrapAnsi(ln, C2).length, 0);
3723
+ const physBefore2 = lines.slice(0, planContentStartLine).reduce((s, ln) => s + wrapAnsi(ln, C2).length, 0);
3724
+ const outRows2 = Math.max(1, R2 - 3);
3725
+ scrollOffset = Math.max(0, totalPhys2 - physBefore2 - outRows2);
3726
+ }
3727
+ scheduleRender();
3728
+ return;
3729
+ } else if (cmd === "trello-execute-plan" || cmd === "jira-execute-plan") {
3730
+ pickerActive = true;
3731
+ const selectedPlan = await new Promise((res) => {
3732
+ listPlans(process.cwd()).then((plans) => {
3733
+ if (!pickerActive) {
3734
+ res(null);
3735
+ return;
3736
+ }
3737
+ if (plans.length === 0) {
3738
+ pickerActive = false;
3739
+ append("\n" + colors.dim(" Kay\u0131tl\u0131 plan yok. \xD6nce /trello-write-plan \xE7al\u0131\u015Ft\u0131r\u0131n.") + "\n");
3740
+ res(null);
3741
+ scheduleRender();
3742
+ return;
3743
+ }
3744
+ picker = { plans, sel: 0, phase: "list", confirmSel: 0, previewScroll: 0 };
3745
+ pickerResolve = res;
3746
+ scheduleRender();
3747
+ }).catch((err) => {
3748
+ pickerActive = false;
3749
+ append("\n" + colors.error("\u2717 " + String(err)) + "\n");
3750
+ res(null);
3751
+ });
3752
+ });
3753
+ pickerActive = false;
3754
+ if (!selectedPlan) {
3755
+ scheduleRender();
3756
+ return;
3757
+ }
3758
+ pickerActive = true;
3759
+ const execMode = await new Promise((res) => {
3760
+ modePicker = { sel: 0 };
3761
+ modePickerResolve = res;
3762
+ render2();
3763
+ });
3764
+ pickerActive = false;
3765
+ if (!execMode) {
3766
+ scheduleRender();
3767
+ return;
3768
+ }
3769
+ const makeApproval = (msg) => new Promise((res) => {
3770
+ approvalPending = { resolve: res, msg };
3771
+ scheduleRender();
3772
+ });
3773
+ const onStepApproval = execMode === "step" ? (n, total, desc) => makeApproval(`Ad\u0131m ${n}/${total}: ${desc}`) : void 0;
3774
+ const onToolApproval = execMode === "file" ? (toolName, toolArgs) => {
3775
+ const target = toolArgs["path"] ?? toolArgs["target_file"] ?? "";
3776
+ return makeApproval(`${toolName}${target ? ": " + target.split(/[/\\]/).slice(-2).join("/") : ""}`);
3777
+ } : void 0;
3778
+ scrollOffset = 0;
3779
+ let execTokens = 0;
3780
+ const execStart = Date.now();
3781
+ const execThinking = startThinking2(() => {
3782
+ const secs = ((Date.now() - execStart) / 1e3).toFixed(1);
3783
+ return `${secs}s \xB7 ~${execTokens} tok`;
3784
+ });
3785
+ const abortController = new AbortController();
3786
+ currentAbortController = abortController;
3787
+ try {
3788
+ await executeAgentPlan(
3789
+ session,
3790
+ selectedPlan,
3791
+ (type) => {
3792
+ if (type === "complete" || type === "error") {
3793
+ execThinking.stop();
3794
+ }
3795
+ },
3796
+ (tokens) => {
3797
+ execTokens = tokens;
3798
+ session.totalTokens = (session.totalTokens ?? 0) + tokens;
3799
+ scheduleRender();
3800
+ },
3801
+ abortController.signal,
3802
+ execMode,
3803
+ onStepApproval,
3804
+ onToolApproval
3805
+ );
3806
+ } catch (err) {
3807
+ execThinking.stop();
3808
+ append("\n" + colors.error("\u2717 " + String(err)) + "\n");
3809
+ } finally {
3810
+ execThinking.stop();
3811
+ currentAbortController = null;
3812
+ }
3813
+ if (abortController.signal.aborted) {
3814
+ append("\n" + colors.dim(" \u26D4 Agent durduruldu.") + "\n");
3815
+ }
3816
+ scheduleRender();
3817
+ return;
3818
+ } else if (cmd === "plan") {
3819
+ const planForContext = await new Promise((res) => {
3820
+ listPlans(process.cwd()).then((plans) => {
3821
+ if (plans.length === 0) {
3822
+ append("\n" + colors.dim(" Kay\u0131tl\u0131 plan yok. \xD6nce /trello-write-plan \xE7al\u0131\u015Ft\u0131r\u0131n.") + "\n");
3823
+ res(null);
3824
+ scheduleRender();
3825
+ return;
3826
+ }
3827
+ picker = { plans, sel: 0, phase: "list", confirmSel: 0, previewScroll: 0 };
3828
+ pickerActive = true;
3829
+ pickerResolve = res;
3830
+ scheduleRender();
3831
+ }).catch(() => {
3832
+ res(null);
3833
+ });
3834
+ });
3835
+ pickerActive = false;
3836
+ if (!planForContext) {
3837
+ scheduleRender();
3838
+ return;
3839
+ }
3840
+ const { promises: fs22 } = await import("fs");
3841
+ const planPath = planForContext.projectRoot ? `${planForContext.projectRoot}/.trello_plans` : `${process.cwd()}/.trello_plans`;
3842
+ let planRaw = planForContext.planMarkdown;
3843
+ const planContext = `## Y\xFCklenen Plan: ${planForContext.trelloCardName}
3844
+ Pano: ${planForContext.boardName} \xB7 Test: ${planForContext.testCommand}
3845
+ Olu\u015Fturma: ${new Date(planForContext.createdAt).toLocaleString("tr-TR")}
3846
+
3847
+ ` + planRaw;
3848
+ session.history.push({ role: "user", content: `A\u015Fa\u011F\u0131daki uygulama plan\u0131n\u0131 incele ve sorular\u0131ma cevap ver:
3849
+
3850
+ ${planContext}` });
3851
+ session.history.push({ role: "assistant", content: `Plan y\xFCklendi \u2713 **${planForContext.trelloCardName}** hakk\u0131nda sorular\u0131n\u0131z\u0131 sorabilirsiniz.` });
3852
+ append("\n" + colors.ok(`\u2713 Plan y\xFCklendi: ${planForContext.trelloCardName}`) + "\n");
3853
+ append(colors.dim(" Planla ilgili sorular\u0131n\u0131z\u0131 sorabilirsiniz.\n"));
3854
+ scheduleRender();
3855
+ return;
3856
+ } else if (cmd === "jira-write-plan") {
3857
+ const { arg } = parseCommand(text);
3858
+ let issueKey = arg.trim();
3859
+ if (!issueKey) {
3860
+ await suspendAndRun(async () => {
3861
+ const k = await import("./ui-QJPYDWYD.js").then(
3862
+ (m) => m.prompt(m.colors.dim("Jira issue key veya URL (\xF6r. PROJ-123): "))
3863
+ );
3864
+ issueKey = (typeof k === "string" ? k : "").trim();
3865
+ });
3866
+ }
3867
+ if (!issueKey) {
3868
+ append("\n" + colors.dim(" \u0130ptal edildi.") + "\n");
3869
+ scheduleRender();
3870
+ return;
3871
+ }
3872
+ let jiraExtraContext = "";
3873
+ let jiraIssueLoaded = false;
3874
+ await suspendAndRun(async () => {
3875
+ try {
3876
+ const { fetchIssue: fetchIssue2 } = await import("./jira-6526JRPQ.js");
3877
+ const issue = await fetchIssue2(session.config, issueKey);
3878
+ jiraIssueLoaded = true;
3879
+ const input2 = await promptExtraContext(issue.summary, issue.description);
3880
+ jiraExtraContext = (typeof input2 === "string" ? input2 : "").trim();
3881
+ } catch (err) {
3882
+ error(String(err));
3883
+ }
3884
+ });
3885
+ if (jiraIssueLoaded) {
3886
+ const chalk5 = (await import("chalk")).default;
3887
+ append("\n" + chalk5.white.bold("(issue y\xFCklendi)") + "\n");
3888
+ }
3889
+ if (jiraExtraContext) {
3890
+ append("\n" + colors.dim(" Ekstra: ") + colors.dim(jiraExtraContext) + "\n");
3891
+ }
3892
+ planToolCalls = [];
3893
+ planStackLineIdx = -1;
3894
+ planStackLineCount = 0;
3895
+ planStackExpanded = false;
3896
+ const jiraThinkingRef = { current: null };
3897
+ const jiraStart = Date.now();
3898
+ let jiraLiveTokens = 0;
3899
+ let jiraPlanContentStartLine = -1;
3900
+ try {
3901
+ await runJiraWritePlan(
3902
+ session,
3903
+ issueKey,
3904
+ process.cwd(),
3905
+ () => {
3906
+ jiraThinkingRef.current = startThinking2(() => {
3907
+ const secs = ((Date.now() - jiraStart) / 1e3).toFixed(1);
3908
+ return `${secs}s \xB7 ~${jiraLiveTokens} tok`;
3909
+ });
3910
+ },
3911
+ (display) => {
3912
+ jiraThinkingRef.current?.stop();
3913
+ jiraThinkingRef.current = null;
3914
+ append("\n" + colors.dim("\u2500".repeat(50)) + "\n");
3915
+ jiraPlanContentStartLine = lines.length;
3916
+ append(display + "\n");
3917
+ scheduleRender();
3918
+ },
3919
+ (tokens) => {
3920
+ jiraLiveTokens = tokens;
3921
+ },
3922
+ (toolText) => {
3923
+ planToolCalls.push(toolText);
3924
+ refreshToolStack();
3925
+ },
3926
+ void 0,
3927
+ (insight) => {
3928
+ jiraThinkingRef.current?.flash(insight);
3929
+ },
3930
+ jiraExtraContext || void 0
3931
+ );
3932
+ } catch (err) {
3933
+ jiraThinkingRef.current?.stop();
3934
+ jiraThinkingRef.current = null;
3935
+ append("\n" + colors.error("\u2717 " + String(err)) + "\n");
3936
+ } finally {
3937
+ jiraThinkingRef.current?.stop();
3938
+ jiraThinkingRef.current = null;
3939
+ }
3940
+ if (jiraPlanContentStartLine !== -1) {
3941
+ const C2 = cols(), R2 = rows();
3942
+ const totalPhys2 = lines.reduce((s, ln) => s + wrapAnsi(ln, C2).length, 0);
3943
+ const physBefore2 = lines.slice(0, jiraPlanContentStartLine).reduce((s, ln) => s + wrapAnsi(ln, C2).length, 0);
3944
+ scrollOffset = Math.max(0, totalPhys2 - physBefore2 - Math.max(1, R2 - 3));
3945
+ }
3946
+ scheduleRender();
3947
+ return;
3948
+ } else {
3949
+ const result = await handleCommand(text, session);
3950
+ exit = Boolean(result.exit);
3951
+ if (result.clearScreen) {
3952
+ lines = [""];
3953
+ status = null;
3954
+ scrollOffset = 0;
3955
+ }
3956
+ }
3957
+ scheduleRender();
3958
+ if (exit) finish();
3959
+ return;
3960
+ }
3961
+ await streamAnswer(text);
3962
+ }
3963
+ function run(text) {
3964
+ busy = true;
3965
+ Promise.resolve(processInput(text)).catch((err) => {
3966
+ try {
3967
+ const stack = err instanceof Error ? err.stack ?? String(err) : String(err);
3968
+ append("\n" + colors.error("\u2717 \u0130\u015Flem hatas\u0131:\n" + stack) + "\n");
3969
+ scheduleRender();
3970
+ } catch {
3971
+ }
3972
+ }).finally(() => {
3973
+ busy = false;
3974
+ if (queue.length > 0 && !finished) {
3975
+ if (queueEditIdx === 0) queueEditIdx = -1;
3976
+ else if (queueEditIdx > 0) queueEditIdx--;
3977
+ run(queue.shift());
3978
+ } else {
3979
+ queueEditIdx = -1;
3980
+ }
3981
+ });
3982
+ }
3983
+ const PASTE_START = "\x1B[200~";
3984
+ const PASTE_END = "\x1B[201~";
3985
+ function onData(chunk) {
3986
+ if (modePicker && modePickerResolve) {
3987
+ const code = chunk.charCodeAt(0);
3988
+ if (code === 3 || code === 27 && chunk.length === 1) {
3989
+ modePicker = null;
3990
+ const res = modePickerResolve;
3991
+ modePickerResolve = null;
3992
+ pickerActive = false;
3993
+ pickerCooldown = true;
3994
+ setTimeout(() => {
3995
+ pickerCooldown = false;
3996
+ }, 500);
3997
+ render2();
3998
+ res(null);
3999
+ return;
4000
+ }
4001
+ if (chunk === "\x1B[A") {
4002
+ modePicker.sel = Math.max(0, modePicker.sel - 1);
4003
+ scheduleRender();
4004
+ return;
4005
+ }
4006
+ if (chunk === "\x1B[B") {
4007
+ modePicker.sel = Math.min(EXEC_MODES.length - 1, modePicker.sel + 1);
4008
+ scheduleRender();
4009
+ return;
4010
+ }
4011
+ if (chunk === "\r" || chunk === "\n") {
4012
+ const mode = EXEC_MODES[modePicker.sel].mode;
4013
+ modePicker = null;
4014
+ const res = modePickerResolve;
4015
+ modePickerResolve = null;
4016
+ render2();
4017
+ res(mode);
4018
+ return;
4019
+ }
4020
+ const num = parseInt(chunk, 10);
4021
+ if (num >= 1 && num <= EXEC_MODES.length) {
4022
+ modePicker.sel = num - 1;
4023
+ scheduleRender();
4024
+ return;
4025
+ }
4026
+ return;
4027
+ }
4028
+ if (approvalPending) {
4029
+ const code = chunk.charCodeAt(0);
4030
+ if (chunk === "\r" || chunk === "\n") {
4031
+ const r = approvalPending.resolve;
4032
+ approvalPending = null;
4033
+ scheduleRender();
4034
+ r(true);
4035
+ return;
4036
+ }
4037
+ if (code === 27 || chunk === "n" || chunk === "N") {
4038
+ const r = approvalPending.resolve;
4039
+ approvalPending = null;
4040
+ scheduleRender();
4041
+ r(false);
4042
+ return;
4043
+ }
4044
+ if (code === 3) {
4045
+ const r = approvalPending.resolve;
4046
+ approvalPending = null;
4047
+ currentAbortController?.abort();
4048
+ scheduleRender();
4049
+ r(false);
4050
+ return;
4051
+ }
4052
+ return;
4053
+ }
4054
+ if (picker && pickerResolve) {
4055
+ const code = chunk.charCodeAt(0);
4056
+ if (picker.phase === "list") {
4057
+ if (code === 3 || code === 27 && chunk.length === 1) {
4058
+ cancelPicker();
4059
+ return;
4060
+ }
4061
+ if (chunk === "\x1B[A") {
4062
+ picker.sel = picker.sel > 0 ? picker.sel - 1 : picker.plans.length - 1;
4063
+ scheduleRender();
4064
+ return;
4065
+ }
4066
+ if (chunk === "\x1B[B") {
4067
+ picker.sel = picker.sel < picker.plans.length - 1 ? picker.sel + 1 : 0;
4068
+ scheduleRender();
4069
+ return;
4070
+ }
4071
+ if (chunk === "\r" || chunk === "\n") {
4072
+ picker.phase = "preview";
4073
+ picker.previewScroll = 0;
4074
+ scheduleRender();
4075
+ return;
4076
+ }
4077
+ } else if (picker.phase === "preview") {
4078
+ if (code === 3 || code === 27 && chunk.length === 1) {
4079
+ picker.phase = "list";
4080
+ scheduleRender();
4081
+ return;
4082
+ }
4083
+ if (chunk === "\x1B[A") {
4084
+ picker.previewScroll = Math.max(0, picker.previewScroll - 1);
4085
+ scheduleRender();
4086
+ return;
4087
+ }
4088
+ if (chunk === "\x1B[B") {
4089
+ picker.previewScroll++;
4090
+ scheduleRender();
4091
+ return;
4092
+ }
4093
+ if (chunk === "\x1B[5~") {
4094
+ picker.previewScroll = Math.max(0, picker.previewScroll - 10);
4095
+ scheduleRender();
4096
+ return;
4097
+ }
4098
+ if (chunk === "\x1B[6~") {
4099
+ picker.previewScroll += 10;
4100
+ scheduleRender();
4101
+ return;
4102
+ }
4103
+ if (chunk === "\r" || chunk === "\n") {
4104
+ const plan = picker.plans[picker.sel];
4105
+ const res = pickerResolve;
4106
+ picker = null;
4107
+ pickerResolve = null;
4108
+ pickerActive = false;
4109
+ render2();
4110
+ res(plan);
4111
+ return;
4112
+ }
4113
+ } else {
4114
+ if (code === 3 || code === 27 && chunk.length === 1) {
4115
+ cancelPicker();
4116
+ return;
4117
+ }
4118
+ if (chunk === "\x1B[A") {
4119
+ picker.confirmSel = picker.confirmSel > 0 ? 0 : 1;
4120
+ scheduleRender();
4121
+ return;
4122
+ }
4123
+ if (chunk === "\x1B[B") {
4124
+ picker.confirmSel = picker.confirmSel < 1 ? 1 : 0;
4125
+ scheduleRender();
4126
+ return;
4127
+ }
4128
+ if (chunk === "\r" || chunk === "\n") {
4129
+ if (picker.confirmSel === 0) {
4130
+ const plan = picker.plans[picker.sel];
4131
+ const res = pickerResolve;
4132
+ picker = null;
4133
+ pickerResolve = null;
4134
+ pickerActive = false;
4135
+ render2();
4136
+ res(plan);
4137
+ return;
4138
+ } else {
4139
+ picker.phase = "list";
4140
+ scheduleRender();
4141
+ return;
4142
+ }
4143
+ }
4144
+ }
4145
+ return;
4146
+ }
4147
+ if (chunk.includes(PASTE_START)) {
4148
+ inBracketedPaste = true;
4149
+ pasteBuffer = "";
4150
+ chunk = chunk.slice(chunk.indexOf(PASTE_START) + PASTE_START.length);
4151
+ }
4152
+ if (inBracketedPaste) {
4153
+ if (chunk.includes(PASTE_END)) {
4154
+ pasteBuffer += chunk.slice(0, chunk.indexOf(PASTE_END));
4155
+ pasteCount++;
4156
+ input = pasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4157
+ inputIsPaste = true;
4158
+ pasteExtra = "";
4159
+ inBracketedPaste = false;
4160
+ pasteBuffer = "";
4161
+ scheduleRender();
4162
+ } else {
4163
+ pasteBuffer += chunk;
4164
+ }
4165
+ return;
4166
+ }
4167
+ if (chunk.charCodeAt(0) === 27) {
4168
+ const R = rows(), C = cols();
4169
+ const queuePanelH = queue.length > 0 ? queue.length + 2 : 0;
4170
+ const pieces = wrapInput(inputDisplay(), C);
4171
+ const outRows = Math.max(1, R - pieces.length - 2 - queuePanelH);
4172
+ let physLen = lines.reduce((s, ln) => s + wrapAnsi(ln, C).length, 0);
4173
+ if (status !== null) physLen += wrapAnsi(status, C).length;
4174
+ const maxScroll = Math.max(0, physLen - outRows);
4175
+ const acQuery = !inputIsPaste && !acClosed && !busy && input.startsWith("/") ? input.slice(1).toLowerCase() : null;
4176
+ const acMatches = acQuery !== null ? COMMAND_LIST.filter(({ cmd }) => cmd.startsWith(acQuery)) : [];
4177
+ const acVisible = acMatches.length > 0;
4178
+ if (chunk === "\x1B[A") {
4179
+ if (acVisible) {
4180
+ acIdx = Math.max(0, Math.min(acIdx, acMatches.length - 1) - 1);
4181
+ scheduleRender();
4182
+ return;
4183
+ }
4184
+ if (busy && queue.length > 0) {
4185
+ if (queueEditIdx === -1) {
4186
+ queueEditIdx = queue.length - 1;
4187
+ } else if (queueEditIdx > 0) {
4188
+ queue[queueEditIdx] = input;
4189
+ queueEditIdx--;
4190
+ }
4191
+ input = queue[queueEditIdx];
4192
+ inputIsPaste = false;
4193
+ pasteExtra = "";
4194
+ } else if (!busy && inputHistory.length > 0) {
4195
+ if (historyIdx === -1) {
4196
+ historySaved = input;
4197
+ historyIdx = inputHistory.length - 1;
4198
+ } else if (historyIdx > 0) {
4199
+ historyIdx--;
4200
+ }
4201
+ input = inputHistory[historyIdx];
4202
+ inputIsPaste = false;
4203
+ pasteExtra = "";
4204
+ } else {
4205
+ scrollOffset = Math.min(scrollOffset + 1, maxScroll);
4206
+ }
4207
+ scheduleRender();
4208
+ } else if (chunk === "\x1B[B") {
4209
+ if (acVisible) {
4210
+ acIdx = Math.min(Math.min(acIdx, acMatches.length - 1) + 1, acMatches.length - 1);
4211
+ scheduleRender();
4212
+ return;
4213
+ }
4214
+ if (busy && queueEditIdx >= 0) {
4215
+ queue[queueEditIdx] = input;
4216
+ if (queueEditIdx < queue.length - 1) {
4217
+ queueEditIdx++;
4218
+ input = queue[queueEditIdx];
4219
+ } else {
4220
+ queueEditIdx = -1;
4221
+ input = "";
4222
+ }
4223
+ inputIsPaste = false;
4224
+ pasteExtra = "";
4225
+ } else if (!busy && historyIdx >= 0) {
4226
+ if (historyIdx < inputHistory.length - 1) {
4227
+ historyIdx++;
4228
+ input = inputHistory[historyIdx];
4229
+ } else {
4230
+ historyIdx = -1;
4231
+ input = historySaved;
4232
+ historySaved = "";
4233
+ }
4234
+ inputIsPaste = false;
4235
+ pasteExtra = "";
4236
+ } else {
4237
+ scrollOffset = Math.max(0, scrollOffset - 1);
4238
+ }
4239
+ scheduleRender();
4240
+ } else if (chunk === "\x1B[5~") {
4241
+ scrollOffset = Math.min(scrollOffset + Math.floor(outRows / 2), maxScroll);
4242
+ scheduleRender();
4243
+ } else if (chunk === "\x1B[6~") {
4244
+ scrollOffset = Math.max(0, scrollOffset - Math.floor(outRows / 2));
4245
+ scheduleRender();
4246
+ }
4247
+ return;
4248
+ }
4249
+ if (chunk === " ") {
4250
+ const tabQuery = !inputIsPaste && !acClosed && !busy && input.startsWith("/") ? input.slice(1).toLowerCase() : null;
4251
+ const tabMatches = tabQuery !== null ? COMMAND_LIST.filter(({ cmd }) => cmd.startsWith(tabQuery)) : [];
4252
+ if (tabMatches.length > 0) {
4253
+ const chosen = tabMatches[Math.max(0, Math.min(acIdx, tabMatches.length - 1))];
4254
+ input = "/" + chosen.cmd + " ";
4255
+ acClosed = true;
4256
+ acIdx = 0;
4257
+ scheduleRender();
4258
+ }
4259
+ return;
4260
+ }
4261
+ if (chunk === "\x1B" || chunk.length === 3 && chunk.startsWith("\x1B") && !chunk.startsWith("\x1B[")) {
4262
+ }
4263
+ if (chunk === "\x1B") {
4264
+ if (!acClosed && input.startsWith("/")) {
4265
+ acClosed = true;
4266
+ scheduleRender();
4267
+ return;
4268
+ }
4269
+ }
4270
+ let submit = false;
4271
+ for (const ch of chunk) {
4272
+ const code = ch.charCodeAt(0);
4273
+ if (ch === "\r" || ch === "\n") {
4274
+ const enterQuery = !inputIsPaste && !acClosed && !busy && input.startsWith("/") ? input.slice(1).toLowerCase() : null;
4275
+ const enterMatches = enterQuery !== null ? COMMAND_LIST.filter(({ cmd }) => cmd.startsWith(enterQuery)) : [];
4276
+ const exactMatch = enterMatches.length === 1 || enterMatches.length > 0 && enterMatches[0].cmd === enterQuery;
4277
+ if (enterMatches.length > 0 && !exactMatch) {
4278
+ const chosen = enterMatches[Math.max(0, Math.min(acIdx, enterMatches.length - 1))];
4279
+ input = "/" + chosen.cmd + " ";
4280
+ acClosed = true;
4281
+ acIdx = 0;
4282
+ scheduleRender();
4283
+ return;
4284
+ }
4285
+ submit = true;
4286
+ break;
4287
+ } else if (code === CTRL_C) {
4288
+ if (currentAbortController) {
4289
+ currentAbortController.abort();
4290
+ } else if (approvalPending) {
4291
+ const r = approvalPending.resolve;
4292
+ approvalPending = null;
4293
+ scheduleRender();
4294
+ r(false);
4295
+ } else if (modePicker) {
4296
+ modePicker = null;
4297
+ const res = modePickerResolve;
4298
+ modePickerResolve = null;
4299
+ pickerActive = false;
4300
+ pickerCooldown = true;
4301
+ setTimeout(() => {
4302
+ pickerCooldown = false;
4303
+ }, 500);
4304
+ render2();
4305
+ res?.(null);
4306
+ } else if (pickerActive || pickerCooldown) {
4307
+ if (pickerActive) cancelPicker();
4308
+ } else {
4309
+ finish();
4310
+ }
4311
+ return;
4312
+ } else if (code === CTRL_O) {
4313
+ if (planToolCalls.length > 0 && planStackLineIdx !== -1) {
4314
+ planStackExpanded = !planStackExpanded;
4315
+ refreshToolStack();
4316
+ }
4317
+ return;
4318
+ } else if (code === BACKSPACE || code === DEL) {
4319
+ acClosed = false;
4320
+ acIdx = 0;
4321
+ if (inputIsPaste) {
4322
+ if (pasteExtra.length > 0) pasteExtra = [...pasteExtra].slice(0, -1).join("");
4323
+ else {
4324
+ input = "";
4325
+ inputIsPaste = false;
4326
+ }
4327
+ } else {
4328
+ input = [...input].slice(0, -1).join("");
4329
+ }
4330
+ } else if (code >= 32) {
4331
+ if (inputIsPaste) pasteExtra += ch;
4332
+ else {
4333
+ input += ch;
4334
+ acClosed = false;
4335
+ acIdx = 0;
4336
+ }
4337
+ }
4338
+ }
4339
+ if (submit) {
4340
+ if (queueEditIdx >= 0) {
4341
+ const edited = input.trim();
4342
+ if (edited.length === 0) {
4343
+ queue.splice(queueEditIdx, 1);
4344
+ } else {
4345
+ queue[queueEditIdx] = edited;
4346
+ }
4347
+ queueEditIdx = -1;
4348
+ input = "";
4349
+ inputIsPaste = false;
4350
+ pasteExtra = "";
4351
+ scheduleRender();
4352
+ return;
4353
+ }
4354
+ const t = (inputIsPaste ? input + (pasteExtra ? "\n" + pasteExtra : "") : input).trim();
4355
+ input = "";
4356
+ inputIsPaste = false;
4357
+ pasteExtra = "";
4358
+ historyIdx = -1;
4359
+ historySaved = "";
4360
+ scrollOffset = 0;
4361
+ scheduleRender();
4362
+ if (t.length > 0) {
4363
+ if (inputHistory[inputHistory.length - 1] !== t) inputHistory.push(t);
4364
+ if (busy) queue.push(t);
4365
+ else run(t);
4366
+ }
4367
+ return;
4368
+ }
4369
+ if (queueEditIdx >= 0 && queueEditIdx < queue.length) {
4370
+ queue[queueEditIdx] = input;
4371
+ }
4372
+ scheduleRender();
4373
+ }
4374
+ async function playLogoDecode(settle) {
4375
+ const TOTAL_FRAMES = 16;
4376
+ const model = session.config?.model;
4377
+ await new Promise((res) => {
4378
+ let frame = 0;
4379
+ const tick = () => {
4380
+ if (finished) {
4381
+ res();
4382
+ return;
4383
+ }
4384
+ const logoLines = logoDecodeLines(frame, TOTAL_FRAMES, settle);
4385
+ const banner = bannerLines(model, logoLines);
4386
+ for (let i = 0; i < banner.length && i < lines.length; i++) {
4387
+ lines[i] = banner[i];
4388
+ }
4389
+ scheduleRender();
4390
+ if (frame < TOTAL_FRAMES) {
4391
+ frame++;
4392
+ setTimeout(tick, 70);
4393
+ } else {
4394
+ const welcome = WELCOMES[Math.floor(Math.random() * WELCOMES.length)];
4395
+ appendLines("\n" + turquoise("\u25E2 LASTGEN ") + turquoise(welcome) + "\n");
4396
+ scheduleRender();
4397
+ setTimeout(res, 350);
4398
+ }
4399
+ };
4400
+ tick();
4401
+ });
4402
+ }
4403
+ enterScreen();
4404
+ const logoSettle = buildLogoSettle(16);
4405
+ {
4406
+ const initialLogo = logoDecodeLines(0, 16, logoSettle);
4407
+ append(bannerLines(session.config?.model, initialLogo).join("\n") + "\n");
4408
+ }
4409
+ if (session.history.length > 0) {
4410
+ const preview = session.history.slice(-6);
4411
+ append(colors.dim(" \u2500\u2500 \xF6nceki oturum \u2500\u2500") + "\n");
4412
+ for (const m of preview) {
4413
+ if (m.role === "user") {
4414
+ append("\n" + userBubble(m.content, cols()) + "\n");
4415
+ } else {
4416
+ append(
4417
+ "\n" + turquoise("\u25E2 LASTGEN") + colors.dim(" \xB7 " + (session.config?.model ?? "")) + "\n" + renderMarkdownBlock(m.content) + "\n"
4418
+ );
4419
+ }
4420
+ }
4421
+ append(colors.dim(" \u2500\u2500 yeni oturum \u2500\u2500") + "\n\n");
4422
+ }
4423
+ playLogoDecode(logoSettle);
4424
+ });
4425
+ }
4426
+
4427
+ // src/repl.ts
4428
+ function buildMessages2(session) {
4429
+ const messages = [];
4430
+ if (session.config?.systemPrompt) {
4431
+ messages.push({ role: "system", content: session.config.systemPrompt });
4432
+ }
4433
+ for (const m of session.history) {
4434
+ messages.push({ role: m.role, content: m.content });
4435
+ }
4436
+ return messages;
4437
+ }
4438
+ async function sendMessage(session, text) {
4439
+ if (!session.config) {
4440
+ error("\xD6nce /login ile giri\u015F yap\u0131n.");
4441
+ return;
4442
+ }
4443
+ if (!session.config.model) {
4444
+ error("Aktif model yok. /models ile bir model se\xE7in.");
4445
+ return;
4446
+ }
4447
+ session.history.push({ role: "user", content: text });
4448
+ let answer = "";
4449
+ let answering = false;
4450
+ const md = createMarkdownStream();
4451
+ const indent = (s) => s.replace(/\n/g, "\n" + GUTTER);
4452
+ const start = Date.now();
4453
+ let liveTokens = 0;
4454
+ let turnTokens = 0;
4455
+ const approxTokens2 = (s) => Math.max(1, Math.ceil([...s].length / 4));
4456
+ assistantHeader(session.config.model);
4457
+ const thinking = startThinking(GUTTER, () => {
4458
+ const secs2 = ((Date.now() - start) / 1e3).toFixed(1);
4459
+ return `${secs2}s \xB7 ~${liveTokens} tok`;
4460
+ });
4461
+ let thinkingStopped = false;
4462
+ const stopThinking = () => {
4463
+ if (!thinkingStopped) {
4464
+ thinking.stop();
4465
+ thinkingStopped = true;
4466
+ }
4467
+ };
4468
+ try {
4469
+ for await (const ev of streamChatWithRetry(session.config, buildMessages2(session))) {
4470
+ if (ev.type === "usage") {
4471
+ if (ev.usage) {
4472
+ session.totalTokens += ev.usage.totalTokens;
4473
+ turnTokens = ev.usage.totalTokens;
4474
+ }
4475
+ continue;
4476
+ }
4477
+ if (ev.type === "reasoning") {
4478
+ liveTokens += approxTokens2(ev.text);
4479
+ continue;
4480
+ }
4481
+ if (!answering) {
4482
+ stopThinking();
4483
+ write(GUTTER);
4484
+ answering = true;
4485
+ }
4486
+ answer += ev.text;
4487
+ write(indent(md.push(ev.text)));
4488
+ }
4489
+ } catch (err) {
4490
+ stopThinking();
4491
+ newline();
4492
+ error(err instanceof ApiError ? err.message : String(err));
4493
+ session.history.pop();
4494
+ return;
4495
+ }
4496
+ stopThinking();
4497
+ write(indent(md.flush()));
4498
+ newline();
4499
+ if (answer.length === 0) {
4500
+ info("(bo\u015F yan\u0131t)");
4501
+ session.history.pop();
4502
+ return;
4503
+ }
4504
+ const secs = ((Date.now() - start) / 1e3).toFixed(1);
4505
+ const meta = turnTokens > 0 ? `${turnTokens} tok \xB7 ${secs}s` : `${secs}s`;
4506
+ write(GUTTER + colors.dim("\u21B3 " + meta) + "\n");
4507
+ session.history.push({ role: "assistant", content: answer });
4508
+ }
4509
+ async function startRepl() {
4510
+ migrateLegacyHistory();
4511
+ const sessionId = createSession();
4512
+ const loadedHistory = [];
4513
+ const session = {
4514
+ config: applyEnvOverrides(await loadConfig()),
4515
+ history: loadedHistory,
4516
+ totalTokens: 0,
4517
+ sessionId
4518
+ };
4519
+ const useTui = !process.env.QAI_NO_TUI;
4520
+ if (!useTui) {
4521
+ await bannerDecode(session.config?.model);
4522
+ }
4523
+ if (!session.config || !session.config.apiKey) {
4524
+ if (session.config) {
4525
+ info("Ba\u011Flant\u0131 ayarlar\u0131 y\xFCklendi \u2014 sadece API key gerekiyor.");
4526
+ } else {
4527
+ info("Yap\u0131land\u0131rma bulunamad\u0131. \xD6nce giri\u015F yapal\u0131m.");
4528
+ }
4529
+ await runLogin(session);
4530
+ if (!session.config) {
4531
+ error("Giri\u015F yap\u0131lmad\u0131. \xC7\u0131k\u0131l\u0131yor.");
4532
+ closeInput();
4533
+ return;
4534
+ }
4535
+ }
4536
+ if (!useTui) newline();
4537
+ if (useTui) {
4538
+ const ran = await runChatTUI(session);
4539
+ if (ran) {
4540
+ saveSessionSync(session.sessionId, session.history);
4541
+ return;
4542
+ }
4543
+ }
4544
+ process.on("SIGINT", () => {
4545
+ goodbye();
4546
+ closeInput();
4547
+ process.exit(0);
4548
+ });
4549
+ while (true) {
4550
+ const input = await promptBoxed(session.totalTokens);
4551
+ if (input.length === 0) continue;
4552
+ const result = await handleCommand(input, session);
4553
+ if (result.exit) break;
4554
+ if (result.clearScreen) {
4555
+ process.stdout.write("\x1Bc");
4556
+ continue;
4557
+ }
4558
+ if (result.handled) continue;
4559
+ await sendMessage(session, input);
4560
+ }
4561
+ goodbye();
4562
+ closeInput();
4563
+ }
4564
+
4565
+ // src/index.ts
4566
+ async function main() {
4567
+ const arg = process.argv[2];
4568
+ if (arg === "--version" || arg === "-v") {
4569
+ write("lastgen 1.0.0\n");
4570
+ return;
4571
+ }
4572
+ if (arg === "--help" || arg === "-h") {
4573
+ write(
4574
+ "Lastgen CLI \u2014 OpenAI uyumlu terminal AI sohbeti\n\nKullan\u0131m: lastgen\n\nEtkile\u015Fimli moda girer. \u0130\xE7eride /help ile komutlar\u0131 g\xF6rebilirsiniz.\n"
4575
+ );
4576
+ return;
4577
+ }
4578
+ await startRepl();
4579
+ }
4580
+ main().catch((err) => {
4581
+ error(String(err?.stack ?? err));
4582
+ process.exit(1);
4583
+ });