@bragduck/cli 1.0.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.
- package/README.md +125 -26
- package/dist/bin/bragduck.js +256 -25
- package/dist/bin/bragduck.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
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();
|
|
@@ -1633,8 +1843,15 @@ import { checkbox, confirm as confirm2, input, select } from "@inquirer/prompts"
|
|
|
1633
1843
|
init_esm_shims();
|
|
1634
1844
|
import chalk5 from "chalk";
|
|
1635
1845
|
import Table from "cli-table3";
|
|
1846
|
+
import terminalLink from "terminal-link";
|
|
1636
1847
|
function formatCommitChoice(commit) {
|
|
1637
|
-
|
|
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
|
+
}
|
|
1638
1855
|
const message = commit.message.split("\n")[0];
|
|
1639
1856
|
const author = chalk5.gray(`by ${commit.author}`);
|
|
1640
1857
|
const date = chalk5.gray(new Date(commit.date).toLocaleDateString());
|
|
@@ -1645,7 +1862,7 @@ function formatCommitChoice(commit) {
|
|
|
1645
1862
|
` [${filesChanged} files, ${chalk5.green(`+${insertions}`)} ${chalk5.red(`-${deletions}`)}]`
|
|
1646
1863
|
);
|
|
1647
1864
|
}
|
|
1648
|
-
return `${
|
|
1865
|
+
return `${displaySha} ${message}${stats}
|
|
1649
1866
|
${author} \u2022 ${date}`;
|
|
1650
1867
|
}
|
|
1651
1868
|
function formatRefinedCommitsTable(commits) {
|
|
@@ -1688,10 +1905,17 @@ function formatCommitStats(commits) {
|
|
|
1688
1905
|
}
|
|
1689
1906
|
function formatSuccessMessage(count) {
|
|
1690
1907
|
const emoji = "\u{1F389}";
|
|
1691
|
-
const title = chalk5.green.bold(
|
|
1908
|
+
const title = chalk5.green.bold(
|
|
1909
|
+
`${emoji} Successfully created ${count} brag${count > 1 ? "s" : ""}!`
|
|
1910
|
+
);
|
|
1692
1911
|
const message = chalk5.white("\nYour achievements are now saved and ready to showcase.");
|
|
1693
1912
|
const hint = chalk5.dim("\nRun ") + chalk5.cyan("bragduck list") + chalk5.dim(" to see all your brags");
|
|
1694
|
-
|
|
1913
|
+
const url = "https://app.bragduck.com/brags";
|
|
1914
|
+
const clickableUrl = terminalLink(url, url, {
|
|
1915
|
+
fallback: () => chalk5.blue.underline(url)
|
|
1916
|
+
});
|
|
1917
|
+
const webUrl = chalk5.dim("\n\nOr, check ") + clickableUrl + chalk5.dim(" to see all your brags");
|
|
1918
|
+
return `${title}${message}${hint}${webUrl}`;
|
|
1695
1919
|
}
|
|
1696
1920
|
function formatErrorMessage(message, hint) {
|
|
1697
1921
|
const title = chalk5.red.bold("\u2717 Error");
|
|
@@ -1713,7 +1937,7 @@ async function promptSelectCommits(commits) {
|
|
|
1713
1937
|
checked: false
|
|
1714
1938
|
}));
|
|
1715
1939
|
const selected = await checkbox({
|
|
1716
|
-
message: "Select
|
|
1940
|
+
message: "Select PRs to brag about (use space to select, enter to confirm):",
|
|
1717
1941
|
choices,
|
|
1718
1942
|
pageSize: 10,
|
|
1719
1943
|
loop: false
|
|
@@ -1735,7 +1959,7 @@ async function promptDaysToScan(defaultDays = 30) {
|
|
|
1735
1959
|
{ name: "Custom", value: "custom", description: "Enter custom number of days" }
|
|
1736
1960
|
];
|
|
1737
1961
|
const selected = await select({
|
|
1738
|
-
message: "How many days back should we scan for
|
|
1962
|
+
message: "How many days back should we scan for PRs?",
|
|
1739
1963
|
choices,
|
|
1740
1964
|
default: "30"
|
|
1741
1965
|
});
|
|
@@ -1767,8 +1991,8 @@ function createSpinner(text) {
|
|
|
1767
1991
|
spinner: "dots"
|
|
1768
1992
|
});
|
|
1769
1993
|
}
|
|
1770
|
-
function
|
|
1771
|
-
return createSpinner(`Fetching
|
|
1994
|
+
function fetchingPRsSpinner(days) {
|
|
1995
|
+
return createSpinner(`Fetching merged PRs from the last ${days} days...`);
|
|
1772
1996
|
}
|
|
1773
1997
|
function refiningCommitsSpinner(count) {
|
|
1774
1998
|
return createSpinner(`Refining ${count} commit${count > 1 ? "s" : ""} with AI...`);
|
|
@@ -1815,9 +2039,9 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1815
2039
|
);
|
|
1816
2040
|
process.exit(1);
|
|
1817
2041
|
}
|
|
1818
|
-
await
|
|
1819
|
-
const repoInfo = await
|
|
1820
|
-
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`);
|
|
1821
2045
|
logger.log("");
|
|
1822
2046
|
let days = options.days;
|
|
1823
2047
|
if (!days) {
|
|
@@ -1825,21 +2049,22 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1825
2049
|
days = await promptDaysToScan(defaultDays);
|
|
1826
2050
|
logger.log("");
|
|
1827
2051
|
}
|
|
1828
|
-
const spinner =
|
|
2052
|
+
const spinner = fetchingPRsSpinner(days);
|
|
1829
2053
|
spinner.start();
|
|
1830
|
-
let
|
|
2054
|
+
let prs;
|
|
1831
2055
|
if (options.all) {
|
|
1832
|
-
|
|
2056
|
+
prs = await githubService.getMergedPRs({ days });
|
|
1833
2057
|
} else {
|
|
1834
|
-
|
|
2058
|
+
prs = await githubService.getPRsByCurrentUser({ days });
|
|
1835
2059
|
}
|
|
2060
|
+
const commits = prs.map((pr) => githubService.transformPRToCommit(pr));
|
|
1836
2061
|
if (commits.length === 0) {
|
|
1837
|
-
failSpinner(spinner, `No
|
|
2062
|
+
failSpinner(spinner, `No merged PRs found in the last ${days} days`);
|
|
1838
2063
|
logger.log("");
|
|
1839
|
-
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");
|
|
1840
2065
|
return;
|
|
1841
2066
|
}
|
|
1842
|
-
succeedSpinner(spinner, `Found ${commits.length}
|
|
2067
|
+
succeedSpinner(spinner, `Found ${commits.length} PR${commits.length > 1 ? "s" : ""}`);
|
|
1843
2068
|
logger.log("");
|
|
1844
2069
|
logger.log(formatCommitStats(commits));
|
|
1845
2070
|
logger.log("");
|
|
@@ -1850,7 +2075,7 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1850
2075
|
}
|
|
1851
2076
|
const selectedCommits = commits.filter((c) => selectedShas.includes(c.sha));
|
|
1852
2077
|
logger.log("");
|
|
1853
|
-
logger.success(`Selected ${selectedCommits.length}
|
|
2078
|
+
logger.success(`Selected ${selectedCommits.length} PR${selectedCommits.length > 1 ? "s" : ""}`);
|
|
1854
2079
|
logger.log("");
|
|
1855
2080
|
const refineSpinner = refiningCommitsSpinner(selectedCommits.length);
|
|
1856
2081
|
refineSpinner.start();
|
|
@@ -1869,7 +2094,7 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1869
2094
|
};
|
|
1870
2095
|
const refineResponse = await apiService.refineCommits(refineRequest);
|
|
1871
2096
|
const refinedCommits = refineResponse.refined_commits;
|
|
1872
|
-
succeedSpinner(refineSpinner, "
|
|
2097
|
+
succeedSpinner(refineSpinner, "PRs refined successfully");
|
|
1873
2098
|
logger.log("");
|
|
1874
2099
|
logger.info("Preview of refined brags:");
|
|
1875
2100
|
logger.log("");
|
|
@@ -1891,8 +2116,11 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1891
2116
|
title: refined.refined_title,
|
|
1892
2117
|
description: refined.refined_description,
|
|
1893
2118
|
tags: refined.suggested_tags,
|
|
1894
|
-
repository: repoInfo.
|
|
1895
|
-
date: originalCommit?.date || (/* @__PURE__ */ new Date()).toISOString()
|
|
2119
|
+
repository: repoInfo.url,
|
|
2120
|
+
date: originalCommit?.date || (/* @__PURE__ */ new Date()).toISOString(),
|
|
2121
|
+
commit_url: refined.commit_url,
|
|
2122
|
+
impact_score: refined.impact_score,
|
|
2123
|
+
impact_description: refined.impact_description
|
|
1896
2124
|
};
|
|
1897
2125
|
})
|
|
1898
2126
|
};
|
|
@@ -1922,8 +2150,11 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
1922
2150
|
}
|
|
1923
2151
|
}
|
|
1924
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
|
+
}
|
|
1925
2156
|
if (error.name === "GitError") {
|
|
1926
|
-
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.";
|
|
1927
2158
|
}
|
|
1928
2159
|
if (error.name === "TokenExpiredError" || error.name === "AuthenticationError") {
|
|
1929
2160
|
return 'Run "bragduck init" to login again';
|