@in-the-loop-labs/pair-review 2.1.0 → 2.1.1

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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/plugin/.claude-plugin/plugin.json +1 -1
  3. package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
  4. package/plugin-code-critic/skills/analyze/references/level1-balanced.md +9 -0
  5. package/plugin-code-critic/skills/analyze/references/level1-fast.md +9 -0
  6. package/plugin-code-critic/skills/analyze/references/level1-thorough.md +9 -0
  7. package/plugin-code-critic/skills/analyze/references/level2-balanced.md +9 -0
  8. package/plugin-code-critic/skills/analyze/references/level2-fast.md +9 -0
  9. package/plugin-code-critic/skills/analyze/references/level2-thorough.md +9 -0
  10. package/plugin-code-critic/skills/analyze/references/level3-balanced.md +9 -0
  11. package/plugin-code-critic/skills/analyze/references/level3-fast.md +9 -0
  12. package/plugin-code-critic/skills/analyze/references/level3-thorough.md +9 -0
  13. package/plugin-code-critic/skills/analyze/references/orchestration-balanced.md +9 -0
  14. package/plugin-code-critic/skills/analyze/references/orchestration-fast.md +9 -0
  15. package/plugin-code-critic/skills/analyze/references/orchestration-thorough.md +9 -0
  16. package/public/js/components/ChatPanel.js +1 -1
  17. package/src/ai/analyzer.js +5 -1
  18. package/src/ai/prompts/baseline/consolidation/balanced.js +9 -0
  19. package/src/ai/prompts/baseline/consolidation/fast.js +9 -0
  20. package/src/ai/prompts/baseline/consolidation/thorough.js +9 -0
  21. package/src/ai/prompts/baseline/level1/balanced.js +9 -0
  22. package/src/ai/prompts/baseline/level1/fast.js +9 -0
  23. package/src/ai/prompts/baseline/level1/thorough.js +9 -0
  24. package/src/ai/prompts/baseline/level2/balanced.js +9 -0
  25. package/src/ai/prompts/baseline/level2/fast.js +9 -0
  26. package/src/ai/prompts/baseline/level2/thorough.js +9 -0
  27. package/src/ai/prompts/baseline/level3/balanced.js +9 -0
  28. package/src/ai/prompts/baseline/level3/fast.js +9 -0
  29. package/src/ai/prompts/baseline/level3/thorough.js +9 -0
  30. package/src/ai/prompts/baseline/orchestration/balanced.js +9 -0
  31. package/src/ai/prompts/baseline/orchestration/fast.js +9 -0
  32. package/src/ai/prompts/baseline/orchestration/thorough.js +9 -0
  33. package/src/ai/prompts/shared/output-schema.js +10 -1
  34. package/src/chat/prompt-builder.js +6 -1
  35. package/src/database.js +5 -1
  36. package/src/main.js +13 -165
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@in-the-loop-labs/pair-review",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pair-review",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
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": "2.1.0",
3
+ "version": "2.1.1",
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",
@@ -92,6 +92,15 @@ Do NOT modify files or run write commands. Analyze and report only.
92
92
  "summary": "Brief summary of findings"
93
93
  }
94
94
 
95
+ ### GitHub Suggestion Syntax
96
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
97
+
98
+ ```suggestion
99
+ replacement content here
100
+ ```
101
+
102
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
103
+
95
104
  ## Line Number Reference (old_or_new field)
96
105
  The "old_or_new" field indicates which line number column to use:
97
106
  - **"NEW"** (default): Use the NEW column number for:
@@ -78,6 +78,15 @@ Annotated diff tool, `cat -n`, ls, find, grep. Do NOT modify files.
78
78
  "summary": "Brief summary"
79
79
  }
80
80
 
81
+ ### GitHub Suggestion Syntax
82
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
83
+
84
+ ```suggestion
85
+ replacement content here
86
+ ```
87
+
88
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
89
+
81
90
  ## Line Numbers (old_or_new)
82
91
  - "NEW" (default): added [+] and context lines
83
92
  - "OLD": deleted [-] lines only
