@dadado/agent-kit-cli 4.8.3 → 4.8.8
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/README.md +59 -0
- package/dashboard/dashboard-data.mjs +235 -77
- package/dashboard/dashboard.html +1789 -443
- package/dashboard/lib/semantic-model.mjs +173 -22
- package/dashboard/lib/terminal-snapshot.mjs +127 -0
- package/dist/index.js +1190 -333
- package/package.json +1 -1
package/dashboard/dashboard.html
CHANGED
|
@@ -67,6 +67,8 @@ html[data-dashboard-skin="legacy"] {
|
|
|
67
67
|
--mc-space-xl: 16px;
|
|
68
68
|
--mc-space-2xl: 24px;
|
|
69
69
|
--mc-card-padding: 16px;
|
|
70
|
+
/* Dense list-card padding (agent/command/skill/process cards, health rows, recent plans). */
|
|
71
|
+
--mc-card-padding-dense: 10px 12px;
|
|
70
72
|
--mc-header-pad-x: 24px;
|
|
71
73
|
/* Trailing pad tighter so More (...) sits closer to the IDE ellipsis column. */
|
|
72
74
|
--mc-header-pad-x-end: 8px;
|
|
@@ -135,6 +137,8 @@ a:focus-visible,
|
|
|
135
137
|
.top-nav-anchor:focus-visible,
|
|
136
138
|
.header-brand:focus-visible,
|
|
137
139
|
.header-home-btn:focus-visible,
|
|
140
|
+
.header-fullscreen-btn:focus-visible,
|
|
141
|
+
.fullscreen-exit-btn:focus-visible,
|
|
138
142
|
.section-home-back:focus-visible,
|
|
139
143
|
.nav-more-btn:focus-visible,
|
|
140
144
|
.nav-more-item:focus-visible,
|
|
@@ -343,6 +347,75 @@ a:focus-visible,
|
|
|
343
347
|
cursor: not-allowed;
|
|
344
348
|
}
|
|
345
349
|
|
|
350
|
+
/* ===== Fullscreen (zen) viewport mode =====
|
|
351
|
+
Hides the 32px app header so the grid/stack reclaims the full panel height.
|
|
352
|
+
Enter control lives in the header; a floating exit control (or Escape) restores chrome. */
|
|
353
|
+
.header-fullscreen-btn {
|
|
354
|
+
background: transparent;
|
|
355
|
+
border: none;
|
|
356
|
+
color: var(--text-secondary);
|
|
357
|
+
width: var(--mc-header-control-size);
|
|
358
|
+
height: var(--mc-header-control-size);
|
|
359
|
+
padding: 0;
|
|
360
|
+
border-radius: var(--mc-radius-chrome);
|
|
361
|
+
cursor: pointer;
|
|
362
|
+
font-family: inherit;
|
|
363
|
+
transition: background 0.2s, color 0.2s, opacity 0.2s;
|
|
364
|
+
flex-shrink: 0;
|
|
365
|
+
display: inline-flex;
|
|
366
|
+
align-items: center;
|
|
367
|
+
justify-content: center;
|
|
368
|
+
}
|
|
369
|
+
.header-fullscreen-btn:hover {
|
|
370
|
+
background: var(--bg-card-hover);
|
|
371
|
+
color: var(--text-primary);
|
|
372
|
+
}
|
|
373
|
+
.header-fullscreen-btn svg {
|
|
374
|
+
width: var(--mc-chrome-icon-size);
|
|
375
|
+
height: var(--mc-chrome-icon-size);
|
|
376
|
+
display: block;
|
|
377
|
+
flex-shrink: 0;
|
|
378
|
+
}
|
|
379
|
+
.fullscreen-exit-btn {
|
|
380
|
+
position: fixed;
|
|
381
|
+
top: var(--mc-space-md);
|
|
382
|
+
right: var(--mc-space-md);
|
|
383
|
+
z-index: 70;
|
|
384
|
+
width: var(--mc-header-control-size);
|
|
385
|
+
height: var(--mc-header-control-size);
|
|
386
|
+
padding: 0;
|
|
387
|
+
display: inline-flex;
|
|
388
|
+
align-items: center;
|
|
389
|
+
justify-content: center;
|
|
390
|
+
background: var(--bg-secondary);
|
|
391
|
+
border: 1px solid var(--border);
|
|
392
|
+
border-radius: var(--mc-radius-chrome);
|
|
393
|
+
color: var(--text-secondary);
|
|
394
|
+
cursor: pointer;
|
|
395
|
+
font-family: inherit;
|
|
396
|
+
transition: background 0.2s, color 0.2s;
|
|
397
|
+
}
|
|
398
|
+
.fullscreen-exit-btn:hover {
|
|
399
|
+
background: var(--bg-card-hover);
|
|
400
|
+
color: var(--text-primary);
|
|
401
|
+
}
|
|
402
|
+
.fullscreen-exit-btn[hidden] {
|
|
403
|
+
display: none;
|
|
404
|
+
}
|
|
405
|
+
.fullscreen-exit-btn svg {
|
|
406
|
+
width: var(--mc-chrome-icon-size);
|
|
407
|
+
height: var(--mc-chrome-icon-size);
|
|
408
|
+
display: block;
|
|
409
|
+
flex-shrink: 0;
|
|
410
|
+
}
|
|
411
|
+
body.mc-fullscreen .header {
|
|
412
|
+
display: none;
|
|
413
|
+
}
|
|
414
|
+
/* Reserve trailing space so the fixed exit control does not cover top-tabs. */
|
|
415
|
+
body.mc-fullscreen .top-tabs-row {
|
|
416
|
+
padding-right: calc(var(--mc-header-control-size) + var(--mc-space-md) * 2 + var(--mc-space-sm));
|
|
417
|
+
}
|
|
418
|
+
|
|
346
419
|
/* ===== Layout ===== */
|
|
347
420
|
.main {
|
|
348
421
|
display: flex;
|
|
@@ -503,6 +576,9 @@ a:focus-visible,
|
|
|
503
576
|
background: var(--border);
|
|
504
577
|
}
|
|
505
578
|
.nav-more-group-label {
|
|
579
|
+
display: flex;
|
|
580
|
+
align-items: center;
|
|
581
|
+
gap: var(--mc-chrome-label-gap);
|
|
506
582
|
padding: 6px 10px 2px;
|
|
507
583
|
font-size: var(--mc-chrome-subtitle-size);
|
|
508
584
|
font-weight: var(--mc-chrome-subtitle-weight);
|
|
@@ -623,6 +699,7 @@ a:focus-visible,
|
|
|
623
699
|
.dot-blue { background: var(--blue); box-shadow: 0 0 6px var(--blue-bg); }
|
|
624
700
|
.dot-purple { background: var(--purple); box-shadow: 0 0 6px var(--purple-bg); }
|
|
625
701
|
.dot-cyan { background: var(--cyan); box-shadow: 0 0 6px var(--cyan-bg); }
|
|
702
|
+
.dot-orange { background: var(--orange); box-shadow: 0 0 6px var(--orange-bg); }
|
|
626
703
|
.dot-gray { background: #3a4a5a; box-shadow: 0 0 6px rgba(58,74,90,0.3); }
|
|
627
704
|
.dot-pulse::after {
|
|
628
705
|
content: '';
|
|
@@ -636,6 +713,8 @@ a:focus-visible,
|
|
|
636
713
|
.dot-green.dot-pulse::after { border-color: var(--green); }
|
|
637
714
|
.dot-blue.dot-pulse::after { border-color: var(--blue); }
|
|
638
715
|
.dot-yellow.dot-pulse::after { border-color: var(--yellow); }
|
|
716
|
+
.dot-red.dot-pulse::after { border-color: var(--red); }
|
|
717
|
+
.dot-orange.dot-pulse::after { border-color: var(--orange); }
|
|
639
718
|
@keyframes pulse {
|
|
640
719
|
0% { opacity: 0.6; transform: scale(1); }
|
|
641
720
|
100% { opacity: 0; transform: scale(2.5); }
|
|
@@ -756,29 +835,66 @@ a:focus-visible,
|
|
|
756
835
|
outline-offset: 1px;
|
|
757
836
|
}
|
|
758
837
|
|
|
759
|
-
/* =====
|
|
760
|
-
.agent-
|
|
761
|
-
|
|
838
|
+
/* ===== Agents ===== */
|
|
839
|
+
.agent-grid {
|
|
840
|
+
display: grid;
|
|
841
|
+
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
842
|
+
gap: 10px;
|
|
843
|
+
}
|
|
844
|
+
.agent-card {
|
|
845
|
+
display: flex;
|
|
846
|
+
flex-direction: column;
|
|
847
|
+
gap: 6px;
|
|
848
|
+
padding: var(--mc-card-padding-dense);
|
|
849
|
+
border-radius: var(--mc-radius);
|
|
850
|
+
background: var(--bg-card);
|
|
851
|
+
border: 1px solid var(--border);
|
|
852
|
+
}
|
|
853
|
+
.agent-card:hover { border-color: var(--border-active); }
|
|
854
|
+
.agent-card-head {
|
|
762
855
|
display: flex;
|
|
763
856
|
align-items: center;
|
|
857
|
+
justify-content: space-between;
|
|
764
858
|
gap: 8px;
|
|
765
|
-
padding: 8px 10px;
|
|
766
|
-
border-radius: 6px;
|
|
767
|
-
font-size: 13px;
|
|
768
|
-
color: var(--text-secondary);
|
|
769
|
-
transition: all 0.15s;
|
|
770
859
|
}
|
|
771
|
-
.agent-
|
|
772
|
-
|
|
773
|
-
|
|
860
|
+
.agent-name {
|
|
861
|
+
display: inline-flex;
|
|
862
|
+
align-items: center;
|
|
863
|
+
gap: 6px;
|
|
864
|
+
font-size: 12px;
|
|
865
|
+
color: var(--text-primary);
|
|
866
|
+
font-family: var(--mc-font-mono);
|
|
867
|
+
}
|
|
868
|
+
.agent-monogram {
|
|
869
|
+
width: 18px;
|
|
870
|
+
height: 18px;
|
|
774
871
|
border-radius: 4px;
|
|
775
|
-
display: flex;
|
|
872
|
+
display: inline-flex;
|
|
776
873
|
align-items: center;
|
|
777
874
|
justify-content: center;
|
|
778
|
-
font-size:
|
|
875
|
+
font-size: 9px;
|
|
779
876
|
font-weight: 700;
|
|
877
|
+
background: var(--bg-card-hover);
|
|
878
|
+
color: var(--text-secondary);
|
|
780
879
|
flex-shrink: 0;
|
|
781
880
|
}
|
|
881
|
+
.agent-desc {
|
|
882
|
+
font-size: 11px;
|
|
883
|
+
color: var(--text-secondary);
|
|
884
|
+
line-height: 1.4;
|
|
885
|
+
display: -webkit-box;
|
|
886
|
+
-webkit-line-clamp: 2;
|
|
887
|
+
-webkit-box-orient: vertical;
|
|
888
|
+
overflow: hidden;
|
|
889
|
+
}
|
|
890
|
+
.agent-file {
|
|
891
|
+
font-size: 10px;
|
|
892
|
+
color: var(--text-muted);
|
|
893
|
+
font-family: var(--mc-font-mono);
|
|
894
|
+
word-break: break-all;
|
|
895
|
+
}
|
|
896
|
+
.agent-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
|
|
897
|
+
.agent-create-btn { margin-left: auto; }
|
|
782
898
|
|
|
783
899
|
/* ===== Terminal List ===== */
|
|
784
900
|
.terminal-list { display: flex; flex-direction: column; gap: 4px; }
|
|
@@ -800,25 +916,160 @@ a:focus-visible,
|
|
|
800
916
|
}
|
|
801
917
|
.terminal-item:hover { background: var(--bg-card-hover); }
|
|
802
918
|
|
|
803
|
-
/* =====
|
|
804
|
-
.
|
|
919
|
+
/* ===== Healthcenter ===== */
|
|
920
|
+
.healthcenter {
|
|
921
|
+
display: flex;
|
|
922
|
+
flex-direction: column;
|
|
923
|
+
gap: 10px;
|
|
924
|
+
}
|
|
925
|
+
.healthcenter-presence {
|
|
926
|
+
display: flex;
|
|
927
|
+
align-items: center;
|
|
928
|
+
gap: 8px;
|
|
929
|
+
font-size: 11px;
|
|
930
|
+
color: var(--text-muted);
|
|
931
|
+
}
|
|
932
|
+
/* Presence liveness rides the shared .dot-pulse::after halo; no element-level
|
|
933
|
+
pulse (it inflated the solid dot 250% and faded it out each cycle). */
|
|
934
|
+
.health-vitals {
|
|
805
935
|
display: grid;
|
|
806
|
-
grid-template-columns: repeat(auto-fill, minmax(
|
|
936
|
+
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
|
807
937
|
gap: 8px;
|
|
808
938
|
}
|
|
809
|
-
.health-
|
|
939
|
+
.health-vital-card {
|
|
940
|
+
display: flex;
|
|
941
|
+
flex-direction: column;
|
|
942
|
+
gap: 6px;
|
|
943
|
+
padding: var(--mc-card-padding-dense);
|
|
944
|
+
border: 1px solid var(--border);
|
|
945
|
+
border-radius: var(--mc-radius);
|
|
946
|
+
background: var(--bg-card);
|
|
947
|
+
}
|
|
948
|
+
.health-vital-card[data-state="attention"] {
|
|
949
|
+
border-color: var(--border-active);
|
|
950
|
+
}
|
|
951
|
+
.health-vital-name {
|
|
952
|
+
font-size: 12px;
|
|
953
|
+
font-weight: 600;
|
|
954
|
+
color: var(--text-primary);
|
|
955
|
+
}
|
|
956
|
+
.health-vital-score {
|
|
957
|
+
font-size: 11px;
|
|
958
|
+
color: var(--text-muted);
|
|
959
|
+
}
|
|
960
|
+
.health-vital-state {
|
|
810
961
|
display: flex;
|
|
811
962
|
align-items: center;
|
|
963
|
+
gap: 6px;
|
|
964
|
+
font-size: 11px;
|
|
965
|
+
}
|
|
966
|
+
.health-vital-card[data-state="pass"] .health-vital-state { color: var(--green); }
|
|
967
|
+
.health-vital-card[data-state="attention"] .health-vital-state { color: var(--red); }
|
|
968
|
+
.health-group {
|
|
969
|
+
display: flex;
|
|
970
|
+
flex-direction: column;
|
|
812
971
|
gap: 8px;
|
|
813
|
-
|
|
814
|
-
|
|
972
|
+
}
|
|
973
|
+
.health-group-title {
|
|
974
|
+
font-size: 11px;
|
|
975
|
+
text-transform: uppercase;
|
|
976
|
+
letter-spacing: 0.04em;
|
|
977
|
+
color: var(--text-muted);
|
|
978
|
+
}
|
|
979
|
+
.health-grid {
|
|
980
|
+
display: grid;
|
|
981
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
982
|
+
gap: 8px;
|
|
983
|
+
}
|
|
984
|
+
.health-card {
|
|
985
|
+
display: flex;
|
|
986
|
+
flex-direction: column;
|
|
987
|
+
gap: 0;
|
|
988
|
+
border-radius: var(--mc-radius);
|
|
815
989
|
background: var(--bg-card);
|
|
816
990
|
border: 1px solid var(--border);
|
|
991
|
+
overflow: hidden;
|
|
992
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
993
|
+
}
|
|
994
|
+
.health-card:hover,
|
|
995
|
+
.health-card.is-expanded {
|
|
996
|
+
border-color: var(--border-active);
|
|
997
|
+
}
|
|
998
|
+
.health-item {
|
|
999
|
+
display: flex;
|
|
1000
|
+
align-items: center;
|
|
1001
|
+
gap: 8px;
|
|
1002
|
+
padding: var(--mc-card-padding-dense);
|
|
817
1003
|
font-size: 12px;
|
|
818
1004
|
color: var(--text-secondary);
|
|
819
|
-
|
|
1005
|
+
cursor: pointer;
|
|
1006
|
+
background: transparent;
|
|
1007
|
+
border: none;
|
|
1008
|
+
width: 100%;
|
|
1009
|
+
text-align: left;
|
|
1010
|
+
font-family: inherit;
|
|
1011
|
+
transition: opacity 0.3s ease, transform 0.3s ease, background 0.15s ease;
|
|
1012
|
+
}
|
|
1013
|
+
.health-item:hover { background: var(--bg-card-hover); }
|
|
1014
|
+
.health-item-label {
|
|
1015
|
+
flex: 1;
|
|
1016
|
+
min-width: 0;
|
|
1017
|
+
color: var(--text-primary);
|
|
1018
|
+
}
|
|
1019
|
+
.health-item-sev {
|
|
1020
|
+
font-size: 10px;
|
|
1021
|
+
text-transform: uppercase;
|
|
1022
|
+
letter-spacing: 0.04em;
|
|
1023
|
+
color: var(--text-muted);
|
|
1024
|
+
flex-shrink: 0;
|
|
1025
|
+
}
|
|
1026
|
+
.health-item-sev[data-sev="ok"] { color: var(--green); }
|
|
1027
|
+
.health-item-sev[data-sev="warning"] { color: var(--yellow); }
|
|
1028
|
+
.health-item-sev[data-sev="degraded"] { color: var(--orange); }
|
|
1029
|
+
.health-item-sev[data-sev="error"] { color: var(--red); }
|
|
1030
|
+
.health-item-chevron {
|
|
1031
|
+
color: var(--text-muted);
|
|
1032
|
+
font-size: 10px;
|
|
1033
|
+
transition: transform 0.15s ease;
|
|
1034
|
+
flex-shrink: 0;
|
|
1035
|
+
}
|
|
1036
|
+
.health-card.is-expanded .health-item-chevron {
|
|
1037
|
+
transform: rotate(180deg);
|
|
1038
|
+
}
|
|
1039
|
+
.health-detail {
|
|
1040
|
+
display: none;
|
|
1041
|
+
padding: 0 12px 12px;
|
|
1042
|
+
border-top: 1px solid var(--border);
|
|
1043
|
+
background: var(--bg-secondary);
|
|
1044
|
+
}
|
|
1045
|
+
.health-card.is-expanded .health-detail {
|
|
1046
|
+
display: block;
|
|
1047
|
+
animation: fadeIn 0.2s ease;
|
|
1048
|
+
}
|
|
1049
|
+
.health-detail-copy {
|
|
1050
|
+
font-size: 11px;
|
|
1051
|
+
color: var(--text-secondary);
|
|
1052
|
+
margin: 10px 0 8px;
|
|
1053
|
+
line-height: 1.4;
|
|
1054
|
+
}
|
|
1055
|
+
.health-detail-actions {
|
|
1056
|
+
display: flex;
|
|
1057
|
+
flex-wrap: wrap;
|
|
1058
|
+
gap: 6px;
|
|
1059
|
+
}
|
|
1060
|
+
.health-autofix-btn {
|
|
1061
|
+
font-size: 11px;
|
|
1062
|
+
padding: 4px 10px;
|
|
1063
|
+
border-radius: 4px;
|
|
1064
|
+
border: 1px solid var(--border-active);
|
|
1065
|
+
background: var(--bg-card);
|
|
1066
|
+
color: var(--text-primary);
|
|
1067
|
+
cursor: pointer;
|
|
1068
|
+
font-family: inherit;
|
|
1069
|
+
}
|
|
1070
|
+
.health-autofix-btn:hover {
|
|
1071
|
+
background: var(--bg-card-hover);
|
|
820
1072
|
}
|
|
821
|
-
.health-item:hover { border-color: var(--border-active); }
|
|
822
1073
|
|
|
823
1074
|
/* ===== Git ===== */
|
|
824
1075
|
.git-info {
|
|
@@ -836,6 +1087,98 @@ a:focus-visible,
|
|
|
836
1087
|
.git-label { color: var(--text-muted); }
|
|
837
1088
|
.git-value { color: var(--text-secondary); font-family: var(--mc-font-mono); font-size: 12px; }
|
|
838
1089
|
|
|
1090
|
+
/* Git tab v2: promotion flow lanes, readable graph, staging hygiene */
|
|
1091
|
+
.git-flow {
|
|
1092
|
+
display: flex;
|
|
1093
|
+
flex-direction: column;
|
|
1094
|
+
gap: 6px;
|
|
1095
|
+
margin-bottom: 12px;
|
|
1096
|
+
}
|
|
1097
|
+
.git-flow-row {
|
|
1098
|
+
display: flex;
|
|
1099
|
+
align-items: center;
|
|
1100
|
+
gap: 10px;
|
|
1101
|
+
font-size: 12px;
|
|
1102
|
+
padding: 8px 10px;
|
|
1103
|
+
border: 1px solid var(--border);
|
|
1104
|
+
border-radius: 8px;
|
|
1105
|
+
background: var(--bg-card);
|
|
1106
|
+
flex-wrap: wrap;
|
|
1107
|
+
}
|
|
1108
|
+
.git-flow-lane {
|
|
1109
|
+
font-weight: 600;
|
|
1110
|
+
color: var(--text-primary);
|
|
1111
|
+
flex-shrink: 0;
|
|
1112
|
+
}
|
|
1113
|
+
.git-flow-arrow { color: var(--text-muted); flex-shrink: 0; }
|
|
1114
|
+
.git-flow-state {
|
|
1115
|
+
display: inline-flex;
|
|
1116
|
+
align-items: center;
|
|
1117
|
+
gap: 6px;
|
|
1118
|
+
color: var(--text-secondary);
|
|
1119
|
+
flex: 1;
|
|
1120
|
+
min-width: 0;
|
|
1121
|
+
}
|
|
1122
|
+
.git-flow-badge {
|
|
1123
|
+
font-size: 10px;
|
|
1124
|
+
font-weight: 600;
|
|
1125
|
+
padding: 1px 8px;
|
|
1126
|
+
border-radius: 999px;
|
|
1127
|
+
border: 1px solid var(--border);
|
|
1128
|
+
color: var(--text-secondary);
|
|
1129
|
+
background: var(--bg-secondary);
|
|
1130
|
+
font-family: var(--mc-font-mono);
|
|
1131
|
+
flex-shrink: 0;
|
|
1132
|
+
}
|
|
1133
|
+
.git-flow-badge.is-ahead { color: var(--yellow); border-color: rgba(234,179,8,0.3); }
|
|
1134
|
+
.git-flow-badge.is-behind { color: var(--orange); border-color: rgba(249,115,22,0.3); }
|
|
1135
|
+
.git-flow-badge.is-sync { color: var(--green); border-color: rgba(34,197,94,0.3); }
|
|
1136
|
+
.git-flow-cta { margin-left: auto; flex-shrink: 0; }
|
|
1137
|
+
.git-graph {
|
|
1138
|
+
margin: 0;
|
|
1139
|
+
padding: 10px 12px;
|
|
1140
|
+
border: 1px solid var(--border);
|
|
1141
|
+
border-radius: 8px;
|
|
1142
|
+
background: var(--bg-secondary);
|
|
1143
|
+
font-family: var(--mc-font-mono);
|
|
1144
|
+
font-size: 11px;
|
|
1145
|
+
line-height: 1.5;
|
|
1146
|
+
color: var(--text-secondary);
|
|
1147
|
+
overflow-x: hidden;
|
|
1148
|
+
white-space: pre-wrap;
|
|
1149
|
+
word-break: break-all;
|
|
1150
|
+
max-height: 420px;
|
|
1151
|
+
overflow-y: auto;
|
|
1152
|
+
}
|
|
1153
|
+
.git-graph-title {
|
|
1154
|
+
font-size: 12px;
|
|
1155
|
+
font-weight: 600;
|
|
1156
|
+
color: var(--text-secondary);
|
|
1157
|
+
margin: 12px 0 6px;
|
|
1158
|
+
}
|
|
1159
|
+
.git-hygiene {
|
|
1160
|
+
display: flex;
|
|
1161
|
+
align-items: flex-start;
|
|
1162
|
+
gap: 10px;
|
|
1163
|
+
font-size: 12px;
|
|
1164
|
+
padding: 10px 12px;
|
|
1165
|
+
border: 1px solid rgba(234,179,8,0.35);
|
|
1166
|
+
border-radius: 8px;
|
|
1167
|
+
background: var(--yellow-bg);
|
|
1168
|
+
color: var(--text-secondary);
|
|
1169
|
+
margin-bottom: 12px;
|
|
1170
|
+
flex-wrap: wrap;
|
|
1171
|
+
}
|
|
1172
|
+
.git-hygiene-body { flex: 1; min-width: 200px; }
|
|
1173
|
+
.git-hygiene-title { font-weight: 600; color: var(--yellow); margin-bottom: 2px; }
|
|
1174
|
+
.git-hygiene-paths {
|
|
1175
|
+
font-family: var(--mc-font-mono);
|
|
1176
|
+
font-size: 11px;
|
|
1177
|
+
color: var(--text-muted);
|
|
1178
|
+
margin-top: 4px;
|
|
1179
|
+
word-break: break-all;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
839
1182
|
/* ===== Memory ===== */
|
|
840
1183
|
.memory-list {
|
|
841
1184
|
display: flex;
|
|
@@ -849,9 +1192,94 @@ a:focus-visible,
|
|
|
849
1192
|
padding: 6px 10px;
|
|
850
1193
|
border-radius: 6px;
|
|
851
1194
|
font-size: 12px;
|
|
852
|
-
color: var(--text-secondary);
|
|
1195
|
+
color: var(--text-secondary);
|
|
1196
|
+
}
|
|
1197
|
+
.memory-item:hover { background: var(--bg-card-hover); }
|
|
1198
|
+
|
|
1199
|
+
/* Memory tab v2: error-o-meter KPIs, green/red icon panels, interactive error rows */
|
|
1200
|
+
.memory-kpi-grid {
|
|
1201
|
+
display: grid;
|
|
1202
|
+
grid-template-columns: repeat(4, 1fr);
|
|
1203
|
+
gap: var(--mc-space-xl);
|
|
1204
|
+
margin-bottom: var(--mc-space-xl);
|
|
1205
|
+
}
|
|
1206
|
+
@media (max-width: 900px) { .memory-kpi-grid { grid-template-columns: 1fr 1fr; } }
|
|
1207
|
+
.memory-kpi {
|
|
1208
|
+
background: var(--bg-card);
|
|
1209
|
+
border: 1px solid var(--border);
|
|
1210
|
+
border-radius: var(--mc-radius-lg);
|
|
1211
|
+
padding: var(--mc-space-lg) var(--mc-card-padding);
|
|
1212
|
+
}
|
|
1213
|
+
.memory-kpi-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }
|
|
1214
|
+
.memory-kpi-value { font-size: 24px; font-weight: 700; color: var(--text-primary); margin-top: 4px; }
|
|
1215
|
+
.memory-kpi-sub { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
|
|
1216
|
+
.memory-tag-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
|
|
1217
|
+
.memory-tag {
|
|
1218
|
+
font-size: 11px;
|
|
1219
|
+
padding: 2px 8px;
|
|
1220
|
+
border-radius: 999px;
|
|
1221
|
+
background: var(--red-bg);
|
|
1222
|
+
color: var(--red);
|
|
1223
|
+
font-family: var(--mc-font-mono);
|
|
1224
|
+
}
|
|
1225
|
+
.memory-panel { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--mc-radius-lg); padding: var(--mc-card-padding); }
|
|
1226
|
+
.memory-panel-header { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
|
|
1227
|
+
.memory-panel-icon {
|
|
1228
|
+
display: inline-flex;
|
|
1229
|
+
align-items: center;
|
|
1230
|
+
justify-content: center;
|
|
1231
|
+
width: 22px;
|
|
1232
|
+
height: 22px;
|
|
1233
|
+
border-radius: 6px;
|
|
1234
|
+
font-size: 13px;
|
|
1235
|
+
flex-shrink: 0;
|
|
1236
|
+
}
|
|
1237
|
+
.memory-panel-icon-green { color: var(--green); background: var(--green-bg); }
|
|
1238
|
+
.memory-panel-icon-red { color: var(--red); background: var(--red-bg); }
|
|
1239
|
+
.memory-panel-title { font-size: 13px; font-weight: 600; color: var(--text-primary); }
|
|
1240
|
+
.memory-panel-sub { font-size: 11px; color: var(--text-muted); margin-left: auto; }
|
|
1241
|
+
.memory-error-card {
|
|
1242
|
+
border: 1px solid var(--border);
|
|
1243
|
+
border-radius: var(--mc-radius);
|
|
1244
|
+
margin-bottom: 6px;
|
|
1245
|
+
overflow: hidden;
|
|
1246
|
+
}
|
|
1247
|
+
.memory-error-head {
|
|
1248
|
+
display: flex;
|
|
1249
|
+
align-items: center;
|
|
1250
|
+
gap: 8px;
|
|
1251
|
+
width: 100%;
|
|
1252
|
+
padding: 8px 10px;
|
|
1253
|
+
background: transparent;
|
|
1254
|
+
border: none;
|
|
1255
|
+
cursor: pointer;
|
|
1256
|
+
text-align: left;
|
|
1257
|
+
color: var(--text-secondary);
|
|
1258
|
+
font-size: 12px;
|
|
1259
|
+
}
|
|
1260
|
+
.memory-error-head:hover { background: var(--bg-card-hover); }
|
|
1261
|
+
.memory-error-head:focus-visible { outline: 2px solid var(--accent, var(--green)); outline-offset: -2px; }
|
|
1262
|
+
.memory-error-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-primary); }
|
|
1263
|
+
.memory-error-date { font-size: 11px; color: var(--text-muted); font-family: var(--mc-font-mono); flex-shrink: 0; }
|
|
1264
|
+
.memory-error-chevron { transition: transform 0.15s ease; color: var(--text-muted); flex-shrink: 0; }
|
|
1265
|
+
.memory-error-card.is-expanded .memory-error-chevron { transform: rotate(90deg); }
|
|
1266
|
+
.memory-error-detail { display: none; padding: 10px 12px; border-top: 1px solid var(--border); font-size: 12px; }
|
|
1267
|
+
.memory-error-card.is-expanded .memory-error-detail { display: block; }
|
|
1268
|
+
.memory-error-field { margin-bottom: 8px; }
|
|
1269
|
+
.memory-error-field-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 2px; }
|
|
1270
|
+
.memory-error-field-body { color: var(--text-secondary); white-space: pre-wrap; word-break: break-word; }
|
|
1271
|
+
.memory-error-files { font-family: var(--mc-font-mono); font-size: 11px; color: var(--text-muted); word-break: break-word; }
|
|
1272
|
+
.memory-error-actions { display: flex; gap: 8px; margin-top: 10px; }
|
|
1273
|
+
.memory-error-copy-btn {
|
|
1274
|
+
font-size: 12px;
|
|
1275
|
+
padding: 5px 12px;
|
|
1276
|
+
border-radius: 6px;
|
|
1277
|
+
border: 1px solid var(--border);
|
|
1278
|
+
background: var(--bg-elevated, var(--bg-card));
|
|
1279
|
+
color: var(--text-primary);
|
|
1280
|
+
cursor: pointer;
|
|
853
1281
|
}
|
|
854
|
-
.memory-
|
|
1282
|
+
.memory-error-copy-btn:hover { background: var(--bg-card-hover); }
|
|
855
1283
|
|
|
856
1284
|
/* ===== Activity Timeline ===== */
|
|
857
1285
|
.activity-list {
|
|
@@ -937,22 +1365,46 @@ a:focus-visible,
|
|
|
937
1365
|
/* ===== Commands ===== */
|
|
938
1366
|
.command-grid {
|
|
939
1367
|
display: grid;
|
|
940
|
-
grid-template-columns: repeat(auto-fill, minmax(
|
|
941
|
-
gap:
|
|
1368
|
+
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
1369
|
+
gap: 10px;
|
|
942
1370
|
}
|
|
943
|
-
.command-
|
|
1371
|
+
.command-card {
|
|
944
1372
|
display: flex;
|
|
945
|
-
|
|
1373
|
+
flex-direction: column;
|
|
946
1374
|
gap: 6px;
|
|
947
|
-
padding:
|
|
948
|
-
border-radius:
|
|
1375
|
+
padding: var(--mc-card-padding-dense);
|
|
1376
|
+
border-radius: var(--mc-radius);
|
|
949
1377
|
background: var(--bg-card);
|
|
950
1378
|
border: 1px solid var(--border);
|
|
1379
|
+
}
|
|
1380
|
+
.command-card:hover { border-color: var(--border-active); }
|
|
1381
|
+
.command-card-head {
|
|
1382
|
+
display: flex;
|
|
1383
|
+
align-items: center;
|
|
1384
|
+
justify-content: space-between;
|
|
1385
|
+
gap: 8px;
|
|
1386
|
+
}
|
|
1387
|
+
.command-name {
|
|
951
1388
|
font-size: 12px;
|
|
952
|
-
color: var(--text-
|
|
1389
|
+
color: var(--text-primary);
|
|
1390
|
+
font-family: var(--mc-font-mono);
|
|
1391
|
+
}
|
|
1392
|
+
.command-lock, .skill-lock, .agent-lock {
|
|
1393
|
+
display: inline-flex;
|
|
1394
|
+
align-items: center;
|
|
1395
|
+
gap: 4px;
|
|
1396
|
+
font-size: 10px;
|
|
1397
|
+
color: var(--text-muted);
|
|
1398
|
+
}
|
|
1399
|
+
.command-lock svg, .skill-lock svg, .agent-lock svg { display: block; }
|
|
1400
|
+
.command-file {
|
|
1401
|
+
font-size: 10px;
|
|
1402
|
+
color: var(--text-muted);
|
|
953
1403
|
font-family: var(--mc-font-mono);
|
|
1404
|
+
word-break: break-all;
|
|
954
1405
|
}
|
|
955
|
-
.command-
|
|
1406
|
+
.command-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
|
|
1407
|
+
.command-create-btn { margin-left: auto; }
|
|
956
1408
|
|
|
957
1409
|
/* ===== Loading ===== */
|
|
958
1410
|
.loading {
|
|
@@ -984,7 +1436,7 @@ a:focus-visible,
|
|
|
984
1436
|
gap: var(--mc-chrome-label-gap);
|
|
985
1437
|
letter-spacing: 0;
|
|
986
1438
|
}
|
|
987
|
-
|
|
1439
|
+
/* Section titles carry no dots: identity comes from the label text (dot semantics table). */
|
|
988
1440
|
.section-subtitle {
|
|
989
1441
|
font-size: var(--mc-chrome-subtitle-size);
|
|
990
1442
|
color: var(--text-muted);
|
|
@@ -993,39 +1445,75 @@ a:focus-visible,
|
|
|
993
1445
|
}
|
|
994
1446
|
|
|
995
1447
|
/* ===== Processes ===== */
|
|
996
|
-
.processes-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1448
|
+
.processes-note {
|
|
1449
|
+
padding: 9px 12px;
|
|
1450
|
+
margin-bottom: 10px;
|
|
1451
|
+
border-radius: 8px;
|
|
1452
|
+
background: var(--blue-bg);
|
|
1453
|
+
border: 1px solid rgba(59, 130, 246, 0.25);
|
|
1454
|
+
color: var(--text-secondary);
|
|
1455
|
+
font-size: 11.5px;
|
|
1456
|
+
line-height: 1.5;
|
|
1001
1457
|
}
|
|
1002
|
-
.processes-
|
|
1003
|
-
|
|
1004
|
-
padding: 8px 10px;
|
|
1005
|
-
color: var(--text-muted);
|
|
1458
|
+
.processes-note strong {
|
|
1459
|
+
color: var(--blue);
|
|
1006
1460
|
font-weight: 600;
|
|
1007
|
-
border-bottom: 1px solid var(--border);
|
|
1008
|
-
font-size: 11px;
|
|
1009
|
-
text-transform: uppercase;
|
|
1010
|
-
letter-spacing: 0.05em;
|
|
1011
1461
|
}
|
|
1012
|
-
.
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1462
|
+
.process-list {
|
|
1463
|
+
display: flex;
|
|
1464
|
+
flex-direction: column;
|
|
1465
|
+
gap: 8px;
|
|
1016
1466
|
}
|
|
1017
|
-
.
|
|
1018
|
-
|
|
1467
|
+
.process-card {
|
|
1468
|
+
padding: var(--mc-card-padding-dense);
|
|
1469
|
+
border-radius: var(--mc-radius);
|
|
1470
|
+
background: var(--bg-card);
|
|
1471
|
+
border: 1px solid var(--border);
|
|
1472
|
+
}
|
|
1473
|
+
.process-card:hover {
|
|
1474
|
+
border-color: var(--border-active);
|
|
1019
1475
|
}
|
|
1020
|
-
.
|
|
1476
|
+
.process-card-head {
|
|
1021
1477
|
display: flex;
|
|
1022
1478
|
align-items: center;
|
|
1023
|
-
gap:
|
|
1479
|
+
gap: 10px;
|
|
1480
|
+
}
|
|
1481
|
+
.process-label-badge {
|
|
1482
|
+
font-size: 10px;
|
|
1483
|
+
font-weight: 600;
|
|
1484
|
+
padding: 2px 8px;
|
|
1485
|
+
border-radius: 4px;
|
|
1486
|
+
background: var(--blue-bg);
|
|
1487
|
+
color: var(--blue);
|
|
1488
|
+
letter-spacing: 0.03em;
|
|
1489
|
+
}
|
|
1490
|
+
.process-card-head .process-actions {
|
|
1491
|
+
margin-left: auto;
|
|
1492
|
+
white-space: nowrap;
|
|
1493
|
+
}
|
|
1494
|
+
.process-desc {
|
|
1495
|
+
margin-top: 6px;
|
|
1496
|
+
color: var(--text-primary);
|
|
1497
|
+
font-size: 12px;
|
|
1498
|
+
line-height: 1.5;
|
|
1499
|
+
}
|
|
1500
|
+
.process-meta {
|
|
1501
|
+
margin-top: 4px;
|
|
1502
|
+
display: flex;
|
|
1503
|
+
gap: 12px;
|
|
1504
|
+
flex-wrap: wrap;
|
|
1505
|
+
color: var(--text-muted);
|
|
1506
|
+
font-size: 11px;
|
|
1507
|
+
font-family: var(--mc-font-mono);
|
|
1024
1508
|
}
|
|
1025
|
-
.
|
|
1026
|
-
|
|
1509
|
+
.process-cmd {
|
|
1510
|
+
margin-top: 4px;
|
|
1511
|
+
color: var(--text-muted);
|
|
1512
|
+
font-size: 11px;
|
|
1513
|
+
font-family: var(--mc-font-mono);
|
|
1514
|
+
overflow: hidden;
|
|
1515
|
+
text-overflow: ellipsis;
|
|
1027
1516
|
white-space: nowrap;
|
|
1028
|
-
text-align: right;
|
|
1029
1517
|
}
|
|
1030
1518
|
.process-copy-btn {
|
|
1031
1519
|
font-size: 10px;
|
|
@@ -1045,61 +1533,45 @@ a:focus-visible,
|
|
|
1045
1533
|
}
|
|
1046
1534
|
|
|
1047
1535
|
/* ===== Skills ===== */
|
|
1048
|
-
.
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
font-size: 12px;
|
|
1053
|
-
font-weight: 600;
|
|
1054
|
-
color: var(--text-muted);
|
|
1055
|
-
text-transform: uppercase;
|
|
1056
|
-
letter-spacing: 0.06em;
|
|
1057
|
-
margin-bottom: 10px;
|
|
1058
|
-
padding: 0 2px;
|
|
1536
|
+
.skill-grid {
|
|
1537
|
+
display: grid;
|
|
1538
|
+
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
1539
|
+
gap: 10px;
|
|
1059
1540
|
}
|
|
1060
|
-
.
|
|
1541
|
+
.skill-card {
|
|
1061
1542
|
display: flex;
|
|
1062
|
-
flex-
|
|
1543
|
+
flex-direction: column;
|
|
1063
1544
|
gap: 6px;
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
display: inline-flex;
|
|
1067
|
-
align-items: center;
|
|
1068
|
-
gap: 5px;
|
|
1069
|
-
padding: 5px 10px;
|
|
1070
|
-
border-radius: 6px;
|
|
1545
|
+
padding: var(--mc-card-padding-dense);
|
|
1546
|
+
border-radius: var(--mc-radius);
|
|
1071
1547
|
background: var(--bg-card);
|
|
1072
1548
|
border: 1px solid var(--border);
|
|
1073
|
-
font-size: 12px;
|
|
1074
|
-
color: var(--text-secondary);
|
|
1075
|
-
cursor: default;
|
|
1076
|
-
transition: all 0.15s;
|
|
1077
|
-
max-width: 280px;
|
|
1078
1549
|
}
|
|
1079
|
-
.skill-
|
|
1080
|
-
|
|
1081
|
-
|
|
1550
|
+
.skill-card:hover { border-color: var(--border-active); }
|
|
1551
|
+
.skill-card-head {
|
|
1552
|
+
display: flex;
|
|
1553
|
+
align-items: center;
|
|
1554
|
+
justify-content: space-between;
|
|
1555
|
+
gap: 8px;
|
|
1082
1556
|
}
|
|
1083
|
-
.skill-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
border-radius: 50%;
|
|
1087
|
-
flex-shrink: 0;
|
|
1557
|
+
.skill-name {
|
|
1558
|
+
font-size: 12px;
|
|
1559
|
+
color: var(--text-primary);
|
|
1088
1560
|
}
|
|
1089
|
-
.skill-
|
|
1090
|
-
font-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1561
|
+
.skill-cat {
|
|
1562
|
+
font-size: 10px;
|
|
1563
|
+
color: var(--text-muted);
|
|
1564
|
+
text-transform: uppercase;
|
|
1565
|
+
letter-spacing: 0.06em;
|
|
1094
1566
|
}
|
|
1095
|
-
.skill-
|
|
1096
|
-
font-size:
|
|
1097
|
-
font-weight: var(--mc-chrome-meta-weight);
|
|
1567
|
+
.skill-file {
|
|
1568
|
+
font-size: 10px;
|
|
1098
1569
|
color: var(--text-muted);
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
text-overflow: ellipsis;
|
|
1570
|
+
font-family: var(--mc-font-mono);
|
|
1571
|
+
word-break: break-all;
|
|
1102
1572
|
}
|
|
1573
|
+
.skill-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
|
|
1574
|
+
.skill-create-btn { margin-left: auto; }
|
|
1103
1575
|
|
|
1104
1576
|
/* ===== Context Packs ===== */
|
|
1105
1577
|
.context-pack-item {
|
|
@@ -1182,16 +1654,6 @@ a:focus-visible,
|
|
|
1182
1654
|
.live-indicator.live-reconnecting {
|
|
1183
1655
|
animation: pulse-text 1.5s ease-in-out infinite;
|
|
1184
1656
|
}
|
|
1185
|
-
.live-pulse-dot {
|
|
1186
|
-
width: 8px;
|
|
1187
|
-
height: 8px;
|
|
1188
|
-
border-radius: 50%;
|
|
1189
|
-
background: var(--green);
|
|
1190
|
-
position: relative;
|
|
1191
|
-
}
|
|
1192
|
-
.live-pulse-dot.live-dot-yellow {
|
|
1193
|
-
background: var(--yellow);
|
|
1194
|
-
}
|
|
1195
1657
|
/* Decorative ring pulses removed; header transport owns freshness */
|
|
1196
1658
|
.live-activity-feed {
|
|
1197
1659
|
max-height: 320px;
|
|
@@ -1278,6 +1740,21 @@ a:focus-visible,
|
|
|
1278
1740
|
width: 1em;
|
|
1279
1741
|
text-align: center;
|
|
1280
1742
|
}
|
|
1743
|
+
.live-activity-feed .monitor-row .monitor-row-avatar {
|
|
1744
|
+
display: inline-flex;
|
|
1745
|
+
align-items: center;
|
|
1746
|
+
justify-content: center;
|
|
1747
|
+
width: 18px;
|
|
1748
|
+
min-width: 18px;
|
|
1749
|
+
height: 18px;
|
|
1750
|
+
border-radius: 4px;
|
|
1751
|
+
font-size: 9px;
|
|
1752
|
+
font-weight: 700;
|
|
1753
|
+
line-height: 1;
|
|
1754
|
+
flex-shrink: 0;
|
|
1755
|
+
background: var(--bg-card-hover);
|
|
1756
|
+
color: var(--text-secondary);
|
|
1757
|
+
}
|
|
1281
1758
|
.live-activity-feed .monitor-row .feed-label {
|
|
1282
1759
|
flex: 1;
|
|
1283
1760
|
min-width: 0;
|
|
@@ -1287,6 +1764,32 @@ a:focus-visible,
|
|
|
1287
1764
|
color: var(--text-secondary);
|
|
1288
1765
|
font-size: 12px;
|
|
1289
1766
|
font-weight: 400;
|
|
1767
|
+
display: flex;
|
|
1768
|
+
align-items: baseline;
|
|
1769
|
+
gap: 6px;
|
|
1770
|
+
}
|
|
1771
|
+
/* Structured label spans: actor + verb never shrink; the plan filename is the
|
|
1772
|
+
low-signal segment and ellipsises first. */
|
|
1773
|
+
.live-activity-feed .monitor-row .feed-seg {
|
|
1774
|
+
flex-shrink: 0;
|
|
1775
|
+
white-space: nowrap;
|
|
1776
|
+
}
|
|
1777
|
+
.live-activity-feed .monitor-row .feed-sep {
|
|
1778
|
+
flex-shrink: 0;
|
|
1779
|
+
color: var(--text-muted);
|
|
1780
|
+
}
|
|
1781
|
+
.live-activity-feed .monitor-row .feed-seg-mid {
|
|
1782
|
+
flex-shrink: 1;
|
|
1783
|
+
min-width: 0;
|
|
1784
|
+
overflow: hidden;
|
|
1785
|
+
text-overflow: ellipsis;
|
|
1786
|
+
}
|
|
1787
|
+
.live-activity-feed .monitor-row .feed-seg-plan {
|
|
1788
|
+
flex-shrink: 3;
|
|
1789
|
+
min-width: 0;
|
|
1790
|
+
overflow: hidden;
|
|
1791
|
+
text-overflow: ellipsis;
|
|
1792
|
+
color: var(--text-muted);
|
|
1290
1793
|
}
|
|
1291
1794
|
.live-activity-feed .monitor-row .feed-time {
|
|
1292
1795
|
color: var(--text-muted);
|
|
@@ -1298,7 +1801,7 @@ a:focus-visible,
|
|
|
1298
1801
|
margin-left: auto;
|
|
1299
1802
|
}
|
|
1300
1803
|
.activity-feed-empty {
|
|
1301
|
-
padding:
|
|
1804
|
+
padding: var(--mc-space-2xl);
|
|
1302
1805
|
text-align: center;
|
|
1303
1806
|
color: var(--text-muted);
|
|
1304
1807
|
font-size: 12px;
|
|
@@ -1369,47 +1872,21 @@ a:focus-visible,
|
|
|
1369
1872
|
}
|
|
1370
1873
|
|
|
1371
1874
|
/* ===== Empty State ===== */
|
|
1875
|
+
/* Empty states scale with the card density ladder (viewport modes shrink
|
|
1876
|
+
--mc-card-padding), so they stay intentional from fullscreen to thin sidebar. */
|
|
1372
1877
|
.empty-state {
|
|
1373
1878
|
text-align: center;
|
|
1374
|
-
padding:
|
|
1879
|
+
padding: calc(var(--mc-card-padding) * 2.5) calc(var(--mc-card-padding) * 1.25);
|
|
1375
1880
|
color: var(--text-muted);
|
|
1376
1881
|
font-size: 13px;
|
|
1377
1882
|
}
|
|
1378
1883
|
|
|
1379
1884
|
/* ===== Clickable Cursors (Phase 1) ===== */
|
|
1380
1885
|
.plan-card { cursor: pointer; }
|
|
1381
|
-
.command-item { cursor: pointer; }
|
|
1382
|
-
.agent-item { cursor: pointer; }
|
|
1383
1886
|
.health-item { cursor: pointer; }
|
|
1384
1887
|
.terminal-item { cursor: pointer; }
|
|
1385
1888
|
.memory-item { cursor: pointer; }
|
|
1386
1889
|
|
|
1387
|
-
/* ===== Agent Expanded View ===== */
|
|
1388
|
-
.agent-details {
|
|
1389
|
-
max-height: 0;
|
|
1390
|
-
overflow: hidden;
|
|
1391
|
-
transition: max-height 0.3s ease, opacity 0.3s ease;
|
|
1392
|
-
opacity: 0;
|
|
1393
|
-
padding: 0 10px;
|
|
1394
|
-
}
|
|
1395
|
-
.agent-details.open {
|
|
1396
|
-
max-height: 200px;
|
|
1397
|
-
opacity: 1;
|
|
1398
|
-
padding: 8px 10px;
|
|
1399
|
-
}
|
|
1400
|
-
.agent-details p {
|
|
1401
|
-
font-size: 12px;
|
|
1402
|
-
color: var(--text-secondary);
|
|
1403
|
-
line-height: 1.5;
|
|
1404
|
-
}
|
|
1405
|
-
.agent-details .agent-path {
|
|
1406
|
-
font-size: 11px;
|
|
1407
|
-
color: var(--text-muted);
|
|
1408
|
-
font-family: var(--mc-font-mono);
|
|
1409
|
-
margin-top: 4px;
|
|
1410
|
-
word-break: break-all;
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
1890
|
/* ===== Copied Toast ===== */
|
|
1414
1891
|
.copied-toast {
|
|
1415
1892
|
position: fixed;
|
|
@@ -1448,7 +1925,7 @@ a:focus-visible,
|
|
|
1448
1925
|
vertical-align: middle;
|
|
1449
1926
|
}
|
|
1450
1927
|
|
|
1451
|
-
/* ===== Health Investigation Message ===== */
|
|
1928
|
+
/* ===== Health Investigation Message (legacy; Healthcenter uses .health-detail) ===== */
|
|
1452
1929
|
.health-message {
|
|
1453
1930
|
font-size: 11px;
|
|
1454
1931
|
color: var(--text-secondary);
|
|
@@ -1554,10 +2031,23 @@ a:focus-visible,
|
|
|
1554
2031
|
|
|
1555
2032
|
/* ===== Config form (session prefs) ===== */
|
|
1556
2033
|
.config-form {
|
|
1557
|
-
display:
|
|
1558
|
-
|
|
2034
|
+
display: grid;
|
|
2035
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
1559
2036
|
gap: 16px;
|
|
1560
|
-
|
|
2037
|
+
}
|
|
2038
|
+
.config-actions {
|
|
2039
|
+
grid-column: 1 / -1;
|
|
2040
|
+
display: flex;
|
|
2041
|
+
align-items: center;
|
|
2042
|
+
gap: 12px;
|
|
2043
|
+
}
|
|
2044
|
+
.config-fieldset-wide {
|
|
2045
|
+
grid-column: 1 / -1;
|
|
2046
|
+
}
|
|
2047
|
+
.config-grid-2 {
|
|
2048
|
+
display: grid;
|
|
2049
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
2050
|
+
gap: 4px 16px;
|
|
1561
2051
|
}
|
|
1562
2052
|
.config-fieldset {
|
|
1563
2053
|
border: 1px solid var(--border);
|
|
@@ -1572,6 +2062,23 @@ a:focus-visible,
|
|
|
1572
2062
|
font-weight: var(--mc-chrome-subtitle-weight);
|
|
1573
2063
|
color: var(--text-muted);
|
|
1574
2064
|
}
|
|
2065
|
+
.btn-config-copy {
|
|
2066
|
+
align-self: flex-start;
|
|
2067
|
+
margin-top: 10px;
|
|
2068
|
+
background: none;
|
|
2069
|
+
border: 1px solid var(--border);
|
|
2070
|
+
color: var(--text-muted);
|
|
2071
|
+
padding: 3px 10px;
|
|
2072
|
+
border-radius: 4px;
|
|
2073
|
+
cursor: pointer;
|
|
2074
|
+
font-size: 11px;
|
|
2075
|
+
font-family: inherit;
|
|
2076
|
+
transition: all 0.15s;
|
|
2077
|
+
}
|
|
2078
|
+
.btn-config-copy:hover {
|
|
2079
|
+
color: var(--text-primary);
|
|
2080
|
+
border-color: var(--text-muted);
|
|
2081
|
+
}
|
|
1575
2082
|
.config-row {
|
|
1576
2083
|
display: flex;
|
|
1577
2084
|
flex-direction: column;
|
|
@@ -1789,12 +2296,12 @@ a:focus-visible,
|
|
|
1789
2296
|
/* ===== Phase 3: Empty State CTAs ===== */
|
|
1790
2297
|
.empty-state-cta {
|
|
1791
2298
|
text-align: center;
|
|
1792
|
-
padding:
|
|
2299
|
+
padding: calc(var(--mc-card-padding) * 2) calc(var(--mc-card-padding) * 1.25);
|
|
1793
2300
|
color: var(--text-muted);
|
|
1794
2301
|
font-size: 13px;
|
|
1795
2302
|
}
|
|
1796
2303
|
.empty-state-cta.compact {
|
|
1797
|
-
padding:
|
|
2304
|
+
padding: var(--mc-card-padding) var(--mc-space-lg);
|
|
1798
2305
|
font-size: 12px;
|
|
1799
2306
|
}
|
|
1800
2307
|
.empty-state-cta .empty-state-icon {
|
|
@@ -1842,7 +2349,7 @@ a:focus-visible,
|
|
|
1842
2349
|
background: var(--bg-card);
|
|
1843
2350
|
border: 1px solid var(--border);
|
|
1844
2351
|
color: var(--text-secondary);
|
|
1845
|
-
padding: var(--mc-space-md)
|
|
2352
|
+
padding: var(--mc-space-md) var(--mc-space-xl);
|
|
1846
2353
|
border-radius: var(--mc-radius-chrome);
|
|
1847
2354
|
cursor: pointer;
|
|
1848
2355
|
font-size: var(--mc-chrome-label-size);
|
|
@@ -1975,6 +2482,8 @@ a:focus-visible,
|
|
|
1975
2482
|
.plan-card-footer { flex-wrap: wrap; gap: var(--mc-space-xs); }
|
|
1976
2483
|
.health-grid { grid-template-columns: 1fr 1fr; }
|
|
1977
2484
|
.command-grid { grid-template-columns: 1fr 1fr; }
|
|
2485
|
+
.skill-grid { grid-template-columns: 1fr 1fr; }
|
|
2486
|
+
.agent-grid { grid-template-columns: 1fr 1fr; }
|
|
1978
2487
|
.git-row { flex-wrap: wrap; gap: var(--mc-space-xs); }
|
|
1979
2488
|
.terminal-popup { max-width: 95vw; padding: 14px; }
|
|
1980
2489
|
}
|
|
@@ -1993,12 +2502,60 @@ a:focus-visible,
|
|
|
1993
2502
|
.terminal-popup { max-width: 95vw; padding: 14px; }
|
|
1994
2503
|
}
|
|
1995
2504
|
|
|
2505
|
+
/* ===== Very thin sidebar (~<340px): tightest chrome, single-column card grids ===== */
|
|
2506
|
+
@media (max-width: 339px) {
|
|
2507
|
+
:root {
|
|
2508
|
+
--mc-card-padding: 10px;
|
|
2509
|
+
--mc-header-pad-x: 8px;
|
|
2510
|
+
--mc-header-pad-x-end: 2px;
|
|
2511
|
+
--mc-content-pad: 8px;
|
|
2512
|
+
}
|
|
2513
|
+
.header { gap: var(--mc-space-sm); }
|
|
2514
|
+
.header-right { gap: var(--mc-space-xs); }
|
|
2515
|
+
.header-workspace { display: none; }
|
|
2516
|
+
.health-grid,
|
|
2517
|
+
.command-grid,
|
|
2518
|
+
.skill-grid,
|
|
2519
|
+
.agent-grid { grid-template-columns: 1fr; }
|
|
2520
|
+
.live-activity-feed { max-height: 180px; }
|
|
2521
|
+
/* Flex feed-label: allow actor/verb to ellipsis so fixed spans do not hard-clip. */
|
|
2522
|
+
.live-activity-feed .monitor-row .feed-seg-actor,
|
|
2523
|
+
.live-activity-feed .monitor-row .feed-seg-verb {
|
|
2524
|
+
flex-shrink: 1;
|
|
2525
|
+
min-width: 0;
|
|
2526
|
+
overflow: hidden;
|
|
2527
|
+
text-overflow: ellipsis;
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
|
|
1996
2531
|
.overview-stack {
|
|
1997
2532
|
display: flex;
|
|
1998
2533
|
flex-direction: column;
|
|
1999
2534
|
gap: 12px;
|
|
2000
2535
|
}
|
|
2001
2536
|
|
|
2537
|
+
/* ===== Mid panel (~701-1023px): two-column overview grid, IA row-major =====
|
|
2538
|
+
Collapse order follows the locked IA (Current mission -> Flight Log -> Checklist
|
|
2539
|
+
-> Crew monitor): Current mission spans row 1, Flight Log + Checklist share row 2,
|
|
2540
|
+
Crew monitor spans row 3. Page keeps scrolling; the one-fold lock starts at 1024px. */
|
|
2541
|
+
@media (min-width: 701px) and (max-width: 1023px) {
|
|
2542
|
+
#section-overview.active .overview-stack {
|
|
2543
|
+
display: grid;
|
|
2544
|
+
grid-template-columns: 1fr 1fr;
|
|
2545
|
+
grid-template-areas:
|
|
2546
|
+
"now now"
|
|
2547
|
+
"attention checklist"
|
|
2548
|
+
"monitor monitor";
|
|
2549
|
+
}
|
|
2550
|
+
#section-overview.active #now-execution-panel { grid-area: now; }
|
|
2551
|
+
#section-overview.active #attention-panel { grid-area: attention; }
|
|
2552
|
+
#section-overview.active #recent-plans-panel { grid-area: checklist; }
|
|
2553
|
+
#section-overview.active #hero-activity {
|
|
2554
|
+
grid-area: monitor;
|
|
2555
|
+
margin-bottom: 0;
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2002
2559
|
/* Desktop one-fold overview: 2x2 fixed-height grid; page does not scroll.
|
|
2003
2560
|
Additive under min-width 1024px only. Non-overview sections keep .content scroll.
|
|
2004
2561
|
Subheader (Cockpit anchors) hides: all four panels are visible; More lives in the header. */
|
|
@@ -2031,8 +2588,8 @@ a:focus-visible,
|
|
|
2031
2588
|
grid-template-columns: 1fr 1fr;
|
|
2032
2589
|
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
|
2033
2590
|
grid-template-areas:
|
|
2034
|
-
"now
|
|
2035
|
-
"
|
|
2591
|
+
"now attention"
|
|
2592
|
+
"checklist monitor";
|
|
2036
2593
|
}
|
|
2037
2594
|
|
|
2038
2595
|
#section-overview.active #now-execution-panel,
|
|
@@ -2108,7 +2665,7 @@ a:focus-visible,
|
|
|
2108
2665
|
display: flex;
|
|
2109
2666
|
flex-direction: column;
|
|
2110
2667
|
gap: var(--mc-space-sm);
|
|
2111
|
-
padding:
|
|
2668
|
+
padding: var(--mc-card-padding-dense);
|
|
2112
2669
|
border: 1px solid var(--border);
|
|
2113
2670
|
border-radius: var(--radius);
|
|
2114
2671
|
background: var(--bg-card);
|
|
@@ -2337,6 +2894,25 @@ a:focus-visible,
|
|
|
2337
2894
|
margin: 0 0 8px;
|
|
2338
2895
|
line-height: 1.4;
|
|
2339
2896
|
}
|
|
2897
|
+
.plan-next-action {
|
|
2898
|
+
padding-top: 10px;
|
|
2899
|
+
font-size: var(--mc-chrome-meta-size);
|
|
2900
|
+
color: var(--text-secondary);
|
|
2901
|
+
line-height: 1.4;
|
|
2902
|
+
}
|
|
2903
|
+
.plan-next-action-label {
|
|
2904
|
+
color: var(--text-muted);
|
|
2905
|
+
font-weight: var(--mc-chrome-label-weight);
|
|
2906
|
+
}
|
|
2907
|
+
.plan-accordion-actions .plan-action-primary,
|
|
2908
|
+
.plan-accordion-actions .plan-action-primary:hover {
|
|
2909
|
+
background: var(--blue);
|
|
2910
|
+
border-color: transparent;
|
|
2911
|
+
color: #fff;
|
|
2912
|
+
}
|
|
2913
|
+
.plan-accordion-actions .plan-action-primary:hover {
|
|
2914
|
+
filter: brightness(1.08);
|
|
2915
|
+
}
|
|
2340
2916
|
.plan-todo-list-full {
|
|
2341
2917
|
display: flex;
|
|
2342
2918
|
flex-direction: column;
|
|
@@ -2348,7 +2924,7 @@ a:focus-visible,
|
|
|
2348
2924
|
|
|
2349
2925
|
/* Flight Log empty state reuses attention-empty className with empty-state-cta. */
|
|
2350
2926
|
.attention-empty {
|
|
2351
|
-
padding:
|
|
2927
|
+
padding: var(--mc-space-lg) var(--mc-space-md);
|
|
2352
2928
|
font-size: 12px;
|
|
2353
2929
|
color: var(--text-muted);
|
|
2354
2930
|
line-height: 1.45;
|
|
@@ -2356,7 +2932,7 @@ a:focus-visible,
|
|
|
2356
2932
|
.empty-state-cta.attention-empty,
|
|
2357
2933
|
.empty-state-cta.activity-feed-empty {
|
|
2358
2934
|
/* Shared renderer already sets padding; keep panel chrome size. */
|
|
2359
|
-
padding:
|
|
2935
|
+
padding: var(--mc-space-lg) var(--mc-space-md);
|
|
2360
2936
|
}
|
|
2361
2937
|
|
|
2362
2938
|
/* ===== Now execution panel (primary Current work) ===== */
|
|
@@ -2405,6 +2981,21 @@ a:focus-visible,
|
|
|
2405
2981
|
color: var(--text-muted);
|
|
2406
2982
|
background: var(--bg-card);
|
|
2407
2983
|
}
|
|
2984
|
+
/* Busy-outside-plan chip: live run-loop activity while the mission is not
|
|
2985
|
+
executing. Advice-family blue tokens (kind palette); text-only so the dot
|
|
2986
|
+
and icon contracts stay untouched. */
|
|
2987
|
+
.mc-busy-chip {
|
|
2988
|
+
display: inline-flex;
|
|
2989
|
+
align-items: center;
|
|
2990
|
+
font-size: var(--mc-chrome-meta-size);
|
|
2991
|
+
font-weight: var(--mc-chrome-meta-weight);
|
|
2992
|
+
letter-spacing: 0.02em;
|
|
2993
|
+
padding: 3px var(--mc-space-md);
|
|
2994
|
+
border-radius: var(--mc-radius-pill);
|
|
2995
|
+
color: var(--blue);
|
|
2996
|
+
background: var(--blue-bg);
|
|
2997
|
+
text-transform: uppercase;
|
|
2998
|
+
}
|
|
2408
2999
|
.now-plan-name {
|
|
2409
3000
|
margin-top: 10px;
|
|
2410
3001
|
font-size: 15px;
|
|
@@ -2653,8 +3244,8 @@ a:focus-visible,
|
|
|
2653
3244
|
width: 100%;
|
|
2654
3245
|
text-align: left;
|
|
2655
3246
|
margin: 0;
|
|
2656
|
-
padding:
|
|
2657
|
-
border-radius:
|
|
3247
|
+
padding: var(--mc-card-padding-dense);
|
|
3248
|
+
border-radius: var(--mc-radius);
|
|
2658
3249
|
border: 1px solid var(--border);
|
|
2659
3250
|
background: var(--bg-elevated, var(--bg-card));
|
|
2660
3251
|
color: var(--text-primary);
|
|
@@ -2662,10 +3253,10 @@ a:focus-visible,
|
|
|
2662
3253
|
font: inherit;
|
|
2663
3254
|
line-height: 1.4;
|
|
2664
3255
|
}
|
|
3256
|
+
/* Base hover/focus: keyboard-visible ring via --border-active (not invisible --border); kind rules below set colors. */
|
|
2665
3257
|
.flight-log-card:hover,
|
|
2666
3258
|
.flight-log-card:focus-visible {
|
|
2667
|
-
|
|
2668
|
-
outline: 2px solid var(--accent, var(--yellow));
|
|
3259
|
+
outline: 2px solid var(--border-active);
|
|
2669
3260
|
outline-offset: 1px;
|
|
2670
3261
|
}
|
|
2671
3262
|
.flight-log-card-current {
|
|
@@ -2679,6 +3270,11 @@ a:focus-visible,
|
|
|
2679
3270
|
.flight-log-card-current.flight-log-kind-residual .flight-log-card-label {
|
|
2680
3271
|
color: var(--yellow);
|
|
2681
3272
|
}
|
|
3273
|
+
.flight-log-card-current.flight-log-kind-residual:hover,
|
|
3274
|
+
.flight-log-card-current.flight-log-kind-residual:focus-visible {
|
|
3275
|
+
border-color: var(--yellow);
|
|
3276
|
+
outline-color: var(--yellow);
|
|
3277
|
+
}
|
|
2682
3278
|
.flight-log-card-current.flight-log-kind-advice {
|
|
2683
3279
|
border-color: var(--blue);
|
|
2684
3280
|
background: var(--blue-bg);
|
|
@@ -2686,6 +3282,11 @@ a:focus-visible,
|
|
|
2686
3282
|
.flight-log-card-current.flight-log-kind-advice .flight-log-card-label {
|
|
2687
3283
|
color: var(--blue);
|
|
2688
3284
|
}
|
|
3285
|
+
.flight-log-card-current.flight-log-kind-advice:hover,
|
|
3286
|
+
.flight-log-card-current.flight-log-kind-advice:focus-visible {
|
|
3287
|
+
border-color: var(--blue);
|
|
3288
|
+
outline-color: var(--blue);
|
|
3289
|
+
}
|
|
2689
3290
|
.flight-log-card-current.flight-log-kind-ok {
|
|
2690
3291
|
border-color: color-mix(in srgb, var(--green) 55%, var(--border));
|
|
2691
3292
|
background: color-mix(in srgb, var(--green-bg) 40%, var(--bg-elevated, var(--bg-card)));
|
|
@@ -2693,6 +3294,11 @@ a:focus-visible,
|
|
|
2693
3294
|
.flight-log-card-current.flight-log-kind-ok .flight-log-card-label {
|
|
2694
3295
|
color: var(--green);
|
|
2695
3296
|
}
|
|
3297
|
+
.flight-log-card-current.flight-log-kind-ok:hover,
|
|
3298
|
+
.flight-log-card-current.flight-log-kind-ok:focus-visible {
|
|
3299
|
+
border-color: var(--green);
|
|
3300
|
+
outline-color: var(--green);
|
|
3301
|
+
}
|
|
2696
3302
|
.flight-log-card-current.flight-log-kind-warning {
|
|
2697
3303
|
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 70%, var(--border));
|
|
2698
3304
|
background: color-mix(in srgb, var(--orange-bg, var(--yellow-bg)) 55%, var(--bg-elevated, var(--bg-card)));
|
|
@@ -2700,6 +3306,11 @@ a:focus-visible,
|
|
|
2700
3306
|
.flight-log-card-current.flight-log-kind-warning .flight-log-card-label {
|
|
2701
3307
|
color: var(--orange, var(--yellow));
|
|
2702
3308
|
}
|
|
3309
|
+
.flight-log-card-current.flight-log-kind-warning:hover,
|
|
3310
|
+
.flight-log-card-current.flight-log-kind-warning:focus-visible {
|
|
3311
|
+
border-color: var(--orange, var(--yellow));
|
|
3312
|
+
outline-color: var(--orange, var(--yellow));
|
|
3313
|
+
}
|
|
2703
3314
|
.flight-log-card-past {
|
|
2704
3315
|
padding: 8px 10px;
|
|
2705
3316
|
font-size: 11px;
|
|
@@ -2709,17 +3320,49 @@ a:focus-visible,
|
|
|
2709
3320
|
.flight-log-card-past.flight-log-kind-residual {
|
|
2710
3321
|
border-color: color-mix(in srgb, var(--yellow) 35%, var(--border));
|
|
2711
3322
|
}
|
|
3323
|
+
/* Earlier: muted color-mix on hover; full kind token on focus-visible for ≥3:1 keyboard ring. */
|
|
3324
|
+
.flight-log-card-past.flight-log-kind-residual:hover {
|
|
3325
|
+
border-color: color-mix(in srgb, var(--yellow) 55%, var(--border));
|
|
3326
|
+
outline-color: color-mix(in srgb, var(--yellow) 55%, var(--border));
|
|
3327
|
+
}
|
|
3328
|
+
.flight-log-card-past.flight-log-kind-residual:focus-visible {
|
|
3329
|
+
border-color: var(--yellow);
|
|
3330
|
+
outline-color: var(--yellow);
|
|
3331
|
+
}
|
|
2712
3332
|
.flight-log-card-past.flight-log-kind-advice {
|
|
2713
3333
|
border-color: color-mix(in srgb, var(--blue) 35%, var(--border));
|
|
2714
3334
|
}
|
|
3335
|
+
.flight-log-card-past.flight-log-kind-advice:hover {
|
|
3336
|
+
border-color: color-mix(in srgb, var(--blue) 55%, var(--border));
|
|
3337
|
+
outline-color: color-mix(in srgb, var(--blue) 55%, var(--border));
|
|
3338
|
+
}
|
|
3339
|
+
.flight-log-card-past.flight-log-kind-advice:focus-visible {
|
|
3340
|
+
border-color: var(--blue);
|
|
3341
|
+
outline-color: var(--blue);
|
|
3342
|
+
}
|
|
2715
3343
|
.flight-log-card-past.flight-log-kind-ok {
|
|
2716
3344
|
border-color: color-mix(in srgb, var(--green) 30%, var(--border));
|
|
2717
3345
|
}
|
|
3346
|
+
.flight-log-card-past.flight-log-kind-ok:hover {
|
|
3347
|
+
border-color: color-mix(in srgb, var(--green) 50%, var(--border));
|
|
3348
|
+
outline-color: color-mix(in srgb, var(--green) 50%, var(--border));
|
|
3349
|
+
}
|
|
3350
|
+
.flight-log-card-past.flight-log-kind-ok:focus-visible {
|
|
3351
|
+
border-color: var(--green);
|
|
3352
|
+
outline-color: var(--green);
|
|
3353
|
+
}
|
|
2718
3354
|
.flight-log-card-past.flight-log-kind-warning {
|
|
2719
3355
|
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 30%, var(--border));
|
|
2720
3356
|
}
|
|
3357
|
+
.flight-log-card-past.flight-log-kind-warning:hover {
|
|
3358
|
+
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 50%, var(--border));
|
|
3359
|
+
outline-color: color-mix(in srgb, var(--orange, var(--yellow)) 50%, var(--border));
|
|
3360
|
+
}
|
|
3361
|
+
.flight-log-card-past.flight-log-kind-warning:focus-visible {
|
|
3362
|
+
border-color: var(--orange, var(--yellow));
|
|
3363
|
+
outline-color: var(--orange, var(--yellow));
|
|
3364
|
+
}
|
|
2721
3365
|
.flight-log-card-warning {
|
|
2722
|
-
padding: 10px 12px;
|
|
2723
3366
|
border-color: color-mix(in srgb, var(--orange, var(--yellow)) 70%, var(--border));
|
|
2724
3367
|
background: color-mix(in srgb, var(--orange-bg, var(--yellow-bg)) 55%, var(--bg-elevated, var(--bg-card)));
|
|
2725
3368
|
font-size: 12px;
|
|
@@ -2728,6 +3371,11 @@ a:focus-visible,
|
|
|
2728
3371
|
.flight-log-card-warning .flight-log-card-label {
|
|
2729
3372
|
color: var(--orange, var(--yellow));
|
|
2730
3373
|
}
|
|
3374
|
+
.flight-log-card-warning:hover,
|
|
3375
|
+
.flight-log-card-warning:focus-visible {
|
|
3376
|
+
border-color: var(--orange, var(--yellow));
|
|
3377
|
+
outline-color: var(--orange, var(--yellow));
|
|
3378
|
+
}
|
|
2731
3379
|
.flight-log-card-label {
|
|
2732
3380
|
display: block;
|
|
2733
3381
|
font-weight: 600;
|
|
@@ -2842,6 +3490,7 @@ a:focus-visible,
|
|
|
2842
3490
|
<span class="last-updated" id="lastUpdated" hidden></span>
|
|
2843
3491
|
<button type="button" class="header-home-btn" id="headerHomeBtn" onclick="return goHome()" aria-label="Home, Overview" title="Home, Overview"></button>
|
|
2844
3492
|
<button class="btn-refresh" onclick="refresh()" id="refreshBtn" aria-label="Refresh" title="Refresh"><svg class="btn-refresh-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="14.5 2.75 14.5 6.75 10.5 6.75"/><path d="M13.25 10a5.5 5.5 0 1 1-1.3-5.72L14.5 6.75"/></svg></button>
|
|
3493
|
+
<button type="button" class="header-fullscreen-btn" id="fullscreenToggleBtn" onclick="return toggleMcFullscreen()" aria-pressed="false" aria-label="Enter fullscreen" title="Enter fullscreen"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.5 2.5h4v4"/><path d="M13.5 2.5 9 7"/><path d="M6.5 13.5h-4v-4"/><path d="M2.5 13.5 7 9"/></svg></button>
|
|
2845
3494
|
<div class="nav-more">
|
|
2846
3495
|
<button type="button" class="nav-more-btn" id="navMoreBtn" aria-haspopup="menu" aria-expanded="false" aria-controls="navMoreMenu" title="More sections"></button>
|
|
2847
3496
|
<div class="nav-more-menu" id="navMoreMenu" role="menu" aria-label="More sections" hidden>
|
|
@@ -2881,12 +3530,10 @@ a:focus-visible,
|
|
|
2881
3530
|
</a>
|
|
2882
3531
|
<a class="top-tab nav-more-item" role="menuitem" href="#" data-section="terminals" tabindex="0" onclick="return showSection('terminals')" aria-label="Terminals section">
|
|
2883
3532
|
Terminals
|
|
2884
|
-
<span class="dot dot-blue" id="navTerminalsDot" aria-hidden="true"></span>
|
|
2885
3533
|
<span class="top-tab-badge" id="navTerminalsBadge">0</span>
|
|
2886
3534
|
</a>
|
|
2887
3535
|
<a class="top-tab nav-more-item" role="menuitem" href="#" data-section="processes" tabindex="0" onclick="return showSection('processes')" aria-label="Processes section">
|
|
2888
3536
|
Processes
|
|
2889
|
-
<span class="dot dot-green" id="navProcessesDot" aria-hidden="true"></span>
|
|
2890
3537
|
<span class="top-tab-badge" id="navProcessesBadge">0</span>
|
|
2891
3538
|
</a>
|
|
2892
3539
|
<a class="top-tab nav-more-item" role="menuitem" href="#" data-section="config" tabindex="0" onclick="return showSection('config')" aria-label="Config section">
|
|
@@ -2894,7 +3541,7 @@ a:focus-visible,
|
|
|
2894
3541
|
</a>
|
|
2895
3542
|
<div class="nav-more-separator" role="separator"></div>
|
|
2896
3543
|
<div class="nav-more-group" role="group" aria-labelledby="navSkinsLabel">
|
|
2897
|
-
<div class="nav-more-group-label" id="navSkinsLabel">Skins</div>
|
|
3544
|
+
<div class="nav-more-group-label" id="navSkinsLabel" data-icon="skins">Skins</div>
|
|
2898
3545
|
<button type="button" class="nav-more-item" role="menuitemradio" aria-checked="true" data-dashboard-skin-option="legacy" tabindex="0" onclick="return setDashboardSkin('legacy')">
|
|
2899
3546
|
Legacy
|
|
2900
3547
|
</button>
|
|
@@ -2906,6 +3553,7 @@ a:focus-visible,
|
|
|
2906
3553
|
</div>
|
|
2907
3554
|
</div>
|
|
2908
3555
|
</div>
|
|
3556
|
+
<button type="button" class="fullscreen-exit-btn" id="fullscreenExitBtn" onclick="return toggleMcFullscreen(false)" aria-label="Exit fullscreen" title="Exit fullscreen (Esc)" hidden><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13.5 6.5h-4v-4"/><path d="M9.5 6.5 14 2"/><path d="M2.5 9.5h4v4"/><path d="M6.5 9.5 2 14"/></svg></button>
|
|
2909
3557
|
|
|
2910
3558
|
<!-- Main -->
|
|
2911
3559
|
<div class="main">
|
|
@@ -2914,18 +3562,18 @@ a:focus-visible,
|
|
|
2914
3562
|
<div class="top-tabs-row">
|
|
2915
3563
|
<div class="cockpit-anchors" role="navigation" aria-label="Cockpit sections">
|
|
2916
3564
|
<a class="top-nav-anchor active" href="#now-execution-panel" data-anchor="now-execution-panel" data-icon="current-mission" onclick="return goCockpitAnchor('now-execution-panel')" aria-current="true">
|
|
2917
|
-
<span class="top-nav-anchor-label">
|
|
3565
|
+
<span class="top-nav-anchor-label">Current mission</span>
|
|
2918
3566
|
<span class="top-tab-badge" id="navOverviewBadge">0</span>
|
|
2919
3567
|
</a>
|
|
2920
|
-
<a class="top-nav-anchor" href="#hero-activity" data-anchor="hero-activity" data-icon="monitor" onclick="return goCockpitAnchor('hero-activity')">
|
|
2921
|
-
<span class="top-nav-anchor-label">Crew Monitor</span>
|
|
2922
|
-
</a>
|
|
2923
3568
|
<a class="top-nav-anchor" href="#attention-panel" data-anchor="attention-panel" data-icon="field-report" onclick="return goCockpitAnchor('attention-panel')">
|
|
2924
3569
|
<span class="top-nav-anchor-label">Flight Log</span>
|
|
2925
3570
|
</a>
|
|
2926
3571
|
<a class="top-nav-anchor" href="#recent-plans-panel" data-anchor="recent-plans-panel" data-icon="checklist" onclick="return goCockpitAnchor('recent-plans-panel')">
|
|
2927
3572
|
<span class="top-nav-anchor-label">Checklist</span>
|
|
2928
3573
|
</a>
|
|
3574
|
+
<a class="top-nav-anchor" href="#hero-activity" data-anchor="hero-activity" data-icon="monitor" onclick="return goCockpitAnchor('hero-activity')">
|
|
3575
|
+
<span class="top-nav-anchor-label">Crew Monitor</span>
|
|
3576
|
+
</a>
|
|
2929
3577
|
</div>
|
|
2930
3578
|
</div>
|
|
2931
3579
|
</nav>
|
|
@@ -3042,8 +3690,46 @@ function escapeJsString(value) {
|
|
|
3042
3690
|
}
|
|
3043
3691
|
|
|
3044
3692
|
// ===== Sections =====
|
|
3693
|
+
/** Navigable section ids (More menu + hash deep links). */
|
|
3694
|
+
const SECTION_IDS = [
|
|
3695
|
+
'overview',
|
|
3696
|
+
'plans',
|
|
3697
|
+
'activity',
|
|
3698
|
+
'agents',
|
|
3699
|
+
'skills',
|
|
3700
|
+
'commands',
|
|
3701
|
+
'health',
|
|
3702
|
+
'git',
|
|
3703
|
+
'memory',
|
|
3704
|
+
'terminals',
|
|
3705
|
+
'processes',
|
|
3706
|
+
'config',
|
|
3707
|
+
];
|
|
3708
|
+
|
|
3709
|
+
/** Read the active section from the URL hash (`#health` -> `health`). */
|
|
3710
|
+
function sectionFromHash() {
|
|
3711
|
+
try {
|
|
3712
|
+
const raw = (location.hash || '').replace(/^#\/?/, '').split('/')[0];
|
|
3713
|
+
return SECTION_IDS.includes(raw) ? raw : 'overview';
|
|
3714
|
+
} catch {
|
|
3715
|
+
return 'overview';
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
|
|
3719
|
+
/** Keep the URL hash in sync so every tab is a shareable deep link. */
|
|
3720
|
+
function syncSectionHash(id) {
|
|
3721
|
+
try {
|
|
3722
|
+
const target = `#${id}`;
|
|
3723
|
+
if (location.hash !== target && history.replaceState) {
|
|
3724
|
+
history.replaceState(null, '', target);
|
|
3725
|
+
}
|
|
3726
|
+
} catch {
|
|
3727
|
+
// file:// or restricted contexts: hash sync is best-effort
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3045
3731
|
/** Active pane id (overview = Cockpit page; others = dropdown destinations). */
|
|
3046
|
-
let activeSectionId =
|
|
3732
|
+
let activeSectionId = sectionFromHash();
|
|
3047
3733
|
/** Last Cockpit in-page anchor while overview is active. */
|
|
3048
3734
|
let activeCockpitAnchor = 'now-execution-panel';
|
|
3049
3735
|
/** Activity tab source filter chip id (`all` or a source key). */
|
|
@@ -3070,6 +3756,25 @@ function goHome() {
|
|
|
3070
3756
|
return showSection('overview');
|
|
3071
3757
|
}
|
|
3072
3758
|
|
|
3759
|
+
/** Fullscreen (zen) viewport mode: hide the app header; Escape or the floating exit control restores chrome. */
|
|
3760
|
+
function toggleMcFullscreen(force) {
|
|
3761
|
+
const on = typeof force === 'boolean' ? force : !document.body.classList.contains('mc-fullscreen');
|
|
3762
|
+
document.body.classList.toggle('mc-fullscreen', on);
|
|
3763
|
+
const enterBtn = document.getElementById('fullscreenToggleBtn');
|
|
3764
|
+
if (enterBtn) enterBtn.setAttribute('aria-pressed', on ? 'true' : 'false');
|
|
3765
|
+
const exitBtn = document.getElementById('fullscreenExitBtn');
|
|
3766
|
+
if (exitBtn) exitBtn.hidden = !on;
|
|
3767
|
+
return false;
|
|
3768
|
+
}
|
|
3769
|
+
document.addEventListener('keydown', (e) => {
|
|
3770
|
+
if (e.key !== 'Escape' || !document.body.classList.contains('mc-fullscreen')) return;
|
|
3771
|
+
// Layered Escape: dismiss open menus first; only then exit fullscreen.
|
|
3772
|
+
if (typeof isNavMoreOpen === 'function' && isNavMoreOpen()) return;
|
|
3773
|
+
if (typeof openRecentPlanActionsKey !== 'undefined' && openRecentPlanActionsKey) return;
|
|
3774
|
+
e.preventDefault();
|
|
3775
|
+
toggleMcFullscreen(false);
|
|
3776
|
+
});
|
|
3777
|
+
|
|
3073
3778
|
/** Sync header Home chrome + section-title back control with active section. */
|
|
3074
3779
|
function updateHeaderHomeChrome() {
|
|
3075
3780
|
const onOverview = activeSectionId === 'overview';
|
|
@@ -3126,12 +3831,19 @@ function showSection(id) {
|
|
|
3126
3831
|
}
|
|
3127
3832
|
|
|
3128
3833
|
updateHeaderHomeChrome();
|
|
3834
|
+
syncSectionHash(activeSectionId);
|
|
3129
3835
|
|
|
3130
3836
|
const content = document.getElementById('content');
|
|
3131
3837
|
if (content) content.scrollTop = 0;
|
|
3132
3838
|
return false;
|
|
3133
3839
|
}
|
|
3134
3840
|
|
|
3841
|
+
/** Back/forward or pasted deep links switch tabs. */
|
|
3842
|
+
window.addEventListener('hashchange', () => {
|
|
3843
|
+
const id = sectionFromHash();
|
|
3844
|
+
if (id !== activeSectionId) showSection(id);
|
|
3845
|
+
});
|
|
3846
|
+
|
|
3135
3847
|
/**
|
|
3136
3848
|
* Anchor scroll still travelling. A snapshot re-render restores the scroll
|
|
3137
3849
|
* position captured when it started, which would strand a smooth scroll part
|
|
@@ -3295,6 +4007,11 @@ function injectCockpitNavIcons() {
|
|
|
3295
4007
|
if (!kind) return;
|
|
3296
4008
|
el.insertAdjacentHTML('afterbegin', spaceIconSvg(kind, { decorative: true }));
|
|
3297
4009
|
});
|
|
4010
|
+
// Skins group label: palette glyph (distinct from Skills gear).
|
|
4011
|
+
const skinsLabel = document.getElementById('navSkinsLabel');
|
|
4012
|
+
if (skinsLabel && !skinsLabel.querySelector('.space-icon')) {
|
|
4013
|
+
skinsLabel.insertAdjacentHTML('afterbegin', spaceIconSvg('skins', { decorative: true }));
|
|
4014
|
+
}
|
|
3298
4015
|
}
|
|
3299
4016
|
|
|
3300
4017
|
// ===== Cockpit nav + more-menu keyboard / outside click =====
|
|
@@ -3386,14 +4103,15 @@ function progressColor(pct) {
|
|
|
3386
4103
|
return 'red';
|
|
3387
4104
|
}
|
|
3388
4105
|
|
|
4106
|
+
// Dot semantics: only state signals survive. completed = good, cancelled =
|
|
4107
|
+
// attention. pending / in_progress are steady states and get no dot (the
|
|
4108
|
+
// status text on the row stays the source of truth).
|
|
3389
4109
|
function statusDot(status) {
|
|
3390
4110
|
const map = {
|
|
3391
4111
|
completed: 'dot-green',
|
|
3392
|
-
in_progress: 'dot-blue dot-pulse',
|
|
3393
|
-
pending: 'dot-gray',
|
|
3394
4112
|
cancelled: 'dot-red',
|
|
3395
4113
|
};
|
|
3396
|
-
return map[status] ||
|
|
4114
|
+
return map[status] || null;
|
|
3397
4115
|
}
|
|
3398
4116
|
|
|
3399
4117
|
// ===== Client diagnostic activity =====
|
|
@@ -3471,11 +4189,12 @@ function semanticEventInfo(kind, commitType) {
|
|
|
3471
4189
|
const map = {
|
|
3472
4190
|
// Monitor live kinds: distinct resting color+icon (no kind-tag text, no left rail).
|
|
3473
4191
|
// run_plan follows .now-status-executing green; delivery uses cyan to avoid green wash.
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
4192
|
+
// Gloss strings are default software lexicon display masks (kind ids unchanged).
|
|
4193
|
+
run_plan: { icon: '\u25b6', tag: 'tick', gloss: 'Tech Lead - live execution', color: 'var(--green)', bg: 'var(--green-bg)' },
|
|
4194
|
+
handoff: { icon: '\u23f8', tag: 'handoff', gloss: 'Scrum Master - awaiting gate', color: 'var(--yellow)', bg: 'var(--yellow-bg)' },
|
|
4195
|
+
agent_step: { icon: '\u25c9', tag: 'step', gloss: 'Full-Stack Developer - task unit', color: 'var(--purple)', bg: 'var(--purple-bg)' },
|
|
4196
|
+
plan_progress: { icon: '\u2691', tag: 'plan', gloss: 'Product Owner - milestone', color: 'var(--orange)', bg: 'var(--orange-bg)' },
|
|
4197
|
+
delivery: { icon: '\u2714', tag: 'delivery', gloss: 'DevOps Engineer - shipped unit', color: 'var(--cyan)', bg: 'var(--cyan-bg)' },
|
|
3479
4198
|
commit: { icon: '\u25cf', tag: 'commit', gloss: 'commit', color: 'var(--orange)', bg: 'var(--orange-bg)' },
|
|
3480
4199
|
agent: { icon: '\u25c6', tag: 'agent', gloss: 'agent', color: 'var(--purple)', bg: 'var(--purple-bg)' },
|
|
3481
4200
|
skill: { icon: '\u2726', tag: 'skill', gloss: 'skill', color: 'var(--orange)', bg: 'var(--orange-bg)' },
|
|
@@ -3487,12 +4206,12 @@ function semanticEventInfo(kind, commitType) {
|
|
|
3487
4206
|
// Locked BMP map (hotfix 2026-07-27): feat✦ fix⚙ docs✎ chore⚒ pr⑂ ship✈
|
|
3488
4207
|
if (kind === 'delivery' && commitType) {
|
|
3489
4208
|
const subtypes = {
|
|
3490
|
-
feat: { icon: '\u2726', tag: 'delivery', gloss: '
|
|
3491
|
-
fix: { icon: '\u2699', tag: 'delivery', gloss: '
|
|
3492
|
-
docs: { icon: '\u270e', tag: 'delivery', gloss: '
|
|
3493
|
-
chore: { icon: '\u2692', tag: 'delivery', gloss: '
|
|
3494
|
-
pr: { icon: '\u2442', tag: 'delivery', gloss: '
|
|
3495
|
-
ship: { icon: '\u2708', tag: 'delivery', gloss: '
|
|
4209
|
+
feat: { icon: '\u2726', tag: 'delivery', gloss: 'DevOps Engineer - feat', color: 'var(--purple)', bg: 'var(--purple-bg)' },
|
|
4210
|
+
fix: { icon: '\u2699', tag: 'delivery', gloss: 'DevOps Engineer - fix', color: 'var(--red)', bg: 'var(--red-bg)' },
|
|
4211
|
+
docs: { icon: '\u270e', tag: 'delivery', gloss: 'DevOps Engineer - docs', color: 'var(--blue)', bg: 'var(--blue-bg)' },
|
|
4212
|
+
chore: { icon: '\u2692', tag: 'delivery', gloss: 'DevOps Engineer - chore', color: 'var(--orange)', bg: 'var(--orange-bg)' },
|
|
4213
|
+
pr: { icon: '\u2442', tag: 'delivery', gloss: 'DevOps Engineer - PR', color: 'var(--cyan)', bg: 'var(--cyan-bg)' },
|
|
4214
|
+
ship: { icon: '\u2708', tag: 'delivery', gloss: 'DevOps Engineer - shipped unit', color: 'var(--cyan)', bg: 'var(--cyan-bg)' },
|
|
3496
4215
|
};
|
|
3497
4216
|
if (subtypes[commitType]) return subtypes[commitType];
|
|
3498
4217
|
}
|
|
@@ -3684,6 +4403,38 @@ function semanticEventTime(ev, _info) {
|
|
|
3684
4403
|
return '';
|
|
3685
4404
|
}
|
|
3686
4405
|
|
|
4406
|
+
/**
|
|
4407
|
+
* Crew feed time: source timestamp first, then the client first-seen stamp so
|
|
4408
|
+
* every row stays timestamped even when the emitter omits `at`.
|
|
4409
|
+
*/
|
|
4410
|
+
function crewEventTime(ev, info) {
|
|
4411
|
+
const source = semanticEventTime(ev, info);
|
|
4412
|
+
if (source) return source;
|
|
4413
|
+
const seenAt = ev && ev.id ? semanticSeenAt.get(ev.id) : null;
|
|
4414
|
+
return seenAt ? escapeHtml(relativeTime(seenAt)) : '';
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
/**
|
|
4418
|
+
* Crew feed actor: kit agent id, else Engineering Manager for delivery, else
|
|
4419
|
+
* Squad when a plan is present (never the full plan filename), else Platform
|
|
4420
|
+
* Engineer. Default software lexicon masks; mirrors briefActivityActor.
|
|
4421
|
+
*/
|
|
4422
|
+
function crewEventActor(ev) {
|
|
4423
|
+
if (ev && ev.agent) return String(ev.agent);
|
|
4424
|
+
if (ev && ev.kind === 'delivery') return 'Engineering Manager';
|
|
4425
|
+
if (ev && ev.refs && ev.refs.plan) return 'Squad';
|
|
4426
|
+
return 'Platform Engineer';
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
/** Avatar-style monogram: first letters of the first two kebab/space parts, else first two chars. */
|
|
4430
|
+
function agentInitials(id) {
|
|
4431
|
+
const clean = String(id || '').trim();
|
|
4432
|
+
if (!clean) return '';
|
|
4433
|
+
const parts = clean.split(/[-_\s.]+/).filter(Boolean);
|
|
4434
|
+
if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
4435
|
+
return clean.slice(0, 2).toUpperCase();
|
|
4436
|
+
}
|
|
4437
|
+
|
|
3687
4438
|
/**
|
|
3688
4439
|
* Mission Control is read-only: it copies text to the clipboard and a human
|
|
3689
4440
|
* pastes it. The panel cannot open a file, a chat, or an editor, so no label,
|
|
@@ -3834,6 +4585,59 @@ function checklistPlanActionCommands(plan) {
|
|
|
3834
4585
|
};
|
|
3835
4586
|
}
|
|
3836
4587
|
|
|
4588
|
+
/**
|
|
4589
|
+
* Plans tab v2: next actionable to-do (in_progress first, else first pending).
|
|
4590
|
+
* Terminal plans return null (no next step to surface).
|
|
4591
|
+
*/
|
|
4592
|
+
function planNextActionTodo(items) {
|
|
4593
|
+
if (!Array.isArray(items) || items.length === 0) return null;
|
|
4594
|
+
const current = items.find((t) => t.status === 'in_progress');
|
|
4595
|
+
if (current) return { id: current.id || '', content: current.content || '' };
|
|
4596
|
+
const next = items.find((t) => t.status === 'pending');
|
|
4597
|
+
if (next) return { id: next.id || '', content: next.content || '' };
|
|
4598
|
+
return null;
|
|
4599
|
+
}
|
|
4600
|
+
|
|
4601
|
+
/**
|
|
4602
|
+
* Plans tab v2: status-aware copy-only actions. Chat commands name the chat
|
|
4603
|
+
* input as paste destination; the path action names the file picker (open =
|
|
4604
|
+
* copy path). The first action is the row's primary next step per lifecycle:
|
|
4605
|
+
* active resumes, backlog runs, parked resumes, exhausted (incomplete)
|
|
4606
|
+
* resumes, completed opens/archives.
|
|
4607
|
+
*/
|
|
4608
|
+
function planTabActions(p) {
|
|
4609
|
+
const basename = checklistPlanBasename(p);
|
|
4610
|
+
const resume = { kind: 'chat', label: 'Copy resume prompt', command: `/continue-plan ${basename}` };
|
|
4611
|
+
const run = { kind: 'chat', label: 'Copy run command', command: `/run-plan ${basename}` };
|
|
4612
|
+
const edit = { kind: 'chat', label: 'Copy edit command', command: `/backlog-edit ${basename}` };
|
|
4613
|
+
const archive = { kind: 'chat', label: 'Copy archive command', command: `/archive-plan ${basename}` };
|
|
4614
|
+
const path = { kind: 'path', label: 'Copy plan path' };
|
|
4615
|
+
switch (p?.lifecycle) {
|
|
4616
|
+
case 'executing':
|
|
4617
|
+
case 'awaiting_user':
|
|
4618
|
+
return [resume, run, path];
|
|
4619
|
+
case 'backlog':
|
|
4620
|
+
return [run, edit, path];
|
|
4621
|
+
case 'parked':
|
|
4622
|
+
return [resume, path];
|
|
4623
|
+
case 'incomplete':
|
|
4624
|
+
return [resume, run, path];
|
|
4625
|
+
case 'completed':
|
|
4626
|
+
return [path, archive];
|
|
4627
|
+
default:
|
|
4628
|
+
return [resume, path];
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
|
|
4632
|
+
function renderPlanTabActionButton(action, p, key, idx) {
|
|
4633
|
+
const primary = idx === 0 ? ' plan-action-primary' : '';
|
|
4634
|
+
const focusKey = `plan-act-${escapeAttr(key)}-${idx}`;
|
|
4635
|
+
if (action.kind === 'path') {
|
|
4636
|
+
return `<button type="button" class="status-bar-btn${primary}" data-focus-key="${focusKey}" onclick="event.stopPropagation();${copyRepoPathHandler(p.path)}" title="${escapeAttr(pathActionTitle(p.path))}">${escapeHtml(action.label)}</button>`;
|
|
4637
|
+
}
|
|
4638
|
+
return `<button type="button" class="status-bar-btn${primary}" data-focus-key="${focusKey}" onclick="event.stopPropagation();${copyForPasteHandler(action.command, action.command, 'chatInput')}" title="${escapeAttr(copyActionTitle(action.command, 'chatInput'))}">${escapeHtml(action.label)}</button>`;
|
|
4639
|
+
}
|
|
4640
|
+
|
|
3837
4641
|
let openRecentPlanActionsKey = null;
|
|
3838
4642
|
|
|
3839
4643
|
/** Anchor the fixed Actions menu above or below the trigger; escape panel overflow. */
|
|
@@ -4192,6 +4996,118 @@ function renderGitFileList(git) {
|
|
|
4192
4996
|
`;
|
|
4193
4997
|
}
|
|
4194
4998
|
|
|
4999
|
+
// ===== Git tab v2: promotion flow lanes + graph + staging hygiene (Phase 4) =====
|
|
5000
|
+
/** Divergence badge: sync (green), ahead (yellow), behind (orange). */
|
|
5001
|
+
function gitFlowBadge(div) {
|
|
5002
|
+
if (!div) return '<span class="git-flow-badge">no upstream</span>';
|
|
5003
|
+
const bits = [];
|
|
5004
|
+
if (div.ahead > 0) bits.push(`<span class="git-flow-badge is-ahead">ahead ${div.ahead}</span>`);
|
|
5005
|
+
if (div.behind > 0) bits.push(`<span class="git-flow-badge is-behind">behind ${div.behind}</span>`);
|
|
5006
|
+
if (!bits.length) bits.push('<span class="git-flow-badge is-sync">in sync</span>');
|
|
5007
|
+
return bits.join(' ');
|
|
5008
|
+
}
|
|
5009
|
+
|
|
5010
|
+
/**
|
|
5011
|
+
* State label + dot per Phase 1 dot semantics (state only, always paired
|
|
5012
|
+
* with a label). tone: green = good/in sync, yellow = important (promotion
|
|
5013
|
+
* pending or branch ahead), red never used here (git surface is read-only).
|
|
5014
|
+
*/
|
|
5015
|
+
function gitFlowStateLabel(div, { syncText, aheadText, behindText }) {
|
|
5016
|
+
if (!div) return { tone: null, text: 'upstream unavailable' };
|
|
5017
|
+
if (div.ahead === 0 && div.behind === 0) return { tone: 'green', text: syncText };
|
|
5018
|
+
if (div.ahead > 0 && div.behind === 0) return { tone: 'yellow', text: aheadText(div.ahead) };
|
|
5019
|
+
if (div.ahead === 0 && div.behind > 0) return { tone: 'yellow', text: behindText(div.behind) };
|
|
5020
|
+
return { tone: 'yellow', text: `diverged (ahead ${div.ahead}, behind ${div.behind})` };
|
|
5021
|
+
}
|
|
5022
|
+
|
|
5023
|
+
function gitFlowDot(tone) {
|
|
5024
|
+
return tone ? `<span class="dot dot-${tone}" aria-hidden="true"></span>` : '';
|
|
5025
|
+
}
|
|
5026
|
+
|
|
5027
|
+
function renderGitFlowRow({ from, to, div, texts, cta }) {
|
|
5028
|
+
const state = gitFlowStateLabel(div, texts);
|
|
5029
|
+
return `
|
|
5030
|
+
<div class="git-flow-row">
|
|
5031
|
+
<span class="git-flow-lane">${escapeHtml(from)}</span>
|
|
5032
|
+
<span class="git-flow-arrow" aria-hidden="true">→</span>
|
|
5033
|
+
<span class="git-flow-lane">${escapeHtml(to)}</span>
|
|
5034
|
+
<span class="git-flow-state">${gitFlowDot(state.tone)}<span>${escapeHtml(state.text)}</span>${gitFlowBadge(div)}</span>
|
|
5035
|
+
${cta || ''}
|
|
5036
|
+
</div>
|
|
5037
|
+
`;
|
|
5038
|
+
}
|
|
5039
|
+
|
|
5040
|
+
/** Promotion flow lanes: work -> staging -> main (what Cursor's git panel does not show). */
|
|
5041
|
+
function renderGitFlowCard(git) {
|
|
5042
|
+
const flow = git?.flow || {};
|
|
5043
|
+
const branch = git?.branch || 'HEAD';
|
|
5044
|
+
const rows = [];
|
|
5045
|
+
rows.push(renderGitFlowRow({
|
|
5046
|
+
from: branch,
|
|
5047
|
+
to: 'origin/staging',
|
|
5048
|
+
div: flow.vsStaging,
|
|
5049
|
+
texts: {
|
|
5050
|
+
syncText: 'Nothing to promote',
|
|
5051
|
+
aheadText: (n) => `${n} commit${n !== 1 ? 's' : ''} ready for staging`,
|
|
5052
|
+
behindText: (n) => `${n} commit${n !== 1 ? 's' : ''} behind staging`,
|
|
5053
|
+
},
|
|
5054
|
+
cta: flow.vsStaging && flow.vsStaging.ahead > 0
|
|
5055
|
+
? `<button type="button" class="git-action-btn primary git-flow-cta" onclick="copyGitStagingCommand()" title="${escapeAttr(copyActionTitle('/git-staging', 'chatInput'))}">Copy /git-staging</button>`
|
|
5056
|
+
: '',
|
|
5057
|
+
}));
|
|
5058
|
+
rows.push(renderGitFlowRow({
|
|
5059
|
+
from: 'origin/staging',
|
|
5060
|
+
to: 'origin/main',
|
|
5061
|
+
div: flow.stagingVsMain,
|
|
5062
|
+
texts: {
|
|
5063
|
+
syncText: 'main is current',
|
|
5064
|
+
aheadText: (n) => `${n} commit${n !== 1 ? 's' : ''} awaiting promotion to main`,
|
|
5065
|
+
behindText: (n) => `staging is ${n} commit${n !== 1 ? 's' : ''} behind main`,
|
|
5066
|
+
},
|
|
5067
|
+
cta: flow.stagingVsMain && flow.stagingVsMain.ahead > 0
|
|
5068
|
+
? `<button type="button" class="git-action-btn git-flow-cta" onclick="${copyForPasteHandler('/git-prod', '/git-prod', 'chatInput')}" title="${escapeAttr(copyActionTitle('/git-prod', 'chatInput'))}">Copy /git-prod</button>`
|
|
5069
|
+
: '',
|
|
5070
|
+
}));
|
|
5071
|
+
rows.push(renderGitFlowRow({
|
|
5072
|
+
from: branch,
|
|
5073
|
+
to: 'origin/main',
|
|
5074
|
+
div: flow.vsMain,
|
|
5075
|
+
texts: {
|
|
5076
|
+
syncText: 'In sync with main',
|
|
5077
|
+
aheadText: (n) => `${n} commit${n !== 1 ? 's' : ''} not yet in main`,
|
|
5078
|
+
behindText: (n) => `${n} commit${n !== 1 ? 's' : ''} behind main`,
|
|
5079
|
+
},
|
|
5080
|
+
cta: '',
|
|
5081
|
+
}));
|
|
5082
|
+
return `<div class="git-flow" aria-label="Promotion flow: work to staging to main">${rows.join('')}</div>`;
|
|
5083
|
+
}
|
|
5084
|
+
|
|
5085
|
+
/** Readable git graph: pre-rendered branch lanes + merges from git log --graph. */
|
|
5086
|
+
function renderGitGraphCard(git) {
|
|
5087
|
+
const lines = Array.isArray(git?.graph) ? git.graph : [];
|
|
5088
|
+
if (!lines.length) return '';
|
|
5089
|
+
return `
|
|
5090
|
+
<div class="git-graph-title">Recent history (all branches, ${lines.length} lines)</div>
|
|
5091
|
+
<pre class="git-graph" aria-label="Git commit graph with branch lanes and merges">${escapeHtml(lines.join('\n'))}</pre>
|
|
5092
|
+
`;
|
|
5093
|
+
}
|
|
5094
|
+
|
|
5095
|
+
/** Staging hygiene: untracked plan-monitor WIP (add-by-name only; ADR 2026-07-29 R14). */
|
|
5096
|
+
function renderGitHygieneHint(git) {
|
|
5097
|
+
const wip = git?.hygiene?.monitorWip || [];
|
|
5098
|
+
if (!wip.length) return '';
|
|
5099
|
+
return `
|
|
5100
|
+
<div class="git-hygiene" role="status">
|
|
5101
|
+
<div class="git-hygiene-body">
|
|
5102
|
+
<div class="git-hygiene-title"><span class="dot dot-yellow" aria-hidden="true"></span> Staging hygiene: ${wip.length} untracked plan-monitor file${wip.length !== 1 ? 's' : ''}</div>
|
|
5103
|
+
<div>Stage monitor files add-by-name only; never a broad <code>git add</code> of <code>.cursor/memory/</code>.</div>
|
|
5104
|
+
<div class="git-hygiene-paths">${wip.map((p) => escapeHtml(p)).join('<br>')}</div>
|
|
5105
|
+
</div>
|
|
5106
|
+
<button type="button" class="git-action-btn git-flow-cta" onclick="copyGitStagingCommand()" title="${escapeAttr(copyActionTitle('/git-staging', 'chatInput'))}">Copy /git-staging</button>
|
|
5107
|
+
</div>
|
|
5108
|
+
`;
|
|
5109
|
+
}
|
|
5110
|
+
|
|
4195
5111
|
// ===== Copy to clipboard + toast (Phase 1) =====
|
|
4196
5112
|
/** Fallback confirmation. Callers should pass a destination-aware message. */
|
|
4197
5113
|
function copyToastLabel(text) {
|
|
@@ -4209,7 +5125,9 @@ async function copyToClipboard(text, options) {
|
|
|
4209
5125
|
}
|
|
4210
5126
|
throw new Error('Clipboard API unavailable');
|
|
4211
5127
|
} catch {
|
|
4212
|
-
|
|
5128
|
+
const raw = String(text || '');
|
|
5129
|
+
const preview = raw.length > 120 ? `${raw.slice(0, 117)}...` : raw;
|
|
5130
|
+
showToast(`Copy failed - select manually: ${preview}`, true);
|
|
4213
5131
|
}
|
|
4214
5132
|
}
|
|
4215
5133
|
|
|
@@ -4291,10 +5209,14 @@ function renderConfigSection(d) {
|
|
|
4291
5209
|
return `
|
|
4292
5210
|
<div class="content-section" id="section-config">
|
|
4293
5211
|
<div class="section-title">
|
|
4294
|
-
|
|
5212
|
+
Config
|
|
4295
5213
|
<span class="section-subtitle">Session prefs in .cursor/context/config.json</span>
|
|
4296
5214
|
</div>
|
|
4297
5215
|
<form class="config-form" id="mission-config-form" onsubmit="return saveMissionConfig(event)">
|
|
5216
|
+
<div class="config-actions">
|
|
5217
|
+
<button type="submit" class="btn-config-save" id="config-save-btn" data-focus-key="config-save-btn">Save Configuration</button>
|
|
5218
|
+
<span class="config-hint">Writes to .cursor/context/config.json via the loopback allowlist. Write path unavailable (server down, non-loopback, read-only deploy)? Use a per-fieldset Copy snippet button and paste into the file by hand.</span>
|
|
5219
|
+
</div>
|
|
4298
5220
|
<fieldset class="config-fieldset">
|
|
4299
5221
|
<legend>Read-only</legend>
|
|
4300
5222
|
<p class="config-readonly">Onboarded: <strong>${escapeHtml(onboarded)}</strong> · Onboarding status: <strong>${escapeHtml(onboardingStatus)}</strong></p>
|
|
@@ -4309,7 +5231,7 @@ function renderConfigSection(d) {
|
|
|
4309
5231
|
<div class="config-row">
|
|
4310
5232
|
<label for="config-interTickCooldownMs">Inter-tick cooldown (ms)</label>
|
|
4311
5233
|
<input type="number" id="config-interTickCooldownMs" name="interTickCooldownMs" min="0" max="3600000" step="1" value="${escapeAttr(String(cooldown))}" data-focus-key="config-interTickCooldownMs" />
|
|
4312
|
-
<span class="config-hint">0 disables
|
|
5234
|
+
<span class="config-hint">0 disables; recommend 15000 on Auto continuous /run-plan.</span>
|
|
4313
5235
|
</div>
|
|
4314
5236
|
<div class="config-row config-row-inline">
|
|
4315
5237
|
<input type="checkbox" id="config-cadence-enabled" name="cadenceEnabled" ${cadenceEnabled ? 'checked' : ''} data-focus-key="config-cadence-enabled" />
|
|
@@ -4318,8 +5240,10 @@ function renderConfigSection(d) {
|
|
|
4318
5240
|
<div class="config-row">
|
|
4319
5241
|
<label for="config-cadence-threshold">Cadence tick threshold</label>
|
|
4320
5242
|
<input type="number" id="config-cadence-threshold" name="cadenceThreshold" min="1" max="100" step="1" value="${escapeAttr(String(cadenceThreshold))}" data-focus-key="config-cadence-threshold" />
|
|
4321
|
-
<span class="config-hint">Warn
|
|
5243
|
+
<span class="config-hint">Warn when unreviewed work remains after N ticks. Default 3.</span>
|
|
4322
5244
|
</div>
|
|
5245
|
+
<button type="button" class="btn-config-copy" data-focus-key="config-copy-session" onclick="return copyConfigSnippet('session')">Copy config.json snippet</button>
|
|
5246
|
+
<button type="button" class="btn-config-copy" data-focus-key="config-copy-dogfood" onclick="return copyStaticConfigSnippet('dogfood')" title="Copy-only; never writable via this tab">Copy dogfood.factoryRoot snippet</button>
|
|
4323
5247
|
</fieldset>
|
|
4324
5248
|
<fieldset class="config-fieldset">
|
|
4325
5249
|
<legend>Update check (opt-in)</legend>
|
|
@@ -4330,10 +5254,12 @@ function renderConfigSection(d) {
|
|
|
4330
5254
|
<div class="config-row">
|
|
4331
5255
|
<label for="config-updateCheck-intervalDays">Check interval (days)</label>
|
|
4332
5256
|
<input type="number" id="config-updateCheck-intervalDays" name="updateCheckIntervalDays" min="1" max="365" step="1" value="${escapeAttr(String(updateCheckInterval))}" data-focus-key="config-updateCheck-intervalDays" />
|
|
4333
|
-
<span class="config-hint">Notify only
|
|
5257
|
+
<span class="config-hint">Notify only; apply stays via the /update confirm. updateApply.auto is never writable here.</span>
|
|
4334
5258
|
</div>
|
|
5259
|
+
<button type="button" class="btn-config-copy" data-focus-key="config-copy-updateCheck" onclick="return copyConfigSnippet('updateCheck')">Copy config.json snippet</button>
|
|
5260
|
+
<button type="button" class="btn-config-copy" data-focus-key="config-copy-updateApply" onclick="return copyStaticConfigSnippet('updateApply')" title="Copy-only; never writable via this tab">Copy updateApply.auto snippet</button>
|
|
4335
5261
|
</fieldset>
|
|
4336
|
-
<fieldset class="config-fieldset">
|
|
5262
|
+
<fieldset class="config-fieldset config-fieldset-wide">
|
|
4337
5263
|
<legend>Audits (external plan review)</legend>
|
|
4338
5264
|
<div class="config-row config-row-inline">
|
|
4339
5265
|
<input type="checkbox" id="config-epr-enabled" name="eprEnabled" ${epr.enabled ? 'checked' : ''} data-focus-key="config-epr-enabled" />
|
|
@@ -4352,6 +5278,7 @@ function renderConfigSection(d) {
|
|
|
4352
5278
|
<select id="config-epr-backend" name="eprBackend" data-focus-key="config-epr-backend">
|
|
4353
5279
|
<option value="claude"${epr.backend === 'claude' || !epr.backend ? ' selected' : ''}>claude</option>
|
|
4354
5280
|
</select>
|
|
5281
|
+
<span class="config-hint">claude is the only backend today.</span>
|
|
4355
5282
|
</div>
|
|
4356
5283
|
<div class="config-row">
|
|
4357
5284
|
<label for="config-epr-mode">Arming mode</label>
|
|
@@ -4359,7 +5286,7 @@ function renderConfigSection(d) {
|
|
|
4359
5286
|
<option value="paste"${epr.mode !== 'autonomous' ? ' selected' : ''}>paste (legacy)</option>
|
|
4360
5287
|
<option value="autonomous"${epr.mode === 'autonomous' ? ' selected' : ''}>autonomous</option>
|
|
4361
5288
|
</select>
|
|
4362
|
-
<span class="config-hint">Missing mode keeps paste behavior
|
|
5289
|
+
<span class="config-hint">Missing mode keeps paste behavior. Autonomous auto-launches a visible review.</span>
|
|
4363
5290
|
</div>
|
|
4364
5291
|
<div class="config-row config-row-inline">
|
|
4365
5292
|
<input type="checkbox" id="config-epr-midBatch" name="eprMidBatch" ${epr.midBatchAudits ? 'checked' : ''} data-focus-key="config-epr-midBatch" />
|
|
@@ -4374,8 +5301,9 @@ function renderConfigSection(d) {
|
|
|
4374
5301
|
</select>
|
|
4375
5302
|
<span class="config-hint">Check owed/untriaged audits before plan-run commands. Missing key = off.</span>
|
|
4376
5303
|
</div>
|
|
5304
|
+
<button type="button" class="btn-config-copy" data-focus-key="config-copy-audits" onclick="return copyConfigSnippet('audits')">Copy config.json snippet</button>
|
|
4377
5305
|
</fieldset>
|
|
4378
|
-
<fieldset class="config-fieldset">
|
|
5306
|
+
<fieldset class="config-fieldset config-fieldset-wide">
|
|
4379
5307
|
<legend>Agent Personas</legend>
|
|
4380
5308
|
<div class="config-row">
|
|
4381
5309
|
<label for="config-persona-default">Default persona</label>
|
|
@@ -4384,19 +5312,17 @@ function renderConfigSection(d) {
|
|
|
4384
5312
|
</select>
|
|
4385
5313
|
<span class="config-hint">Chat/CLI tone only. Interface Skins stay in the More menu.</span>
|
|
4386
5314
|
</div>
|
|
4387
|
-
|
|
5315
|
+
<div class="config-grid-2">
|
|
5316
|
+
${modeRows}
|
|
5317
|
+
</div>
|
|
5318
|
+
<button type="button" class="btn-config-copy" data-focus-key="config-copy-personas" onclick="return copyConfigSnippet('personas')">Copy config.json snippet</button>
|
|
4388
5319
|
</fieldset>
|
|
4389
|
-
<button type="submit" class="btn-config-save" id="config-save-btn" data-focus-key="config-save-btn">Save</button>
|
|
4390
5320
|
</form>
|
|
4391
5321
|
</div>
|
|
4392
5322
|
`;
|
|
4393
5323
|
}
|
|
4394
5324
|
|
|
4395
|
-
|
|
4396
|
-
if (event && typeof event.preventDefault === 'function') event.preventDefault();
|
|
4397
|
-
const btn = document.getElementById('config-save-btn');
|
|
4398
|
-
if (btn) btn.disabled = true;
|
|
4399
|
-
|
|
5325
|
+
function collectMissionConfigPayload() {
|
|
4400
5326
|
const autoHandoff = !!document.getElementById('config-autoHandoff')?.checked;
|
|
4401
5327
|
const cooldownRaw = document.getElementById('config-interTickCooldownMs')?.value;
|
|
4402
5328
|
const interTickCooldownMs = Number.parseInt(String(cooldownRaw || '0'), 10);
|
|
@@ -4437,6 +5363,51 @@ async function saveMissionConfig(event) {
|
|
|
4437
5363
|
const el = document.getElementById(`config-persona-${mode.id}`);
|
|
4438
5364
|
if (el && el.value) payload.agentPersona.modes[mode.id] = el.value;
|
|
4439
5365
|
}
|
|
5366
|
+
return payload;
|
|
5367
|
+
}
|
|
5368
|
+
|
|
5369
|
+
/* Copy-only CRUD fallback: snippets reflect current form values and go to the
|
|
5370
|
+
clipboard only. No write surface is added; paste target is config.json. */
|
|
5371
|
+
const CONFIG_SNIPPET_GROUPS = {
|
|
5372
|
+
session: ['autoHandoff', 'interTickCooldownMs', 'fieldReportReviewCadence'],
|
|
5373
|
+
updateCheck: ['updateCheck'],
|
|
5374
|
+
audits: ['externalPlanReview'],
|
|
5375
|
+
personas: ['agentPersona'],
|
|
5376
|
+
};
|
|
5377
|
+
|
|
5378
|
+
function copyConfigSnippet(group) {
|
|
5379
|
+
const keys = CONFIG_SNIPPET_GROUPS[group];
|
|
5380
|
+
if (!keys) return false;
|
|
5381
|
+
const payload = collectMissionConfigPayload();
|
|
5382
|
+
const snippet = {};
|
|
5383
|
+
for (const key of keys) snippet[key] = payload[key];
|
|
5384
|
+
void copyToClipboard(JSON.stringify(snippet, null, 2), {
|
|
5385
|
+
toastMessage: 'Copied config snippet - paste into .cursor/context/config.json',
|
|
5386
|
+
});
|
|
5387
|
+
return false;
|
|
5388
|
+
}
|
|
5389
|
+
|
|
5390
|
+
/* Static copy-only snippets for knobs the tab can never write (allowlist unchanged). */
|
|
5391
|
+
const CONFIG_STATIC_SNIPPETS = {
|
|
5392
|
+
updateApply: { updateApply: { auto: false } },
|
|
5393
|
+
dogfood: { dogfood: { factoryRoot: '/absolute/path/to/agent-kit-dev' } },
|
|
5394
|
+
};
|
|
5395
|
+
|
|
5396
|
+
function copyStaticConfigSnippet(id) {
|
|
5397
|
+
const snippet = CONFIG_STATIC_SNIPPETS[id];
|
|
5398
|
+
if (!snippet) return false;
|
|
5399
|
+
void copyToClipboard(JSON.stringify(snippet, null, 2), {
|
|
5400
|
+
toastMessage: 'Copied config snippet - paste into .cursor/context/config.json (not writable via tab)',
|
|
5401
|
+
});
|
|
5402
|
+
return false;
|
|
5403
|
+
}
|
|
5404
|
+
|
|
5405
|
+
async function saveMissionConfig(event) {
|
|
5406
|
+
if (event && typeof event.preventDefault === 'function') event.preventDefault();
|
|
5407
|
+
const btn = document.getElementById('config-save-btn');
|
|
5408
|
+
if (btn) btn.disabled = true;
|
|
5409
|
+
|
|
5410
|
+
const payload = collectMissionConfigPayload();
|
|
4440
5411
|
|
|
4441
5412
|
try {
|
|
4442
5413
|
const res = await fetch('/api/config', {
|
|
@@ -4470,20 +5441,151 @@ async function saveMissionConfig(event) {
|
|
|
4470
5441
|
return false;
|
|
4471
5442
|
}
|
|
4472
5443
|
|
|
4473
|
-
// =====
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
5444
|
+
// ===== Healthcenter: per-check expand + Autofix map (copy-only) =====
|
|
5445
|
+
/** Fixed v1 check set: plans, handoff, agents, commands, memory, git, config. */
|
|
5446
|
+
const HEALTH_CHECK_META = {
|
|
5447
|
+
plans: {
|
|
5448
|
+
okDetail: 'Plans directory is present with at least one plan file.',
|
|
5449
|
+
failDetail: 'No plans under .cursor/plans/, or the plans directory is missing.',
|
|
5450
|
+
autofix: { text: '/start-project', subject: '/start-project', destination: 'chatInput', label: 'Autofix' },
|
|
5451
|
+
},
|
|
5452
|
+
handoff: {
|
|
5453
|
+
okDetail: 'HANDOFF.md is present and parseable (idle Plan is still healthy).',
|
|
5454
|
+
failDetail: 'HANDOFF.md is missing or not parseable.',
|
|
5455
|
+
autofix: { text: '/continue-plan', subject: '/continue-plan', destination: 'chatInput', label: 'Autofix' },
|
|
5456
|
+
},
|
|
5457
|
+
agents: {
|
|
5458
|
+
okDetail: 'Agents are optional in L0; empty .cursor/agents/ is healthy. Cataloged agents (if any) are listed in Agents.',
|
|
5459
|
+
// Intentionally unreachable: dashboard-data keeps agents.ok constant-true (L0-optional).
|
|
5460
|
+
failDetail: 'Intentionally unreachable (agents ok is constant-true; L0-optional inventory).',
|
|
5461
|
+
autofix: null,
|
|
5462
|
+
},
|
|
5463
|
+
commands: {
|
|
5464
|
+
okDetail: 'Slash commands are available under .cursor/commands/.',
|
|
5465
|
+
failDetail: 'No commands under .cursor/commands/.',
|
|
5466
|
+
autofix: { text: '.cursor/commands/', subject: 'path .cursor/commands/', destination: 'filePicker', label: 'Copy path' },
|
|
5467
|
+
},
|
|
5468
|
+
memory: {
|
|
5469
|
+
okDetail: 'Memory has recorded errors and/or decisions.',
|
|
5470
|
+
failDetail: 'Memory index has no errors or decisions yet (optional until first WRITE).',
|
|
5471
|
+
autofix: { text: '/agent-kit-onboard', subject: '/agent-kit-onboard', destination: 'chatInput', label: 'Autofix' },
|
|
5472
|
+
},
|
|
5473
|
+
git: {
|
|
5474
|
+
okDetail: 'Git branch signal is available.',
|
|
5475
|
+
failDetail: 'Git repository signal missing (no branch).',
|
|
5476
|
+
autofix: { text: 'git status', subject: 'git status', destination: 'terminal', label: 'Autofix' },
|
|
5477
|
+
},
|
|
5478
|
+
config: {
|
|
5479
|
+
okDetail: 'Workspace config is loaded.',
|
|
5480
|
+
failDetail: 'Workspace config is missing or not loaded.',
|
|
5481
|
+
autofix: { text: 'agent-kit doctor', subject: 'agent-kit doctor', destination: 'terminal', label: 'Autofix' },
|
|
5482
|
+
},
|
|
5483
|
+
};
|
|
5484
|
+
|
|
5485
|
+
/** Vitals grouping: checks grouped into vital systems for the diagnosis dashboard. */
|
|
5486
|
+
const HEALTH_VITAL_GROUPS = [
|
|
5487
|
+
{ label: 'Planning spine', checks: ['plans', 'handoff'] },
|
|
5488
|
+
{ label: 'Agent surface', checks: ['agents', 'commands'] },
|
|
5489
|
+
{ label: 'Memory loop', checks: ['memory'] },
|
|
5490
|
+
{ label: 'Workspace', checks: ['git', 'config'] },
|
|
5491
|
+
];
|
|
5492
|
+
|
|
5493
|
+
function healthCheckSeverity(check, aggregateStatus) {
|
|
5494
|
+
if (check && check.ok) return 'ok';
|
|
5495
|
+
if (aggregateStatus === 'degraded') return 'degraded';
|
|
5496
|
+
if (aggregateStatus === 'error') return 'error';
|
|
5497
|
+
return 'warning';
|
|
5498
|
+
}
|
|
5499
|
+
|
|
5500
|
+
/* Single severity chrome mapping (ok | warning | degraded | error): tone drives
|
|
5501
|
+
the dot class, label drives text/aria, token names the [data-sev] CSS color. */
|
|
5502
|
+
const HEALTH_SEVERITY_CHROME = {
|
|
5503
|
+
ok: { tone: 'green', label: 'ok', token: 'green' },
|
|
5504
|
+
warning: { tone: 'yellow', label: 'warn', token: 'yellow' },
|
|
5505
|
+
degraded: { tone: 'orange', label: 'degraded', token: 'orange' },
|
|
5506
|
+
error: { tone: 'red', label: 'error', token: 'red' },
|
|
5507
|
+
};
|
|
5508
|
+
|
|
5509
|
+
function healthSeverityChrome(sev) {
|
|
5510
|
+
return HEALTH_SEVERITY_CHROME[sev] || { tone: 'gray', label: sev || 'unknown', token: 'text-muted' };
|
|
5511
|
+
}
|
|
5512
|
+
|
|
5513
|
+
function healthSeverityLabel(sev) {
|
|
5514
|
+
return healthSeverityChrome(sev).label;
|
|
5515
|
+
}
|
|
5516
|
+
|
|
5517
|
+
function healthDotClass(sev) {
|
|
5518
|
+
return `dot-${healthSeverityChrome(sev).tone}`;
|
|
5519
|
+
}
|
|
5520
|
+
|
|
5521
|
+
function toggleHealthCheck(checkId) {
|
|
5522
|
+
const card = document.getElementById('health-card-' + checkId);
|
|
5523
|
+
if (!card) return;
|
|
5524
|
+
const open = card.classList.toggle('is-expanded');
|
|
5525
|
+
const btn = card.querySelector('.health-item');
|
|
5526
|
+
if (btn) btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
5527
|
+
}
|
|
5528
|
+
|
|
5529
|
+
function healthCheckKeydown(event, checkId) {
|
|
5530
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
5531
|
+
event.preventDefault();
|
|
5532
|
+
toggleHealthCheck(checkId);
|
|
4481
5533
|
}
|
|
4482
5534
|
}
|
|
4483
5535
|
|
|
4484
|
-
// =====
|
|
5536
|
+
// ===== Memory tab: interactive error rows (expand + copy fix prompt) =====
|
|
5537
|
+
function toggleMemoryError(idx) {
|
|
5538
|
+
const card = document.getElementById('memory-error-' + idx);
|
|
5539
|
+
if (!card) return;
|
|
5540
|
+
const open = card.classList.toggle('is-expanded');
|
|
5541
|
+
const btn = card.querySelector('.memory-error-head');
|
|
5542
|
+
if (btn) btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
5543
|
+
}
|
|
5544
|
+
|
|
5545
|
+
/**
|
|
5546
|
+
* One expandable error row: header toggles detail (error / cause / solution /
|
|
5547
|
+
* files / tags), and a copy-only fix prompt names the chat input as destination.
|
|
5548
|
+
*/
|
|
5549
|
+
function renderMemoryErrorRow(entry, idx) {
|
|
5550
|
+
const title = entry.title || entry.id;
|
|
5551
|
+
const dateLabel = entry.date || (entry.modifiedAt ? entry.modifiedAt.slice(0, 10) : '');
|
|
5552
|
+
const fixPrompt = [
|
|
5553
|
+
`Fix this recorded problem class: ${title}`,
|
|
5554
|
+
entry.error ? `Symptom: ${entry.error}` : '',
|
|
5555
|
+
entry.solution ? `Known solution direction: ${entry.solution}` : '',
|
|
5556
|
+
entry.path || `.cursor/memory/errors/${entry.id}.md`,
|
|
5557
|
+
].filter(Boolean).join('\n');
|
|
5558
|
+
const fields = [
|
|
5559
|
+
['Error', entry.error],
|
|
5560
|
+
['Cause', entry.cause],
|
|
5561
|
+
['Solution', entry.solution],
|
|
5562
|
+
].filter(([, body]) => body);
|
|
5563
|
+
return `
|
|
5564
|
+
<div class="memory-error-card" id="memory-error-${idx}">
|
|
5565
|
+
<button type="button" class="memory-error-head" aria-expanded="false" aria-controls="memory-error-detail-${idx}" onclick="toggleMemoryError(${idx})">
|
|
5566
|
+
<span class="memory-error-title">${escapeHtml(title)}</span>
|
|
5567
|
+
${dateLabel ? `<span class="memory-error-date">${escapeHtml(dateLabel)}</span>` : ''}
|
|
5568
|
+
<span class="memory-error-chevron" aria-hidden="true">›</span>
|
|
5569
|
+
</button>
|
|
5570
|
+
<div class="memory-error-detail" id="memory-error-detail-${idx}">
|
|
5571
|
+
${fields.map(([label, body]) => `
|
|
5572
|
+
<div class="memory-error-field">
|
|
5573
|
+
<div class="memory-error-field-label">${escapeHtml(label)}</div>
|
|
5574
|
+
<div class="memory-error-field-body">${escapeHtml(body)}</div>
|
|
5575
|
+
</div>
|
|
5576
|
+
`).join('')}
|
|
5577
|
+
${entry.files ? `<div class="memory-error-field"><div class="memory-error-field-label">Files</div><div class="memory-error-files">${escapeHtml(entry.files)}</div></div>` : ''}
|
|
5578
|
+
${(entry.tags || []).length > 0 ? `<div class="memory-tag-row">${entry.tags.map(t => `<span class="memory-tag">${escapeHtml(t)}</span>`).join('')}</div>` : ''}
|
|
5579
|
+
<div class="memory-error-actions">
|
|
5580
|
+
<button type="button" class="memory-error-copy-btn" onclick="event.stopPropagation(); ${copyForPasteHandler(fixPrompt, 'fix prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('fix prompt', 'chatInput'))}">Copy fix prompt</button>
|
|
5581
|
+
</div>
|
|
5582
|
+
</div>
|
|
5583
|
+
</div>
|
|
5584
|
+
`;
|
|
5585
|
+
}
|
|
5586
|
+
|
|
5587
|
+
/** @deprecated Prefer toggleHealthCheck; kept for any leftover callers. */
|
|
4485
5588
|
function showHealthInfo(el, ok) {
|
|
4486
|
-
// Remove any existing message siblings
|
|
4487
5589
|
const parent = el.parentElement;
|
|
4488
5590
|
const existing = parent.querySelector('.health-message');
|
|
4489
5591
|
if (existing) { existing.remove(); return; }
|
|
@@ -4571,7 +5673,8 @@ function updateTransportChrome() {
|
|
|
4571
5673
|
if (!statusDot || !statusLabel) return;
|
|
4572
5674
|
|
|
4573
5675
|
const healthStatus = data?.health?.status || 'ok';
|
|
4574
|
-
const
|
|
5676
|
+
const healthChrome = healthSeverityChrome(healthStatus);
|
|
5677
|
+
const healthTone = healthChrome.tone;
|
|
4575
5678
|
|
|
4576
5679
|
if (sseReconnecting) {
|
|
4577
5680
|
statusDot.className = 'dot dot-yellow dot-pulse reconnecting-pulse';
|
|
@@ -4587,10 +5690,14 @@ function updateTransportChrome() {
|
|
|
4587
5690
|
statusDot.className = healthStatus === 'ok'
|
|
4588
5691
|
? 'dot dot-green'
|
|
4589
5692
|
: `dot dot-${healthTone} dot-pulse`;
|
|
4590
|
-
statusLabel.textContent = healthStatus === 'ok'
|
|
5693
|
+
statusLabel.textContent = healthStatus === 'ok'
|
|
5694
|
+
? 'Live'
|
|
5695
|
+
: healthChrome.label.charAt(0).toUpperCase() + healthChrome.label.slice(1);
|
|
4591
5696
|
} else {
|
|
4592
5697
|
statusDot.className = `dot dot-${healthStatus === 'ok' ? 'yellow' : healthTone}`;
|
|
4593
|
-
statusLabel.textContent = healthStatus === 'ok'
|
|
5698
|
+
statusLabel.textContent = healthStatus === 'ok'
|
|
5699
|
+
? 'Polling'
|
|
5700
|
+
: healthChrome.label.charAt(0).toUpperCase() + healthChrome.label.slice(1);
|
|
4594
5701
|
}
|
|
4595
5702
|
|
|
4596
5703
|
if (sseTimer) {
|
|
@@ -4708,6 +5815,7 @@ function nowFingerprint(now) {
|
|
|
4708
5815
|
nextTodo: now.nextTodo?.id || null,
|
|
4709
5816
|
modifiedAt: now.modifiedAt,
|
|
4710
5817
|
lifecycle: now.lifecycle,
|
|
5818
|
+
busyOutsidePlan: now.busyOutsidePlan?.active === true,
|
|
4711
5819
|
});
|
|
4712
5820
|
}
|
|
4713
5821
|
|
|
@@ -4772,7 +5880,6 @@ function captureUiState() {
|
|
|
4772
5880
|
return {
|
|
4773
5881
|
scrollTop: contentEl ? contentEl.scrollTop : 0,
|
|
4774
5882
|
focusKey,
|
|
4775
|
-
openAgents: Array.from(document.querySelectorAll('.agent-details.open')).map((el) => el.id),
|
|
4776
5883
|
panelScrolls: capturePanelScrollOffsets(),
|
|
4777
5884
|
openRecentPlanActionsKey,
|
|
4778
5885
|
};
|
|
@@ -4780,14 +5887,6 @@ function captureUiState() {
|
|
|
4780
5887
|
|
|
4781
5888
|
function restoreUiState(state) {
|
|
4782
5889
|
if (!state) return;
|
|
4783
|
-
for (const id of state.openAgents || []) {
|
|
4784
|
-
const el = document.getElementById(id);
|
|
4785
|
-
if (!el) continue;
|
|
4786
|
-
el.classList.add('open');
|
|
4787
|
-
const arrowId = id.replace(/^agent-details-/, 'agent-arrow-');
|
|
4788
|
-
const arrow = document.getElementById(arrowId);
|
|
4789
|
-
if (arrow) arrow.style.transform = 'rotate(180deg)';
|
|
4790
|
-
}
|
|
4791
5890
|
const contentEl = document.getElementById('content');
|
|
4792
5891
|
if (contentEl) {
|
|
4793
5892
|
if (pendingAnchorScroll && Date.now() < pendingAnchorScroll.until) {
|
|
@@ -4870,8 +5969,20 @@ function mergePlansForUi(d) {
|
|
|
4870
5969
|
.split('/')
|
|
4871
5970
|
.pop();
|
|
4872
5971
|
const enriched = byKey.get(key) || {};
|
|
4873
|
-
|
|
4874
|
-
|
|
5972
|
+
// Live status bar data: frontmatter to-do items are the source of truth.
|
|
5973
|
+
// Summary fields only fill in when items are absent (malformed frontmatter).
|
|
5974
|
+
const items = Array.isArray(raw.todos?.items) ? raw.todos.items : [];
|
|
5975
|
+
const fromItems = items.length > 0;
|
|
5976
|
+
const completed = fromItems
|
|
5977
|
+
? items.filter((t) => t.status === 'completed').length
|
|
5978
|
+
: (raw.todos?.completed ?? enriched.progress?.completed ?? 0);
|
|
5979
|
+
const total = fromItems
|
|
5980
|
+
? items.length
|
|
5981
|
+
: (raw.todos?.total ?? enriched.progress?.total ?? 0);
|
|
5982
|
+
const inProgress = fromItems
|
|
5983
|
+
? items.filter((t) => t.status === 'in_progress').length
|
|
5984
|
+
: (raw.todos?.inProgress ?? 0);
|
|
5985
|
+
const nextActionTodo = planNextActionTodo(items);
|
|
4875
5986
|
let lifecycle = enriched.lifecycle;
|
|
4876
5987
|
if (!lifecycle) {
|
|
4877
5988
|
if (total > 0 && completed >= total && (raw.todos?.inProgress || 0) === 0) {
|
|
@@ -4887,14 +5998,16 @@ function mergePlansForUi(d) {
|
|
|
4887
5998
|
overview: raw.overview || enriched.overview || '',
|
|
4888
5999
|
modifiedAt: raw.modifiedAt || enriched.modifiedAt || null,
|
|
4889
6000
|
progressPct:
|
|
4890
|
-
|
|
4891
|
-
?
|
|
4892
|
-
:
|
|
4893
|
-
?
|
|
6001
|
+
total > 0
|
|
6002
|
+
? Math.round((completed / total) * 100)
|
|
6003
|
+
: typeof raw.progress === 'number'
|
|
6004
|
+
? raw.progress
|
|
4894
6005
|
: 0,
|
|
4895
|
-
progressLabel:
|
|
6006
|
+
progressLabel: `${completed} of ${total} complete`,
|
|
4896
6007
|
progressCompleted: completed,
|
|
4897
6008
|
progressTotal: total,
|
|
6009
|
+
progressInProgress: inProgress,
|
|
6010
|
+
nextActionTodo,
|
|
4898
6011
|
lifecycle,
|
|
4899
6012
|
// /run-plan-all layer from the semantic model (display-only; the panel
|
|
4900
6013
|
// never writes the queue). 'none' / null outside queue mode.
|
|
@@ -5157,21 +6270,26 @@ function renderPlansAccordion(d) {
|
|
|
5157
6270
|
${queueRolePill(p)}
|
|
5158
6271
|
<span class="recent-plan-progress">${escapeHtml(p.progressLabel)}</span>
|
|
5159
6272
|
</span>
|
|
5160
|
-
<span class="progress-bar plan-progress-bar" role="
|
|
6273
|
+
<span class="progress-bar plan-progress-bar" role="progressbar" aria-valuenow="${pct}" aria-valuemin="0" aria-valuemax="100" aria-valuetext="${escapeAttr(p.progressLabel)}" aria-label="Progress">
|
|
5161
6274
|
<span class="progress-fill plan-progress-fill ${color}${life.key === 'executing' ? ' is-executing' : ''}" style="width:${pct}%"></span>
|
|
5162
6275
|
</span>
|
|
5163
6276
|
<span class="plan-accordion-overview">${escapeHtml(p.overview || '\u2014')}</span>
|
|
5164
6277
|
<span class="plan-accordion-meta">
|
|
5165
6278
|
<span>${fmtDate(p.modifiedAt)}</span>
|
|
5166
6279
|
<span>${pct}%</span>
|
|
6280
|
+
${p.progressInProgress > 0 ? `<span>${p.progressInProgress} in progress</span>` : ''}
|
|
6281
|
+
${p.nextActionTodo ? `<span>Next: <code>${escapeHtml(p.nextActionTodo.id)}</code></span>` : ''}
|
|
5167
6282
|
</span>
|
|
5168
6283
|
</span>
|
|
5169
6284
|
<span class="plan-accordion-chevron" aria-hidden="true">\u25BC</span>
|
|
5170
6285
|
</button>
|
|
5171
6286
|
<div class="plan-accordion-panel" id="plan-panel-${escapeAttr(key)}" role="region" aria-labelledby="plan-trigger-${escapeAttr(key)}"${open ? '' : ' hidden'}>
|
|
5172
6287
|
<p class="plan-accordion-hint">Narrow panels open one plan at a time. Shift-click a header (or turn Multiple open on) to keep more expanded.</p>
|
|
6288
|
+
${p.nextActionTodo
|
|
6289
|
+
? `<div class="plan-next-action"><span class="plan-next-action-label">Next action:</span> <code>${escapeHtml(p.nextActionTodo.id)}</code>${p.nextActionTodo.content ? ` \u2014 ${escapeHtml(p.nextActionTodo.content)}` : ''}</div>`
|
|
6290
|
+
: ''}
|
|
5173
6291
|
<div class="plan-accordion-actions">
|
|
5174
|
-
|
|
6292
|
+
${planTabActions(p).map((action, idx) => renderPlanTabActionButton(action, p, key, idx)).join('')}
|
|
5175
6293
|
</div>
|
|
5176
6294
|
${todos.length > 0
|
|
5177
6295
|
? `<div class="plan-todo-list plan-todo-list-full" role="list">
|
|
@@ -5184,7 +6302,7 @@ function renderPlansAccordion(d) {
|
|
|
5184
6302
|
title="${escapeAttr(pathActionTitle(p.path))}"
|
|
5185
6303
|
onclick="${copyRepoPathHandler(p.path)}"
|
|
5186
6304
|
onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();${copyRepoPathHandler(p.path)}}">
|
|
5187
|
-
|
|
6305
|
+
${statusDot(t.status) ? `<span class="dot ${statusDot(t.status)}" aria-hidden="true"></span>` : ''}
|
|
5188
6306
|
<span style="flex:1"><code style="font-size:11px">${escapeHtml(t.id || '')}</code>${t.content ? ` \u2014 ${escapeHtml(t.content)}` : ''}</span>
|
|
5189
6307
|
<span style="color:var(--text-muted);font-size:11px">${escapeHtml(t.status)}</span>
|
|
5190
6308
|
</div>`,
|
|
@@ -5290,9 +6408,13 @@ function nowMetaIconSvg(kind, opts) {
|
|
|
5290
6408
|
/**
|
|
5291
6409
|
* Space-theme Cockpit/nav icon set (inline SVG, no font/fetch/dependency).
|
|
5292
6410
|
* Shared chrome SVG shell with header refresh / nowMetaIconSvg:
|
|
5293
|
-
* viewBox 0 0 16 16, stroke
|
|
6411
|
+
* viewBox 0 0 16 16, stroke 1.5, round caps/joins, CSS box via --mc-chrome-icon-size.
|
|
6412
|
+
* Legibility floor (holds at every render size, hero included): no feature
|
|
6413
|
+
* smaller than the 1.5 stroke; stroke-edge clearance between adjacent
|
|
6414
|
+
* features >= 1 unit. Detail that cannot meet the floor is drawn filled
|
|
6415
|
+
* (fill="currentColor" stroke="none"), never as a sub-stroke stroked shape.
|
|
5294
6416
|
* Static path markup only; never concatenate untrusted text into the SVG.
|
|
5295
|
-
* @param {'current-mission'|'monitor'|'field-report'|'checklist'|'more-sections'|'overview'|'plans'|'activity'|'agents'|'skills'|'commands'|'health'|'git'|'memory'|'terminals'|'processes'|'config'} kind
|
|
6417
|
+
* @param {'current-mission'|'monitor'|'field-report'|'checklist'|'more-sections'|'overview'|'plans'|'activity'|'agents'|'skills'|'skins'|'commands'|'health'|'git'|'memory'|'terminals'|'processes'|'config'} kind
|
|
5296
6418
|
* @param {{ decorative?: boolean }} [opts] decorative true (default): aria-hidden next to a visible label.
|
|
5297
6419
|
* decorative false: role=img + aria-label + title for icon-only controls (e.g. more-sections).
|
|
5298
6420
|
*/
|
|
@@ -5310,6 +6432,7 @@ function spaceIconSvg(kind, opts) {
|
|
|
5310
6432
|
activity: 'Activity',
|
|
5311
6433
|
agents: 'Agents',
|
|
5312
6434
|
skills: 'Skills',
|
|
6435
|
+
skins: 'Skins',
|
|
5313
6436
|
commands: 'Commands',
|
|
5314
6437
|
health: 'Health',
|
|
5315
6438
|
git: 'Git',
|
|
@@ -5319,37 +6442,35 @@ function spaceIconSvg(kind, opts) {
|
|
|
5319
6442
|
config: 'Config',
|
|
5320
6443
|
};
|
|
5321
6444
|
const paths = {
|
|
5322
|
-
// Rocket (mission) —
|
|
6445
|
+
// Rocket (mission) — window is a solid dot (a stroked ring here would sit below the stroke floor)
|
|
5323
6446
|
'current-mission':
|
|
5324
6447
|
'<path d="M8 2c1.4 2 2.5 4 2.5 6a2.5 2.5 0 01-5 0C5.5 6 6.6 4 8 2z"/>' +
|
|
5325
|
-
'<circle cx="8" cy="7.5" r="
|
|
6448
|
+
'<circle cx="8" cy="7.5" r="1" fill="currentColor" stroke="none"/>' +
|
|
5326
6449
|
'<path d="M6 10.5l-1 2M10 10.5l1 2M7 13h2"/>',
|
|
5327
|
-
// Radar (monitor) —
|
|
6450
|
+
// Radar (monitor) — two rings + sweep wedge; a third ring or centre dot would sit below the stroke floor
|
|
5328
6451
|
monitor:
|
|
5329
6452
|
'<circle cx="8" cy="8" r="5.5"/>' +
|
|
5330
|
-
'<circle cx="8" cy="8" r="
|
|
5331
|
-
'<
|
|
5332
|
-
'<path d="M8 8L12.8 4.2A5.5 5.5 0 0 1 13.3 7.2Z"/>' +
|
|
5333
|
-
'<circle cx="8" cy="8" r=".55"/>',
|
|
6453
|
+
'<circle cx="8" cy="8" r="2.75"/>' +
|
|
6454
|
+
'<path d="M8 8L12.8 4.2A5.5 5.5 0 0 1 13.3 7.2Z"/>',
|
|
5334
6455
|
// Clipboard (Flight Log) — matches copy/source affordance; not PTT radio
|
|
5335
6456
|
'field-report':
|
|
5336
6457
|
'<rect x="4.5" y="3.5" width="7" height="10" rx="1.2"/>' +
|
|
5337
6458
|
'<path d="M6.5 3.5V2.5a1.5 1.5 0 013 0v1"/>' +
|
|
5338
6459
|
'<path d="M6.5 7h3M6.5 9.5h3"/>',
|
|
5339
|
-
// Checklist lines
|
|
6460
|
+
// Checklist lines
|
|
5340
6461
|
checklist:
|
|
5341
6462
|
'<path d="M3.5 4.5h9M3.5 8h6.5M3.5 11.5h5.5"/>' +
|
|
5342
6463
|
'<path d="M10.5 10.5l1.2 1.2L14 9.5"/>',
|
|
5343
|
-
//
|
|
6464
|
+
// Ellipsis (more sections) — solid dots: stroked rings at 4px spacing land on exact tangency
|
|
5344
6465
|
'more-sections':
|
|
5345
|
-
'<circle cx="4" cy="8" r=".
|
|
5346
|
-
'<circle cx="8" cy="8" r=".
|
|
5347
|
-
'<circle cx="12" cy="8" r=".
|
|
5348
|
-
// House (overview / home)
|
|
6466
|
+
'<circle cx="4" cy="8" r="1.1" fill="currentColor" stroke="none"/>' +
|
|
6467
|
+
'<circle cx="8" cy="8" r="1.1" fill="currentColor" stroke="none"/>' +
|
|
6468
|
+
'<circle cx="12" cy="8" r="1.1" fill="currentColor" stroke="none"/>',
|
|
6469
|
+
// House (overview / home) — roof + walls + door as one coherent glyph; door interior stays above the stroke floor
|
|
5349
6470
|
overview:
|
|
5350
|
-
'<path d="M2.5
|
|
5351
|
-
'<path d="M4.
|
|
5352
|
-
'<path d="
|
|
6471
|
+
'<path d="M2.5 7.5L8 2.75l5.5 4.75"/>' +
|
|
6472
|
+
'<path d="M4.25 7.75v6.5h7.5v-6.5"/>' +
|
|
6473
|
+
'<path d="M6.5 14.25v-3.25h3v3.25"/>',
|
|
5353
6474
|
// Document (plans)
|
|
5354
6475
|
plans:
|
|
5355
6476
|
'<path d="M4.5 2.5h5l4 4v8.5a1 1 0 01-1 1h-8a1 1 0 01-1-1v-12a1 1 0 011-1z"/>' +
|
|
@@ -5361,10 +6482,14 @@ function spaceIconSvg(kind, opts) {
|
|
|
5361
6482
|
agents:
|
|
5362
6483
|
'<circle cx="8" cy="5.5" r="2.5"/>' +
|
|
5363
6484
|
'<path d="M3 14.5c0-3 2.2-5 5-5s5 2 5 5"/>',
|
|
5364
|
-
// Gear (skills)
|
|
6485
|
+
// Gear (skills) — ring + 8 spokes outside it; arcs/teeth cut into the ring would sink below the stroke floor
|
|
5365
6486
|
skills:
|
|
5366
6487
|
'<circle cx="8" cy="8" r="2"/>' +
|
|
5367
|
-
'<path d="
|
|
6488
|
+
'<path d="M8 5V3.5M8 11v1.5M5 8H3.5M11 8h1.5M10.1 5.9l1.1-1.1M10.1 10.1l1.1 1.1M5.9 10.1l-1.1 1.1M5.9 5.9l-1.1-1.1"/>',
|
|
6489
|
+
// Overlapping swatches (skins) — distinct from Skills gear
|
|
6490
|
+
skins:
|
|
6491
|
+
'<rect x="2.5" y="3.5" width="7" height="7" rx="1.25"/>' +
|
|
6492
|
+
'<rect x="6.5" y="5.5" width="7" height="7" rx="1.25"/>',
|
|
5368
6493
|
// Terminal (commands)
|
|
5369
6494
|
commands:
|
|
5370
6495
|
'<path d="M2.5 3.5l5 5-5 5"/>' +
|
|
@@ -5378,11 +6503,11 @@ function spaceIconSvg(kind, opts) {
|
|
|
5378
6503
|
'<path d="M10.5 4.5v7a2 2 0 01-2 2h-2"/>' +
|
|
5379
6504
|
'<path d="M6.5 11.5a2 2 0 100-4 2 2 0 000 4z"/>' +
|
|
5380
6505
|
'<path d="M10.5 6.5v2"/>',
|
|
5381
|
-
// Chip (memory)
|
|
6506
|
+
// Chip (memory) — two internal lines 3 units apart; a third line would drop clearance below the stroke floor
|
|
5382
6507
|
memory:
|
|
5383
6508
|
'<path d="M4.5 2.5h7a1 1 0 011 1v9a1 1 0 01-1 1h-7a1 1 0 01-1-1v-9a1 1 0 011-1z"/>' +
|
|
5384
6509
|
'<path d="M6.5 1.5v2M9.5 1.5v2M6.5 12.5v2M9.5 12.5v2"/>' +
|
|
5385
|
-
'<path d="M6 6.5h4M6
|
|
6510
|
+
'<path d="M6 6.5h4M6 9.5h2.5"/>',
|
|
5386
6511
|
// Screen (terminals)
|
|
5387
6512
|
terminals:
|
|
5388
6513
|
'<path d="M1.5 3.5h13a1 1 0 011 1v7a1 1 0 01-1 1H1.5a1 1 0 01-1-1v-7a1 1 0 011-1z"/>' +
|
|
@@ -5405,7 +6530,7 @@ function spaceIconSvg(kind, opts) {
|
|
|
5405
6530
|
const a11y = decorative
|
|
5406
6531
|
? 'aria-hidden="true"'
|
|
5407
6532
|
: `role="img" aria-label="${label}" title="${label}"`;
|
|
5408
|
-
return `<svg class="space-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="
|
|
6533
|
+
return `<svg class="space-icon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" ${a11y}>${body}</svg>`;
|
|
5409
6534
|
}
|
|
5410
6535
|
|
|
5411
6536
|
/**
|
|
@@ -5504,9 +6629,10 @@ function flightLogKindClassName(kind) {
|
|
|
5504
6629
|
}
|
|
5505
6630
|
|
|
5506
6631
|
/**
|
|
5507
|
-
* Gaps card on Flight Log (
|
|
6632
|
+
* Gaps card on Flight Log (NOW large / Earlier smaller). Labels locked.
|
|
5508
6633
|
* Kind selects Mission Control palette tokens (typed notification chrome).
|
|
5509
|
-
*
|
|
6634
|
+
* One composed action button per entry (semantic model action; fallback compose).
|
|
6635
|
+
* @param {{ text?: string, at?: string|null, sourcePath?: string, variant?: string, kind?: string, action?: { label?: string, command?: string, sourcePath?: string } }} entry
|
|
5510
6636
|
* @param {number} idx
|
|
5511
6637
|
*/
|
|
5512
6638
|
function renderFlightLogCard(entry, idx) {
|
|
@@ -5517,7 +6643,7 @@ function renderFlightLogCard(entry, idx) {
|
|
|
5517
6643
|
? entry.sourcePath.trim()
|
|
5518
6644
|
: '.cursor/HANDOFF.md';
|
|
5519
6645
|
const variant = entry?.variant === 'past' ? 'past' : 'current';
|
|
5520
|
-
const label = variant === 'current' ? '
|
|
6646
|
+
const label = variant === 'current' ? 'NOW' : 'Earlier';
|
|
5521
6647
|
const kind = flightLogMessageKind(text, { kind: entry?.kind });
|
|
5522
6648
|
const kindClass = flightLogKindClassName(kind);
|
|
5523
6649
|
const metaBits = [];
|
|
@@ -5525,8 +6651,16 @@ function renderFlightLogCard(entry, idx) {
|
|
|
5525
6651
|
metaBits.push(escapeHtml(String(entry.at)));
|
|
5526
6652
|
}
|
|
5527
6653
|
metaBits.push(escapeHtml(sourcePath));
|
|
5528
|
-
const
|
|
5529
|
-
const
|
|
6654
|
+
const action = entry?.action && typeof entry.action === 'object' ? entry.action : null;
|
|
6655
|
+
const actionLabel =
|
|
6656
|
+
typeof action?.label === 'string' && action.label.trim()
|
|
6657
|
+
? action.label.trim()
|
|
6658
|
+
: 'Copy fix prompt';
|
|
6659
|
+
const actionCommand =
|
|
6660
|
+
typeof action?.command === 'string' && action.command.trim()
|
|
6661
|
+
? action.command
|
|
6662
|
+
: `${variant === 'current' ? 'Act on these open residuals:' : 'Act on these earlier residuals:'}\n${text}\n${sourcePath}`;
|
|
6663
|
+
const copyActionHandler = copyForPasteHandler(actionCommand, 'fix prompt', 'chatInput');
|
|
5530
6664
|
const cardClass =
|
|
5531
6665
|
variant === 'current'
|
|
5532
6666
|
? `flight-log-card flight-log-card-current ${kindClass}`
|
|
@@ -5538,8 +6672,7 @@ function renderFlightLogCard(entry, idx) {
|
|
|
5538
6672
|
<div class="flight-log-card-text">${escapeHtml(text)}</div>
|
|
5539
6673
|
<div class="flight-log-card-meta">${metaBits.join(' · ')}</div>
|
|
5540
6674
|
<div class="flight-log-card-actions">
|
|
5541
|
-
<button type="button" class="status-bar-btn" data-focus-key="flight-log-
|
|
5542
|
-
<button type="button" class="status-bar-btn" data-focus-key="flight-log-copy-path-${variant}-${idx}" onclick="${copyPathHandler}" style="font-size:11px;padding:3px 10px" title="${escapeAttr(pathActionTitle(sourcePath))}">${PATH_COPY_LABEL}</button>
|
|
6675
|
+
<button type="button" class="status-bar-btn" data-focus-key="flight-log-action-${variant}-${idx}" onclick="${copyActionHandler}" style="font-size:11px;padding:3px 10px" title="${escapeAttr(copyActionTitle('fix prompt', 'chatInput'))}">${escapeHtml(actionLabel)}</button>
|
|
5543
6676
|
</div>
|
|
5544
6677
|
</div>
|
|
5545
6678
|
`;
|
|
@@ -5547,8 +6680,9 @@ function renderFlightLogCard(entry, idx) {
|
|
|
5547
6680
|
|
|
5548
6681
|
/**
|
|
5549
6682
|
* Operator Warning card on Flight Log (API/usage or orchestrator heads-up).
|
|
5550
|
-
* No Review all / Resolve all / cadence CTAs.
|
|
5551
|
-
*
|
|
6683
|
+
* No Review all / Resolve all / cadence CTAs. One composed action button
|
|
6684
|
+
* per entry (semantic model action; fallback compose).
|
|
6685
|
+
* @param {{ id?: string, kind?: string, title?: string, text?: string, sourcePath?: string, action?: { label?: string, command?: string, sourcePath?: string } }} warning
|
|
5552
6686
|
* @param {number} idx
|
|
5553
6687
|
*/
|
|
5554
6688
|
function renderFlightLogWarningCard(warning, idx) {
|
|
@@ -5563,8 +6697,17 @@ function renderFlightLogWarningCard(warning, idx) {
|
|
|
5563
6697
|
? warning.sourcePath.trim()
|
|
5564
6698
|
: '.cursor/HANDOFF.md';
|
|
5565
6699
|
const kind = typeof warning?.kind === 'string' ? warning.kind : 'warning';
|
|
5566
|
-
const
|
|
5567
|
-
const
|
|
6700
|
+
const actionSubject = kind === 'api_limit' ? 'recovery prompt' : 'follow-up prompt';
|
|
6701
|
+
const action = warning?.action && typeof warning.action === 'object' ? warning.action : null;
|
|
6702
|
+
const actionLabel =
|
|
6703
|
+
typeof action?.label === 'string' && action.label.trim()
|
|
6704
|
+
? action.label.trim()
|
|
6705
|
+
: `Copy ${actionSubject}`;
|
|
6706
|
+
const actionCommand =
|
|
6707
|
+
typeof action?.command === 'string' && action.command.trim()
|
|
6708
|
+
? action.command
|
|
6709
|
+
: `${kind === 'api_limit' ? 'Resume after this quota pause:' : 'Act on this heads-up:'}\n${text}\n${sourcePath}`;
|
|
6710
|
+
const copyActionHandler = copyForPasteHandler(actionCommand, actionSubject, 'chatInput');
|
|
5568
6711
|
const aria = `${title}: ${text}`;
|
|
5569
6712
|
return `
|
|
5570
6713
|
<div class="flight-log-card flight-log-card-warning" role="listitem" tabindex="0" data-focus-key="flight-log-warning-${idx}" data-warning-kind="${escapeAttr(kind)}" aria-label="${escapeAttr(aria)}">
|
|
@@ -5572,8 +6715,7 @@ function renderFlightLogWarningCard(warning, idx) {
|
|
|
5572
6715
|
<div class="flight-log-card-text">${escapeHtml(text)}</div>
|
|
5573
6716
|
<div class="flight-log-card-meta">${escapeHtml(sourcePath)}</div>
|
|
5574
6717
|
<div class="flight-log-card-actions">
|
|
5575
|
-
<button type="button" class="status-bar-btn" data-focus-key="flight-log-warning-
|
|
5576
|
-
<button type="button" class="status-bar-btn" data-focus-key="flight-log-warning-copy-path-${idx}" onclick="${copyPathHandler}" style="font-size:11px;padding:3px 10px" title="${escapeAttr(pathActionTitle(sourcePath))}">${PATH_COPY_LABEL}</button>
|
|
6718
|
+
<button type="button" class="status-bar-btn" data-focus-key="flight-log-warning-action-${idx}" onclick="${copyActionHandler}" style="font-size:11px;padding:3px 10px" title="${escapeAttr(copyActionTitle(actionSubject, 'chatInput'))}">${escapeHtml(actionLabel)}</button>
|
|
5577
6719
|
</div>
|
|
5578
6720
|
</div>
|
|
5579
6721
|
`;
|
|
@@ -5581,9 +6723,9 @@ function renderFlightLogWarningCard(warning, idx) {
|
|
|
5581
6723
|
|
|
5582
6724
|
/**
|
|
5583
6725
|
* Quiet-state open-triage row (untriaged external review). Prompt chrome;
|
|
5584
|
-
*
|
|
6726
|
+
* one composed triage-command button per row. No Review all / Resolve all.
|
|
5585
6727
|
* Kind class from flightLogKindClassName('prompt') (SoT map; not a literal).
|
|
5586
|
-
* @param {{ id?: string, label?: string, sourcePath?: string, action?: { target?: string, label?: string, subject?: string } }} item
|
|
6728
|
+
* @param {{ id?: string, label?: string, sourcePath?: string, action?: { target?: string, label?: string, subject?: string, command?: string } }} item
|
|
5587
6729
|
* @param {number} idx
|
|
5588
6730
|
*/
|
|
5589
6731
|
function renderFlightLogQuietOpenTriageCard(item, idx) {
|
|
@@ -5600,8 +6742,19 @@ function renderFlightLogQuietOpenTriageCard(item, idx) {
|
|
|
5600
6742
|
typeof item?.action?.target === 'string' && item.action.target.trim()
|
|
5601
6743
|
? item.action.target.trim()
|
|
5602
6744
|
: `/plan-review-triage ${sourcePath}`;
|
|
5603
|
-
const
|
|
5604
|
-
|
|
6745
|
+
const actionSubject =
|
|
6746
|
+
typeof item?.action?.subject === 'string' && item.action.subject.trim()
|
|
6747
|
+
? item.action.subject.trim()
|
|
6748
|
+
: 'triage command';
|
|
6749
|
+
const actionLabel =
|
|
6750
|
+
typeof item?.action?.label === 'string' && item.action.label.trim()
|
|
6751
|
+
? item.action.label.trim()
|
|
6752
|
+
: 'Copy triage command';
|
|
6753
|
+
const actionCommand =
|
|
6754
|
+
typeof item?.action?.command === 'string' && item.action.command.trim()
|
|
6755
|
+
? item.action.command
|
|
6756
|
+
: triageCmd;
|
|
6757
|
+
const copyActionHandler = copyForPasteHandler(actionCommand, actionSubject, 'chatInput');
|
|
5605
6758
|
const aria = `Review: ${label}`;
|
|
5606
6759
|
const kindClass = flightLogKindClassName('prompt');
|
|
5607
6760
|
return `
|
|
@@ -5610,8 +6763,7 @@ function renderFlightLogQuietOpenTriageCard(item, idx) {
|
|
|
5610
6763
|
<div class="flight-log-card-text">${escapeHtml(label)}</div>
|
|
5611
6764
|
<div class="flight-log-card-meta">${escapeHtml(sourcePath)}</div>
|
|
5612
6765
|
<div class="flight-log-card-actions">
|
|
5613
|
-
<button type="button" class="status-bar-btn" data-focus-key="flight-log-open-triage-
|
|
5614
|
-
<button type="button" class="status-bar-btn" data-focus-key="flight-log-open-triage-path-${idx}" onclick="${copyPathHandler}" style="font-size:11px;padding:3px 10px" title="${escapeAttr(pathActionTitle(sourcePath))}">${PATH_COPY_LABEL}</button>
|
|
6766
|
+
<button type="button" class="status-bar-btn" data-focus-key="flight-log-open-triage-action-${idx}" onclick="${copyActionHandler}" style="font-size:11px;padding:3px 10px" title="${escapeAttr(copyActionTitle(actionSubject, 'chatInput'))}">${escapeHtml(actionLabel)}</button>
|
|
5615
6767
|
</div>
|
|
5616
6768
|
</div>
|
|
5617
6769
|
`;
|
|
@@ -5691,6 +6843,7 @@ function renderAttentionPanel(d, attentionChanged) {
|
|
|
5691
6843
|
sourcePath: e.sourcePath || sourcePath,
|
|
5692
6844
|
variant: 'past',
|
|
5693
6845
|
kind: e.kind || null,
|
|
6846
|
+
action: e.action && typeof e.action === 'object' ? e.action : null,
|
|
5694
6847
|
},
|
|
5695
6848
|
idx,
|
|
5696
6849
|
),
|
|
@@ -5736,11 +6889,15 @@ function renderAttentionPanel(d, attentionChanged) {
|
|
|
5736
6889
|
sourcePath,
|
|
5737
6890
|
variant: 'current',
|
|
5738
6891
|
kind: typeof fl?.currentKind === 'string' ? fl.currentKind : null,
|
|
6892
|
+
action:
|
|
6893
|
+
fl?.currentAction && typeof fl.currentAction === 'object'
|
|
6894
|
+
? fl.currentAction
|
|
6895
|
+
: null,
|
|
5739
6896
|
},
|
|
5740
6897
|
0,
|
|
5741
6898
|
)
|
|
5742
|
-
: `<div class="flight-log-card flight-log-card-current flight-log-kind-ok" role="status" data-flight-log-kind="ok" aria-label="No
|
|
5743
|
-
<span class="flight-log-card-label">
|
|
6899
|
+
: `<div class="flight-log-card flight-log-card-current flight-log-kind-ok" role="status" data-flight-log-kind="ok" aria-label="No Gaps now">
|
|
6900
|
+
<span class="flight-log-card-label">NOW</span>
|
|
5744
6901
|
<div class="flight-log-card-text" style="color:var(--text-muted)">Quiet · nothing live</div>
|
|
5745
6902
|
</div>`;
|
|
5746
6903
|
body = `${warningsBlock}<div class="flight-log-stack" role="list">${currentCard}${pastCards}</div>`;
|
|
@@ -5764,6 +6921,7 @@ function renderAttentionPanel(d, attentionChanged) {
|
|
|
5764
6921
|
<div class="card${attentionChanged ? ' card-flash' : ''}" id="attention-panel">
|
|
5765
6922
|
<div class="card-header now-panel-header">
|
|
5766
6923
|
<span class="card-title">${spaceIconSvg('field-report')}Flight Log</span>
|
|
6924
|
+
${renderBusyOutsideChip(d.missionControl?.now)}
|
|
5767
6925
|
<span class="section-subtitle" style="margin-left:auto">${escapeHtml(countLabel)}</span>
|
|
5768
6926
|
</div>
|
|
5769
6927
|
<div class="panel-scroll-body">${body}</div>
|
|
@@ -5861,7 +7019,9 @@ function flightLogFingerprint(d) {
|
|
|
5861
7019
|
)
|
|
5862
7020
|
.join('\n')
|
|
5863
7021
|
: '';
|
|
5864
|
-
|
|
7022
|
+
// Busy-outside-plan header chip must re-render the card on flip.
|
|
7023
|
+
const busy = (d.missionControl?.now ?? d.now)?.busyOutsidePlan?.active === true;
|
|
7024
|
+
return `${current}\0${past}\0${warnings}\0${openTriages}\0${busy ? 'busy' : ''}`;
|
|
5865
7025
|
}
|
|
5866
7026
|
|
|
5867
7027
|
// Step progress bar: one segment per plan step (completed / current / remaining).
|
|
@@ -5997,6 +7157,14 @@ function renderNowStepper(now, handoff, status, meta) {
|
|
|
5997
7157
|
`;
|
|
5998
7158
|
}
|
|
5999
7159
|
|
|
7160
|
+
// Live "busy outside the plan" header chip (Current mission + Flight Log).
|
|
7161
|
+
// Text-only on the advice-family blue tokens; not a Flight Log entry, so the
|
|
7162
|
+
// Gaps voice, kind, and NOW/Earlier/All clear label contracts stay untouched.
|
|
7163
|
+
function renderBusyOutsideChip(now) {
|
|
7164
|
+
if (!now?.busyOutsidePlan?.active) return '';
|
|
7165
|
+
return `<span class="mc-busy-chip" role="status" aria-label="Agents working outside the active plan" title="Run-loop activity outside the active plan">Busy \u00b7 outside plan</span>`;
|
|
7166
|
+
}
|
|
7167
|
+
|
|
6000
7168
|
function renderNowExecutionPanel(d, nowChanged) {
|
|
6001
7169
|
const now = d.missionControl?.now || null;
|
|
6002
7170
|
const handoff = d.system?.handoff || null;
|
|
@@ -6015,6 +7183,7 @@ function renderNowExecutionPanel(d, nowChanged) {
|
|
|
6015
7183
|
const updatedSource = now?.modifiedAt || handoff?.lastUpdated || null;
|
|
6016
7184
|
|
|
6017
7185
|
const statusClass = `now-status now-status-${meta.key}${meta.live ? ' now-status-live' : ''}`;
|
|
7186
|
+
const busyChip = renderBusyOutsideChip(now);
|
|
6018
7187
|
|
|
6019
7188
|
// True idle: no HANDOFF plan reference (or non-mission status). Reserve empty IDLE for that case.
|
|
6020
7189
|
if (!showMission) {
|
|
@@ -6027,6 +7196,7 @@ function renderNowExecutionPanel(d, nowChanged) {
|
|
|
6027
7196
|
<span class="now-status-mark" aria-hidden="true">${meta.mark}</span>
|
|
6028
7197
|
${escapeHtml(meta.label)}
|
|
6029
7198
|
</span>
|
|
7199
|
+
${busyChip}
|
|
6030
7200
|
</div>
|
|
6031
7201
|
<div class="panel-scroll-body">
|
|
6032
7202
|
${renderEmptyStateCta({
|
|
@@ -6058,6 +7228,7 @@ function renderNowExecutionPanel(d, nowChanged) {
|
|
|
6058
7228
|
<span class="now-status-mark" aria-hidden="true">${meta.mark}</span>
|
|
6059
7229
|
${escapeHtml(meta.label)}
|
|
6060
7230
|
</span>
|
|
7231
|
+
${busyChip}
|
|
6061
7232
|
</div>
|
|
6062
7233
|
<div class="panel-scroll-body">
|
|
6063
7234
|
<div class="now-plan-name">${escapeHtml(planName)}</div>
|
|
@@ -6234,14 +7405,7 @@ function renderUnsafe() {
|
|
|
6234
7405
|
gitDot.className = 'dot dot-green';
|
|
6235
7406
|
}
|
|
6236
7407
|
|
|
6237
|
-
// Terminals / processes
|
|
6238
|
-
const termDot = document.getElementById('navTerminalsDot');
|
|
6239
|
-
termDot.className = `dot dot-${d.terminals.length > 0 ? 'blue' : 'gray'}`;
|
|
6240
|
-
|
|
6241
|
-
const procDot = document.getElementById('navProcessesDot');
|
|
6242
|
-
if (procDot) {
|
|
6243
|
-
procDot.className = `dot dot-${d.processes.length > 0 ? 'green' : 'gray'}`;
|
|
6244
|
-
}
|
|
7408
|
+
// Terminals / processes nav items carry count badges only (no decorative dots).
|
|
6245
7409
|
|
|
6246
7410
|
// Build full HTML string once
|
|
6247
7411
|
const parts = [];
|
|
@@ -6266,6 +7430,10 @@ function renderUnsafe() {
|
|
|
6266
7430
|
<div class="overview-stack">
|
|
6267
7431
|
${renderNowExecutionPanel(d, !!(nowChanged || handoffChanged))}
|
|
6268
7432
|
|
|
7433
|
+
${renderAttentionPanel(d, !!attentionChanged)}
|
|
7434
|
+
|
|
7435
|
+
${renderRecentPlanCards(d)}
|
|
7436
|
+
|
|
6269
7437
|
<div class="section-hero" id="hero-activity">
|
|
6270
7438
|
<div class="live-activity-header">
|
|
6271
7439
|
<span class="card-title">${spaceIconSvg('monitor')}Crew Monitor</span>
|
|
@@ -6285,23 +7453,46 @@ function renderUnsafe() {
|
|
|
6285
7453
|
const info = semanticEventInfo(ev.kind, ev.refs?.commitType);
|
|
6286
7454
|
const gloss = info.gloss || info.tag || ev.kind || 'event';
|
|
6287
7455
|
const staggerIdx = Math.min(idx, 5);
|
|
7456
|
+
const actor = crewEventActor(ev);
|
|
7457
|
+
const initials = agentInitials(actor);
|
|
6288
7458
|
const actionAttrs = activityTargetAttributes(ev, 'monitor-activity', { kindGloss: gloss });
|
|
6289
7459
|
const rowA11y = actionAttrs
|
|
6290
7460
|
? actionAttrs
|
|
6291
7461
|
: ` aria-label="${escapeAttr(`${gloss}: ${ev.label || ''}`)}"`;
|
|
7462
|
+
// Structured label spans: actor + verb stay fixed; later segments
|
|
7463
|
+
// shrink, and the low-signal plan filename ellipsises first.
|
|
7464
|
+
// Prefer refs.plan over sniffing the rendered string (truncation-safe).
|
|
7465
|
+
const planRef = ev.refs && ev.refs.plan ? String(ev.refs.plan) : '';
|
|
7466
|
+
const planBase = planRef.replace(/\.plan\.md$/i, '');
|
|
7467
|
+
const feedSegs = String(ev.label || '').split(' · ');
|
|
7468
|
+
const feedLabelHtml = feedSegs
|
|
7469
|
+
.map((seg, i) => {
|
|
7470
|
+
const isPlanSeg = Boolean(planRef) && (
|
|
7471
|
+
seg === planRef ||
|
|
7472
|
+
seg === planBase ||
|
|
7473
|
+
/\.plan\.md/i.test(seg)
|
|
7474
|
+
);
|
|
7475
|
+
const cls = i === 0
|
|
7476
|
+
? 'feed-seg feed-seg-actor'
|
|
7477
|
+
: i === 1
|
|
7478
|
+
? 'feed-seg feed-seg-verb'
|
|
7479
|
+
: isPlanSeg
|
|
7480
|
+
? 'feed-seg feed-seg-plan'
|
|
7481
|
+
: 'feed-seg feed-seg-mid';
|
|
7482
|
+
return `<span class="${cls}">${escapeHtml(seg)}</span>`;
|
|
7483
|
+
})
|
|
7484
|
+
.join('<span class="feed-sep" aria-hidden="true"> · </span>');
|
|
7485
|
+
const feedTitle = ev.labelFull || ev.label || '';
|
|
6292
7486
|
return `
|
|
6293
7487
|
<div class="monitor-row stagger-fade stagger-${staggerIdx}"${rowA11y}>
|
|
7488
|
+
<span class="monitor-row-avatar" title="${escapeAttr(actor)}" aria-hidden="true">${escapeHtml(initials)}</span>
|
|
6294
7489
|
<span class="monitor-row-chip" style="background:${info.bg};color:${info.color}" title="${escapeAttr(gloss)}" aria-hidden="true"><span class="monitor-row-icon">${info.icon}</span></span>
|
|
6295
|
-
<span class="feed-label"
|
|
6296
|
-
<span class="feed-time">${
|
|
7490
|
+
<span class="feed-label" title="${escapeAttr(feedTitle)}">${feedLabelHtml}</span>
|
|
7491
|
+
<span class="feed-time">${crewEventTime(ev, info)}</span>
|
|
6297
7492
|
</div>`;
|
|
6298
7493
|
}).join('')}
|
|
6299
7494
|
</div>
|
|
6300
7495
|
</div>
|
|
6301
|
-
|
|
6302
|
-
${renderAttentionPanel(d, !!attentionChanged)}
|
|
6303
|
-
|
|
6304
|
-
${renderRecentPlanCards(d)}
|
|
6305
7496
|
</div>
|
|
6306
7497
|
</div>
|
|
6307
7498
|
`);
|
|
@@ -6310,7 +7501,7 @@ function renderUnsafe() {
|
|
|
6310
7501
|
parts.push(`
|
|
6311
7502
|
<div class="content-section" id="section-plans">
|
|
6312
7503
|
<div class="section-title">
|
|
6313
|
-
|
|
7504
|
+
Plans
|
|
6314
7505
|
<span class="section-subtitle">${d.plans.length} total</span>
|
|
6315
7506
|
</div>
|
|
6316
7507
|
${renderPlansAccordion(d)}
|
|
@@ -6318,13 +7509,18 @@ function renderUnsafe() {
|
|
|
6318
7509
|
`);
|
|
6319
7510
|
|
|
6320
7511
|
// ===== Agents =====
|
|
7512
|
+
const agents = d.agents || [];
|
|
7513
|
+
const agentLockSvg = `<svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/></svg>`;
|
|
7514
|
+
const agentCreatePrompt = 'Create a new agent definition under .cursor/agents/ (markdown file with a description plus the behavior body). Ask me for the name and behavior first.';
|
|
7515
|
+
const agentEditableCount = agents.filter(a => a.kitManaged !== true).length;
|
|
6321
7516
|
parts.push(`
|
|
6322
7517
|
<div class="content-section" id="section-agents">
|
|
6323
7518
|
<div class="section-title">
|
|
6324
|
-
|
|
6325
|
-
<span class="section-subtitle">${
|
|
7519
|
+
Agents
|
|
7520
|
+
<span class="section-subtitle">${agents.length} agents · ${agentEditableCount} editable</span>
|
|
7521
|
+
<button type="button" class="git-action-btn primary agent-create-btn" data-focus-key="cta-agent-create" onclick="${copyForPasteHandler(agentCreatePrompt, 'create prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('create prompt', 'chatInput'))}">Copy create prompt</button>
|
|
6326
7522
|
</div>
|
|
6327
|
-
${
|
|
7523
|
+
${agents.length === 0
|
|
6328
7524
|
? renderEmptyStateCta({
|
|
6329
7525
|
headline: 'No agents aboard',
|
|
6330
7526
|
supportHtml: 'Add <code>.md</code> files under <code>.cursor/agents/</code>.',
|
|
@@ -6337,34 +7533,49 @@ function renderUnsafe() {
|
|
|
6337
7533
|
},
|
|
6338
7534
|
})
|
|
6339
7535
|
: ''}
|
|
6340
|
-
<div class="agent-
|
|
6341
|
-
${
|
|
6342
|
-
const
|
|
6343
|
-
const
|
|
6344
|
-
|
|
7536
|
+
<div class="agent-grid">
|
|
7537
|
+
${agents.map(a => {
|
|
7538
|
+
const kitManaged = a.kitManaged === true;
|
|
7539
|
+
const lockBadge = kitManaged
|
|
7540
|
+
? `<span class="agent-lock" title="Kit managed: owned by kit updates, read-only here">${agentLockSvg}Kit managed</span>`
|
|
7541
|
+
: '';
|
|
7542
|
+
const agentPath = a.path || `.cursor/agents/${a.file || `${a.id}.md`}`;
|
|
7543
|
+
const usePrompt = `Use this agent definition when it matches the task:\n${agentPath}`;
|
|
7544
|
+
const useBtn = `<button type="button" class="git-action-btn primary" onclick="${copyForPasteHandler(usePrompt, 'use prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('use prompt', 'chatInput'))}">Copy use prompt</button>`;
|
|
7545
|
+
const editPrompt = `Edit this agent definition file (markdown body):\n${agentPath}`;
|
|
7546
|
+
const editBtn = kitManaged
|
|
7547
|
+
? ''
|
|
7548
|
+
: `<button type="button" class="git-action-btn" onclick="${copyForPasteHandler(editPrompt, 'edit prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('edit prompt', 'chatInput'))}">Copy edit prompt</button>`;
|
|
7549
|
+
const deletePrompt = `Delete this agent definition file after confirming with me:\n${agentPath}`;
|
|
7550
|
+
const deleteBtn = kitManaged
|
|
7551
|
+
? ''
|
|
7552
|
+
: `<button type="button" class="git-action-btn" onclick="${copyForPasteHandler(deletePrompt, 'delete prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('delete prompt', 'chatInput'))}">Copy delete prompt</button>`;
|
|
6345
7553
|
return `
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
<span
|
|
6349
|
-
|
|
6350
|
-
<span style="font-size:10px;color:var(--text-muted);transition:transform 0.2s" id="agent-arrow-${escapeAttr(agentId)}">\u25bc</span>
|
|
6351
|
-
</div>
|
|
6352
|
-
<div class="agent-details" id="agent-details-${escapeAttr(agentId)}">
|
|
6353
|
-
<p>${escapeHtml(a.description || 'No description available.')}</p>
|
|
6354
|
-
<div class="agent-path">${escapeHtml(a.path || 'Path not available')}</div>
|
|
7554
|
+
<div class="agent-card"${kitManaged ? ' data-kit-managed="true"' : ''}>
|
|
7555
|
+
<div class="agent-card-head">
|
|
7556
|
+
<span class="agent-name"><span class="agent-monogram" aria-hidden="true">${escapeHtml(agentInitials(a.id))}</span>${escapeHtml(a.id)}</span>
|
|
7557
|
+
${lockBadge}
|
|
6355
7558
|
</div>
|
|
6356
|
-
|
|
7559
|
+
<div class="agent-desc">${escapeHtml(a.description || 'No description available.')}</div>
|
|
7560
|
+
<div class="agent-file">${escapeHtml(agentPath)}</div>
|
|
7561
|
+
<div class="agent-actions">${useBtn}${editBtn}${deleteBtn}</div>
|
|
7562
|
+
</div>
|
|
7563
|
+
`;
|
|
6357
7564
|
}).join('')}
|
|
6358
7565
|
</div>
|
|
6359
7566
|
</div>
|
|
6360
7567
|
`);
|
|
6361
7568
|
|
|
6362
7569
|
// ===== Commands =====
|
|
7570
|
+
const commandLockSvg = `<svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/></svg>`;
|
|
7571
|
+
const commandCreatePrompt = 'Create a new slash command under .cursor/commands/ (plain markdown body, no frontmatter). Ask me for the name and behavior first.';
|
|
7572
|
+
const commandEditableCount = d.commands.filter(c => c.kitManaged !== true).length;
|
|
6363
7573
|
parts.push(`
|
|
6364
7574
|
<div class="content-section" id="section-commands">
|
|
6365
7575
|
<div class="section-title">
|
|
6366
|
-
|
|
6367
|
-
<span class="section-subtitle">${d.commands.length} slash commands</span>
|
|
7576
|
+
Commands
|
|
7577
|
+
<span class="section-subtitle">${d.commands.length} slash commands · ${commandEditableCount} editable</span>
|
|
7578
|
+
<button type="button" class="git-action-btn primary command-create-btn" data-focus-key="cta-command-create" onclick="${copyForPasteHandler(commandCreatePrompt, 'create prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('create prompt', 'chatInput'))}">Copy create prompt</button>
|
|
6368
7579
|
</div>
|
|
6369
7580
|
${d.commands.length === 0
|
|
6370
7581
|
? renderEmptyStateCta({
|
|
@@ -6383,10 +7594,28 @@ function renderUnsafe() {
|
|
|
6383
7594
|
<div class="command-grid">
|
|
6384
7595
|
${d.commands.map(c => {
|
|
6385
7596
|
const slash = `/${c.id}`;
|
|
7597
|
+
const cmdPath = c.path || `.cursor/commands/${c.file || `${c.id}.md`}`;
|
|
7598
|
+
const kitManaged = c.kitManaged === true;
|
|
7599
|
+
const lockBadge = kitManaged
|
|
7600
|
+
? `<span class="command-lock" title="Kit managed: owned by kit updates, read-only here">${commandLockSvg}Kit managed</span>`
|
|
7601
|
+
: '';
|
|
7602
|
+
const runBtn = `<button type="button" class="git-action-btn primary" onclick="${copyForPasteHandler(slash, slash, 'chatInput')}" title="${escapeAttr(copyActionTitle(slash, 'chatInput'))}">Copy run command</button>`;
|
|
7603
|
+
const editPrompt = `Edit this slash command file (plain markdown body, no frontmatter):\n${cmdPath}`;
|
|
7604
|
+
const editBtn = kitManaged
|
|
7605
|
+
? ''
|
|
7606
|
+
: `<button type="button" class="git-action-btn" onclick="${copyForPasteHandler(editPrompt, 'edit prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('edit prompt', 'chatInput'))}">Copy edit prompt</button>`;
|
|
7607
|
+
const deletePrompt = `Delete this slash command file after confirming with me:\n${cmdPath}`;
|
|
7608
|
+
const deleteBtn = kitManaged
|
|
7609
|
+
? ''
|
|
7610
|
+
: `<button type="button" class="git-action-btn" onclick="${copyForPasteHandler(deletePrompt, 'delete prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('delete prompt', 'chatInput'))}">Copy delete prompt</button>`;
|
|
6386
7611
|
return `
|
|
6387
|
-
<div class="command-
|
|
6388
|
-
<
|
|
6389
|
-
|
|
7612
|
+
<div class="command-card"${kitManaged ? ' data-kit-managed="true"' : ''}>
|
|
7613
|
+
<div class="command-card-head">
|
|
7614
|
+
<span class="command-name">${escapeHtml(slash)}</span>
|
|
7615
|
+
${lockBadge}
|
|
7616
|
+
</div>
|
|
7617
|
+
<div class="command-file">${escapeHtml(cmdPath)}</div>
|
|
7618
|
+
<div class="command-actions">${runBtn}${editBtn}${deleteBtn}</div>
|
|
6390
7619
|
</div>
|
|
6391
7620
|
`;
|
|
6392
7621
|
}).join('')}
|
|
@@ -6394,30 +7623,136 @@ function renderUnsafe() {
|
|
|
6394
7623
|
</div>
|
|
6395
7624
|
`);
|
|
6396
7625
|
|
|
6397
|
-
// ===== Health =====
|
|
7626
|
+
// ===== Healthcenter (More → Health) =====
|
|
6398
7627
|
const checks = health.checks || [];
|
|
7628
|
+
const healthStatus = health.status || 'ok';
|
|
7629
|
+
const passing = checks.filter(c => c.ok).length;
|
|
7630
|
+
const isSnapshotError = healthStatus === 'error' && checks.length === 0;
|
|
7631
|
+
const isOffline = !isSnapshotError && checks.length === 0;
|
|
7632
|
+
const presenceTone = healthSeverityChrome(healthStatus).tone;
|
|
7633
|
+
const presencePulse = healthStatus !== 'ok' ? ' dot-pulse' : '';
|
|
7634
|
+
const presenceLabel = isSnapshotError
|
|
7635
|
+
? 'Snapshot error'
|
|
7636
|
+
: isOffline
|
|
7637
|
+
? 'Health offline'
|
|
7638
|
+
: healthStatus === 'ok'
|
|
7639
|
+
? 'Live'
|
|
7640
|
+
: healthStatus === 'warning'
|
|
7641
|
+
? 'Warning'
|
|
7642
|
+
: healthStatus === 'degraded'
|
|
7643
|
+
? 'Degraded'
|
|
7644
|
+
: 'Error';
|
|
7645
|
+
const subtitle = isSnapshotError
|
|
7646
|
+
? 'Snapshot unavailable'
|
|
7647
|
+
: isOffline
|
|
7648
|
+
? 'No health data'
|
|
7649
|
+
: `${passing}/${checks.length} checks passing`;
|
|
7650
|
+
|
|
7651
|
+
let healthBody = '';
|
|
7652
|
+
if (isSnapshotError) {
|
|
7653
|
+
healthBody = renderEmptyStateCta({
|
|
7654
|
+
headline: 'Health snapshot error',
|
|
7655
|
+
support: (typeof d.error === 'string' && d.error)
|
|
7656
|
+
|| 'The dashboard could not load health checks (serve/snapshot failure). This is not a per-check fail.',
|
|
7657
|
+
iconKind: 'health',
|
|
7658
|
+
cta: {
|
|
7659
|
+
text: 'agent-kit doctor',
|
|
7660
|
+
subject: 'agent-kit doctor',
|
|
7661
|
+
destination: 'terminal',
|
|
7662
|
+
label: 'Autofix: copy agent-kit doctor',
|
|
7663
|
+
focusKey: 'cta-health-snapshot-error',
|
|
7664
|
+
},
|
|
7665
|
+
});
|
|
7666
|
+
} else if (isOffline) {
|
|
7667
|
+
healthBody = renderEmptyStateCta({
|
|
7668
|
+
headline: 'Health offline',
|
|
7669
|
+
support: 'No health data available yet. Refresh after the snapshot loads, or run doctor if the panel stays empty.',
|
|
7670
|
+
iconKind: 'health',
|
|
7671
|
+
cta: {
|
|
7672
|
+
text: 'agent-kit doctor',
|
|
7673
|
+
subject: 'agent-kit doctor',
|
|
7674
|
+
destination: 'terminal',
|
|
7675
|
+
label: 'Autofix: copy agent-kit doctor',
|
|
7676
|
+
focusKey: 'cta-health-offline',
|
|
7677
|
+
},
|
|
7678
|
+
});
|
|
7679
|
+
} else {
|
|
7680
|
+
const checkId = (c) => c.id || c.label || 'check';
|
|
7681
|
+
const renderCheckCard = (c) => {
|
|
7682
|
+
const id = checkId(c);
|
|
7683
|
+
const meta = HEALTH_CHECK_META[id] || {
|
|
7684
|
+
okDetail: 'Check is passing.',
|
|
7685
|
+
failDetail: 'Check is failing. Review configuration for this item.',
|
|
7686
|
+
autofix: null,
|
|
7687
|
+
};
|
|
7688
|
+
const sev = healthCheckSeverity(c, healthStatus);
|
|
7689
|
+
const detail = c.ok ? meta.okDetail : meta.failDetail;
|
|
7690
|
+
const autofix = !c.ok && meta.autofix ? meta.autofix : null;
|
|
7691
|
+
const fixPrompt = [`Fix this failing health check: ${c.label || id}`, detail].join('\n');
|
|
7692
|
+
const fixBtn = `<button type="button" class="health-autofix-btn" data-focus-key="health-fix-${escapeAttr(id)}" onclick="event.stopPropagation();${copyForPasteHandler(fixPrompt, 'fix prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('fix prompt', 'chatInput'))}">Copy fix prompt</button>`;
|
|
7693
|
+
const autofixBtn = autofix
|
|
7694
|
+
? `<button type="button" class="health-autofix-btn" data-focus-key="health-autofix-${escapeAttr(id)}" onclick="event.stopPropagation();${copyForPasteHandler(autofix.text, autofix.subject, autofix.destination)}" title="${escapeAttr(copyActionTitle(autofix.subject, autofix.destination))}">${escapeHtml(autofix.label || 'Autofix')}</button>`
|
|
7695
|
+
: '';
|
|
7696
|
+
const actions = c.ok
|
|
7697
|
+
? `<span class="health-detail-copy" style="margin:0">No action needed.</span>`
|
|
7698
|
+
: `${fixBtn}${autofixBtn}`;
|
|
7699
|
+
return `
|
|
7700
|
+
<div class="health-card" role="listitem" id="health-card-${escapeAttr(id)}" data-check-id="${escapeAttr(id)}" data-severity="${escapeAttr(sev)}">
|
|
7701
|
+
<button type="button" class="health-item" aria-expanded="false" aria-controls="health-detail-${escapeAttr(id)}" aria-label="${escapeAttr(c.label)}: ${escapeAttr(healthSeverityLabel(sev))}" onclick="toggleHealthCheck('${escapeJsString(id)}')" onkeydown="healthCheckKeydown(event, '${escapeJsString(id)}')">
|
|
7702
|
+
<span class="dot ${healthDotClass(sev)}"></span>
|
|
7703
|
+
<span class="health-item-label">${escapeHtml(c.label)}</span>
|
|
7704
|
+
<span class="health-item-sev" data-sev="${escapeAttr(sev)}">${escapeHtml(healthSeverityLabel(sev))}</span>
|
|
7705
|
+
<span class="health-item-chevron" aria-hidden="true">▾</span>
|
|
7706
|
+
</button>
|
|
7707
|
+
<div class="health-detail" id="health-detail-${escapeAttr(id)}">
|
|
7708
|
+
<p class="health-detail-copy">${escapeHtml(detail)}</p>
|
|
7709
|
+
<div class="health-detail-actions">${actions}</div>
|
|
7710
|
+
</div>
|
|
7711
|
+
</div>`;
|
|
7712
|
+
};
|
|
7713
|
+
const grouped = HEALTH_VITAL_GROUPS.map(g => ({
|
|
7714
|
+
label: g.label,
|
|
7715
|
+
items: g.checks.map(id => checks.find(c => checkId(c) === id)).filter(Boolean),
|
|
7716
|
+
})).filter(g => g.items.length > 0);
|
|
7717
|
+
const knownIds = new Set(HEALTH_VITAL_GROUPS.flatMap(g => g.checks));
|
|
7718
|
+
const others = checks.filter(c => !knownIds.has(checkId(c)));
|
|
7719
|
+
if (others.length > 0) grouped.push({ label: 'Other checks', items: others });
|
|
7720
|
+
const vitalsStrip = `
|
|
7721
|
+
<div class="health-vitals" role="list" aria-label="Vital systems">
|
|
7722
|
+
${grouped.map(g => {
|
|
7723
|
+
const total = g.items.length;
|
|
7724
|
+
const okCount = g.items.filter(c => c.ok).length;
|
|
7725
|
+
const state = okCount === total ? 'pass' : 'attention';
|
|
7726
|
+
return `
|
|
7727
|
+
<div class="health-vital-card" role="listitem" data-state="${state}">
|
|
7728
|
+
<span class="health-vital-name">${escapeHtml(g.label)}</span>
|
|
7729
|
+
<span class="health-vital-score">${okCount}/${total} passing</span>
|
|
7730
|
+
<span class="health-vital-state"><span class="dot ${state === 'pass' ? 'dot-green' : 'dot-red'}"></span>${state === 'pass' ? 'Pass' : 'Attention'}</span>
|
|
7731
|
+
</div>`;
|
|
7732
|
+
}).join('')}
|
|
7733
|
+
</div>`;
|
|
7734
|
+
healthBody = `${vitalsStrip}
|
|
7735
|
+
${grouped.map(g => `
|
|
7736
|
+
<section class="health-group" aria-label="${escapeAttr(g.label)}">
|
|
7737
|
+
<div class="health-group-title">${escapeHtml(g.label)}</div>
|
|
7738
|
+
<div class="health-grid" role="list">
|
|
7739
|
+
${g.items.map(renderCheckCard).join('')}
|
|
7740
|
+
</div>
|
|
7741
|
+
</section>`).join('')}`;
|
|
7742
|
+
}
|
|
7743
|
+
|
|
6399
7744
|
parts.push(`
|
|
6400
7745
|
<div class="content-section" id="section-health">
|
|
6401
7746
|
<div class="section-title">
|
|
6402
|
-
<span class="dot dot-${health.status === 'ok' ? 'green' : health.status === 'warning' ? 'yellow' : 'red'}"></span>
|
|
6403
7747
|
Health
|
|
6404
|
-
<span class="section-subtitle">${
|
|
7748
|
+
<span class="section-subtitle">${escapeHtml(subtitle)}</span>
|
|
6405
7749
|
</div>
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
<div class="health-grid">
|
|
6413
|
-
${checks.map(c => `
|
|
6414
|
-
<div style="position:relative">
|
|
6415
|
-
<div class="health-item" role="button" tabindex="0" aria-label="Show info for ${escapeAttr(c.label)}" onclick="showHealthInfo(this, ${c.ok})">
|
|
6416
|
-
<span class="dot dot-${c.ok ? 'green' : 'red'}"></span>
|
|
6417
|
-
<span>${escapeHtml(c.label)}</span>
|
|
6418
|
-
</div>
|
|
6419
|
-
</div>
|
|
6420
|
-
`).join('')}
|
|
7750
|
+
<div class="healthcenter">
|
|
7751
|
+
<div class="healthcenter-presence" role="status" aria-live="polite">
|
|
7752
|
+
<span class="dot dot-${presenceTone}${presencePulse}"></span>
|
|
7753
|
+
<span>${escapeHtml(presenceLabel)}</span>
|
|
7754
|
+
</div>
|
|
7755
|
+
${healthBody}
|
|
6421
7756
|
</div>
|
|
6422
7757
|
</div>
|
|
6423
7758
|
`);
|
|
@@ -6426,9 +7761,8 @@ function renderUnsafe() {
|
|
|
6426
7761
|
parts.push(`
|
|
6427
7762
|
<div class="content-section" id="section-git">
|
|
6428
7763
|
<div class="section-title">
|
|
6429
|
-
<span class="dot dot-${d.git?.dirty ? 'yellow' : 'green'}"></span>
|
|
6430
7764
|
Git
|
|
6431
|
-
<span class="section-subtitle">${escapeHtml(d.git?.branch || '\u2014')}</span>
|
|
7765
|
+
<span class="section-subtitle">${escapeHtml(d.git?.branch || '\u2014')}${d.git?.branch ? (d.git?.dirty ? ` · ${d.git.dirtyCount} changed` : ' · clean') : ''}</span>
|
|
6432
7766
|
</div>
|
|
6433
7767
|
${!d.git?.branch
|
|
6434
7768
|
? renderEmptyStateCta({
|
|
@@ -6437,28 +7771,15 @@ function renderUnsafe() {
|
|
|
6437
7771
|
})
|
|
6438
7772
|
: ''}
|
|
6439
7773
|
<div class="card" style="${d.git?.branch ? '' : 'display:none'}">
|
|
7774
|
+
${renderGitHygieneHint(d.git)}
|
|
7775
|
+
${renderGitFlowCard(d.git)}
|
|
6440
7776
|
<div class="git-info">
|
|
6441
|
-
<div class="git-row">
|
|
6442
|
-
<span class="git-label">Branch</span>
|
|
6443
|
-
<span class="git-value">${escapeHtml(d.git?.branch || '\u2014')}</span>
|
|
6444
|
-
</div>
|
|
6445
|
-
<div class="git-row">
|
|
6446
|
-
<span class="git-label">Working tree</span>
|
|
6447
|
-
<span class="git-value">${d.git?.dirty ? `${d.git.dirtyCount} file${d.git.dirtyCount !== 1 ? 's' : ''} modified` : 'Clean'}</span>
|
|
6448
|
-
</div>
|
|
6449
7777
|
<div class="git-row">
|
|
6450
7778
|
<span class="git-label">Last commit</span>
|
|
6451
7779
|
<span class="git-value">${escapeHtml(d.git?.lastCommit || '\u2014')}</span>
|
|
6452
7780
|
</div>
|
|
6453
|
-
<div class="git-row">
|
|
6454
|
-
<span class="git-label">Ahead of origin/main</span>
|
|
6455
|
-
<span class="git-value">${d.git?.ahead ?? '\u2014'}</span>
|
|
6456
|
-
</div>
|
|
6457
|
-
<div class="git-row">
|
|
6458
|
-
<span class="git-label">Behind origin/main</span>
|
|
6459
|
-
<span class="git-value">${d.git?.behind ?? '\u2014'}</span>
|
|
6460
|
-
</div>
|
|
6461
7781
|
</div>
|
|
7782
|
+
${renderGitGraphCard(d.git)}
|
|
6462
7783
|
${d.git?.dirty ? renderGitFileList(d.git) : ''}
|
|
6463
7784
|
</div>
|
|
6464
7785
|
</div>
|
|
@@ -6466,49 +7787,79 @@ function renderUnsafe() {
|
|
|
6466
7787
|
|
|
6467
7788
|
// ===== Memory =====
|
|
6468
7789
|
const recentDecisions = d.memory?.recentDecisions || [];
|
|
7790
|
+
const recentErrors = d.memory?.recentErrors || [];
|
|
7791
|
+
const errorStats = d.memory?.errorStats || null;
|
|
6469
7792
|
parts.push(`
|
|
6470
7793
|
<div class="content-section" id="section-memory">
|
|
6471
7794
|
<div class="section-title">
|
|
6472
|
-
|
|
7795
|
+
Memory
|
|
6473
7796
|
<span class="section-subtitle">${d.memory.errors || 0} errors \u00b7 ${d.memory.decisions || 0} decisions</span>
|
|
6474
7797
|
</div>
|
|
6475
|
-
<div class="grid-
|
|
6476
|
-
<div class="
|
|
6477
|
-
<div class="
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
</div>
|
|
6481
|
-
<div class="card-value">${d.memory.errors || 0}</div>
|
|
6482
|
-
<div class="card-subtitle">resolved errors in memory</div>
|
|
7798
|
+
<div class="memory-kpi-grid" aria-label="Error-o-meter: memory error statistics">
|
|
7799
|
+
<div class="memory-kpi">
|
|
7800
|
+
<div class="memory-kpi-label">Error-o-meter</div>
|
|
7801
|
+
<div class="memory-kpi-value">${errorStats ? errorStats.total : (d.memory.errors || 0)}</div>
|
|
7802
|
+
<div class="memory-kpi-sub">resolved errors recorded</div>
|
|
6483
7803
|
</div>
|
|
6484
|
-
<div class="
|
|
6485
|
-
<div class="
|
|
6486
|
-
|
|
6487
|
-
|
|
7804
|
+
<div class="memory-kpi">
|
|
7805
|
+
<div class="memory-kpi-label">Last 30 days</div>
|
|
7806
|
+
<div class="memory-kpi-value">${errorStats ? errorStats.last30d : 0}</div>
|
|
7807
|
+
<div class="memory-kpi-sub">new error entries</div>
|
|
7808
|
+
</div>
|
|
7809
|
+
<div class="memory-kpi">
|
|
7810
|
+
<div class="memory-kpi-label">Weekly rate</div>
|
|
7811
|
+
<div class="memory-kpi-value">${errorStats ? errorStats.weeklyRate : 0}</div>
|
|
7812
|
+
<div class="memory-kpi-sub">errors per week (30d avg)</div>
|
|
7813
|
+
</div>
|
|
7814
|
+
<div class="memory-kpi">
|
|
7815
|
+
<div class="memory-kpi-label">Top tags</div>
|
|
7816
|
+
<div class="memory-tag-row">
|
|
7817
|
+
${errorStats && errorStats.topTags.length > 0
|
|
7818
|
+
? errorStats.topTags.map(t => `<span class="memory-tag" title="${escapeAttr(`${t.count} entries tagged ${t.tag}`)}">${escapeHtml(t.tag)} ×${t.count}</span>`).join('')
|
|
7819
|
+
: '<span class="memory-kpi-sub">no tags recorded</span>'}
|
|
6488
7820
|
</div>
|
|
6489
|
-
<div class="card-value">${d.memory.decisions || 0}</div>
|
|
6490
|
-
<div class="card-subtitle">architectural decisions</div>
|
|
6491
7821
|
</div>
|
|
6492
7822
|
</div>
|
|
6493
|
-
|
|
6494
|
-
<div class="
|
|
6495
|
-
<div class="
|
|
6496
|
-
<span class="
|
|
7823
|
+
<div class="grid-2">
|
|
7824
|
+
<div class="memory-panel">
|
|
7825
|
+
<div class="memory-panel-header">
|
|
7826
|
+
<span class="memory-panel-icon memory-panel-icon-green" aria-hidden="true">✓</span>
|
|
7827
|
+
<span class="memory-panel-title">Healthy memory</span>
|
|
7828
|
+
<span class="memory-panel-sub">${d.memory.decisions || 0} decisions recorded</span>
|
|
6497
7829
|
</div>
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
<
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
7830
|
+
${recentDecisions.length > 0 ? `
|
|
7831
|
+
<div class="memory-list">
|
|
7832
|
+
${recentDecisions.map(dd => {
|
|
7833
|
+
const memPath = dd.path || `.cursor/memory/decisions/${dd.id || dd}.md`;
|
|
7834
|
+
const memId = dd.id || dd;
|
|
7835
|
+
return `
|
|
7836
|
+
<div class="memory-item" role="button" tabindex="0" aria-label="Copy path for ${escapeAttr(memId)}" onclick="${copyRepoPathHandler(memPath)}" title="${escapeAttr(pathActionTitle(memPath))}">
|
|
7837
|
+
<span>${escapeHtml(memId)}</span>
|
|
7838
|
+
</div>
|
|
7839
|
+
`;
|
|
7840
|
+
}).join('')}
|
|
7841
|
+
</div>
|
|
7842
|
+
` : renderEmptyStateCta({
|
|
7843
|
+
headline: 'No decisions yet',
|
|
7844
|
+
support: 'Accepted tradeoffs land in .cursor/memory/decisions/ as ADR-lite entries.',
|
|
7845
|
+
compact: true,
|
|
7846
|
+
})}
|
|
7847
|
+
</div>
|
|
7848
|
+
<div class="memory-panel">
|
|
7849
|
+
<div class="memory-panel-header">
|
|
7850
|
+
<span class="memory-panel-icon memory-panel-icon-red" aria-hidden="true">⚠</span>
|
|
7851
|
+
<span class="memory-panel-title">Recent errors</span>
|
|
7852
|
+
<span class="memory-panel-sub">${recentErrors.length} of ${d.memory.errors || 0}</span>
|
|
6509
7853
|
</div>
|
|
7854
|
+
${recentErrors.length > 0
|
|
7855
|
+
? recentErrors.map((entry, idx) => renderMemoryErrorRow(entry, idx)).join('')
|
|
7856
|
+
: renderEmptyStateCta({
|
|
7857
|
+
headline: 'No errors recorded',
|
|
7858
|
+
support: 'Investigated errors with cause and fix land in .cursor/memory/errors/.',
|
|
7859
|
+
compact: true,
|
|
7860
|
+
})}
|
|
6510
7861
|
</div>
|
|
6511
|
-
|
|
7862
|
+
</div>
|
|
6512
7863
|
</div>
|
|
6513
7864
|
`);
|
|
6514
7865
|
|
|
@@ -6517,7 +7868,6 @@ function renderUnsafe() {
|
|
|
6517
7868
|
parts.push(`
|
|
6518
7869
|
<div class="content-section" id="section-terminals">
|
|
6519
7870
|
<div class="section-title">
|
|
6520
|
-
<span class="dot dot-${d.terminals.length > 0 ? 'blue dot-pulse' : 'gray'}"></span>
|
|
6521
7871
|
Terminals
|
|
6522
7872
|
<span class="section-subtitle">${d.terminals.length} active</span>
|
|
6523
7873
|
</div>
|
|
@@ -6563,55 +7913,40 @@ function renderUnsafe() {
|
|
|
6563
7913
|
parts.push(`
|
|
6564
7914
|
<div class="content-section" id="section-processes">
|
|
6565
7915
|
<div class="section-title">
|
|
6566
|
-
<span class="dot dot-${processes.length > 0 ? 'green' : 'gray'}"></span>
|
|
6567
7916
|
Processes
|
|
6568
7917
|
<span class="section-subtitle">${processes.length} running</span>
|
|
6569
7918
|
</div>
|
|
7919
|
+
<div class="processes-note">
|
|
7920
|
+
<span><strong>Live ps snapshot.</strong> Agent chats spawned inside the IDE do not appear here; the Crew monitor on the Overview tab tracks that activity.</span>
|
|
7921
|
+
</div>
|
|
6570
7922
|
${processes.length === 0
|
|
6571
7923
|
? renderEmptyStateCta({
|
|
6572
7924
|
headline: 'All quiet',
|
|
6573
|
-
support: 'No relevant processes running.',
|
|
7925
|
+
support: 'No relevant processes running. Node, git, and dashboard processes appear here while active.',
|
|
6574
7926
|
})
|
|
6575
7927
|
: `
|
|
6576
|
-
<div class="
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
<
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
<span class="process-label">
|
|
6599
|
-
<span class="dot ${dotClass}"></span>
|
|
6600
|
-
<span>${escapeHtml(p.label || 'other')}</span>
|
|
6601
|
-
</span>
|
|
6602
|
-
</td>
|
|
6603
|
-
<td style="color:var(--text-muted)">${escapeHtml(pidStr)}</td>
|
|
6604
|
-
<td>${escapeHtml(p.cpu || '\u2014')}</td>
|
|
6605
|
-
<td>${escapeHtml(p.mem || '\u2014')}</td>
|
|
6606
|
-
<td style="color:var(--text-muted);max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${escapeAttr(p.command || '\u2014')}">${escapeHtml(p.command || '\u2014')}</td>
|
|
6607
|
-
<td class="process-actions">
|
|
6608
|
-
<button type="button" class="process-copy-btn" onclick="event.stopPropagation();${copyForPasteHandler(pidStr, `PID ${pidStr}`, 'terminal')}" title="${escapeAttr(copyActionTitle(`PID ${pidStr}`, 'terminal'))}">Copy PID</button>
|
|
6609
|
-
</td>
|
|
6610
|
-
</tr>
|
|
6611
|
-
`;
|
|
6612
|
-
}).join('')}
|
|
6613
|
-
</tbody>
|
|
6614
|
-
</table>
|
|
7928
|
+
<div class="process-list">
|
|
7929
|
+
${processes.map(p => {
|
|
7930
|
+
const pidStr = String(p.pid ?? '');
|
|
7931
|
+
return `
|
|
7932
|
+
<div class="process-card">
|
|
7933
|
+
<div class="process-card-head">
|
|
7934
|
+
<span class="process-label-badge">${escapeHtml(p.label || 'other')}</span>
|
|
7935
|
+
<span class="process-actions">
|
|
7936
|
+
<button type="button" class="process-copy-btn" onclick="event.stopPropagation();${copyForPasteHandler(pidStr, `PID ${pidStr}`, 'terminal')}" title="${escapeAttr(copyActionTitle(`PID ${pidStr}`, 'terminal'))}">Copy PID</button>
|
|
7937
|
+
</span>
|
|
7938
|
+
</div>
|
|
7939
|
+
${p.description ? `<div class="process-desc">${escapeHtml(p.description)}</div>` : ''}
|
|
7940
|
+
<div class="process-meta">
|
|
7941
|
+
<span>PID ${escapeHtml(pidStr)}</span>
|
|
7942
|
+
<span>CPU ${escapeHtml(p.cpu || '\u2014')}%</span>
|
|
7943
|
+
<span>MEM ${escapeHtml(p.mem || '\u2014')}%</span>
|
|
7944
|
+
${p.etime ? `<span>up ${escapeHtml(p.etime)}</span>` : ''}
|
|
7945
|
+
</div>
|
|
7946
|
+
<div class="process-cmd" title="${escapeAttr(p.command || '\u2014')}">${escapeHtml(p.command || '\u2014')}</div>
|
|
7947
|
+
</div>
|
|
7948
|
+
`;
|
|
7949
|
+
}).join('')}
|
|
6615
7950
|
</div>
|
|
6616
7951
|
`}
|
|
6617
7952
|
</div>
|
|
@@ -6619,19 +7954,15 @@ function renderUnsafe() {
|
|
|
6619
7954
|
|
|
6620
7955
|
// ===== Skills =====
|
|
6621
7956
|
const skills = d.skills || [];
|
|
6622
|
-
const
|
|
6623
|
-
skills
|
|
6624
|
-
|
|
6625
|
-
if (!categories[cat]) categories[cat] = [];
|
|
6626
|
-
categories[cat].push(s);
|
|
6627
|
-
});
|
|
6628
|
-
const catColors = ['#a855f7','#3b82f6','#22c55e','#eab308','#f97316','#06b6d4','#ec4899','#ef4444'];
|
|
6629
|
-
let catIdx = 0;
|
|
7957
|
+
const skillLockSvg = `<svg width="11" height="11" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="7" width="10" height="7" rx="1.5"/><path d="M5.5 7V5a2.5 2.5 0 0 1 5 0v2"/></svg>`;
|
|
7958
|
+
const skillCreatePrompt = 'Create a new Agent Skill under .cursor/skills/ (directory with a SKILL.md markdown body). Ask me for the name and behavior first.';
|
|
7959
|
+
const skillEditableCount = skills.filter(s => s.kitManaged !== true).length;
|
|
6630
7960
|
parts.push(`
|
|
6631
7961
|
<div class="content-section" id="section-skills">
|
|
6632
7962
|
<div class="section-title">
|
|
6633
|
-
|
|
6634
|
-
<span class="section-subtitle">${skills.length}
|
|
7963
|
+
Skills
|
|
7964
|
+
<span class="section-subtitle">${skills.length} skills · ${skillEditableCount} editable</span>
|
|
7965
|
+
<button type="button" class="git-action-btn primary skill-create-btn" data-focus-key="cta-skill-create" onclick="${copyForPasteHandler(skillCreatePrompt, 'create prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('create prompt', 'chatInput'))}">Copy create prompt</button>
|
|
6635
7966
|
</div>
|
|
6636
7967
|
${skills.length === 0
|
|
6637
7968
|
? renderEmptyStateCta({
|
|
@@ -6646,23 +7977,38 @@ function renderUnsafe() {
|
|
|
6646
7977
|
focusKey: 'cta-skills-path',
|
|
6647
7978
|
},
|
|
6648
7979
|
})
|
|
6649
|
-
:
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
7980
|
+
: ''}
|
|
7981
|
+
<div class="skill-grid">
|
|
7982
|
+
${skills.map(s => {
|
|
7983
|
+
const skillFile = s.file || `.cursor/skills/${s.id}/SKILL.md`;
|
|
7984
|
+
const skillDir = skillFile.replace(/\/SKILL\.md$/, '');
|
|
7985
|
+
const kitManaged = s.kitManaged === true;
|
|
7986
|
+
const lockBadge = kitManaged
|
|
7987
|
+
? `<span class="skill-lock" title="Kit managed: owned by kit updates, read-only here">${skillLockSvg}Kit managed</span>`
|
|
7988
|
+
: '';
|
|
7989
|
+
const usePrompt = `Read this skill file and follow its instructions:\n${skillFile}`;
|
|
7990
|
+
const useBtn = `<button type="button" class="git-action-btn primary" onclick="${copyForPasteHandler(usePrompt, 'use prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('use prompt', 'chatInput'))}">Copy use prompt</button>`;
|
|
7991
|
+
const editPrompt = `Edit this skill file (SKILL.md markdown body):\n${skillFile}`;
|
|
7992
|
+
const editBtn = kitManaged
|
|
7993
|
+
? ''
|
|
7994
|
+
: `<button type="button" class="git-action-btn" onclick="${copyForPasteHandler(editPrompt, 'edit prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('edit prompt', 'chatInput'))}">Copy edit prompt</button>`;
|
|
7995
|
+
const deletePrompt = `Delete this skill directory after confirming with me:\n${skillDir}`;
|
|
7996
|
+
const deleteBtn = kitManaged
|
|
7997
|
+
? ''
|
|
7998
|
+
: `<button type="button" class="git-action-btn" onclick="${copyForPasteHandler(deletePrompt, 'delete prompt', 'chatInput')}" title="${escapeAttr(copyActionTitle('delete prompt', 'chatInput'))}">Copy delete prompt</button>`;
|
|
7999
|
+
return `
|
|
8000
|
+
<div class="skill-card"${kitManaged ? ' data-kit-managed="true"' : ''} title="${escapeAttr(s.description || '')}">
|
|
8001
|
+
<div class="skill-card-head">
|
|
8002
|
+
<span class="skill-name">${escapeHtml(s.title)}</span>
|
|
8003
|
+
${lockBadge}
|
|
6662
8004
|
</div>
|
|
8005
|
+
<span class="skill-cat">${escapeHtml(s.category || 'root')}</span>
|
|
8006
|
+
<div class="skill-file">${escapeHtml(skillFile)}</div>
|
|
8007
|
+
<div class="skill-actions">${useBtn}${editBtn}${deleteBtn}</div>
|
|
6663
8008
|
</div>
|
|
6664
8009
|
`;
|
|
6665
|
-
|
|
8010
|
+
}).join('')}
|
|
8011
|
+
</div>
|
|
6666
8012
|
</div>
|
|
6667
8013
|
`);
|
|
6668
8014
|
|
|
@@ -6702,7 +8048,7 @@ function renderUnsafe() {
|
|
|
6702
8048
|
parts.push(`
|
|
6703
8049
|
<div class="content-section" id="section-activity">
|
|
6704
8050
|
<div class="section-title">
|
|
6705
|
-
|
|
8051
|
+
Activity
|
|
6706
8052
|
<span class="section-subtitle">${escapeHtml(activityCountLabel)} · Last refreshed ${escapeHtml(relativeTime(d.generatedAt))}</span>
|
|
6707
8053
|
</div>
|
|
6708
8054
|
${renderActivityFilterChips()}
|