-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrefactor-plan.html
More file actions
698 lines (648 loc) · 60.5 KB
/
Copy pathrefactor-plan.html
File metadata and controls
698 lines (648 loc) · 60.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RefactorPlan — AI Generator</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Syne:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--bg:#0e0f11;--bg2:#161719;--bg3:#1e2022;--border:#2a2d31;--accent:#f5a623;--accent2:#e8543a;--text:#e8e9eb;--muted:#6b7280;--green:#4ade80;--blue:#60a5fa;--purple:#a78bfa}
body{background:var(--bg);color:var(--text);font-family:'Syne',sans-serif;min-height:100vh;padding:28px 24px;max-width:820px;margin:0 auto;position:relative}
header{display:flex;align-items:center;gap:14px;margin-bottom:32px}
.logo{width:40px;height:40px;background:var(--accent);border-radius:10px;display:flex;align-items:center;justify-content:center;font-family:'JetBrains Mono',monospace;font-weight:700;font-size:15px;color:#000;flex-shrink:0}
h1{font-size:22px;font-weight:800;letter-spacing:-0.5px}h1 span{color:var(--accent)}
.subtitle{font-size:12px;color:var(--muted);font-family:'JetBrains Mono',monospace;margin-top:2px}
.input-area{background:var(--bg2);border:1px solid var(--border);border-radius:14px;padding:22px;margin-bottom:18px}
.input-label{font-size:11px;font-family:'JetBrains Mono',monospace;color:var(--accent);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px}
textarea{width:100%;background:var(--bg3);border:1px solid var(--border);border-radius:8px;color:var(--text);font-family:'Syne',sans-serif;font-size:14px;padding:14px;resize:none;outline:none;transition:border-color .2s;min-height:110px;line-height:1.6}
textarea:focus{border-color:var(--accent)}textarea::placeholder{color:var(--muted)}
textarea.dragover{border-color:var(--green);background:rgba(74,222,128,.08)}
.options-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}
.chip{background:var(--bg3);border:1px solid var(--border);border-radius:20px;padding:6px 13px;font-size:12px;cursor:pointer;transition:all .15s;color:var(--muted);font-family:'JetBrains Mono',monospace}
.chip:hover{border-color:var(--accent);color:var(--accent)}.chip.active{background:var(--accent);border-color:var(--accent);color:#000;font-weight:600}
.btn{width:100%;background:var(--accent);color:#000;border:none;border-radius:10px;padding:15px;font-family:'Syne',sans-serif;font-weight:800;font-size:14px;cursor:pointer;transition:all .2s;letter-spacing:.5px;margin-top:14px}
.btn:hover{background:#f5b84a;transform:translateY(-1px)}.btn:disabled{background:var(--border);color:var(--muted);transform:none;cursor:not-allowed}
.btn-sec{flex:1;background:var(--bg2);border:1px solid var(--border);color:var(--text);border-radius:8px;padding:11px;font-family:'Syne',sans-serif;font-weight:600;font-size:12px;cursor:pointer;transition:all .15s}
.btn-sec:hover{border-color:var(--accent);color:var(--accent)}
.loading{display:none;text-align:center;padding:36px;color:var(--muted);font-family:'JetBrains Mono',monospace;font-size:13px}
.spinner{width:30px;height:30px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 14px}
@keyframes spin{to{transform:rotate(360deg)}}
.plan-output{display:none}
.plan-header{background:linear-gradient(135deg,var(--bg2) 0%,#1a1c20 100%);border:1px solid var(--border);border-radius:14px;padding:22px;margin-bottom:18px;position:relative;overflow:hidden}
.plan-header::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--accent),var(--accent2))}
.plan-title{font-size:19px;font-weight:800;margin-bottom:6px}
.plan-meta{font-size:13px;color:var(--muted);line-height:1.5}
.stats-row{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:16px}
.stat{background:var(--bg3);border-radius:8px;padding:12px;text-align:center}
.stat-val{font-size:24px;font-weight:800;color:var(--accent)}.stat-label{font-size:10px;color:var(--muted);font-family:'JetBrains Mono',monospace;margin-top:2px}
.phase{background:var(--bg2);border:1px solid var(--border);border-radius:12px;margin-bottom:12px;overflow:hidden;animation:fadeIn .4s ease forwards;opacity:0}
@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
.phase-header{display:flex;align-items:center;gap:12px;padding:16px 18px;cursor:pointer;user-select:none}
.phase-num{width:30px;height:30px;border-radius:7px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;font-family:'JetBrains Mono',monospace;flex-shrink:0}
.phase-title{font-weight:700;font-size:15px;flex:1}.phase-badge{font-size:10px;padding:3px 9px;border-radius:20px;font-family:'JetBrains Mono',monospace}
.phase-dur{font-size:11px;color:var(--muted);font-family:'JetBrains Mono',monospace}.phase-toggle{color:var(--muted);font-size:18px;transition:transform .2s}
.phase.open .phase-toggle{transform:rotate(180deg)}.phase-body{display:none;padding:0 18px 18px}.phase.open .phase-body{display:block}
.progress-bar{height:4px;background:var(--border);border-radius:2px;margin-bottom:14px}.progress-fill{height:100%;border-radius:2px;background:linear-gradient(90deg,var(--accent),var(--accent2));transition:width .4s;width:0%}
.task-list{list-style:none}.task{display:flex;align-items:flex-start;gap:10px;padding:10px 0;border-bottom:1px solid var(--border);font-size:13px;line-height:1.5}
.task:last-child{border-bottom:none}.task-check{width:19px;height:19px;border:1.5px solid var(--border);border-radius:4px;cursor:pointer;flex-shrink:0;margin-top:2px;display:flex;align-items:center;justify-content:center;transition:all .15s}
.task-check.done{background:var(--green);border-color:var(--green)}.task-check.done::after{content:'✓';font-size:11px;font-weight:700;color:#000}
.task-text{flex:1}.task-text.done{text-decoration:line-through;color:var(--muted)}
.task-note{display:block;font-size:11px;color:var(--muted);font-family:'JetBrains Mono',monospace;margin-top:3px}
.task-priority{font-size:9px;padding:2px 7px;border-radius:10px;font-family:'JetBrains Mono',monospace;flex-shrink:0;margin-top:3px;font-weight:600}
.p-high{background:rgba(229,83,58,.2);color:var(--accent2)}.p-medium{background:rgba(245,166,35,.2);color:var(--accent)}.p-low{background:rgba(96,165,250,.2);color:var(--blue)}
.risk-box{background:rgba(229,83,58,.08);border:1px solid rgba(229,83,58,.25);border-radius:8px;padding:11px 13px;margin-top:12px;font-size:12px;color:#f87171;font-family:'JetBrains Mono',monospace;line-height:1.5}
.risk-box strong{display:block;margin-bottom:4px;color:var(--accent2);font-size:11px}
.note-box{background:rgba(96,165,250,.08);border:1px solid rgba(96,165,250,.2);border-radius:8px;padding:11px 13px;margin-top:10px;font-size:12px;color:var(--blue);font-family:'JetBrains Mono',monospace;line-height:1.5}
.export-row{display:flex;gap:10px;margin-top:18px;flex-wrap:wrap}.export-row .btn-sec{flex:1;min-width:120px}
.error-box{display:none;background:rgba(229,83,58,.1);border:1px solid rgba(229,83,58,.3);border-radius:10px;padding:18px;margin-top:14px;color:#f87171;font-size:13px;font-family:'JetBrains Mono',monospace}
.api-note{font-size:11px;color:var(--muted);font-family:'JetBrains Mono',monospace;text-align:center;margin-bottom:10px}
.api-key-row{display:flex;gap:8px;margin-bottom:16px}
#apiKey,#customUrl,#customModel,#ghUrl{flex:1;background:var(--bg3);border:1px solid var(--border);border-radius:8px;color:var(--text);font-family:'JetBrains Mono',monospace;font-size:12px;padding:10px 12px;outline:none}
#apiKey:focus,#customUrl:focus,#customModel:focus,#ghUrl:focus{border-color:var(--accent)}
#apiKey::placeholder,#customUrl::placeholder,#customModel::placeholder,#ghUrl::placeholder{color:var(--muted)}
#customSettings{margin-bottom:10px;display:none}
#modelSelect{width:100%;background:var(--bg3);border:1px solid var(--border);border-radius:8px;color:var(--text);font-family:'JetBrains Mono',monospace;font-size:12px;padding:10px 12px;outline:none}
#modelSelect:focus{border-color:var(--accent)}
.slider-row{display:flex;align-items:center;gap:12px;margin-top:8px}
#tempSlider{flex:1;-webkit-appearance:none;height:6px;background:var(--border);border-radius:3px;outline:none}
#tempSlider::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;background:var(--accent);border-radius:50%;cursor:pointer}
#tempVal{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--accent);min-width:36px}
.advanced-toggle{font-size:11px;color:var(--muted);cursor:pointer;margin-top:10px;display:inline-block}
.advanced-toggle:hover{color:var(--accent)}
#sysPromptBox{display:none;margin-top:10px}
.stream-box{display:none;background:var(--bg3);border:1px solid var(--border);border-radius:8px;padding:12px;margin-top:14px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--muted);max-height:200px;overflow:auto;white-space:pre-wrap}
.sidebar{position:fixed;top:0;left:0;width:260px;height:100%;background:var(--bg2);border-right:1px solid var(--border);transform:translateX(-100%);transition:transform .3s;z-index:100;padding:20px;overflow-y:auto}
.sidebar.open{transform:translateX(0)}.sidebar-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);display:none;z-index:99}
.sidebar-overlay.open{display:block}.sidebar h2{font-size:14px;margin-bottom:14px}.history-item{background:var(--bg3);border:1px solid var(--border);border-radius:8px;padding:10px;margin-bottom:8px;cursor:pointer;font-size:12px}
.history-item:hover{border-color:var(--accent)}.history-item .h-date{font-size:10px;color:var(--muted);margin-top:4px}
.history-empty{color:var(--muted);font-size:12px;text-align:center;padding:20px}
.history-btn{position:fixed;top:16px;right:16px;background:var(--bg3);border:1px solid var(--border);border-radius:8px;padding:8px 12px;color:var(--text);font-family:'JetBrains Mono',monospace;font-size:12px;cursor:pointer;z-index:50}
.history-btn:hover{border-color:var(--accent);color:var(--accent)}
.close-sidebar{position:absolute;top:10px;right:10px;background:none;border:none;color:var(--muted);font-size:18px;cursor:pointer}
.drag-hint{font-size:11px;color:var(--muted);margin-top:4px}
.gh-row{display:flex;gap:8px;margin-top:10px;align-items:center}
.gh-row .btn-sec{margin-top:0;flex:0 0 auto}
#ghUrl{flex:1}
.header-actions{display:flex;gap:8px;align-items:center;margin-bottom:18px}
.header-actions .btn-sec{margin-top:0;flex:0 0 auto;padding:8px 12px;font-size:11px}
.vault-row{display:flex;gap:8px;margin-top:12px;align-items:center}
.vault-row input{flex:1}
.vault-row .btn-sec{margin-top:0;flex:0 0 auto}
.vault-note{font-size:11px;color:var(--muted);margin-top:6px}
</style>
</head>
<body>
<button class="history-btn" onclick="toggleSidebar()">📜 Historia</button>
<div class="sidebar-overlay" id="overlay" onclick="toggleSidebar()"></div>
<div class="sidebar" id="sidebar">
<button class="close-sidebar" onclick="toggleSidebar()">×</button>
<h2>📜 Historia planów</h2>
<div id="historyList"></div>
</div>
<header>
<div class="logo">RF</div>
<div>
<h1>Refactor<span>Plan</span></h1>
<div class="subtitle">// AI-powered refactoring planner</div>
</div>
</header>
<div class="input-area">
<div class="input-label">// Dostawca AI</div>
<div class="options-row" id="vendorRow">
<div class="chip active" data-vendor="anthropic">🟡 Claude</div>
<div class="chip" data-vendor="openai">🟢 ChatGPT</div>
<div class="chip" data-vendor="gemini">🔵 Gemini</div>
<div class="chip" data-vendor="qwen">🟣 Qwen</div>
<div class="chip" data-vendor="deepseek">⚫ DeepSeek</div>
<div class="chip" data-vendor="glm">🟤 GLM</div>
<div class="chip" data-vendor="kimi">⚪ Kimi</div>
<div class="chip" data-vendor="groq">🟠 Groq</div>
<div class="chip" data-vendor="grok">⚡ Grok</div>
<div class="chip" data-vendor="claude-code">🛠️ Claude Code</div>
<div class="chip" data-vendor="custom">🔧 Custom</div>
</div>
<div class="input-label">// Model</div>
<select id="modelSelect"></select>
<div id="customSettings">
<div class="input-label">// Własny adres API</div>
<input type="text" id="customUrl" placeholder="https://api.example.com/v1/chat/completions" />
<div class="input-label" style="margin-top:12px;">// Nazwa modelu</div>
<input type="text" id="customModel" placeholder="np. gpt-4, llama3, itp." />
<div class="input-label" style="margin-top:12px;">// Typ autoryzacji</div>
<div class="options-row">
<div class="chip active" data-auth="bearer">Bearer Token</div>
<div class="chip" data-auth="xapikey">x-api-key</div>
<div class="chip" data-auth="query">?key=</div>
<div class="chip" data-auth="none">Brak</div>
</div>
</div>
<div class="input-label">// Klucz API</div>
<div class="api-key-row">
<input type="password" id="apiKey" placeholder="Wklej klucz API..." />
</div>
<div class="api-note">Klucz nie jest nigdzie zapisywany — działa tylko lokalnie w tej sesji</div>
<div class="input-label">// 🔐 Zaszyfrowany vault</div>
<div class="vault-row">
<input type="password" id="vaultPw" placeholder="Hasło główne do vaultu..." />
<button class="btn-sec" onclick="exportVault()">💾 Eksport</button>
<button class="btn-sec" onclick="document.getElementById('vaultImport').click()">📂 Import</button>
<input type="file" id="vaultImport" accept=".vault,.json" style="display:none" onchange="importVault(this)" />
</div>
<div class="vault-note">Vault szyfruje klucze AES-GCM + PBKDF2 (100k iteracji). Zapisz plik .vault i ładuj go zamiast wpisywać dane ręcznie.</div>
<div class="input-label">// Temperatura (kreatywność)</div>
<div class="slider-row">
<input type="range" id="tempSlider" min="0" max="1" step="0.1" value="0.7" />
<span id="tempVal">0.7</span>
</div>
<span class="advanced-toggle" onclick="document.getElementById('sysPromptBox').style.display=document.getElementById('sysPromptBox').style.display==='block'?'none':'block'">⚙️ Edytuj system prompt...</span>
<div id="sysPromptBox">
<textarea id="sysPrompt" rows="6" style="min-height:120px;margin-top:8px;font-family:'JetBrains Mono',monospace;font-size:12px;"></textarea>
</div>
<div class="input-label">// Szablony</div>
<div class="options-row" id="templateRow">
<div class="chip" data-template="0">Monolith → Microservices</div>
<div class="chip" data-template="1">Modernize jQuery → React/Vue</div>
<div class="chip" data-template="2">Normalize Database</div>
<div class="chip" data-template="3">Clean Architecture</div>
<div class="chip" data-template="4">Add TypeScript</div>
<div class="chip" data-template="5">Extract Shared Library</div>
<div class="chip" data-template="6">Introduce Testing Strategy</div>
<div class="chip" data-template="7">Security Hardening (OWASP)</div>
<div class="chip" data-template="8">Performance Optimization</div>
<div class="chip" data-template="9">Containerize (Docker/K8s)</div>
<div class="chip" data-template="10">Add CI/CD Pipeline</div>
<div class="chip" data-template="11">REST → GraphQL/gRPC</div>
<div class="chip" data-template="12">Event-Driven Architecture</div>
<div class="chip" data-template="13">Add i18n / Localization</div>
<div class="chip" data-template="14">Remove Dead Code & Debt</div>
<div class="chip" data-template="15">Refactor God Class / File</div>
<div class="chip" data-template="16">Add Observability</div>
<div class="chip" data-template="17">Migrate to Serverless</div>
<div class="chip" data-template="18">API Versioning Strategy</div>
<div class="chip" data-template="19">Upgrade Framework Version</div>
<div class="chip" data-template="20">Introduce ORM / Query Builder</div>
<div class="chip" data-template="21">CQRS / Event Sourcing</div>
<div class="chip" data-template="22">API Contracts & Validation</div>
<div class="chip" data-template="23">Optimize Build & Bundle</div>
</div>
<div class="input-label">// Import z GitHub</div>
<div class="gh-row">
<input type="text" id="ghUrl" placeholder="https://github.com/owner/repo" />
<button class="btn-sec" onclick="importGitHub()">📥 Import</button>
</div>
<div class="input-label">// Co chcesz zrefaktoryzować?</div>
<textarea id="desc" placeholder="Opisz co chcesz zrefaktoryzować — możesz też przeciągnąć pliki .py / .js / .sql tutaj..." rows="4"></textarea>
<div class="drag-hint">💡 Przeciągnij pliki kodu, aby wypełnić opis automatycznie</div>
<div class="options-row">
<div class="input-label" style="width:100%;margin-top:10px;margin-bottom:4px;">// Typ projektu</div>
<div class="chip active" data-type="auto">🤖 Auto</div>
<div class="chip" data-type="code">💻 Kod</div>
<div class="chip" data-type="db">🗄️ Baza danych</div>
<div class="chip" data-type="excel">📊 Excel/CRM</div>
<div class="chip" data-type="architecture">🏗️ Architektura</div>
<div class="chip" data-type="ui">🎨 UI/UX</div>
</div>
<div class="options-row">
<div class="input-label" style="width:100%;margin-bottom:4px;">// Horyzont czasowy</div>
<div class="chip" data-scope="quick">⚡ Szybkie (1–3 dni)</div>
<div class="chip active" data-scope="medium">📅 Średnie (1–2 tyg.)</div>
<div class="chip" data-scope="large">🗓️ Duże (1+ mies.)</div>
</div>
<button class="btn" id="genBtn" onclick="generatePlan()">⚡ Wygeneruj Plan Refaktoryzacji</button>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<div id="loadingText">Analizuję projekt i buduję plan...</div>
</div>
<div class="stream-box" id="streamBox"></div>
<div class="error-box" id="errorBox"></div>
<div class="plan-output" id="planOutput"></div>
<script>
const defaultSystem = `Jesteś ekspertem od refaktoryzacji oprogramowania i systemów. Generujesz szczegółowe, praktyczne plany refaktoryzacji.\n\nOdpowiedz WYŁĄCZNIE w formacie JSON (bez tekstu przed/po, bez backticks). Struktura:\n{\n "title": "Nazwa projektu",\n "summary": "1-2 zdania opisu",\n "estimated_time": "szacowany czas",\n "complexity": "Niska|Średnia|Wysoka",\n "total_tasks": liczba,\n "phases": [\n {\n "id": 1,\n "name": "Nazwa fazy",\n "duration": "czas",\n "priority": "Wysoki|Średni|Niski",\n "tasks": [\n { "text": "Zadanie", "priority": "high|medium|low", "note": "wskazówka lub null" }\n ],\n "risk": "opis ryzyka lub null",\n "tip": "wskazówka lub null"\n }\n ],\n "quick_wins": ["Szybka wygrana 1", "Szybka wygrana 2", "Szybka wygrana 3"]\n}\nGeneruj 3–5 faz, każda z 3–6 zadaniami. Bądź konkretny i praktyczny.`;
const claudeCodeSystem = `You are Claude Code — an agentic coding assistant focused on hands-on refactoring. Treat the user's description as a live codebase to refactor.\n\nRespond ONLY in JSON (no text before/after, no markdown fences). Structure:\n{\n "title": "Project name",\n "summary": "1-2 sentence overview",\n "estimated_time": "estimated duration",\n "complexity": "Low|Medium|High",\n "total_tasks": number,\n "phases": [\n {\n "id": 1,\n "name": "Phase name",\n "duration": "duration",\n "priority": "High|Medium|Low",\n "tasks": [\n { "text": "Task with concrete file/command examples", "priority": "high|medium|low", "note": "bash command, file path, or null" }\n ],\n "risk": "risk description or null",\n "tip": "tip or null"\n }\n ],\n "quick_wins": ["Quick win 1", "Quick win 2", "Quick win 3"]\n}\nGenerate 3–5 phases, 3–6 tasks each. Be extremely specific: include exact file names, shell commands, git operations, and tool recommendations where applicable.`;
const vendorModels = {
anthropic: [{v:'claude-sonnet-4',l:'Claude Sonnet 4.6 (latest)'},{v:'claude-opus-4',l:'Claude Opus 4.7 (latest)'},{v:'claude-haiku-4',l:'Claude Haiku 4.5'},{v:'claude-opus-4-6',l:'Claude Opus 4.6'},{v:'claude-sonnet-4-5',l:'Claude Sonnet 4.5'},{v:'claude-opus-3',l:'Claude Opus 3'},{v:'claude-sonnet-4-20250514',l:'Claude Sonnet 4 (pinned)'},{v:'claude-haiku-3-5-20250701',l:'Claude Haiku 3.5 (legacy)'}],
openai: [{v:'gpt-5.4',l:'GPT-5.4'},{v:'gpt-5.4-mini',l:'GPT-5.4 mini'},{v:'gpt-5.4-nano',l:'GPT-5.4 nano'},{v:'gpt-5-mini',l:'GPT-5 mini'},{v:'gpt-5.3-codex',l:'GPT-5.3 Codex'},{v:'gpt-4.1',l:'GPT-4.1'},{v:'gpt-4.1-mini',l:'GPT-4.1 mini'},{v:'gpt-4o-mini',l:'GPT-4o mini'}],
gemini: [{v:'gemini-3-flash',l:'Gemini 3 Flash'},{v:'gemini-3.1-pro',l:'Gemini 3.1 Pro (math & code)'},{v:'gemini-3-thinking',l:'Gemini 3 Thinking'},{v:'gemini-2.5-flash',l:'Gemini 2.5 Flash'},{v:'gemini-2.5-pro',l:'Gemini 2.5 Pro'}],
qwen: [{v:'qwen-max-latest',l:'Qwen Max (Qwen3.6)'},{v:'qwen-plus-latest',l:'Qwen Plus (Qwen3.6)'},{v:'qwen-flash-latest',l:'Qwen Flash (Qwen3.5)'}],
deepseek: [{v:'deepseek-v4-flash',l:'DeepSeek V4 Flash'},{v:'deepseek-v4-pro',l:'DeepSeek V4 Pro'}],
glm: [{v:'glm-5.1',l:'GLM-5.1'},{v:'glm-5-turbo',l:'GLM-5-Turbo'},{v:'glm-5v-turbo',l:'GLM-5V-Turbo'},{v:'glm-5',l:'GLM-5'}],
kimi: [{v:'kimi-k2.6',l:'Kimi K2.6'},{v:'kimi-k2.5',l:'Kimi K2.5'}],
groq: [{v:'meta-llama/llama-4-scout-17b-16e-instruct',l:'Llama 4 Scout'},{v:'llama-3.3-70b-versatile',l:'Llama 3.3 70B'},{v:'qwen/qwen3-32b',l:'Qwen 3 32B'},{v:'openai/gpt-oss-120b',l:'GPT-OSS 120B'},{v:'openai/gpt-oss-20b',l:'GPT-OSS 20B'}],
grok: [{v:'grok-4.3-beta',l:'Grok 4.3 (beta)'},{v:'grok-4.3-fast',l:'Grok 4.3 Fast'},{v:'grok-4.3-expert',l:'Grok 4.3 Expert'},{v:'grok-4.3-heavy',l:'Grok 4.3 Heavy'}],
'claude-code': [{v:'claude-sonnet-4',l:'Claude Code (Sonnet 4.6 agentic)'},{v:'claude-opus-4',l:'Claude Code Opus (Opus 4.7)'}],
custom: []
};
const templates = [
{type:'architecture', text:'We have a monolithic backend application that has grown to 150k+ lines of code with 50+ engineers contributing. Deployment cycles take 2-3 weeks because any small change requires full regression testing. We want to decompose this into bounded-context microservices using Domain-Driven Design principles. Each service should own its own data store, expose REST/gRPC APIs, and deploy independently via CI/CD. We need a migration roadmap that minimizes downtime, preserves data integrity, and includes service discovery, centralized logging, and a fallback strategy if decomposition fails.'},
{type:'ui', text:'Our frontend is a 8-year-old jQuery application with 300+ inline event handlers, global state scattered across DOM elements, and no component structure. New features take 3x longer than estimated due to spaghetti code and lack of testability. We want to migrate to React 18 with TypeScript, using a component-driven architecture, centralized state management (Zustand/Redux), and React Query for server state. The migration must be incremental — we cannot afford a big-bang rewrite. We need a strangler-fig pattern, feature flags for rollout, and a plan for retiring legacy jQuery modules progressively.'},
{type:'db', text:'The production PostgreSQL database has grown organically over 6 years without a DBA. Tables lack foreign keys, have redundant columns, inconsistent naming conventions, and N+1 query problems are rampant. Critical tables exceed 50M rows with missing indexes. We need a normalization strategy to reach 3NF, index recommendations based on query patterns, zero-downtime migration scripts using shadow tables or triggers, rollback procedures, and a monitoring plan to catch regressions.'},
{type:'architecture', text:'Our .NET/Java/Node backend has business logic leaking into controllers, direct database calls from UI layers, and circular dependencies between modules. Unit testing requires heavy mocking of framework internals. We want to adopt Clean Architecture / Hexagonal Architecture with clear layers: Entities, Use Cases, Interface Adapters, and Frameworks. The plan must include dependency direction enforcement (e.g. ArchUnit, dependency-cruiser), incremental refactoring sprints, and how to handle existing ORM annotations/framework coupling.'},
{type:'code', text:'We have a 200k-line JavaScript codebase (Node + React) with frequent runtime errors due to missing property checks, wrong function arguments, and implicit any types. Developers waste hours debugging issues that a type checker would catch. We want to migrate to strict TypeScript incrementally: first allowJS with loose config, then progressively enable strictNullChecks, noImplicitAny, and strictFunctionTypes. The plan must address third-party library types, gradual file migration order, CI enforcement gates, and developer onboarding.'},
{type:'code', text:'Three teams maintain separate repositories that share 40% identical utility code (validation, date formatting, API clients, auth helpers). Changes require copy-pasting across repos, leading to version drift and bugs. We want to extract a shared internal library with semantic versioning, automated publishing to a private registry, comprehensive documentation, and a migration plan for each consuming repository. Include monorepo vs multi-repo decision criteria and governance model.'},
{type:'code', text:'Current test coverage is 12%, mostly manual QA. Production bugs reach customers weekly. We need a multi-layer testing strategy: unit tests with TDD culture, integration tests against testcontainers/dedicated test DB, contract tests between services, and E2E tests for critical user journeys. The plan must include test data factories, mocking strategy for external APIs, CI pipeline integration with coverage gates, flaky test prevention, and team training schedule.'},
{type:'code', text:'Last penetration test revealed XSS, insecure direct object references, missing rate limiting, and hardcoded secrets in repositories. We store PII and need GDPR compliance. The plan must cover: OWASP Top 10 mitigation, secrets rotation to HashiCorp Vault/AWS Secrets Manager, input validation with allowlists, output encoding, CSP headers, dependency vulnerability scanning (Snyk/Dependabot), RBAC redesign, audit logging, and security champion program for developers.'},
{type:'code', text:'Application response time P95 is 4.2s during peak hours. Database CPU hits 95%, frontend bundle is 2.8MB uncompressed, and mobile users experience 6s+ TTI. We need a systematic performance engineering plan: query plan analysis, adding Redis caching layers, DB connection pooling, N+1 query elimination, image optimization, code splitting, lazy loading, WebWorker offloading, memory leak detection, and load testing benchmarks with clear SLO targets.'},
{type:'architecture', text:'Currently deploying via FTP/SSH to VMs with manual configuration drift. Environment parity between dev/staging/prod is non-existent. We want to containerize all services with Docker, standardize on Docker Compose for local development, and create Kubernetes manifests for production. Include: multi-stage builds for image size optimization, health checks and readiness probes, secret management via K8s secrets or external secret operators, horizontal pod autoscaling, and migration strategy from VMs to K8s without downtime.'},
{type:'architecture', text:'Deployments are manual, error-prone, and happen bi-monthly because of fear. We need a modern CI/CD pipeline: GitHub Actions / GitLab CI with parallel job execution, automated unit/integration/E2E tests on every PR, SAST/DAST security scanning, automated semantic versioning and changelog generation, blue-green or canary deployments to staging, production deployment with mandatory approval gates, automatic rollback on error metrics, and deployment frequency metrics tracking toward DORA elite performance.'},
{type:'architecture', text:'Mobile clients make 15+ REST calls per screen, leading to over-fetching and waterfall latency. We want to migrate to GraphQL (Apollo Federation) or gRPC with protobuf for internal services. The plan must include: schema design principles and versioning, resolver/service implementation patterns, client code generation, caching strategy at gateway level, backward compatibility during transition (support both REST and new protocol), Playground/documentation, and performance benchmarks comparing before/after.'},
{type:'architecture', text:'Current synchronous request chains create tight coupling and cascading failures. When the payment service is slow, the entire order flow blocks. We want to move to event-driven architecture using Kafka/RabbitMQ/Azure Event Hubs. Include: event schema registry (Avro/JSON Schema), idempotent consumer design, saga pattern for distributed transactions, outbox pattern for reliable event publishing, dead letter queues with retry policies, observability of event flows, and team alignment on eventual consistency semantics.'},
{type:'ui', text:'Product needs to support 12 languages across EU, APAC, and LATAM markets. Currently all strings are hardcoded in English. We need: extraction workflow for UI strings using i18next/react-intl, pluralization and ICU message format support, RTL layout adaptation, locale-aware date/number/currency formatting, translation management platform integration (Crowdin/Phrase), pseudolocalization for UI testing, and a governance process to prevent new hardcoded strings.'},
{type:'code', text:'Codebase contains 30% dead code from pivoted features, 200+ unused npm dependencies increasing install time and attack surface, and deprecated API endpoints still maintained for unknown consumers. We need: static analysis tools to identify unreachable code and unused exports (Knip, ts-prune, depcheck), incremental removal sprints with feature flags for safety, dependency audit and upgrade path, documentation of architectural decision records (ADRs), and a recurring tech-debt budget allocation.'},
{type:'code', text:'We have a 6000-line OrderService class handling validation, pricing, discounts, tax, inventory, notifications, and payments — violating SRP in every method. Changes require touching 40+ files due to ripple effects. We need to apply extract-class refactoring: identify cohesive responsibility groups, create focused collaborator classes with explicit interfaces, preserve exact behavioral contracts through comprehensive characterization tests, migrate callers incrementally via delegation pattern, and document the new module boundaries.'},
{type:'architecture', text:'Production incidents take 45+ minutes to diagnose because logs are unstructured text files across 20 servers, metrics are non-existent, and tracing requires manual correlation. We need: structured JSON logging with correlation IDs, OpenTelemetry tracing across service boundaries, Prometheus/Grafana metrics for golden signals (latency, traffic, errors, saturation), alertmanager routing with PagerDuty/Slack integration, distributed log aggregation (Loki/ELK), custom business metrics dashboards, and runbooks for common alerts.'},
{type:'architecture', text:'Current infrastructure runs on over-provisioned EC2 VMs with 15% average utilization. We want to migrate suitable workloads to AWS Lambda / Azure Functions / Cloud Run. The plan must cover: function decomposition criteria (what stays VM, what moves), cold start mitigation strategies (provisioned concurrency, SnapStart), stateless design requirements, event triggers and IAM least-privilege, local development emulation, cost modeling and monitoring, and a pilot service selection for proof of concept.'},
{type:'architecture', text:'Breaking API changes are communicated via Slack messages, leading to angry client developers. We need a formal API versioning and deprecation policy: semantic versioning for APIs, URL path versioning vs header-based negotiation, sunset timeline with automated notifications to registered consumers, backward compatibility layers using adapter pattern, deprecation headers in responses, migration guides with code examples, and a registry of all API consumers with contact information.'},
{type:'code', text:'We are stuck on Angular 12 / React 17 / Django 3.2 / Node 14 which are approaching end-of-life. Security patches are no longer available and new libraries refuse to install. We need a major version upgrade plan: breaking change inventory using codemods and migration guides, polyfill and shim strategy for removed APIs, incremental module-by-module upgrade path, extensive regression test suite as safety net, staged rollout (canary internal users first), rollback plan, and post-upgrade performance validation.'},
{type:'db', text:'Raw SQL is scattered across 300+ repository methods with string concatenation, no parameterization, and SQL injection risks. Schema changes require manually updating 50+ queries. We want to introduce a modern ORM (Prisma/TypeORM/EF Core/SQLAlchemy) or query builder. Include: migration strategy per query complexity tier, performance benchmarking to prevent N+1 regressions, raw SQL escape hatches for complex reports, team training curriculum, and CI checks to prevent new raw SQL additions.'},
{type:'architecture', text:'Our read and write workloads interfere with each other — reporting queries lock transactional tables. We need CQRS with separate read-optimized and write-optimized models. For audit compliance, we also need event sourcing: append-only event store, aggregate snapshots for performance, projection rebuild capability, consistency boundaries and saga coordination, event versioning and upcasting, and read model refresh strategies. Include team training on eventual consistency and debugging techniques.'},
{type:'code', text:'API consumers frequently report 500 errors caused by missing fields or wrong types. Frontend and backend teams blame each other. We want strict contracts: OpenAPI 3.1 specification as source of truth, automatic request/response validation middleware, generated TypeScript client SDKs, Pydantic/Zod runtime validation, schema-driven test generation, breaking change detection in CI, and a design-first API review process requiring spec approval before implementation.'},
{type:'code', text:'Frontend build takes 8 minutes, bundle size is 3.5MB, and Lighthouse performance score is 22. We need build pipeline optimization: webpack/Vite migration with HMR, tree shaking and sideEffects configuration, route-based code splitting, dynamic imports for heavy libraries, image optimization pipeline (WebP/AVIF), dependency audit to remove duplicates, build caching in CI, module federation for micro-frontends, and a performance budget gate that fails CI on regression.'}
];
const phaseColors = [
{bg:'rgba(245,166,35,0.2)',color:'#f5a623'},{bg:'rgba(96,165,250,0.2)',color:'#60a5fa'},
{bg:'rgba(167,139,250,0.2)',color:'#a78bfa'},{bg:'rgba(74,222,128,0.2)',color:'#4ade80'},
{bg:'rgba(229,83,58,0.2)',color:'#e8543a'}
];
let selectedType='auto',selectedScope='medium',selectedVendor='anthropic',selectedAuth='bearer',planData=null;
let currentChecks = {};
function populateModels() {
const sel = document.getElementById('modelSelect');
sel.innerHTML = '';
const models = vendorModels[selectedVendor] || [];
models.forEach(m => { const o=document.createElement('option');o.value=m.v;o.textContent=m.l;sel.appendChild(o); });
if (!models.length) { const o=document.createElement('option');o.value='';o.textContent='— ustaw w sekcji Custom —';sel.appendChild(o); }
}
function autoResize(t) { t.style.height='auto';t.style.height=t.scrollHeight+'px'; }
document.getElementById('desc').addEventListener('input',function(){autoResize(this);saveDraft();});
document.getElementById('desc').addEventListener('keydown',function(e){if(e.ctrlKey&&e.key==='Enter'){e.preventDefault();generatePlan();}});
document.getElementById('desc').addEventListener('dragover',function(e){e.preventDefault();this.classList.add('dragover');});
document.getElementById('desc').addEventListener('dragleave',function(){this.classList.remove('dragover');});
document.getElementById('desc').addEventListener('drop',function(e){
e.preventDefault();this.classList.remove('dragover');
const files=Array.from(e.dataTransfer.files).filter(f=>f.name.match(/\.(py|js|ts|jsx|tsx|sql|java|cpp|c|h|go|rs|php|rb)$/i));
if(!files.length)return;
Promise.all(files.map(f=>f.text())).then(contents=>{
const text=files.map((f,i)=>`=== ${f.name} (${Math.round(f.size/1024)}KB, ~${contents[i].split('\n').length} linii) ===\n${contents[i].substring(0,4000)}${contents[i].length>4000?'...':''}`).join('\n\n');
this.value=text;autoResize(this);saveDraft();
});
});
document.getElementById('tempSlider').addEventListener('input',function(){document.getElementById('tempVal').textContent=this.value;});
document.querySelectorAll('.chip[data-type]').forEach(c=>{
c.addEventListener('click',()=>{document.querySelectorAll('.chip[data-type]').forEach(x=>x.classList.remove('active'));c.classList.add('active');selectedType=c.dataset.type;saveDraft();});
});
document.querySelectorAll('.chip[data-scope]').forEach(c=>{
c.addEventListener('click',()=>{document.querySelectorAll('.chip[data-scope]').forEach(x=>x.classList.remove('active'));c.classList.add('active');selectedScope=c.dataset.scope;saveDraft();});
});
document.querySelectorAll('.chip[data-vendor]').forEach(c=>{
c.addEventListener('click',()=>{
document.querySelectorAll('.chip[data-vendor]').forEach(x=>x.classList.remove('active'));
c.classList.add('active');selectedVendor=c.dataset.vendor;
const ph={anthropic:'sk-ant-...',openai:'sk-...',gemini:'AIza...',qwen:'sk-...',deepseek:'sk-...',glm:'sk-...',kimi:'sk-...',groq:'gsk_...',grok:'xai-...','claude-code':'sk-ant-...'}[selectedVendor];
document.getElementById('apiKey').placeholder=ph?'Wklej klucz API ('+ph+')':'Wklej klucz API...';
document.getElementById('customSettings').style.display=selectedVendor==='custom'?'block':'none';
if(selectedVendor==='claude-code'){document.getElementById('sysPrompt').value=claudeCodeSystem;}
populateModels();saveDraft();
});
});
document.querySelectorAll('.chip[data-auth]').forEach(c=>{
c.addEventListener('click',()=>{document.querySelectorAll('.chip[data-auth]').forEach(x=>x.classList.remove('active'));c.classList.add('active');selectedAuth=c.dataset.auth;saveDraft();});
});
document.querySelectorAll('.chip[data-template]').forEach(c=>{
c.addEventListener('click',()=>{
const tmpl=templates[parseInt(c.dataset.template)];
document.getElementById('desc').value=tmpl.text;autoResize(document.getElementById('desc'));
selectedType=tmpl.type;
document.querySelectorAll('.chip[data-type]').forEach(x=>x.classList.remove('active'));
document.querySelector(`.chip[data-type="${tmpl.type}"]`)?.classList.add('active');
saveDraft();
});
});
document.getElementById('modelSelect').addEventListener('change',saveDraft);
document.getElementById('apiKey').addEventListener('input',saveDraft);
document.getElementById('customUrl').addEventListener('input',saveDraft);
document.getElementById('customModel').addEventListener('input',saveDraft);
document.getElementById('sysPrompt').addEventListener('input',saveDraft);
function supportsStream(v){return v!=='gemini';}
function toggleSidebar(){document.getElementById('sidebar').classList.toggle('open');document.getElementById('overlay').classList.toggle('open');renderHistory();}
function saveDraft(){
const d={desc:document.getElementById('desc').value,type:selectedType,scope:selectedScope,vendor:selectedVendor,auth:selectedAuth,model:document.getElementById('modelSelect').value,apiKey:document.getElementById('apiKey').value,temp:document.getElementById('tempSlider').value,sysPrompt:document.getElementById('sysPrompt').value,customUrl:document.getElementById('customUrl').value,customModel:document.getElementById('customModel').value};
localStorage.setItem('rf_draft',JSON.stringify(d));
}
function loadDraft(){
try{const d=JSON.parse(localStorage.getItem('rf_draft')||'{}');if(!d.vendor)return;
document.getElementById('desc').value=d.desc||'';autoResize(document.getElementById('desc'));
document.getElementById('apiKey').value=d.apiKey||'';document.getElementById('tempSlider').value=d.temp||0.7;document.getElementById('tempVal').textContent=d.temp||0.7;
document.getElementById('sysPrompt').value=d.sysPrompt||defaultSystem;document.getElementById('customUrl').value=d.customUrl||'';document.getElementById('customModel').value=d.customModel||'';
selectedType=d.type||'auto';selectedScope=d.scope||'medium';selectedVendor=(d.vendor==='gemma'?'groq':d.vendor)||'anthropic';selectedAuth=d.auth||'bearer';
document.querySelectorAll('.chip[data-type],.chip[data-scope],.chip[data-vendor],.chip[data-auth]').forEach(x=>x.classList.remove('active'));
document.querySelector(`.chip[data-type="${selectedType}"]`)?.classList.add('active');
document.querySelector(`.chip[data-scope="${selectedScope}"]`)?.classList.add('active');
document.querySelector(`.chip[data-vendor="${selectedVendor}"]`)?.classList.add('active');
document.querySelector(`.chip[data-auth="${selectedAuth}"]`)?.classList.add('active');
document.getElementById('customSettings').style.display=selectedVendor==='custom'?'block':'none';
populateModels();document.getElementById('modelSelect').value=d.model||vendorModels[selectedVendor]?.[0]?.v||'';
}catch(e){document.getElementById('sysPrompt').value=defaultSystem;}
}
function savePlanToHistory(p){
const plans=JSON.parse(localStorage.getItem('rf_plans')||'[]');
const entry={id:Date.now(),title:p.title,date:new Date().toLocaleString('pl-PL'),data:p,checks:currentChecks};
plans.unshift(entry);if(plans.length>30)plans.pop();
localStorage.setItem('rf_plans',JSON.stringify(plans));renderHistory();
}
function renderHistory(){
const list=document.getElementById('historyList');
const plans=JSON.parse(localStorage.getItem('rf_plans')||'[]');
if(!plans.length){list.innerHTML='<div class="history-empty">Brak zapisanych planów</div>';return;}
list.innerHTML=plans.map(pl=>`<div class="history-item" onclick="loadHistory(${pl.id})"><div>${escapeHtml(pl.title)}</div><div class="h-date">${pl.date}</div></div>`).join('');
}
function loadHistory(id){
const plans=JSON.parse(localStorage.getItem('rf_plans')||'[]');
const entry=plans.find(p=>p.id===id);if(!entry)return;
planData=entry.data;currentChecks=entry.checks||{};renderPlan(planData);toggleSidebar();
}
function escapeHtml(t){return t.replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));}
async function deriveKey(password,salt){
const enc=new TextEncoder();
const keyMaterial=await crypto.subtle.importKey('raw',enc.encode(password),'PBKDF2',false,['deriveKey']);
return crypto.subtle.deriveKey({name:'PBKDF2',salt,iterations:100000,hash:'SHA-256'},keyMaterial,{name:'AES-GCM',length:256},false,['encrypt','decrypt']);
}
function arrToB64(arr){return btoa(String.fromCharCode(...arr));}
function b64ToArr(str){return new Uint8Array([...atob(str)].map(c=>c.charCodeAt(0)));}
async function exportVault(){
const pw=document.getElementById('vaultPw').value.trim();
if(!pw){alert('Wpisz hasło główne.');return;}
const payload={apiKey:document.getElementById('apiKey').value,customUrl:document.getElementById('customUrl').value,customModel:document.getElementById('customModel').value,vendor:selectedVendor,auth:selectedAuth,model:document.getElementById('modelSelect').value};
const enc=new TextEncoder();const salt=crypto.getRandomValues(new Uint8Array(16));const iv=crypto.getRandomValues(new Uint8Array(12));
const key=await deriveKey(pw,salt);const ct=await crypto.subtle.encrypt({name:'AES-GCM',iv},key,enc.encode(JSON.stringify(payload)));
const blob=new Blob([JSON.stringify({salt:arrToB64(salt),iv:arrToB64(iv),data:arrToB64(new Uint8Array(ct))},null,2)],{type:'application/json'});
const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='refactor-plan.vault';a.click();
}
async function importVault(input){
const file=input.files[0];if(!file)return;
const pw=document.getElementById('vaultPw').value.trim();
if(!pw){alert('Wpisz hasło główne przed importem.');input.value='';return;}
try{
const j=JSON.parse(await file.text());
const key=await deriveKey(pw,b64ToArr(j.salt));
const dec=await crypto.subtle.decrypt({name:'AES-GCM',iv:b64ToArr(j.iv)},key,b64ToArr(j.data).buffer);
const data=JSON.parse(new TextDecoder().decode(dec));
document.getElementById('apiKey').value=data.apiKey||'';
document.getElementById('customUrl').value=data.customUrl||'';
document.getElementById('customModel').value=data.customModel||'';
if(data.vendor){selectedVendor=data.vendor;document.querySelectorAll('.chip[data-vendor]').forEach(x=>x.classList.remove('active'));document.querySelector(`.chip[data-vendor="${data.vendor}"]`)?.classList.add('active');populateModels();}
if(data.auth){selectedAuth=data.auth;document.querySelectorAll('.chip[data-auth]').forEach(x=>x.classList.remove('active'));document.querySelector(`.chip[data-auth="${data.auth}"]`)?.classList.add('active');}
document.getElementById('modelSelect').value=data.model||'';
document.getElementById('customSettings').style.display=selectedVendor==='custom'?'block':'none';
saveDraft();alert('Vault zaimportowany pomyślnie.');
}catch(e){alert('Błąd odszyfrowania: nieprawidłowe hasło lub uszkodzony plik.');}
input.value='';
}
async function importGitHub(){
const url=document.getElementById('ghUrl').value.trim();
const m=url.match(/github\.com\/([^\/]+)\/([^\/]+)/);if(!m){alert('Nieprawidłowy URL GitHub');return;}
const [_,owner,repo]=m;
const branches=['main','master'];
for(const br of branches){
try{
const r=await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${br}/README.md`);
if(!r.ok)continue;
let txt=await r.text();txt=txt.substring(0,8000);if(txt.length===8000)txt+='...';
document.getElementById('desc').value=`Projekt: ${owner}/${repo}\n\n${txt}`;autoResize(document.getElementById('desc'));saveDraft();return;
}catch(e){}
}
alert('Nie udało się pobrać README. Upewnij się, że repozytorium jest publiczne.');
}
async function generatePlan(isRetry=false){
const apiKey=document.getElementById('apiKey').value.trim();
const desc=document.getElementById('desc').value.trim();
const sysPrompt=document.getElementById('sysPrompt').value.trim()||defaultSystem;
const temp=parseFloat(document.getElementById('tempSlider').value);
if(!apiKey&&selectedVendor!=='custom'){alert('Wpisz klucz API.');return;}
if(selectedVendor==='custom'){if(!document.getElementById('customUrl').value.trim()){alert('Wpisz adres API.');return;}if(!document.getElementById('customModel').value.trim()){alert('Wpisz nazwę modelu.');return;}}
if(!desc){document.getElementById('desc').focus();return;}
document.getElementById('genBtn').disabled=true;
document.getElementById('loading').style.display='block';
document.getElementById('planOutput').style.display='none';
document.getElementById('errorBox').style.display='none';
document.getElementById('streamBox').style.display='none';
document.getElementById('streamBox').textContent='';
const scopeLabel={quick:'1–3 dni',medium:'1–2 tygodnie',large:'1+ miesiąc'}[selectedScope];
const typeLabel={auto:'automatyczny',code:'kod',db:'baza danych',excel:'Excel/CRM',architecture:'architektura',ui:'UI/UX'}[selectedType];
const userPrompt=`Plan refaktoryzacji dla: "${desc}"\nTyp: ${typeLabel}\nHoryzont: ${scopeLabel}`;
const useStream=supportsStream(selectedVendor);
let url,headers={'Content-Type':'application/json'},body,extractText;
const model=document.getElementById('modelSelect').value;
if(selectedVendor==='anthropic'){
url='https://api.anthropic.com/v1/messages';
headers={...headers,'x-api-key':apiKey,'anthropic-version':'2023-06-01','anthropic-dangerous-direct-browser-access':'true'};
body=JSON.stringify({model:model||'claude-sonnet-4',max_tokens:1800,temperature:temp,system:sysPrompt,messages:[{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.content?.map(b=>b.text||'').join('');
}else if(selectedVendor==='openai'){
url='https://api.openai.com/v1/chat/completions';
headers={...headers,'Authorization':'Bearer '+apiKey};
body=JSON.stringify({model:model||'gpt-5.4-mini',max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||'';
}else if(selectedVendor==='gemini'){
url='https://generativelanguage.googleapis.com/v1beta/models/'+(model||'gemini-3-flash')+':generateContent?key='+encodeURIComponent(apiKey);
headers={'Content-Type':'application/json'};
body=JSON.stringify({systemInstruction:{parts:[{text:sysPrompt}]},contents:[{role:'user',parts:[{text:userPrompt}]}],generationConfig:{temperature:temp}});
extractText=(data)=>data.candidates?.[0]?.content?.parts?.map(p=>p.text||'').join('')||'';
}else if(selectedVendor==='qwen'){
url='https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions';
headers={...headers,'Authorization':'Bearer '+apiKey};
body=JSON.stringify({model:model||'qwen-flash-latest',max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||'';
}else if(selectedVendor==='deepseek'){
url='https://api.deepseek.com/v1/chat/completions';
headers={...headers,'Authorization':'Bearer '+apiKey};
body=JSON.stringify({model:model||'deepseek-v4-flash',max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||'';
}else if(selectedVendor==='glm'){
url='https://open.bigmodel.cn/api/paas/v4/chat/completions';
headers={...headers,'Authorization':'Bearer '+apiKey};
body=JSON.stringify({model:model||'glm-5.1',max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||'';
}else if(selectedVendor==='kimi'){
url='https://api.moonshot.cn/v1/chat/completions';
headers={...headers,'Authorization':'Bearer '+apiKey};
body=JSON.stringify({model:model||'kimi-k2.6',max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||'';
}else if(selectedVendor==='groq'){
url='https://api.groq.com/openai/v1/chat/completions';
headers={...headers,'Authorization':'Bearer '+apiKey};
body=JSON.stringify({model:model||'meta-llama/llama-4-scout-17b-16e-instruct',max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||'';
}else if(selectedVendor==='grok'){
url='https://api.x.ai/v1/chat/completions';
headers={...headers,'Authorization':'Bearer '+apiKey};
body=JSON.stringify({model:model||'grok-4.3-beta',max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||'';
}else if(selectedVendor==='claude-code'){
url='https://api.anthropic.com/v1/messages';
headers={...headers,'x-api-key':apiKey,'anthropic-version':'2023-06-01','anthropic-dangerous-direct-browser-access':'true'};
body=JSON.stringify({model:model||'claude-sonnet-4',max_tokens:1800,temperature:temp,system:sysPrompt,messages:[{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.content?.map(b=>b.text||'').join('');
}else if(selectedVendor==='custom'){
const customUrl=document.getElementById('customUrl').value.trim();
const customModel=document.getElementById('customModel').value.trim();
url=customUrl;
if(selectedAuth==='bearer') headers={...headers,'Authorization':'Bearer '+apiKey};
else if(selectedAuth==='xapikey') headers={...headers,'x-api-key':apiKey};
else if(selectedAuth==='query'){const sep=url.includes('?')?'&':'?';url=url+sep+'key='+encodeURIComponent(apiKey);}
body=JSON.stringify({model:customModel,max_tokens:1800,temperature:temp,messages:[{role:'system',content:sysPrompt},{role:'user',content:userPrompt}],stream:useStream});
extractText=(data)=>data.choices?.[0]?.message?.content||data.content?.map(b=>b.text||'').join('')||data.candidates?.[0]?.content?.parts?.map(p=>p.text||'').join('')||'';
}else{throw new Error('Nieznany dostawca: '+selectedVendor);}
try{
let raw='';
if(useStream){
document.getElementById('streamBox').style.display='block';
const res=await fetch(url,{method:'POST',headers,body});
if(!res.ok){const err=await res.json().catch(()=>null);throw new Error(err?.error?.message||'HTTP '+res.status);}
const reader=res.body.getReader();const decoder=new TextDecoder();let buffer='';
while(true){
const {done,value}=await reader.read();if(done)break;
buffer+=decoder.decode(value,{stream:true});
const lines=buffer.split('\n');buffer=lines.pop()||'';
for(const line of lines){
if(!line.startsWith('data: '))continue;
const data=line.slice(6);if(data==='[DONE]')continue;
try{
const j=JSON.parse(data);
let chunk='';
if(j.choices?.[0]?.delta?.content)chunk=j.choices[0].delta.content;
else if(j.delta?.text)chunk=j.delta.text;
else if(j.content_block?.text)chunk=j.content_block.text;
raw+=chunk;document.getElementById('streamBox').textContent=raw;
}catch(e){}
}
}
}else{
const res=await fetch(url,{method:'POST',headers,body});
const data=await res.json();
if(data.error)throw new Error(data.error.message||JSON.stringify(data.error));
raw=extractText(data);
}
const clean=raw.replace(/```json|```/g,'').trim();
planData=JSON.parse(clean);currentChecks={};
renderPlan(planData);savePlanToHistory(planData);
}catch(e){
document.getElementById('errorBox').style.display='block';
document.getElementById('errorBox').textContent='⚠️ Błąd: '+e.message;
}finally{
document.getElementById('loading').style.display='none';
document.getElementById('genBtn').disabled=false;
}
}
function renderPlan(p){
const out=document.getElementById('planOutput');
const complexityColor={'Niska':'#4ade80','Średnia':'#f5a623','Wysoka':'#e8543a'}[p.complexity]||'#f5a623';
let html=`<div class="plan-header">
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:12px;">
<div style="flex:1;"><div class="plan-title">📋 ${escapeHtml(p.title)}</div><div class="plan-meta">${escapeHtml(p.summary)}</div></div>
<div class="header-actions">
<button class="btn-sec" onclick="toggleAllPhases(true)">Rozwiń</button>
<button class="btn-sec" onclick="toggleAllPhases(false)">Zwiń</button>
</div>
</div>
<div class="stats-row">
<div class="stat"><div class="stat-val">${p.phases.length}</div><div class="stat-label">FAZY</div></div>
<div class="stat"><div class="stat-val">${p.total_tasks}</div><div class="stat-label">ZADANIA</div></div>
<div class="stat"><div class="stat-val" style="font-size:15px;color:${complexityColor}">${p.complexity}</div><div class="stat-label">ZŁOŻONOŚĆ</div></div>
</div>
</div>`;
p.phases.forEach((ph,i)=>{
const col=phaseColors[i%phaseColors.length];
const badgeClass={'Wysoki':'p-high','Średni':'p-medium','Niski':'p-low'}[ph.priority]||'p-medium';
const tasks=ph.tasks.map((t,ti)=>{
const ck=currentChecks[`${i}_${ti}`]?'done':'';
return `<li class="task"><div class="task-check ${ck}" onclick="toggleTask(this,${i},${ti})"></div><span class="task-text ${ck}">${escapeHtml(t.text)}${t.note?`<span class="task-note">💡 ${escapeHtml(t.note)}</span>`:''}</span><span class="task-priority ${t.priority==='high'?'p-high':t.priority==='medium'?'p-medium':'p-low'}">${t.priority==='high'?'PILNE':t.priority==='medium'?'WAŻNE':'OPCJA'}</span></li>`;
}).join('');
html+=`<div class="phase open" data-idx="${i}" style="animation-delay:${i*0.1}s">
<div class="phase-header" onclick="togglePhase(this.parentElement)">
<div class="phase-num" style="background:${col.bg};color:${col.color}">${i+1}</div>
<div class="phase-title">${escapeHtml(ph.name)}</div>
<span class="phase-badge ${badgeClass}">${ph.priority}</span>
<span class="phase-dur">${ph.duration}</span>
<span class="phase-toggle">▾</span>
</div>
<div class="phase-body">
<div class="progress-bar"><div class="progress-fill"></div></div>
<ul class="task-list">${tasks}</ul>
${ph.risk?`<div class="risk-box"><strong>⚠️ RYZYKO</strong>${escapeHtml(ph.risk)}</div>`:''}
${ph.tip?`<div class="note-box">💡 ${escapeHtml(ph.tip)}</div>`:''}
</div>
</div>`;
});
if(p.quick_wins?.length){
const wins=p.quick_wins.map((w,wi)=>{
const ck=currentChecks[`qw_${wi}`]?'done':'';
return `<li class="task"><div class="task-check ${ck}" onclick="toggleTask(this,'qw',${wi})"></div><span class="task-text ${ck}">⚡ ${escapeHtml(w)}</span></li>`;
}).join('');
html+=`<div class="phase open" style="animation-delay:${p.phases.length*0.1}s">
<div class="phase-header" onclick="togglePhase(this.parentElement)">
<div class="phase-num" style="background:rgba(74,222,128,0.2);color:#4ade80">⚡</div>
<div class="phase-title">Szybkie Wygrane</div>
<span class="phase-badge p-high">ZACZNIJ TU</span>
<span class="phase-toggle">▾</span>
</div>
<div class="phase-body"><ul class="task-list" style="margin-top:4px">${wins}</ul></div>
</div>`;
}
html+=`<div class="export-row">
<button class="btn-sec" onclick="copyMarkdown()">📋 Kopiuj MD</button>
<button class="btn-sec" onclick="downloadMd()">💾 Pobierz .md</button>
<button class="btn-sec" onclick="downloadJson()">💾 Pobierz .json</button>
<button class="btn-sec" onclick="copyGitHub()">🐙 GitHub MD</button>
<button class="btn-sec" onclick="generatePlan(true)">🔄 Generuj od nowa</button>
<button class="btn-sec" onclick="resetUI()">🆕 Nowy plan</button>
</div>`;
out.innerHTML=html;out.style.display='block';
updateProgressBars();
}
function togglePhase(el){el.classList.toggle('open');updateProgressBars();}
function toggleAllPhases(open){document.querySelectorAll('.phase').forEach(p=>p.classList.toggle('open',open));updateProgressBars();}
function toggleTask(el,pi,ti){
el.classList.toggle('done');el.nextElementSibling.classList.toggle('done');
currentChecks[`${pi}_${ti}`]=el.classList.contains('done');
saveChecks();updateProgressBars();
}
function saveChecks(){
if(!planData)return;
const plans=JSON.parse(localStorage.getItem('rf_plans')||'[]');
const entry=plans.find(p=>p.title===planData.title);
if(entry){entry.checks={...currentChecks};localStorage.setItem('rf_plans',JSON.stringify(plans));}
}
function updateProgressBars(){
document.querySelectorAll('.phase').forEach(ph=>{
const all=ph.querySelectorAll('.task-check').length;
const done=ph.querySelectorAll('.task-check.done').length;
const bar=ph.querySelector('.progress-fill');
if(bar)bar.style.width=(done/all*100)+'%';
});
}
function resetUI(){
document.getElementById('planOutput').style.display='none';
document.getElementById('desc').value='';autoResize(document.getElementById('desc'));
planData=null;currentChecks={};saveDraft();
}
function buildMarkdown(p,github=false){
let md=`# ${p.title}\n\n${p.summary}\n\n**Czas:** ${p.estimated_time} | **Złożoność:** ${p.complexity}\n\n`;
p.phases.forEach((ph,i)=>{
md+=`## Faza ${i+1}: ${ph.name} (${ph.duration})\n\n`;
ph.tasks.forEach(t=>{const box=github?'- [ ]':'-';md+=`${box} ${t.text}\n`;if(t.note)md+=` - _${t.note}_\n`;});md+='\n';
if(ph.risk)md+=`> ⚠️ Ryzyko: ${ph.risk}\n\n`;
});
if(p.quick_wins?.length){md+=`## ⚡ Szybkie Wygrane\n\n`;p.quick_wins.forEach(w=>md+=`${github?'- [ ]':'-'} ${w}\n`);}
return md;
}
function copyMarkdown(){if(!planData)return;navigator.clipboard.writeText(buildMarkdown(planData));flashBtn('📋 Kopiuj MD','✅ Skopiowano!');}
function copyGitHub(){if(!planData)return;navigator.clipboard.writeText(buildMarkdown(planData,true));flashBtn('🐙 GitHub MD','✅ Skopiowano!');}
function flashBtn(orig,text){const btns=[...document.querySelectorAll('.btn-sec')];const btn=btns.find(b=>b.textContent===orig);if(btn){const o=btn.textContent;btn.textContent=text;setTimeout(()=>btn.textContent=o,2000);}}
function downloadMd(){if(!planData)return;const blob=new Blob([buildMarkdown(planData)],{type:'text/markdown'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='refactor-plan.md';a.click();}
function downloadJson(){if(!planData)return;const blob=new Blob([JSON.stringify(planData,null,2)],{type:'application/json'});const a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='refactor-plan.json';a.click();}
populateModels();loadDraft();renderHistory();
</script>
</body>
</html>