@@ -138,6 +138,15 @@ Output JSON with this structure:
138
138
  "summary": "Brief summary of findings"
139
139
  }
140
140
 
141
+ ### GitHub Suggestion Syntax
142
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
143
+
144
+ ```suggestion
145
+ replacement content here
146
+ ```
147
+
148
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
149
+
141
150
  ## Line Number Reference (old_or_new field)
142
151
  The "old_or_new" field indicates which line number column to use:
143
152
  - **"NEW"** (default): Use the NEW column number. This is correct for:
@@ -103,6 +103,15 @@ Note: You may use parallel read-only Tasks to examine multiple files simultaneou
103
103
  "summary": "Brief summary of file context findings"
104
104
  }
105
105
 
106
+ ### GitHub Suggestion Syntax
107
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
108
+
109
+ ```suggestion
110
+ replacement content here
111
+ ```
112
+
113
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
114
+
106
115
  ## Line Number Reference (old_or_new field)
107
116
  - **"NEW"** (default): For ADDED [+] lines and CONTEXT lines
108
117
  - **"OLD"**: ONLY for DELETED [-] lines
@@ -87,6 +87,15 @@ Annotated diff tool (preferred), `cat -n <file>`, ls, find, grep. Do NOT modify
87
87
  "summary": "Brief summary"
88
88
  }
89
89
 
90
+ ### GitHub Suggestion Syntax
91
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
92
+
93
+ ```suggestion
94
+ replacement content here
95
+ ```
96
+
97
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
98
+
90
99
  ## Line Numbers
91
100
  "NEW" (default): added [+] and context lines. "OLD": only deleted [-] lines.
92
101
 
@@ -177,6 +177,15 @@ Output JSON with this structure:
177
177
  "summary": "Brief summary of file context findings"
178
178
  }
179
179
 
180
+ ### GitHub Suggestion Syntax
181
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
182
+
183
+ ```suggestion
184
+ replacement content here
185
+ ```
186
+
187
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
188
+
180
189
  ## old_or_new Field Reference
181
190
  Use "NEW" (the default) for added lines [+] and context lines. Use "OLD" only for deleted lines [-]. When uncertain, use "NEW".
182
191
 
@@ -140,6 +140,15 @@ Output JSON with this structure:
140
140
  "summary": "Brief summary of how these changes connect to and impact the codebase"
141
141
  }
142
142
 
143
+ ### GitHub Suggestion Syntax
144
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
145
+
146
+ ```suggestion
147
+ replacement content here
148
+ ```
149
+
150
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
151
+
143
152
  ## Line Number Reference (old_or_new field)
144
153
  The "old_or_new" field indicates which line number column to use:
145
154
  - **"NEW"** (default): Use the NEW column number. This is correct for:
@@ -110,6 +110,15 @@ Output JSON with this structure:
110
110
  "summary": "Brief summary of how these changes connect to and impact the codebase"
111
111
  }
112
112
 
113
+ ### GitHub Suggestion Syntax
114
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
115
+
116
+ ```suggestion
117
+ replacement content here
118
+ ```
119
+
120
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
121
+
113
122
  ## Line Numbers (old_or_new)
114
123
  - **"NEW"** (default): For added lines [+] and context lines
115
124
  - **"OLD"**: Only for deleted lines [-]
@@ -251,6 +251,15 @@ Output JSON with this structure:
251
251
  "summary": "Brief summary of how these changes connect to and impact the codebase"
252
252
  }
253
253
 
254
+ ### GitHub Suggestion Syntax
255
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
256
+
257
+ ```suggestion
258
+ replacement content here
259
+ ```
260
+
261
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
262
+
254
263
  ## Line Number Reference (old_or_new field)
255
264
  The "old_or_new" field indicates which line number column to use:
256
265
  - **"NEW"** (default): Use the NEW column number. This is correct for:
@@ -120,6 +120,15 @@ Output JSON with this structure:
120
120
  "summary": "Brief summary of the key findings and their significance to the reviewer. Focus on WHAT was found, not HOW it was found. Do NOT mention 'orchestration', 'levels', 'merged from Level 1/2/3' etc. Write as if a single reviewer produced this analysis."
