@in-the-loop-labs/pair-review 3.2.3 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/package.json +5 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
- package/public/css/repo-settings.css +347 -0
- package/public/index.html +46 -9
- package/public/js/components/AIPanel.js +79 -37
- package/public/js/components/DiffOptionsDropdown.js +84 -1
- package/public/js/index.js +31 -6
- package/public/js/pr.js +22 -0
- package/public/js/repo-settings.js +334 -6
- package/public/repo-settings.html +29 -0
- package/src/ai/analyzer.js +28 -19
- package/src/ai/claude-cli.js +2 -0
- package/src/ai/claude-provider.js +4 -1
- package/src/ai/provider.js +7 -6
- package/src/chat/session-manager.js +6 -3
- package/src/config.js +230 -38
- package/src/database.js +766 -38
- package/src/git/worktree-pool-lifecycle.js +674 -0
- package/src/git/worktree-pool-usage.js +216 -0
- package/src/git/worktree.js +46 -13
- package/src/main.js +185 -26
- package/src/routes/analyses.js +48 -26
- package/src/routes/chat.js +27 -3
- package/src/routes/config.js +17 -5
- package/src/routes/executable-analysis.js +38 -19
- package/src/routes/local.js +19 -6
- package/src/routes/mcp.js +13 -2
- package/src/routes/pr.js +72 -29
- package/src/routes/setup.js +41 -4
- package/src/routes/stack-analysis.js +29 -10
- package/src/routes/worktrees.js +294 -9
- package/src/server.js +20 -3
- package/src/setup/pr-setup.js +161 -27
- package/src/ws/server.js +51 -1
package/README.md
CHANGED
|
@@ -746,28 +746,29 @@ These commands update your MCP configuration in `~/.claude/settings.json` (user-
|
|
|
746
746
|
### Prerequisites
|
|
747
747
|
|
|
748
748
|
- Node.js 20.0.0 or higher
|
|
749
|
+
- [pnpm](https://pnpm.io/) 10.x
|
|
749
750
|
- Git
|
|
750
751
|
|
|
751
752
|
### Running Locally
|
|
752
753
|
|
|
753
754
|
```bash
|
|
754
755
|
# Install dependencies
|
|
755
|
-
|
|
756
|
+
pnpm install
|
|
756
757
|
|
|
757
758
|
# Run tests
|
|
758
|
-
|
|
759
|
+
pnpm test
|
|
759
760
|
|
|
760
761
|
# Run tests in watch mode
|
|
761
|
-
|
|
762
|
+
pnpm run test:watch
|
|
762
763
|
|
|
763
764
|
# Run E2E tests
|
|
764
|
-
|
|
765
|
+
pnpm run test:e2e
|
|
765
766
|
|
|
766
767
|
# Run E2E tests with visible browser
|
|
767
|
-
|
|
768
|
+
pnpm run test:e2e:headed
|
|
768
769
|
|
|
769
770
|
# Start development server
|
|
770
|
-
|
|
771
|
+
pnpm run dev
|
|
771
772
|
```
|
|
772
773
|
|
|
773
774
|
### Architecture
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@in-the-loop-labs/pair-review",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Your AI-powered code review partner - Close the feedback loop with AI coding agents",
|
|
5
5
|
"main": "src/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"test:e2e:debug": "playwright test --debug",
|
|
33
33
|
"generate:skill-prompts": "node scripts/generate-skill-prompts.js",
|
|
34
34
|
"changeset": "changeset",
|
|
35
|
-
"version": "changeset version &&
|
|
36
|
-
"release": "npm whoami > /dev/null || { echo 'Error: Not logged in to npm. Run: npm login'; exit 1; } &&
|
|
35
|
+
"version": "changeset version && pnpm install --lockfile-only && node scripts/sync-plugin-versions.js && git add package.json pnpm-lock.yaml CHANGELOG.md .changeset .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json plugin-code-critic/.claude-plugin/plugin.json && git commit -m \"RELEASING: v$(node -p \"require('./package.json').version\")\"",
|
|
36
|
+
"release": "npm whoami > /dev/null || { echo 'Error: Not logged in to npm. Run: npm login'; exit 1; } && pnpm run version && changeset tag && npm publish && git push && git push --tags"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|
|
39
39
|
"code-review",
|
|
@@ -71,7 +71,8 @@
|
|
|
71
71
|
"simple-git": "^3.19.1",
|
|
72
72
|
"update-notifier": "^5.1.0",
|
|
73
73
|
"uuid": "^11.1.0",
|
|
74
|
-
"ws": "^8.19.0"
|
|
74
|
+
"ws": "^8.19.0",
|
|
75
|
+
"zod": "^4.3.6"
|
|
75
76
|
},
|
|
76
77
|
"devDependencies": {
|
|
77
78
|
"@changesets/cli": "^2.29.8",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pair-review",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "pair-review app integration — Open PRs and local changes in the pair-review web UI, run server-side AI analysis, and address review feedback. Requires the pair-review MCP server.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "in-the-loop-labs",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-critic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "AI-powered code review analysis — Run three-level AI analysis and implement-review-fix loops directly in your coding agent. Works standalone, no server required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "in-the-loop-labs",
|
|
@@ -893,6 +893,331 @@ html, body {
|
|
|
893
893
|
font-size: 12px;
|
|
894
894
|
}
|
|
895
895
|
|
|
896
|
+
/* ============================================
|
|
897
|
+
Worktrees Section
|
|
898
|
+
============================================ */
|
|
899
|
+
|
|
900
|
+
@keyframes worktree-spin {
|
|
901
|
+
from { transform: rotate(0deg); }
|
|
902
|
+
to { transform: rotate(360deg); }
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.worktree-icon-spin {
|
|
906
|
+
animation: worktree-spin 1s linear infinite;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.worktree-pool-config {
|
|
910
|
+
display: flex;
|
|
911
|
+
flex-direction: column;
|
|
912
|
+
gap: 12px;
|
|
913
|
+
padding: 12px 16px;
|
|
914
|
+
background: var(--color-bg-primary);
|
|
915
|
+
border: 1px solid var(--color-border-primary);
|
|
916
|
+
border-radius: 8px;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.worktree-pool-config-items {
|
|
920
|
+
display: flex;
|
|
921
|
+
align-items: flex-end;
|
|
922
|
+
gap: 24px;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.worktree-pool-config-item {
|
|
926
|
+
display: flex;
|
|
927
|
+
flex-direction: column;
|
|
928
|
+
gap: 4px;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.worktree-pool-config-label {
|
|
932
|
+
font-size: 11px;
|
|
933
|
+
color: var(--color-text-muted);
|
|
934
|
+
text-transform: uppercase;
|
|
935
|
+
letter-spacing: 0.04em;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.worktree-pool-input-group {
|
|
939
|
+
display: flex;
|
|
940
|
+
align-items: center;
|
|
941
|
+
gap: 6px;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.worktree-pool-input {
|
|
945
|
+
width: 72px;
|
|
946
|
+
padding: 4px 8px;
|
|
947
|
+
font-size: 14px;
|
|
948
|
+
color: var(--color-text-primary);
|
|
949
|
+
background: var(--color-bg-secondary);
|
|
950
|
+
border: 1px solid var(--color-border-primary);
|
|
951
|
+
border-radius: 6px;
|
|
952
|
+
outline: none;
|
|
953
|
+
transition: border-color 0.15s ease;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.worktree-pool-input:focus {
|
|
957
|
+
border-color: var(--color-accent-primary);
|
|
958
|
+
box-shadow: 0 0 0 2px rgba(var(--color-accent-primary-rgb, 31, 111, 235), 0.15);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.worktree-pool-input::placeholder {
|
|
962
|
+
color: var(--color-text-muted);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.worktree-pool-input-note {
|
|
966
|
+
font-size: 12px;
|
|
967
|
+
color: var(--color-text-muted);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
.worktree-list {
|
|
971
|
+
display: flex;
|
|
972
|
+
flex-direction: column;
|
|
973
|
+
gap: 8px;
|
|
974
|
+
margin-top: 16px;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.worktree-item {
|
|
978
|
+
display: flex;
|
|
979
|
+
flex-direction: column;
|
|
980
|
+
gap: 6px;
|
|
981
|
+
padding: 10px 14px;
|
|
982
|
+
background: var(--color-bg-primary);
|
|
983
|
+
border: 1px solid var(--color-border-primary);
|
|
984
|
+
border-radius: 8px;
|
|
985
|
+
transition: background 0.15s ease;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.worktree-item:hover {
|
|
989
|
+
background: var(--color-bg-tertiary);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.worktree-item-top {
|
|
993
|
+
display: flex;
|
|
994
|
+
align-items: center;
|
|
995
|
+
justify-content: space-between;
|
|
996
|
+
gap: 12px;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.worktree-item-bottom {
|
|
1000
|
+
padding-left: 2px;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
.worktree-item-left {
|
|
1004
|
+
display: flex;
|
|
1005
|
+
align-items: center;
|
|
1006
|
+
gap: 10px;
|
|
1007
|
+
flex-wrap: wrap;
|
|
1008
|
+
min-width: 0;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
.worktree-item-right {
|
|
1012
|
+
display: flex;
|
|
1013
|
+
align-items: center;
|
|
1014
|
+
gap: 10px;
|
|
1015
|
+
flex-shrink: 0;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
.worktree-pool-badge {
|
|
1019
|
+
display: inline-flex;
|
|
1020
|
+
align-items: center;
|
|
1021
|
+
padding: 2px 8px;
|
|
1022
|
+
font-size: 11px;
|
|
1023
|
+
font-weight: 600;
|
|
1024
|
+
letter-spacing: 0.03em;
|
|
1025
|
+
border-radius: 10px;
|
|
1026
|
+
white-space: nowrap;
|
|
1027
|
+
background: rgba(14, 165, 233, 0.12);
|
|
1028
|
+
color: #0284c7;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
[data-theme="dark"] .worktree-pool-badge {
|
|
1032
|
+
background: rgba(56, 189, 248, 0.15);
|
|
1033
|
+
color: #38bdf8;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.worktree-adhoc-badge {
|
|
1037
|
+
display: inline-flex;
|
|
1038
|
+
align-items: center;
|
|
1039
|
+
padding: 2px 8px;
|
|
1040
|
+
font-size: 11px;
|
|
1041
|
+
font-weight: 600;
|
|
1042
|
+
letter-spacing: 0.03em;
|
|
1043
|
+
border-radius: 10px;
|
|
1044
|
+
white-space: nowrap;
|
|
1045
|
+
background: var(--color-bg-tertiary);
|
|
1046
|
+
color: var(--color-text-tertiary);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.worktree-pr-info {
|
|
1050
|
+
font-size: 14px;
|
|
1051
|
+
font-weight: 500;
|
|
1052
|
+
color: var(--color-text-primary);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.worktree-path {
|
|
1056
|
+
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', monospace;
|
|
1057
|
+
font-size: 12px;
|
|
1058
|
+
color: var(--color-text-tertiary);
|
|
1059
|
+
word-break: break-all;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
.worktree-status {
|
|
1063
|
+
display: inline-flex;
|
|
1064
|
+
align-items: center;
|
|
1065
|
+
gap: 5px;
|
|
1066
|
+
font-size: 12px;
|
|
1067
|
+
font-weight: 500;
|
|
1068
|
+
white-space: nowrap;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
.worktree-status--available {
|
|
1072
|
+
color: #16a34a;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
[data-theme="dark"] .worktree-status--available {
|
|
1076
|
+
color: #4ade80;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
.worktree-status--in-use {
|
|
1080
|
+
color: #d97706;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
[data-theme="dark"] .worktree-status--in-use {
|
|
1084
|
+
color: #fbbf24;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
.worktree-status--switching {
|
|
1088
|
+
color: #2563eb;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
[data-theme="dark"] .worktree-status--switching {
|
|
1092
|
+
color: #60a5fa;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
.worktree-status--creating {
|
|
1096
|
+
color: #2563eb;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
[data-theme="dark"] .worktree-status--creating {
|
|
1100
|
+
color: #60a5fa;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
.worktree-timestamp {
|
|
1104
|
+
font-size: 12px;
|
|
1105
|
+
color: var(--color-text-muted);
|
|
1106
|
+
white-space: nowrap;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
.worktree-delete-btn {
|
|
1110
|
+
display: inline-flex;
|
|
1111
|
+
align-items: center;
|
|
1112
|
+
justify-content: center;
|
|
1113
|
+
width: 28px;
|
|
1114
|
+
height: 28px;
|
|
1115
|
+
padding: 0;
|
|
1116
|
+
background: transparent;
|
|
1117
|
+
border: 1px solid transparent;
|
|
1118
|
+
border-radius: 6px;
|
|
1119
|
+
color: var(--color-text-tertiary);
|
|
1120
|
+
cursor: pointer;
|
|
1121
|
+
transition: all 0.15s ease;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.worktree-delete-btn:hover {
|
|
1125
|
+
background: rgba(239, 68, 68, 0.1);
|
|
1126
|
+
border-color: rgba(239, 68, 68, 0.3);
|
|
1127
|
+
color: var(--color-danger);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
.worktree-disk-warning {
|
|
1131
|
+
display: inline-flex;
|
|
1132
|
+
align-items: center;
|
|
1133
|
+
gap: 4px;
|
|
1134
|
+
padding: 2px 8px;
|
|
1135
|
+
font-size: 11px;
|
|
1136
|
+
font-weight: 500;
|
|
1137
|
+
border-radius: 10px;
|
|
1138
|
+
white-space: nowrap;
|
|
1139
|
+
background: var(--color-warning-bg);
|
|
1140
|
+
border: 1px solid var(--color-warning-border);
|
|
1141
|
+
color: var(--color-warning-text);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
.worktree-actions {
|
|
1145
|
+
display: flex;
|
|
1146
|
+
justify-content: flex-end;
|
|
1147
|
+
margin-top: 16px;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.worktree-delete-all-btn {
|
|
1151
|
+
display: inline-flex;
|
|
1152
|
+
align-items: center;
|
|
1153
|
+
gap: 6px;
|
|
1154
|
+
padding: 6px 12px;
|
|
1155
|
+
background: transparent;
|
|
1156
|
+
border: 1px solid var(--color-danger);
|
|
1157
|
+
border-radius: 8px;
|
|
1158
|
+
font-size: 12px;
|
|
1159
|
+
font-weight: 500;
|
|
1160
|
+
color: var(--color-danger);
|
|
1161
|
+
cursor: pointer;
|
|
1162
|
+
transition: all 0.15s ease;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
.worktree-delete-all-btn:hover {
|
|
1166
|
+
background: var(--color-danger);
|
|
1167
|
+
color: white;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
.worktree-empty {
|
|
1171
|
+
padding: 24px 16px;
|
|
1172
|
+
text-align: center;
|
|
1173
|
+
font-size: 14px;
|
|
1174
|
+
font-style: italic;
|
|
1175
|
+
color: var(--color-text-muted);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
.worktree-pool-hint {
|
|
1179
|
+
display: flex;
|
|
1180
|
+
align-items: center;
|
|
1181
|
+
gap: 6px;
|
|
1182
|
+
margin-top: 12px;
|
|
1183
|
+
font-size: 12px;
|
|
1184
|
+
color: var(--color-text-muted);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
.worktree-pool-hint svg {
|
|
1188
|
+
flex-shrink: 0;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
[data-theme="dark"] .worktree-pool-config {
|
|
1192
|
+
background: var(--color-bg-secondary);
|
|
1193
|
+
border-color: var(--color-border-secondary);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
[data-theme="dark"] .worktree-pool-input {
|
|
1197
|
+
background: var(--color-bg-tertiary);
|
|
1198
|
+
border-color: var(--color-border-secondary);
|
|
1199
|
+
color: var(--color-text-primary);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
[data-theme="dark"] .worktree-item {
|
|
1203
|
+
background: var(--color-bg-secondary);
|
|
1204
|
+
border-color: var(--color-border-secondary);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
[data-theme="dark"] .worktree-item:hover {
|
|
1208
|
+
background: var(--color-bg-tertiary);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
[data-theme="dark"] .worktree-adhoc-badge {
|
|
1212
|
+
background: rgba(255, 255, 255, 0.08);
|
|
1213
|
+
color: var(--color-text-secondary);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
[data-theme="dark"] .worktree-delete-btn:hover {
|
|
1217
|
+
background: rgba(248, 81, 73, 0.15);
|
|
1218
|
+
border-color: rgba(248, 81, 73, 0.3);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
896
1221
|
/* ============================================
|
|
897
1222
|
Danger Zone
|
|
898
1223
|
============================================ */
|
|
@@ -1180,4 +1505,26 @@ html, body {
|
|
|
1180
1505
|
.action-buttons .btn {
|
|
1181
1506
|
flex: 1;
|
|
1182
1507
|
}
|
|
1508
|
+
|
|
1509
|
+
/* Worktrees responsive */
|
|
1510
|
+
.worktree-pool-config-items {
|
|
1511
|
+
flex-wrap: wrap;
|
|
1512
|
+
gap: 12px;
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
.worktree-item-top {
|
|
1516
|
+
flex-direction: column;
|
|
1517
|
+
align-items: flex-start;
|
|
1518
|
+
gap: 8px;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
.worktree-item-left {
|
|
1522
|
+
flex-wrap: wrap;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
.worktree-item-right {
|
|
1526
|
+
width: 100%;
|
|
1527
|
+
justify-content: flex-start;
|
|
1528
|
+
flex-wrap: wrap;
|
|
1529
|
+
}
|
|
1183
1530
|
}
|
package/public/index.html
CHANGED
|
@@ -473,17 +473,18 @@
|
|
|
473
473
|
font-size: 14px;
|
|
474
474
|
font-weight: 500;
|
|
475
475
|
line-height: 20px;
|
|
476
|
-
color:
|
|
477
|
-
background-color: var(--ai-
|
|
478
|
-
border:
|
|
476
|
+
color: var(--ai-primary);
|
|
477
|
+
background-color: var(--ai-subtle);
|
|
478
|
+
border: 1px solid var(--ai-border);
|
|
479
479
|
border-radius: var(--radius-md);
|
|
480
480
|
cursor: pointer;
|
|
481
|
-
transition: background-color var(--transition-fast);
|
|
481
|
+
transition: background-color var(--transition-fast), border-color var(--transition-fast);
|
|
482
482
|
white-space: nowrap;
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
.start-review-btn:hover:not(:disabled) {
|
|
486
|
-
background-color:
|
|
486
|
+
background-color: var(--ai-glow);
|
|
487
|
+
border-color: var(--ai-primary);
|
|
487
488
|
}
|
|
488
489
|
|
|
489
490
|
.start-review-btn:disabled {
|
|
@@ -491,6 +492,36 @@
|
|
|
491
492
|
cursor: not-allowed;
|
|
492
493
|
}
|
|
493
494
|
|
|
495
|
+
.start-review-btn-primary {
|
|
496
|
+
color: #ffffff;
|
|
497
|
+
background-color: var(--ai-primary);
|
|
498
|
+
border-color: var(--ai-primary);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.start-review-btn-primary:hover:not(:disabled) {
|
|
502
|
+
background-color: #b45309;
|
|
503
|
+
border-color: #b45309;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
[data-theme="dark"] .start-review-btn-primary:hover:not(:disabled) {
|
|
507
|
+
background-color: #d97706;
|
|
508
|
+
border-color: #d97706;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.start-review-btn-analyze {
|
|
512
|
+
transition: background-color var(--transition-fast), border-color var(--transition-fast), box-shadow 0.3s ease;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.start-review-btn-analyze:hover:not(:disabled) {
|
|
516
|
+
box-shadow: 0 0 8px rgba(217, 119, 6, 0.35), 0 0 20px rgba(251, 191, 36, 0.15);
|
|
517
|
+
animation: glow-pulse 2s ease-in-out infinite;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
@keyframes glow-pulse {
|
|
521
|
+
0%, 100% { box-shadow: 0 0 8px rgba(217, 119, 6, 0.35), 0 0 20px rgba(251, 191, 36, 0.15); }
|
|
522
|
+
50% { box-shadow: 0 0 12px rgba(217, 119, 6, 0.5), 0 0 28px rgba(251, 191, 36, 0.25); }
|
|
523
|
+
}
|
|
524
|
+
|
|
494
525
|
.btn-browse {
|
|
495
526
|
background-color: var(--ai-subtle);
|
|
496
527
|
color: var(--ai-primary);
|
|
@@ -1330,8 +1361,11 @@
|
|
|
1330
1361
|
autocomplete="off"
|
|
1331
1362
|
spellcheck="false"
|
|
1332
1363
|
>
|
|
1333
|
-
<button type="submit" class="start-review-btn" id="start-review-btn">
|
|
1334
|
-
|
|
1364
|
+
<button type="submit" class="start-review-btn start-review-btn-primary" id="start-review-btn" title="Open review without running analysis">
|
|
1365
|
+
Open
|
|
1366
|
+
</button>
|
|
1367
|
+
<button type="button" class="start-review-btn start-review-btn-analyze" id="analyze-review-btn" title="Open and analyze with default settings">
|
|
1368
|
+
Analyze
|
|
1335
1369
|
</button>
|
|
1336
1370
|
</form>
|
|
1337
1371
|
<div class="start-review-error" id="start-review-error-pr"></div>
|
|
@@ -1386,8 +1420,11 @@
|
|
|
1386
1420
|
<button type="button" class="start-review-btn btn-browse" id="browse-local-btn" title="Browse for directory">
|
|
1387
1421
|
Browse
|
|
1388
1422
|
</button>
|
|
1389
|
-
<button type="submit" class="start-review-btn" id="start-local-btn">
|
|
1390
|
-
|
|
1423
|
+
<button type="submit" class="start-review-btn start-review-btn-primary" id="start-local-btn" title="Open review without running analysis">
|
|
1424
|
+
Open
|
|
1425
|
+
</button>
|
|
1426
|
+
<button type="button" class="start-review-btn start-review-btn-analyze" id="analyze-local-btn" title="Open and analyze with default settings">
|
|
1427
|
+
Analyze
|
|
1391
1428
|
</button>
|
|
1392
1429
|
</form>
|
|
1393
1430
|
<div class="start-review-error" id="start-review-error-local"></div>
|
|
@@ -793,38 +793,7 @@ class AIPanel {
|
|
|
793
793
|
this.findingsList.querySelectorAll('.quick-action-chat').forEach(btn => {
|
|
794
794
|
btn.addEventListener('click', (e) => {
|
|
795
795
|
e.stopPropagation(); // Prevent triggering item click
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
const findingId = btn.dataset.findingId ? parseInt(btn.dataset.findingId, 10) : null;
|
|
799
|
-
const commentId = btn.dataset.commentId ? parseInt(btn.dataset.commentId, 10) : null;
|
|
800
|
-
const file = btn.dataset.findingFile || '';
|
|
801
|
-
const title = btn.dataset.findingTitle || '';
|
|
802
|
-
|
|
803
|
-
// Build context from the finding data
|
|
804
|
-
let suggestionContext = { title, file };
|
|
805
|
-
|
|
806
|
-
if (findingId && this.findings) {
|
|
807
|
-
const finding = this.findings.find(f => f.id === findingId);
|
|
808
|
-
if (finding) {
|
|
809
|
-
suggestionContext = {
|
|
810
|
-
suggestionId: findingId ? String(findingId) : null,
|
|
811
|
-
title: finding.title || title,
|
|
812
|
-
body: finding.formattedBody || finding.body || '',
|
|
813
|
-
type: finding.type || '',
|
|
814
|
-
file: finding.file || file,
|
|
815
|
-
line_start: finding.line_start || null,
|
|
816
|
-
line_end: finding.line_end || null,
|
|
817
|
-
side: 'RIGHT',
|
|
818
|
-
reasoning: null
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
window.chatPanel.open({
|
|
824
|
-
reviewId: window.prManager?.currentPR?.id,
|
|
825
|
-
suggestionId: findingId ? String(findingId) : (commentId ? String(commentId) : undefined),
|
|
826
|
-
suggestionContext
|
|
827
|
-
});
|
|
796
|
+
this.openQuickActionChat(btn);
|
|
828
797
|
});
|
|
829
798
|
});
|
|
830
799
|
|
|
@@ -887,6 +856,79 @@ class AIPanel {
|
|
|
887
856
|
}
|
|
888
857
|
}
|
|
889
858
|
|
|
859
|
+
/**
|
|
860
|
+
* Handle chat button clicks from review panel quick actions.
|
|
861
|
+
* Suggestions use suggestionContext; comments use commentContext.
|
|
862
|
+
* @param {HTMLButtonElement} btn - The clicked chat button
|
|
863
|
+
*/
|
|
864
|
+
openQuickActionChat(btn) {
|
|
865
|
+
if (!window.chatPanel) return;
|
|
866
|
+
|
|
867
|
+
const findingId = btn.dataset.findingId ? parseInt(btn.dataset.findingId, 10) : null;
|
|
868
|
+
const commentId = btn.dataset.commentId ? parseInt(btn.dataset.commentId, 10) : null;
|
|
869
|
+
const reviewId = window.prManager?.currentPR?.id;
|
|
870
|
+
|
|
871
|
+
const buildCommentContext = (comment, fallbackDataset = {}) => ({
|
|
872
|
+
commentId: comment?.id ? String(comment.id) : String(commentId),
|
|
873
|
+
body: comment?.body || '',
|
|
874
|
+
file: comment?.file || fallbackDataset.commentFile || '',
|
|
875
|
+
line_start: comment?.line_start ?? (fallbackDataset.commentLineStart ? parseInt(fallbackDataset.commentLineStart, 10) : null),
|
|
876
|
+
line_end: comment?.line_end ?? (fallbackDataset.commentLineEnd ? parseInt(fallbackDataset.commentLineEnd, 10) : null),
|
|
877
|
+
parentId: comment?.parent_id ?? (fallbackDataset.commentParentId ? parseInt(fallbackDataset.commentParentId, 10) : null),
|
|
878
|
+
source: 'user',
|
|
879
|
+
isFileLevel: comment?.is_file_level === 1 || comment?.is_file_level === true
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
if (commentId) {
|
|
883
|
+
const comment = this.comments?.find(c => c.id === commentId);
|
|
884
|
+
|
|
885
|
+
window.chatPanel.open({
|
|
886
|
+
reviewId,
|
|
887
|
+
commentContext: buildCommentContext(comment, btn.dataset)
|
|
888
|
+
});
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
const file = btn.dataset.findingFile || '';
|
|
893
|
+
const title = btn.dataset.findingTitle || '';
|
|
894
|
+
let suggestionContext = { title, file };
|
|
895
|
+
|
|
896
|
+
if (findingId && this.findings) {
|
|
897
|
+
const finding = this.findings.find(f => f.id === findingId);
|
|
898
|
+
if (finding) {
|
|
899
|
+
if (finding.status === 'adopted') {
|
|
900
|
+
const adoptedComment = this.comments?.find(c => c.parent_id === findingId && c.status !== 'inactive')
|
|
901
|
+
|| this.comments?.find(c => c.parent_id === findingId);
|
|
902
|
+
if (adoptedComment) {
|
|
903
|
+
window.chatPanel.open({
|
|
904
|
+
reviewId,
|
|
905
|
+
commentContext: buildCommentContext(adoptedComment)
|
|
906
|
+
});
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
suggestionContext = {
|
|
912
|
+
suggestionId: String(findingId),
|
|
913
|
+
title: finding.title || title,
|
|
914
|
+
body: finding.formattedBody || finding.body || '',
|
|
915
|
+
type: finding.type || '',
|
|
916
|
+
file: finding.file || file,
|
|
917
|
+
line_start: finding.line_start ?? null,
|
|
918
|
+
line_end: finding.line_end ?? null,
|
|
919
|
+
side: 'RIGHT',
|
|
920
|
+
reasoning: null
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
window.chatPanel.open({
|
|
926
|
+
reviewId,
|
|
927
|
+
suggestionId: findingId ? String(findingId) : undefined,
|
|
928
|
+
suggestionContext
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
|
|
890
932
|
onFindingClick(item) {
|
|
891
933
|
const itemId = item.dataset.id;
|
|
892
934
|
const itemType = item.dataset.itemType;
|
|
@@ -1204,9 +1246,9 @@ class AIPanel {
|
|
|
1204
1246
|
`;
|
|
1205
1247
|
}
|
|
1206
1248
|
|
|
1207
|
-
// Chat button for
|
|
1249
|
+
// Chat button for all findings when chat is available
|
|
1208
1250
|
let chatAction = '';
|
|
1209
|
-
if (
|
|
1251
|
+
if (document.documentElement.getAttribute('data-chat') === 'available') {
|
|
1210
1252
|
chatAction = `
|
|
1211
1253
|
<div class="finding-chat-action">
|
|
1212
1254
|
<button class="quick-action-btn quick-action-chat" data-finding-id="${finding.id}" data-finding-file="${finding.file || ''}" data-finding-title="${this.escapeHtml(title)}" title="Chat" aria-label="Chat about suggestion">
|
|
@@ -1279,12 +1321,12 @@ class AIPanel {
|
|
|
1279
1321
|
`;
|
|
1280
1322
|
}
|
|
1281
1323
|
|
|
1282
|
-
// Chat button for active
|
|
1324
|
+
// Chat button for active comments
|
|
1283
1325
|
let chatAction = '';
|
|
1284
|
-
if (!isDismissed &&
|
|
1326
|
+
if (!isDismissed && document.documentElement.getAttribute('data-chat') === 'available') {
|
|
1285
1327
|
chatAction = `
|
|
1286
1328
|
<div class="finding-chat-action">
|
|
1287
|
-
<button class="quick-action-btn quick-action-chat" data-comment-id="${comment.id}" data-
|
|
1329
|
+
<button class="quick-action-btn quick-action-chat" data-comment-id="${comment.id}" data-comment-file="${this.escapeHtml(comment.file || '')}" data-comment-line-start="${comment.line_start ?? ''}" data-comment-line-end="${comment.line_end ?? ''}" data-comment-parent-id="${comment.parent_id || ''}" title="Chat" aria-label="Chat about comment">
|
|
1288
1330
|
<svg viewBox="0 0 16 16" fill="currentColor" width="12" height="12"><path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"/></svg>
|
|
1289
1331
|
</button>
|
|
1290
1332
|
</div>
|