vicem-cli 0.0.0-stage → 1.1.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/index.html ADDED
@@ -0,0 +1,1410 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline';" />
7
+ <title>Vicem — Zero-Dependency AI Agent Context Baton &amp; Diet Suite</title>
8
+ <style>
9
+ :root {
10
+ --bg-base: #09090b;
11
+ --bg-surface: #111114;
12
+ --bg-elevated: #18181c;
13
+ --bg-glass: rgba(17, 17, 20, 0.84);
14
+ --border-hairline: #232329;
15
+ --border-strong: #34343d;
16
+ --text-primary: #fafafa;
17
+ --text-secondary: #a1a1aa;
18
+ --text-muted: #71717a;
19
+ --accent-primary: #f4f4f5;
20
+ --accent-green: #e4e4e7;
21
+ --accent-amber: #a1a1aa;
22
+ --accent-red: #71717a;
23
+ --accent-cyan: #d4d4d8;
24
+ --font-sans: -apple-system, BlinkMacSystemFont, "Inter", "SF Pro Display", "Segoe UI", Roboto, sans-serif;
25
+ --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Monaco, Consolas, monospace;
26
+ }
27
+
28
+ * {
29
+ box-sizing: border-box;
30
+ margin: 0;
31
+ padding: 0;
32
+ }
33
+
34
+ html {
35
+ scroll-behavior: smooth;
36
+ }
37
+
38
+ body {
39
+ background: var(--bg-base);
40
+ color: var(--text-primary);
41
+ font-family: var(--font-sans);
42
+ line-height: 1.6;
43
+ -webkit-font-smoothing: antialiased;
44
+ overflow-x: hidden;
45
+ }
46
+
47
+ /* Top Navigation */
48
+ header {
49
+ position: sticky;
50
+ top: 0;
51
+ z-index: 50;
52
+ background: var(--bg-glass);
53
+ backdrop-filter: blur(12px);
54
+ -webkit-backdrop-filter: blur(12px);
55
+ border-bottom: 1px solid var(--border-hairline);
56
+ }
57
+
58
+ .nav-container {
59
+ max-width: 1160px;
60
+ margin: 0 auto;
61
+ height: 60px;
62
+ padding: 0 24px;
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: space-between;
66
+ }
67
+
68
+ .brand {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 12px;
72
+ font-weight: 700;
73
+ font-size: 1.05rem;
74
+ letter-spacing: -0.02em;
75
+ color: var(--text-primary);
76
+ text-decoration: none;
77
+ }
78
+
79
+ .brand-mark {
80
+ width: 26px;
81
+ height: 26px;
82
+ display: inline-flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ }
86
+
87
+ .nav-links {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: 28px;
91
+ font-size: 0.86rem;
92
+ color: var(--text-secondary);
93
+ }
94
+
95
+ .nav-links a {
96
+ color: var(--text-secondary);
97
+ text-decoration: none;
98
+ transition: color 0.15s;
99
+ }
100
+
101
+ .nav-links a:hover {
102
+ color: var(--text-primary);
103
+ }
104
+
105
+ .badge-zero-net {
106
+ font-family: var(--font-mono);
107
+ font-size: 0.72rem;
108
+ color: var(--text-secondary);
109
+ background: var(--bg-surface);
110
+ border: 1px solid var(--border-hairline);
111
+ padding: 4px 10px;
112
+ border-radius: 6px;
113
+ }
114
+
115
+ /* Layout Container */
116
+ .container {
117
+ max-width: 1160px;
118
+ margin: 0 auto;
119
+ padding: 0 24px;
120
+ }
121
+
122
+ /* Hero Showcase Section */
123
+ .hero {
124
+ padding: 88px 0 64px;
125
+ border-bottom: 1px solid var(--border-hairline);
126
+ }
127
+
128
+ .eyebrow {
129
+ display: inline-flex;
130
+ align-items: center;
131
+ gap: 10px;
132
+ font-family: var(--font-mono);
133
+ font-size: 0.75rem;
134
+ letter-spacing: 0.06em;
135
+ text-transform: uppercase;
136
+ color: var(--text-secondary);
137
+ background: var(--bg-surface);
138
+ border: 1px solid var(--border-hairline);
139
+ padding: 5px 12px;
140
+ border-radius: 999px;
141
+ margin-bottom: 24px;
142
+ }
143
+
144
+ .eyebrow-dot {
145
+ width: 6px;
146
+ height: 6px;
147
+ border-radius: 50%;
148
+ background: var(--accent-green);
149
+ }
150
+
151
+ .hero h1 {
152
+ font-size: clamp(2.4rem, 5vw, 4.1rem);
153
+ font-weight: 800;
154
+ letter-spacing: -0.04em;
155
+ line-height: 1.08;
156
+ max-width: 860px;
157
+ margin-bottom: 22px;
158
+ }
159
+
160
+ .hero-lead {
161
+ font-size: clamp(1.05rem, 1.8vw, 1.25rem);
162
+ color: var(--text-secondary);
163
+ max-width: 760px;
164
+ margin-bottom: 36px;
165
+ font-weight: 400;
166
+ }
167
+
168
+ .hero-grid {
169
+ display: grid;
170
+ grid-template-columns: 0.92fr 1.28fr;
171
+ gap: 28px;
172
+ align-items: stretch;
173
+ margin-top: 20px;
174
+ }
175
+
176
+ @media (max-width: 920px) {
177
+ .hero-grid {
178
+ grid-template-columns: 1fr;
179
+ }
180
+ .nav-links {
181
+ display: none;
182
+ }
183
+ }
184
+
185
+ .install-card {
186
+ background: var(--bg-surface);
187
+ border: 1px solid var(--border-hairline);
188
+ border-radius: 10px;
189
+ padding: 22px;
190
+ min-width: 0;
191
+ }
192
+
193
+ .install-title {
194
+ font-size: 0.78rem;
195
+ font-family: var(--font-mono);
196
+ text-transform: uppercase;
197
+ letter-spacing: 0.06em;
198
+ color: var(--text-muted);
199
+ margin-bottom: 12px;
200
+ }
201
+
202
+ .cmd-box {
203
+ background: var(--bg-base);
204
+ border: 1px solid var(--border-hairline);
205
+ border-radius: 7px;
206
+ padding: 13px 14px;
207
+ font-family: var(--font-mono);
208
+ font-size: 0.82rem;
209
+ color: var(--text-primary);
210
+ overflow-x: auto;
211
+ margin-bottom: 16px;
212
+ white-space: nowrap;
213
+ }
214
+
215
+ .os-matrix {
216
+ display: grid;
217
+ grid-template-columns: repeat(3, 1fr);
218
+ gap: 8px;
219
+ margin-top: 12px;
220
+ }
221
+
222
+ .os-pill {
223
+ background: var(--bg-elevated);
224
+ border: 1px solid var(--border-hairline);
225
+ border-radius: 6px;
226
+ padding: 10px 12px;
227
+ font-size: 0.78rem;
228
+ }
229
+
230
+ .os-pill strong {
231
+ display: block;
232
+ color: var(--text-primary);
233
+ margin-bottom: 2px;
234
+ }
235
+
236
+ .os-pill span {
237
+ font-family: var(--font-mono);
238
+ font-size: 0.72rem;
239
+ color: var(--text-secondary);
240
+ }
241
+
242
+ /* Terminal Showcase Window */
243
+ .terminal-window {
244
+ background: #050507;
245
+ border: 1px solid var(--border-strong);
246
+ border-radius: 10px;
247
+ overflow: hidden;
248
+ min-width: 0;
249
+ box-shadow: 0 24px 48px rgba(0, 0, 0, 0.55);
250
+ }
251
+
252
+ .terminal-bar {
253
+ background: var(--bg-surface);
254
+ border-bottom: 1px solid var(--border-hairline);
255
+ height: 36px;
256
+ padding: 0 14px;
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: space-between;
260
+ font-family: var(--font-mono);
261
+ font-size: 0.74rem;
262
+ color: var(--text-muted);
263
+ }
264
+
265
+ .terminal-dots {
266
+ display: flex;
267
+ gap: 6px;
268
+ }
269
+
270
+ .terminal-dots span {
271
+ width: 10px;
272
+ height: 10px;
273
+ border-radius: 50%;
274
+ background: var(--border-strong);
275
+ }
276
+
277
+ .terminal-body {
278
+ padding: 18px;
279
+ font-family: var(--font-mono);
280
+ font-size: 0.71rem;
281
+ line-height: 1.65;
282
+ color: #d4d4d8;
283
+ overflow-x: auto;
284
+ white-space: pre;
285
+ }
286
+
287
+ .t-muted { color: #71717a; }
288
+ .t-red { color: #a1a1aa; }
289
+ .t-green { color: #fafafa; font-weight: 600; }
290
+ .t-cyan { color: #a1a1aa; }
291
+ .t-amber { color: #d4d4d8; }
292
+ .t-bold { color: #fafafa; font-weight: 600; }
293
+
294
+ /* Architectural Spec Grid */
295
+ .section {
296
+ padding: 72px 0;
297
+ border-bottom: 1px solid var(--border-hairline);
298
+ }
299
+
300
+ .section-header {
301
+ margin-bottom: 40px;
302
+ }
303
+
304
+ .section-tag {
305
+ font-family: var(--font-mono);
306
+ font-size: 0.74rem;
307
+ text-transform: uppercase;
308
+ letter-spacing: 0.08em;
309
+ color: var(--text-muted);
310
+ margin-bottom: 8px;
311
+ }
312
+
313
+ .section-title {
314
+ font-size: 1.85rem;
315
+ font-weight: 700;
316
+ letter-spacing: -0.03em;
317
+ }
318
+
319
+ .spec-grid {
320
+ display: grid;
321
+ grid-template-columns: repeat(3, 1fr);
322
+ border-top: 1px solid var(--border-hairline);
323
+ border-left: 1px solid var(--border-hairline);
324
+ }
325
+
326
+ @media (max-width: 860px) {
327
+ .spec-grid {
328
+ grid-template-columns: 1fr;
329
+ }
330
+ }
331
+
332
+ .spec-cell {
333
+ padding: 28px;
334
+ border-right: 1px solid var(--border-hairline);
335
+ border-bottom: 1px solid var(--border-hairline);
336
+ background: var(--bg-base);
337
+ }
338
+
339
+ .spec-num {
340
+ font-family: var(--font-mono);
341
+ font-size: 0.75rem;
342
+ color: var(--text-muted);
343
+ margin-bottom: 12px;
344
+ }
345
+
346
+ .spec-cell h3 {
347
+ font-size: 1.05rem;
348
+ font-weight: 600;
349
+ margin-bottom: 10px;
350
+ letter-spacing: -0.01em;
351
+ }
352
+
353
+ .spec-cell p {
354
+ font-size: 0.88rem;
355
+ color: var(--text-secondary);
356
+ line-height: 1.6;
357
+ }
358
+
359
+ /* Comparison Table */
360
+ .comp-table-wrap {
361
+ overflow-x: auto;
362
+ border: 1px solid var(--border-hairline);
363
+ border-radius: 10px;
364
+ }
365
+
366
+ table {
367
+ width: 100%;
368
+ border-collapse: collapse;
369
+ text-align: left;
370
+ font-size: 0.86rem;
371
+ }
372
+
373
+ th, td {
374
+ padding: 14px 18px;
375
+ border-bottom: 1px solid var(--border-hairline);
376
+ }
377
+
378
+ th {
379
+ background: var(--bg-surface);
380
+ color: var(--text-secondary);
381
+ font-weight: 600;
382
+ font-family: var(--font-mono);
383
+ font-size: 0.76rem;
384
+ text-transform: uppercase;
385
+ letter-spacing: 0.04em;
386
+ }
387
+
388
+ td {
389
+ color: var(--text-secondary);
390
+ }
391
+
392
+ td strong {
393
+ color: var(--text-primary);
394
+ }
395
+
396
+ tr:last-child td {
397
+ border-bottom: none;
398
+ }
399
+
400
+ /* Interactive Sandbox Workbench (Zone A + Zone B + Zone C) */
401
+ .workbench-grid {
402
+ display: grid;
403
+ grid-template-columns: 360px 1fr;
404
+ gap: 16px;
405
+ min-height: 520px;
406
+ }
407
+
408
+ @media (max-width: 920px) {
409
+ .workbench-grid {
410
+ grid-template-columns: 1fr;
411
+ }
412
+ }
413
+
414
+ .zone-card {
415
+ background: var(--bg-surface);
416
+ border: 1px solid var(--border-hairline);
417
+ border-radius: 10px;
418
+ display: flex;
419
+ flex-direction: column;
420
+ overflow: hidden;
421
+ }
422
+
423
+ .zone-header {
424
+ padding: 12px 16px;
425
+ border-bottom: 1px solid var(--border-hairline);
426
+ font-family: var(--font-mono);
427
+ font-size: 0.76rem;
428
+ font-weight: 600;
429
+ text-transform: uppercase;
430
+ letter-spacing: 0.05em;
431
+ color: var(--text-secondary);
432
+ display: flex;
433
+ justify-content: space-between;
434
+ align-items: center;
435
+ background: var(--bg-elevated);
436
+ }
437
+
438
+ .drop-area {
439
+ margin: 14px;
440
+ padding: 16px;
441
+ border: 1px dashed var(--border-strong);
442
+ border-radius: 8px;
443
+ text-align: center;
444
+ cursor: pointer;
445
+ background: var(--bg-base);
446
+ transition: border-color 0.15s;
447
+ }
448
+
449
+ .drop-area:hover, .drop-area.drag-over {
450
+ border-color: var(--text-primary);
451
+ }
452
+
453
+ .input-group {
454
+ padding: 0 14px 12px;
455
+ display: flex;
456
+ flex-direction: column;
457
+ gap: 6px;
458
+ flex: 1;
459
+ }
460
+
461
+ .input-group label {
462
+ font-size: 0.76rem;
463
+ color: var(--text-secondary);
464
+ font-weight: 500;
465
+ }
466
+
467
+ input[type="text"], textarea, select {
468
+ background: var(--bg-base);
469
+ color: var(--text-primary);
470
+ border: 1px solid var(--border-hairline);
471
+ border-radius: 6px;
472
+ padding: 9px 11px;
473
+ font-family: var(--font-mono);
474
+ font-size: 0.78rem;
475
+ outline: none;
476
+ }
477
+
478
+ input[type="text"]:focus, textarea:focus, select:focus {
479
+ border-color: var(--border-strong);
480
+ }
481
+
482
+ textarea {
483
+ flex: 1;
484
+ min-height: 180px;
485
+ resize: vertical;
486
+ }
487
+
488
+ .diet-controls {
489
+ padding: 14px 16px;
490
+ background: var(--bg-base);
491
+ border-bottom: 1px solid var(--border-hairline);
492
+ display: grid;
493
+ grid-template-columns: 1fr 1fr;
494
+ gap: 20px;
495
+ align-items: center;
496
+ }
497
+
498
+ @media (max-width: 680px) {
499
+ .diet-controls {
500
+ grid-template-columns: 1fr;
501
+ }
502
+ }
503
+
504
+ .slider-box {
505
+ display: flex;
506
+ flex-direction: column;
507
+ gap: 6px;
508
+ }
509
+
510
+ .slider-labels {
511
+ display: flex;
512
+ justify-content: space-between;
513
+ font-size: 0.72rem;
514
+ font-family: var(--font-mono);
515
+ color: var(--text-muted);
516
+ }
517
+
518
+ .gauge-box {
519
+ display: flex;
520
+ flex-direction: column;
521
+ gap: 8px;
522
+ }
523
+
524
+ .gauge-bar-track {
525
+ height: 8px;
526
+ background: var(--bg-elevated);
527
+ border-radius: 999px;
528
+ overflow: hidden;
529
+ border: 1px solid var(--border-hairline);
530
+ }
531
+
532
+ .gauge-bar-fill {
533
+ height: 100%;
534
+ width: 35%;
535
+ background: var(--accent-green);
536
+ transition: width 0.2s, background 0.2s;
537
+ }
538
+
539
+ .tabs-bar {
540
+ display: flex;
541
+ border-bottom: 1px solid var(--border-hairline);
542
+ background: var(--bg-surface);
543
+ padding: 0 12px;
544
+ gap: 4px;
545
+ }
546
+
547
+ .tab-btn {
548
+ background: transparent;
549
+ color: var(--text-secondary);
550
+ border: none;
551
+ border-bottom: 2px solid transparent;
552
+ padding: 10px 14px;
553
+ font-size: 0.8rem;
554
+ font-weight: 500;
555
+ cursor: pointer;
556
+ }
557
+
558
+ .tab-btn[aria-selected="true"] {
559
+ color: var(--text-primary);
560
+ border-bottom-color: var(--text-primary);
561
+ }
562
+
563
+ .inspector-pane {
564
+ flex: 1;
565
+ padding: 16px;
566
+ overflow-y: auto;
567
+ font-family: var(--font-mono);
568
+ font-size: 0.79rem;
569
+ line-height: 1.55;
570
+ white-space: pre-wrap;
571
+ background: #050507;
572
+ min-height: 320px;
573
+ }
574
+
575
+ .workbench-footer {
576
+ margin-top: 16px;
577
+ background: var(--bg-surface);
578
+ border: 1px solid var(--border-hairline);
579
+ border-radius: 10px;
580
+ padding: 14px 18px;
581
+ display: flex;
582
+ align-items: center;
583
+ justify-content: space-between;
584
+ flex-wrap: wrap;
585
+ gap: 12px;
586
+ }
587
+
588
+ .action-buttons {
589
+ display: flex;
590
+ gap: 10px;
591
+ align-items: center;
592
+ flex-wrap: wrap;
593
+ }
594
+
595
+ .btn {
596
+ background: var(--bg-elevated);
597
+ color: var(--text-primary);
598
+ border: 1px solid var(--border-strong);
599
+ border-radius: 6px;
600
+ padding: 8px 14px;
601
+ font-size: 0.82rem;
602
+ font-weight: 500;
603
+ cursor: pointer;
604
+ transition: opacity 0.15s;
605
+ }
606
+
607
+ .btn-primary {
608
+ background: var(--text-primary);
609
+ border-color: var(--text-primary);
610
+ color: var(--bg-base);
611
+ font-weight: 600;
612
+ }
613
+
614
+ .btn:hover {
615
+ opacity: 0.9;
616
+ }
617
+
618
+ .status-pill {
619
+ font-size: 0.78rem;
620
+ color: var(--text-secondary);
621
+ font-family: var(--font-mono);
622
+ }
623
+
624
+ .site-footer {
625
+ padding: 40px 0;
626
+ font-size: 0.8rem;
627
+ color: var(--text-muted);
628
+ font-family: var(--font-mono);
629
+ display: flex;
630
+ justify-content: space-between;
631
+ flex-wrap: wrap;
632
+ gap: 12px;
633
+ }
634
+ </style>
635
+ </head>
636
+ <body>
637
+ <!-- Top Sticky Navigation -->
638
+ <header role="banner">
639
+ <div class="nav-container">
640
+ <a href="#top" class="brand">
641
+ <span class="brand-mark" aria-hidden="true">
642
+ <svg viewBox="0 0 64 64" width="24" height="24" fill="none" stroke="#fafafa" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round">
643
+ <path d="M10 14 L32 54 L54 12 L41 15 L29 40 C26 44 23 42 20 36 L10 14 Z"/>
644
+ <path d="M16 14 L26 35"/>
645
+ <path d="M22 15 C27 16 30 20 33 28"/>
646
+ </svg>
647
+ </span>
648
+ <span>Vicem</span>
649
+ </a>
650
+ <nav class="nav-links" aria-label="Primary Navigation">
651
+ <a href="#architecture">Architecture</a>
652
+ <a href="#impact">Pipeline Impact</a>
653
+ <a href="#install">OS Releases</a>
654
+ <a href="#sandbox">Interactive Sandbox</a>
655
+ </nav>
656
+ <div class="badge-zero-net" aria-label="Security status: 100% Local Zero-Network">
657
+ dependencies: 0 · CSP connect-src 'none'
658
+ </div>
659
+ </div>
660
+ </header>
661
+
662
+ <div class="container" id="top">
663
+ <!-- Hero Showcase Section -->
664
+ <section class="hero">
665
+ <div class="eyebrow">
666
+ <span class="eyebrow-dot"></span>
667
+ <span>Vicem v1.0.0 · Zero-Dependency Context Baton &amp; Diet Suite</span>
668
+ </div>
669
+ <h1>Diet the Context.<br />Pass the Baton in 3 Seconds.</h1>
670
+ <p class="hero-lead">
671
+ When you hit Claude Code's 5-hour quota limit mid-refactor or suffer context rot at 150k tokens, Vicem harvests your live session, git diff, and compressed code skeletons into a bookended baton and relays work straight into OpenAI Codex, Gemini CLI, Cursor, Antigravity IDE, or Web AI.
672
+ </p>
673
+
674
+ <div class="hero-grid">
675
+ <div class="install-card" id="install">
676
+ <div class="install-title">1-Click Setup (macOS · Windows · Linux)</div>
677
+ <div class="cmd-box">git clone github.com/Rakkunn/vicem.git &amp;&amp; cd vicem &amp;&amp; ./vicem.mjs setup</div>
678
+ <p style="font-size: 0.84rem; color: var(--text-secondary); margin-bottom: 14px;">
679
+ Or download the standalone OS release bundle from GitHub Releases (<code>github.com/Rakkunn/vicem/releases</code>) and double-click the launcher:
680
+ </p>
681
+ <div class="os-matrix">
682
+ <div class="os-pill">
683
+ <strong>macOS</strong>
684
+ <span>setup.command</span>
685
+ </div>
686
+ <div class="os-pill">
687
+ <strong>Windows</strong>
688
+ <span>setup.bat</span>
689
+ </div>
690
+ <div class="os-pill">
691
+ <strong>Linux / WSL</strong>
692
+ <span>./vicem.mjs setup</span>
693
+ </div>
694
+ </div>
695
+ </div>
696
+
697
+ <div class="terminal-window" aria-label="Terminal execution preview">
698
+ <div class="terminal-bar">
699
+ <div class="terminal-dots"><span></span><span></span><span></span></div>
700
+ <span>zsh — my-api (feat/rate-limit)</span>
701
+ <span>80x24</span>
702
+ </div>
703
+ <div class="terminal-body"><span class="t-red">Error: You've hit your usage limit for Claude Pro (5-hour window).</span>
704
+ <span class="t-muted">Session: 42 turns · 184,200 tokens in context · 3 dirty files.</span>
705
+
706
+ <span class="t-green">➜</span> <span class="t-cyan">my-api</span> <span class="t-bold">$ vicem codex</span>
707
+ <span class="t-cyan">╭─ VICEM CONTEXT BATON &amp; DIET RECEIPT ───────────────────────╮</span>
708
+ <span class="t-cyan">│</span> Source : <span class="t-bold">Claude Code</span> (sess-9f3a2c · 42 turns harvested) <span class="t-cyan">│</span>
709
+ <span class="t-cyan">│</span> Diet : <span class="t-red">184,200 tok ($2.76)</span> ──► <span class="t-green">14,800 tok [-91.9%]</span> <span class="t-cyan">│</span>
710
+ <span class="t-cyan">│</span> Skeleton : 18 files compressed (31 bodies in ledger) <span class="t-cyan">│</span>
711
+ <span class="t-cyan">│</span> Security : <span class="t-amber">2 secrets redacted</span> (OPENAI_KEY, AWS_KEY) <span class="t-cyan">│</span>
712
+ <span class="t-cyan">│</span> Target : <span class="t-bold">OpenAI Codex CLI</span> (interactive TTY handoff) <span class="t-cyan">│</span>
713
+ <span class="t-cyan">╰────────────────────────────────────────────────────────────╯</span>
714
+ <span class="t-green">[OK] [Vicem]</span> Wrote <span class="t-bold">.vicem/BATON.md</span> (182ms). Launching <span class="t-bold">codex</span>...</div>
715
+ </div>
716
+ </div>
717
+ </section>
718
+
719
+ <!-- Architecture Grid -->
720
+ <section class="section" id="architecture">
721
+ <div class="section-header">
722
+ <div class="section-tag">System Architecture</div>
723
+ <h2 class="section-title">Engineered with Zero Third-Party Dependencies</h2>
724
+ </div>
725
+
726
+ <div class="spec-grid">
727
+ <div class="spec-cell">
728
+ <div class="spec-num">01 / HARVEST &amp; RELAY</div>
729
+ <h3>Project-Scoped Session Harvesting</h3>
730
+ <p>
731
+ Automatically detects and parses active transcripts strictly scoped to the current working directory across Claude Code, Codex, Gemini CLI, Cursor (SQLite WAL backup), Antigravity IDE, Aider, OpenCode, and Goose.
732
+ </p>
733
+ </div>
734
+ <div class="spec-cell">
735
+ <div class="spec-num">02 / 4-STAGE CONTEXT DIET</div>
736
+ <h3>Deterministic Skeletonization &amp; MCP Rehydration</h3>
737
+ <p>
738
+ Preserves TypeScript/Rust/Go/Python type contracts and signatures 100% intact while collapsing function bodies &gt;= 12 lines into <code>{ /* ... */ }</code> and indexing full bodies in <code>.vicem/ledger.json</code> for on-demand <code>vicem_rehydrate</code> calls.
739
+ </p>
740
+ </div>
741
+ <div class="spec-cell">
742
+ <div class="spec-num">03 / SAFETY &amp; MINIMAL TCB</div>
743
+ <h3>3-Tier Secret Redaction &amp; Write Guards</h3>
744
+ <p>
745
+ Masks provider keys, PEM blocks, and high-entropy assignments locally before any handoff. Built-in <code>SkeletonWriteGuard</code> and <code>SecretCommitGuard</code> prevent placeholder corruption in source files.
746
+ </p>
747
+ </div>
748
+ </div>
749
+ </section>
750
+
751
+ <!-- Pipeline Impact & Contribution Section -->
752
+ <section class="section" id="impact">
753
+ <div class="section-header">
754
+ <div class="section-tag">Measured Pipeline Impact</div>
755
+ <h2 class="section-title">Stage-by-Stage Context Reduction &amp; Continuity Contribution</h2>
756
+ </div>
757
+
758
+ <div class="comp-table-wrap">
759
+ <table>
760
+ <thead>
761
+ <tr>
762
+ <th>Pipeline Stage</th>
763
+ <th>Raw Session State (Before Vicem)</th>
764
+ <th>After Vicem Baton &amp; Diet</th>
765
+ <th>Measured Contribution</th>
766
+ </tr>
767
+ </thead>
768
+ <tbody>
769
+ <tr>
770
+ <td><strong>1. Session Continuity Relay</strong></td>
771
+ <td>40+ turns of raw JSONL / SQLite history locked in a single agent</td>
772
+ <td>Structured <code>Goal</code>, <code>Done</code>, <code>Immediate NEXT</code> &amp; <code>git diff</code> in <code>.vicem/BATON.md</code></td>
773
+ <td><strong>&lt; 3s handoff across 15+ CLI, IDE &amp; Web agents</strong></td>
774
+ </tr>
775
+ <tr>
776
+ <td><strong>2. Code Skeletonization</strong></td>
777
+ <td>Full source files with long implementation bodies (~120,000 tok)</td>
778
+ <td>Types &amp; signatures 100% intact; bodies &gt;= 12 lines indexed in <code>.vicem/ledger.json</code></td>
779
+ <td><strong>-80% to -90% code tokens (100% lossless via MCP <code>vicem_rehydrate</code>)</strong></td>
780
+ </tr>
781
+ <tr>
782
+ <td><strong>3. Test &amp; Error Log Slimming</strong></td>
783
+ <td>Verbose ANSI spinners &amp; <code>node_modules</code> stack traces (~45,000 tok)</td>
784
+ <td>Root failure message + top 5 project stack frames (~1,200 tok)</td>
785
+ <td><strong>-95% log noise while preserving exact root cause</strong></td>
786
+ </tr>
787
+ <tr>
788
+ <td><strong>4. Data Sampling &amp; Noise Gate</strong></td>
789
+ <td>Lockfiles, build bundles, and multi-megabyte JSON / CSV / SQL dumps</td>
790
+ <td>Artifacts filtered; data files reduced to inferred schema + 2 sample rows</td>
791
+ <td><strong>-98% data bloat &amp; zero context window overflow</strong></td>
792
+ </tr>
793
+ <tr>
794
+ <td><strong>5. Security &amp; Write Integrity</strong></td>
795
+ <td>Plaintext credentials in logs &amp; risk of placeholder overwrites</td>
796
+ <td>3-Tier secret redaction + <code>SkeletonWriteGuard</code> &amp; <code>SecretCommitGuard</code></td>
797
+ <td><strong>0 leaked secrets &amp; 0 external dependencies (<code>dependencies: 0</code>)</strong></td>
798
+ </tr>
799
+ </tbody>
800
+ </table>
801
+ </div>
802
+ </section>
803
+
804
+ <!-- Interactive Sandbox Workbench (Preserves 100% of Zone A, Zone B, Zone C functional contracts) -->
805
+ <section class="section" id="sandbox" style="border-bottom: none;">
806
+ <div class="section-header">
807
+ <div class="section-tag">Interactive Browser Workbench</div>
808
+ <h2 class="section-title">Test the 4-Stage Context Diet &amp; Secret Redactor Locally</h2>
809
+ <p style="font-size: 0.9rem; color: var(--text-secondary); margin-top: 6px;">
810
+ Runs 100% in your browser memory under strict Content-Security-Policy (<code>default-src 'none'</code>). Drop a project folder or edit code below to inspect real-time skeletonization and secret masking.
811
+ </p>
812
+ </div>
813
+
814
+ <main class="workbench-grid">
815
+ <!-- ZONE A: SOURCE DROP & INPUT -->
816
+ <section class="zone-card" role="region" aria-label="Zone A: Source Drop and Project Input">
817
+ <div class="zone-header">
818
+ <span>Zone A · Source Input</span>
819
+ <span id="fileCountBadge">1 file</span>
820
+ </div>
821
+
822
+ <div class="drop-area" id="dropZone" tabindex="0" role="button" aria-label="Drop project folder or files here, or click to select files">
823
+ <div style="font-weight: 600; font-size: 0.84rem; margin-bottom: 4px;">Drop Project Folder or Source Files</div>
824
+ <div style="font-size: 0.74rem; color: var(--text-muted);">Supports TS / JS / Python / Go / Rust, logs, and chat exports</div>
825
+ <input type="file" id="filePicker" multiple webkitdirectory style="display: none;" />
826
+ </div>
827
+
828
+ <div class="input-group" style="flex: 0;">
829
+ <label for="goalInput">Task Goal</label>
830
+ <input type="text" id="goalInput" value="Fix JWT expiration bug and verify rate-limit middleware" />
831
+ </div>
832
+
833
+ <div class="input-group" style="flex: 0;">
834
+ <label for="nextInput">Immediate NEXT Step</label>
835
+ <input type="text" id="nextInput" value="Update verifySessionToken in src/auth.ts and run npm test" />
836
+ </div>
837
+
838
+ <div class="input-group">
839
+ <label for="rawSourceInput">Raw Code / Error Log / Diff Input</label>
840
+ <textarea id="rawSourceInput" aria-label="Raw source code or error log input">export interface AuthConfig {
841
+ issuer: string;
842
+ ttlSeconds: number;
843
+ }
844
+
845
+ export async function verifySessionToken(token: string, cfg: AuthConfig): Promise<boolean> {
846
+ const apiKey = "sk-proj-1234567890abcdefghijklmnop";
847
+ const step1 = token.trim();
848
+ const step2 = step1.split('.');
849
+ if (step2.length !== 3) {
850
+ return false;
851
+ }
852
+ const payload = step2[1];
853
+ const isValid = payload.length > 0 && cfg.ttlSeconds > 0;
854
+ const auditTag = `${cfg.issuer}:${isValid}`;
855
+ return Boolean(auditTag && apiKey);
856
+ }</textarea>
857
+ </div>
858
+ </section>
859
+
860
+ <!-- ZONE B: LIVE DIET SLIDER & 3-TAB INSPECTOR -->
861
+ <section class="zone-card" role="region" aria-label="Zone B: Live Diet Slider and 3-Tab Baton Inspector">
862
+ <div class="zone-header">
863
+ <span>Zone B · Live Diet Slider &amp; Inspector</span>
864
+ <span id="secretShieldSummary" aria-live="polite">1 secret masked</span>
865
+ </div>
866
+
867
+ <div class="diet-controls">
868
+ <div class="slider-box">
869
+ <label for="dietSlider" style="font-size: 0.78rem; font-weight: 600;">
870
+ Diet Level: <span id="dietLevelName">L2 · Balanced Skeletons (Recommended)</span>
871
+ </label>
872
+ <input type="range" id="dietSlider" min="1" max="3" step="1" value="2" aria-label="Context Diet Compression Level (1 to 3)" />
873
+ <div class="slider-labels">
874
+ <span>L1 Light (Logs)</span>
875
+ <span>L2 Balanced (Skeletons)</span>
876
+ <span>L3 Max (Diff Only)</span>
877
+ </div>
878
+ </div>
879
+
880
+ <div class="gauge-box" aria-live="polite">
881
+ <div style="display: flex; justify-content: space-between; font-size: 0.78rem; font-weight: 600; font-family: var(--font-mono);">
882
+ <span id="tokenStatsText">Diet: 120 → 78 tokens (-35%)</span>
883
+ <span id="budgetStatusText" style="color: var(--accent-green);">SAFE (&lt; 4,000 tok)</span>
884
+ </div>
885
+ <div class="gauge-bar-track">
886
+ <div class="gauge-bar-fill" id="gaugeFill"></div>
887
+ </div>
888
+ </div>
889
+ </div>
890
+
891
+ <div class="tabs-bar" role="tablist" aria-label="Baton Inspector Views">
892
+ <button class="tab-btn" role="tab" id="tabPreview" aria-selected="true" data-tab="preview">Baton Preview (BATON.md)</button>
893
+ <button class="tab-btn" role="tab" id="tabSkeleton" aria-selected="false" data-tab="skeleton">Skeleton Diff</button>
894
+ <button class="tab-btn" role="tab" id="tabSecrets" aria-selected="false" data-tab="secrets">Masked Secrets (<span id="secretTabCount">1</span>)</button>
895
+ </div>
896
+
897
+ <pre class="inspector-pane" id="inspectorOutput" role="tabpanel" aria-live="polite"></pre>
898
+ </section>
899
+ </main>
900
+
901
+ <!-- ZONE C: 1-CLICK ACTION BAR -->
902
+ <footer class="workbench-footer" role="region" aria-label="Zone C: 1-Click Action Bar">
903
+ <div class="status-pill" id="actionReceipt" aria-live="polite">
904
+ Zone C · Ready · Press Cmd+Enter (or Ctrl+Enter) to Copy Baton
905
+ </div>
906
+ <div class="action-buttons">
907
+ <label for="targetSelect" style="font-size: 0.78rem; color: var(--text-secondary);">Target Agent:</label>
908
+ <select id="targetSelect" aria-label="Select target AI agent">
909
+ <option value="codex">OpenAI Codex CLI</option>
910
+ <option value="claude">Claude Code</option>
911
+ <option value="antigravity">Antigravity IDE</option>
912
+ <option value="cursor">Cursor</option>
913
+ <option value="web">ChatGPT / Claude Web</option>
914
+ </select>
915
+ <button class="btn btn-primary" id="copyBatonBtn" title="Shortcut: Cmd+Enter / Ctrl+Enter">
916
+ Copy Baton (Cmd+Enter)
917
+ </button>
918
+ <button class="btn" id="downloadBatonBtn">
919
+ Download BATON.md
920
+ </button>
921
+ </div>
922
+ </footer>
923
+ </section>
924
+
925
+ <div class="site-footer">
926
+ <span>Vicem v1.0.0 · MIT License · Zero External Dependencies</span>
927
+ <span>Repository: github.com/Rakkunn/vicem</span>
928
+ </div>
929
+ </div>
930
+
931
+ <script>
932
+ (() => {
933
+ // 1. Browser-side Calibrated Token Estimator (100% identical to vicem.mjs C-05 formula — BUG-07 Fix)
934
+ function estimateTokens(text) {
935
+ if (!text || typeof text !== 'string' || text.length === 0) return 0;
936
+ const koreanMatches = text.match(/[\uac00-\ud7a3\u1100-\u11ff\u3130-\u318f\u3000-\u9fff\uf900-\ufaff]/g);
937
+ const punctMatches = text.match(/[{}[\],:;"'()=><_./\\-]/g);
938
+ const lineMatches = text.match(/\n/g);
939
+ const korean = koreanMatches ? koreanMatches.length : 0;
940
+ const punct = punctMatches ? punctMatches.length : 0;
941
+ const lines = lineMatches ? lineMatches.length : 0;
942
+ const other = Math.max(0, text.length - korean - punct);
943
+ const tokens = (other * 0.245) + (punct * 0.45) + (korean * 0.76) + (lines * 0.15);
944
+ return Math.max(1, Math.round(tokens));
945
+ }
946
+
947
+ // 2. Deterministic Secret Redactor (Isomorphic with vicem.mjs C-06 + Shannon Entropy — Fix 11-B)
948
+ const SECRET_RULES = [
949
+ { provider: 'AWS_KEY', regex: /\b(?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\b/g },
950
+ { provider: 'GITHUB', regex: /\b(?:ghp_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9_]{82}|gho_[a-zA-Z0-9]{36}|ghs_[a-zA-Z0-9]{36})\b/g },
951
+ { provider: 'ANTHROPIC', regex: /\bsk-ant-[a-zA-Z0-9\-_]{20,}\b/g },
952
+ { provider: 'OPENAI', regex: /\bsk-(?:proj-|svcacct-)?[a-zA-Z0-9\-_]{20,}\b/g },
953
+ { provider: 'GOOGLE', regex: /\bAIza[0-9A-Za-z\-_]{35}\b/g },
954
+ { provider: 'SLACK', regex: /\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,32}\b/g },
955
+ { provider: 'STRIPE', regex: /\b(?:sk|rk)_live_[0-9a-zA-Z]{24,99}\b/g },
956
+ { provider: 'JWT', regex: /\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
957
+ { provider: 'PRIVATE_KEY', regex: /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----/g }
958
+ ];
959
+
960
+ function shortHash4(str) {
961
+ let h = 2166136261;
962
+ for (let i = 0; i < str.length; i++) {
963
+ h ^= str.charCodeAt(i);
964
+ h = Math.imul(h, 16777619);
965
+ }
966
+ return (h >>> 0).toString(16).padStart(4, '0').slice(0, 4);
967
+ }
968
+
969
+ function shannonEntropy(str) {
970
+ if (!str || str.length === 0) return 0;
971
+ const freq = Object.create(null);
972
+ for (let i = 0; i < str.length; i++) {
973
+ const ch = str[i];
974
+ freq[ch] = (freq[ch] || 0) + 1;
975
+ }
976
+ let entropy = 0;
977
+ const len = str.length;
978
+ for (const k in freq) {
979
+ const p = freq[k] / len;
980
+ entropy -= p * Math.log2(p);
981
+ }
982
+ return entropy;
983
+ }
984
+
985
+ function redactSecrets(text) {
986
+ let sanitized = String(text || '');
987
+ const findings = [];
988
+ for (const rule of SECRET_RULES) {
989
+ sanitized = sanitized.replace(rule.regex, (m) => {
990
+ const token = `‹REDACTED:${rule.provider}:${shortHash4(m)}›`;
991
+ findings.push({ provider: rule.provider, token, preview: m.slice(0, 7) + '...' });
992
+ return token;
993
+ });
994
+ }
995
+ const assignRegex = /\b((?:api[_-]?key|secret|token|password|passwd|auth[_-]?token|access[_-]?key|client[_-]?secret)[a-zA-Z0-9_]*)\s*([:=])\s*(['"`])([a-zA-Z0-9+/=_\-.]{16,})\3/gi;
996
+ sanitized = sanitized.replace(assignRegex, (full, keyName, sep, quote, val) => {
997
+ if (val.includes('REDACTED:')) return full;
998
+ if (shannonEntropy(val) >= 3.5) {
999
+ const token = `‹REDACTED:HIGH_ENTROPY:${shortHash4(val)}›`;
1000
+ findings.push({ provider: 'HIGH_ENTROPY', token, preview: val.slice(0, 6) + '...' });
1001
+ return `${keyName}${sep}${quote}${token}${quote}`;
1002
+ }
1003
+ return full;
1004
+ });
1005
+ const standaloneRegex = /(?<![A-Za-z0-9/_.-])\b([A-Za-z0-9+/_-]{28,})\b(?![A-Za-z0-9/_.-])/g;
1006
+ sanitized = sanitized.replace(standaloneRegex, (match) => {
1007
+ if (match.includes('REDACTED:') || match.includes('/')) return match;
1008
+ const hasUpper = /[A-Z]/.test(match);
1009
+ const hasLower = /[a-z]/.test(match);
1010
+ const hasDigit = /[0-9]/.test(match);
1011
+ if (hasUpper && hasLower && hasDigit && shannonEntropy(match) >= 4.5) {
1012
+ const token = `‹REDACTED:SECRET:${shortHash4(match)}›`;
1013
+ findings.push({ provider: 'SECRET', token, preview: match.slice(0, 6) + '...' });
1014
+ return token;
1015
+ }
1016
+ return match;
1017
+ });
1018
+ return { sanitized, findings };
1019
+ }
1020
+
1021
+ // 3. Isomorphic Brace-Balanced & Indentation Code Skeletonizer (matches vicem.mjs CodeSkeletonizer — Fix 11-B)
1022
+ function findBalancedBraces(code) {
1023
+ const pairs = [];
1024
+ const stack = [];
1025
+ const tmplStack = [];
1026
+ let state = 'CODE';
1027
+ for (let i = 0; i < code.length; i++) {
1028
+ const ch = code[i];
1029
+ const next = code[i + 1] || '';
1030
+ if (state === 'LINE_COMMENT') {
1031
+ if (ch === '\n') state = 'CODE';
1032
+ continue;
1033
+ }
1034
+ if (state === 'BLOCK_COMMENT') {
1035
+ if (ch === '*' && next === '/') { state = 'CODE'; i++; }
1036
+ continue;
1037
+ }
1038
+ if (state === 'SINGLE_STRING') {
1039
+ if (ch === '\\') { i++; continue; }
1040
+ if (ch === "'") state = 'CODE';
1041
+ continue;
1042
+ }
1043
+ if (state === 'DOUBLE_STRING') {
1044
+ if (ch === '\\') { i++; continue; }
1045
+ if (ch === '"') state = 'CODE';
1046
+ continue;
1047
+ }
1048
+ if (state === 'TEMPLATE_STRING') {
1049
+ if (ch === '\\') { i++; continue; }
1050
+ if (ch === '$' && next === '{') {
1051
+ tmplStack.push(stack.length);
1052
+ state = 'CODE';
1053
+ i++;
1054
+ continue;
1055
+ }
1056
+ if (ch === '`') state = 'CODE';
1057
+ continue;
1058
+ }
1059
+ if (ch === '/' && next === '/') { state = 'LINE_COMMENT'; i++; continue; }
1060
+ if (ch === '/' && next === '*') { state = 'BLOCK_COMMENT'; i++; continue; }
1061
+ if (ch === "'") { state = 'SINGLE_STRING'; continue; }
1062
+ if (ch === '"') { state = 'DOUBLE_STRING'; continue; }
1063
+ if (ch === '`') { state = 'TEMPLATE_STRING'; continue; }
1064
+ if (ch === '{') {
1065
+ stack.push(i);
1066
+ } else if (ch === '}') {
1067
+ if (tmplStack.length > 0 && stack.length === tmplStack[tmplStack.length - 1]) {
1068
+ tmplStack.pop();
1069
+ state = 'TEMPLATE_STRING';
1070
+ continue;
1071
+ }
1072
+ if (stack.length > 0) {
1073
+ const openIdx = stack.pop();
1074
+ pairs.push({ openIdx, closeIdx: i, depth: stack.length });
1075
+ }
1076
+ }
1077
+ }
1078
+ return pairs;
1079
+ }
1080
+
1081
+ function stripNestedParens(str) {
1082
+ let out = '';
1083
+ let depth = 0;
1084
+ for (let i = 0; i < str.length; i++) {
1085
+ const ch = str[i];
1086
+ if (ch === '(') {
1087
+ if (depth === 0) out += '(';
1088
+ depth++;
1089
+ } else if (ch === ')') {
1090
+ depth = Math.max(0, depth - 1);
1091
+ if (depth === 0) out += ')';
1092
+ } else if (depth === 0) {
1093
+ out += ch;
1094
+ }
1095
+ }
1096
+ return out;
1097
+ }
1098
+
1099
+ function classifyCBlockHeader(code, openIdx) {
1100
+ const lookbackStart = Math.max(0, openIdx - 500);
1101
+ const rawBefore = code.slice(lookbackStart, openIdx);
1102
+ const collapsed = stripNestedParens(rawBefore);
1103
+ const segments = collapsed.split(/[;{}]/);
1104
+ const lastSeg = (segments[segments.length - 1] || '').trim();
1105
+ if (!lastSeg) return { kind: 'other' };
1106
+ if (/\b(?:interface|type|enum|struct|trait)\s+[A-Za-z0-9_$]+/.test(lastSeg)) return { kind: 'contract' };
1107
+ if (/\b(?:class|namespace|mod|impl)\b/.test(lastSeg)) return { kind: 'container' };
1108
+ if (/\b(?:if|for|while|switch|catch|try|else|do)\s*(?:\(\))?$/.test(lastSeg)) return { kind: 'control' };
1109
+ const isArrow = /=>\s*$/.test(lastSeg);
1110
+ const isFunc = /\b(?:function|fn|func)\b/.test(lastSeg) || /(?:^|[\s\n])(?:async\s+|static\s+|public\s+|private\s+|protected\s+|get\s+|set\s+)*[A-Za-z_$][A-Za-z0-9_$]*\s*\(\)[^{;]*$/.test(lastSeg);
1111
+ if (isArrow || isFunc) return { kind: 'function' };
1112
+ return { kind: 'other' };
1113
+ }
1114
+
1115
+ function skeletonizePython(code, minLines = 12) {
1116
+ const lines = code.split(/\r?\n/);
1117
+ const out = [];
1118
+ let i = 0;
1119
+ while (i < lines.length) {
1120
+ const line = lines[i];
1121
+ const m = line.match(/^(\s*)(?:async\s+)?def\s+([A-Za-z0-9_]+)\s*\(/);
1122
+ if (!m) { out.push(line); i++; continue; }
1123
+ const defIndent = m[1].length;
1124
+ let sigEnd = i;
1125
+ while (sigEnd < lines.length && !/:\s*(?:#.*)?$/.test(lines[sigEnd])) sigEnd++;
1126
+ if (sigEnd >= lines.length) { out.push(line); i++; continue; }
1127
+ let bodyEnd = sigEnd;
1128
+ let j = sigEnd + 1;
1129
+ while (j < lines.length) {
1130
+ const trimmed = lines[j].trim();
1131
+ if (trimmed === '' || trimmed.startsWith('#')) { j++; continue; }
1132
+ const indent = (lines[j].match(/^(\s*)/)?.[1] || '').length;
1133
+ if (indent <= defIndent) break;
1134
+ bodyEnd = j;
1135
+ j++;
1136
+ }
1137
+ const span = bodyEnd - i + 1;
1138
+ if (span >= minLines) {
1139
+ for (let k = i; k <= sigEnd; k++) out.push(lines[k]);
1140
+ out.push(' '.repeat(defIndent + 4) + 'pass # ... [skeletonized]');
1141
+ i = bodyEnd + 1;
1142
+ } else {
1143
+ for (let k = i; k <= bodyEnd; k++) out.push(lines[k]);
1144
+ i = bodyEnd + 1;
1145
+ }
1146
+ }
1147
+ return out.join('\n');
1148
+ }
1149
+
1150
+ function skeletonizeCode(code, level) {
1151
+ if (level === 1) return code;
1152
+ if (level === 3) {
1153
+ return code
1154
+ .split('\n')
1155
+ .filter((line) => /^(?:\s*export\s+|\s*(?:interface|type|enum|class|function|def|fn|func)\b)/.test(line))
1156
+ .join('\n');
1157
+ }
1158
+ let working = code;
1159
+ if (/^\s*(?:async\s+)?def\s+[A-Za-z0-9_]+\s*\(/m.test(working)) {
1160
+ working = skeletonizePython(working, 12);
1161
+ }
1162
+ const pairs = findBalancedBraces(working);
1163
+ const candidates = [];
1164
+ for (const p of pairs) {
1165
+ const bodySlice = working.slice(p.openIdx, p.closeIdx + 1);
1166
+ const lineSpan = bodySlice.split('\n').length;
1167
+ if (lineSpan < 12) continue;
1168
+ const info = classifyCBlockHeader(working, p.openIdx);
1169
+ if (info.kind === 'function') {
1170
+ candidates.push(p);
1171
+ }
1172
+ }
1173
+ candidates.sort((a, b) => a.openIdx - b.openIdx);
1174
+ const filtered = [];
1175
+ let lastEnd = -1;
1176
+ for (const c of candidates) {
1177
+ if (c.openIdx > lastEnd) {
1178
+ filtered.push(c);
1179
+ lastEnd = c.closeIdx;
1180
+ }
1181
+ }
1182
+ if (filtered.length === 0) return working;
1183
+ let result = '';
1184
+ let cursor = 0;
1185
+ for (const c of filtered) {
1186
+ result += working.slice(cursor, c.openIdx) + '{ /* ... */ }';
1187
+ cursor = c.closeIdx + 1;
1188
+ }
1189
+ result += working.slice(cursor);
1190
+ return result;
1191
+ }
1192
+
1193
+ // DOM elements
1194
+ const goalInput = document.getElementById('goalInput');
1195
+ const nextInput = document.getElementById('nextInput');
1196
+ const rawSourceInput = document.getElementById('rawSourceInput');
1197
+ const dietSlider = document.getElementById('dietSlider');
1198
+ const dietLevelName = document.getElementById('dietLevelName');
1199
+ const tokenStatsText = document.getElementById('tokenStatsText');
1200
+ const budgetStatusText = document.getElementById('budgetStatusText');
1201
+ const gaugeFill = document.getElementById('gaugeFill');
1202
+ const inspectorOutput = document.getElementById('inspectorOutput');
1203
+ const secretShieldSummary = document.getElementById('secretShieldSummary');
1204
+ const secretTabCount = document.getElementById('secretTabCount');
1205
+ const targetSelect = document.getElementById('targetSelect');
1206
+ const actionReceipt = document.getElementById('actionReceipt');
1207
+ const copyBatonBtn = document.getElementById('copyBatonBtn');
1208
+ const downloadBatonBtn = document.getElementById('downloadBatonBtn');
1209
+ const dropZone = document.getElementById('dropZone');
1210
+ const filePicker = document.getElementById('filePicker');
1211
+ const fileCountBadge = document.getElementById('fileCountBadge');
1212
+
1213
+ let activeTab = 'preview';
1214
+ let currentState = { batonMd: '', skeletonPreview: '', findings: [], rawTokens: 0, dietTokens: 0 };
1215
+
1216
+ const LEVEL_LABELS = {
1217
+ 1: 'L1 · Light (Log Slimming + Secret Redaction)',
1218
+ 2: 'L2 · Balanced Skeletons (Recommended)',
1219
+ 3: 'L3 · Aggressive (Signatures & Diff Only)'
1220
+ };
1221
+
1222
+ function recomputeBaton() {
1223
+ const level = parseInt(dietSlider.value, 10) || 2;
1224
+ dietLevelName.textContent = LEVEL_LABELS[level];
1225
+
1226
+ const rawCode = rawSourceInput.value || '';
1227
+ const goal = goalInput.value || 'Project Handoff Task';
1228
+ const nextStep = nextInput.value || 'Inspect BATON.md and continue implementation';
1229
+ const target = targetSelect.value || 'codex';
1230
+
1231
+ const skeleton = skeletonizeCode(rawCode, level);
1232
+ const { sanitized: cleanSkel, findings } = redactSecrets(skeleton);
1233
+ const rawTok = estimateTokens(goal + '\n' + nextStep + '\n' + rawCode);
1234
+
1235
+ const draftBaton = [
1236
+ `# BATON: ${goal.slice(0, 80)}`,
1237
+ `> Generated by Vicem Studio v1.0.0 | Target: ${target} | Diet Level: L${level}`,
1238
+ ``,
1239
+ `## 1. Goal`,
1240
+ goal,
1241
+ ``,
1242
+ `## 2. Immediate NEXT`,
1243
+ `1. **${nextStep}**`,
1244
+ ``,
1245
+ `## 3. Architectural Context (Code Skeletons)`,
1246
+ '```ts',
1247
+ cleanSkel.trim(),
1248
+ '```',
1249
+ ``,
1250
+ `---`,
1251
+ `> **EXECUTION ANCHOR:** Never write skeleton placeholders (\`{ ... }\`, \`/* ... */\`) or \`‹REDACTED›\` tokens into source files. Begin immediately from **Section 2 (Immediate NEXT #1)**.`
1252
+ ].join('\n');
1253
+
1254
+ const dietTok = estimateTokens(draftBaton);
1255
+ const effRaw = Math.max(rawTok, dietTok);
1256
+ const ratio = effRaw > dietTok ? Math.round(((effRaw - dietTok) / effRaw) * 100) : 0;
1257
+
1258
+ tokenStatsText.textContent = `Diet: ${effRaw} → ${dietTok} tokens (-${ratio}%)`;
1259
+ const pct = Math.min(100, Math.max(6, Math.round((dietTok / 4000) * 100)));
1260
+ gaugeFill.style.width = `${pct}%`;
1261
+
1262
+ if (dietTok <= 2000) {
1263
+ gaugeFill.style.background = 'var(--accent-green)';
1264
+ budgetStatusText.textContent = 'SAFE (URL Prefill & CLI Ready)';
1265
+ budgetStatusText.style.color = 'var(--accent-green)';
1266
+ } else if (dietTok <= 4000) {
1267
+ gaugeFill.style.background = 'var(--accent-amber)';
1268
+ budgetStatusText.textContent = 'CLIPBOARD MODE (< 4,000 tok)';
1269
+ budgetStatusText.style.color = 'var(--accent-amber)';
1270
+ } else {
1271
+ gaugeFill.style.background = 'var(--accent-red)';
1272
+ budgetStatusText.textContent = 'OVER BUDGET (> 4,000 tok)';
1273
+ budgetStatusText.style.color = 'var(--accent-red)';
1274
+ }
1275
+
1276
+ secretShieldSummary.textContent = `${findings.length} secret(s) masked`;
1277
+ secretTabCount.textContent = String(findings.length);
1278
+
1279
+ currentState = {
1280
+ batonMd: draftBaton,
1281
+ skeletonPreview: cleanSkel,
1282
+ findings,
1283
+ rawTokens: effRaw,
1284
+ dietTokens: dietTok,
1285
+ ratio
1286
+ };
1287
+
1288
+ renderInspector();
1289
+ }
1290
+
1291
+ function renderInspector() {
1292
+ if (activeTab === 'preview') {
1293
+ inspectorOutput.textContent = currentState.batonMd;
1294
+ } else if (activeTab === 'skeleton') {
1295
+ inspectorOutput.textContent = currentState.skeletonPreview;
1296
+ } else {
1297
+ if (currentState.findings.length === 0) {
1298
+ inspectorOutput.textContent = '[OK] Zero secrets detected in input.';
1299
+ } else {
1300
+ inspectorOutput.textContent = currentState.findings
1301
+ .map((f, i) => `${i + 1}. [${f.provider}] ${f.preview} --> ${f.token}`)
1302
+ .join('\n');
1303
+ }
1304
+ }
1305
+ }
1306
+
1307
+ // Tab switching
1308
+ document.querySelectorAll('.tab-btn').forEach((btn) => {
1309
+ btn.addEventListener('click', () => {
1310
+ document.querySelectorAll('.tab-btn').forEach((b) => b.setAttribute('aria-selected', 'false'));
1311
+ btn.setAttribute('aria-selected', 'true');
1312
+ activeTab = btn.getAttribute('data-tab');
1313
+ renderInspector();
1314
+ });
1315
+ });
1316
+
1317
+ // Live input listeners
1318
+ [goalInput, nextInput, rawSourceInput, dietSlider, targetSelect].forEach((el) => {
1319
+ el.addEventListener('input', recomputeBaton);
1320
+ });
1321
+
1322
+ // Copy Baton Action
1323
+ async function copyBaton() {
1324
+ try {
1325
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1326
+ await navigator.clipboard.writeText(currentState.batonMd);
1327
+ }
1328
+ actionReceipt.textContent = `[OK] [Vicem] Copied Baton (${currentState.rawTokens} → ${currentState.dietTokens} tok, ${currentState.findings.length} secrets masked)`;
1329
+ } catch {
1330
+ actionReceipt.textContent = `[OK] [Vicem] Baton ready in preview (${currentState.dietTokens} tok)`;
1331
+ }
1332
+ }
1333
+
1334
+ copyBatonBtn.addEventListener('click', copyBaton);
1335
+
1336
+ // Keyboard shortcut: Cmd+Enter / Ctrl+Enter copies Baton immediately
1337
+ window.addEventListener('keydown', (e) => {
1338
+ if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
1339
+ e.preventDefault();
1340
+ copyBaton();
1341
+ }
1342
+ });
1343
+
1344
+ // Download BATON.md
1345
+ downloadBatonBtn.addEventListener('click', () => {
1346
+ const blob = new Blob([currentState.batonMd], { type: 'text/markdown;charset=utf-8' });
1347
+ const a = document.createElement('a');
1348
+ a.href = URL.createObjectURL(blob);
1349
+ a.download = 'BATON.md';
1350
+ a.click();
1351
+ URL.revokeObjectURL(a.href);
1352
+ actionReceipt.textContent = `[OK] [Vicem] Downloaded BATON.md (${currentState.dietTokens} tokens)`;
1353
+ });
1354
+
1355
+ // File Drop & Picker (Credential-blind: skips .env / *key* / *token*)
1356
+ function isSafeFileName(name) {
1357
+ return !/^(?:\.env|auth\.json|credentials\.json|id_rsa|id_ed25519)$/i.test(name);
1358
+ }
1359
+
1360
+ async function handleFiles(fileList) {
1361
+ const safeFiles = Array.from(fileList).filter((f) => isSafeFileName(f.name)).slice(0, 15);
1362
+ if (safeFiles.length === 0) return;
1363
+ const chunks = [];
1364
+ for (const f of safeFiles) {
1365
+ const text = await f.text();
1366
+ chunks.push(`// File: ${f.name}\n${text}`);
1367
+ }
1368
+ rawSourceInput.value = chunks.join('\n\n');
1369
+ fileCountBadge.textContent = `${safeFiles.length} file(s)`;
1370
+ recomputeBaton();
1371
+ }
1372
+
1373
+ dropZone.addEventListener('click', () => filePicker.click());
1374
+ filePicker.addEventListener('change', (e) => handleFiles(e.target.files));
1375
+ dropZone.addEventListener('dragover', (e) => {
1376
+ e.preventDefault();
1377
+ dropZone.classList.add('drag-over');
1378
+ });
1379
+ dropZone.addEventListener('dragleave', () => dropZone.classList.remove('drag-over'));
1380
+ dropZone.addEventListener('drop', (e) => {
1381
+ e.preventDefault();
1382
+ dropZone.classList.remove('drag-over');
1383
+ if (e.dataTransfer && e.dataTransfer.files) {
1384
+ handleFiles(e.dataTransfer.files);
1385
+ }
1386
+ });
1387
+
1388
+ // Boot Hydration from `.vicem/studio.live.html` (#vicem-hydration-data — Fix 11-A)
1389
+ const hydrationEl = document.getElementById('vicem-hydration-data');
1390
+ if (hydrationEl && hydrationEl.textContent) {
1391
+ try {
1392
+ const hydrated = JSON.parse(hydrationEl.textContent);
1393
+ if (hydrated.goal) goalInput.value = hydrated.goal;
1394
+ if (Array.isArray(hydrated.nextSteps) && hydrated.nextSteps[0]) {
1395
+ nextInput.value = hydrated.nextSteps[0];
1396
+ } else if (typeof hydrated.nextStep === 'string') {
1397
+ nextInput.value = hydrated.nextStep;
1398
+ }
1399
+ if (typeof hydrated.rawSource === 'string') rawSourceInput.value = hydrated.rawSource;
1400
+ if (typeof hydrated.targetAgent === 'string') targetSelect.value = hydrated.targetAgent;
1401
+ if (typeof hydrated.fileCount === 'number') fileCountBadge.textContent = `${hydrated.fileCount} file(s)`;
1402
+ } catch {}
1403
+ }
1404
+
1405
+ // Initial render
1406
+ recomputeBaton();
1407
+ })();
1408
+ </script>
1409
+ </body>
1410
+ </html>