121
121
  }
122
122
 
123
+ ### GitHub Suggestion Syntax
124
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
125
+
126
+ ```suggestion
127
+ replacement content here
128
+ ```
129
+
130
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
131
+
123
132
  ## Line Number Reference (old_or_new field)
124
133
  The "old_or_new" field indicates which line number column to use:
125
134
  - **"NEW"** (default): Correct for ADDED lines and CONTEXT lines (unchanged lines in both versions)
@@ -97,6 +97,15 @@ Use "Consider...", "Worth noting..." - guidance not mandates.
97
97
  "summary": "Key findings as if from single reviewer (no mention of levels/orchestration)"
98
98
  }
99
99
 
100
+ ### GitHub Suggestion Syntax
101
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
102
+
103
+ ```suggestion
104
+ replacement content here
105
+ ```
106
+
107
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
108
+
100
109
  ## old_or_new
101
110
  "NEW" (default): added [+] and context lines. "OLD": deleted [-] only. Preserve from input.
102
111
 
@@ -250,6 +250,15 @@ Output JSON with this structure:
250
250
  "summary": "Brief summary of the key findings and their significance to the reviewer. Focus on WHAT was found, not HOW it was found. Do NOT mention 'orchestration', 'levels', 'merged from Level 1/2/3' etc. Write as if a single reviewer produced this analysis."
251
251
  }
252
252
 
253
+ ### GitHub Suggestion Syntax
254
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
255
+
256
+ ```suggestion
257
+ replacement content here
258
+ ```
259
+
260
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
261
+
253
262
  ## Line Number Reference (old_or_new field)
254
263
  The "old_or_new" field indicates which line number column to use:
255
264
  - **"NEW"** (default): Use the NEW column number. This is correct for:
@@ -2827,7 +2827,7 @@ class ChatPanel {
2827
2827
  _handleDismissCommentClick() {
2828
2828
  if (this.isStreaming || !this._contextItemId) return;
2829
2829
  this._pendingActionContext = { type: 'dismiss-comment', itemId: this._contextItemId };
2830
- this.inputEl.value = 'Please delete this comment.';
2830
+ this.inputEl.value = 'Please dismiss this comment.';
2831
2831
  this.sendMessage();
2832
2832
  }
2833
2833
 
@@ -3505,8 +3505,12 @@ File-level suggestions should NOT have a line number. They apply to the entire f
3505
3505
  continue;
3506
3506
  }
3507
3507
 
3508
+ const suggestionText = suggestion.suggestion;
3509
+ const hasSuggestionBlock = suggestionText?.trimStart().startsWith('```suggestion');
3508
3510
  const body = suggestion.description +
3509
- (suggestion.suggestion ? '\n\n**Suggestion:** ' + suggestion.suggestion : '');
3511
+ (suggestionText
3512
+ ? (hasSuggestionBlock ? '\n\n' + suggestionText : '\n\n**Suggestion:** ' + suggestionText)
3513
+ : '');
3510
3514
 
3511
3515
  const isFileLevel = suggestion.is_file_level === true || suggestion.line_start === null ? 1 : 0;
3512
3516
  const side = suggestion.old_or_new === 'OLD' ? 'LEFT' : 'RIGHT';
@@ -118,6 +118,15 @@ Output JSON with this structure:
118
118
  }],
119
119
  "summary": "Brief consolidation summary. Write as if a single reviewer produced this analysis — do NOT mention 'consolidation', 'merging', or 'multiple reviewers'."
120
120
  }
