n2b 0.5.1 โ†’ 2.0.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.
@@ -0,0 +1,397 @@
1
+ #!/bin/bash
2
+
3
+ # Branch Audit Script - Safely analyze all branches for unmerged code
4
+ # Author: Augment Agent
5
+ # Purpose: Find valuable code in branches without touching main
6
+
7
+ set -e # Exit on any error
8
+
9
+ # Colors for output
10
+ RED='\033[0;31m'
11
+ GREEN='\033[0;32m'
12
+ YELLOW='\033[1;33m'
13
+ BLUE='\033[0;34m'
14
+ NC='\033[0m' # No Color
15
+
16
+ # Configuration
17
+ MAIN_BRANCH="main"
18
+ REPORT_FILE="branch_audit_report_$(date +%Y-%m-%d_%H-%M-%S).md"
19
+ TEMP_DIR="temp_branch_audit_$$"
20
+ WORKTREE_DIR="worktree_audit_$$"
21
+
22
+ # Arrays to store results
23
+ declare -a MERGEABLE_BRANCHES=()
24
+ declare -a CONFLICTED_BRANCHES=()
25
+ declare -a EMPTY_BRANCHES=()
26
+ declare -a ERROR_BRANCHES=()
27
+
28
+ # Cleanup function
29
+ cleanup() {
30
+ echo -e "${YELLOW}๐Ÿงน Cleaning up temporary files...${NC}"
31
+ if [ -d "$TEMP_DIR" ]; then
32
+ rm -rf "$TEMP_DIR"
33
+ fi
34
+ if [ -d "$WORKTREE_DIR" ]; then
35
+ git worktree remove "$WORKTREE_DIR" 2>/dev/null || true
36
+ rm -rf "$WORKTREE_DIR" 2>/dev/null || true
37
+ fi
38
+ }
39
+
40
+ # Set up cleanup trap
41
+ trap cleanup EXIT
42
+
43
+ # Function to log with timestamp
44
+ log() {
45
+ echo -e "${BLUE}[$(date '+%H:%M:%S')]${NC} $1"
46
+ }
47
+
48
+ # Function to check if branch exists
49
+ branch_exists() {
50
+ git show-ref --verify --quiet "refs/heads/$1" 2>/dev/null
51
+ }
52
+
53
+ # Function to check if remote branch exists
54
+ remote_branch_exists() {
55
+ git show-ref --verify --quiet "refs/remotes/$1" 2>/dev/null
56
+ }
57
+
58
+ # Function to get unique commits in branch vs main
59
+ get_unique_commits() {
60
+ local branch="$1"
61
+ git log --oneline "${MAIN_BRANCH}..${branch}" 2>/dev/null || echo ""
62
+ }
63
+
64
+ # Function to get file changes summary
65
+ get_file_changes() {
66
+ local branch="$1"
67
+ git diff --name-status "${MAIN_BRANCH}...${branch}" 2>/dev/null || echo ""
68
+ }
69
+
70
+ # Function to check if fast-forward merge is possible
71
+ can_fast_forward() {
72
+ local branch="$1"
73
+ local merge_base=$(git merge-base "$MAIN_BRANCH" "$branch" 2>/dev/null || echo "")
74
+ local main_commit=$(git rev-parse "$MAIN_BRANCH" 2>/dev/null || echo "")
75
+
76
+ if [ -z "$merge_base" ] || [ -z "$main_commit" ]; then
77
+ return 1
78
+ fi
79
+
80
+ # If merge base equals main commit, then it's a fast-forward
81
+ [ "$merge_base" = "$main_commit" ]
82
+ }
83
+
84
+ # Function to analyze a single branch
85
+ analyze_branch() {
86
+ local branch="$1"
87
+ local branch_type="$2" # "local" or "remote"
88
+
89
+ log "Analyzing ${branch_type} branch: $branch"
90
+
91
+ # Skip main branch and its variants
92
+ if [[ "$branch" =~ ^(main|master|origin/main|origin/master)$ ]]; then
93
+ log "Skipping main branch: $branch"
94
+ return
95
+ fi
96
+
97
+ # For remote branches, create local tracking branch if needed
98
+ local local_branch="$branch"
99
+ if [ "$branch_type" = "remote" ]; then
100
+ local_branch="${branch#origin/}"
101
+ if ! branch_exists "$local_branch"; then
102
+ log "Creating local tracking branch for $branch"
103
+ git branch "$local_branch" "$branch" 2>/dev/null || {
104
+ ERROR_BRANCHES+=("$branch (failed to create local branch)")
105
+ return
106
+ }
107
+ fi
108
+ fi
109
+
110
+ # Get unique commits
111
+ local unique_commits=$(get_unique_commits "$local_branch")
112
+ local commit_count=0
113
+ if [ -n "$unique_commits" ] && [ "$unique_commits" != "" ]; then
114
+ commit_count=$(echo "$unique_commits" | wc -l | tr -d ' ')
115
+ fi
116
+
117
+ if [ "$commit_count" -eq 0 ]; then
118
+ log "Branch $branch has no unique commits"
119
+ EMPTY_BRANCHES+=("$branch")
120
+ return
121
+ fi
122
+
123
+ # Check if fast-forward is possible
124
+ if can_fast_forward "$local_branch"; then
125
+ log "โœ… Branch $branch can be fast-forward merged ($commit_count commits)"
126
+ MERGEABLE_BRANCHES+=("$branch|$commit_count|$unique_commits")
127
+ else
128
+ log "โš ๏ธ Branch $branch has conflicts or diverged ($commit_count commits)"
129
+ CONFLICTED_BRANCHES+=("$branch|$commit_count|$unique_commits")
130
+ fi
131
+ }
132
+
133
+ # Main execution starts here
134
+ main() {
135
+ echo -e "${GREEN}๐Ÿ” Starting Branch Audit Analysis${NC}"
136
+ echo -e "${BLUE}Repository: $(pwd)${NC}"
137
+ echo -e "${BLUE}Main branch: $MAIN_BRANCH${NC}"
138
+ echo -e "${BLUE}Report file: $REPORT_FILE${NC}"
139
+ echo ""
140
+
141
+ # Verify we're in a git repository
142
+ if ! git rev-parse --git-dir > /dev/null 2>&1; then
143
+ echo -e "${RED}โŒ Error: Not in a git repository${NC}"
144
+ exit 1
145
+ fi
146
+
147
+ # Verify main branch exists
148
+ if ! git show-ref --verify --quiet "refs/heads/$MAIN_BRANCH"; then
149
+ echo -e "${RED}โŒ Error: Main branch '$MAIN_BRANCH' not found${NC}"
150
+ exit 1
151
+ fi
152
+
153
+ # Fetch latest from remote
154
+ log "Fetching latest changes from remote..."
155
+ git fetch --all --prune
156
+
157
+ # Create temporary directory
158
+ mkdir -p "$TEMP_DIR"
159
+
160
+ # Get current branch to restore later
161
+ CURRENT_BRANCH=$(git branch --show-current)
162
+
163
+ # Ensure we're on main branch for analysis
164
+ log "Switching to main branch for analysis..."
165
+ git checkout "$MAIN_BRANCH" >/dev/null 2>&1
166
+
167
+ # Get all local branches
168
+ log "Analyzing local branches..."
169
+ while IFS= read -r branch; do
170
+ branch=$(echo "$branch" | sed 's/^[* ] //' | xargs)
171
+ analyze_branch "$branch" "local"
172
+ done < <(git branch | grep -v "^\*.*$MAIN_BRANCH$")
173
+
174
+ # Get all remote branches
175
+ log "Analyzing remote branches..."
176
+ while IFS= read -r branch; do
177
+ branch=$(echo "$branch" | sed 's|^ remotes/||' | xargs)
178
+ # Skip if we already have this as a local branch
179
+ local_name="${branch#origin/}"
180
+ if ! branch_exists "$local_name"; then
181
+ analyze_branch "$branch" "remote"
182
+ fi
183
+ done < <(git branch -r | grep -v "HEAD\|$MAIN_BRANCH$")
184
+
185
+ # Restore original branch
186
+ if [ -n "$CURRENT_BRANCH" ] && [ "$CURRENT_BRANCH" != "$MAIN_BRANCH" ]; then
187
+ log "Restoring original branch: $CURRENT_BRANCH"
188
+ git checkout "$CURRENT_BRANCH" >/dev/null 2>&1
189
+ fi
190
+
191
+ # Generate report
192
+ generate_report
193
+
194
+ echo ""
195
+ echo -e "${GREEN}โœ… Branch audit completed!${NC}"
196
+ echo -e "${BLUE}๐Ÿ“„ Report saved to: $REPORT_FILE${NC}"
197
+ }
198
+
199
+ # Function to generate detailed report
200
+ generate_report() {
201
+ log "Generating detailed report..."
202
+
203
+ cat > "$REPORT_FILE" << EOF
204
+ # Branch Audit Report
205
+ **Generated:** $(date)
206
+ **Repository:** $(pwd)
207
+ **Main Branch:** $MAIN_BRANCH
208
+
209
+ ## ๐Ÿ“Š Executive Summary
210
+
211
+ - **Mergeable Branches (Fast-Forward):** ${#MERGEABLE_BRANCHES[@]}
212
+ - **Conflicted Branches (Manual Review):** ${#CONFLICTED_BRANCHES[@]}
213
+ - **Empty Branches (No Changes):** ${#EMPTY_BRANCHES[@]}
214
+ - **Error Branches:** ${#ERROR_BRANCHES[@]}
215
+ - **Total Analyzed:** $((${#MERGEABLE_BRANCHES[@]} + ${#CONFLICTED_BRANCHES[@]} + ${#EMPTY_BRANCHES[@]} + ${#ERROR_BRANCHES[@]}))
216
+
217
+ ---
218
+
219
+ EOF
220
+
221
+ # Mergeable branches section
222
+ if [ ${#MERGEABLE_BRANCHES[@]} -gt 0 ]; then
223
+ cat >> "$REPORT_FILE" << EOF
224
+ ## โœ… Mergeable Branches (Fast-Forward Possible)
225
+
226
+ These branches can be safely merged into main without conflicts:
227
+
228
+ EOF
229
+ for branch_info in "${MERGEABLE_BRANCHES[@]}"; do
230
+ IFS='|' read -r branch commit_count commits <<< "$branch_info"
231
+ cat >> "$REPORT_FILE" << EOF
232
+ ### ๐ŸŒฟ \`$branch\`
233
+ - **Unique Commits:** $commit_count
234
+ - **File Changes:** $(get_file_changes "$branch" | wc -l) files
235
+ - **Recent Commits:**
236
+ \`\`\`
237
+ $commits
238
+ \`\`\`
239
+ - **Files Modified:**
240
+ \`\`\`
241
+ $(get_file_changes "$branch" | head -10)
242
+ $([ $(get_file_changes "$branch" | wc -l) -gt 10 ] && echo "... and $(($(get_file_changes "$branch" | wc -l) - 10)) more files")
243
+ \`\`\`
244
+
245
+ **Recommendation:** โœ… Safe to merge with \`git merge $branch\`
246
+
247
+ ---
248
+
249
+ EOF
250
+ done
251
+ fi
252
+
253
+ # Conflicted branches section
254
+ if [ ${#CONFLICTED_BRANCHES[@]} -gt 0 ]; then
255
+ cat >> "$REPORT_FILE" << EOF
256
+ ## โš ๏ธ Conflicted Branches (Manual Review Required)
257
+
258
+ These branches have diverged from main and may have conflicts:
259
+
260
+ EOF
261
+ for branch_info in "${CONFLICTED_BRANCHES[@]}"; do
262
+ IFS='|' read -r branch commit_count commits <<< "$branch_info"
263
+ cat >> "$REPORT_FILE" << EOF
264
+ ### ๐Ÿ”€ \`$branch\`
265
+ - **Unique Commits:** $commit_count
266
+ - **File Changes:** $(get_file_changes "$branch" | wc -l) files
267
+ - **Recent Commits:**
268
+ \`\`\`
269
+ $commits
270
+ \`\`\`
271
+ - **Files Modified:**
272
+ \`\`\`
273
+ $(get_file_changes "$branch" | head -10)
274
+ $([ $(get_file_changes "$branch" | wc -l) -gt 10 ] && echo "... and $(($(get_file_changes "$branch" | wc -l) - 10)) more files")
275
+ \`\`\`
276
+
277
+ **Recommendation:** โš ๏ธ Manual review required - check for conflicts before merging
278
+
279
+ ---
280
+
281
+ EOF
282
+ done
283
+ fi
284
+
285
+ # Empty branches section
286
+ if [ ${#EMPTY_BRANCHES[@]} -gt 0 ]; then
287
+ cat >> "$REPORT_FILE" << EOF
288
+ ## ๐Ÿ—‘๏ธ Empty Branches (Safe to Delete)
289
+
290
+ These branches have no unique commits compared to main:
291
+
292
+ EOF
293
+ for branch in "${EMPTY_BRANCHES[@]}"; do
294
+ cat >> "$REPORT_FILE" << EOF
295
+ - \`$branch\` - No unique changes
296
+ EOF
297
+ done
298
+ cat >> "$REPORT_FILE" << EOF
299
+
300
+ **Cleanup Commands:**
301
+ \`\`\`bash
302
+ # Delete local empty branches
303
+ EOF
304
+ for branch in "${EMPTY_BRANCHES[@]}"; do
305
+ if [[ ! "$branch" =~ ^origin/ ]]; then
306
+ echo "git branch -d $branch" >> "$REPORT_FILE"
307
+ fi
308
+ done
309
+ cat >> "$REPORT_FILE" << EOF
310
+
311
+ # Delete remote empty branches (be careful!)
312
+ EOF
313
+ for branch in "${EMPTY_BRANCHES[@]}"; do
314
+ if [[ "$branch" =~ ^origin/ ]]; then
315
+ remote_branch="${branch#origin/}"
316
+ echo "git push origin --delete $remote_branch" >> "$REPORT_FILE"
317
+ fi
318
+ done
319
+ cat >> "$REPORT_FILE" << EOF
320
+ \`\`\`
321
+
322
+ ---
323
+
324
+ EOF
325
+ fi
326
+
327
+ # Error branches section
328
+ if [ ${#ERROR_BRANCHES[@]} -gt 0 ]; then
329
+ cat >> "$REPORT_FILE" << EOF
330
+ ## โŒ Error Branches
331
+
332
+ These branches encountered errors during analysis:
333
+
334
+ EOF
335
+ for branch in "${ERROR_BRANCHES[@]}"; do
336
+ cat >> "$REPORT_FILE" << EOF
337
+ - \`$branch\`
338
+ EOF
339
+ done
340
+ cat >> "$REPORT_FILE" << EOF
341
+
342
+ **Recommendation:** Manual investigation required
343
+
344
+ ---
345
+
346
+ EOF
347
+ fi
348
+
349
+ # Summary and recommendations
350
+ cat >> "$REPORT_FILE" << EOF
351
+ ## ๐ŸŽฏ Recommended Actions
352
+
353
+ ### Immediate Actions (Safe)
354
+ 1. **Merge fast-forward branches:** ${#MERGEABLE_BRANCHES[@]} branches ready
355
+ 2. **Delete empty branches:** ${#EMPTY_BRANCHES[@]} branches can be removed
356
+ 3. **Review conflicted branches:** ${#CONFLICTED_BRANCHES[@]} branches need manual review
357
+
358
+ ### Cleanup Script
359
+ \`\`\`bash
360
+ # Run this script to clean up empty branches
361
+ EOF
362
+
363
+ # Generate cleanup commands for empty branches
364
+ for branch in "${EMPTY_BRANCHES[@]}"; do
365
+ if [[ ! "$branch" =~ ^origin/ ]]; then
366
+ echo "git branch -d $branch" >> "$REPORT_FILE"
367
+ fi
368
+ done
369
+
370
+ cat >> "$REPORT_FILE" << EOF
371
+ \`\`\`
372
+
373
+ ### Merge Script for Fast-Forward Branches
374
+ \`\`\`bash
375
+ # Run these commands to merge ready branches
376
+ EOF
377
+
378
+ # Generate merge commands for mergeable branches
379
+ for branch_info in "${MERGEABLE_BRANCHES[@]}"; do
380
+ IFS='|' read -r branch commit_count commits <<< "$branch_info"
381
+ if [[ ! "$branch" =~ ^origin/ ]]; then
382
+ echo "git merge $branch # $commit_count commits" >> "$REPORT_FILE"
383
+ fi
384
+ done
385
+
386
+ cat >> "$REPORT_FILE" << EOF
387
+ \`\`\`
388
+
389
+ ---
390
+ *Report generated by branch-audit.sh on $(date)*
391
+ EOF
392
+
393
+ log "Report generated: $REPORT_FILE"
394
+ }
395
+
396
+ # Run main function
397
+ main "$@"
data/bin/n2b-diff ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'n2b'
4
+
5
+ N2B::MergeCLI.run(ARGV)
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require_relative '../lib/n2b/github_client'
5
+
6
+ config_file = File.expand_path('~/.n2b/config.yml')
7
+ unless File.exist?(config_file)
8
+ puts "โŒ Config file not found: #{config_file}"
9
+ puts "Please run 'n2b --advanced-config' to set up GitHub integration first."
10
+ exit 1
11
+ end
12
+
13
+ config = YAML.load_file(config_file)
14
+
15
+ begin
16
+ client = N2B::GitHubClient.new(config)
17
+ rescue => e
18
+ puts "โŒ #{e.message}"
19
+ exit 1
20
+ end
21
+
22
+ client.test_connection
data/bin/n2b-test-jira CHANGED
@@ -124,11 +124,11 @@ class JiraConnectionTester
124
124
  puts "3๏ธโƒฃ Testing required permissions..."
125
125
 
126
126
  begin
127
- # Test 1: Project access (Browse Projects permission)
127
+ # Test 1: Project access (read:project:jira scope)
128
128
  response = make_api_request('GET', '/project')
129
129
 
130
130
  if response.is_a?(Array) && response.length > 0
131
- puts " โœ… Browse Projects: Can access #{response.length} projects"
131
+ puts " โœ… Project Access (read:project:jira): Can access #{response.length} projects"
132
132
  response.first(3).each do |project|
133
133
  puts " - #{project['key']}: #{project['name']}"
134
134
  end
@@ -141,30 +141,30 @@ class JiraConnectionTester
141
141
  begin
142
142
  issues_response = make_api_request('GET', "/search?jql=project=#{test_project['key']}&maxResults=1")
143
143
  if issues_response['issues'] && issues_response['issues'].length > 0
144
- puts " โœ… Browse Issues: Can access issues in #{test_project['key']}"
144
+ puts " โœ… Issue Access (read:issue:jira): Can access issues in #{test_project['key']}"
145
145
 
146
146
  # Test 3: Comment access (if we found an issue)
147
147
  test_issue = issues_response['issues'].first
148
148
  begin
149
149
  comments_response = make_api_request('GET', "/issue/#{test_issue['key']}/comment")
150
- puts " โœ… View Comments: Can access comments on #{test_issue['key']}"
150
+ puts " โœ… Comment Access (read:comment:jira): Can access comments on #{test_issue['key']}"
151
151
  rescue => e
152
- puts " โŒ View Comments: Cannot access comments (#{e.message})"
152
+ puts " โŒ Comment Access (read:comment:jira): Cannot access comments (#{e.message})"
153
153
  end
154
154
 
155
155
  # Test 4: Add comment permission (we won't actually add, just check the endpoint)
156
- puts " โ„น๏ธ Add Comments: Will be tested when actually posting comments"
156
+ puts " โ„น๏ธ Comment Creation (write:comment:jira): Will be tested when actually posting comments"
157
157
  else
158
158
  puts " โš ๏ธ No issues found in #{test_project['key']} to test comment permissions"
159
159
  end
160
160
  rescue => e
161
- puts " โŒ Browse Issues: Cannot search issues (#{e.message})"
161
+ puts " โŒ Issue Access (read:issue:jira): Cannot search issues (#{e.message})"
162
162
  end
163
163
 
164
164
  puts
165
165
  else
166
- puts " โŒ Browse Projects: No projects accessible"
167
- puts " Your API token needs 'Browse Projects' permission"
166
+ puts " โŒ Project Access: No projects accessible"
167
+ puts " Your API token needs 'read:project:jira' scope"
168
168
  puts
169
169
  end
170
170
  rescue => e
@@ -173,11 +173,14 @@ class JiraConnectionTester
173
173
  puts
174
174
  end
175
175
 
176
- puts " ๐Ÿ“‹ Required Jira Permissions for N2B:"
177
- puts " โ€ข Browse Projects - to access project list"
178
- puts " โ€ข Browse Issues - to read ticket details"
179
- puts " โ€ข View Comments - to read ticket comments"
180
- puts " โ€ข Add Comments - to post analysis results"
176
+ puts " ๐Ÿ“‹ Required Jira API Scopes for N2B:"
177
+ puts " โ€ข read:project:jira - to access project list"
178
+ puts " โ€ข read:issue:jira - to read ticket details"
179
+ puts " โ€ข read:comment:jira - to read ticket comments"
180
+ puts " โ€ข write:comment:jira - to post analysis results"
181
+ puts
182
+ puts " ๐Ÿ“ Legacy Permission Names (older Jira versions):"
183
+ puts " โ€ข Browse Projects, Browse Issues, View Comments, Add Comments"
181
184
  puts
182
185
  end
183
186