@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 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
- npm install
756
+ pnpm install
756
757
 
757
758
  # Run tests
758
- npm test
759
+ pnpm test
759
760
 
760
761
  # Run tests in watch mode
761
- npm run test:watch
762
+ pnpm run test:watch
762
763
 
763
764
  # Run E2E tests
764
- npm run test:e2e
765
+ pnpm run test:e2e
765
766
 
766
767
  # Run E2E tests with visible browser
767
- npm run test:e2e:headed
768
+ pnpm run test:e2e:headed
768
769
 
769
770
  # Start development server
770
- npm run dev
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.2.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 && npm install --package-lock-only && node scripts/sync-plugin-versions.js && git add package.json package-lock.json 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; } && npm run version && changeset tag && npm publish && git push && git push --tags"
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.2.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.2.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: #ffffff;
477
- background-color: var(--ai-primary);
478
- border: none;
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: #b45309;
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
- Start Review
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
- Review Local
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
- if (!window.chatPanel) return;
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 active and dismissed findings (upper-right corner)
1249
+ // Chat button for all findings when chat is available
1208
1250
  let chatAction = '';
1209
- if (finding.status !== 'adopted' && document.documentElement.getAttribute('data-chat') === 'available') {
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 AI-originated comments
1324
+ // Chat button for active comments
1283
1325
  let chatAction = '';
1284
- if (!isDismissed && comment.parent_id && document.documentElement.getAttribute('data-chat') === 'available') {
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-finding-file="${comment.file || ''}" data-finding-title="${this.escapeHtml(title)}" title="Chat" aria-label="Chat about comment">
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>