121
+
122
+ ### GitHub Suggestion Syntax
123
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
124
+
125
+ \`\`\`suggestion
126
+ replacement content here
127
+ \`\`\`
128
+
129
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
121
130
  </section>
122
131
 
123
132
  <section name="diff-instructions" required="true">
@@ -89,6 +89,15 @@ Merge suggestions from multiple AI reviewers. Deduplicate. Resolve conflicts. Ke
89
89
  }],
90
90
  "summary": "Key findings as if from single reviewer (no mention of consolidation/merging)"
91
91
  }
92
+
93
+ ### GitHub Suggestion Syntax
94
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
95
+
96
+ \`\`\`suggestion
97
+ replacement content here
98
+ \`\`\`
99
+
100
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
92
101
  </section>
93
102
 
94
103
  <section name="diff-instructions" required="true" tier="fast">
@@ -173,6 +173,15 @@ Output JSON with this structure:
173
173
  }],
174
174
  "summary": "Brief summary of the key findings and their significance. Write as if a single reviewer produced this analysis — do NOT mention 'consolidation', 'merging', or 'multiple reviewers'."
175
175
  }
176
+
177
+ ### GitHub Suggestion Syntax
178
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
179
+
180
+ \`\`\`suggestion
181
+ replacement content here
182
+ \`\`\`
183
+
184
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
176
185
  </section>
177
186
 
178
187
  <section name="diff-instructions" required="true" tier="thorough">
@@ -119,6 +119,15 @@ Do NOT modify files or run write commands. Analyze and report only.
119
119
  }],
120
120
  "summary": "Brief summary of findings"
121
121
  }
122
+
123
+ ### GitHub Suggestion Syntax
124
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
125
+
126
+ \`\`\`suggestion
127
+ replacement content here
128
+ \`\`\`
129
+
130
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
122
131
  </section>
123
132
 
124
133
  <section name="diff-instructions" required="true">
@@ -98,6 +98,15 @@ Annotated diff tool, \`cat -n\`, ls, find, grep. Do NOT modify files.
98
98
  }],
99
99
  "summary": "Brief summary"
100
100
  }
101
+
102
+ ### GitHub Suggestion Syntax
103
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
104
+
105
+ \`\`\`suggestion
106
+ replacement content here
107
+ \`\`\`
108
+
109
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
101
110
  </section>
102
111
 
103
112
  <section name="diff-instructions" required="true" tier="fast">
@@ -161,6 +161,15 @@ Output JSON with this structure:
161
161
  }],
162
162
  "summary": "Brief summary of findings"
163
163
  }
164
+
165
+ ### GitHub Suggestion Syntax
166
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
167
+
168
+ \`\`\`suggestion
169
+ replacement content here
170
+ \`\`\`
171
+
172
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
164
173
  </section>
165
174
 
166
175
  <section name="diff-instructions" required="true" tier="thorough">
@@ -126,6 +126,15 @@ Note: You may use parallel read-only Tasks to examine multiple files simultaneou
126
126
  }],
127
127
  "summary": "Brief summary of file context findings"
128
128
  }
129
+
130
+ ### GitHub Suggestion Syntax
131
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
132
+
133
+ \`\`\`suggestion
134
+ replacement content here
135
+ \`\`\`
136
+
137
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
129
138
  </section>
130
139
 
131
140
  <section name="diff-instructions" required="true">
@@ -107,6 +107,15 @@ Annotated diff tool (preferred), \`cat -n <file>\`, ls, find, grep. Do NOT modif
107
107
  }],
108
108
  "summary": "Brief summary"
109
109
  }
110
+
111
+ ### GitHub Suggestion Syntax
112
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
113
+
114
+ \`\`\`suggestion
115
+ replacement content here
116
+ \`\`\`
117
+
118
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
110
119
  </section>
111
120
 
112
121
  <section name="diff-instructions" required="true" tier="fast">
@@ -201,6 +201,15 @@ Output JSON with this structure:
201
201
  }],
202
202
  "summary": "Brief summary of file context findings"
203
203
  }
204
+
205
+ ### GitHub Suggestion Syntax
206
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
207
+
208
+ \`\`\`suggestion
209
+ replacement content here
210
+ \`\`\`
211
+
212
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
204
213
  </section>
205
214
 
206
215
  <section name="diff-instructions" required="true" tier="thorough">
@@ -150,6 +150,15 @@ Output JSON with this structure:
150
150
  }],
151
151
  "summary": "Brief summary of how these changes connect to and impact the codebase"
152
152
  }
153
+
154
+ ### GitHub Suggestion Syntax
155
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
156
+
157
+ \`\`\`suggestion
158
+ replacement content here
159
+ \`\`\`
160
+
161
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
153
162
  </section>
154
163
 
155
164
  <section name="diff-instructions" required="true">
@@ -124,6 +124,15 @@ Output JSON with this structure:
124
124
  }],
125
125
  "summary": "Brief summary of how these changes connect to and impact the codebase"
126
126
  }
127
+
128
+ ### GitHub Suggestion Syntax
129
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
130
+
131
+ \`\`\`suggestion
132
+ replacement content here
133
+ \`\`\`
134
+
135
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
127
136
  </section>
128
137
 
129
138
  <section name="diff-instructions" required="true" tier="fast">
@@ -268,6 +268,15 @@ Output JSON with this structure:
268
268
  }],
269
269
  "summary": "Brief summary of how these changes connect to and impact the codebase"
270
270
  }
271
+
272
+ ### GitHub Suggestion Syntax
273
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
274
+
275
+ \`\`\`suggestion
276
+ replacement content here
277
+ \`\`\`
278
+
279
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
271
280
  </section>
272
281
 
273
282
  <section name="diff-instructions" required="true" tier="thorough">
@@ -140,6 +140,15 @@ Output JSON with this structure:
140
140
  }],
141
141
  "summary": "Brief summary of the key findings and their significance to the reviewer. Focus on WHAT was found, not HOW it was found. Do NOT mention 'orchestration', 'levels', 'merged from Level 1/2/3' etc. Write as if a single reviewer produced this analysis."
142
142
  }
143
+
144
+ ### GitHub Suggestion Syntax
145
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
146
+
147
+ \`\`\`suggestion
148
+ replacement content here
149
+ \`\`\`
150
+
151
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
143
152
  </section>
144
153
 
145
154
  <section name="diff-instructions" required="true">
@@ -120,6 +120,15 @@ Use "Consider...", "Worth noting..." - guidance not mandates.
120
120
  }],
121
121
  "summary": "Key findings as if from single reviewer (no mention of levels/orchestration)"
122
122
  }
123
+
124
+ ### GitHub Suggestion Syntax
125
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
126
+
127
+ \`\`\`suggestion
128
+ replacement content here
129
+ \`\`\`
130
+
131
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
123
132
  </section>
124
133
 
125
134
  <section name="diff-instructions" required="true" tier="fast">
@@ -283,6 +283,15 @@ Output JSON with this structure:
283
283
  }],
284
284
  "summary": "Brief summary of the key findings and their significance to the reviewer. Focus on WHAT was found, not HOW it was found. Do NOT mention 'orchestration', 'levels', 'merged from Level 1/2/3' etc. Write as if a single reviewer produced this analysis."
285
285
  }
286
+
287
+ ### GitHub Suggestion Syntax
288
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
289
+
290
+ \`\`\`suggestion
291
+ replacement content here
292
+ \`\`\`
293
+
294
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.
286
295
  </section>
287
296
 
288
297
  <section name="diff-instructions" required="true" tier="thorough">
@@ -148,7 +148,16 @@ function buildOutputSchemaSection(schema) {
148
148
  Output ONLY valid JSON with no additional text, explanations, or markdown code blocks. Do not wrap the JSON in \`\`\`json blocks. The response must start with { and end with }.
149
149
 
150
150
  Output JSON with this structure:
151
- ${JSON.stringify(schema, null, 2)}`
151
+ ${JSON.stringify(schema, null, 2)}
152
+
153
+ ### GitHub Suggestion Syntax
154
+ When suggesting a specific change, **embed** a GitHub suggestion block within the "suggestion" field:
155
+
156
+ \`\`\`suggestion
157
+ replacement content here
158
+ \`\`\`
159
+
160
+ The content inside the block is the complete replacement for the commented line(s). Do not include explanation inside the block — any explanation should appear as plain text outside it. For non-specific suggestions, use plain text only.`
152
161
  };
