@bragduck/cli 1.0.3 → 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.
- package/README.md +125 -26
- package/dist/bin/bragduck.js +242 -24
- package/dist/bin/bragduck.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# Bragduck CLI
|
|
2
2
|
|
|
3
|
-
> Transform your
|
|
3
|
+
> Transform your GitHub Pull Requests into polished achievements with AI-powered refinement
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@bragduck/cli)
|
|
6
6
|
[](https://nodejs.org)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
[](https://github.com/medhatdawoud/bragduck-cli)
|
|
9
9
|
|
|
10
10
|
## Overview
|
|
11
11
|
|
|
12
|
-
Bragduck CLI is a powerful command-line tool that helps developers track and showcase their achievements by transforming
|
|
12
|
+
Bragduck CLI is a powerful command-line tool that helps developers track and showcase their achievements by transforming GitHub Pull Requests into polished "brags" using AI-powered refinement. Perfect for updating your portfolio, preparing for performance reviews, or simply keeping track of your accomplishments.
|
|
13
13
|
|
|
14
14
|
### Why Bragduck?
|
|
15
15
|
|
|
@@ -20,6 +20,33 @@ Bragduck CLI is a powerful command-line tool that helps developers track and sho
|
|
|
20
20
|
- 🚀 **Fast**: Optimized build size (69KB) for quick installation and execution
|
|
21
21
|
- 🌐 **Cross-Platform**: Works on macOS, Windows, and Linux
|
|
22
22
|
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
Before installing Bragduck CLI, you need:
|
|
26
|
+
|
|
27
|
+
1. **Node.js ≥18.0.0**
|
|
28
|
+
```bash
|
|
29
|
+
node --version # Should be v18.0.0 or higher
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
2. **GitHub CLI (gh)**
|
|
33
|
+
```bash
|
|
34
|
+
# macOS
|
|
35
|
+
brew install gh
|
|
36
|
+
|
|
37
|
+
# Windows
|
|
38
|
+
winget install --id GitHub.cli
|
|
39
|
+
|
|
40
|
+
# Linux
|
|
41
|
+
# See https://github.com/cli/cli#installation
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
3. **Authenticate with GitHub CLI**
|
|
45
|
+
```bash
|
|
46
|
+
gh auth login
|
|
47
|
+
```
|
|
48
|
+
Follow the prompts to authenticate with your GitHub account.
|
|
49
|
+
|
|
23
50
|
## Installation
|
|
24
51
|
|
|
25
52
|
```bash
|
|
@@ -30,25 +57,34 @@ npm install -g @bragduck/cli
|
|
|
30
57
|
|
|
31
58
|
### Quick Start
|
|
32
59
|
|
|
33
|
-
1. **Install
|
|
60
|
+
1. **Install GitHub CLI and authenticate:**
|
|
61
|
+
```bash
|
|
62
|
+
# Install GitHub CLI (if not already installed)
|
|
63
|
+
brew install gh # macOS
|
|
64
|
+
|
|
65
|
+
# Authenticate with GitHub
|
|
66
|
+
gh auth login
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
2. **Install Bragduck CLI globally:**
|
|
34
70
|
```bash
|
|
35
71
|
npm install -g @bragduck/cli
|
|
36
72
|
```
|
|
37
73
|
|
|
38
|
-
|
|
74
|
+
3. **Initialize and authenticate:**
|
|
39
75
|
```bash
|
|
40
76
|
bragduck init
|
|
41
77
|
```
|
|
42
78
|
This opens your browser for OAuth authentication. Once completed, your credentials are securely stored.
|
|
43
79
|
|
|
44
|
-
|
|
80
|
+
4. **Scan your merged PRs:**
|
|
45
81
|
```bash
|
|
46
|
-
cd /path/to/your/
|
|
82
|
+
cd /path/to/your/github/repo
|
|
47
83
|
bragduck scan
|
|
48
84
|
```
|
|
49
|
-
Select
|
|
85
|
+
Select PRs interactively, preview AI-refined brags, and create them.
|
|
50
86
|
|
|
51
|
-
|
|
87
|
+
5. **View your brags:**
|
|
52
88
|
```bash
|
|
53
89
|
bragduck list
|
|
54
90
|
```
|
|
@@ -65,16 +101,16 @@ bragduck init
|
|
|
65
101
|
|
|
66
102
|
#### `bragduck scan`
|
|
67
103
|
|
|
68
|
-
Scan
|
|
104
|
+
Scan merged GitHub Pull Requests and create brags with AI-powered refinement.
|
|
69
105
|
|
|
70
106
|
```bash
|
|
71
|
-
# Scan last 30 days (default)
|
|
107
|
+
# Scan last 30 days (default, your PRs only)
|
|
72
108
|
bragduck scan
|
|
73
109
|
|
|
74
110
|
# Scan last 60 days
|
|
75
111
|
bragduck scan --days 60
|
|
76
112
|
|
|
77
|
-
# Include all
|
|
113
|
+
# Include all PRs (not just yours)
|
|
78
114
|
bragduck scan --all
|
|
79
115
|
|
|
80
116
|
# Combine options
|
|
@@ -83,15 +119,28 @@ bragduck scan --days 90 --all
|
|
|
83
119
|
|
|
84
120
|
**Options:**
|
|
85
121
|
- `-d, --days <number>` - Number of days to scan (default: 30)
|
|
86
|
-
- `-a, --all` - Include all
|
|
122
|
+
- `-a, --all` - Include all PRs, not just current user's
|
|
87
123
|
|
|
88
124
|
**Workflow:**
|
|
89
|
-
1.
|
|
90
|
-
2. Fetches
|
|
91
|
-
3.
|
|
92
|
-
4.
|
|
93
|
-
5.
|
|
94
|
-
6.
|
|
125
|
+
1. Validates GitHub repository
|
|
126
|
+
2. Fetches merged PRs in timeframe (filtered by author unless --all)
|
|
127
|
+
3. Displays PRs in format: `#123 PR Title [stats]`
|
|
128
|
+
4. Interactive checkbox selection
|
|
129
|
+
5. AI refines selected PRs (using title + description)
|
|
130
|
+
6. Preview refined brags in table format
|
|
131
|
+
7. Confirm and create brags
|
|
132
|
+
|
|
133
|
+
**What gets scanned:**
|
|
134
|
+
- ✅ Merged Pull Requests only
|
|
135
|
+
- ✅ PR title and description used for context
|
|
136
|
+
- ✅ Aggregate PR statistics (files changed, insertions, deletions)
|
|
137
|
+
- ✅ Filtered by PR author (person who created the PR)
|
|
138
|
+
- ❌ Draft or closed-without-merge PRs excluded
|
|
139
|
+
- ❌ Individual commits not scanned
|
|
140
|
+
|
|
141
|
+
**Requirements:**
|
|
142
|
+
- Must be in a GitHub repository (GitLab/Bitbucket not supported)
|
|
143
|
+
- GitHub CLI must be installed and authenticated (`gh auth login`)
|
|
95
144
|
|
|
96
145
|
#### `bragduck list`
|
|
97
146
|
|
|
@@ -196,6 +245,7 @@ bragduck --help
|
|
|
196
245
|
- ✅ Windows (Compatible)
|
|
197
246
|
- ✅ Linux (Compatible)
|
|
198
247
|
- ✅ Node.js ≥18.0.0
|
|
248
|
+
- ✅ GitHub repositories only (GitLab/Bitbucket not supported)
|
|
199
249
|
|
|
200
250
|
## Configuration
|
|
201
251
|
|
|
@@ -241,17 +291,62 @@ git status # Verify it's a git repo
|
|
|
241
291
|
bragduck scan
|
|
242
292
|
```
|
|
243
293
|
|
|
244
|
-
###
|
|
294
|
+
### GitHub CLI Not Authenticated
|
|
245
295
|
|
|
246
|
-
**Problem**: "
|
|
296
|
+
**Problem**: "Not authenticated with GitHub" error
|
|
297
|
+
|
|
298
|
+
**Solution**:
|
|
299
|
+
```bash
|
|
300
|
+
# Authenticate with GitHub CLI
|
|
301
|
+
gh auth login
|
|
302
|
+
|
|
303
|
+
# Verify authentication
|
|
304
|
+
gh auth status
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Not a GitHub Repository
|
|
308
|
+
|
|
309
|
+
**Problem**: "This repository is not hosted on GitHub" error
|
|
310
|
+
|
|
311
|
+
**Solution**:
|
|
312
|
+
- Bragduck CLI currently only supports GitHub repositories
|
|
313
|
+
- Make sure you're in a repository with a GitHub remote:
|
|
314
|
+
```bash
|
|
315
|
+
git remote -v # Should show github.com URL
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### No Merged PRs Found
|
|
319
|
+
|
|
320
|
+
**Problem**: "No merged PRs found in the last X days"
|
|
247
321
|
|
|
248
322
|
**Solution**:
|
|
249
323
|
```bash
|
|
250
324
|
# Increase the scan days
|
|
251
325
|
bragduck scan --days 90
|
|
252
326
|
|
|
253
|
-
# Or include all
|
|
327
|
+
# Or include all PRs (not just yours)
|
|
254
328
|
bragduck scan --all
|
|
329
|
+
|
|
330
|
+
# Check if you have merged PRs on GitHub
|
|
331
|
+
gh pr list --state merged --limit 10
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### GitHub CLI Not Installed
|
|
335
|
+
|
|
336
|
+
**Problem**: "GitHub CLI (gh) is not installed" error
|
|
337
|
+
|
|
338
|
+
**Solution**:
|
|
339
|
+
```bash
|
|
340
|
+
# macOS
|
|
341
|
+
brew install gh
|
|
342
|
+
|
|
343
|
+
# Windows
|
|
344
|
+
winget install --id GitHub.cli
|
|
345
|
+
|
|
346
|
+
# Linux - see https://github.com/cli/cli#installation
|
|
347
|
+
|
|
348
|
+
# Verify installation
|
|
349
|
+
gh --version
|
|
255
350
|
```
|
|
256
351
|
|
|
257
352
|
### API Connection Issues
|
|
@@ -377,10 +472,10 @@ npx vitest
|
|
|
377
472
|
```
|
|
378
473
|
|
|
379
474
|
**Test Stats:**
|
|
380
|
-
- ✅
|
|
475
|
+
- ✅ 126 tests passing
|
|
381
476
|
- 📊 50% code coverage (focused on core logic)
|
|
382
|
-
- 🧪
|
|
383
|
-
- Services (Storage, Git, API)
|
|
477
|
+
- 🧪 8 test suites covering:
|
|
478
|
+
- Services (Storage, Git, API, GitHub)
|
|
384
479
|
- Commands (Config, List)
|
|
385
480
|
- Utilities (Version, Errors)
|
|
386
481
|
|
|
@@ -398,6 +493,7 @@ src/
|
|
|
398
493
|
│ ├── auth.service.ts # Authentication & token management
|
|
399
494
|
│ ├── api.service.ts # Backend API client (ofetch)
|
|
400
495
|
│ ├── git.service.ts # Git operations (simple-git)
|
|
496
|
+
│ ├── github.service.ts # GitHub PR fetching (gh CLI)
|
|
401
497
|
│ └── storage.service.ts # Encrypted credential storage
|
|
402
498
|
├── ui/ # Terminal UI components
|
|
403
499
|
│ ├── prompts.ts # Interactive prompts (Inquirer)
|
|
@@ -432,6 +528,9 @@ src/
|
|
|
432
528
|
- chalk, ora, cli-table3, boxen (terminal styling)
|
|
433
529
|
- conf (configuration management)
|
|
434
530
|
|
|
531
|
+
**External Tools:**
|
|
532
|
+
- GitHub CLI (gh) - Required for PR scanning
|
|
533
|
+
|
|
435
534
|
## Contributing
|
|
436
535
|
|
|
437
536
|
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
@@ -457,7 +556,7 @@ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for gui
|
|
|
457
556
|
|
|
458
557
|
### Getting Help
|
|
459
558
|
|
|
460
|
-
- 📖 **Documentation**: Read this README and [
|
|
559
|
+
- 📖 **Documentation**: Read this README and [TESTING_GUIDE.md](TESTING_GUIDE.md)
|
|
461
560
|
- 🐛 **Issues**: [GitHub Issues](https://github.com/medhatdawoud/bragduck-cli/issues)
|
|
462
561
|
- 💬 **Discussions**: [GitHub Discussions](https://github.com/medhatdawoud/bragduck-cli/discussions)
|
|
463
562
|
|
package/dist/bin/bragduck.js
CHANGED
|
@@ -308,7 +308,7 @@ var init_storage_service = __esm({
|
|
|
308
308
|
});
|
|
309
309
|
|
|
310
310
|
// src/utils/errors.ts
|
|
311
|
-
var BragduckError, AuthenticationError, GitError, ApiError, NetworkError, ValidationError, OAuthError, TokenExpiredError;
|
|
311
|
+
var BragduckError, AuthenticationError, GitError, ApiError, NetworkError, ValidationError, OAuthError, TokenExpiredError, GitHubError;
|
|
312
312
|
var init_errors = __esm({
|
|
313
313
|
"src/utils/errors.ts"() {
|
|
314
314
|
"use strict";
|
|
@@ -366,6 +366,12 @@ var init_errors = __esm({
|
|
|
366
366
|
this.code = "TOKEN_EXPIRED";
|
|
367
367
|
}
|
|
368
368
|
};
|
|
369
|
+
GitHubError = class extends BragduckError {
|
|
370
|
+
constructor(message, details) {
|
|
371
|
+
super(message, "GITHUB_ERROR", details);
|
|
372
|
+
this.name = "GitHubError";
|
|
373
|
+
}
|
|
374
|
+
};
|
|
369
375
|
}
|
|
370
376
|
});
|
|
371
377
|
|
|
@@ -1399,6 +1405,13 @@ init_esm_shims();
|
|
|
1399
1405
|
import boxen4 from "boxen";
|
|
1400
1406
|
import chalk7 from "chalk";
|
|
1401
1407
|
|
|
1408
|
+
// src/services/github.service.ts
|
|
1409
|
+
init_esm_shims();
|
|
1410
|
+
init_errors();
|
|
1411
|
+
init_logger();
|
|
1412
|
+
import { exec as exec2 } from "child_process";
|
|
1413
|
+
import { promisify as promisify2 } from "util";
|
|
1414
|
+
|
|
1402
1415
|
// src/services/git.service.ts
|
|
1403
1416
|
init_esm_shims();
|
|
1404
1417
|
import simpleGit from "simple-git";
|
|
@@ -1481,7 +1494,7 @@ var GitService = class {
|
|
|
1481
1494
|
* Fetch recent commits
|
|
1482
1495
|
*/
|
|
1483
1496
|
async getRecentCommits(options = {}) {
|
|
1484
|
-
const { days = 30, limit, author
|
|
1497
|
+
const { days = 30, limit, author } = options;
|
|
1485
1498
|
try {
|
|
1486
1499
|
await this.validateRepository();
|
|
1487
1500
|
logger.debug(`Fetching commits from last ${days} days`);
|
|
@@ -1619,6 +1632,203 @@ var GitService = class {
|
|
|
1619
1632
|
};
|
|
1620
1633
|
var gitService = new GitService();
|
|
1621
1634
|
|
|
1635
|
+
// src/services/github.service.ts
|
|
1636
|
+
var execAsync2 = promisify2(exec2);
|
|
1637
|
+
var GitHubService = class {
|
|
1638
|
+
MAX_BODY_LENGTH = 5e3;
|
|
1639
|
+
PR_SEARCH_FIELDS = "number,title,body,author,mergedAt,additions,deletions,changedFiles,url,labels";
|
|
1640
|
+
/**
|
|
1641
|
+
* Check if GitHub CLI is installed and available
|
|
1642
|
+
*/
|
|
1643
|
+
async checkGitHubCLI() {
|
|
1644
|
+
try {
|
|
1645
|
+
await execAsync2("command gh --version");
|
|
1646
|
+
return true;
|
|
1647
|
+
} catch (_error) {
|
|
1648
|
+
return false;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Validate that GitHub CLI is installed
|
|
1653
|
+
*/
|
|
1654
|
+
async ensureGitHubCLI() {
|
|
1655
|
+
const isInstalled = await this.checkGitHubCLI();
|
|
1656
|
+
if (!isInstalled) {
|
|
1657
|
+
throw new GitHubError("GitHub CLI (gh) is not installed", {
|
|
1658
|
+
hint: "Install from https://cli.github.com/"
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Check if user is authenticated with GitHub CLI
|
|
1664
|
+
*/
|
|
1665
|
+
async checkAuthentication() {
|
|
1666
|
+
try {
|
|
1667
|
+
await execAsync2("command gh auth status");
|
|
1668
|
+
return true;
|
|
1669
|
+
} catch (_error) {
|
|
1670
|
+
return false;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Ensure user is authenticated with GitHub CLI
|
|
1675
|
+
*/
|
|
1676
|
+
async ensureAuthentication() {
|
|
1677
|
+
const isAuthenticated = await this.checkAuthentication();
|
|
1678
|
+
if (!isAuthenticated) {
|
|
1679
|
+
throw new GitHubError("Not authenticated with GitHub", {
|
|
1680
|
+
hint: 'Run "gh auth login" to authenticate'
|
|
1681
|
+
});
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Validate that the current repository is hosted on GitHub
|
|
1686
|
+
*/
|
|
1687
|
+
async validateGitHubRepository() {
|
|
1688
|
+
try {
|
|
1689
|
+
await this.ensureGitHubCLI();
|
|
1690
|
+
await this.ensureAuthentication();
|
|
1691
|
+
await gitService.validateRepository();
|
|
1692
|
+
const { stdout } = await execAsync2("command gh repo view --json url");
|
|
1693
|
+
const data = JSON.parse(stdout);
|
|
1694
|
+
if (!data.url) {
|
|
1695
|
+
throw new GitHubError("This repository is not hosted on GitHub", {
|
|
1696
|
+
hint: "Only GitHub repositories are currently supported for PR scanning"
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
} catch (error) {
|
|
1700
|
+
if (error instanceof GitHubError || error instanceof GitError) {
|
|
1701
|
+
throw error;
|
|
1702
|
+
}
|
|
1703
|
+
if (error.message?.includes("not a git repository")) {
|
|
1704
|
+
throw new GitHubError("Not a git repository", {
|
|
1705
|
+
hint: "Navigate to a git repository directory"
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
if (error.message?.includes("Could not resolve to a Repository")) {
|
|
1709
|
+
throw new GitHubError("This repository is not hosted on GitHub", {
|
|
1710
|
+
hint: "Only GitHub repositories are currently supported for PR scanning"
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
throw new GitHubError("Failed to validate GitHub repository", {
|
|
1714
|
+
originalError: error.message
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Get GitHub repository information
|
|
1720
|
+
*/
|
|
1721
|
+
async getRepositoryInfo() {
|
|
1722
|
+
try {
|
|
1723
|
+
await this.ensureGitHubCLI();
|
|
1724
|
+
const { stdout } = await execAsync2(
|
|
1725
|
+
"command gh repo view --json owner,name,url,nameWithOwner"
|
|
1726
|
+
);
|
|
1727
|
+
const data = JSON.parse(stdout);
|
|
1728
|
+
return {
|
|
1729
|
+
owner: data.owner.login,
|
|
1730
|
+
name: data.name,
|
|
1731
|
+
fullName: data.nameWithOwner,
|
|
1732
|
+
url: data.url,
|
|
1733
|
+
isGitHub: true
|
|
1734
|
+
};
|
|
1735
|
+
} catch (error) {
|
|
1736
|
+
if (error instanceof GitHubError || error instanceof GitError) {
|
|
1737
|
+
throw error;
|
|
1738
|
+
}
|
|
1739
|
+
throw new GitHubError("Failed to get GitHub repository information", {
|
|
1740
|
+
originalError: error.message
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Get the current authenticated GitHub user
|
|
1746
|
+
*/
|
|
1747
|
+
async getCurrentGitHubUser() {
|
|
1748
|
+
try {
|
|
1749
|
+
const { stdout } = await execAsync2("command gh api user --jq .login");
|
|
1750
|
+
return stdout.trim() || null;
|
|
1751
|
+
} catch (_error) {
|
|
1752
|
+
logger.debug("Failed to get GitHub user");
|
|
1753
|
+
return null;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Fetch merged PRs from GitHub
|
|
1758
|
+
*/
|
|
1759
|
+
async getMergedPRs(options = {}) {
|
|
1760
|
+
const { days = 30, limit, author } = options;
|
|
1761
|
+
try {
|
|
1762
|
+
await this.ensureGitHubCLI();
|
|
1763
|
+
await this.ensureAuthentication();
|
|
1764
|
+
logger.debug(`Fetching merged PRs from the last ${days} days`);
|
|
1765
|
+
const since = /* @__PURE__ */ new Date();
|
|
1766
|
+
since.setDate(since.getDate() - days);
|
|
1767
|
+
const sinceDate = since.toISOString().split("T")[0];
|
|
1768
|
+
let searchQuery = `merged:>=${sinceDate}`;
|
|
1769
|
+
if (author) {
|
|
1770
|
+
searchQuery += ` author:${author}`;
|
|
1771
|
+
}
|
|
1772
|
+
const limitArg = limit ? `--limit ${limit}` : "";
|
|
1773
|
+
const command = `command gh pr list --state merged --json ${this.PR_SEARCH_FIELDS} --search "${searchQuery}" ${limitArg}`;
|
|
1774
|
+
logger.debug(`Running: ${command}`);
|
|
1775
|
+
const { stdout } = await execAsync2(command);
|
|
1776
|
+
const prs = JSON.parse(stdout);
|
|
1777
|
+
logger.debug(`Found ${prs.length} merged PRs`);
|
|
1778
|
+
return prs;
|
|
1779
|
+
} catch (error) {
|
|
1780
|
+
if (error instanceof GitHubError || error instanceof GitError) {
|
|
1781
|
+
throw error;
|
|
1782
|
+
}
|
|
1783
|
+
throw new GitHubError("Failed to fetch PRs from GitHub", {
|
|
1784
|
+
originalError: error.message,
|
|
1785
|
+
days
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* Get merged PRs by current user (PRs authored by the current user)
|
|
1791
|
+
*/
|
|
1792
|
+
async getPRsByCurrentUser(options = {}) {
|
|
1793
|
+
const currentUser = await this.getCurrentGitHubUser();
|
|
1794
|
+
if (!currentUser) {
|
|
1795
|
+
logger.warning("Could not determine GitHub user, returning all PRs");
|
|
1796
|
+
return this.getMergedPRs(options);
|
|
1797
|
+
}
|
|
1798
|
+
logger.debug(`Filtering PRs by author: ${currentUser}`);
|
|
1799
|
+
return this.getMergedPRs({
|
|
1800
|
+
...options,
|
|
1801
|
+
author: currentUser
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Transform a GitHub PR into a GitCommit structure
|
|
1806
|
+
* This allows PR data to work with existing commit-based UI and API
|
|
1807
|
+
*/
|
|
1808
|
+
transformPRToCommit(pr) {
|
|
1809
|
+
const title = pr.title;
|
|
1810
|
+
const body = pr.body || "";
|
|
1811
|
+
const truncatedBody = body.length > this.MAX_BODY_LENGTH ? body.substring(0, this.MAX_BODY_LENGTH) + "...[truncated]" : body;
|
|
1812
|
+
const message = truncatedBody ? `${title}
|
|
1813
|
+
|
|
1814
|
+
${truncatedBody}` : title;
|
|
1815
|
+
return {
|
|
1816
|
+
sha: `pr-${pr.number}`,
|
|
1817
|
+
message,
|
|
1818
|
+
author: pr.author.login,
|
|
1819
|
+
authorEmail: "",
|
|
1820
|
+
// Not available from gh PR API
|
|
1821
|
+
date: pr.mergedAt,
|
|
1822
|
+
diffStats: {
|
|
1823
|
+
filesChanged: pr.changedFiles,
|
|
1824
|
+
insertions: pr.additions,
|
|
1825
|
+
deletions: pr.deletions
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
};
|
|
1830
|
+
var githubService = new GitHubService();
|
|
1831
|
+
|
|
1622
1832
|
// src/commands/scan.ts
|
|
1623
1833
|
init_api_service();
|
|
1624
1834
|
init_auth_service();
|
|
@@ -1635,7 +1845,13 @@ import chalk5 from "chalk";
|
|
|
1635
1845
|
import Table from "cli-table3";
|
|
1636
1846
|
import terminalLink from "terminal-link";
|
|
1637
1847
|
function formatCommitChoice(commit) {
|
|
1638
|
-
|
|
1848
|
+
let displaySha;
|
|
1849
|
+
if (commit.sha.startsWith("pr-")) {
|
|
1850
|
+
const prNumber = commit.sha.replace("pr-", "#");
|
|
1851
|
+
displaySha = chalk5.yellow(prNumber);
|
|
1852
|
+
} else {
|
|
1853
|
+
displaySha = chalk5.yellow(commit.sha.substring(0, 7));
|
|
1854
|
+
}
|
|
1639
1855
|
const message = commit.message.split("\n")[0];
|
|
1640
1856
|
const author = chalk5.gray(`by ${commit.author}`);
|
|
1641
1857
|
const date = chalk5.gray(new Date(commit.date).toLocaleDateString());
|
|
@@ -1646,7 +1862,7 @@ function formatCommitChoice(commit) {
|
|
|
1646
1862
|
` [${filesChanged} files, ${chalk5.green(`+${insertions}`)} ${chalk5.red(`-${deletions}`)}]`
|
|
1647
1863
|
);
|
|
1648
1864
|
}
|
|
1649
|
-
return `${
|
|
1865
|
+
return `${displaySha} ${message}${stats}
|
|
1650
1866
|
${author} \u2022 ${date}`;
|
|
1651
1867
|
}
|
|
1652
1868
|
function formatRefinedCommitsTable(commits) {
|
|
@@ -1721,7 +1937,7 @@ async function promptSelectCommits(commits) {
|
|
|
1721
1937
|
checked: false
|
|
1722
1938
|
}));
|
|
1723
1939
|
const selected = await checkbox({
|
|
1724
|
-
message: "Select
|
|
1940
|
+
message: "Select PRs to brag about (use space to select, enter to confirm):",
|
|
1725
1941
|
choices,
|
|
1726
1942
|
pageSize: 10,
|
|
1727
1943
|
loop: false
|
|
@@ -1743,7 +1959,7 @@ async function promptDaysToScan(defaultDays = 30) {
|
|
|
1743
1959
|
{ name: "Custom", value: "custom", description: "Enter custom number of days" }
|
|
1744
1960
|
];
|
|
1745
1961
|
const selected = await select({
|
|
1746
|
-
message: "How many days back should we scan for
|
|
1962
|
+
message: "How many days back should we scan for PRs?",
|
|
1747
1963
|
choices,
|
|
1748
1964
|
default: "30"
|
|
1749
1965
|
});
|
|
@@ -1775,8 +1991,8 @@ function createSpinner(text) {
|
|
|
1775
1991
|
spinner: "dots"
|
|
1776
1992
|
});
|
|
1777
1993
|
}
|
|
1778
|
-
function
|
|
1779
|
-
return createSpinner(`Fetching
|
|
1994
|
+
function fetchingPRsSpinner(days) {
|
|
1995
|
+
return createSpinner(`Fetching merged PRs from the last ${days} days...`);
|
|
1780
1996
|
}
|
|
1781
1997
|
function refiningCommitsSpinner(count) {
|
|
1782
1998
|
return createSpinner(`Refining ${count} commit${count > 1 ? "s" : ""} with AI...`);
|
|
@@ -1823,9 +2039,9 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1823
2039
|
);
|
|
1824
2040
|
process.exit(1);
|
|
1825
2041
|
}
|
|
1826
|
-
await
|
|
1827
|
-
const repoInfo = await
|
|
1828
|
-
logger.info(`Repository: ${chalk7.cyan(repoInfo.
|
|
2042
|
+
await githubService.validateGitHubRepository();
|
|
2043
|
+
const repoInfo = await githubService.getRepositoryInfo();
|
|
2044
|
+
logger.info(`Repository: ${chalk7.cyan(repoInfo.fullName)} on GitHub`);
|
|
1829
2045
|
logger.log("");
|
|
1830
2046
|
let days = options.days;
|
|
1831
2047
|
if (!days) {
|
|
@@ -1833,21 +2049,22 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1833
2049
|
days = await promptDaysToScan(defaultDays);
|
|
1834
2050
|
logger.log("");
|
|
1835
2051
|
}
|
|
1836
|
-
const spinner =
|
|
2052
|
+
const spinner = fetchingPRsSpinner(days);
|
|
1837
2053
|
spinner.start();
|
|
1838
|
-
let
|
|
2054
|
+
let prs;
|
|
1839
2055
|
if (options.all) {
|
|
1840
|
-
|
|
2056
|
+
prs = await githubService.getMergedPRs({ days });
|
|
1841
2057
|
} else {
|
|
1842
|
-
|
|
2058
|
+
prs = await githubService.getPRsByCurrentUser({ days });
|
|
1843
2059
|
}
|
|
2060
|
+
const commits = prs.map((pr) => githubService.transformPRToCommit(pr));
|
|
1844
2061
|
if (commits.length === 0) {
|
|
1845
|
-
failSpinner(spinner, `No
|
|
2062
|
+
failSpinner(spinner, `No merged PRs found in the last ${days} days`);
|
|
1846
2063
|
logger.log("");
|
|
1847
|
-
logger.info("Try increasing the number of days or check your
|
|
2064
|
+
logger.info("Try increasing the number of days or check your GitHub activity");
|
|
1848
2065
|
return;
|
|
1849
2066
|
}
|
|
1850
|
-
succeedSpinner(spinner, `Found ${commits.length}
|
|
2067
|
+
succeedSpinner(spinner, `Found ${commits.length} PR${commits.length > 1 ? "s" : ""}`);
|
|
1851
2068
|
logger.log("");
|
|
1852
2069
|
logger.log(formatCommitStats(commits));
|
|
1853
2070
|
logger.log("");
|
|
@@ -1858,9 +2075,7 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1858
2075
|
}
|
|
1859
2076
|
const selectedCommits = commits.filter((c) => selectedShas.includes(c.sha));
|
|
1860
2077
|
logger.log("");
|
|
1861
|
-
logger.success(
|
|
1862
|
-
`Selected ${selectedCommits.length} commit${selectedCommits.length > 1 ? "s" : ""}`
|
|
1863
|
-
);
|
|
2078
|
+
logger.success(`Selected ${selectedCommits.length} PR${selectedCommits.length > 1 ? "s" : ""}`);
|
|
1864
2079
|
logger.log("");
|
|
1865
2080
|
const refineSpinner = refiningCommitsSpinner(selectedCommits.length);
|
|
1866
2081
|
refineSpinner.start();
|
|
@@ -1879,7 +2094,7 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1879
2094
|
};
|
|
1880
2095
|
const refineResponse = await apiService.refineCommits(refineRequest);
|
|
1881
2096
|
const refinedCommits = refineResponse.refined_commits;
|
|
1882
|
-
succeedSpinner(refineSpinner, "
|
|
2097
|
+
succeedSpinner(refineSpinner, "PRs refined successfully");
|
|
1883
2098
|
logger.log("");
|
|
1884
2099
|
logger.info("Preview of refined brags:");
|
|
1885
2100
|
logger.log("");
|
|
@@ -1901,7 +2116,7 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1901
2116
|
title: refined.refined_title,
|
|
1902
2117
|
description: refined.refined_description,
|
|
1903
2118
|
tags: refined.suggested_tags,
|
|
1904
|
-
repository: repoInfo.
|
|
2119
|
+
repository: repoInfo.url,
|
|
1905
2120
|
date: originalCommit?.date || (/* @__PURE__ */ new Date()).toISOString(),
|
|
1906
2121
|
commit_url: refined.commit_url,
|
|
1907
2122
|
impact_score: refined.impact_score,
|
|
@@ -1935,8 +2150,11 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1935
2150
|
}
|
|
1936
2151
|
}
|
|
1937
2152
|
function getErrorHint2(error) {
|
|
2153
|
+
if (error.name === "GitHubError") {
|
|
2154
|
+
return 'Make sure you are in a GitHub repository and have authenticated with "gh auth login"';
|
|
2155
|
+
}
|
|
1938
2156
|
if (error.name === "GitError") {
|
|
1939
|
-
return "Make sure you are in a git repository
|
|
2157
|
+
return "Make sure you are in a git repository. Note: Only GitHub repositories are supported for PR scanning.";
|
|
1940
2158
|
}
|
|
1941
2159
|
if (error.name === "TokenExpiredError" || error.name === "AuthenticationError") {
|
|
1942
2160
|
return 'Run "bragduck init" to login again';
|