@bragduck/cli 2.3.11 → 2.4.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/dist/bin/bragduck.js +1125 -426
- package/dist/bin/bragduck.js.map +1 -1
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/bragduck.js
CHANGED
|
@@ -232,6 +232,82 @@ var init_storage_service = __esm({
|
|
|
232
232
|
}
|
|
233
233
|
return true;
|
|
234
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Get credentials for a specific service
|
|
237
|
+
*/
|
|
238
|
+
async getServiceCredentials(service) {
|
|
239
|
+
const credentials = await this.getCredentials();
|
|
240
|
+
if (!credentials) return null;
|
|
241
|
+
if (service === "bragduck" && credentials.accessToken && !credentials.services) {
|
|
242
|
+
return {
|
|
243
|
+
accessToken: credentials.accessToken,
|
|
244
|
+
refreshToken: credentials.refreshToken,
|
|
245
|
+
expiresAt: credentials.expiresAt
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return credentials.services?.[service] || null;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Set credentials for a specific service
|
|
252
|
+
*/
|
|
253
|
+
async setServiceCredentials(service, creds) {
|
|
254
|
+
let existing = await this.getCredentials();
|
|
255
|
+
if (existing && existing.accessToken && !existing.services) {
|
|
256
|
+
existing = {
|
|
257
|
+
services: {
|
|
258
|
+
bragduck: {
|
|
259
|
+
accessToken: existing.accessToken,
|
|
260
|
+
refreshToken: existing.refreshToken,
|
|
261
|
+
expiresAt: existing.expiresAt
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const updated = {
|
|
267
|
+
...existing,
|
|
268
|
+
services: {
|
|
269
|
+
...existing?.services,
|
|
270
|
+
[service]: creds
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
await this.setCredentials(updated);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Delete credentials for a specific service
|
|
277
|
+
*/
|
|
278
|
+
async deleteServiceCredentials(service) {
|
|
279
|
+
const existing = await this.getCredentials();
|
|
280
|
+
if (!existing?.services?.[service]) return;
|
|
281
|
+
delete existing.services[service];
|
|
282
|
+
await this.setCredentials(existing);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Check if a specific service is authenticated
|
|
286
|
+
*/
|
|
287
|
+
async isServiceAuthenticated(service) {
|
|
288
|
+
const creds = await this.getServiceCredentials(service);
|
|
289
|
+
if (!creds?.accessToken) return false;
|
|
290
|
+
if (creds.expiresAt && creds.expiresAt < Date.now()) return false;
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* List all authenticated services
|
|
295
|
+
*/
|
|
296
|
+
async getAuthenticatedServices() {
|
|
297
|
+
const credentials = await this.getCredentials();
|
|
298
|
+
if (!credentials) return [];
|
|
299
|
+
if (credentials.accessToken && !credentials.services) {
|
|
300
|
+
return ["bragduck"];
|
|
301
|
+
}
|
|
302
|
+
if (!credentials.services) return [];
|
|
303
|
+
const services = [];
|
|
304
|
+
for (const [service, creds] of Object.entries(credentials.services)) {
|
|
305
|
+
if (creds?.accessToken) {
|
|
306
|
+
services.push(service);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return services;
|
|
310
|
+
}
|
|
235
311
|
/**
|
|
236
312
|
* Store user information
|
|
237
313
|
*/
|
|
@@ -932,12 +1008,12 @@ __export(version_exports, {
|
|
|
932
1008
|
});
|
|
933
1009
|
import { readFileSync as readFileSync3 } from "fs";
|
|
934
1010
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
935
|
-
import { dirname as dirname3, join as
|
|
1011
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
936
1012
|
import chalk4 from "chalk";
|
|
937
|
-
import
|
|
1013
|
+
import boxen2 from "boxen";
|
|
938
1014
|
function getCurrentVersion() {
|
|
939
1015
|
try {
|
|
940
|
-
const packageJsonPath2 =
|
|
1016
|
+
const packageJsonPath2 = join4(__dirname4, "../../package.json");
|
|
941
1017
|
const packageJson2 = JSON.parse(readFileSync3(packageJsonPath2, "utf-8"));
|
|
942
1018
|
return packageJson2.version;
|
|
943
1019
|
} catch {
|
|
@@ -1003,7 +1079,7 @@ Latest version: ${chalk4.green(latestVersion)}
|
|
|
1003
1079
|
Update with: ${chalk4.cyan("npm install -g @bragduck/cli@latest")}`;
|
|
1004
1080
|
console.log("");
|
|
1005
1081
|
console.log(
|
|
1006
|
-
|
|
1082
|
+
boxen2(message, {
|
|
1007
1083
|
padding: 1,
|
|
1008
1084
|
margin: { top: 0, right: 1, bottom: 0, left: 1 },
|
|
1009
1085
|
borderStyle: "round",
|
|
@@ -1039,10 +1115,10 @@ import { ofetch as ofetch2 } from "ofetch";
|
|
|
1039
1115
|
import { readFileSync as readFileSync4 } from "fs";
|
|
1040
1116
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1041
1117
|
import { URLSearchParams as URLSearchParams2 } from "url";
|
|
1042
|
-
import { dirname as dirname4, join as
|
|
1118
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
1043
1119
|
function getCliVersion() {
|
|
1044
1120
|
try {
|
|
1045
|
-
const packageJsonPath2 =
|
|
1121
|
+
const packageJsonPath2 = join5(__dirname5, "../../package.json");
|
|
1046
1122
|
const packageJson2 = JSON.parse(readFileSync4(packageJsonPath2, "utf-8"));
|
|
1047
1123
|
return packageJson2.version;
|
|
1048
1124
|
} catch {
|
|
@@ -1354,29 +1430,202 @@ import { readFileSync as readFileSync5 } from "fs";
|
|
|
1354
1430
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
1355
1431
|
import { dirname as dirname5, join as join7 } from "path";
|
|
1356
1432
|
|
|
1357
|
-
// src/commands/
|
|
1433
|
+
// src/commands/auth.ts
|
|
1358
1434
|
init_esm_shims();
|
|
1359
1435
|
init_auth_service();
|
|
1436
|
+
init_storage_service();
|
|
1360
1437
|
init_logger();
|
|
1361
|
-
import ora from "ora";
|
|
1362
1438
|
import boxen from "boxen";
|
|
1439
|
+
import chalk3 from "chalk";
|
|
1440
|
+
|
|
1441
|
+
// src/ui/theme.ts
|
|
1442
|
+
init_esm_shims();
|
|
1363
1443
|
import chalk2 from "chalk";
|
|
1364
|
-
|
|
1444
|
+
var colors = {
|
|
1445
|
+
// Primary colors for main actions and interactive elements
|
|
1446
|
+
primary: chalk2.cyan,
|
|
1447
|
+
// Success states
|
|
1448
|
+
success: chalk2.green,
|
|
1449
|
+
successBold: chalk2.green.bold,
|
|
1450
|
+
// Warning states
|
|
1451
|
+
warning: chalk2.yellow,
|
|
1452
|
+
warningBold: chalk2.yellow.bold,
|
|
1453
|
+
// Error states
|
|
1454
|
+
error: chalk2.red,
|
|
1455
|
+
errorBold: chalk2.red.bold,
|
|
1456
|
+
// Info and metadata
|
|
1457
|
+
info: chalk2.gray,
|
|
1458
|
+
infoDim: chalk2.dim,
|
|
1459
|
+
// Highlighted/important data
|
|
1460
|
+
highlight: chalk2.yellow.bold,
|
|
1461
|
+
highlightCyan: chalk2.cyan.bold,
|
|
1462
|
+
// Text emphasis
|
|
1463
|
+
bold: chalk2.bold,
|
|
1464
|
+
dim: chalk2.dim,
|
|
1465
|
+
// Special purpose
|
|
1466
|
+
white: chalk2.white,
|
|
1467
|
+
gray: chalk2.gray,
|
|
1468
|
+
// Links and URLs
|
|
1469
|
+
link: chalk2.blue.underline
|
|
1470
|
+
};
|
|
1471
|
+
var theme = {
|
|
1472
|
+
/**
|
|
1473
|
+
* Format command names and CLI actions
|
|
1474
|
+
*/
|
|
1475
|
+
command: (text) => colors.primary(text),
|
|
1476
|
+
/**
|
|
1477
|
+
* Format success messages
|
|
1478
|
+
*/
|
|
1479
|
+
success: (text) => colors.success(`\u2713 ${text}`),
|
|
1480
|
+
successBold: (text) => colors.successBold(`\u2713 ${text}`),
|
|
1481
|
+
/**
|
|
1482
|
+
* Format error messages
|
|
1483
|
+
*/
|
|
1484
|
+
error: (text) => colors.error(`\u2717 ${text}`),
|
|
1485
|
+
errorBold: (text) => colors.errorBold(`\u2717 ${text}`),
|
|
1486
|
+
/**
|
|
1487
|
+
* Format warning messages
|
|
1488
|
+
*/
|
|
1489
|
+
warning: (text) => colors.warning(`\u26A0 ${text}`),
|
|
1490
|
+
/**
|
|
1491
|
+
* Format info messages
|
|
1492
|
+
*/
|
|
1493
|
+
info: (text) => colors.info(text),
|
|
1494
|
+
/**
|
|
1495
|
+
* Format labels (e.g., "User:", "Email:")
|
|
1496
|
+
*/
|
|
1497
|
+
label: (text) => colors.gray(`${text}:`),
|
|
1498
|
+
/**
|
|
1499
|
+
* Format values
|
|
1500
|
+
*/
|
|
1501
|
+
value: (text) => colors.highlight(text),
|
|
1502
|
+
/**
|
|
1503
|
+
* Format counts and numbers
|
|
1504
|
+
*/
|
|
1505
|
+
count: (num) => colors.highlightCyan(num.toString()),
|
|
1506
|
+
/**
|
|
1507
|
+
* Format file paths
|
|
1508
|
+
*/
|
|
1509
|
+
path: (text) => colors.info(text),
|
|
1510
|
+
/**
|
|
1511
|
+
* Format step indicators (e.g., "Step 1/5:")
|
|
1512
|
+
*/
|
|
1513
|
+
step: (current, total) => colors.primary(`[${current}/${total}]`),
|
|
1514
|
+
/**
|
|
1515
|
+
* Format progress text
|
|
1516
|
+
*/
|
|
1517
|
+
progress: (text) => colors.infoDim(text),
|
|
1518
|
+
/**
|
|
1519
|
+
* Format emphasized text
|
|
1520
|
+
*/
|
|
1521
|
+
emphasis: (text) => colors.bold(text),
|
|
1522
|
+
/**
|
|
1523
|
+
* Format de-emphasized/secondary text
|
|
1524
|
+
*/
|
|
1525
|
+
secondary: (text) => colors.dim(text),
|
|
1526
|
+
/**
|
|
1527
|
+
* Format interactive elements (prompts, selections)
|
|
1528
|
+
*/
|
|
1529
|
+
interactive: (text) => colors.primary(text),
|
|
1530
|
+
/**
|
|
1531
|
+
* Format keys in key-value pairs
|
|
1532
|
+
*/
|
|
1533
|
+
key: (text) => colors.white(text)
|
|
1534
|
+
};
|
|
1535
|
+
var boxStyles = {
|
|
1536
|
+
success: {
|
|
1537
|
+
borderColor: "green",
|
|
1538
|
+
borderStyle: "round",
|
|
1539
|
+
padding: 1,
|
|
1540
|
+
margin: 1
|
|
1541
|
+
},
|
|
1542
|
+
error: {
|
|
1543
|
+
borderColor: "red",
|
|
1544
|
+
borderStyle: "round",
|
|
1545
|
+
padding: 1,
|
|
1546
|
+
margin: 1
|
|
1547
|
+
},
|
|
1548
|
+
warning: {
|
|
1549
|
+
borderColor: "yellow",
|
|
1550
|
+
borderStyle: "round",
|
|
1551
|
+
padding: 1,
|
|
1552
|
+
margin: 1
|
|
1553
|
+
},
|
|
1554
|
+
info: {
|
|
1555
|
+
borderColor: "cyan",
|
|
1556
|
+
borderStyle: "round",
|
|
1557
|
+
padding: 1,
|
|
1558
|
+
margin: 1
|
|
1559
|
+
},
|
|
1560
|
+
plain: {
|
|
1561
|
+
borderColor: "gray",
|
|
1562
|
+
borderStyle: "round",
|
|
1563
|
+
padding: 1,
|
|
1564
|
+
margin: 1
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
var tableStyles = {
|
|
1568
|
+
default: {
|
|
1569
|
+
style: {
|
|
1570
|
+
head: [],
|
|
1571
|
+
border: ["gray"]
|
|
1572
|
+
}
|
|
1573
|
+
},
|
|
1574
|
+
compact: {
|
|
1575
|
+
style: {
|
|
1576
|
+
head: [],
|
|
1577
|
+
border: ["dim"],
|
|
1578
|
+
compact: true
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
var sizeIndicators = {
|
|
1583
|
+
small: colors.success("\u25CF"),
|
|
1584
|
+
medium: colors.warning("\u25CF"),
|
|
1585
|
+
large: colors.error("\u25CF")
|
|
1586
|
+
};
|
|
1587
|
+
function getSizeIndicator(insertions, deletions) {
|
|
1588
|
+
const total = insertions + deletions;
|
|
1589
|
+
if (total < 50) {
|
|
1590
|
+
return `${sizeIndicators.small} Small`;
|
|
1591
|
+
} else if (total < 200) {
|
|
1592
|
+
return `${sizeIndicators.medium} Medium`;
|
|
1593
|
+
} else {
|
|
1594
|
+
return `${sizeIndicators.large} Large`;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
function formatDiffStats(insertions, deletions) {
|
|
1598
|
+
return `${colors.success(`+${insertions}`)} ${colors.error(`-${deletions}`)}`;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// src/commands/auth.ts
|
|
1602
|
+
async function authCommand(subcommand) {
|
|
1603
|
+
if (!subcommand || subcommand === "login") {
|
|
1604
|
+
await authLogin();
|
|
1605
|
+
} else if (subcommand === "status") {
|
|
1606
|
+
await authStatus();
|
|
1607
|
+
} else {
|
|
1608
|
+
logger.error(`Unknown auth subcommand: ${subcommand}`);
|
|
1609
|
+
logger.info("Available subcommands: login, status");
|
|
1610
|
+
process.exit(1);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
async function authLogin() {
|
|
1365
1614
|
logger.log("");
|
|
1366
|
-
logger.info("
|
|
1615
|
+
logger.info("Authenticating with Bragduck...");
|
|
1367
1616
|
logger.log("");
|
|
1368
|
-
const isAuthenticated = await
|
|
1617
|
+
const isAuthenticated = await storageService.isServiceAuthenticated("bragduck");
|
|
1369
1618
|
if (isAuthenticated) {
|
|
1370
1619
|
const userInfo = authService.getUserInfo();
|
|
1371
1620
|
if (userInfo) {
|
|
1372
1621
|
logger.log(
|
|
1373
1622
|
boxen(
|
|
1374
|
-
`${
|
|
1623
|
+
`${chalk3.yellow("Already authenticated!")}
|
|
1375
1624
|
|
|
1376
|
-
${
|
|
1377
|
-
${
|
|
1625
|
+
${chalk3.gray("User:")} ${userInfo.name}
|
|
1626
|
+
${chalk3.gray("Email:")} ${userInfo.email}
|
|
1378
1627
|
|
|
1379
|
-
${
|
|
1628
|
+
${chalk3.dim("Run")} ${chalk3.cyan("bragduck logout")} ${chalk3.dim("to sign out")}`,
|
|
1380
1629
|
{
|
|
1381
1630
|
padding: 1,
|
|
1382
1631
|
margin: 1,
|
|
@@ -1386,26 +1635,20 @@ ${chalk2.dim("Run")} ${chalk2.cyan("bragduck logout")} ${chalk2.dim("to sign out
|
|
|
1386
1635
|
)
|
|
1387
1636
|
);
|
|
1388
1637
|
logger.log("");
|
|
1389
|
-
globalThis.setTimeout(() => {
|
|
1390
|
-
process.exit(0);
|
|
1391
|
-
}, 100);
|
|
1392
1638
|
return;
|
|
1393
1639
|
}
|
|
1394
1640
|
}
|
|
1395
|
-
const spinner = ora("Opening browser for authentication...").start();
|
|
1396
1641
|
try {
|
|
1397
|
-
spinner.text = "Waiting for authentication...";
|
|
1398
1642
|
const userInfo = await authService.login();
|
|
1399
|
-
spinner.succeed("Authentication successful!");
|
|
1400
1643
|
logger.log("");
|
|
1401
1644
|
logger.log(
|
|
1402
1645
|
boxen(
|
|
1403
|
-
`${
|
|
1646
|
+
`${chalk3.green.bold("\u2713 Successfully authenticated!")}
|
|
1404
1647
|
|
|
1405
|
-
${
|
|
1406
|
-
${
|
|
1648
|
+
${chalk3.gray("Welcome,")} ${chalk3.cyan(userInfo.name)}
|
|
1649
|
+
${chalk3.gray("Email:")} ${userInfo.email}
|
|
1407
1650
|
|
|
1408
|
-
${
|
|
1651
|
+
${chalk3.dim("Use")} ${chalk3.cyan("bragduck sync")} ${chalk3.dim("to create brags")}`,
|
|
1409
1652
|
{
|
|
1410
1653
|
padding: 1,
|
|
1411
1654
|
margin: 1,
|
|
@@ -1415,132 +1658,200 @@ ${chalk2.dim("You can now use")} ${chalk2.cyan("bragduck scan")} ${chalk2.dim("t
|
|
|
1415
1658
|
)
|
|
1416
1659
|
);
|
|
1417
1660
|
logger.log("");
|
|
1418
|
-
globalThis.setTimeout(() => {
|
|
1419
|
-
process.exit(0);
|
|
1420
|
-
}, 100);
|
|
1421
|
-
return;
|
|
1422
1661
|
} catch (error) {
|
|
1423
|
-
spinner.fail("Authentication failed");
|
|
1424
|
-
logger.log("");
|
|
1425
1662
|
const err = error;
|
|
1663
|
+
logger.log("");
|
|
1426
1664
|
logger.log(
|
|
1427
|
-
boxen(
|
|
1428
|
-
`${chalk2.red.bold("\u2717 Authentication Failed")}
|
|
1429
|
-
|
|
1430
|
-
${err.message}
|
|
1665
|
+
boxen(`${chalk3.red.bold("\u2717 Authentication Failed")}
|
|
1431
1666
|
|
|
1432
|
-
${
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
}
|
|
1439
|
-
)
|
|
1667
|
+
${err.message}`, {
|
|
1668
|
+
padding: 1,
|
|
1669
|
+
margin: 1,
|
|
1670
|
+
borderStyle: "round",
|
|
1671
|
+
borderColor: "red"
|
|
1672
|
+
})
|
|
1440
1673
|
);
|
|
1441
1674
|
process.exit(1);
|
|
1442
1675
|
}
|
|
1443
1676
|
}
|
|
1444
|
-
function
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1677
|
+
async function authStatus() {
|
|
1678
|
+
logger.log("");
|
|
1679
|
+
logger.info("Authentication Status:");
|
|
1680
|
+
logger.log("");
|
|
1681
|
+
const services = await storageService.getAuthenticatedServices();
|
|
1682
|
+
if (services.length === 0) {
|
|
1683
|
+
logger.info(theme.secondary("Not authenticated with any services"));
|
|
1684
|
+
logger.log("");
|
|
1685
|
+
logger.info(`Run ${theme.command("bragduck auth login")} to authenticate`);
|
|
1686
|
+
logger.log("");
|
|
1687
|
+
return;
|
|
1453
1688
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1689
|
+
const bragduckAuth = await storageService.isServiceAuthenticated("bragduck");
|
|
1690
|
+
if (bragduckAuth) {
|
|
1691
|
+
const userInfo = authService.getUserInfo();
|
|
1692
|
+
logger.info(`${theme.success("\u2713")} Bragduck: ${userInfo?.name || "Authenticated"}`);
|
|
1693
|
+
} else {
|
|
1694
|
+
logger.info(`${theme.error("\u2717")} Bragduck: Not authenticated`);
|
|
1456
1695
|
}
|
|
1457
|
-
|
|
1458
|
-
|
|
1696
|
+
for (const service of services) {
|
|
1697
|
+
if (service !== "bragduck") {
|
|
1698
|
+
logger.info(`${theme.success("\u2713")} ${service}: Authenticated`);
|
|
1699
|
+
}
|
|
1459
1700
|
}
|
|
1460
|
-
|
|
1701
|
+
logger.log("");
|
|
1461
1702
|
}
|
|
1462
1703
|
|
|
1463
|
-
// src/commands/
|
|
1704
|
+
// src/commands/sync.ts
|
|
1464
1705
|
init_esm_shims();
|
|
1465
|
-
init_auth_service();
|
|
1466
|
-
init_logger();
|
|
1467
|
-
import { confirm } from "@inquirer/prompts";
|
|
1468
|
-
import boxen2 from "boxen";
|
|
1469
|
-
import chalk3 from "chalk";
|
|
1470
|
-
import ora2 from "ora";
|
|
1471
|
-
async function logoutCommand() {
|
|
1472
|
-
const isAuthenticated = await authService.isAuthenticated();
|
|
1473
|
-
if (!isAuthenticated) {
|
|
1474
|
-
logger.log(
|
|
1475
|
-
boxen2(
|
|
1476
|
-
`${chalk3.yellow("Not currently authenticated")}
|
|
1477
1706
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
1485
|
-
)
|
|
1486
|
-
);
|
|
1487
|
-
return;
|
|
1488
|
-
}
|
|
1489
|
-
const userInfo = authService.getUserInfo();
|
|
1490
|
-
const userName = userInfo?.name || "Unknown User";
|
|
1491
|
-
logger.log("");
|
|
1492
|
-
const shouldLogout = await confirm({
|
|
1493
|
-
message: `Are you sure you want to logout? (${chalk3.cyan(userName)})`,
|
|
1494
|
-
default: false
|
|
1495
|
-
});
|
|
1496
|
-
if (!shouldLogout) {
|
|
1497
|
-
logger.info("Logout cancelled");
|
|
1498
|
-
return;
|
|
1499
|
-
}
|
|
1500
|
-
const spinner = ora2("Logging out...").start();
|
|
1501
|
-
try {
|
|
1502
|
-
await authService.logout();
|
|
1503
|
-
spinner.succeed("Logged out successfully");
|
|
1504
|
-
logger.log("");
|
|
1505
|
-
logger.log(
|
|
1506
|
-
boxen2(
|
|
1507
|
-
`${chalk3.green.bold("\u2713 Logged out successfully")}
|
|
1707
|
+
// node_modules/@inquirer/core/dist/esm/lib/errors.mjs
|
|
1708
|
+
init_esm_shims();
|
|
1709
|
+
var CancelPromptError = class extends Error {
|
|
1710
|
+
name = "CancelPromptError";
|
|
1711
|
+
message = "Prompt was canceled";
|
|
1712
|
+
};
|
|
1508
1713
|
|
|
1509
|
-
|
|
1714
|
+
// src/commands/sync.ts
|
|
1715
|
+
init_api_service();
|
|
1716
|
+
init_storage_service();
|
|
1717
|
+
init_auth_service();
|
|
1718
|
+
import boxen6 from "boxen";
|
|
1510
1719
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1720
|
+
// src/utils/source-detector.ts
|
|
1721
|
+
init_esm_shims();
|
|
1722
|
+
init_errors();
|
|
1723
|
+
import { exec as exec2 } from "child_process";
|
|
1724
|
+
import { promisify as promisify2 } from "util";
|
|
1725
|
+
var execAsync2 = promisify2(exec2);
|
|
1726
|
+
var SourceDetector = class {
|
|
1727
|
+
/**
|
|
1728
|
+
* Detect all possible sources from git remotes
|
|
1729
|
+
*/
|
|
1730
|
+
async detectSources() {
|
|
1731
|
+
const detected = [];
|
|
1732
|
+
try {
|
|
1733
|
+
const { stdout } = await execAsync2("git remote -v");
|
|
1734
|
+
const remotes = this.parseRemotes(stdout);
|
|
1735
|
+
for (const remote of remotes) {
|
|
1736
|
+
const source = this.parseRemoteUrl(remote.url);
|
|
1737
|
+
if (source) {
|
|
1738
|
+
const isAuthenticated = await this.checkAuthentication(source.type);
|
|
1739
|
+
detected.push({
|
|
1740
|
+
...source,
|
|
1741
|
+
remoteUrl: remote.url,
|
|
1742
|
+
isAuthenticated
|
|
1743
|
+
});
|
|
1517
1744
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1745
|
+
}
|
|
1746
|
+
} catch {
|
|
1747
|
+
throw new GitError("Not a git repository");
|
|
1748
|
+
}
|
|
1749
|
+
const recommended = this.selectRecommendedSource(detected);
|
|
1750
|
+
return { detected, recommended };
|
|
1524
1751
|
}
|
|
1525
|
-
|
|
1752
|
+
/**
|
|
1753
|
+
* Parse git remote -v output
|
|
1754
|
+
*/
|
|
1755
|
+
parseRemotes(output) {
|
|
1756
|
+
const lines = output.split("\n").filter(Boolean);
|
|
1757
|
+
const remotes = /* @__PURE__ */ new Map();
|
|
1758
|
+
for (const line of lines) {
|
|
1759
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+\(fetch\)$/);
|
|
1760
|
+
if (match && match[1] && match[2]) {
|
|
1761
|
+
remotes.set(match[1], match[2]);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
return Array.from(remotes.entries()).map(([name, url]) => ({ name, url }));
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Parse remote URL to detect source type
|
|
1768
|
+
*/
|
|
1769
|
+
parseRemoteUrl(url) {
|
|
1770
|
+
if (url.includes("github.com")) {
|
|
1771
|
+
const match = url.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
|
|
1772
|
+
if (match && match[1] && match[2]) {
|
|
1773
|
+
return {
|
|
1774
|
+
type: "github",
|
|
1775
|
+
host: "github.com",
|
|
1776
|
+
owner: match[1],
|
|
1777
|
+
repo: match[2]
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
if (url.includes("gitlab.com")) {
|
|
1782
|
+
const match = url.match(/gitlab\.com[:/]([^/]+)\/([^/.]+)/);
|
|
1783
|
+
if (match && match[1] && match[2]) {
|
|
1784
|
+
return {
|
|
1785
|
+
type: "gitlab",
|
|
1786
|
+
host: "gitlab.com",
|
|
1787
|
+
owner: match[1],
|
|
1788
|
+
repo: match[2]
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
if (url.includes("bitbucket.org")) {
|
|
1793
|
+
const match = url.match(/bitbucket\.org[:/]([^/]+)\/([^/.]+)/);
|
|
1794
|
+
if (match && match[1] && match[2]) {
|
|
1795
|
+
return {
|
|
1796
|
+
type: "bitbucket",
|
|
1797
|
+
host: "bitbucket.org",
|
|
1798
|
+
owner: match[1],
|
|
1799
|
+
repo: match[2]
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
if (url.match(/\/scm\/|bitbucket\./)) {
|
|
1804
|
+
const match = url.match(/([^/:]+)[:/]scm\/([^/]+)\/([^/.]+)/);
|
|
1805
|
+
if (match && match[1] && match[2] && match[3]) {
|
|
1806
|
+
return {
|
|
1807
|
+
type: "atlassian",
|
|
1808
|
+
host: match[1],
|
|
1809
|
+
owner: match[2],
|
|
1810
|
+
repo: match[3]
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
return null;
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Check if user is authenticated with a specific source
|
|
1818
|
+
*/
|
|
1819
|
+
async checkAuthentication(type) {
|
|
1820
|
+
try {
|
|
1821
|
+
if (type === "github") {
|
|
1822
|
+
await execAsync2("command gh auth status");
|
|
1823
|
+
return true;
|
|
1824
|
+
}
|
|
1825
|
+
return false;
|
|
1826
|
+
} catch {
|
|
1827
|
+
return false;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
/**
|
|
1831
|
+
* Select recommended source (prefer authenticated GitHub)
|
|
1832
|
+
*/
|
|
1833
|
+
selectRecommendedSource(sources) {
|
|
1834
|
+
const authenticated = sources.find((s) => s.isAuthenticated);
|
|
1835
|
+
if (authenticated) return authenticated.type;
|
|
1836
|
+
const github = sources.find((s) => s.type === "github");
|
|
1837
|
+
if (github) return "github";
|
|
1838
|
+
return sources[0]?.type;
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
var sourceDetector = new SourceDetector();
|
|
1526
1842
|
|
|
1527
|
-
// src/
|
|
1843
|
+
// src/sync/adapter-factory.ts
|
|
1528
1844
|
init_esm_shims();
|
|
1529
|
-
import boxen6 from "boxen";
|
|
1530
1845
|
|
|
1531
|
-
//
|
|
1846
|
+
// src/sync/github-adapter.ts
|
|
1532
1847
|
init_esm_shims();
|
|
1533
|
-
var CancelPromptError = class extends Error {
|
|
1534
|
-
name = "CancelPromptError";
|
|
1535
|
-
message = "Prompt was canceled";
|
|
1536
|
-
};
|
|
1537
1848
|
|
|
1538
1849
|
// src/services/github.service.ts
|
|
1539
1850
|
init_esm_shims();
|
|
1540
1851
|
init_errors();
|
|
1541
1852
|
init_logger();
|
|
1542
|
-
import { exec as
|
|
1543
|
-
import { promisify as
|
|
1853
|
+
import { exec as exec3 } from "child_process";
|
|
1854
|
+
import { promisify as promisify3 } from "util";
|
|
1544
1855
|
|
|
1545
1856
|
// src/services/git.service.ts
|
|
1546
1857
|
init_esm_shims();
|
|
@@ -1550,9 +1861,9 @@ import simpleGit from "simple-git";
|
|
|
1550
1861
|
init_esm_shims();
|
|
1551
1862
|
init_errors();
|
|
1552
1863
|
import { existsSync as existsSync2 } from "fs";
|
|
1553
|
-
import { join as
|
|
1864
|
+
import { join as join6 } from "path";
|
|
1554
1865
|
function validateGitRepository(path2) {
|
|
1555
|
-
const gitDir =
|
|
1866
|
+
const gitDir = join6(path2, ".git");
|
|
1556
1867
|
if (!existsSync2(gitDir)) {
|
|
1557
1868
|
throw new GitError(
|
|
1558
1869
|
"Not a git repository. Please run this command from within a git repository.",
|
|
@@ -1765,7 +2076,7 @@ var GitService = class {
|
|
|
1765
2076
|
var gitService = new GitService();
|
|
1766
2077
|
|
|
1767
2078
|
// src/services/github.service.ts
|
|
1768
|
-
var
|
|
2079
|
+
var execAsync3 = promisify3(exec3);
|
|
1769
2080
|
var GitHubService = class {
|
|
1770
2081
|
MAX_BODY_LENGTH = 5e3;
|
|
1771
2082
|
PR_SEARCH_FIELDS = "number,title,body,author,mergedAt,additions,deletions,changedFiles,url,labels";
|
|
@@ -1774,7 +2085,7 @@ var GitHubService = class {
|
|
|
1774
2085
|
*/
|
|
1775
2086
|
async checkGitHubCLI() {
|
|
1776
2087
|
try {
|
|
1777
|
-
await
|
|
2088
|
+
await execAsync3("command gh --version");
|
|
1778
2089
|
return true;
|
|
1779
2090
|
} catch {
|
|
1780
2091
|
return false;
|
|
@@ -1796,7 +2107,7 @@ var GitHubService = class {
|
|
|
1796
2107
|
*/
|
|
1797
2108
|
async checkAuthentication() {
|
|
1798
2109
|
try {
|
|
1799
|
-
await
|
|
2110
|
+
await execAsync3("command gh auth status");
|
|
1800
2111
|
return true;
|
|
1801
2112
|
} catch {
|
|
1802
2113
|
return false;
|
|
@@ -1821,7 +2132,7 @@ var GitHubService = class {
|
|
|
1821
2132
|
await this.ensureGitHubCLI();
|
|
1822
2133
|
await this.ensureAuthentication();
|
|
1823
2134
|
await gitService.validateRepository();
|
|
1824
|
-
const { stdout } = await
|
|
2135
|
+
const { stdout } = await execAsync3("command gh repo view --json url");
|
|
1825
2136
|
const data = JSON.parse(stdout);
|
|
1826
2137
|
if (!data.url) {
|
|
1827
2138
|
throw new GitHubError("This repository is not hosted on GitHub", {
|
|
@@ -1853,7 +2164,7 @@ var GitHubService = class {
|
|
|
1853
2164
|
async getRepositoryInfo() {
|
|
1854
2165
|
try {
|
|
1855
2166
|
await this.ensureGitHubCLI();
|
|
1856
|
-
const { stdout } = await
|
|
2167
|
+
const { stdout } = await execAsync3(
|
|
1857
2168
|
"command gh repo view --json owner,name,url,nameWithOwner"
|
|
1858
2169
|
);
|
|
1859
2170
|
const data = JSON.parse(stdout);
|
|
@@ -1878,7 +2189,7 @@ var GitHubService = class {
|
|
|
1878
2189
|
*/
|
|
1879
2190
|
async getCurrentGitHubUser() {
|
|
1880
2191
|
try {
|
|
1881
|
-
const { stdout } = await
|
|
2192
|
+
const { stdout } = await execAsync3("command gh api user --jq .login");
|
|
1882
2193
|
return stdout.trim() || null;
|
|
1883
2194
|
} catch {
|
|
1884
2195
|
logger.debug("Failed to get GitHub user");
|
|
@@ -1904,7 +2215,7 @@ var GitHubService = class {
|
|
|
1904
2215
|
const limitArg = limit ? `--limit ${limit}` : "";
|
|
1905
2216
|
const command = `command gh pr list --state merged --json ${this.PR_SEARCH_FIELDS} --search "${searchQuery}" ${limitArg}`;
|
|
1906
2217
|
logger.debug(`Running: ${command}`);
|
|
1907
|
-
const { stdout } = await
|
|
2218
|
+
const { stdout } = await execAsync3(command);
|
|
1908
2219
|
const prs = JSON.parse(stdout);
|
|
1909
2220
|
logger.debug(`Found ${prs.length} merged PRs`);
|
|
1910
2221
|
return prs;
|
|
@@ -1962,189 +2273,219 @@ ${truncatedBody}` : title;
|
|
|
1962
2273
|
};
|
|
1963
2274
|
var githubService = new GitHubService();
|
|
1964
2275
|
|
|
1965
|
-
// src/
|
|
1966
|
-
|
|
1967
|
-
|
|
2276
|
+
// src/sync/github-adapter.ts
|
|
2277
|
+
var GitHubSyncAdapter = class {
|
|
2278
|
+
name = "github";
|
|
2279
|
+
async validate() {
|
|
2280
|
+
await githubService.validateGitHubRepository();
|
|
2281
|
+
}
|
|
2282
|
+
async getRepositoryInfo() {
|
|
2283
|
+
const info = await githubService.getRepositoryInfo();
|
|
2284
|
+
return {
|
|
2285
|
+
owner: info.owner,
|
|
2286
|
+
name: info.name,
|
|
2287
|
+
fullName: info.fullName,
|
|
2288
|
+
url: info.url
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
async fetchWorkItems(options) {
|
|
2292
|
+
let prs;
|
|
2293
|
+
if (options.author) {
|
|
2294
|
+
prs = await githubService.getMergedPRs({
|
|
2295
|
+
days: options.days,
|
|
2296
|
+
limit: options.limit,
|
|
2297
|
+
author: options.author
|
|
2298
|
+
});
|
|
2299
|
+
} else {
|
|
2300
|
+
prs = await githubService.getPRsByCurrentUser({
|
|
2301
|
+
days: options.days,
|
|
2302
|
+
limit: options.limit
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
return prs.map((pr) => githubService.transformPRToCommit(pr));
|
|
2306
|
+
}
|
|
2307
|
+
async isAuthenticated() {
|
|
2308
|
+
try {
|
|
2309
|
+
await githubService.validateGitHubRepository();
|
|
2310
|
+
return true;
|
|
2311
|
+
} catch {
|
|
2312
|
+
return false;
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
async getCurrentUser() {
|
|
2316
|
+
return githubService.getCurrentGitHubUser();
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
var githubSyncAdapter = new GitHubSyncAdapter();
|
|
2320
|
+
|
|
2321
|
+
// src/sync/adapter-factory.ts
|
|
2322
|
+
var AdapterFactory = class {
|
|
2323
|
+
/**
|
|
2324
|
+
* Get adapter for a specific source type
|
|
2325
|
+
*/
|
|
2326
|
+
static getAdapter(source) {
|
|
2327
|
+
switch (source) {
|
|
2328
|
+
case "github":
|
|
2329
|
+
return githubSyncAdapter;
|
|
2330
|
+
case "gitlab":
|
|
2331
|
+
case "bitbucket":
|
|
2332
|
+
case "atlassian":
|
|
2333
|
+
throw new Error(`${source} adapter not yet implemented`);
|
|
2334
|
+
default:
|
|
2335
|
+
throw new Error(`Unknown source type: ${source}`);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
/**
|
|
2339
|
+
* Check if adapter is available for source
|
|
2340
|
+
*/
|
|
2341
|
+
static isSupported(source) {
|
|
2342
|
+
return source === "github";
|
|
2343
|
+
}
|
|
2344
|
+
};
|
|
2345
|
+
|
|
2346
|
+
// src/commands/sync.ts
|
|
1968
2347
|
init_logger();
|
|
1969
|
-
init_browser();
|
|
1970
2348
|
|
|
1971
2349
|
// src/utils/auth-helper.ts
|
|
1972
2350
|
init_esm_shims();
|
|
1973
2351
|
init_auth_service();
|
|
1974
2352
|
import boxen5 from "boxen";
|
|
2353
|
+
|
|
2354
|
+
// src/commands/init.ts
|
|
2355
|
+
init_esm_shims();
|
|
2356
|
+
init_auth_service();
|
|
2357
|
+
init_logger();
|
|
2358
|
+
import ora from "ora";
|
|
2359
|
+
import boxen3 from "boxen";
|
|
2360
|
+
import chalk5 from "chalk";
|
|
2361
|
+
async function initCommand() {
|
|
2362
|
+
logger.log("");
|
|
2363
|
+
logger.log(
|
|
2364
|
+
boxen3(
|
|
2365
|
+
chalk5.yellow.bold("\u26A0 Deprecation Notice") + `
|
|
2366
|
+
|
|
2367
|
+
The ${chalk5.cyan("init")} command is deprecated.
|
|
2368
|
+
Please use ${chalk5.cyan("bragduck auth login")} instead.
|
|
2369
|
+
|
|
2370
|
+
` + chalk5.dim("This command will be removed in v3.0.0"),
|
|
2371
|
+
{
|
|
2372
|
+
padding: 1,
|
|
2373
|
+
borderStyle: "round",
|
|
2374
|
+
borderColor: "yellow",
|
|
2375
|
+
dimBorder: true
|
|
2376
|
+
}
|
|
2377
|
+
)
|
|
2378
|
+
);
|
|
2379
|
+
logger.log("");
|
|
2380
|
+
logger.info("Starting authentication flow...");
|
|
2381
|
+
logger.log("");
|
|
2382
|
+
const isAuthenticated = await authService.isAuthenticated();
|
|
2383
|
+
if (isAuthenticated) {
|
|
2384
|
+
const userInfo = authService.getUserInfo();
|
|
2385
|
+
if (userInfo) {
|
|
2386
|
+
logger.log(
|
|
2387
|
+
boxen3(
|
|
2388
|
+
`${chalk5.yellow("Already authenticated!")}
|
|
2389
|
+
|
|
2390
|
+
${chalk5.gray("User:")} ${userInfo.name}
|
|
2391
|
+
${chalk5.gray("Email:")} ${userInfo.email}
|
|
2392
|
+
|
|
2393
|
+
${chalk5.dim("Run")} ${chalk5.cyan("bragduck logout")} ${chalk5.dim("to sign out")}`,
|
|
2394
|
+
{
|
|
2395
|
+
padding: 1,
|
|
2396
|
+
margin: 1,
|
|
2397
|
+
borderStyle: "round",
|
|
2398
|
+
borderColor: "yellow"
|
|
2399
|
+
}
|
|
2400
|
+
)
|
|
2401
|
+
);
|
|
2402
|
+
logger.log("");
|
|
2403
|
+
globalThis.setTimeout(() => {
|
|
2404
|
+
process.exit(0);
|
|
2405
|
+
}, 100);
|
|
2406
|
+
return;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
const spinner = ora("Opening browser for authentication...").start();
|
|
2410
|
+
try {
|
|
2411
|
+
spinner.text = "Waiting for authentication...";
|
|
2412
|
+
const userInfo = await authService.login();
|
|
2413
|
+
spinner.succeed("Authentication successful!");
|
|
2414
|
+
logger.log("");
|
|
2415
|
+
logger.log(
|
|
2416
|
+
boxen3(
|
|
2417
|
+
`${chalk5.green.bold("\u2713 Successfully authenticated!")}
|
|
2418
|
+
|
|
2419
|
+
${chalk5.gray("Welcome,")} ${chalk5.cyan(userInfo.name)}
|
|
2420
|
+
${chalk5.gray("Email:")} ${userInfo.email}
|
|
2421
|
+
|
|
2422
|
+
${chalk5.dim("You can now use")} ${chalk5.cyan("bragduck scan")} ${chalk5.dim("to create brags!")}`,
|
|
2423
|
+
{
|
|
2424
|
+
padding: 1,
|
|
2425
|
+
margin: 1,
|
|
2426
|
+
borderStyle: "round",
|
|
2427
|
+
borderColor: "green"
|
|
2428
|
+
}
|
|
2429
|
+
)
|
|
2430
|
+
);
|
|
2431
|
+
logger.log("");
|
|
2432
|
+
globalThis.setTimeout(() => {
|
|
2433
|
+
process.exit(0);
|
|
2434
|
+
}, 100);
|
|
2435
|
+
return;
|
|
2436
|
+
} catch (error) {
|
|
2437
|
+
spinner.fail("Authentication failed");
|
|
2438
|
+
logger.log("");
|
|
2439
|
+
const err = error;
|
|
2440
|
+
logger.log(
|
|
2441
|
+
boxen3(
|
|
2442
|
+
`${chalk5.red.bold("\u2717 Authentication Failed")}
|
|
2443
|
+
|
|
2444
|
+
${err.message}
|
|
2445
|
+
|
|
2446
|
+
${chalk5.dim("Hint:")} ${getErrorHint(err)}`,
|
|
2447
|
+
{
|
|
2448
|
+
padding: 1,
|
|
2449
|
+
margin: 1,
|
|
2450
|
+
borderStyle: "round",
|
|
2451
|
+
borderColor: "red"
|
|
2452
|
+
}
|
|
2453
|
+
)
|
|
2454
|
+
);
|
|
2455
|
+
process.exit(1);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
function getErrorHint(error) {
|
|
2459
|
+
if (error.name === "OAuthError") {
|
|
2460
|
+
if (error.message.includes("timeout")) {
|
|
2461
|
+
return "Try again and complete the authentication within 2 minutes";
|
|
2462
|
+
}
|
|
2463
|
+
if (error.message.includes("CSRF")) {
|
|
2464
|
+
return "This might be a security issue. Try running the command again";
|
|
2465
|
+
}
|
|
2466
|
+
return "Check your internet connection and try again";
|
|
2467
|
+
}
|
|
2468
|
+
if (error.name === "NetworkError") {
|
|
2469
|
+
return "Check your internet connection and firewall settings";
|
|
2470
|
+
}
|
|
2471
|
+
if (error.name === "AuthenticationError") {
|
|
2472
|
+
return "Verify your credentials and try again";
|
|
2473
|
+
}
|
|
2474
|
+
return "Try running the command again or check the logs with DEBUG=* bragduck init";
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
// src/utils/auth-helper.ts
|
|
1975
2478
|
init_logger();
|
|
1976
2479
|
|
|
1977
2480
|
// src/ui/prompts.ts
|
|
1978
2481
|
init_esm_shims();
|
|
1979
|
-
import { checkbox, confirm
|
|
2482
|
+
import { checkbox, confirm, input, select, editor } from "@inquirer/prompts";
|
|
1980
2483
|
import boxen4 from "boxen";
|
|
1981
2484
|
|
|
1982
2485
|
// src/ui/formatters.ts
|
|
1983
2486
|
init_esm_shims();
|
|
1984
2487
|
import Table from "cli-table3";
|
|
1985
2488
|
import terminalLink from "terminal-link";
|
|
1986
|
-
|
|
1987
|
-
// src/ui/theme.ts
|
|
1988
|
-
init_esm_shims();
|
|
1989
|
-
import chalk5 from "chalk";
|
|
1990
|
-
var colors = {
|
|
1991
|
-
// Primary colors for main actions and interactive elements
|
|
1992
|
-
primary: chalk5.cyan,
|
|
1993
|
-
// Success states
|
|
1994
|
-
success: chalk5.green,
|
|
1995
|
-
successBold: chalk5.green.bold,
|
|
1996
|
-
// Warning states
|
|
1997
|
-
warning: chalk5.yellow,
|
|
1998
|
-
warningBold: chalk5.yellow.bold,
|
|
1999
|
-
// Error states
|
|
2000
|
-
error: chalk5.red,
|
|
2001
|
-
errorBold: chalk5.red.bold,
|
|
2002
|
-
// Info and metadata
|
|
2003
|
-
info: chalk5.gray,
|
|
2004
|
-
infoDim: chalk5.dim,
|
|
2005
|
-
// Highlighted/important data
|
|
2006
|
-
highlight: chalk5.yellow.bold,
|
|
2007
|
-
highlightCyan: chalk5.cyan.bold,
|
|
2008
|
-
// Text emphasis
|
|
2009
|
-
bold: chalk5.bold,
|
|
2010
|
-
dim: chalk5.dim,
|
|
2011
|
-
// Special purpose
|
|
2012
|
-
white: chalk5.white,
|
|
2013
|
-
gray: chalk5.gray,
|
|
2014
|
-
// Links and URLs
|
|
2015
|
-
link: chalk5.blue.underline
|
|
2016
|
-
};
|
|
2017
|
-
var theme = {
|
|
2018
|
-
/**
|
|
2019
|
-
* Format command names and CLI actions
|
|
2020
|
-
*/
|
|
2021
|
-
command: (text) => colors.primary(text),
|
|
2022
|
-
/**
|
|
2023
|
-
* Format success messages
|
|
2024
|
-
*/
|
|
2025
|
-
success: (text) => colors.success(`\u2713 ${text}`),
|
|
2026
|
-
successBold: (text) => colors.successBold(`\u2713 ${text}`),
|
|
2027
|
-
/**
|
|
2028
|
-
* Format error messages
|
|
2029
|
-
*/
|
|
2030
|
-
error: (text) => colors.error(`\u2717 ${text}`),
|
|
2031
|
-
errorBold: (text) => colors.errorBold(`\u2717 ${text}`),
|
|
2032
|
-
/**
|
|
2033
|
-
* Format warning messages
|
|
2034
|
-
*/
|
|
2035
|
-
warning: (text) => colors.warning(`\u26A0 ${text}`),
|
|
2036
|
-
/**
|
|
2037
|
-
* Format info messages
|
|
2038
|
-
*/
|
|
2039
|
-
info: (text) => colors.info(text),
|
|
2040
|
-
/**
|
|
2041
|
-
* Format labels (e.g., "User:", "Email:")
|
|
2042
|
-
*/
|
|
2043
|
-
label: (text) => colors.gray(`${text}:`),
|
|
2044
|
-
/**
|
|
2045
|
-
* Format values
|
|
2046
|
-
*/
|
|
2047
|
-
value: (text) => colors.highlight(text),
|
|
2048
|
-
/**
|
|
2049
|
-
* Format counts and numbers
|
|
2050
|
-
*/
|
|
2051
|
-
count: (num) => colors.highlightCyan(num.toString()),
|
|
2052
|
-
/**
|
|
2053
|
-
* Format file paths
|
|
2054
|
-
*/
|
|
2055
|
-
path: (text) => colors.info(text),
|
|
2056
|
-
/**
|
|
2057
|
-
* Format step indicators (e.g., "Step 1/5:")
|
|
2058
|
-
*/
|
|
2059
|
-
step: (current, total) => colors.primary(`[${current}/${total}]`),
|
|
2060
|
-
/**
|
|
2061
|
-
* Format progress text
|
|
2062
|
-
*/
|
|
2063
|
-
progress: (text) => colors.infoDim(text),
|
|
2064
|
-
/**
|
|
2065
|
-
* Format emphasized text
|
|
2066
|
-
*/
|
|
2067
|
-
emphasis: (text) => colors.bold(text),
|
|
2068
|
-
/**
|
|
2069
|
-
* Format de-emphasized/secondary text
|
|
2070
|
-
*/
|
|
2071
|
-
secondary: (text) => colors.dim(text),
|
|
2072
|
-
/**
|
|
2073
|
-
* Format interactive elements (prompts, selections)
|
|
2074
|
-
*/
|
|
2075
|
-
interactive: (text) => colors.primary(text),
|
|
2076
|
-
/**
|
|
2077
|
-
* Format keys in key-value pairs
|
|
2078
|
-
*/
|
|
2079
|
-
key: (text) => colors.white(text)
|
|
2080
|
-
};
|
|
2081
|
-
var boxStyles = {
|
|
2082
|
-
success: {
|
|
2083
|
-
borderColor: "green",
|
|
2084
|
-
borderStyle: "round",
|
|
2085
|
-
padding: 1,
|
|
2086
|
-
margin: 1
|
|
2087
|
-
},
|
|
2088
|
-
error: {
|
|
2089
|
-
borderColor: "red",
|
|
2090
|
-
borderStyle: "round",
|
|
2091
|
-
padding: 1,
|
|
2092
|
-
margin: 1
|
|
2093
|
-
},
|
|
2094
|
-
warning: {
|
|
2095
|
-
borderColor: "yellow",
|
|
2096
|
-
borderStyle: "round",
|
|
2097
|
-
padding: 1,
|
|
2098
|
-
margin: 1
|
|
2099
|
-
},
|
|
2100
|
-
info: {
|
|
2101
|
-
borderColor: "cyan",
|
|
2102
|
-
borderStyle: "round",
|
|
2103
|
-
padding: 1,
|
|
2104
|
-
margin: 1
|
|
2105
|
-
},
|
|
2106
|
-
plain: {
|
|
2107
|
-
borderColor: "gray",
|
|
2108
|
-
borderStyle: "round",
|
|
2109
|
-
padding: 1,
|
|
2110
|
-
margin: 1
|
|
2111
|
-
}
|
|
2112
|
-
};
|
|
2113
|
-
var tableStyles = {
|
|
2114
|
-
default: {
|
|
2115
|
-
style: {
|
|
2116
|
-
head: [],
|
|
2117
|
-
border: ["gray"]
|
|
2118
|
-
}
|
|
2119
|
-
},
|
|
2120
|
-
compact: {
|
|
2121
|
-
style: {
|
|
2122
|
-
head: [],
|
|
2123
|
-
border: ["dim"],
|
|
2124
|
-
compact: true
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
};
|
|
2128
|
-
var sizeIndicators = {
|
|
2129
|
-
small: colors.success("\u25CF"),
|
|
2130
|
-
medium: colors.warning("\u25CF"),
|
|
2131
|
-
large: colors.error("\u25CF")
|
|
2132
|
-
};
|
|
2133
|
-
function getSizeIndicator(insertions, deletions) {
|
|
2134
|
-
const total = insertions + deletions;
|
|
2135
|
-
if (total < 50) {
|
|
2136
|
-
return `${sizeIndicators.small} Small`;
|
|
2137
|
-
} else if (total < 200) {
|
|
2138
|
-
return `${sizeIndicators.medium} Medium`;
|
|
2139
|
-
} else {
|
|
2140
|
-
return `${sizeIndicators.large} Large`;
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
function formatDiffStats(insertions, deletions) {
|
|
2144
|
-
return `${colors.success(`+${insertions}`)} ${colors.error(`-${deletions}`)}`;
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
// src/ui/formatters.ts
|
|
2148
2489
|
function formatCommitChoice(commit) {
|
|
2149
2490
|
let displaySha;
|
|
2150
2491
|
if (commit.sha.startsWith("pr-")) {
|
|
@@ -2281,7 +2622,7 @@ async function promptSelectCommits(commits) {
|
|
|
2281
2622
|
return selected;
|
|
2282
2623
|
}
|
|
2283
2624
|
async function promptConfirm(message, defaultValue = true) {
|
|
2284
|
-
return await
|
|
2625
|
+
return await confirm({
|
|
2285
2626
|
message,
|
|
2286
2627
|
default: defaultValue
|
|
2287
2628
|
});
|
|
@@ -2420,123 +2761,465 @@ ${theme.label("PR Link")} ${colors.link(prUrl)}`;
|
|
|
2420
2761
|
for (let j = i + 1; j < refinedBrags.length; j++) {
|
|
2421
2762
|
acceptedBrags.push(refinedBrags[j]);
|
|
2422
2763
|
}
|
|
2423
|
-
reviewingBrag = false;
|
|
2424
|
-
i = refinedBrags.length - 1;
|
|
2425
|
-
break;
|
|
2426
|
-
}
|
|
2427
|
-
if (action === "skip") {
|
|
2428
|
-
reviewingBrag = false;
|
|
2429
|
-
continue;
|
|
2430
|
-
}
|
|
2431
|
-
if (action === "accept") {
|
|
2432
|
-
acceptedBrags.push(currentBrag);
|
|
2433
|
-
reviewingBrag = false;
|
|
2434
|
-
continue;
|
|
2435
|
-
}
|
|
2436
|
-
let editedBrag = { ...currentBrag };
|
|
2437
|
-
if (action === "edit-title" || action === "edit-both") {
|
|
2438
|
-
console.log("");
|
|
2439
|
-
const newTitle = await input({
|
|
2440
|
-
message: "Enter new title:",
|
|
2441
|
-
default: currentBrag.refined_title
|
|
2442
|
-
});
|
|
2443
|
-
editedBrag.refined_title = newTitle;
|
|
2444
|
-
}
|
|
2445
|
-
if (action === "edit-desc" || action === "edit-both") {
|
|
2446
|
-
console.log("");
|
|
2447
|
-
const newDesc = await editor({
|
|
2448
|
-
message: "Edit description (will open your default editor):",
|
|
2449
|
-
default: currentBrag.refined_description
|
|
2450
|
-
});
|
|
2451
|
-
editedBrag.refined_description = newDesc;
|
|
2764
|
+
reviewingBrag = false;
|
|
2765
|
+
i = refinedBrags.length - 1;
|
|
2766
|
+
break;
|
|
2767
|
+
}
|
|
2768
|
+
if (action === "skip") {
|
|
2769
|
+
reviewingBrag = false;
|
|
2770
|
+
continue;
|
|
2771
|
+
}
|
|
2772
|
+
if (action === "accept") {
|
|
2773
|
+
acceptedBrags.push(currentBrag);
|
|
2774
|
+
reviewingBrag = false;
|
|
2775
|
+
continue;
|
|
2776
|
+
}
|
|
2777
|
+
let editedBrag = { ...currentBrag };
|
|
2778
|
+
if (action === "edit-title" || action === "edit-both") {
|
|
2779
|
+
console.log("");
|
|
2780
|
+
const newTitle = await input({
|
|
2781
|
+
message: "Enter new title:",
|
|
2782
|
+
default: currentBrag.refined_title
|
|
2783
|
+
});
|
|
2784
|
+
editedBrag.refined_title = newTitle;
|
|
2785
|
+
}
|
|
2786
|
+
if (action === "edit-desc" || action === "edit-both") {
|
|
2787
|
+
console.log("");
|
|
2788
|
+
const newDesc = await editor({
|
|
2789
|
+
message: "Edit description (will open your default editor):",
|
|
2790
|
+
default: currentBrag.refined_description
|
|
2791
|
+
});
|
|
2792
|
+
editedBrag.refined_description = newDesc;
|
|
2793
|
+
}
|
|
2794
|
+
currentBrag = editedBrag;
|
|
2795
|
+
console.log("\n" + theme.success("\u2713 Changes saved. Review your edits:") + "\n");
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
return acceptedBrags;
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
// src/utils/auth-helper.ts
|
|
2802
|
+
async function ensureAuthenticated() {
|
|
2803
|
+
const isAuthenticated = await authService.isAuthenticated();
|
|
2804
|
+
if (isAuthenticated) {
|
|
2805
|
+
return true;
|
|
2806
|
+
}
|
|
2807
|
+
logger.log("");
|
|
2808
|
+
logger.log(
|
|
2809
|
+
boxen5(
|
|
2810
|
+
theme.warning("Not authenticated") + "\n\nYou need to be logged in to use this command.\n\nWould you like to authenticate now?",
|
|
2811
|
+
boxStyles.warning
|
|
2812
|
+
)
|
|
2813
|
+
);
|
|
2814
|
+
logger.log("");
|
|
2815
|
+
const shouldAuth = await promptConfirm("Authenticate now?", true);
|
|
2816
|
+
if (!shouldAuth) {
|
|
2817
|
+
logger.log("");
|
|
2818
|
+
logger.info(
|
|
2819
|
+
theme.secondary("Authentication skipped. Run ") + theme.command("bragduck init") + theme.secondary(" when you're ready to authenticate.")
|
|
2820
|
+
);
|
|
2821
|
+
logger.log("");
|
|
2822
|
+
return false;
|
|
2823
|
+
}
|
|
2824
|
+
try {
|
|
2825
|
+
await initCommand();
|
|
2826
|
+
return true;
|
|
2827
|
+
} catch {
|
|
2828
|
+
return false;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
// src/ui/spinners.ts
|
|
2833
|
+
init_esm_shims();
|
|
2834
|
+
import ora2 from "ora";
|
|
2835
|
+
function createSpinner(text) {
|
|
2836
|
+
return ora2({
|
|
2837
|
+
text,
|
|
2838
|
+
color: "cyan",
|
|
2839
|
+
spinner: "dots"
|
|
2840
|
+
});
|
|
2841
|
+
}
|
|
2842
|
+
function createStepSpinner(currentStep, totalSteps, text) {
|
|
2843
|
+
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2844
|
+
return ora2({
|
|
2845
|
+
text: `${stepIndicator} ${text}`,
|
|
2846
|
+
color: "cyan",
|
|
2847
|
+
spinner: "dots"
|
|
2848
|
+
});
|
|
2849
|
+
}
|
|
2850
|
+
function fetchingBragsSpinner() {
|
|
2851
|
+
return createSpinner("Fetching your brags...");
|
|
2852
|
+
}
|
|
2853
|
+
function succeedSpinner(spinner, text) {
|
|
2854
|
+
if (text) {
|
|
2855
|
+
spinner.succeed(colors.success(text));
|
|
2856
|
+
} else {
|
|
2857
|
+
spinner.succeed();
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
function failSpinner(spinner, text) {
|
|
2861
|
+
if (text) {
|
|
2862
|
+
spinner.fail(colors.error(text));
|
|
2863
|
+
} else {
|
|
2864
|
+
spinner.fail();
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
function succeedStepSpinner(spinner, currentStep, totalSteps, text) {
|
|
2868
|
+
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2869
|
+
spinner.succeed(`${stepIndicator} ${colors.success(text)}`);
|
|
2870
|
+
}
|
|
2871
|
+
function failStepSpinner(spinner, currentStep, totalSteps, text) {
|
|
2872
|
+
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2873
|
+
spinner.fail(`${stepIndicator} ${colors.error(text)}`);
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
// src/commands/sync.ts
|
|
2877
|
+
async function syncCommand(options = {}) {
|
|
2878
|
+
logger.log("");
|
|
2879
|
+
const TOTAL_STEPS = 5;
|
|
2880
|
+
try {
|
|
2881
|
+
const isAuthenticated = await ensureAuthenticated();
|
|
2882
|
+
if (!isAuthenticated) {
|
|
2883
|
+
process.exit(1);
|
|
2884
|
+
}
|
|
2885
|
+
logger.debug("Fetching subscription status...");
|
|
2886
|
+
const subscriptionStatus = await apiService.getSubscriptionStatus();
|
|
2887
|
+
logger.debug("Subscription status response:", JSON.stringify(subscriptionStatus, null, 2));
|
|
2888
|
+
if (subscriptionStatus.tier === "FREE") {
|
|
2889
|
+
logger.debug("FREE tier detected - blocking sync command");
|
|
2890
|
+
logger.log("");
|
|
2891
|
+
logger.log(
|
|
2892
|
+
boxen6(
|
|
2893
|
+
theme.warning("CLI Access Requires Subscription") + "\n\nThe Bragduck CLI is available for Plus and Pro subscribers.\nUpgrade now to unlock:\n\n \u2022 Automatic work item scanning\n \u2022 AI-powered brag generation\n \u2022 Unlimited brags\n\n" + colors.highlight("Start your free trial today"),
|
|
2894
|
+
{
|
|
2895
|
+
...boxStyles.warning,
|
|
2896
|
+
padding: 1,
|
|
2897
|
+
margin: 1
|
|
2898
|
+
}
|
|
2899
|
+
)
|
|
2900
|
+
);
|
|
2901
|
+
logger.log("");
|
|
2902
|
+
return;
|
|
2903
|
+
}
|
|
2904
|
+
logger.debug(`Subscription tier "${subscriptionStatus.tier}" - proceeding with sync`);
|
|
2905
|
+
const detectionSpinner = createStepSpinner(1, TOTAL_STEPS, "Detecting repository source");
|
|
2906
|
+
detectionSpinner.start();
|
|
2907
|
+
const detectionResult = await sourceDetector.detectSources();
|
|
2908
|
+
if (detectionResult.detected.length === 0) {
|
|
2909
|
+
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, "No supported sources detected");
|
|
2910
|
+
logger.log("");
|
|
2911
|
+
logger.info("Make sure you are in a git repository with a remote URL");
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
const sourceType = options.source || detectionResult.recommended;
|
|
2915
|
+
if (!sourceType) {
|
|
2916
|
+
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, "Could not determine source");
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2919
|
+
if (!AdapterFactory.isSupported(sourceType)) {
|
|
2920
|
+
failStepSpinner(detectionSpinner, 1, TOTAL_STEPS, `Source ${sourceType} not yet supported`);
|
|
2921
|
+
logger.log("");
|
|
2922
|
+
logger.info(`Currently supported: GitHub`);
|
|
2923
|
+
logger.info(`Coming soon: GitLab, Atlassian, Bitbucket`);
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
succeedStepSpinner(detectionSpinner, 1, TOTAL_STEPS, `Source: ${theme.value(sourceType)}`);
|
|
2927
|
+
logger.log("");
|
|
2928
|
+
const adapter = AdapterFactory.getAdapter(sourceType);
|
|
2929
|
+
const repoSpinner = createStepSpinner(2, TOTAL_STEPS, "Validating repository");
|
|
2930
|
+
repoSpinner.start();
|
|
2931
|
+
await adapter.validate();
|
|
2932
|
+
const repoInfo = await adapter.getRepositoryInfo();
|
|
2933
|
+
succeedStepSpinner(
|
|
2934
|
+
repoSpinner,
|
|
2935
|
+
2,
|
|
2936
|
+
TOTAL_STEPS,
|
|
2937
|
+
`Repository: ${theme.value(repoInfo.fullName)}`
|
|
2938
|
+
);
|
|
2939
|
+
logger.log("");
|
|
2940
|
+
let days = options.days;
|
|
2941
|
+
if (!days) {
|
|
2942
|
+
const defaultDays = storageService.getConfig("defaultCommitDays");
|
|
2943
|
+
days = await promptDaysToScan(defaultDays);
|
|
2944
|
+
logger.log("");
|
|
2945
|
+
}
|
|
2946
|
+
const fetchSpinner = createStepSpinner(
|
|
2947
|
+
3,
|
|
2948
|
+
TOTAL_STEPS,
|
|
2949
|
+
`Fetching work items from the last ${days} days`
|
|
2950
|
+
);
|
|
2951
|
+
fetchSpinner.start();
|
|
2952
|
+
const workItems = await adapter.fetchWorkItems({
|
|
2953
|
+
days,
|
|
2954
|
+
author: options.all ? void 0 : await adapter.getCurrentUser() || void 0
|
|
2955
|
+
});
|
|
2956
|
+
if (workItems.length === 0) {
|
|
2957
|
+
failStepSpinner(fetchSpinner, 3, TOTAL_STEPS, `No work items found in the last ${days} days`);
|
|
2958
|
+
logger.log("");
|
|
2959
|
+
logger.info("Try increasing the number of days or check your activity");
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
succeedStepSpinner(
|
|
2963
|
+
fetchSpinner,
|
|
2964
|
+
3,
|
|
2965
|
+
TOTAL_STEPS,
|
|
2966
|
+
`Found ${theme.count(workItems.length)} work item${workItems.length > 1 ? "s" : ""}`
|
|
2967
|
+
);
|
|
2968
|
+
logger.log("");
|
|
2969
|
+
logger.log(formatCommitStats(workItems));
|
|
2970
|
+
logger.log("");
|
|
2971
|
+
let sortedCommits = [...workItems];
|
|
2972
|
+
if (workItems.length > 1) {
|
|
2973
|
+
const sortOption = await promptSortOption();
|
|
2974
|
+
logger.log("");
|
|
2975
|
+
if (sortOption === "date") {
|
|
2976
|
+
sortedCommits.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
2977
|
+
} else if (sortOption === "size") {
|
|
2978
|
+
sortedCommits.sort((a, b) => {
|
|
2979
|
+
const sizeA = (a.diffStats?.insertions || 0) + (a.diffStats?.deletions || 0);
|
|
2980
|
+
const sizeB = (b.diffStats?.insertions || 0) + (b.diffStats?.deletions || 0);
|
|
2981
|
+
return sizeB - sizeA;
|
|
2982
|
+
});
|
|
2983
|
+
} else if (sortOption === "files") {
|
|
2984
|
+
sortedCommits.sort((a, b) => {
|
|
2985
|
+
const filesA = a.diffStats?.filesChanged || 0;
|
|
2986
|
+
const filesB = b.diffStats?.filesChanged || 0;
|
|
2987
|
+
return filesB - filesA;
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
const selectedShas = await promptSelectCommits(sortedCommits);
|
|
2992
|
+
if (selectedShas.length === 0) {
|
|
2993
|
+
logger.log("");
|
|
2994
|
+
logger.info(theme.secondary("No work items selected. Sync cancelled."));
|
|
2995
|
+
logger.log("");
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2998
|
+
const selectedCommits = sortedCommits.filter((c) => selectedShas.includes(c.sha));
|
|
2999
|
+
logger.log(formatSelectionSummary(selectedCommits.length, selectedCommits));
|
|
3000
|
+
logger.log("");
|
|
3001
|
+
const existingBrags = await apiService.listBrags({ limit: 100 });
|
|
3002
|
+
logger.debug(`Fetched ${existingBrags.brags.length} existing brags`);
|
|
3003
|
+
const existingUrls = new Set(existingBrags.brags.flatMap((b) => b.attachments || []));
|
|
3004
|
+
logger.debug(`Existing URLs in attachments: ${existingUrls.size}`);
|
|
3005
|
+
const duplicates = selectedCommits.filter((c) => c.url && existingUrls.has(c.url));
|
|
3006
|
+
const newCommits = selectedCommits.filter((c) => !c.url || !existingUrls.has(c.url));
|
|
3007
|
+
logger.debug(`Duplicates: ${duplicates.length}, New: ${newCommits.length}`);
|
|
3008
|
+
if (duplicates.length > 0) {
|
|
3009
|
+
logger.log("");
|
|
3010
|
+
logger.info(
|
|
3011
|
+
colors.warning(
|
|
3012
|
+
`${duplicates.length} work item${duplicates.length > 1 ? "s" : ""} already added to Bragduck - skipping`
|
|
3013
|
+
)
|
|
3014
|
+
);
|
|
3015
|
+
logger.log("");
|
|
3016
|
+
}
|
|
3017
|
+
if (newCommits.length === 0) {
|
|
3018
|
+
logger.log("");
|
|
3019
|
+
logger.info(
|
|
3020
|
+
theme.secondary("All selected work items already exist in Bragduck. Nothing to refine.")
|
|
3021
|
+
);
|
|
3022
|
+
logger.log("");
|
|
3023
|
+
return;
|
|
3024
|
+
}
|
|
3025
|
+
const refineSpinner = createStepSpinner(
|
|
3026
|
+
4,
|
|
3027
|
+
TOTAL_STEPS,
|
|
3028
|
+
`Refining ${theme.count(newCommits.length)} work item${newCommits.length > 1 ? "s" : ""} with AI`
|
|
3029
|
+
);
|
|
3030
|
+
refineSpinner.start();
|
|
3031
|
+
const refineRequest = {
|
|
3032
|
+
brags: newCommits.map((c) => ({
|
|
3033
|
+
text: c.message,
|
|
3034
|
+
date: c.date,
|
|
3035
|
+
title: c.message.split("\n")[0]
|
|
3036
|
+
}))
|
|
3037
|
+
};
|
|
3038
|
+
const refineResponse = await apiService.refineBrags(refineRequest);
|
|
3039
|
+
let refinedBrags = refineResponse.refined_brags;
|
|
3040
|
+
succeedStepSpinner(refineSpinner, 4, TOTAL_STEPS, "Work items refined successfully");
|
|
3041
|
+
logger.log("");
|
|
3042
|
+
logger.info("Preview of refined brags:");
|
|
3043
|
+
logger.log("");
|
|
3044
|
+
logger.log(formatRefinedCommitsTable(refinedBrags, newCommits));
|
|
3045
|
+
logger.log("");
|
|
3046
|
+
const acceptedBrags = await promptReviewBrags(refinedBrags, newCommits);
|
|
3047
|
+
if (acceptedBrags.length === 0) {
|
|
3048
|
+
logger.log("");
|
|
3049
|
+
logger.info(theme.secondary("No brags selected for creation. Sync cancelled."));
|
|
3050
|
+
logger.log("");
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
logger.log("");
|
|
3054
|
+
let selectedOrgId = null;
|
|
3055
|
+
const userInfo = authService.getUserInfo();
|
|
3056
|
+
if (userInfo?.id) {
|
|
3057
|
+
try {
|
|
3058
|
+
const orgsResponse = await apiService.listUserOrganisations(userInfo.id);
|
|
3059
|
+
if (orgsResponse.items.length > 0) {
|
|
3060
|
+
selectedOrgId = await promptSelectOrganisation(orgsResponse.items);
|
|
3061
|
+
logger.log("");
|
|
3062
|
+
}
|
|
3063
|
+
} catch {
|
|
3064
|
+
logger.debug("Failed to fetch organisations, skipping org selection");
|
|
2452
3065
|
}
|
|
2453
|
-
currentBrag = editedBrag;
|
|
2454
|
-
console.log("\n" + theme.success("\u2713 Changes saved. Review your edits:") + "\n");
|
|
2455
3066
|
}
|
|
3067
|
+
const createSpinner2 = createStepSpinner(
|
|
3068
|
+
5,
|
|
3069
|
+
TOTAL_STEPS,
|
|
3070
|
+
`Creating ${theme.count(acceptedBrags.length)} brag${acceptedBrags.length > 1 ? "s" : ""}`
|
|
3071
|
+
);
|
|
3072
|
+
createSpinner2.start();
|
|
3073
|
+
const createRequest = {
|
|
3074
|
+
brags: acceptedBrags.map((refined, index) => {
|
|
3075
|
+
const originalCommit = newCommits[index];
|
|
3076
|
+
return {
|
|
3077
|
+
commit_sha: originalCommit?.sha || `brag-${index}`,
|
|
3078
|
+
title: refined.refined_title,
|
|
3079
|
+
description: refined.refined_description,
|
|
3080
|
+
tags: refined.suggested_tags,
|
|
3081
|
+
repository: repoInfo.url,
|
|
3082
|
+
date: originalCommit?.date || "",
|
|
3083
|
+
commit_url: originalCommit?.url || "",
|
|
3084
|
+
impact_score: refined.suggested_impactLevel,
|
|
3085
|
+
impact_description: refined.impact_description,
|
|
3086
|
+
attachments: originalCommit?.url ? [originalCommit.url] : [],
|
|
3087
|
+
orgId: selectedOrgId || void 0
|
|
3088
|
+
};
|
|
3089
|
+
})
|
|
3090
|
+
};
|
|
3091
|
+
const createResponse = await apiService.createBrags(createRequest);
|
|
3092
|
+
succeedStepSpinner(
|
|
3093
|
+
createSpinner2,
|
|
3094
|
+
5,
|
|
3095
|
+
TOTAL_STEPS,
|
|
3096
|
+
`Created ${theme.count(createResponse.created)} brag${createResponse.created > 1 ? "s" : ""}`
|
|
3097
|
+
);
|
|
3098
|
+
logger.log("");
|
|
3099
|
+
logger.log(boxen6(formatSuccessMessage(createResponse.created), boxStyles.success));
|
|
3100
|
+
} catch (error) {
|
|
3101
|
+
if (error instanceof CancelPromptError) {
|
|
3102
|
+
logger.log("");
|
|
3103
|
+
logger.info(theme.secondary("Sync cancelled."));
|
|
3104
|
+
logger.log("");
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
const err = error;
|
|
3108
|
+
logger.log("");
|
|
3109
|
+
logger.log(boxen6(formatErrorMessage(err.message, getErrorHint2(err)), boxStyles.error));
|
|
3110
|
+
process.exit(1);
|
|
2456
3111
|
}
|
|
2457
|
-
return acceptedBrags;
|
|
2458
3112
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
const isAuthenticated = await authService.isAuthenticated();
|
|
2463
|
-
if (isAuthenticated) {
|
|
2464
|
-
return true;
|
|
3113
|
+
function getErrorHint2(error) {
|
|
3114
|
+
if (error.name === "GitHubError") {
|
|
3115
|
+
return 'Make sure you are in a GitHub repository and have authenticated with "gh auth login"';
|
|
2465
3116
|
}
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
boxen5(
|
|
2469
|
-
theme.warning("Not authenticated") + "\n\nYou need to be logged in to use this command.\n\nWould you like to authenticate now?",
|
|
2470
|
-
boxStyles.warning
|
|
2471
|
-
)
|
|
2472
|
-
);
|
|
2473
|
-
logger.log("");
|
|
2474
|
-
const shouldAuth = await promptConfirm("Authenticate now?", true);
|
|
2475
|
-
if (!shouldAuth) {
|
|
2476
|
-
logger.log("");
|
|
2477
|
-
logger.info(
|
|
2478
|
-
theme.secondary("Authentication skipped. Run ") + theme.command("bragduck init") + theme.secondary(" when you're ready to authenticate.")
|
|
2479
|
-
);
|
|
2480
|
-
logger.log("");
|
|
2481
|
-
return false;
|
|
3117
|
+
if (error.name === "GitError") {
|
|
3118
|
+
return "Make sure you are in a git repository";
|
|
2482
3119
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
return
|
|
3120
|
+
if (error.name === "TokenExpiredError" || error.name === "AuthenticationError") {
|
|
3121
|
+
return 'Run "bragduck auth login" to login again';
|
|
3122
|
+
}
|
|
3123
|
+
if (error.name === "NetworkError") {
|
|
3124
|
+
return "Check your internet connection and try again";
|
|
2488
3125
|
}
|
|
3126
|
+
if (error.name === "ApiError") {
|
|
3127
|
+
return "The server might be experiencing issues. Try again later";
|
|
3128
|
+
}
|
|
3129
|
+
return "Try running with DEBUG=* for more information";
|
|
2489
3130
|
}
|
|
2490
3131
|
|
|
2491
|
-
// src/commands/
|
|
2492
|
-
init_auth_service();
|
|
2493
|
-
|
|
2494
|
-
// src/ui/spinners.ts
|
|
3132
|
+
// src/commands/logout.ts
|
|
2495
3133
|
init_esm_shims();
|
|
3134
|
+
init_auth_service();
|
|
3135
|
+
init_logger();
|
|
3136
|
+
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
3137
|
+
import boxen7 from "boxen";
|
|
3138
|
+
import chalk6 from "chalk";
|
|
2496
3139
|
import ora3 from "ora";
|
|
2497
|
-
function
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
3140
|
+
async function logoutCommand() {
|
|
3141
|
+
const isAuthenticated = await authService.isAuthenticated();
|
|
3142
|
+
if (!isAuthenticated) {
|
|
3143
|
+
logger.log(
|
|
3144
|
+
boxen7(
|
|
3145
|
+
`${chalk6.yellow("Not currently authenticated")}
|
|
3146
|
+
|
|
3147
|
+
${chalk6.dim("Nothing to logout from")}`,
|
|
3148
|
+
{
|
|
3149
|
+
padding: 1,
|
|
3150
|
+
margin: 1,
|
|
3151
|
+
borderStyle: "round",
|
|
3152
|
+
borderColor: "yellow"
|
|
3153
|
+
}
|
|
3154
|
+
)
|
|
3155
|
+
);
|
|
3156
|
+
return;
|
|
3157
|
+
}
|
|
3158
|
+
const userInfo = authService.getUserInfo();
|
|
3159
|
+
const userName = userInfo?.name || "Unknown User";
|
|
3160
|
+
logger.log("");
|
|
3161
|
+
const shouldLogout = await confirm2({
|
|
3162
|
+
message: `Are you sure you want to logout? (${chalk6.cyan(userName)})`,
|
|
3163
|
+
default: false
|
|
2510
3164
|
});
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
}
|
|
2515
|
-
function succeedSpinner(spinner, text) {
|
|
2516
|
-
if (text) {
|
|
2517
|
-
spinner.succeed(colors.success(text));
|
|
2518
|
-
} else {
|
|
2519
|
-
spinner.succeed();
|
|
3165
|
+
if (!shouldLogout) {
|
|
3166
|
+
logger.info("Logout cancelled");
|
|
3167
|
+
return;
|
|
2520
3168
|
}
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
spinner.
|
|
2525
|
-
|
|
2526
|
-
|
|
3169
|
+
const spinner = ora3("Logging out...").start();
|
|
3170
|
+
try {
|
|
3171
|
+
await authService.logout();
|
|
3172
|
+
spinner.succeed("Logged out successfully");
|
|
3173
|
+
logger.log("");
|
|
3174
|
+
logger.log(
|
|
3175
|
+
boxen7(
|
|
3176
|
+
`${chalk6.green.bold("\u2713 Logged out successfully")}
|
|
3177
|
+
|
|
3178
|
+
${chalk6.dim("Your credentials have been cleared")}
|
|
3179
|
+
|
|
3180
|
+
${chalk6.dim("Run")} ${chalk6.cyan("bragduck init")} ${chalk6.dim("to login again")}`,
|
|
3181
|
+
{
|
|
3182
|
+
padding: 1,
|
|
3183
|
+
margin: 1,
|
|
3184
|
+
borderStyle: "round",
|
|
3185
|
+
borderColor: "green"
|
|
3186
|
+
}
|
|
3187
|
+
)
|
|
3188
|
+
);
|
|
3189
|
+
} catch {
|
|
3190
|
+
spinner.fail("Logout failed");
|
|
3191
|
+
logger.error("Failed to logout. Please try again.");
|
|
3192
|
+
process.exit(1);
|
|
2527
3193
|
}
|
|
2528
3194
|
}
|
|
2529
|
-
function succeedStepSpinner(spinner, currentStep, totalSteps, text) {
|
|
2530
|
-
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2531
|
-
spinner.succeed(`${stepIndicator} ${colors.success(text)}`);
|
|
2532
|
-
}
|
|
2533
|
-
function failStepSpinner(spinner, currentStep, totalSteps, text) {
|
|
2534
|
-
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2535
|
-
spinner.fail(`${stepIndicator} ${colors.error(text)}`);
|
|
2536
|
-
}
|
|
2537
3195
|
|
|
2538
3196
|
// src/commands/scan.ts
|
|
3197
|
+
init_esm_shims();
|
|
3198
|
+
import boxen8 from "boxen";
|
|
3199
|
+
import chalk7 from "chalk";
|
|
3200
|
+
init_api_service();
|
|
3201
|
+
init_storage_service();
|
|
3202
|
+
init_logger();
|
|
3203
|
+
init_browser();
|
|
3204
|
+
init_auth_service();
|
|
2539
3205
|
async function scanCommand(options = {}) {
|
|
3206
|
+
logger.log("");
|
|
3207
|
+
logger.log(
|
|
3208
|
+
boxen8(
|
|
3209
|
+
chalk7.yellow.bold("\u26A0 Deprecation Notice") + `
|
|
3210
|
+
|
|
3211
|
+
The ${chalk7.cyan("scan")} command is deprecated.
|
|
3212
|
+
Please use ${chalk7.cyan("bragduck sync")} instead.
|
|
3213
|
+
|
|
3214
|
+
` + chalk7.dim("This command will be removed in v3.0.0"),
|
|
3215
|
+
{
|
|
3216
|
+
padding: 1,
|
|
3217
|
+
borderStyle: "round",
|
|
3218
|
+
borderColor: "yellow",
|
|
3219
|
+
dimBorder: true
|
|
3220
|
+
}
|
|
3221
|
+
)
|
|
3222
|
+
);
|
|
2540
3223
|
logger.log("");
|
|
2541
3224
|
const TOTAL_STEPS = 5;
|
|
2542
3225
|
try {
|
|
@@ -2555,7 +3238,7 @@ async function scanCommand(options = {}) {
|
|
|
2555
3238
|
logger.debug("FREE tier detected - blocking scan command");
|
|
2556
3239
|
logger.log("");
|
|
2557
3240
|
logger.log(
|
|
2558
|
-
|
|
3241
|
+
boxen8(
|
|
2559
3242
|
theme.warning("CLI Access Requires Subscription") + "\n\nThe Bragduck CLI is available for Plus and Pro subscribers.\nUpgrade now to unlock:\n\n \u2022 Automatic PR scanning\n \u2022 AI-powered brag generation\n \u2022 Unlimited brags\n\n" + colors.highlight("Start your free trial today!"),
|
|
2560
3243
|
{
|
|
2561
3244
|
...boxStyles.warning,
|
|
@@ -2758,7 +3441,7 @@ async function scanCommand(options = {}) {
|
|
|
2758
3441
|
`Created ${theme.count(createResponse.created)} brag${createResponse.created > 1 ? "s" : ""}`
|
|
2759
3442
|
);
|
|
2760
3443
|
logger.log("");
|
|
2761
|
-
logger.log(
|
|
3444
|
+
logger.log(boxen8(formatSuccessMessage(createResponse.created), boxStyles.success));
|
|
2762
3445
|
} catch (error) {
|
|
2763
3446
|
if (error instanceof CancelPromptError) {
|
|
2764
3447
|
logger.log("");
|
|
@@ -2768,11 +3451,11 @@ async function scanCommand(options = {}) {
|
|
|
2768
3451
|
}
|
|
2769
3452
|
const err = error;
|
|
2770
3453
|
logger.log("");
|
|
2771
|
-
logger.log(
|
|
3454
|
+
logger.log(boxen8(formatErrorMessage(err.message, getErrorHint3(err)), boxStyles.error));
|
|
2772
3455
|
process.exit(1);
|
|
2773
3456
|
}
|
|
2774
3457
|
}
|
|
2775
|
-
function
|
|
3458
|
+
function getErrorHint3(error) {
|
|
2776
3459
|
if (error.name === "GitHubError") {
|
|
2777
3460
|
return 'Make sure you are in a GitHub repository and have authenticated with "gh auth login"';
|
|
2778
3461
|
}
|
|
@@ -2794,7 +3477,7 @@ function getErrorHint2(error) {
|
|
|
2794
3477
|
// src/commands/list.ts
|
|
2795
3478
|
init_esm_shims();
|
|
2796
3479
|
init_api_service();
|
|
2797
|
-
import
|
|
3480
|
+
import boxen9 from "boxen";
|
|
2798
3481
|
import Table2 from "cli-table3";
|
|
2799
3482
|
init_logger();
|
|
2800
3483
|
async function listCommand(options = {}) {
|
|
@@ -2861,7 +3544,7 @@ async function listCommand(options = {}) {
|
|
|
2861
3544
|
} catch (error) {
|
|
2862
3545
|
const err = error;
|
|
2863
3546
|
logger.log("");
|
|
2864
|
-
logger.log(
|
|
3547
|
+
logger.log(boxen9(formatErrorMessage(err.message, getErrorHint4(err)), boxStyles.error));
|
|
2865
3548
|
process.exit(1);
|
|
2866
3549
|
}
|
|
2867
3550
|
}
|
|
@@ -2914,7 +3597,7 @@ function extractRepoName(url) {
|
|
|
2914
3597
|
return url;
|
|
2915
3598
|
}
|
|
2916
3599
|
}
|
|
2917
|
-
function
|
|
3600
|
+
function getErrorHint4(error) {
|
|
2918
3601
|
if (error.name === "TokenExpiredError" || error.name === "AuthenticationError") {
|
|
2919
3602
|
return 'Run "bragduck init" to login again';
|
|
2920
3603
|
}
|
|
@@ -2932,8 +3615,8 @@ init_esm_shims();
|
|
|
2932
3615
|
init_storage_service();
|
|
2933
3616
|
init_logger();
|
|
2934
3617
|
init_constants();
|
|
2935
|
-
import
|
|
2936
|
-
import
|
|
3618
|
+
import boxen10 from "boxen";
|
|
3619
|
+
import chalk8 from "chalk";
|
|
2937
3620
|
import Table3 from "cli-table3";
|
|
2938
3621
|
init_errors();
|
|
2939
3622
|
var VALID_CONFIG_KEYS = Object.values(CONFIG_KEYS);
|
|
@@ -2968,7 +3651,7 @@ async function configCommand(subcommand, key, value) {
|
|
|
2968
3651
|
const err = error;
|
|
2969
3652
|
logger.log("");
|
|
2970
3653
|
logger.log(
|
|
2971
|
-
|
|
3654
|
+
boxen10(formatErrorMessage(err.message, getConfigHint(err)), {
|
|
2972
3655
|
padding: 1,
|
|
2973
3656
|
margin: 1,
|
|
2974
3657
|
borderStyle: "round",
|
|
@@ -2984,7 +3667,7 @@ async function handleListConfig() {
|
|
|
2984
3667
|
autoVersionCheck: storageService.getConfig("autoVersionCheck")
|
|
2985
3668
|
};
|
|
2986
3669
|
const table = new Table3({
|
|
2987
|
-
head: [
|
|
3670
|
+
head: [chalk8.cyan("Key"), chalk8.cyan("Value"), chalk8.cyan("Default")],
|
|
2988
3671
|
colWidths: [25, 40, 40],
|
|
2989
3672
|
wordWrap: true,
|
|
2990
3673
|
style: {
|
|
@@ -2993,36 +3676,36 @@ async function handleListConfig() {
|
|
|
2993
3676
|
}
|
|
2994
3677
|
});
|
|
2995
3678
|
table.push([
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
3679
|
+
chalk8.white("defaultCommitDays"),
|
|
3680
|
+
chalk8.yellow(String(config2.defaultCommitDays)),
|
|
3681
|
+
chalk8.dim(String(DEFAULT_CONFIG.defaultCommitDays))
|
|
2999
3682
|
]);
|
|
3000
3683
|
table.push([
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3684
|
+
chalk8.white("autoVersionCheck"),
|
|
3685
|
+
chalk8.yellow(String(config2.autoVersionCheck)),
|
|
3686
|
+
chalk8.dim(String(DEFAULT_CONFIG.autoVersionCheck))
|
|
3004
3687
|
]);
|
|
3005
3688
|
logger.info("Current configuration:");
|
|
3006
3689
|
logger.log("");
|
|
3007
3690
|
logger.log(table.toString());
|
|
3008
3691
|
logger.log("");
|
|
3009
|
-
logger.info(
|
|
3692
|
+
logger.info(chalk8.dim("To change a value: ") + chalk8.cyan("bragduck config set <key> <value>"));
|
|
3010
3693
|
logger.log("");
|
|
3011
3694
|
}
|
|
3012
3695
|
async function handleGetConfig(key) {
|
|
3013
3696
|
validateConfigKey(key);
|
|
3014
3697
|
const value = storageService.getConfig(key);
|
|
3015
3698
|
const defaultValue = DEFAULT_CONFIG[key];
|
|
3016
|
-
logger.info(`Configuration for ${
|
|
3699
|
+
logger.info(`Configuration for ${chalk8.cyan(key)}:`);
|
|
3017
3700
|
logger.log("");
|
|
3018
|
-
logger.log(` ${
|
|
3019
|
-
logger.log(` ${
|
|
3701
|
+
logger.log(` ${chalk8.white("Current:")} ${chalk8.yellow(String(value))}`);
|
|
3702
|
+
logger.log(` ${chalk8.white("Default:")} ${chalk8.dim(String(defaultValue))}`);
|
|
3020
3703
|
logger.log("");
|
|
3021
3704
|
if (value === defaultValue) {
|
|
3022
|
-
logger.info(
|
|
3705
|
+
logger.info(chalk8.dim("Using default value"));
|
|
3023
3706
|
} else {
|
|
3024
3707
|
logger.info(
|
|
3025
|
-
|
|
3708
|
+
chalk8.dim("Custom value set. Reset with: ") + chalk8.cyan(`bragduck config set ${key} ${defaultValue}`)
|
|
3026
3709
|
);
|
|
3027
3710
|
}
|
|
3028
3711
|
logger.log("");
|
|
@@ -3032,10 +3715,10 @@ async function handleSetConfig(key, value) {
|
|
|
3032
3715
|
const typedValue = validateAndConvertValue(key, value);
|
|
3033
3716
|
storageService.setConfig(key, typedValue);
|
|
3034
3717
|
logger.log(
|
|
3035
|
-
|
|
3036
|
-
`${
|
|
3718
|
+
boxen10(
|
|
3719
|
+
`${chalk8.green.bold("\u2713 Configuration updated")}
|
|
3037
3720
|
|
|
3038
|
-
${
|
|
3721
|
+
${chalk8.white(key)}: ${chalk8.yellow(String(typedValue))}`,
|
|
3039
3722
|
{
|
|
3040
3723
|
padding: 1,
|
|
3041
3724
|
margin: 1,
|
|
@@ -3101,6 +3784,22 @@ var packageJsonPath = join7(__dirname6, "../../package.json");
|
|
|
3101
3784
|
var packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
|
|
3102
3785
|
var program = new Command();
|
|
3103
3786
|
program.name("bragduck").description("CLI tool for managing developer achievements and brags\nAliases: bd, duck, brag").version(packageJson.version, "-v, --version", "Display version number").helpOption("-h, --help", "Display help information").option("--skip-version-check", "Skip automatic version check on startup").option("--debug", "Enable debug mode (shows detailed logs)");
|
|
3787
|
+
program.command("auth [subcommand]").description("Manage authentication (subcommands: login, status)").action(async (subcommand) => {
|
|
3788
|
+
try {
|
|
3789
|
+
await authCommand(subcommand);
|
|
3790
|
+
} catch (error) {
|
|
3791
|
+
console.error(error);
|
|
3792
|
+
process.exit(1);
|
|
3793
|
+
}
|
|
3794
|
+
});
|
|
3795
|
+
program.command("sync").description("Sync work items and create brags").option("-d, --days <number>", "Number of days to scan", (val) => parseInt(val, 10)).option("-a, --all", "Include all items (not just current user)").option("-s, --source <type>", "Explicit source type (github)").action(async (options) => {
|
|
3796
|
+
try {
|
|
3797
|
+
await syncCommand(options);
|
|
3798
|
+
} catch (error) {
|
|
3799
|
+
console.error(error);
|
|
3800
|
+
process.exit(1);
|
|
3801
|
+
}
|
|
3802
|
+
});
|
|
3104
3803
|
program.command("init").description("Authenticate with Bragduck").action(async () => {
|
|
3105
3804
|
try {
|
|
3106
3805
|
await initCommand();
|
|
@@ -3109,7 +3808,7 @@ program.command("init").description("Authenticate with Bragduck").action(async (
|
|
|
3109
3808
|
process.exit(1);
|
|
3110
3809
|
}
|
|
3111
3810
|
});
|
|
3112
|
-
program.command("scan").description("Scan git commits and create brags").option("-d, --days <number>", "Number of days to scan", (val) => parseInt(val, 10)).option("-a, --all", "Include all commits (not just current user)").action(async (options) => {
|
|
3811
|
+
program.command("scan").description("Scan git commits and create brags (deprecated, use sync)").option("-d, --days <number>", "Number of days to scan", (val) => parseInt(val, 10)).option("-a, --all", "Include all commits (not just current user)").action(async (options) => {
|
|
3113
3812
|
try {
|
|
3114
3813
|
await scanCommand(options);
|
|
3115
3814
|
} catch (error) {
|