153
162
  }
154
163
 
@@ -44,6 +44,7 @@ function buildChatPrompt({ review, prData, skillPath, chatInstructions }) {
44
44
  '- **Comments** are human-curated review findings (created by the reviewer).',
45
45
  '- **Suggestions** are AI-generated findings from analysis runs.',
46
46
  '- **Workflow**: AI generates suggestions → reviewer triages (adopt, edit, or dismiss) → adopted suggestions become comments.',
47
+ '- **IMPORTANT**: Do NOT adopt, dismiss, or modify suggestions or comments unless the user explicitly asks you to. Your role is to discuss and explain — the reviewer decides what action to take.',
47
48
  '- **Analysis runs** are the process that produces suggestions. Each run has a provider, model, tier, and status.',
48
49
  '- **Review ID** is a stable integer identifying this review session, used in all API calls.'
49
50
  ];
@@ -69,7 +70,11 @@ function buildChatPrompt({ review, prData, skillPath, chatInstructions }) {
69
70
  'These become clickable links in the UI. Do NOT use backtick code spans for file references you want to be clickable.\n\n' +
70
71
  'Files in the diff can be referenced freely. Files outside the diff can also be referenced; ' +
71
72
  'to make them visible in the diff panel, add them as context files via the API (see skill). ' +
72
- 'Add context files judiciously — only when directly relevant, with focused line ranges.'
73
+ 'Add context files judiciously — only when directly relevant, with focused line ranges.\n\n' +
74
+ 'When the user asks to see code (e.g. "Show me…", "Where is…", "What does X look like?"), ' +
75
+ 'add the relevant file as a context file with a focused line range so it appears in the diff panel. ' +
76
+ 'When explaining code, prefer referencing specific line ranges (e.g. [[file:path.js:10-25]]) ' +
77
+ 'over quoting large blocks in your response. Short inline snippets (a few lines) are fine when they clarify the explanation.'
73
78
  );
