@uniswap/ai-toolkit-nx-claude 0.5.13 → 0.5.14

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.
@@ -0,0 +1,879 @@
1
+ ---
2
+ name: work-through-pr-comments
3
+ description: Methodically work through GitHub pull request comments in a conversational workflow, analyzing each comment, presenting solution options, gathering your decisions, and implementing approved changes.
4
+ argument-hint: <pr-number> OR <owner/repo> <pr-number>
5
+ allowed-tools: Bash(*), Read(*), Write(*), Edit(*), Grep(*), Glob(*), AskUserQuestion(*), mcp__github__get_pull_request(*), mcp__github__get_pull_request_comments(*), mcp__github__get_pull_request_reviews(*)
6
+ ---
7
+
8
+ # Work Through PR Comments
9
+
10
+ Methodically work through GitHub pull request comments in a conversational workflow, analyzing each comment, presenting solution options, gathering your decisions, and implementing approved changes.
11
+
12
+ ## Usage
13
+
14
+ ```bash
15
+ /work-through-pr-comments <pr-number> # Work through comments on PR in current repo
16
+ /work-through-pr-comments <owner/repo> <pr-number> # Work through comments on PR in specific repo
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ ```bash
22
+ /work-through-pr-comments 154 # Work through comments on PR #154 in current repo
23
+ /work-through-pr-comments Uniswap/ai-toolkit 154 # Work through comments on PR #154 in Uniswap/ai-toolkit
24
+ ```
25
+
26
+ ## Workflow Overview
27
+
28
+ This command implements a **conversational, methodical workflow** for addressing PR comments:
29
+
30
+ 1. **Fetch PR Details**: Get PR information, reviews, and inline comments
31
+ 2. **Analyze Each Comment**: For each comment, provide context and analysis
32
+ 3. **Present Options**: Suggest multiple solution approaches with pros/cons
33
+ 4. **Gather Decisions**: Ask you which approach to take
34
+ 5. **Implement Changes**: Make the approved changes
35
+ 6. **Verify**: Test and validate the changes
36
+ 7. **Repeat**: Move to next comment until all are addressed
37
+ 8. **Commit**: Offer to create a single commit with all changes
38
+
39
+ ## Input Parameters
40
+
41
+ ### Required
42
+
43
+ - **pr-number**: The pull request number (e.g., `154`)
44
+
45
+ ### Optional
46
+
47
+ - **owner/repo**: Repository in format `owner/repo` (defaults to current repo detected from git remote)
48
+
49
+ ## Step-by-Step Implementation
50
+
51
+ ### Step 1: Parse Input and Detect Repository
52
+
53
+ ```typescript
54
+ // Parse command arguments
55
+ const args = userInput.trim().split(/\s+/);
56
+
57
+ let owner: string;
58
+ let repo: string;
59
+ let prNumber: number;
60
+
61
+ if (args.length === 1) {
62
+ // Format: /work-through-pr-comments 154
63
+ // Detect from current git remote
64
+ const remoteUrl = await Bash('git config --get remote.origin.url');
65
+ // Parse owner/repo from: git@github.com:Uniswap/ai-toolkit.git or https://github.com/Uniswap/ai-toolkit.git
66
+ const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
67
+ if (!match) {
68
+ throw new Error(
69
+ 'Could not detect repository from git remote. Use: /work-through-pr-comments <owner/repo> <pr-number>'
70
+ );
71
+ }
72
+ [, owner, repo] = match;
73
+ prNumber = parseInt(args[0]);
74
+ } else if (args.length === 2) {
75
+ // Format: /work-through-pr-comments Uniswap/ai-toolkit 154
76
+ [owner, repo] = args[0].split('/');
77
+ prNumber = parseInt(args[1]);
78
+ } else {
79
+ throw new Error(
80
+ 'Usage: /work-through-pr-comments <pr-number> OR /work-through-pr-comments <owner/repo> <pr-number>'
81
+ );
82
+ }
83
+
84
+ // Validate PR number
85
+ if (isNaN(prNumber) || prNumber <= 0) {
86
+ throw new Error(`Invalid PR number: ${args[args.length - 1]}`);
87
+ }
88
+
89
+ console.log(`📋 Analyzing PR #${prNumber} in ${owner}/${repo}...`);
90
+ ```
91
+
92
+ ### Step 2: Fetch PR Data
93
+
94
+ Fetch all PR-related data in parallel for efficiency:
95
+
96
+ ```typescript
97
+ // Fetch PR details, comments, and reviews in parallel
98
+ const [prDetails, prComments, prReviews] = await Promise.all([
99
+ mcp__github__get_pull_request({ owner, repo, pull_number: prNumber }),
100
+ mcp__github__get_pull_request_comments({ owner, repo, pull_number: prNumber }),
101
+ mcp__github__get_pull_request_reviews({ owner, repo, pull_number: prNumber }),
102
+ ]);
103
+
104
+ console.log(`\n**PR Title**: ${prDetails.title}`);
105
+ console.log(`**Author**: ${prDetails.user.login}`);
106
+ console.log(`**State**: ${prDetails.state}`);
107
+ console.log(`**URL**: ${prDetails.html_url}\n`);
108
+ ```
109
+
110
+ ### Step 3: Organize and Categorize Comments
111
+
112
+ Organize comments into categories for clear presentation:
113
+
114
+ ```typescript
115
+ interface Comment {
116
+ id: string;
117
+ type: 'inline' | 'review';
118
+ author: string;
119
+ body: string;
120
+ path?: string;
121
+ line?: number;
122
+ position?: number;
123
+ created_at: string;
124
+ html_url: string;
125
+ }
126
+
127
+ // Collect all comments
128
+ const allComments: Comment[] = [];
129
+
130
+ // Add inline comments (code review comments)
131
+ prComments.forEach((comment) => {
132
+ allComments.push({
133
+ id: `comment-${comment.id}`,
134
+ type: 'inline',
135
+ author: comment.user.login,
136
+ body: comment.body,
137
+ path: comment.path,
138
+ line: comment.line,
139
+ position: comment.position,
140
+ created_at: comment.created_at,
141
+ html_url: comment.html_url,
142
+ });
143
+ });
144
+
145
+ // Add review comments (from review body)
146
+ prReviews.forEach((review) => {
147
+ if (review.body && review.body.trim()) {
148
+ allComments.push({
149
+ id: `review-${review.id}`,
150
+ type: 'review',
151
+ author: review.user.login,
152
+ body: review.body,
153
+ created_at: review.submitted_at,
154
+ html_url: review.html_url,
155
+ });
156
+ }
157
+ });
158
+
159
+ // Sort by creation date (oldest first)
160
+ allComments.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
161
+
162
+ console.log(`Found ${allComments.length} comment(s) to address`);
163
+
164
+ if (allComments.length === 0) {
165
+ console.log('✅ No comments to address!');
166
+ return;
167
+ }
168
+ ```
169
+
170
+ ### Step 4: Process Each Comment Conversationally
171
+
172
+ For each comment, follow the conversational workflow:
173
+
174
+ ````typescript
175
+ // Track decisions and changes
176
+ const decisions = [];
177
+ const filesToChange = new Set<string>();
178
+
179
+ for (let i = 0; i < allComments.length; i++) {
180
+ const comment = allComments[i];
181
+ const commentNum = i + 1;
182
+
183
+ console.log(`\n${'='.repeat(80)}`);
184
+ console.log(`📝 Comment ${commentNum}/${allComments.length}`);
185
+ console.log(`${'='.repeat(80)}\n`);
186
+
187
+ // Display comment context
188
+ console.log(`**Author**: ${comment.author}`);
189
+ console.log(
190
+ `**Type**: ${comment.type === 'inline' ? 'Inline code comment' : 'General review comment'}`
191
+ );
192
+ if (comment.path) {
193
+ console.log(`**Location**: \`${comment.path}:${comment.line || comment.position}\``);
194
+ }
195
+ console.log(`**Comment**:\n> ${comment.body}\n`);
196
+
197
+ // Read affected file if inline comment
198
+ let fileContext = null;
199
+ if (comment.type === 'inline' && comment.path) {
200
+ try {
201
+ const fullPath = `${process.cwd()}/${comment.path}`;
202
+ fileContext = await Read(fullPath);
203
+
204
+ // Show relevant section around the comment line
205
+ const lines = fileContext.split('\n');
206
+ const targetLine = comment.line || comment.position || 0;
207
+ const startLine = Math.max(0, targetLine - 5);
208
+ const endLine = Math.min(lines.length, targetLine + 5);
209
+
210
+ console.log(`**Current Code Context** (\`${comment.path}\`):\n`);
211
+ console.log('```');
212
+ for (let i = startLine; i < endLine; i++) {
213
+ const lineNum = i + 1;
214
+ const prefix = lineNum === targetLine ? '→ ' : ' ';
215
+ console.log(`${prefix}${String(lineNum).padStart(4)} ${lines[i]}`);
216
+ }
217
+ console.log('```\n');
218
+
219
+ filesToChange.add(fullPath);
220
+ } catch (error) {
221
+ console.log(`⚠️ Could not read file: ${comment.path}\n`);
222
+ }
223
+ }
224
+
225
+ // AI Analysis: Understand the comment and suggest solutions
226
+ console.log('## 🤔 My Analysis\n');
227
+
228
+ // This is where Claude should analyze the comment based on its content
229
+ // The actual analysis will be done by Claude in real-time
230
+ // Key things to analyze:
231
+ // - What is the reviewer asking for?
232
+ // - Why are they asking for it?
233
+ // - What are the implications?
234
+ // - What are possible approaches?
235
+
236
+ console.log('## 💡 Suggested Solutions\n');
237
+
238
+ // Present options (Claude should generate these based on the comment)
239
+ // Example structure:
240
+ console.log('**Option A**: [First approach]');
241
+ console.log('- ✅ Pros: ...');
242
+ console.log('- ❌ Cons: ...\n');
243
+
244
+ console.log('**Option B**: [Second approach]');
245
+ console.log('- ✅ Pros: ...');
246
+ console.log('- ❌ Cons: ...\n');
247
+
248
+ console.log('**💭 My Recommendation**: [Preferred option and reasoning]\n');
249
+
250
+ // Ask user for decision using AskUserQuestion
251
+ const userChoice = await AskUserQuestion({
252
+ questions: [
253
+ {
254
+ question: `How would you like to address this comment from ${comment.author}?`,
255
+ header: 'Approach',
256
+ multiSelect: false,
257
+ options: [
258
+ {
259
+ label: 'Option A',
260
+ description: 'Implement the first suggested approach',
261
+ },
262
+ {
263
+ label: 'Option B',
264
+ description: 'Implement the second suggested approach',
265
+ },
266
+ {
267
+ label: 'Custom',
268
+ description: "I'll tell you what I want to do",
269
+ },
270
+ {
271
+ label: 'Skip',
272
+ description: 'Skip this comment for now',
273
+ },
274
+ ],
275
+ },
276
+ ],
277
+ });
278
+
279
+ const selectedOption = userChoice.answers['Approach'];
280
+
281
+ if (selectedOption === 'Skip') {
282
+ console.log('⏭️ Skipping this comment\n');
283
+ decisions.push({
284
+ comment: commentNum,
285
+ author: comment.author,
286
+ decision: 'skipped',
287
+ reason: 'User chose to skip',
288
+ });
289
+ continue;
290
+ }
291
+
292
+ if (selectedOption === 'Custom') {
293
+ console.log(
294
+ "📝 Please describe what you'd like me to do for this comment, and I'll implement it.\n"
295
+ );
296
+ // Wait for user to provide instructions
297
+ // Then implement based on their guidance
298
+ continue;
299
+ }
300
+
301
+ // Implement the chosen solution
302
+ console.log(`\n## 🔧 Implementing: ${selectedOption}\n`);
303
+
304
+ // Make the necessary changes based on the chosen option
305
+ // This will vary depending on the comment, but generally:
306
+ // 1. Use Read tool to get current file content (if not already read)
307
+ // 2. Use Edit tool to make changes
308
+ // 3. Verify changes
309
+ // 4. Track changed files
310
+
311
+ decisions.push({
312
+ comment: commentNum,
313
+ author: comment.author,
314
+ decision: selectedOption,
315
+ files_changed: Array.from(filesToChange),
316
+ });
317
+
318
+ console.log('✅ Changes implemented\n');
319
+ }
320
+ ````
321
+
322
+ ### Step 5: Summary and Commit
323
+
324
+ After processing all comments, provide a summary and offer to commit:
325
+
326
+ ````typescript
327
+ console.log(`\n${'='.repeat(80)}`);
328
+ console.log('📊 Summary of Changes');
329
+ console.log(`${'='.repeat(80)}\n`);
330
+
331
+ const addressedCount = decisions.filter((d) => d.decision !== 'skipped').length;
332
+ const skippedCount = decisions.filter((d) => d.decision === 'skipped').length;
333
+
334
+ console.log(`Total comments processed: ${allComments.length}`);
335
+ console.log(`Comments addressed: ${addressedCount}`);
336
+ console.log(`Comments skipped: ${skippedCount}`);
337
+ console.log(`Files modified: ${filesToChange.size}\n`);
338
+
339
+ // List all changed files
340
+ if (filesToChange.size > 0) {
341
+ console.log('**Files changed:**');
342
+ filesToChange.forEach((file) => {
343
+ console.log(` - ${file}`);
344
+ });
345
+ console.log('');
346
+ }
347
+
348
+ // Run formatting and linting
349
+ console.log('🔍 Running code quality checks...\n');
350
+
351
+ try {
352
+ // Format code
353
+ await Bash('npx nx format:write --uncommitted');
354
+ console.log('✅ Code formatting complete');
355
+
356
+ // Lint markdown if any .md files changed
357
+ const hasMarkdownChanges = Array.from(filesToChange).some((f) => f.endsWith('.md'));
358
+ if (hasMarkdownChanges) {
359
+ await Bash('npm exec markdownlint-cli2 -- --fix "**/*.md"');
360
+ console.log('✅ Markdown linting complete');
361
+ }
362
+ } catch (error) {
363
+ console.log(`⚠️ Some quality checks failed: ${error.message}`);
364
+ console.log('Continuing...\n');
365
+ }
366
+
367
+ // Show git status
368
+ const gitStatus = await Bash('git status --short');
369
+ console.log('\n**Git status:**');
370
+ console.log('```');
371
+ console.log(gitStatus);
372
+ console.log('```\n');
373
+
374
+ // Ask if user wants to commit
375
+ const shouldCommit = await AskUserQuestion({
376
+ questions: [
377
+ {
378
+ question: 'Would you like to commit these changes?',
379
+ header: 'Commit',
380
+ multiSelect: false,
381
+ options: [
382
+ {
383
+ label: 'Yes',
384
+ description: 'Stage all changes and create a commit',
385
+ },
386
+ {
387
+ label: 'No',
388
+ description: 'Leave changes unstaged for manual review',
389
+ },
390
+ ],
391
+ },
392
+ ],
393
+ });
394
+
395
+ if (shouldCommit.answers['Commit'] === 'Yes') {
396
+ // Stage all changes
397
+ await Bash('git add -A');
398
+
399
+ // Create commit message
400
+ const commitMessage = `fix: address PR #${prNumber} review comments
401
+
402
+ Addressed ${addressedCount} review comment(s) from ${
403
+ new Set(decisions.map((d) => d.author)).size
404
+ } reviewer(s)
405
+ Modified ${filesToChange.size} file(s)
406
+
407
+ PR: ${prDetails.html_url}`;
408
+
409
+ // Create commit using heredoc for proper formatting
410
+ await Bash(`git commit -m "$(cat <<'EOF'
411
+ ${commitMessage}
412
+ EOF
413
+ )"`);
414
+
415
+ console.log('✅ Changes committed successfully!');
416
+
417
+ // Show the commit
418
+ await Bash('git log -1 --oneline');
419
+ } else {
420
+ console.log('ℹ️ Changes left unstaged. Review and commit when ready.');
421
+ }
422
+ ````
423
+
424
+ ## Workflow Principles
425
+
426
+ ### 1. Conversational Approach
427
+
428
+ - **One comment at a time**: Don't overwhelm with all comments at once
429
+ - **Clear context**: Always show the comment, location, and relevant code
430
+ - **Ask questions**: Use AskUserQuestion tool to gather user preferences
431
+ - **Wait for decisions**: Never assume what the user wants
432
+ - **Support custom responses**: Allow user to provide their own solution
433
+
434
+ ### 2. Methodical Analysis
435
+
436
+ For each comment:
437
+
438
+ 1. **Understand Intent**: What is the reviewer asking for?
439
+ 2. **Identify Impact**: What files/code needs to change?
440
+ 3. **Assess Complexity**: Is this simple or complex?
441
+ 4. **Consider Tradeoffs**: What are the pros/cons of different approaches?
442
+ 5. **Provide Recommendation**: Share your preferred option with reasoning
443
+
444
+ ### 3. Present Options
445
+
446
+ Always provide **2-3 options** with:
447
+
448
+ - Clear, concise description
449
+ - Pros and cons for each
450
+ - Your recommendation (with reasoning)
451
+ - "Custom" option for user-provided solutions
452
+ - "Skip" option for comments the user wants to defer
453
+
454
+ ### 4. Implement Cleanly
455
+
456
+ - Read files before editing (required by Edit tool)
457
+ - Make targeted changes (don't refactor unrelated code)
458
+ - Verify changes work (run linting, type checking if applicable)
459
+ - Track all modified files for the summary
460
+ - Show progress and results after each implementation
461
+
462
+ ### 5. Quality Checks
463
+
464
+ Before offering to commit:
465
+
466
+ - Run code formatting (`npx nx format:write --uncommitted`)
467
+ - Run markdown linting (if .md files changed)
468
+ - Run affected linting (if applicable)
469
+ - Show git status for user review
470
+ - Handle errors gracefully
471
+
472
+ ### 6. Single Commit
473
+
474
+ Create one well-formatted commit for all PR comment changes:
475
+
476
+ - Clear commit message following conventional commits
477
+ - Summarize what was addressed
478
+ - Include PR URL for reference
479
+ - List number of comments addressed and files changed
480
+
481
+ ## Example Workflow
482
+
483
+ ````
484
+ /work-through-pr-comments 154
485
+
486
+ 📋 Analyzing PR #154 in Uniswap/ai-toolkit...
487
+
488
+ **PR Title**: feat(claude): add local claude code workflows...
489
+ **Author**: wkoutre
490
+ **State**: open
491
+ **URL**: https://github.com/Uniswap/ai-toolkit/pull/154
492
+
493
+ Found 3 comment(s) to address
494
+
495
+ ================================================================================
496
+ 📝 Comment 1/3
497
+ ================================================================================
498
+
499
+ **Author**: Melvillian
500
+ **Type**: Inline code comment
501
+ **Location**: `scripts/lefthook/lint-markdown.sh:14`
502
+ **Comment**:
503
+ > security-nit: pin to `npx markdownlint-cli2@v1.18.1 --fix`
504
+
505
+ **Current Code Context** (`scripts/lefthook/lint-markdown.sh`):
506
+
507
+ ```text
508
+ 9 echo "ℹ️ No markdown files to lint"
509
+ 10 exit 0
510
+ 11 fi
511
+ 12
512
+ 13 # Lint and fix markdown files (uses .markdownlint-cli2.jsonc config)
513
+ → 14 echo "$STAGED_MD_FILES" | xargs npx markdownlint-cli2 --fix
514
+ 15 MD_EXIT_CODE=$?
515
+ 16
516
+ 17 # Re-stage any auto-fixed files
517
+ 18 echo "$STAGED_MD_FILES" | xargs git add
518
+ ````
519
+
520
+ ## 🤔 My Analysis
521
+
522
+ The reviewer is raising a security concern about using `npx` without version
523
+ pinning. When `npx` fetches a package without a specified version, it could
524
+ potentially fetch a compromised or incompatible version from npm.
525
+
526
+ However, I notice the reviewer suggested version `v1.18.1`, but your package.json
527
+ shows `markdownlint-cli2: ^0.18.1`. This appears to be a typo.
528
+
529
+ The core concern is valid: unpinned npm package fetching can be a security risk.
530
+
531
+ ## 💡 Suggested Solutions
532
+
533
+ **Option A**: Pin to exact version in script (`@0.18.1`)
534
+
535
+ - ✅ Pros: Clear version in script, reproducible builds
536
+ - ✅ Pros: Matches your package.json version
537
+ - ❌ Cons: Version duplication between package.json and script
538
+ - ❌ Cons: Need to update in two places when upgrading
539
+
540
+ **Option B**: Use locally installed version (`npm exec`)
541
+
542
+ - ✅ Pros: Single source of truth (package.json)
543
+ - ✅ Pros: More secure - uses locked version from package-lock.json
544
+ - ✅ Pros: No version string duplication
545
+ - ❌ Cons: Requires `npm install` to have been run first
546
+
547
+ **Option C**: Clarify version with reviewer first
548
+
549
+ - ✅ Pros: Ensures we understand their intent
550
+ - ❌ Cons: Delays implementation
551
+
552
+ **💭 My Recommendation**: Option B - Use `npm exec markdownlint-cli2` instead
553
+ of `npx markdownlint-cli2`. This addresses the security concern while maintaining
554
+ a single source of truth in package.json, and it's the most maintainable approach.
555
+
556
+ How would you like to address this comment from Melvillian?
557
+ ○ Option A
558
+ ● Option B
559
+ ○ Option C
560
+ ○ Custom
561
+ ○ Skip
562
+
563
+ ## 🔧 Implementing: Option B
564
+
565
+ Updating scripts/lefthook/lint-markdown.sh...
566
+ ✅ Changes implemented
567
+
568
+ [Continues with comments 2 and 3...]
569
+
570
+ ## 📊 Summary of Changes
571
+
572
+ Total comments processed: 3
573
+ Comments addressed: 3
574
+ Comments skipped: 0
575
+ Files modified: 7
576
+
577
+ **Files changed:**
578
+
579
+ - scripts/lefthook/lint-markdown.sh
580
+ - CLAUDE.md
581
+ - .claude/commands/aggregate-people-team-faqs.md
582
+ - .claude/agents/slack-fetcher.md
583
+ - .claude/agents/slack-analyzer.md
584
+ - .claude/agents/question-extractor.md
585
+ - .claude/agents/deduplicator.md
586
+
587
+ 🔍 Running code quality checks...
588
+ ✅ Code formatting complete
589
+ ✅ Markdown linting complete
590
+
591
+ **Git status:**
592
+
593
+ ```
594
+
595
+ M .claude/agents/deduplicator.md
596
+ M .claude/agents/question-extractor.md
597
+ M .claude/agents/slack-analyzer.md
598
+ M .claude/agents/slack-fetcher.md
599
+ R .claude/commands/aggregate-faqs.md -> .claude/commands/aggregate-people-team-faqs.md
600
+ M CLAUDE.md
601
+ M scripts/lefthook/lint-markdown.sh
602
+
603
+ ```
604
+
605
+ Would you like to commit these changes?
606
+ ● Yes
607
+ ○ No
608
+
609
+ ✅ Changes committed successfully!
610
+ c3b5993 fix: address PR #154 review comments
611
+
612
+ ````
613
+
614
+ ## Error Handling
615
+
616
+ ### No Comments Found
617
+
618
+ ```typescript
619
+ if (allComments.length === 0) {
620
+ console.log('✅ No comments to address! This PR has no review comments.');
621
+ return;
622
+ }
623
+ ````
624
+
625
+ ### Invalid PR Number
626
+
627
+ ```typescript
628
+ try {
629
+ const prDetails = await mcp__github__get_pull_request({ owner, repo, pull_number: prNumber });
630
+ } catch (error) {
631
+ console.log(`❌ Could not find PR #${prNumber} in ${owner}/${repo}`);
632
+ console.log(`Error: ${error.message}`);
633
+ console.log('\nPlease verify:');
634
+ console.log(' - The PR number is correct');
635
+ console.log(' - You have access to this repository');
636
+ console.log(' - The repository name is correct');
637
+ return;
638
+ }
639
+ ```
640
+
641
+ ### File Read Failures
642
+
643
+ ```typescript
644
+ try {
645
+ const fullPath = `${process.cwd()}/${comment.path}`;
646
+ fileContext = await Read(fullPath);
647
+ } catch (error) {
648
+ console.log(`⚠️ Could not read file: ${comment.path}`);
649
+ console.log('Proceeding without file context...\n');
650
+ // Continue - we can still analyze the comment without file context
651
+ }
652
+ ```
653
+
654
+ ### Git Operation Failures
655
+
656
+ ```typescript
657
+ try {
658
+ await Bash('git add -A');
659
+ await Bash(`git commit -m "${commitMessage}"`);
660
+ console.log('✅ Changes committed successfully!');
661
+ } catch (error) {
662
+ console.log('❌ Failed to create commit');
663
+ console.log(`Error: ${error.message}`);
664
+ console.log('\nYour changes have been made but not committed.');
665
+ console.log('Please review the changes and commit manually with:');
666
+ console.log(' git add -A');
667
+ console.log(' git commit -m "fix: address PR review comments"');
668
+ }
669
+ ```
670
+
671
+ ### Repository Detection Failures
672
+
673
+ ```typescript
674
+ try {
675
+ const remoteUrl = await Bash('git config --get remote.origin.url');
676
+ const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
677
+ if (!match) throw new Error('Invalid GitHub URL format');
678
+ [, owner, repo] = match;
679
+ } catch (error) {
680
+ console.log('❌ Could not detect repository from git remote');
681
+ console.log('Please specify repository explicitly:');
682
+ console.log(' /work-through-pr-comments <owner/repo> <pr-number>');
683
+ console.log('\nExample:');
684
+ console.log(' /work-through-pr-comments Uniswap/ai-toolkit 154');
685
+ return;
686
+ }
687
+ ```
688
+
689
+ ## Best Practices
690
+
691
+ ### 1. Always Read Files Before Editing
692
+
693
+ The Edit tool requires files to be read first:
694
+
695
+ ```typescript
696
+ // ✅ Correct
697
+ const content = await Read(filePath);
698
+ await Edit({ file_path: filePath, old_string: '...', new_string: '...' });
699
+
700
+ // ❌ Wrong - will fail with error
701
+ await Edit({ file_path: filePath, old_string: '...', new_string: '...' });
702
+ ```
703
+
704
+ ### 2. Track All Modified Files
705
+
706
+ Maintain a Set to avoid duplicates:
707
+
708
+ ```typescript
709
+ const filesToChange = new Set<string>();
710
+
711
+ // After each file modification
712
+ filesToChange.add(fullPath);
713
+
714
+ // Later, get count and list
715
+ console.log(`Modified ${filesToChange.size} file(s)`);
716
+ filesToChange.forEach((file) => console.log(` - ${file}`));
717
+ ```
718
+
719
+ ### 3. Show Progress Clearly
720
+
721
+ Use clear visual separators and progress indicators:
722
+
723
+ ```typescript
724
+ console.log(`\n${'='.repeat(80)}`);
725
+ console.log(`📝 Comment ${commentNum}/${allComments.length}`);
726
+ console.log(`${'='.repeat(80)}\n`);
727
+
728
+ // After implementation
729
+ console.log('✅ Changes implemented\n');
730
+ ```
731
+
732
+ ### 4. Provide Rich Context
733
+
734
+ Show the code being discussed:
735
+
736
+ ```typescript
737
+ // Highlight the specific line mentioned in the comment
738
+ for (let i = startLine; i < endLine; i++) {
739
+ const lineNum = i + 1;
740
+ const prefix = lineNum === targetLine ? '→ ' : ' ';
741
+ console.log(`${prefix}${String(lineNum).padStart(4)} ${lines[i]}`);
742
+ }
743
+ ```
744
+
745
+ ### 5. Handle User's Custom Solutions
746
+
747
+ Be ready to implement what the user describes:
748
+
749
+ ```typescript
750
+ if (selectedOption === 'Custom') {
751
+ console.log("📝 Please describe what you'd like me to do for this comment.\n");
752
+ // Wait for user instructions in next message
753
+ // Then implement exactly what they ask for
754
+ continue; // Will resume after user provides guidance
755
+ }
756
+ ```
757
+
758
+ ### 6. Create Well-Formatted Commits
759
+
760
+ Use heredoc for multi-line commit messages:
761
+
762
+ ```typescript
763
+ const commitMessage = `fix: address PR #${prNumber} review comments
764
+
765
+ Addressed ${addressedCount} review comment(s)
766
+ Modified ${filesToChange.size} file(s)
767
+
768
+ PR: ${prDetails.html_url}`;
769
+
770
+ await Bash(`git commit -m "$(cat <<'EOF'
771
+ ${commitMessage}
772
+ EOF
773
+ )"`);
774
+ ```
775
+
776
+ ## Advanced Usage
777
+
778
+ ### Working with Multiple Reviewers
779
+
780
+ Track comments by reviewer:
781
+
782
+ ```typescript
783
+ const commentsByReviewer = allComments.reduce((acc, comment) => {
784
+ if (!acc[comment.author]) acc[comment.author] = [];
785
+ acc[comment.author].push(comment);
786
+ return acc;
787
+ }, {});
788
+
789
+ console.log('\n**Comments by reviewer:**');
790
+ Object.entries(commentsByReviewer).forEach(([author, comments]) => {
791
+ console.log(` - ${author}: ${comments.length} comment(s)`);
792
+ });
793
+ ```
794
+
795
+ ### Categorizing Comments
796
+
797
+ Group by type or severity:
798
+
799
+ ```typescript
800
+ function categorizeComment(comment: Comment): string {
801
+ const body = comment.body.toLowerCase();
802
+
803
+ if (body.includes('security') || body.includes('vulnerability')) return 'security';
804
+ if (body.includes('nit') || body.includes('nitpick')) return 'style';
805
+ if (body.includes('blocker') || body.includes('must')) return 'blocker';
806
+ if (body.includes('suggestion') || body.includes('consider')) return 'suggestion';
807
+
808
+ return 'general';
809
+ }
810
+
811
+ const commentsByCategory = allComments.reduce((acc, comment) => {
812
+ const category = categorizeComment(comment);
813
+ if (!acc[category]) acc[category] = [];
814
+ acc[category].push(comment);
815
+ return acc;
816
+ }, {});
817
+ ```
818
+
819
+ ### Batch Similar Changes
820
+
821
+ If multiple comments ask for the same type of change:
822
+
823
+ ```typescript
824
+ // Group similar comments
825
+ const similarComments = allComments.filter(
826
+ (c) => c.body.toLowerCase().includes('pin version') || c.body.toLowerCase().includes('add type')
827
+ );
828
+
829
+ if (similarComments.length > 1) {
830
+ console.log(`\n**Note**: Found ${similarComments.length} similar comments about [topic].`);
831
+ console.log('Would you like to address them together?\n');
832
+ }
833
+ ```
834
+
835
+ ## Integration with Other Tools
836
+
837
+ ### Using GitHub CLI
838
+
839
+ If `gh` CLI is available, use it for richer interactions:
840
+
841
+ ```typescript
842
+ const hasGH = await Bash('which gh')
843
+ .then(() => true)
844
+ .catch(() => false);
845
+
846
+ if (hasGH) {
847
+ // Can use gh for more features
848
+ await Bash(`gh pr view ${prNumber}`);
849
+ await Bash(`gh pr checks ${prNumber}`);
850
+ }
851
+ ```
852
+
853
+ ### Running Project-Specific Checks
854
+
855
+ After making changes, run project-specific validations:
856
+
857
+ ```typescript
858
+ // Run affected tests
859
+ await Bash('npx nx affected --target=test --base=HEAD~1');
860
+
861
+ // Run affected linting
862
+ await Bash('npx nx affected --target=lint --base=HEAD~1');
863
+
864
+ // Run type checking
865
+ await Bash('npx nx affected --target=typecheck --base=HEAD~1');
866
+ ```
867
+
868
+ ## Notes
869
+
870
+ - This command emphasizes **conversation and collaboration**
871
+ - Always wait for user decisions before making changes
872
+ - Provide clear, honest analysis with pros/cons
873
+ - Support user's custom solutions
874
+ - Track all changes for comprehensive summary
875
+ - Run quality checks before offering to commit
876
+ - Create a single, well-formatted commit
877
+ - Handle errors gracefully and provide helpful guidance
878
+ - The "Custom" option lets users direct you specifically
879
+ - The "Skip" option allows deferring comments without blocking progress