-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathquiz.html
More file actions
8194 lines (8114 loc) · 498 KB
/
quiz.html
File metadata and controls
8194 lines (8114 loc) · 498 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
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Claude Code 源码精通测验 — 616题深度挑战</title>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/dist/confetti.browser.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--bg:#0d1117;--surface:#161b22;--surface2:#21262d;--border:#30363d;
--text:#e6edf3;--text2:#8b949e;--accent:#58a6ff;--accent2:#1f6feb;
--green:#3fb950;--orange:#d29922;--red:#f85149;--purple:#bc8cff;
--cyan:#39d353;--cat-tool:#58a6ff;--cat-query:#3fb950;--cat-agent:#bc8cff;
--cat-memory:#d29922;--cat-ui:#79c0ff;
--cat-auth:#f97583;--cat-service:#56d364;--cat-infra:#d2a8ff;--cat-remote:#ff7b72;--cat-render:#ffa657;
--cat-detail:#79c0ff;--cat-cmd:#b392f0;--cat-type:#ffab70;
--cat-ink:#85e89d;--cat-perm:#f692ce;
--cat-hooks:#ffd700;--cat-cache:#00bcd4;--cat-vim:#ff6e96;
--cat-security:#ff4757;
--cat-process:#2ed573;
}
html{scroll-behavior:smooth}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;background:var(--bg);color:var(--text);line-height:1.7;overflow-x:hidden}
a{color:var(--accent);text-decoration:none}
::-webkit-scrollbar{width:6px}
::-webkit-scrollbar-track{background:var(--bg)}
::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}
::-webkit-scrollbar-thumb:hover{background:var(--text2)}
.hero{min-height:50vh;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:3rem 1.5rem 2rem;position:relative;overflow:hidden}
.hero::before{content:"";position:absolute;top:-50%;left:-50%;width:200%;height:200%;background:radial-gradient(circle at 30% 40%,rgba(88,166,255,.08) 0%,transparent 50%),radial-gradient(circle at 70% 60%,rgba(188,140,255,.06) 0%,transparent 50%);animation:bgShift 15s ease-in-out infinite alternate}
@keyframes bgShift{0%{transform:translate(0,0)}100%{transform:translate(-5%,3%)}}
.hero h1{font-size:clamp(1.8rem,5vw,3.2rem);font-weight:800;background:linear-gradient(135deg,var(--accent),var(--purple));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:.75rem;position:relative}
.hero .subtitle{font-size:clamp(.85rem,2vw,1.05rem);color:var(--text2);max-width:750px;margin-bottom:2rem;position:relative;line-height:1.8}
.hero .hero-stats{display:flex;gap:2rem;flex-wrap:wrap;justify-content:center;margin-bottom:1.5rem;position:relative}
.hero .hero-stats .stat{text-align:center}
.hero .hero-stats .num{font-size:1.8rem;font-weight:700;color:var(--accent)}
.hero .hero-stats .label{font-size:.8rem;color:var(--text2)}
.back-link{position:absolute;top:1.5rem;left:1.5rem;color:var(--text2);font-size:.9rem;display:flex;align-items:center;gap:.4rem;z-index:10;text-decoration:none}
.back-link:hover{color:var(--accent)}
.container{max-width:900px;margin:0 auto;padding:0 1.5rem 4rem}
.float-progress{position:fixed;top:0;left:0;right:0;z-index:1000;background:rgba(13,17,23,.92);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--border);padding:.6rem 1.5rem;display:flex;align-items:center;gap:1rem;transform:translateY(-100%);transition:transform .3s ease}
.float-progress.visible{transform:translateY(0)}
.progress-bar-wrap{flex:1;height:6px;background:var(--surface2);border-radius:3px;overflow:hidden}
.progress-bar-fill{height:100%;background:linear-gradient(90deg,var(--accent),var(--purple));border-radius:3px;transition:width .4s ease}
.progress-text{font-size:.8rem;color:var(--text2);white-space:nowrap}
.score-text{font-size:.85rem;font-weight:600;color:var(--green);white-space:nowrap}
.filters{margin-bottom:2rem;position:relative;z-index:5}
.filter-row{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.75rem;align-items:center}
.filter-label{font-size:.8rem;color:var(--text2);margin-right:.25rem;font-weight:600}
.filter-btn{padding:.35rem .85rem;border-radius:20px;border:1px solid var(--border);background:transparent;color:var(--text2);font-size:.8rem;cursor:pointer;transition:all .2s;font-family:inherit}
.filter-btn:hover{border-color:var(--text2);color:var(--text)}
.filter-btn.active{background:var(--accent2);border-color:var(--accent);color:#fff}
.filter-btn.cat-tool.active{background:rgba(88,166,255,.2);border-color:var(--cat-tool);color:var(--cat-tool)}
.filter-btn.cat-query.active{background:rgba(63,185,80,.2);border-color:var(--cat-query);color:var(--cat-query)}
.filter-btn.cat-agent.active{background:rgba(188,140,255,.2);border-color:var(--cat-agent);color:var(--cat-agent)}
.filter-btn.cat-memory.active{background:rgba(210,153,34,.2);border-color:var(--cat-memory);color:var(--cat-memory)}
.filter-btn.cat-ui.active{background:rgba(121,192,255,.2);border-color:var(--cat-ui);color:var(--cat-ui)}
.filter-btn.cat-auth.active{background:rgba(249,117,131,.2);border-color:var(--cat-auth);color:var(--cat-auth)}
.filter-btn.cat-service.active{background:rgba(86,211,100,.2);border-color:var(--cat-service);color:var(--cat-service)}
.filter-btn.cat-infra.active{background:rgba(210,168,255,.2);border-color:var(--cat-infra);color:var(--cat-infra)}
.filter-btn.cat-remote.active{background:rgba(255,123,114,.2);border-color:var(--cat-remote);color:var(--cat-remote)}
.filter-btn.cat-render.active{background:rgba(255,166,87,.2);border-color:var(--cat-render);color:var(--cat-render)}
.filter-btn.diff-basic.active{background:rgba(63,185,80,.2);border-color:var(--green);color:var(--green)}
.filter-btn.diff-mid.active{background:rgba(210,153,34,.2);border-color:var(--orange);color:var(--orange)}
.filter-btn.diff-adv.active{background:rgba(248,81,73,.2);border-color:var(--red);color:var(--red)}
.filter-btn.cat-hooks.active{background:rgba(255,215,0,.2);border-color:var(--cat-hooks);color:var(--cat-hooks)}
.filter-btn.cat-cache.active{background:rgba(0,188,212,.2);border-color:var(--cat-cache);color:var(--cat-cache)}
.filter-btn.cat-vim.active{background:rgba(255,110,150,.2);border-color:var(--cat-vim);color:var(--cat-vim)}
.filter-btn.cat-security.active{background:rgba(255,71,87,.2);border-color:var(--cat-security);color:var(--cat-security)}
.filter-btn.cat-process.active{background:rgba(46,213,115,.2);border-color:var(--cat-process);color:var(--cat-process)}
.action-row{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:2rem}
.action-btn{padding:.45rem 1rem;border-radius:8px;border:1px solid var(--border);background:var(--surface);color:var(--text2);font-size:.8rem;cursor:pointer;transition:all .2s;font-family:inherit;display:flex;align-items:center;gap:.4rem}
.action-btn:hover{border-color:var(--accent);color:var(--text);background:var(--surface2)}
.action-btn svg{width:14px;height:14px}
.question-card{background:rgba(22,27,34,.65);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(48,54,61,.6);border-radius:14px;padding:1.75rem;margin-bottom:1.25rem;transition:all .4s ease;opacity:0;transform:translateY(20px);position:relative;overflow:hidden}
.question-card.visible{opacity:1;transform:translateY(0)}
.question-card::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;opacity:0;transition:opacity .3s}
.question-card[data-cat="Tool\u7CFB\u7EDF"]::before{background:var(--cat-tool)}
.question-card[data-cat="Query\u5F15\u64CE\u4E0E\u5BF9\u8BDD"]::before{background:var(--cat-query)}
.question-card[data-cat="\u591AAgent\u4E0E\u534F\u8C03"]::before{background:var(--cat-agent)}
.question-card[data-cat="Memory\u4E0E\u914D\u7F6E"]::before{background:var(--cat-memory)}
.question-card[data-cat="\u542F\u52A8\u4E0EUI"]::before{background:var(--cat-ui)}
.question-card[data-cat="\u8BA4\u8BC1\u4E0E\u9065\u6D4B"]::before{background:var(--cat-auth)}
.question-card[data-cat="\u670D\u52A1\u5C42\u67B6\u6784"]::before{background:var(--cat-service)}
.question-card[data-cat="\u5DE5\u5177\u4E0E\u57FA\u7840\u8BBE\u65BD"]::before{background:var(--cat-infra)}
.question-card[data-cat="\u8FDC\u7A0B\u4E0E\u96C6\u6210"]::before{background:var(--cat-remote)}
.question-card[data-cat="\u754C\u9762\u4E0E\u6E32\u67D3"]::before{background:var(--cat-render)}
.question-card[data-cat="Hooks\u7CFB\u7EDF"]::before{background:var(--cat-hooks)}
.question-card[data-cat="Prompt\u7F13\u5B58\u4E0E\u4E0A\u4E0B\u6587"]::before{background:var(--cat-cache)}
.question-card[data-cat="Vim\u6A21\u5F0F"]::before{background:var(--cat-vim)}
.question-card[data-cat="\u5B89\u5168\u673A\u5236"]::before{background:var(--cat-security)}
.question-card[data-cat="\u8FDB\u7A0B\u4E0E\u5E76\u53D1"]::before{background:var(--cat-process)}
.question-card:hover::before,.question-card.answered::before{opacity:1}
.q-header{display:flex;align-items:flex-start;gap:.75rem;margin-bottom:1rem;flex-wrap:wrap}
.q-number{font-size:.75rem;font-weight:700;color:var(--text2);background:var(--surface2);padding:.2rem .6rem;border-radius:6px;white-space:nowrap}
.q-badges{display:flex;gap:.4rem;flex-wrap:wrap;margin-left:auto}
.badge{font-size:.7rem;padding:.15rem .55rem;border-radius:10px;font-weight:600;white-space:nowrap}
.badge-cat{border:1px solid}
.badge-cat-tool{color:var(--cat-tool);border-color:rgba(88,166,255,.3);background:rgba(88,166,255,.1)}
.badge-cat-query{color:var(--cat-query);border-color:rgba(63,185,80,.3);background:rgba(63,185,80,.1)}
.badge-cat-agent{color:var(--cat-agent);border-color:rgba(188,140,255,.3);background:rgba(188,140,255,.1)}
.badge-cat-memory{color:var(--cat-memory);border-color:rgba(210,153,34,.3);background:rgba(210,153,34,.1)}
.badge-cat-ui{color:var(--cat-ui);border-color:rgba(121,192,255,.3);background:rgba(121,192,255,.1)}
.badge-cat-auth{color:var(--cat-auth);border-color:rgba(249,117,131,.3);background:rgba(249,117,131,.1)}
.badge-cat-service{color:var(--cat-service);border-color:rgba(86,211,100,.3);background:rgba(86,211,100,.1)}
.badge-cat-infra{color:var(--cat-infra);border-color:rgba(210,168,255,.3);background:rgba(210,168,255,.1)}
.badge-cat-remote{color:var(--cat-remote);border-color:rgba(255,123,114,.3);background:rgba(255,123,114,.1)}
.badge-cat-render{color:var(--cat-render);border-color:rgba(255,166,87,.3);background:rgba(255,166,87,.1)}
.badge-cat-detail{color:var(--cat-detail);border-color:rgba(121,192,255,.3);background:rgba(121,192,255,.1)}
.badge-cat-cmd{color:var(--cat-cmd);border-color:rgba(179,146,240,.3);background:rgba(179,146,240,.1)}
.badge-cat-type{color:var(--cat-type);border-color:rgba(255,171,112,.3);background:rgba(255,171,112,.1)}
.badge-cat-ink{color:var(--cat-ink);border-color:rgba(133,232,157,.3);background:rgba(133,232,157,.1)}
.badge-cat-perm{color:var(--cat-perm);border-color:rgba(246,146,206,.3);background:rgba(246,146,206,.1)}
.badge-cat-hooks{color:var(--cat-hooks);border-color:rgba(255,215,0,.3);background:rgba(255,215,0,.08)}
.badge-cat-cache{color:var(--cat-cache);border-color:rgba(0,188,212,.3);background:rgba(0,188,212,.08)}
.badge-diff{border:1px solid}
.badge-basic{color:var(--green);border-color:rgba(63,185,80,.3);background:rgba(63,185,80,.1)}
.badge-mid{color:var(--orange);border-color:rgba(210,153,34,.3);background:rgba(210,153,34,.1)}
.badge-adv{color:var(--red);border-color:rgba(248,81,73,.3);background:rgba(248,81,73,.1)}
.q-text{font-size:.95rem;font-weight:600;line-height:1.7;margin-bottom:1.25rem;color:var(--text)}
.options{display:flex;flex-direction:column;gap:.5rem;margin-bottom:.75rem}
.option-btn{display:flex;align-items:flex-start;gap:.75rem;padding:.75rem 1rem;border-radius:10px;border:1px solid var(--border);background:rgba(22,27,34,.6);backdrop-filter:blur(4px);cursor:pointer;transition:all .25s;text-align:left;font-family:inherit;font-size:.88rem;color:var(--text);line-height:1.5;width:100%}
.option-btn:hover:not(.disabled){border-color:var(--accent);background:rgba(88,166,255,.06);transform:translateX(4px)}
.option-btn .opt-letter{font-weight:700;color:var(--accent);min-width:20px;flex-shrink:0}
.option-btn.correct{border-color:var(--green)!important;background:rgba(63,185,80,.12)!important;color:var(--green)}
.option-btn.correct .opt-letter{color:var(--green)}
.option-btn.wrong{border-color:var(--red)!important;background:rgba(248,81,73,.1)!important;color:var(--red)}
.option-btn.wrong .opt-letter{color:var(--red)}
.option-btn.disabled{cursor:default;opacity:.6}
.option-btn.disabled:hover{transform:none;border-color:var(--border);background:rgba(22,27,34,.6)}
.option-btn.correct.disabled{opacity:1}
.option-btn.wrong.disabled{opacity:.85}
.option-btn.show-correct{border-color:rgba(63,185,80,.4)!important;opacity:1!important;background:rgba(63,185,80,.06)!important}
.show-explain-btn{padding:.35rem .75rem;border-radius:6px;border:1px solid var(--border);background:transparent;color:var(--text2);font-size:.78rem;cursor:pointer;transition:all .2s;font-family:inherit;margin-top:.25rem}
.show-explain-btn:hover{border-color:var(--accent);color:var(--accent)}
.explanation{max-height:0;overflow:hidden;transition:max-height .5s ease,opacity .4s ease,padding .4s ease;opacity:0;background:rgba(33,38,45,.5);border-radius:10px;margin-top:.75rem;font-size:.85rem;line-height:1.8;color:var(--text2)}
.explanation.open{max-height:600px;opacity:1;padding:1rem 1.25rem}
.explanation strong{color:var(--accent)}
.results-section{display:none;animation:fadeInUp .6s ease}
.results-card{background:rgba(22,27,34,.65);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(48,54,61,.6);border-radius:16px;padding:2rem;margin-bottom:1.5rem;text-align:center}
.results-card h2{font-size:1.8rem;margin-bottom:.5rem;background:linear-gradient(135deg,var(--accent),var(--purple));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.results-score{font-size:4rem;font-weight:800;margin:.5rem 0}
.results-score.great{color:var(--green)}
.results-score.good{color:var(--orange)}
.results-score.low{color:var(--red)}
.results-message{color:var(--text2);margin-bottom:1.5rem;font-size:1rem}
.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:1rem;margin-bottom:2rem}
.stat-card{background:var(--surface2);border-radius:10px;padding:1rem;text-align:center}
.stat-card .stat-label{font-size:.75rem;color:var(--text2);margin-bottom:.5rem}
.stat-card .stat-value{font-size:1.4rem;font-weight:700}
.stat-card .stat-sub{font-size:.7rem;color:var(--text2);margin-top:.25rem}
.chart-section{background:rgba(22,27,34,.65);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(48,54,61,.6);border-radius:16px;padding:2rem;margin-bottom:1.5rem}
.chart-section h3{font-size:1.1rem;margin-bottom:1.5rem;text-align:center}
.bar-chart{display:flex;flex-direction:column;gap:1rem}
.bar-row{display:flex;align-items:center;gap:.75rem}
.bar-label{font-size:.78rem;color:var(--text2);min-width:100px;text-align:right}
.bar-track{flex:1;height:26px;background:var(--surface2);border-radius:6px;overflow:hidden;position:relative}
.bar-fill{height:100%;border-radius:6px;transition:width 1s ease;display:flex;align-items:center;justify-content:flex-end;padding-right:.5rem;font-size:.7rem;font-weight:700;color:#fff;min-width:fit-content}
.bar-fill.cat-tool{background:linear-gradient(90deg,rgba(88,166,255,.3),var(--cat-tool))}
.bar-fill.cat-query{background:linear-gradient(90deg,rgba(63,185,80,.3),var(--cat-query))}
.bar-fill.cat-agent{background:linear-gradient(90deg,rgba(188,140,255,.3),var(--cat-agent))}
.bar-fill.cat-memory{background:linear-gradient(90deg,rgba(210,153,34,.3),var(--cat-memory))}
.bar-fill.cat-ui{background:linear-gradient(90deg,rgba(121,192,255,.3),var(--cat-ui))}
.bar-fill.cat-auth{background:linear-gradient(90deg,rgba(249,117,131,.3),var(--cat-auth))}
.bar-fill.cat-service{background:linear-gradient(90deg,rgba(86,211,100,.3),var(--cat-service))}
.bar-fill.cat-infra{background:linear-gradient(90deg,rgba(210,168,255,.3),var(--cat-infra))}
.bar-fill.cat-remote{background:linear-gradient(90deg,rgba(255,123,114,.3),var(--cat-remote))}
.bar-fill.cat-render{background:linear-gradient(90deg,rgba(255,166,87,.3),var(--cat-render))}
.bar-fill.cat-hooks{background:linear-gradient(90deg,rgba(255,215,0,.3),var(--cat-hooks))}
.bar-fill.cat-cache{background:linear-gradient(90deg,rgba(0,188,212,.3),var(--cat-cache))}
.bar-value{font-size:.78rem;color:var(--text2);min-width:50px;text-align:left;margin-left:.5rem}
.diff-chart .bar-fill.diff-basic{background:linear-gradient(90deg,rgba(63,185,80,.3),var(--green))}
.diff-chart .bar-fill.diff-mid{background:linear-gradient(90deg,rgba(210,153,34,.3),var(--orange))}
.diff-chart .bar-fill.diff-adv{background:linear-gradient(90deg,rgba(248,81,73,.3),var(--red))}
@keyframes fadeInUp{from{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}
@keyframes pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.05)}}
.pulse{animation:pulse .6s ease}
.empty-state{text-align:center;padding:4rem 2rem;color:var(--text2)}
.empty-state .empty-icon{font-size:3rem;margin-bottom:1rem}
.empty-state p{font-size:1rem}
@media(max-width:640px){
.hero{padding:2rem 1rem 1.5rem;min-height:auto}
.hero h1{font-size:1.5rem}
.container{padding:0 1rem 3rem}
.question-card{padding:1.25rem}
.q-header{gap:.5rem}
.option-btn{padding:.6rem .75rem;font-size:.82rem}
.float-progress{padding:.5rem 1rem}
.bar-label{min-width:70px;font-size:.7rem}
.filter-row{gap:.35rem}
.filter-btn{padding:.3rem .6rem;font-size:.72rem}
.stats-grid{grid-template-columns:repeat(2,1fr)}
.results-card{padding:1.5rem}
.back-link{top:1rem;left:1rem;font-size:.8rem}
}
</style>
</head>
<body>
<a href="index.html" class="back-link">← 返回主页</a>
<div class="float-progress" id="floatProgress">
<div class="progress-text" id="progressText">0/100</div>
<div class="progress-bar-wrap"><div class="progress-bar-fill" id="progressFill" style="width:0%"></div></div>
<div class="score-text" id="scoreText">✓ 0</div>
</div>
<section class="hero">
<h1>Claude Code 源码精通测验</h1>
<p class="subtitle">基于 1913 个源文件的深度代码分析,覆盖 Tool 系统、Query 引擎、多 Agent 协调、Memory/memdir、migrations 迁移、vim 模式、voice 语音、bridge 协议、server 模式、entrypoints 启动序列、outputStyles 渲染、utils 工具函数、Hooks 架构、Buddy 伴侣、native-ts 等十九大核心模块</p>
<div class="hero-stats">
<div class="stat"><div class="num">368</div><div class="label">题目</div></div>
<div class="stat"><div class="num">18</div><div class="label">分类</div></div>
<div class="stat"><div class="num">3</div><div class="label">难度等级</div></div>
</div>
</section>
<div class="container">
<div class="filters">
<div class="filter-row">
<span class="filter-label">分类</span>
<button class="filter-btn active" data-filter="cat" data-value="all">全部</button>
<button class="filter-btn cat-tool" data-filter="cat" data-value="Tool系统">🔧 Tool系统</button>
<button class="filter-btn cat-query" data-filter="cat" data-value="Query引擎与对话">📡 Query引擎</button>
<button class="filter-btn cat-agent" data-filter="cat" data-value="多Agent与协调">🤖 多Agent</button>
<button class="filter-btn cat-memory" data-filter="cat" data-value="Memory与配置">📁 Memory</button>
<button class="filter-btn cat-ui" data-filter="cat" data-value="启动与UI">🖥 启动/UI</button>
<button class="filter-btn cat-auth" data-filter="cat" data-value="认证与遥测">🔒 认证与遥测</button>
<button class="filter-btn cat-service" data-filter="cat" data-value="服务层架构">⚙ 服务层架构</button>
<button class="filter-btn cat-infra" data-filter="cat" data-value="工具与基础设施">🔧 工具与基础设施</button>
<button class="filter-btn cat-remote" data-filter="cat" data-value="远程与集成">🌐 远程与集成</button>
<button class="filter-btn cat-render" data-filter="cat" data-value="界面与渲染">🎨 界面与渲染</button>
<button class="filter-btn cat-detail" data-filter="cat" data-value="工具实现细节">🔎 工具实现细节</button>
<button class="filter-btn cat-cmd" data-filter="cat" data-value="命令系统">💻 命令系统</button>
<button class="filter-btn cat-type" data-filter="cat" data-value="类型与数据模型">📊 类型与数据模型</button>
<button class="filter-btn cat-ink" data-filter="cat" data-value="Ink与组件">🎨 Ink与组件</button>
<button class="filter-btn cat-perm" data-filter="cat" data-value="权限与通知">🔒 权限与通知</button>
<button class="filter-btn cat-hooks" data-filter="cat" data-value="Hooks系统">⚓ Hooks系统</button>
<button class="filter-btn cat-cache" data-filter="cat" data-value="Prompt缓存与上下文">💾 Prompt缓存</button>
</div>
<div class="filter-row">
<span class="filter-label">难度</span>
<button class="filter-btn active" data-filter="diff" data-value="all">全部</button>
<button class="filter-btn diff-basic" data-filter="diff" data-value="基础">基础</button>
<button class="filter-btn diff-mid" data-filter="diff" data-value="进阶">进阶</button>
<button class="filter-btn diff-adv" data-filter="diff" data-value="高级">高级</button>
</div>
</div>
<div class="action-row">
<button class="action-btn" id="btnRevealAll" onclick="revealAllExplanations()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
全部展开解析
</button>
<button class="action-btn" id="btnShuffle" onclick="toggleShuffle()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/><polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/><line x1="4" y1="4" x2="9" y2="9"/></svg>
随机出题
</button>
<button class="action-btn" id="btnRestart" onclick="restartQuiz()">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="1 4 1 10 7 10"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/></svg>
重新开始
</button>
<button class="action-btn" id="btnResults" onclick="showResults()" style="display:none">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg>
查看结果
</button>
</div>
<div id="questionsContainer"></div>
<div class="empty-state" id="emptyState" style="display:none">
<div class="empty-icon">🔍</div>
<p>没有符合筛选条件的题目</p>
</div>
<div class="results-section" id="resultsSection"></div>
</div>
<script>
// Load questions
const ALL_QUESTIONS = [
{
"question": "在 bashPermissions.ts 中,bashToolCheckPermission 函数对权限检查的顺序是什么?",
"options": [
"A. 先检查路径约束 → 再检查 deny 规则 → 再检查 allow 规则 → 最后检查模式",
"B. 先检查 deny/ask 规则(精确匹配)→ 再检查 deny/ask 规则(前缀匹配)→ 再检查路径约束 → 再检查 allow 规则 → 再检查 sed 约束 → 再检查 mode",
"C. 先检查 mode → 再检查 deny 规则 → 再检查路径约束 → 最后检查 allow 规则",
"D. 先检查 allow 规则 → 再检查 deny 规则 → 最后检查路径约束"
],
"answer": "B",
"explanation": "在 bashPermissions.ts 的 bashToolCheckPermission 函数中,注释明确写明:「SECURITY FIX: Check Bash deny/ask rules BEFORE path constraints to prevent bypass via absolute paths outside the project directory (HackerOne report)」。完整顺序为:精确匹配 → prefix/wildcard 的 deny/ask 规则 → 路径约束 → allow 规则 → sed 约束 → mode 检查 → 只读规则。",
"category": "Tool系统",
"difficulty": "高级"
},
{
"question": "stripSafeWrappers 函数在 bashPermissions.ts 中分两个阶段处理命令,第二阶段与第一阶段的关键区别是什么?",
"options": [
"A. 第一阶段剥离环境变量和注释;第二阶段剥离环境变量和 wrapper 命令",
"B. 第一阶段剥离 wrapper 命令;第二阶段剥离环境变量",
"C. 第一阶段剥离环境变量和注释;第二阶段只剥离 wrapper 命令和注释,不再剥离环境变量",
"D. 两阶段都剥离环境变量和 wrapper 命令,区别仅在于处理顺序"
],
"answer": "C",
"explanation": "源码注释说明:Phase 2 只剥离 wrapper commands 和注释,不剥离环境变量。因为 wrapper 命令(timeout, time, nice, nohup)使用 execvp 执行参数,wrapper 后面的 VAR=val 是作为命令执行而非环境变量赋值。这是针对 HackerOne #3543050 的安全修复。",
"category": "Tool系统",
"difficulty": "高级"
},
{
"question": "在 bashSecurity.ts 中,nonMisparsingValidators 集合中的 validator 返回 ask 时,系统如何处理?",
"options": [
"A. 这些 validator 的优先级最高,会最先运行",
"B. 结果会被延迟(defer),等待后续 misparsing validator 的结果,若有则优先返回 misparsing 结果",
"C. 这些 validator 永远不会触发 ask",
"D. 使用不同的 logEvent 事件名称"
],
"answer": "B",
"explanation": "源码注释说:non-misparsing 的 ask 结果不带 isBashSecurityCheckForMisparsing 标志,如果短路了,后续 misparsing validator 就无法运行,可能让危险命令漏网。代码使用 deferredNonMisparsingResult 变量来实现延迟逻辑。",
"category": "Tool系统",
"difficulty": "高级"
},
{
"question": "在 shouldUseSandbox.ts 中,excludedCommands 被定义为什么性质的特性?",
"options": [
"A. 高安全性边界,绕过属于安全漏洞",
"B. 用户便利特性(convenience feature),不是安全边界,绕过不属于安全漏洞",
"C. 企业级策略控制,只能由管理员配置",
"D. 与 deny 权限规则等效的安全控制"
],
"answer": "B",
"explanation": "源码注释明确写道:excludedCommands is a user-facing convenience feature, not a security boundary. It is not a security bug to be able to bypass excludedCommands — the sandbox permission system (which prompts users) is the actual security control。",
"category": "Tool系统",
"difficulty": "进阶"
},
{
"question": "在 sandbox-adapter.ts 中,沙箱会无条件将哪些路径加入 denyWrite 列表?",
"options": [
"A. 所有 .git 目录和 .env 文件",
"B. 所有 settings.json 文件路径、managed settings drop-in 目录、以及 .claude/skills 目录",
"C. 只有当前工作目录下的 .claude/settings.json",
"D. 只有 policySettings 来源的配置文件"
],
"answer": "B",
"explanation": "源码注释写道「Always deny writes to settings.json files to prevent sandbox escape」,列出三类:所有 SETTING_SOURCES 对应的文件路径、getManagedSettingsDropInDir() 目录、.claude/skills 目录(Skills 与 commands/agents 有相同权限级别)。",
"category": "Tool系统",
"difficulty": "进阶"
},
{
"question": "deny 规则匹配时使用 stripAllEnvVars: true,而 allow 规则不使用,为什么?",
"options": [
"A. 性能优化,allow 规则更频繁",
"B. deny 规则需防止 FOO=bar denied_command 绕过;allow 规则若也剥离所有环境变量则 DOCKER_HOST=evil docker ps 会错误匹配 Bash(docker ps:*)",
"C. allow 规则不支持环境变量前缀语法",
"D. deny 规则只在 bypassPermissions 模式下生效"
],
"answer": "B",
"explanation": "deny 规则必须更难被绕过,所以剥离所有环境变量前缀。但 allow 规则不能这样做,否则 DOCKER_HOST=tcp://evil.com docker ps 会匹配 Bash(docker ps:*) 规则。这是 HackerOne 安全报告的结果。",
"category": "Tool系统",
"difficulty": "高级"
},
{
"question": "PermissionMode.ts 中 auto 模式的 external 字段设置为什么?",
"options": [
"A. external: 'auto'",
"B. external: 'default',因为 auto 模式是 ant-only 内部特性",
"C. external: 'bypassPermissions'",
"D. external: 'acceptEdits'"
],
"answer": "B",
"explanation": "auto 模式配置为 { title: 'Auto mode', external: 'default' as ExternalPermissionMode },isExternalPermissionMode 函数判断 mode !== 'auto'。auto 模式对外部 API 透明展示为 default。",
"category": "Tool系统",
"difficulty": "进阶"
},
{
"question": "当子命令数量超过 MAX_SUBCOMMANDS_FOR_SECURITY_CHECK(50)且 AST 不可用时,系统采取什么行动?",
"options": [
"A. 直接 deny 该命令",
"B. 允许执行但记录告警",
"C. 返回 behavior: 'ask',要求用户确认",
"D. 截断命令只处理前 50 个"
],
"answer": "C",
"explanation": "返回 { behavior: 'ask', reason: 'Command splits into N subcommands, too many to safety-check individually' }。注释解释:合法用户命令不会拆分这么多子命令,超过上限时回退到 ask 是安全默认行为。",
"category": "Tool系统",
"difficulty": "进阶"
},
{
"question": "ONE_SHOT_BUILTIN_AGENT_TYPES 集合包含哪些 agent 类型?设计目的是什么?",
"options": [
"A. Explore 和 Plan,跳过 agentId/SendMessage/usage trailer 以节省约 135 字符 token",
"B. Explore、Plan 和 Verification,限制只运行一轮",
"C. 所有内置 agent,标识不需要用户确认",
"D. Explore 和 Verification,启用并行执行"
],
"answer": "A",
"explanation": "ONE_SHOT_BUILTIN_AGENT_TYPES 只包含 Explore 和 Plan。注释说明这些 agent 运行一次就返回报告,父进程不会 SendMessage 继续它们,跳过 trailer 每次节省约 135 token,在每周 3400 万次 Explore 调用下非常显著。",
"category": "Tool系统",
"difficulty": "进阶"
},
{
"question": "BARE_SHELL_PREFIXES 中的 wrapper 命令(nice/stdbuf/nohup/timeout/time)为什么被阻止作为前缀规则建议?",
"options": [
"A. 这些命令本身危险",
"B. Bash(nice:*) 规则实际等于 Bash(*),因为安全检查会剥离这些 wrapper,导致 nice rm -rf / 通过该规则",
"C. 不支持子命令语法",
"D. 会导致性能问题"
],
"answer": "B",
"explanation": "源码注释:checkSemantics strips these wrappers to check the wrapped command. Suggesting Bash(nice:*) would be approximately Bash(*) — users would add it, then nice rm -rf / passes。系统阻止将 wrapper 名称生成为前缀建议,防止用户创建过于宽泛的 allow 规则。",
"category": "Tool系统",
"difficulty": "高级"
},
{
"question": "tokenBudget.ts 中 checkTokenBudget() 判断“收益递减”(isDiminishing)的条件是什么?",
"options": [
"A. continuationCount >= 3 且连续两次 delta 均 < 500 tokens",
"B. turnTokens 超过 budget 的 90%",
"C. continuationCount >= 5 且单次 delta < 1000 tokens",
"D. agentId 不为 undefined 时自动触发"
],
"answer": "A",
"explanation": "isDiminishing 的三重条件:tracker.continuationCount >= 3、deltaSinceLastCheck < DIMINISHING_THRESHOLD(500)且 tracker.lastDeltaTokens < DIMINISHING_THRESHOLD(500)。COMPLETION_THRESHOLD(0.9)是另一个独立的停止条件,agentId 存在则直接返回 stop。",
"category": "Query引擎与对话",
"difficulty": "高级"
},
{
"question": "QueryDeps 类型定义了 Query 引擎的 4 个 I/O 依赖,以下哪个不是其中之一?",
"options": [
"A. callModel(queryModelWithStreaming)",
"B. microcompact(microcompactMessages)",
"C. autocompact(autoCompactIfNeeded)",
"D. logEvent(analytics 事件上报)"
],
"answer": "D",
"explanation": "QueryDeps 仅包含4个依赖:callModel、microcompact、autocompact 和 uuid(randomUUID)。logEvent 仅作为未来扩展候选被列在注释里,当前版本并不在接口中。注释说明 scope 是 intentionally narrow。",
"category": "Query引擎与对话",
"difficulty": "进阶"
},
{
"question": "QueryConfig 中注释明确排除了哪类 gate?原因是什么?",
"options": [
"A. 环境变量 gate",
"B. feature() gate——tree-shaking boundary,必须内联以实现死代码消除",
"C. Statsig CACHED_MAY_BE_STALE gate",
"D. GrowthBook feature gate"
],
"answer": "B",
"explanation": "注释写道:Intentionally excludes feature() gates — those are tree-shaking boundaries and must stay inline at the guarded blocks for dead-code elimination。feature() 是 bun:bundle 的编译时工具。",
"category": "Query引擎与对话",
"difficulty": "高级"
},
{
"question": "getEffectiveContextWindowSize() 计算有效上下文窗口大小的公式是什么?",
"options": [
"A. contextWindow - AUTOCOMPACT_BUFFER_TOKENS(13000)",
"B. contextWindow - min(maxOutputTokensForModel, MAX_OUTPUT_TOKENS_FOR_SUMMARY=20000)",
"C. contextWindow × 0.9",
"D. contextWindow - WARNING_THRESHOLD_BUFFER_TOKENS(20000)"
],
"answer": "B",
"explanation": "先计算 reservedTokensForSummary = Math.min(getMaxOutputTokensForModel(model), 20000)(基于 compact 输出的 p99.99 为 17,387 tokens),最终返回 contextWindow - reservedTokensForSummary。AUTOCOMPACT_BUFFER_TOKENS 是另一层触发阈值。",
"category": "Query引擎与对话",
"difficulty": "进阶"
},
{
"question": "FOREGROUND_529_RETRY_SOURCES 集合的核心设计目的是什么?",
"options": [
"A. 列出所有应重试 401 错误的 query source",
"B. 区分前台用户等待的请求(重试529)与后台任务(立即放弃),防止容量级联放大",
"C. 标识需使用 fallback 模型的 query source",
"D. 控制哪些 source 可用 fast mode"
],
"answer": "B",
"explanation": "注释:Foreground query sources where the user IS blocking on the result — these retry on 529. Everything else bails immediately: during a capacity cascade each retry is 3-10x gateway amplification. shouldRetry529() 对不在集合内的 source 直接抛出 CannotRetryError。",
"category": "Query引擎与对话",
"difficulty": "进阶"
},
{
"question": "updateUsage() 中 input_tokens 等字段使用 > 0 非零守卫,而 output_tokens 直接覆写,为什么?",
"options": [
"A. input token 数量在流式过程中持续变化",
"B. message_delta 事件可能对 input 字段发送显式 0,真实值已在 message_start 中设定",
"C. output_tokens 只出现在 message_start 中",
"D. SDK 类型定义中 input_tokens 是可选字段"
],
"answer": "B",
"explanation": "注释说明:message_delta events may send explicit 0 values for input fields, which should not overwrite values from message_start. output_tokens 在流式过程中是正确递增的。这是 Anthropic 流式 API 的微妙行为特征。",
"category": "Query引擎与对话",
"difficulty": "高级"
},
{
"question": "createCompactCanUseTool() 返回的函数对所有工具调用返回什么行为?",
"options": [
"A. allow,因为需要读取文件",
"B. ask,要求用户确认",
"C. deny,禁止所有工具使用,因为 compaction agent 只应生成文本摘要",
"D. 根据工具名称动态决定"
],
"answer": "C",
"explanation": "始终返回 deny,message 为 'Tool use is not allowed during compaction',确保 compact 进程只输出纯文本摘要,不产生任何工具调用副作用。",
"category": "Query引擎与对话",
"difficulty": "基础"
},
{
"question": "COMPACTABLE_TOOLS 集合包含哪类工具?微压缩对它们做什么处理?",
"options": [
"A. 所有内置工具;完全删除 tool_result",
"B. 仅 FileReadTool;替换为摘要",
"C. 文件读写、Shell、Grep、Glob、Web搜索/抓取等大输出工具;替换内容为占位符字符串",
"D. 所有 MCP 工具;因为无法缓存"
],
"answer": "C",
"explanation": "包含 FILE_READ、SHELL、GREP、GLOB、WEB_SEARCH、WEB_FETCH、FILE_EDIT、FILE_WRITE。微压缩时替换为 TIME_BASED_MC_CLEARED_MESSAGE = '[Old tool result content cleared]',保持 tool_use/tool_result 配对完整性。",
"category": "Query引擎与对话",
"difficulty": "进阶"
},
{
"question": "skipCacheWrite = true 时,cache_control 标记被放在哪个消息位置?",
"options": [
"A. 最后一条消息",
"B. 倒数第二条消息(最后一个共享前缀点)",
"C. 第一条用户消息",
"D. 不添加任何标记"
],
"answer": "B",
"explanation": "markerIndex = skipCacheWrite ? messages.length - 2 : messages.length - 1。compact 进程通过 skipCacheWrite: true 将标记移至倒数第二条,重用主线程 prompt cache,不在 KVCC 中写入独立缓存尾部。",
"category": "Query引擎与对话",
"difficulty": "高级"
},
{
"question": "groupMessagesByApiRound() 中 API 轮次边界是通过什么机制判断的?",
"options": [
"A. 每遇到 type === 'user' 的消息就分组",
"B. 新 assistant 消息的 message.id 与上一条不同时触发分组",
"C. 根据 timestamp 间隔判断",
"D. 根据 tool_use 是否都有对应 tool_result"
],
"answer": "B",
"explanation": "用 lastAssistantId 追踪。streaming chunks from the same API response share an id, so boundaries only fire at the start of a genuinely new round。同一请求的多个流式内容块正确归入同一组。",
"category": "Query引擎与对话",
"difficulty": "进阶"
},
{
"question": "isCoordinatorMode() 函数是如何检测协调者模式的?",
"options": [
"A. 读取 ~/.claude/config.json 中的 coordinatorMode 字段",
"B. 实时读取 process.env.CLAUDE_CODE_COORDINATOR_MODE 环境变量(不缓存)",
"C. 检查 AppState 中的 isCoordinator 布尔标志",
"D. 通过 GrowthBook feature gate 控制"
],
"answer": "B",
"explanation": "每次调用都直接读取环境变量,没有缓存。这样设计是因为 matchSessionMode() 需要在运行时动态修改这个环境变量来匹配恢复的会话模式。",
"category": "多Agent与协调",
"difficulty": "进阶"
},
{
"question": "isForkSubagentEnabled() 在哪两种情况下强制返回 false?",
"options": [
"A. isCoordinatorMode() 为 true,或 getIsNonInteractiveSession() 为 true",
"B. swarm 后端为 in-process,或当前 agent 有 worktree",
"C. permissionMode 为 acceptEdits,或 maxTurns 超过 100",
"D. CLAUDE_CODE_AGENT_ID 已设置,或 model 不为 inherit"
],
"answer": "A",
"explanation": "即使 feature('FORK_SUBAGENT') 为真,如果 isCoordinatorMode() 或 getIsNonInteractiveSession() 返回 true,函数仍返回 false。Fork Subagent 与 coordinator mode 互斥。",
"category": "多Agent与协调",
"difficulty": "进阶"
},
{
"question": "FORK_AGENT 定义中 model 字段的值是什么?",
"options": [
"A. model: 'claude-3-5-sonnet'",
"B. model: undefined",
"C. model: 'inherit',保持与父进程相同模型以保证上下文长度一致性",
"D. model: 'haiku'"
],
"answer": "C",
"explanation": "FORK_AGENT 中 model: 'inherit' 保持父进程模型以保证 prompt cache 命中率,因为 fork 子进程继承完整对话历史。",
"category": "多Agent与协调",
"difficulty": "高级"
},
{
"question": "所有 fork 子进程的 tool_result 占位符文本使用什么常量值?",
"options": [
"A. 'Task delegated to fork child'",
"B. 'Fork started — processing in background'",
"C. 'Background agent spawned successfully'",
"D. 'Agent task initiated'"
],
"answer": "B",
"explanation": "FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'。所有 fork 子进程生成字节完全相同的 API 请求前缀,最大化 prompt cache 命中率,只有最后的 directive 文本块因子进程而异。",
"category": "多Agent与协调",
"difficulty": "高级"
},
{
"question": "INTERNAL_WORKER_TOOLS 集合包含哪些工具?",
"options": [
"A. Bash、Edit、Read",
"B. TEAM_CREATE、TEAM_DELETE、SEND_MESSAGE、SYNTHETIC_OUTPUT",
"C. Agent、WebSearch、WebFetch",
"D. Computer、Screen"
],
"answer": "B",
"explanation": "Worker 不应能创建/删除团队(coordinator 职责),不应直接向用户发送消息(绕过协调者),也不应使用合成输出工具(系统内部通信)。",
"category": "多Agent与协调",
"difficulty": "进阶"
},
{
"question": "isTeamLeader() 函数通过什么条件判断当前 agent 是团队 leader?",
"options": [
"A. CLAUDE_CODE_IS_LEADER 环境变量为 true",
"B. 拥有 ~/.claude/teams/ 目录写权限",
"C. agentId 不存在或等于字符串 'team-lead'",
"D. AppState 中的 isLeader 标志"
],
"answer": "C",
"explanation": "实现为:const agentId = getAgentId(); return !agentId || agentId === 'team-lead'。没有设置 agent ID 的进程(主进程/leader)或显式设置 'team-lead' 的进程被视为 leader。",
"category": "多Agent与协调",
"difficulty": "进阶"
},
{
"question": "权限请求文件的 pending 和 resolved 分别存储在哪里?",
"options": [
"A. ~/.claude/permissions/pending/ 和 resolved/",
"B. ~/.claude/teams/{teamName}/permissions/pending/ 和 resolved/",
"C. /tmp/claude-permissions/{teamName}/",
"D. ~/.claude/teams/{teamName}/mailbox/"
],
"answer": "B",
"explanation": "getPermissionDir(teamName) 返回 join(getTeamDir(teamName), 'permissions'),getPendingDir() 追加 'pending',getResolvedDir() 追加 'resolved'。文件名格式为 perm-{timestamp}-{random}.json。",
"category": "多Agent与协调",
"difficulty": "基础"
},
{
"question": "coordinator 模式下运行时,model 参数会发生什么处理?",
"options": [
"A. 直接传递给子 agent",
"B. 强制设为 claude-3-5-haiku",
"C. 强制设为 undefined(const model = isCoordinatorMode() ? undefined : modelParam)",
"D. 触发警告日志但仍被使用"
],
"answer": "C",
"explanation": "与 coordinator 系统提示词一致:Do not set the model parameter. Workers need the default model for the substantive tasks you delegate。强制 undefined 确保 worker 使用系统默认模型。",
"category": "多Agent与协调",
"difficulty": "进阶"
},
{
"question": "PaneBackend 和 TeammateExecutor 接口的主要区别是什么?",
"options": [
"A. PaneBackend 用于 in-process,TeammateExecutor 用于 tmux",
"B. PaneBackend 处理底层 terminal pane 操作,TeammateExecutor 处理高级 teammate 生命周期且跨所有后端统一",
"C. TeammateExecutor 支持权限管理,PaneBackend 只支持 UI",
"D. 功能完全相同,命名不同"
],
"answer": "B",
"explanation": "PaneBackend 包含 createTeammatePaneInSwarmView、setPaneBorderColor 等 UI 操作。TeammateExecutor 提供 spawn、sendMessage、terminate、kill、isActive 等高层抽象,跨所有后端工作。",
"category": "多Agent与协调",
"difficulty": "进阶"
},
{
"question": "auto-background 功能的超时时间是多少?由什么条件触发?",
"options": [
"A. 60000ms,由 CLAUDE_BACKGROUND_TIMEOUT 触发",
"B. 120000ms(2分钟),由 CLAUDE_AUTO_BACKGROUND_TASKS 环境变量或 tengu_auto_background_agents gate 触发",
"C. 30000ms,由 run_in_background 参数触发",
"D. 300000ms,由 coordinator mode 触发"
],
"answer": "B",
"explanation": "getAutoBackgroundMs() 在环境变量或 GrowthBook feature gate 启用时返回 120_000(2分钟),超过此时间后 UI 自动将 agent 任务转为后台显示。",
"category": "多Agent与协调",
"difficulty": "高级"
},
{
"question": "MEMORY.md 文件的最大行数限制和最大字节限制分别是多少?",
"options": [
"A. 100行 / 10KB",
"B. 200行 / 25KB",
"C. 500行 / 50KB",
"D. 无行数限制 / 25KB"
],
"answer": "B",
"explanation": "MAX_ENTRYPOINT_LINES = 200,MAX_ENTRYPOINT_BYTES = 25,000。truncateEntrypointContent() 先按行截断再按字节截断,最后追加 WARNING Markdown 引用块警告。",
"category": "Memory与配置",
"difficulty": "进阶"
},
{
"question": "记忆内容文件支持哪四种 type 字段值?",
"options": [
"A. User, Project, Local, Managed",
"B. user, feedback, project, reference",
"C. personal, team, shared, auto",
"D. note, rule, context, pointer"
],
"answer": "B",
"explanation": "memdir/memoryTypes.ts 中 MEMORY_TYPES = ['user', 'feedback', 'project', 'reference']。注意与 utils/memory/types.ts 的 MEMORY_TYPE_VALUES(来源/作用域分类)区分。",
"category": "Memory与配置",
"difficulty": "进阶"
},
{
"question": "设置优先级链从低到高的正确顺序是?",
"options": [
"A. projectSettings → userSettings → localSettings → flagSettings → policySettings",
"B. userSettings → projectSettings → localSettings → flagSettings → policySettings",
"C. localSettings → projectSettings → userSettings → policySettings → flagSettings",
"D. userSettings → localSettings → projectSettings → flagSettings → policySettings"
],
"answer": "B",
"explanation": "SETTING_SOURCES 数组顺序为 ['userSettings', 'projectSettings', 'localSettings', 'flagSettings', 'policySettings'],注释说 later sources override earlier ones。policySettings 最高优先级且不可编辑。",
"category": "Memory与配置",
"difficulty": "基础"
},
{
"question": "Hooks 系统支持哪四种 Hook type?",
"options": [
"A. shell, script, webhook, ai",
"B. command, prompt, agent, http",
"C. bash, python, llm, rest",
"D. exec, query, verify, post"
],
"answer": "B",
"explanation": "HookCommandSchema 是 discriminatedUnion('type'):command(shell命令)、prompt(LLM轻量判断)、agent(完整AI代理)、http(HTTP POST)。command 类型独有 asyncRewake 字段。",
"category": "Memory与配置",
"difficulty": "基础"
},
{
"question": "auto-memory 目录路径的解析优先级(从高到低)是什么?projectSettings 为什么被排除?",
"options": [
"A. CLAUDE_CODE_REMOTE_MEMORY_DIR > settings > 默认;projectSettings 因性能排除",
"B. CLAUDE_COWORK_MEMORY_PATH_OVERRIDE > settings(仅 policy/flag/local/user)> 默认路径;projectSettings 被排除防止恶意仓库重定向到 ~/.ssh 等目录",
"C. settings > 环境变量 > 默认路径;无安全考量",
"D. 固定为 ~/.claude/memory/,不可自定义"
],
"answer": "B",
"explanation": "getAutoMemPathSetting() 注释:projectSettings is intentionally excluded — a malicious repo could set autoMemoryDirectory: '~/.ssh' and gain silent write access。优先级:环境变量 > 受信 settings > 默认的 projects/<sanitized-git-root>/memory/。",
"category": "Memory与配置",
"difficulty": "高级"
},
{
"question": "allowManagedHooksOnly: true 与 disableAllHooks: true 的区别是什么?",
"options": [
"A. 完全相同,都禁用所有 Hook",
"B. 前者只运行企业托管 Hook、忽略用户/项目/本地 Hook;后者禁用所有 Hook 包括企业 Hook 和 statusLine",
"C. 前者只影响 PreToolUse 事件",
"D. allowManagedHooksOnly 只能由用户设置"
],
"answer": "B",
"explanation": "allowManagedHooksOnly 是来源过滤(保留企业Hook),disableAllHooks 是全部关闭(含企业Hook和状态栏脚本)。两者在 policySettings 中设置才发挥完整管理效果。",
"category": "Memory与配置",
"difficulty": "进阶"
},
{
"question": "managed-mcp.json 存在时,系统如何处理用户配置的 MCP 服务器?",
"options": [
"A. 企业服务器与用户服务器合并,企业优先",
"B. 企业配置独占控制,直接返回企业服务器,用户/项目/本地配置全部忽略",
"C. 用户服务器保留,企业以更低优先级追加",
"D. 只有设置 allowManagedMcpServersOnly 时才忽略"
],
"answer": "B",
"explanation": "注释:If an enterprise mcp config exists, do not use any others; this has exclusive control over all MCP servers。直接对企业服务器应用 policy 过滤后提前返回,后续代码不执行。",
"category": "Memory与配置",
"difficulty": "进阶"
},
{
"question": "内置插件 ID 格式是什么?启用状态如何决定?",
"options": [
"A. builtin:name;由 defaultEnabled 固定决定",
"B. {name}@builtin;用户 enabledPlugins 设置 > 插件 defaultEnabled > 默认 true",
"C. @builtin/{name};由环境变量控制",
"D. {name}@internal;固定 true 不可禁用"
],
"answer": "B",
"explanation": "BUILTIN_MARKETPLACE_NAME = 'builtin',ID 为 ${name}@builtin。启用逻辑:用户显式设置优先 > 插件定义的 defaultEnabled > 回退到 true。isBuiltinPluginId() 用 endsWith('@builtin') 检测。",
"category": "Memory与配置",
"difficulty": "基础"
},
{
"question": "findRelevantMemories() 使用什么模型筛选?最多返回多少个相关记忆?",
"options": [
"A. Haiku,最多 10 个",
"B. 默认 Sonnet,最多 5 个",
"C. BM25 关键词算法,最多 20 个",
"D. Opus,最多 3 个"
],
"answer": "B",
"explanation": "使用 getDefaultSonnetModel() 而非 Haiku,系统提示说 up to 5。scanMemoryFiles() 最多扫描 200 个文件按 mtime 排序,Sonnet 从中选出不超过 5 个。alreadySurfaced 参数避免重复。",
"category": "Memory与配置",
"difficulty": "高级"
},
{
"question": "strictPluginOnlyCustomization 可锁定哪些 surface?哪些来源仍被信任?",
"options": [
"A. skills, hooks, mcp, commands;只有 policySettings",
"B. skills, agents, hooks, mcp;plugin, policySettings, built-in, builtin, bundled",
"C. 所有 settings 字段;policySettings 和 flagSettings",
"D. skills, agents, hooks;mcp 单独由 allowManagedMcpServersOnly 控制"
],
"answer": "B",
"explanation": "CUSTOMIZATION_SURFACES = ['skills', 'agents', 'hooks', 'mcp'](注意是 agents 非 commands)。ADMIN_TRUSTED_SOURCES 有 5 个:plugin、policySettings、built-in、builtin、bundled。用户配置在锁定 surface 上被跳过。",
"category": "Memory与配置",
"difficulty": "高级"
},
{
"question": "--version 标志的处理方式与其他标志有何本质区别?",
"options": [
"A. 通过动态导入加载版本模块",
"B. 零模块加载的快速路径,直接输出 MACRO.VERSION",
"C. 通过 init() 函数初始化后获取",
"D. 需先加载 startupProfiler"
],
"answer": "B",
"explanation": "注释写 zero module loading needed,检测到该标志后直接 console.log(MACRO.VERSION) 返回,MACRO.VERSION 是构建时内联的宏。所有其他路径都先 await import startupProfiler。",
"category": "启动与UI",
"difficulty": "进阶"
},
{
"question": "全局状态 STATE 初始化时 cwd 字段有何特殊处理?",
"options": [
"A. 直接赋值 process.cwd()",
"B. realpathSync 解析符号链接 + .normalize('NFC') Unicode 规范化",
"C. 异步 fs.realpath()",
"D. 从环境变量 PWD 读取"
],
"answer": "B",
"explanation": "用 realpathSync(rawCwd).normalize('NFC') 解析符号链接并做 NFC 规范化,确保与 shell.ts 的 setCwd 行为一致。若 realpathSync 抛出(如 macOS CloudStorage EPERM),回退到直接 .normalize('NFC')。",
"category": "启动与UI",
"difficulty": "进阶"
},
{
"question": "updateLastInteractionTime() 的 immediate 参数有什么作用?",
"options": [
"A. 区分鼠标和键盘事件优先级",
"B. 节流 API 调用",
"C. 默认推迟到下一次 Ink 渲染帧批量处理;immediate=true 用于 useEffect 等回调确保立即更新",
"D. immediate=true 触发强制重新渲染"
],
"answer": "C",
"explanation": "默认将 Date.now() 推迟到下一次 Ink 渲染帧,将多次按键批处理。但在 useEffect 等运行在渲染周期之后的代码中,需要 immediate: true,否则时间戳会停留在旧值。",
"category": "启动与UI",
"difficulty": "高级"
},
{
"question": "ink/instances.ts 文件的核心作用是什么?",
"options": [
"A. 存储所有活跃 React 组件实例",
"B. 以 stdout 流为 key 维护 Map<WriteStream, Ink>,确保复用同一 Ink 实例",
"C. 提供 Ink 渲染器单例工厂",
"D. 跟踪已挂载 DOM 节点总数"
],
"answer": "B",
"explanation": "确保对同一输出流的连续 render() 调用复用同一 Ink 实例。在 root.ts 的 getInstance() 中查找现有实例,存在则复用。该 Map 独立存放避免循环依赖。",
"category": "启动与UI",
"difficulty": "基础"
},
{
"question": "ElementNames 类型定义了哪些自定义宿主元素?",
"options": [
"A. ink-div、ink-span、ink-text、ink-input",
"B. ink-root、ink-box、ink-text、ink-virtual-text、ink-link、ink-progress、ink-raw-ansi",
"C. ink-root、ink-box、ink-text、ink-ansi、ink-color",
"D. ink-container、ink-item、ink-text、ink-virtual-text"
],
"answer": "B",
"explanation": "7 种自定义元素:ink-root、ink-box、ink-text、ink-virtual-text、ink-link、ink-progress、ink-raw-ansi。文本节点单独为 TextName = '#text'。",
"category": "启动与UI",
"difficulty": "基础"
},
{
"question": "Ink 类的 scheduleRender 如何平衡性能与正确性?",
"options": [
"A. setImmediate 在事件循环末尾调度",
"B. requestAnimationFrame 浏览器帧率",
"C. queueMicrotask(onRender) 包装在 lodash throttle 中,间隔 16ms(60fps),leading/trailing 均为 true",
"D. 直接同步调用 onRender"
],
"answer": "C",
"explanation": "throttle(deferredRender, FRAME_INTERVAL_MS=16, { leading: true, trailing: true })。用微任务延迟确保在 useLayoutEffect 执行完毕后再渲染,避免光标位置滞后一帧。测试环境使用同步 onImmediateRender。",
"category": "启动与UI",
"difficulty": "高级"
},
{
"question": "Ink 渲染器使用什么布局引擎?布局计算在哪个阶段触发?",
"options": [
"A. 自研 flexbox,每次按键后重新计算",
"B. CSS Grid 的 Node.js 端口,组件挂载时一次性计算",
"C. Yoga 布局引擎,在 React reconciler 的 resetAfterCommit 回调中(commit 阶段)触发",
"D. 直接字符计数,无布局引擎"
],
"answer": "C",
"explanation": "使用 yoga-layout(Facebook 的 flexbox 实现),rootNode.onComputeLayout 在 reconciler 的 resetAfterCommit 中调用 yogaNode.calculateLayout(terminalColumns)。useLayoutEffect 能访问最新布局数据。",
"category": "启动与UI",
"difficulty": "进阶"
},
{
"question": "vim 普通模式状态机中,operatorTextObj 状态代表什么?",
"options": [
"A. { type: 'textObject'; op: Operator; count: number }",
"B. { type: 'operatorTextObj'; op: Operator; count: number; scope: TextObjScope },表示操作符已输入、已按 i/a,等待文本对象类型",
"C. { type: 'operatorScope'; op: Operator; count: number; dir: 'i' | 'a' }",
"D. { type: 'operatorWait'; op: Operator }"
],
"answer": "B",
"explanation": "operator → [ia] → operatorTextObj 转换路径。TextObjScope 为 'inner' | 'around',对应 TEXT_OBJ_SCOPES 常量中的 { i: 'inner', a: 'around' }。",
"category": "启动与UI",
"difficulty": "进阶"
},
{
"question": "PersistentState 与 VimState 的本质区别是什么?",
"options": [
"A. 两者相同都跟踪当前模式",
"B. PersistentState 跟踪 lastChange/lastFind/register/registerIsLinewise 在命令间持久保存;VimState 记录当前命令状态每次重置",
"C. PersistentState 仅保存撤销历史",
"D. PersistentState 是 VimState 子集"
],
"answer": "B",
"explanation": "PersistentState 是 vim 的「记忆」:lastChange(.点重复)、lastFind(;/,重复)、register(yank/delete内容)、registerIsLinewise(行级标志)。VimState 跟踪瞬时状态:INSERT 的 insertedText 或 NORMAL 的 command 状态机。",
"category": "启动与UI",
"difficulty": "高级"
},
{
"question": "bash 模式是如何在输入中被标识的?",
"options": [
"A. 检查是否包含 shell 语法如 | >",
"B. 独立枚举状态标志,与输入字符串无关",
"C. 输入以 ! 开头识别;prependModeCharacterToInput 加 ! 前缀;getValueFromInput 用 slice(1) 去除",
"D. 输入以 $ 开头识别"
],
"answer": "C",
"explanation": "getModeFromInput 通过 input.startsWith('!') 判断。模式信息编码进字符串前缀,使 useArrowKeyHistory 可通过同一字符串区分 bash 历史和 prompt 历史,无需额外存储结构。",
"category": "启动与UI",
"difficulty": "基础"
},
{
"question": "在 crypto.ts 中,PKCE 的 generateCodeChallenge() 函数使用什么算法将 code verifier 转换为 code challenge?",
"options": ["A. 直接将 code verifier 进行 Base64URL 编码,无需哈希", "B. 对 code verifier 进行 SHA-256 哈希后再做 Base64URL 编码", "C. 对 code verifier 进行 MD5 哈希后再做 Base64 标准编码", "D. 对 code verifier 进行 HMAC-SHA256 签名后编码"],
"answer": "B",
"explanation": "在 src/services/oauth/crypto.ts 的 generateCodeChallenge(verifier) 中,使用 createHash('sha256') 对 verifier 哈希,再调用 base64URLEncode() 编码,对应 RFC 7636 的 code_challenge_method=S256。",
"category": "认证与遥测",
"difficulty": "基础"
},
{
"question": "在 auth-code-listener.ts 中,OAuth 回调服务器收到浏览器请求后将 ServerResponse 存入 pendingResponse 的目的是什么?",
"options": ["A. 允许服务器在同一连接上接收多个授权码", "B. 让调用方先完成令牌交换,再决定将浏览器重定向到哪个成功/错误页面", "C. 保持连接以便通过 HTTP 长轮询传递令牌", "D. 等待服务端推送通知确认令牌有效"],
"answer": "B",
"explanation": "在 validateAndRespond() 中校验 state(CSRF 防护)后,将 res 存入 pendingResponse 并 resolve Promise。实际 302 跳转由后续的 handleSuccessRedirect/handleErrorRedirect 发出,让调用方可先用 code 换取 token。",
"category": "认证与遥测",
"difficulty": "进阶"
},
{
"question": "refreshOAuthToken() 中跳过 /api/oauth/profile 请求(节省约 700 万次/天)的触发条件是什么?",
"options": ["A. access token 剩余有效期超过 1 小时", "B. global config 的 billingType/accountCreatedAt/subscriptionCreatedAt 均已定义,且 secure storage 中 subscriptionType 和 rateLimitTier 非 null", "C. 用户在 30 分钟内刚完成过登录", "D. 订阅类型为 enterprise 的用户永远跳过"],
"answer": "B",
"explanation": "haveProfileAlready 同时检查五个条件:config 中三个字段加上 secure storage 中两个字段。必须检查 secure storage 因为 CLAUDE_CODE_OAUTH_REFRESH_TOKEN 重登录路径会调用 performLogout() 清空 secure storage。",
"category": "认证与遥测",
"difficulty": "高级"
},
{
"question": "macOsKeychainStorage.ts 中 SECURITY_STDIN_LINE_LIMIT = 4032 (4096-64) 超出后会怎样?",
"options": ["A. 拒绝写入并抛出错误", "B. 回退到 argv 模式(命令行传 hex 参数)并记录 warn 日志", "C. 自动压缩数据后重试 stdin 模式", "D. 改为写入 plainTextStorage"],
"answer": "B",
"explanation": "优先使用 security -i stdin 模式以免进程监控软件(如 CrowdStrike)看到凭证。超过 4032 字节时回退到 argv 模式 execaSync('security', [..., '-X', hexValue]) 并记录 warn。4032 来自 fgets() 缓冲区 4096 减去 64 字节安全余量。",
"category": "认证与遥测",
"difficulty": "进阶"
},
{
"question": "keychainPrefetch.ts 中预取超时(timedOut=true)时为什么不将 null 写入缓存?",
"options": ["A. 将 null 写入缓存标记为无凭证让后续快速失败", "B. 不写入缓存让后续同步 read() 重新尝试,因为超时意味着 keychain 可能有我们未能获取的 key", "C. 写入上次旧缓存值", "D. 触发同步重试最多等待 10 秒"],
"answer": "B",
"explanation": "超时意味着 keychain 可能有未获取的 key,若此时缓存 null 同步路径就会错误地以为没有凭证。保持 cachedAt===0 的未初始化状态让后续调用重新尝试。KEYCHAIN_PREFETCH_TIMEOUT_MS = 10000。",
"category": "认证与遥测",
"difficulty": "高级"
},
{
"question": "sinkKillswitch.ts 中控制分析 sink 紧急关闭的 GrowthBook 配置名称是什么?",
"options": ["A. tengu_analytics_killswitch", "B. tengu_frond_boric", "C. tengu_sink_disable", "D. tengu_log_datadog_events"],
"answer": "B",
"explanation": "SINK_KILLSWITCH_CONFIG_NAME = 'tengu_frond_boric',注释写道 Mangled name: per-sink analytics killswitch。isSinkKilled() 读取该配置的 { datadog?: boolean, firstParty?: boolean },为 true 时停止分发事件。",
"category": "认证与遥测",
"difficulty": "进阶"
},
{
"question": "AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS 的 TypeScript 类型声明为 never,实践中起什么作用?",
"options": ["A. 运行时调用 sanitize 函数检查内容", "B. 无法直接赋值,强制所有使用处通过 as 断言显式标注,在代码审查中留下人工核查痕迹", "C. 由自定义 ESLint 插件在编译时拦截", "D. 在生产构建中被 tree-shaking 移除"],
"answer": "B",