74
79
 
75
80
  // Tool usage discipline — avoid unnecessary Task delegation
package/src/database.js CHANGED
@@ -2193,8 +2193,12 @@ class CommentRepository {
2193
2193
  }
2194
2194
 
2195
2195
  for (const suggestion of normalized) {
2196
+ const suggestionText = suggestion.suggestion;
2197
+ const hasSuggestionBlock = suggestionText?.trimStart().startsWith('```suggestion');
2196
2198
  const body = suggestion.description +
2197
- (suggestion.suggestion ? '\n\n**Suggestion:** ' + suggestion.suggestion : '');
2199
+ (suggestionText
2200
+ ? (hasSuggestionBlock ? '\n\n' + suggestionText : '\n\n**Suggestion:** ' + suggestionText)
2201
+ : '');
2198
2202
 
2199
2203
  // File-level suggestions have is_file_level=true or have null line_start
2200
2204
  const isFileLevel = suggestion.is_file_level === true || suggestion.line_start === null ? 1 : 0;
package/src/main.js CHANGED
@@ -466,8 +466,6 @@ AI PROVIDERS:
466
466
  * @param {Object} flags - Parsed command line flags
467
467
  */
468
468
  async function handlePullRequest(args, config, db, flags = {}) {
469
- let prInfo = null; // Declare prInfo outside try block for error handling
470
-
471
469
  try {
472
470
  // Get GitHub token (env var takes precedence over config)
473
471
  const githubToken = getGitHubToken(config);
@@ -477,156 +475,36 @@ async function handlePullRequest(args, config, db, flags = {}) {
477
475
 
478
476
  // Parse PR arguments
479
477
  const parser = new PRArgumentParser();
480
- prInfo = await parser.parsePRArguments(args);
481
-
482
- console.log(`Processing pull request #${prInfo.number} from ${prInfo.owner}/${prInfo.repo}`);
483
-
484
- // Create GitHub client and verify repository access
485
- const githubClient = new GitHubClient(githubToken);
486
- const repoExists = await githubClient.repositoryExists(prInfo.owner, prInfo.repo);
487
- if (!repoExists) {
488
- throw new Error(`Repository ${prInfo.owner}/${prInfo.repo} not found or not accessible`);
489
- }
490
-
491
- // Fetch PR data from GitHub
492
- console.log('Fetching pull request data from GitHub...');
493
- const prData = await githubClient.fetchPullRequest(prInfo.owner, prInfo.repo, prInfo.number);
478
+ const prInfo = await parser.parsePRArguments(args);
494
479
 
495
- // Determine repository path: only use cwd if it matches the target repo
480
+ // Register cwd as known repo path if it matches the target repo
496
481
  const currentDir = parser.getCurrentDirectory();
497
482
  const isMatchingRepo = await parser.isMatchingRepository(currentDir, prInfo.owner, prInfo.repo);
498
- const repository = normalizeRepository(prInfo.owner, prInfo.repo);
499
-
500
- let repositoryPath;
501
- let worktreeSourcePath;
502
- let checkoutScript;
503
- let checkoutTimeout;
504
- let worktreeConfig = null;
505
483
  if (isMatchingRepo) {
506
- // Current directory is a checkout of the target repository
507
- repositoryPath = currentDir;
508
- // Register the known repository location for future web UI usage
509
484
  await registerRepositoryLocation(db, currentDir, prInfo.owner, prInfo.repo);
510
-
511
- // Resolve monorepo config options (checkout_script, worktree_directory, worktree_name_template)
512
- // even when running from inside the target repo, so they are not silently ignored.
513
- const resolved = resolveMonorepoOptions(config, repository);
514
- checkoutScript = resolved.checkoutScript;
515
- checkoutTimeout = resolved.checkoutTimeout;
516
- worktreeConfig = resolved.worktreeConfig;
517
- } else {
518
- // Current directory is not the target repository - find or clone it
519
- console.log(`Current directory is not a checkout of ${prInfo.owner}/${prInfo.repo}, locating repository...`);
520
- const result = await findRepositoryPath({
521
- db,
522
- owner: prInfo.owner,
523
- repo: prInfo.repo,
524
- repository,
525
- prNumber: prInfo.number,
526
- config,
527
- onProgress: (progress) => {
528
- if (progress.message) {
529
- console.log(progress.message);
530
- }
531
- }
532
- });
533
- repositoryPath = result.repositoryPath;
534
- worktreeSourcePath = result.worktreeSourcePath;
535
- checkoutScript = result.checkoutScript;
536
- checkoutTimeout = result.checkoutTimeout;
537
- worktreeConfig = result.worktreeConfig;
538
485
  }
539
486
 
540
- // Setup git worktree
541
- console.log('Setting up git worktree...');
542
- const worktreeManager = new GitWorktreeManager(db, worktreeConfig || {});
543
- const worktreePath = await worktreeManager.createWorktreeForPR(prInfo, prData, repositoryPath, {
544
- worktreeSourcePath,
545
- checkoutScript,
546
- checkoutTimeout
547
- });
548
-
549
- // Generate unified diff
550
- console.log('Generating unified diff...');
551
- const diff = await worktreeManager.generateUnifiedDiff(worktreePath, prData);
552
- const changedFiles = await worktreeManager.getChangedFiles(worktreePath, prData);
487
+ // Set model override if provided via CLI flag
488
+ if (flags.model) {
489
+ process.env.PAIR_REVIEW_MODEL = flags.model;
490
+ }
553
491
 
554
- // Store PR data in database
555
- console.log('Storing pull request data...');
556
- await storePRData(db, prInfo, prData, diff, changedFiles, worktreePath);
492
+ // Start server and open browser to setup page
493
+ const port = await startServer(db);
557
494
 
558
- // Start server with PR context
559
- console.log('Starting server...');
560
- const port = await startServerWithPRContext(config, prInfo, flags);
495
+ // Async cleanup of stale worktrees (don't block startup)
496
+ cleanupStaleWorktreesAsync(config);
561
497
 
562
- // Trigger AI analysis server-side if --ai flag is present
498
+ let url = `http://localhost:${port}/pr/${prInfo.owner}/${prInfo.repo}/${prInfo.number}`;
563
499
  if (flags.ai) {
564
- console.log('Starting AI analysis...');
565
-
566
- // Wait for server to be ready with retry logic
567
- const maxRetries = 5;
568
- const retryDelay = 200; // ms
569
- let lastError;
570
-
571
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
572
- try {
573
- // Add small delay to ensure server is fully initialized
574
- if (attempt > 1) {
575
- await new Promise(resolve => setTimeout(resolve, retryDelay * attempt));
576
- }
577
-
578
- const response = await fetch(`http://localhost:${port}/api/pr/${prInfo.owner}/${prInfo.repo}/${prInfo.number}/analyses`, {
579
- method: 'POST',
580
- headers: { 'Content-Type': 'application/json' }
581
- });
582
-
583
- if (response.ok) {
584
- const result = await response.json();
585
- console.log(`AI analysis started (ID: ${result.analysisId})`);
586
- break; // Success, exit retry loop
587
- } else {
588
- lastError = `Server responded with ${response.status}: ${await response.text()}`;
589
- if (attempt === maxRetries) {
590
- console.warn('Failed to start AI analysis:', lastError);
591
- }
592
- }
593
- } catch (error) {
594
- lastError = error.message;
595
- if (attempt === maxRetries) {
596
- console.warn('Could not start AI analysis after', maxRetries, 'attempts:', lastError);
597
- }
598
- }
599
- }
500
+ url += '?analyze=true';
600
501
  }
601
502
 
602
- // Open browser to PR view
603
- const url = `http://localhost:${port}/pr/${prInfo.owner}/${prInfo.repo}/${prInfo.number}`;
604
-
605
503
  console.log(`Opening browser to: ${url}`);
606
504
  await open(url);
607
505
 
608
506
  } catch (error) {
609
- // Provide cleaner error messages for common issues
610
- if (error.message && error.message.includes('not found in repository')) {
611
- if (prInfo) {
612
- console.error(`\n❌ Pull request #${prInfo.number} does not exist in ${prInfo.owner}/${prInfo.repo}`);
613
- } else {
614
- console.error(`\n❌ ${error.message}`);
615
- }
616
- console.error('Please check the PR number and try again.\n');
617
- } else if (error.message && error.message.includes('authentication failed')) {
618
- console.error('\n❌ GitHub authentication failed');
619
- console.error('Please check your token in ~/.pair-review/config.json\n');
620
- } else if (error.message && error.message.includes('Repository') && error.message.includes('not found')) {
621
- console.error(`\n❌ ${error.message}`);
622
- console.error('Please check the repository name and your access permissions.\n');
623
- } else if (error.message && error.message.includes('Network error')) {
624
- console.error('\n❌ Network connection error');
625
- console.error('Please check your internet connection and try again.\n');
626
- } else {
627
- // For other errors, show a clean message without stack trace
628
- console.error(`\n❌ Error: ${error.message}\n`);
629
- }
507
+ console.error(`\n❌ Error: ${error.message}\n`);
630
508
  process.exit(1);
631
509
  }
632
510
  }
@@ -647,36 +525,6 @@ async function startServerOnly(config) {
647
525
  await open(url);
648
526
  }
649
527
 
650
- /**
651
- * Start server with PR context
652
- * @param {Object} config - Application configuration
653
- * @param {Object} prInfo - PR information
654
- * @param {Object} flags - Command line flags
655
- * @returns {Promise<number>} Server port
656
- */
657
- async function startServerWithPRContext(config, prInfo, flags = {}) {
658
- // Set environment variable for PR context
659
- process.env.PAIR_REVIEW_PR = JSON.stringify(prInfo);
660
-
661
- // Set environment variable for auto-AI flag
662
- if (flags.ai) {
663
- process.env.PAIR_REVIEW_AUTO_AI = 'true';
664
- }
665
-
666
- // Set environment variable for model override (CLI takes priority)
667
- if (flags.model) {
668
- process.env.PAIR_REVIEW_MODEL = flags.model;
669
- }
670
-
671
- const { startServer } = require('./server');
672
- const actualPort = await startServer(db);
673
-
674
- // Async cleanup of stale worktrees (don't block startup)
675
- cleanupStaleWorktreesAsync(config);
676
-
677
- // Return the actual port the server started on
678
- return actualPort;
679
- }
680
528
 
681
529
  /**
682
530
  * Format AI suggestion with emoji and category prefix