@bragduck/cli 2.0.1 → 2.0.3
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 +501 -216
- package/dist/bin/bragduck.js.map +1 -1
- package/dist/index.js +36 -37
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/bin/bragduck.js
CHANGED
|
@@ -312,11 +312,13 @@ var init_errors = __esm({
|
|
|
312
312
|
"use strict";
|
|
313
313
|
init_esm_shims();
|
|
314
314
|
BragduckError = class extends Error {
|
|
315
|
+
code;
|
|
316
|
+
details;
|
|
315
317
|
constructor(message, code, details) {
|
|
316
318
|
super(message);
|
|
319
|
+
this.name = "BragduckError";
|
|
317
320
|
this.code = code;
|
|
318
321
|
this.details = details;
|
|
319
|
-
this.name = "BragduckError";
|
|
320
322
|
Error.captureStackTrace(this, this.constructor);
|
|
321
323
|
}
|
|
322
324
|
};
|
|
@@ -333,10 +335,11 @@ var init_errors = __esm({
|
|
|
333
335
|
}
|
|
334
336
|
};
|
|
335
337
|
ApiError = class extends BragduckError {
|
|
338
|
+
statusCode;
|
|
336
339
|
constructor(message, statusCode, details) {
|
|
337
340
|
super(message, "API_ERROR", details);
|
|
338
|
-
this.statusCode = statusCode;
|
|
339
341
|
this.name = "ApiError";
|
|
342
|
+
this.statusCode = statusCode;
|
|
340
343
|
}
|
|
341
344
|
};
|
|
342
345
|
NetworkError = class extends BragduckError {
|
|
@@ -439,7 +442,7 @@ async function findAvailablePort() {
|
|
|
439
442
|
testServer.listen(port, "127.0.0.1");
|
|
440
443
|
});
|
|
441
444
|
return port;
|
|
442
|
-
} catch
|
|
445
|
+
} catch {
|
|
443
446
|
continue;
|
|
444
447
|
}
|
|
445
448
|
}
|
|
@@ -450,10 +453,10 @@ async function startOAuthCallbackServer(expectedState) {
|
|
|
450
453
|
const timeout = OAUTH_CONFIG.TIMEOUT_MS;
|
|
451
454
|
return new Promise((resolve, reject) => {
|
|
452
455
|
let server = null;
|
|
453
|
-
let timeoutId;
|
|
456
|
+
let timeoutId = null;
|
|
454
457
|
const cleanup = () => {
|
|
455
458
|
if (timeoutId) {
|
|
456
|
-
clearTimeout(timeoutId);
|
|
459
|
+
globalThis.clearTimeout(timeoutId);
|
|
457
460
|
}
|
|
458
461
|
if (server) {
|
|
459
462
|
if (typeof server.closeAllConnections === "function") {
|
|
@@ -499,7 +502,7 @@ async function startOAuthCallbackServer(expectedState) {
|
|
|
499
502
|
}
|
|
500
503
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
501
504
|
res.end(SUCCESS_HTML);
|
|
502
|
-
setTimeout(() => {
|
|
505
|
+
globalThis.setTimeout(() => {
|
|
503
506
|
cleanup();
|
|
504
507
|
resolve({
|
|
505
508
|
code: String(code),
|
|
@@ -521,7 +524,7 @@ async function startOAuthCallbackServer(expectedState) {
|
|
|
521
524
|
server.listen(port, "127.0.0.1", () => {
|
|
522
525
|
logger.debug(`OAuth callback server listening on http://127.0.0.1:${port}${OAUTH_CONFIG.CALLBACK_PATH}`);
|
|
523
526
|
});
|
|
524
|
-
timeoutId = setTimeout(() => {
|
|
527
|
+
timeoutId = globalThis.setTimeout(() => {
|
|
525
528
|
logger.debug("OAuth callback timeout");
|
|
526
529
|
cleanup();
|
|
527
530
|
reject(new OAuthError("Authentication timeout - no callback received within 2 minutes"));
|
|
@@ -699,7 +702,7 @@ var init_browser = __esm({
|
|
|
699
702
|
// src/services/auth.service.ts
|
|
700
703
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
701
704
|
import { readFileSync as readFileSync2 } from "fs";
|
|
702
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
705
|
+
import { fileURLToPath as fileURLToPath3, URLSearchParams } from "url";
|
|
703
706
|
import { dirname as dirname2, join as join3 } from "path";
|
|
704
707
|
import { ofetch } from "ofetch";
|
|
705
708
|
function getUserAgent() {
|
|
@@ -710,7 +713,7 @@ function getUserAgent() {
|
|
|
710
713
|
const platform = process.platform;
|
|
711
714
|
const arch = process.arch;
|
|
712
715
|
return `BragDuck-CLI/${version2} (${platform}-${arch})`;
|
|
713
|
-
} catch
|
|
716
|
+
} catch {
|
|
714
717
|
logger.debug("Failed to read package.json version");
|
|
715
718
|
return "BragDuck-CLI/2.0.0";
|
|
716
719
|
}
|
|
@@ -808,7 +811,7 @@ var init_auth_service = __esm({
|
|
|
808
811
|
const serverPromise = startOAuthCallbackServer(state);
|
|
809
812
|
try {
|
|
810
813
|
await openBrowser(authUrl);
|
|
811
|
-
} catch
|
|
814
|
+
} catch {
|
|
812
815
|
logger.warning("Could not open browser automatically");
|
|
813
816
|
logger.info(`Please open this URL in your browser:`);
|
|
814
817
|
logger.log(authUrl);
|
|
@@ -927,7 +930,7 @@ function getCurrentVersion() {
|
|
|
927
930
|
const packageJsonPath2 = join5(__dirname4, "../../package.json");
|
|
928
931
|
const packageJson2 = JSON.parse(readFileSync3(packageJsonPath2, "utf-8"));
|
|
929
932
|
return packageJson2.version;
|
|
930
|
-
} catch
|
|
933
|
+
} catch {
|
|
931
934
|
logger.debug("Failed to read package.json version");
|
|
932
935
|
return "1.0.0";
|
|
933
936
|
}
|
|
@@ -1025,13 +1028,14 @@ var init_version = __esm({
|
|
|
1025
1028
|
import { ofetch as ofetch2 } from "ofetch";
|
|
1026
1029
|
import { readFileSync as readFileSync4 } from "fs";
|
|
1027
1030
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
1031
|
+
import { URLSearchParams as URLSearchParams2 } from "url";
|
|
1028
1032
|
import { dirname as dirname4, join as join6 } from "path";
|
|
1029
1033
|
function getCliVersion() {
|
|
1030
1034
|
try {
|
|
1031
1035
|
const packageJsonPath2 = join6(__dirname5, "../../package.json");
|
|
1032
1036
|
const packageJson2 = JSON.parse(readFileSync4(packageJsonPath2, "utf-8"));
|
|
1033
1037
|
return packageJson2.version;
|
|
1034
|
-
} catch
|
|
1038
|
+
} catch {
|
|
1035
1039
|
logger.debug("Failed to read package.json version");
|
|
1036
1040
|
return "2.0.0";
|
|
1037
1041
|
}
|
|
@@ -1061,29 +1065,23 @@ var init_api_service = __esm({
|
|
|
1061
1065
|
baseURL: this.baseURL,
|
|
1062
1066
|
// Request interceptor
|
|
1063
1067
|
onRequest: async ({ options }) => {
|
|
1064
|
-
|
|
1068
|
+
const extendedOptions = options;
|
|
1069
|
+
logger.debug(`API Request: ${options.method} ${extendedOptions.baseURL}${extendedOptions.url}`);
|
|
1065
1070
|
const cliVersion = getCliVersion();
|
|
1066
1071
|
const platform = getPlatformInfo();
|
|
1067
1072
|
const userAgent = `BragDuck-CLI/${cliVersion} (${platform})`;
|
|
1068
1073
|
const token = await authService.getAccessToken();
|
|
1074
|
+
const headers = {
|
|
1075
|
+
...options.headers,
|
|
1076
|
+
"User-Agent": userAgent
|
|
1077
|
+
};
|
|
1069
1078
|
if (token) {
|
|
1070
|
-
|
|
1071
|
-
...options.headers,
|
|
1072
|
-
"User-Agent": userAgent,
|
|
1073
|
-
Authorization: `Bearer ${token}`
|
|
1074
|
-
};
|
|
1075
|
-
} else {
|
|
1076
|
-
options.headers = {
|
|
1077
|
-
...options.headers,
|
|
1078
|
-
"User-Agent": userAgent
|
|
1079
|
-
};
|
|
1079
|
+
headers.Authorization = `Bearer ${token}`;
|
|
1080
1080
|
}
|
|
1081
1081
|
if (options.method && ["POST", "PUT", "PATCH"].includes(options.method)) {
|
|
1082
|
-
|
|
1083
|
-
...options.headers,
|
|
1084
|
-
"Content-Type": "application/json"
|
|
1085
|
-
};
|
|
1082
|
+
headers["Content-Type"] = "application/json";
|
|
1086
1083
|
}
|
|
1084
|
+
options.headers = headers;
|
|
1087
1085
|
},
|
|
1088
1086
|
// Response interceptor for success
|
|
1089
1087
|
onResponse: ({ response }) => {
|
|
@@ -1092,7 +1090,8 @@ var init_api_service = __esm({
|
|
|
1092
1090
|
// Response interceptor for errors
|
|
1093
1091
|
onResponseError: async ({ response, options }) => {
|
|
1094
1092
|
const status = response.status;
|
|
1095
|
-
const
|
|
1093
|
+
const extendedOptions = options;
|
|
1094
|
+
const url = `${extendedOptions.baseURL}${extendedOptions.url}`;
|
|
1096
1095
|
logger.debug(`API Error: ${status} ${response.statusText} - ${url}`);
|
|
1097
1096
|
if (status === HTTP_STATUS.UNAUTHORIZED) {
|
|
1098
1097
|
logger.debug("Token expired, attempting refresh");
|
|
@@ -1164,9 +1163,9 @@ var init_api_service = __esm({
|
|
|
1164
1163
|
);
|
|
1165
1164
|
logger.debug(`Successfully refined ${response.refined_commits.length} commits`);
|
|
1166
1165
|
return response;
|
|
1167
|
-
} catch (
|
|
1166
|
+
} catch (_error) {
|
|
1168
1167
|
logger.debug("Failed to refine commits");
|
|
1169
|
-
throw
|
|
1168
|
+
throw _error;
|
|
1170
1169
|
}
|
|
1171
1170
|
}
|
|
1172
1171
|
/**
|
|
@@ -1184,9 +1183,9 @@ var init_api_service = __esm({
|
|
|
1184
1183
|
);
|
|
1185
1184
|
logger.debug(`Successfully created ${response.created} brags`);
|
|
1186
1185
|
return response;
|
|
1187
|
-
} catch (
|
|
1186
|
+
} catch (_error) {
|
|
1188
1187
|
logger.debug("Failed to create brags");
|
|
1189
|
-
throw
|
|
1188
|
+
throw _error;
|
|
1190
1189
|
}
|
|
1191
1190
|
}
|
|
1192
1191
|
/**
|
|
@@ -1196,7 +1195,7 @@ var init_api_service = __esm({
|
|
|
1196
1195
|
const { limit = 50, offset = 0, tags, search } = params;
|
|
1197
1196
|
logger.debug(`Listing brags: limit=${limit}, offset=${offset}`);
|
|
1198
1197
|
try {
|
|
1199
|
-
const queryParams = new
|
|
1198
|
+
const queryParams = new URLSearchParams2({
|
|
1200
1199
|
limit: limit.toString(),
|
|
1201
1200
|
offset: offset.toString()
|
|
1202
1201
|
});
|
|
@@ -1212,9 +1211,9 @@ var init_api_service = __esm({
|
|
|
1212
1211
|
});
|
|
1213
1212
|
logger.debug(`Successfully fetched ${response.brags.length} brags (total: ${response.total})`);
|
|
1214
1213
|
return response;
|
|
1215
|
-
} catch (
|
|
1214
|
+
} catch (_error) {
|
|
1216
1215
|
logger.debug("Failed to list brags");
|
|
1217
|
-
throw
|
|
1216
|
+
throw _error;
|
|
1218
1217
|
}
|
|
1219
1218
|
}
|
|
1220
1219
|
/**
|
|
@@ -1231,7 +1230,7 @@ var init_api_service = __esm({
|
|
|
1231
1230
|
);
|
|
1232
1231
|
logger.debug(`Latest version: ${response.latest_version}`);
|
|
1233
1232
|
return response;
|
|
1234
|
-
} catch
|
|
1233
|
+
} catch {
|
|
1235
1234
|
logger.debug("Failed to check version");
|
|
1236
1235
|
const { version: version2 } = await Promise.resolve().then(() => (init_version(), version_exports));
|
|
1237
1236
|
return {
|
|
@@ -1247,7 +1246,7 @@ var init_api_service = __esm({
|
|
|
1247
1246
|
try {
|
|
1248
1247
|
await this.checkVersion();
|
|
1249
1248
|
return true;
|
|
1250
|
-
} catch
|
|
1249
|
+
} catch {
|
|
1251
1250
|
return false;
|
|
1252
1251
|
}
|
|
1253
1252
|
}
|
|
@@ -1310,7 +1309,7 @@ ${chalk2.dim("Run")} ${chalk2.cyan("bragduck logout")} ${chalk2.dim("to sign out
|
|
|
1310
1309
|
)
|
|
1311
1310
|
);
|
|
1312
1311
|
logger.log("");
|
|
1313
|
-
setTimeout(() => {
|
|
1312
|
+
globalThis.setTimeout(() => {
|
|
1314
1313
|
process.exit(0);
|
|
1315
1314
|
}, 100);
|
|
1316
1315
|
return;
|
|
@@ -1339,7 +1338,7 @@ ${chalk2.dim("You can now use")} ${chalk2.cyan("bragduck scan")} ${chalk2.dim("t
|
|
|
1339
1338
|
)
|
|
1340
1339
|
);
|
|
1341
1340
|
logger.log("");
|
|
1342
|
-
setTimeout(() => {
|
|
1341
|
+
globalThis.setTimeout(() => {
|
|
1343
1342
|
process.exit(0);
|
|
1344
1343
|
}, 100);
|
|
1345
1344
|
return;
|
|
@@ -1438,7 +1437,7 @@ ${chalk3.dim("Run")} ${chalk3.cyan("bragduck init")} ${chalk3.dim("to login agai
|
|
|
1438
1437
|
}
|
|
1439
1438
|
)
|
|
1440
1439
|
);
|
|
1441
|
-
} catch
|
|
1440
|
+
} catch {
|
|
1442
1441
|
spinner.fail("Logout failed");
|
|
1443
1442
|
logger.error("Failed to logout. Please try again.");
|
|
1444
1443
|
process.exit(1);
|
|
@@ -1447,8 +1446,7 @@ ${chalk3.dim("Run")} ${chalk3.cyan("bragduck init")} ${chalk3.dim("to login agai
|
|
|
1447
1446
|
|
|
1448
1447
|
// src/commands/scan.ts
|
|
1449
1448
|
init_esm_shims();
|
|
1450
|
-
import
|
|
1451
|
-
import chalk7 from "chalk";
|
|
1449
|
+
import boxen6 from "boxen";
|
|
1452
1450
|
|
|
1453
1451
|
// src/services/github.service.ts
|
|
1454
1452
|
init_esm_shims();
|
|
@@ -1627,7 +1625,7 @@ var GitService = class {
|
|
|
1627
1625
|
...commit,
|
|
1628
1626
|
diffStats: stats
|
|
1629
1627
|
};
|
|
1630
|
-
} catch
|
|
1628
|
+
} catch {
|
|
1631
1629
|
logger.debug(`Failed to get stats for commit ${commit.sha}, continuing without stats`);
|
|
1632
1630
|
return commit;
|
|
1633
1631
|
}
|
|
@@ -1642,7 +1640,7 @@ var GitService = class {
|
|
|
1642
1640
|
try {
|
|
1643
1641
|
const email = await this.git.raw(["config", "user.email"]);
|
|
1644
1642
|
return email.trim() || null;
|
|
1645
|
-
} catch
|
|
1643
|
+
} catch {
|
|
1646
1644
|
logger.debug("Failed to get git user email");
|
|
1647
1645
|
return null;
|
|
1648
1646
|
}
|
|
@@ -1654,7 +1652,7 @@ var GitService = class {
|
|
|
1654
1652
|
try {
|
|
1655
1653
|
const name = await this.git.raw(["config", "user.name"]);
|
|
1656
1654
|
return name.trim() || null;
|
|
1657
|
-
} catch
|
|
1655
|
+
} catch {
|
|
1658
1656
|
logger.debug("Failed to get git user name");
|
|
1659
1657
|
return null;
|
|
1660
1658
|
}
|
|
@@ -1689,7 +1687,7 @@ var GitHubService = class {
|
|
|
1689
1687
|
try {
|
|
1690
1688
|
await execAsync2("command gh --version");
|
|
1691
1689
|
return true;
|
|
1692
|
-
} catch
|
|
1690
|
+
} catch {
|
|
1693
1691
|
return false;
|
|
1694
1692
|
}
|
|
1695
1693
|
}
|
|
@@ -1711,7 +1709,7 @@ var GitHubService = class {
|
|
|
1711
1709
|
try {
|
|
1712
1710
|
await execAsync2("command gh auth status");
|
|
1713
1711
|
return true;
|
|
1714
|
-
} catch
|
|
1712
|
+
} catch {
|
|
1715
1713
|
return false;
|
|
1716
1714
|
}
|
|
1717
1715
|
}
|
|
@@ -1793,7 +1791,7 @@ var GitHubService = class {
|
|
|
1793
1791
|
try {
|
|
1794
1792
|
const { stdout } = await execAsync2("command gh api user --jq .login");
|
|
1795
1793
|
return stdout.trim() || null;
|
|
1796
|
-
} catch
|
|
1794
|
+
} catch {
|
|
1797
1795
|
logger.debug("Failed to get GitHub user");
|
|
1798
1796
|
return null;
|
|
1799
1797
|
}
|
|
@@ -1876,68 +1874,248 @@ var githubService = new GitHubService();
|
|
|
1876
1874
|
|
|
1877
1875
|
// src/commands/scan.ts
|
|
1878
1876
|
init_api_service();
|
|
1879
|
-
init_auth_service();
|
|
1880
1877
|
init_storage_service();
|
|
1881
1878
|
init_logger();
|
|
1882
1879
|
|
|
1880
|
+
// src/utils/auth-helper.ts
|
|
1881
|
+
init_esm_shims();
|
|
1882
|
+
init_auth_service();
|
|
1883
|
+
import boxen5 from "boxen";
|
|
1884
|
+
init_logger();
|
|
1885
|
+
|
|
1883
1886
|
// src/ui/prompts.ts
|
|
1884
1887
|
init_esm_shims();
|
|
1885
|
-
import { checkbox, confirm as confirm2, input, select } from "@inquirer/prompts";
|
|
1888
|
+
import { checkbox, confirm as confirm2, input, select, editor } from "@inquirer/prompts";
|
|
1889
|
+
import boxen4 from "boxen";
|
|
1886
1890
|
|
|
1887
1891
|
// src/ui/formatters.ts
|
|
1888
1892
|
init_esm_shims();
|
|
1889
|
-
import chalk5 from "chalk";
|
|
1890
1893
|
import Table from "cli-table3";
|
|
1891
1894
|
import terminalLink from "terminal-link";
|
|
1895
|
+
|
|
1896
|
+
// src/ui/theme.ts
|
|
1897
|
+
init_esm_shims();
|
|
1898
|
+
import chalk5 from "chalk";
|
|
1899
|
+
var colors = {
|
|
1900
|
+
// Primary colors for main actions and interactive elements
|
|
1901
|
+
primary: chalk5.cyan,
|
|
1902
|
+
// Success states
|
|
1903
|
+
success: chalk5.green,
|
|
1904
|
+
successBold: chalk5.green.bold,
|
|
1905
|
+
// Warning states
|
|
1906
|
+
warning: chalk5.yellow,
|
|
1907
|
+
warningBold: chalk5.yellow.bold,
|
|
1908
|
+
// Error states
|
|
1909
|
+
error: chalk5.red,
|
|
1910
|
+
errorBold: chalk5.red.bold,
|
|
1911
|
+
// Info and metadata
|
|
1912
|
+
info: chalk5.gray,
|
|
1913
|
+
infoDim: chalk5.dim,
|
|
1914
|
+
// Highlighted/important data
|
|
1915
|
+
highlight: chalk5.yellow.bold,
|
|
1916
|
+
highlightCyan: chalk5.cyan.bold,
|
|
1917
|
+
// Text emphasis
|
|
1918
|
+
bold: chalk5.bold,
|
|
1919
|
+
dim: chalk5.dim,
|
|
1920
|
+
// Special purpose
|
|
1921
|
+
white: chalk5.white,
|
|
1922
|
+
gray: chalk5.gray
|
|
1923
|
+
};
|
|
1924
|
+
var theme = {
|
|
1925
|
+
/**
|
|
1926
|
+
* Format command names and CLI actions
|
|
1927
|
+
*/
|
|
1928
|
+
command: (text) => colors.primary(text),
|
|
1929
|
+
/**
|
|
1930
|
+
* Format success messages
|
|
1931
|
+
*/
|
|
1932
|
+
success: (text) => colors.success(`\u2713 ${text}`),
|
|
1933
|
+
successBold: (text) => colors.successBold(`\u2713 ${text}`),
|
|
1934
|
+
/**
|
|
1935
|
+
* Format error messages
|
|
1936
|
+
*/
|
|
1937
|
+
error: (text) => colors.error(`\u2717 ${text}`),
|
|
1938
|
+
errorBold: (text) => colors.errorBold(`\u2717 ${text}`),
|
|
1939
|
+
/**
|
|
1940
|
+
* Format warning messages
|
|
1941
|
+
*/
|
|
1942
|
+
warning: (text) => colors.warning(`\u26A0 ${text}`),
|
|
1943
|
+
/**
|
|
1944
|
+
* Format info messages
|
|
1945
|
+
*/
|
|
1946
|
+
info: (text) => colors.info(text),
|
|
1947
|
+
/**
|
|
1948
|
+
* Format labels (e.g., "User:", "Email:")
|
|
1949
|
+
*/
|
|
1950
|
+
label: (text) => colors.gray(`${text}:`),
|
|
1951
|
+
/**
|
|
1952
|
+
* Format values
|
|
1953
|
+
*/
|
|
1954
|
+
value: (text) => colors.highlight(text),
|
|
1955
|
+
/**
|
|
1956
|
+
* Format counts and numbers
|
|
1957
|
+
*/
|
|
1958
|
+
count: (num) => colors.highlightCyan(num.toString()),
|
|
1959
|
+
/**
|
|
1960
|
+
* Format file paths
|
|
1961
|
+
*/
|
|
1962
|
+
path: (text) => colors.info(text),
|
|
1963
|
+
/**
|
|
1964
|
+
* Format step indicators (e.g., "Step 1/5:")
|
|
1965
|
+
*/
|
|
1966
|
+
step: (current, total) => colors.primary(`[${current}/${total}]`),
|
|
1967
|
+
/**
|
|
1968
|
+
* Format progress text
|
|
1969
|
+
*/
|
|
1970
|
+
progress: (text) => colors.infoDim(text),
|
|
1971
|
+
/**
|
|
1972
|
+
* Format emphasized text
|
|
1973
|
+
*/
|
|
1974
|
+
emphasis: (text) => colors.bold(text),
|
|
1975
|
+
/**
|
|
1976
|
+
* Format de-emphasized/secondary text
|
|
1977
|
+
*/
|
|
1978
|
+
secondary: (text) => colors.dim(text),
|
|
1979
|
+
/**
|
|
1980
|
+
* Format interactive elements (prompts, selections)
|
|
1981
|
+
*/
|
|
1982
|
+
interactive: (text) => colors.primary(text),
|
|
1983
|
+
/**
|
|
1984
|
+
* Format keys in key-value pairs
|
|
1985
|
+
*/
|
|
1986
|
+
key: (text) => colors.white(text)
|
|
1987
|
+
};
|
|
1988
|
+
var boxStyles = {
|
|
1989
|
+
success: {
|
|
1990
|
+
borderColor: "green",
|
|
1991
|
+
borderStyle: "round",
|
|
1992
|
+
padding: 1,
|
|
1993
|
+
margin: 1
|
|
1994
|
+
},
|
|
1995
|
+
error: {
|
|
1996
|
+
borderColor: "red",
|
|
1997
|
+
borderStyle: "round",
|
|
1998
|
+
padding: 1,
|
|
1999
|
+
margin: 1
|
|
2000
|
+
},
|
|
2001
|
+
warning: {
|
|
2002
|
+
borderColor: "yellow",
|
|
2003
|
+
borderStyle: "round",
|
|
2004
|
+
padding: 1,
|
|
2005
|
+
margin: 1
|
|
2006
|
+
},
|
|
2007
|
+
info: {
|
|
2008
|
+
borderColor: "cyan",
|
|
2009
|
+
borderStyle: "round",
|
|
2010
|
+
padding: 1,
|
|
2011
|
+
margin: 1
|
|
2012
|
+
},
|
|
2013
|
+
plain: {
|
|
2014
|
+
borderColor: "gray",
|
|
2015
|
+
borderStyle: "round",
|
|
2016
|
+
padding: 1,
|
|
2017
|
+
margin: 1
|
|
2018
|
+
}
|
|
2019
|
+
};
|
|
2020
|
+
var tableStyles = {
|
|
2021
|
+
default: {
|
|
2022
|
+
style: {
|
|
2023
|
+
head: [],
|
|
2024
|
+
border: ["gray"]
|
|
2025
|
+
}
|
|
2026
|
+
},
|
|
2027
|
+
compact: {
|
|
2028
|
+
style: {
|
|
2029
|
+
head: [],
|
|
2030
|
+
border: ["dim"],
|
|
2031
|
+
compact: true
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
var sizeIndicators = {
|
|
2036
|
+
small: colors.success("\u25CF"),
|
|
2037
|
+
medium: colors.warning("\u25CF"),
|
|
2038
|
+
large: colors.error("\u25CF")
|
|
2039
|
+
};
|
|
2040
|
+
function getSizeIndicator(insertions, deletions) {
|
|
2041
|
+
const total = insertions + deletions;
|
|
2042
|
+
if (total < 50) {
|
|
2043
|
+
return `${sizeIndicators.small} Small`;
|
|
2044
|
+
} else if (total < 200) {
|
|
2045
|
+
return `${sizeIndicators.medium} Medium`;
|
|
2046
|
+
} else {
|
|
2047
|
+
return `${sizeIndicators.large} Large`;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
function formatDiffStats(insertions, deletions) {
|
|
2051
|
+
return `${colors.success(`+${insertions}`)} ${colors.error(`-${deletions}`)}`;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
// src/ui/formatters.ts
|
|
1892
2055
|
function formatCommitChoice(commit) {
|
|
1893
2056
|
let displaySha;
|
|
1894
2057
|
if (commit.sha.startsWith("pr-")) {
|
|
1895
2058
|
const prNumber = commit.sha.replace("pr-", "#");
|
|
1896
|
-
displaySha =
|
|
2059
|
+
displaySha = colors.highlight(prNumber);
|
|
1897
2060
|
} else {
|
|
1898
|
-
displaySha =
|
|
2061
|
+
displaySha = colors.highlight(commit.sha.substring(0, 7));
|
|
2062
|
+
}
|
|
2063
|
+
const lines = commit.message.split("\n");
|
|
2064
|
+
const title = lines[0];
|
|
2065
|
+
let bodyPreview = "";
|
|
2066
|
+
if (lines.length > 1) {
|
|
2067
|
+
const body = lines.slice(1).join(" ").trim();
|
|
2068
|
+
if (body.length > 0) {
|
|
2069
|
+
const preview = body.length > 80 ? body.substring(0, 80) + "..." : body;
|
|
2070
|
+
bodyPreview = `
|
|
2071
|
+
${colors.infoDim(preview)}`;
|
|
2072
|
+
}
|
|
1899
2073
|
}
|
|
1900
|
-
const
|
|
1901
|
-
const
|
|
1902
|
-
const date = chalk5.gray(new Date(commit.date).toLocaleDateString());
|
|
2074
|
+
const author = colors.info(`by ${commit.author}`);
|
|
2075
|
+
const date = colors.info(new Date(commit.date).toLocaleDateString());
|
|
1903
2076
|
let stats = "";
|
|
2077
|
+
let sizeIndicator = "";
|
|
1904
2078
|
if (commit.diffStats) {
|
|
1905
2079
|
const { filesChanged, insertions, deletions } = commit.diffStats;
|
|
1906
|
-
|
|
1907
|
-
|
|
2080
|
+
sizeIndicator = ` ${getSizeIndicator(insertions, deletions)}`;
|
|
2081
|
+
stats = colors.info(
|
|
2082
|
+
` [${filesChanged} files, ${formatDiffStats(insertions, deletions)}]`
|
|
1908
2083
|
);
|
|
1909
2084
|
}
|
|
1910
|
-
return `${displaySha} ${
|
|
1911
|
-
${author} \u2022 ${date}`;
|
|
2085
|
+
return `${displaySha} ${title}${sizeIndicator}${stats}
|
|
2086
|
+
${author} \u2022 ${date}${bodyPreview}`;
|
|
1912
2087
|
}
|
|
1913
2088
|
function formatRefinedCommitsTable(commits) {
|
|
1914
2089
|
const table = new Table({
|
|
1915
2090
|
head: [
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
2091
|
+
colors.primary("SHA"),
|
|
2092
|
+
colors.primary("Original"),
|
|
2093
|
+
colors.primary("Refined Title"),
|
|
2094
|
+
colors.primary("Description"),
|
|
2095
|
+
colors.primary("Tags")
|
|
1921
2096
|
],
|
|
1922
|
-
colWidths: [
|
|
2097
|
+
colWidths: [12, 28, 30, 40, 20],
|
|
1923
2098
|
wordWrap: true,
|
|
1924
|
-
style:
|
|
1925
|
-
head: [],
|
|
1926
|
-
border: ["gray"]
|
|
1927
|
-
}
|
|
2099
|
+
style: tableStyles.default.style
|
|
1928
2100
|
});
|
|
1929
|
-
commits.forEach((commit) => {
|
|
1930
|
-
|
|
2101
|
+
commits.forEach((commit, index) => {
|
|
2102
|
+
let displaySha;
|
|
2103
|
+
if (commit.sha.startsWith("pr-")) {
|
|
2104
|
+
displaySha = commit.sha.replace("pr-", "#");
|
|
2105
|
+
} else {
|
|
2106
|
+
displaySha = commit.sha.substring(0, 7);
|
|
2107
|
+
}
|
|
1931
2108
|
const original = commit.original_message.split("\n")[0] || "";
|
|
1932
2109
|
const title = commit.refined_title;
|
|
1933
|
-
const description = commit.refined_description.substring(0,
|
|
2110
|
+
const description = commit.refined_description.length > 100 ? commit.refined_description.substring(0, 97) + "..." : commit.refined_description;
|
|
1934
2111
|
const tags = (commit.suggested_tags || []).join(", ") || "none";
|
|
2112
|
+
const rowNum = colors.infoDim(`${index + 1}.`);
|
|
1935
2113
|
table.push([
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2114
|
+
`${rowNum} ${colors.highlight(displaySha)}`,
|
|
2115
|
+
colors.info(original),
|
|
2116
|
+
colors.white(title),
|
|
2117
|
+
colors.dim(description),
|
|
2118
|
+
colors.primary(tags)
|
|
1941
2119
|
]);
|
|
1942
2120
|
});
|
|
1943
2121
|
return table.toString();
|
|
@@ -1946,26 +2124,35 @@ function formatCommitStats(commits) {
|
|
|
1946
2124
|
const totalFiles = commits.reduce((sum, c) => sum + (c.diffStats?.filesChanged || 0), 0);
|
|
1947
2125
|
const totalInsertions = commits.reduce((sum, c) => sum + (c.diffStats?.insertions || 0), 0);
|
|
1948
2126
|
const totalDeletions = commits.reduce((sum, c) => sum + (c.diffStats?.deletions || 0), 0);
|
|
1949
|
-
return
|
|
2127
|
+
return colors.info("Total changes: ") + colors.white(`${totalFiles} files`) + colors.info(", ") + formatDiffStats(totalInsertions, totalDeletions);
|
|
2128
|
+
}
|
|
2129
|
+
function formatSelectionSummary(count, commits) {
|
|
2130
|
+
const totalFiles = commits.reduce((sum, c) => sum + (c.diffStats?.filesChanged || 0), 0);
|
|
2131
|
+
const totalInsertions = commits.reduce((sum, c) => sum + (c.diffStats?.insertions || 0), 0);
|
|
2132
|
+
const totalDeletions = commits.reduce((sum, c) => sum + (c.diffStats?.deletions || 0), 0);
|
|
2133
|
+
const selectedText = theme.success(`Selected ${theme.count(count)} PR${count > 1 ? "s" : ""}`);
|
|
2134
|
+
const stats = `${theme.count(totalFiles)} files changed, ${formatDiffStats(totalInsertions, totalDeletions)}`;
|
|
2135
|
+
return `
|
|
2136
|
+
${selectedText}: ${stats}`;
|
|
1950
2137
|
}
|
|
1951
2138
|
function formatSuccessMessage(count) {
|
|
1952
2139
|
const emoji = "\u{1F389}";
|
|
1953
|
-
const title =
|
|
2140
|
+
const title = colors.successBold(
|
|
1954
2141
|
`${emoji} Successfully created ${count} brag${count > 1 ? "s" : ""}!`
|
|
1955
2142
|
);
|
|
1956
|
-
const message =
|
|
1957
|
-
const hint =
|
|
1958
|
-
const url = "https://
|
|
2143
|
+
const message = colors.white("\nYour achievements are now saved and ready to showcase.");
|
|
2144
|
+
const hint = theme.secondary("\nRun ") + theme.command("bragduck list") + theme.secondary(" to see all your brags");
|
|
2145
|
+
const url = "https://bragduck.com/app/brags";
|
|
1959
2146
|
const clickableUrl = terminalLink(url, url, {
|
|
1960
|
-
fallback: () =>
|
|
2147
|
+
fallback: () => colors.primary(url)
|
|
1961
2148
|
});
|
|
1962
|
-
const webUrl =
|
|
2149
|
+
const webUrl = theme.secondary("\n\nOr, check ") + clickableUrl + theme.secondary(" to see all your brags");
|
|
1963
2150
|
return `${title}${message}${hint}${webUrl}`;
|
|
1964
2151
|
}
|
|
1965
2152
|
function formatErrorMessage(message, hint) {
|
|
1966
|
-
const title =
|
|
1967
|
-
const error =
|
|
1968
|
-
const hintText = hint ?
|
|
2153
|
+
const title = colors.errorBold("\u2717 Error");
|
|
2154
|
+
const error = colors.white(message);
|
|
2155
|
+
const hintText = hint ? theme.secondary("\n\nHint: ") + theme.command(hint) : "";
|
|
1969
2156
|
return `${title}
|
|
1970
2157
|
|
|
1971
2158
|
${error}${hintText}`;
|
|
@@ -2024,11 +2211,118 @@ async function promptDaysToScan(defaultDays = 30) {
|
|
|
2024
2211
|
}
|
|
2025
2212
|
return parseInt(selected, 10);
|
|
2026
2213
|
}
|
|
2214
|
+
async function promptSortOption() {
|
|
2215
|
+
const choices = [
|
|
2216
|
+
{ name: "By date (newest first)", value: "date", description: "Most recent PRs first" },
|
|
2217
|
+
{ name: "By size (largest first)", value: "size", description: "Most lines changed" },
|
|
2218
|
+
{ name: "By files (most files)", value: "files", description: "Most files changed" },
|
|
2219
|
+
{ name: "No sorting", value: "none", description: "Keep original order" }
|
|
2220
|
+
];
|
|
2221
|
+
return await select({
|
|
2222
|
+
message: "How would you like to sort the PRs?",
|
|
2223
|
+
choices,
|
|
2224
|
+
default: "date"
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
async function promptReviewBrags(refinedCommits) {
|
|
2228
|
+
const acceptedBrags = [];
|
|
2229
|
+
console.log("\n" + theme.info("Review each brag before creation:") + "\n");
|
|
2230
|
+
for (let i = 0; i < refinedCommits.length; i++) {
|
|
2231
|
+
const commit = refinedCommits[i];
|
|
2232
|
+
const current = i + 1;
|
|
2233
|
+
const total = refinedCommits.length;
|
|
2234
|
+
const displaySha = commit.sha.startsWith("pr-") ? commit.sha.replace("pr-", "#") : commit.sha.substring(0, 7);
|
|
2235
|
+
const bragDetails = `${theme.step(current, total)} ${colors.highlight(displaySha)}
|
|
2236
|
+
|
|
2237
|
+
${theme.label("Title")} ${colors.white(commit.refined_title)}
|
|
2238
|
+
|
|
2239
|
+
${theme.label("Description")}
|
|
2240
|
+
${colors.white(commit.refined_description)}
|
|
2241
|
+
|
|
2242
|
+
${theme.label("Tags")} ${colors.primary((commit.suggested_tags || []).join(", ") || "none")}
|
|
2243
|
+
|
|
2244
|
+
${theme.label("Impact Score")} ${colors.highlight(commit.impact_score?.toString() || "N/A")}`;
|
|
2245
|
+
console.log(boxen4(bragDetails, boxStyles.info));
|
|
2246
|
+
console.log("");
|
|
2247
|
+
const action = await select({
|
|
2248
|
+
message: `What would you like to do with this brag?`,
|
|
2249
|
+
choices: [
|
|
2250
|
+
{ name: "\u2713 Accept", value: "accept", description: "Add this brag as-is" },
|
|
2251
|
+
{ name: "\u270E Edit title", value: "edit-title", description: "Modify the title" },
|
|
2252
|
+
{ name: "\u270E Edit description", value: "edit-desc", description: "Modify the description" },
|
|
2253
|
+
{ name: "\u270E Edit both", value: "edit-both", description: "Modify title and description" },
|
|
2254
|
+
{ name: "\u2717 Skip", value: "skip", description: "Skip this brag" },
|
|
2255
|
+
...current < total ? [{ name: "\u2713 Accept all remaining", value: "accept-all", description: "Accept this and all remaining brags" }] : [],
|
|
2256
|
+
{ name: "\u2717 Cancel", value: "cancel", description: "Cancel and discard all brags" }
|
|
2257
|
+
]
|
|
2258
|
+
});
|
|
2259
|
+
if (action === "cancel") {
|
|
2260
|
+
return [];
|
|
2261
|
+
}
|
|
2262
|
+
if (action === "accept-all") {
|
|
2263
|
+
acceptedBrags.push(commit);
|
|
2264
|
+
for (let j = i + 1; j < refinedCommits.length; j++) {
|
|
2265
|
+
acceptedBrags.push(refinedCommits[j]);
|
|
2266
|
+
}
|
|
2267
|
+
break;
|
|
2268
|
+
}
|
|
2269
|
+
if (action === "skip") {
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
let editedCommit = { ...commit };
|
|
2273
|
+
if (action === "edit-title" || action === "edit-both") {
|
|
2274
|
+
const newTitle = await input({
|
|
2275
|
+
message: "Enter new title:",
|
|
2276
|
+
default: commit.refined_title
|
|
2277
|
+
});
|
|
2278
|
+
editedCommit.refined_title = newTitle;
|
|
2279
|
+
}
|
|
2280
|
+
if (action === "edit-desc" || action === "edit-both") {
|
|
2281
|
+
const newDesc = await editor({
|
|
2282
|
+
message: "Edit description (will open your default editor):",
|
|
2283
|
+
default: commit.refined_description
|
|
2284
|
+
});
|
|
2285
|
+
editedCommit.refined_description = newDesc;
|
|
2286
|
+
}
|
|
2287
|
+
acceptedBrags.push(editedCommit);
|
|
2288
|
+
}
|
|
2289
|
+
return acceptedBrags;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
// src/utils/auth-helper.ts
|
|
2293
|
+
async function ensureAuthenticated() {
|
|
2294
|
+
const isAuthenticated = await authService.isAuthenticated();
|
|
2295
|
+
if (isAuthenticated) {
|
|
2296
|
+
return true;
|
|
2297
|
+
}
|
|
2298
|
+
logger.log("");
|
|
2299
|
+
logger.log(
|
|
2300
|
+
boxen5(
|
|
2301
|
+
theme.warning("Not authenticated") + "\n\nYou need to be logged in to use this command.\n\nWould you like to authenticate now?",
|
|
2302
|
+
boxStyles.warning
|
|
2303
|
+
)
|
|
2304
|
+
);
|
|
2305
|
+
logger.log("");
|
|
2306
|
+
const shouldAuth = await promptConfirm("Authenticate now?", true);
|
|
2307
|
+
if (!shouldAuth) {
|
|
2308
|
+
logger.log("");
|
|
2309
|
+
logger.info(
|
|
2310
|
+
theme.secondary("Authentication skipped. Run ") + theme.command("bragduck init") + theme.secondary(" when you're ready to authenticate.")
|
|
2311
|
+
);
|
|
2312
|
+
logger.log("");
|
|
2313
|
+
return false;
|
|
2314
|
+
}
|
|
2315
|
+
try {
|
|
2316
|
+
await initCommand();
|
|
2317
|
+
return true;
|
|
2318
|
+
} catch {
|
|
2319
|
+
return false;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2027
2322
|
|
|
2028
2323
|
// src/ui/spinners.ts
|
|
2029
2324
|
init_esm_shims();
|
|
2030
2325
|
import ora3 from "ora";
|
|
2031
|
-
import chalk6 from "chalk";
|
|
2032
2326
|
function createSpinner(text) {
|
|
2033
2327
|
return ora3({
|
|
2034
2328
|
text,
|
|
@@ -2036,57 +2330,59 @@ function createSpinner(text) {
|
|
|
2036
2330
|
spinner: "dots"
|
|
2037
2331
|
});
|
|
2038
2332
|
}
|
|
2039
|
-
function
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
return createSpinner(`Creating ${count} brag${count > 1 ? "s" : ""}...`);
|
|
2333
|
+
function createStepSpinner(currentStep, totalSteps, text) {
|
|
2334
|
+
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2335
|
+
return ora3({
|
|
2336
|
+
text: `${stepIndicator} ${text}`,
|
|
2337
|
+
color: "cyan",
|
|
2338
|
+
spinner: "dots"
|
|
2339
|
+
});
|
|
2047
2340
|
}
|
|
2048
2341
|
function fetchingBragsSpinner() {
|
|
2049
2342
|
return createSpinner("Fetching your brags...");
|
|
2050
2343
|
}
|
|
2051
2344
|
function succeedSpinner(spinner, text) {
|
|
2052
2345
|
if (text) {
|
|
2053
|
-
spinner.succeed(
|
|
2346
|
+
spinner.succeed(colors.success(text));
|
|
2054
2347
|
} else {
|
|
2055
2348
|
spinner.succeed();
|
|
2056
2349
|
}
|
|
2057
2350
|
}
|
|
2058
2351
|
function failSpinner(spinner, text) {
|
|
2059
2352
|
if (text) {
|
|
2060
|
-
spinner.fail(
|
|
2353
|
+
spinner.fail(colors.error(text));
|
|
2061
2354
|
} else {
|
|
2062
2355
|
spinner.fail();
|
|
2063
2356
|
}
|
|
2064
2357
|
}
|
|
2358
|
+
function succeedStepSpinner(spinner, currentStep, totalSteps, text) {
|
|
2359
|
+
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2360
|
+
spinner.succeed(`${stepIndicator} ${colors.success(text)}`);
|
|
2361
|
+
}
|
|
2362
|
+
function failStepSpinner(spinner, currentStep, totalSteps, text) {
|
|
2363
|
+
const stepIndicator = theme.step(currentStep, totalSteps);
|
|
2364
|
+
spinner.fail(`${stepIndicator} ${colors.error(text)}`);
|
|
2365
|
+
}
|
|
2065
2366
|
|
|
2066
2367
|
// src/commands/scan.ts
|
|
2067
2368
|
async function scanCommand(options = {}) {
|
|
2068
2369
|
logger.log("");
|
|
2370
|
+
const TOTAL_STEPS = 5;
|
|
2069
2371
|
try {
|
|
2070
|
-
const isAuthenticated = await
|
|
2372
|
+
const isAuthenticated = await ensureAuthenticated();
|
|
2071
2373
|
if (!isAuthenticated) {
|
|
2072
|
-
logger.log(
|
|
2073
|
-
boxen4(
|
|
2074
|
-
`${chalk7.yellow.bold("\u26A0 Not authenticated")}
|
|
2075
|
-
|
|
2076
|
-
Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
2077
|
-
{
|
|
2078
|
-
padding: 1,
|
|
2079
|
-
margin: 1,
|
|
2080
|
-
borderStyle: "round",
|
|
2081
|
-
borderColor: "yellow"
|
|
2082
|
-
}
|
|
2083
|
-
)
|
|
2084
|
-
);
|
|
2085
2374
|
process.exit(1);
|
|
2086
2375
|
}
|
|
2376
|
+
const repoSpinner = createStepSpinner(1, TOTAL_STEPS, "Validating GitHub repository");
|
|
2377
|
+
repoSpinner.start();
|
|
2087
2378
|
await githubService.validateGitHubRepository();
|
|
2088
2379
|
const repoInfo = await githubService.getRepositoryInfo();
|
|
2089
|
-
|
|
2380
|
+
succeedStepSpinner(
|
|
2381
|
+
repoSpinner,
|
|
2382
|
+
1,
|
|
2383
|
+
TOTAL_STEPS,
|
|
2384
|
+
`Repository: ${theme.value(repoInfo.fullName)}`
|
|
2385
|
+
);
|
|
2090
2386
|
logger.log("");
|
|
2091
2387
|
let days = options.days;
|
|
2092
2388
|
if (!days) {
|
|
@@ -2094,8 +2390,8 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
2094
2390
|
days = await promptDaysToScan(defaultDays);
|
|
2095
2391
|
logger.log("");
|
|
2096
2392
|
}
|
|
2097
|
-
const
|
|
2098
|
-
|
|
2393
|
+
const prSpinner = createStepSpinner(2, TOTAL_STEPS, `Fetching merged PRs from the last ${days} days`);
|
|
2394
|
+
prSpinner.start();
|
|
2099
2395
|
let prs;
|
|
2100
2396
|
if (options.all) {
|
|
2101
2397
|
prs = await githubService.getMergedPRs({ days });
|
|
@@ -2104,25 +2400,46 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
2104
2400
|
}
|
|
2105
2401
|
const commits = prs.map((pr) => githubService.transformPRToCommit(pr));
|
|
2106
2402
|
if (commits.length === 0) {
|
|
2107
|
-
|
|
2403
|
+
failStepSpinner(prSpinner, 2, TOTAL_STEPS, `No merged PRs found in the last ${days} days`);
|
|
2108
2404
|
logger.log("");
|
|
2109
2405
|
logger.info("Try increasing the number of days or check your GitHub activity");
|
|
2110
2406
|
return;
|
|
2111
2407
|
}
|
|
2112
|
-
|
|
2408
|
+
succeedStepSpinner(prSpinner, 2, TOTAL_STEPS, `Found ${theme.count(commits.length)} PR${commits.length > 1 ? "s" : ""}`);
|
|
2113
2409
|
logger.log("");
|
|
2114
2410
|
logger.log(formatCommitStats(commits));
|
|
2115
2411
|
logger.log("");
|
|
2116
|
-
const
|
|
2412
|
+
const sortOption = await promptSortOption();
|
|
2413
|
+
logger.log("");
|
|
2414
|
+
let sortedCommits = [...commits];
|
|
2415
|
+
if (sortOption === "date") {
|
|
2416
|
+
sortedCommits.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
2417
|
+
} else if (sortOption === "size") {
|
|
2418
|
+
sortedCommits.sort((a, b) => {
|
|
2419
|
+
const sizeA = (a.diffStats?.insertions || 0) + (a.diffStats?.deletions || 0);
|
|
2420
|
+
const sizeB = (b.diffStats?.insertions || 0) + (b.diffStats?.deletions || 0);
|
|
2421
|
+
return sizeB - sizeA;
|
|
2422
|
+
});
|
|
2423
|
+
} else if (sortOption === "files") {
|
|
2424
|
+
sortedCommits.sort((a, b) => {
|
|
2425
|
+
const filesA = a.diffStats?.filesChanged || 0;
|
|
2426
|
+
const filesB = b.diffStats?.filesChanged || 0;
|
|
2427
|
+
return filesB - filesA;
|
|
2428
|
+
});
|
|
2429
|
+
}
|
|
2430
|
+
const selectedShas = await promptSelectCommits(sortedCommits);
|
|
2117
2431
|
if (selectedShas.length === 0) {
|
|
2118
|
-
logger.warning("No
|
|
2432
|
+
logger.warning("No PRs selected");
|
|
2119
2433
|
return;
|
|
2120
2434
|
}
|
|
2121
|
-
const selectedCommits =
|
|
2122
|
-
logger.log(
|
|
2123
|
-
logger.success(`Selected ${selectedCommits.length} PR${selectedCommits.length > 1 ? "s" : ""}`);
|
|
2435
|
+
const selectedCommits = sortedCommits.filter((c) => selectedShas.includes(c.sha));
|
|
2436
|
+
logger.log(formatSelectionSummary(selectedCommits.length, selectedCommits));
|
|
2124
2437
|
logger.log("");
|
|
2125
|
-
const refineSpinner =
|
|
2438
|
+
const refineSpinner = createStepSpinner(
|
|
2439
|
+
3,
|
|
2440
|
+
TOTAL_STEPS,
|
|
2441
|
+
`Refining ${theme.count(selectedCommits.length)} PR${selectedCommits.length > 1 ? "s" : ""} with AI`
|
|
2442
|
+
);
|
|
2126
2443
|
refineSpinner.start();
|
|
2127
2444
|
const refineRequest = {
|
|
2128
2445
|
commits: selectedCommits.map((c) => ({
|
|
@@ -2138,23 +2455,27 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
2138
2455
|
}))
|
|
2139
2456
|
};
|
|
2140
2457
|
const refineResponse = await apiService.refineCommits(refineRequest);
|
|
2141
|
-
|
|
2142
|
-
|
|
2458
|
+
let refinedCommits = refineResponse.refined_commits;
|
|
2459
|
+
succeedStepSpinner(refineSpinner, 3, TOTAL_STEPS, "PRs refined successfully");
|
|
2143
2460
|
logger.log("");
|
|
2144
2461
|
logger.info("Preview of refined brags:");
|
|
2145
2462
|
logger.log("");
|
|
2146
2463
|
logger.log(formatRefinedCommitsTable(refinedCommits));
|
|
2147
2464
|
logger.log("");
|
|
2148
|
-
const
|
|
2149
|
-
if (
|
|
2150
|
-
logger.warning("
|
|
2465
|
+
const acceptedBrags = await promptReviewBrags(refinedCommits);
|
|
2466
|
+
if (acceptedBrags.length === 0) {
|
|
2467
|
+
logger.warning("No brags selected for creation");
|
|
2151
2468
|
return;
|
|
2152
2469
|
}
|
|
2153
2470
|
logger.log("");
|
|
2154
|
-
const createSpinner2 =
|
|
2471
|
+
const createSpinner2 = createStepSpinner(
|
|
2472
|
+
4,
|
|
2473
|
+
TOTAL_STEPS,
|
|
2474
|
+
`Creating ${theme.count(acceptedBrags.length)} brag${acceptedBrags.length > 1 ? "s" : ""}`
|
|
2475
|
+
);
|
|
2155
2476
|
createSpinner2.start();
|
|
2156
2477
|
const createRequest = {
|
|
2157
|
-
brags:
|
|
2478
|
+
brags: acceptedBrags.map((refined) => {
|
|
2158
2479
|
const originalCommit = selectedCommits.find((c) => c.sha === refined.sha);
|
|
2159
2480
|
return {
|
|
2160
2481
|
commit_sha: refined.sha,
|
|
@@ -2170,27 +2491,13 @@ Please run ${chalk7.cyan("bragduck init")} to login first.`,
|
|
|
2170
2491
|
})
|
|
2171
2492
|
};
|
|
2172
2493
|
const createResponse = await apiService.createBrags(createRequest);
|
|
2173
|
-
|
|
2494
|
+
succeedStepSpinner(createSpinner2, 4, TOTAL_STEPS, `Created ${theme.count(createResponse.created)} brag${createResponse.created > 1 ? "s" : ""}`);
|
|
2174
2495
|
logger.log("");
|
|
2175
|
-
logger.log(
|
|
2176
|
-
boxen4(formatSuccessMessage(createResponse.created), {
|
|
2177
|
-
padding: 1,
|
|
2178
|
-
margin: 1,
|
|
2179
|
-
borderStyle: "round",
|
|
2180
|
-
borderColor: "green"
|
|
2181
|
-
})
|
|
2182
|
-
);
|
|
2496
|
+
logger.log(boxen6(formatSuccessMessage(createResponse.created), boxStyles.success));
|
|
2183
2497
|
} catch (error) {
|
|
2184
2498
|
const err = error;
|
|
2185
2499
|
logger.log("");
|
|
2186
|
-
logger.log(
|
|
2187
|
-
boxen4(formatErrorMessage(err.message, getErrorHint2(err)), {
|
|
2188
|
-
padding: 1,
|
|
2189
|
-
margin: 1,
|
|
2190
|
-
borderStyle: "round",
|
|
2191
|
-
borderColor: "red"
|
|
2192
|
-
})
|
|
2193
|
-
);
|
|
2500
|
+
logger.log(boxen6(formatErrorMessage(err.message, getErrorHint2(err)), boxStyles.error));
|
|
2194
2501
|
process.exit(1);
|
|
2195
2502
|
}
|
|
2196
2503
|
}
|
|
@@ -2216,29 +2523,14 @@ function getErrorHint2(error) {
|
|
|
2216
2523
|
// src/commands/list.ts
|
|
2217
2524
|
init_esm_shims();
|
|
2218
2525
|
init_api_service();
|
|
2219
|
-
|
|
2220
|
-
init_logger();
|
|
2221
|
-
import boxen5 from "boxen";
|
|
2222
|
-
import chalk8 from "chalk";
|
|
2526
|
+
import boxen7 from "boxen";
|
|
2223
2527
|
import Table2 from "cli-table3";
|
|
2528
|
+
init_logger();
|
|
2224
2529
|
async function listCommand(options = {}) {
|
|
2225
2530
|
logger.log("");
|
|
2226
2531
|
try {
|
|
2227
|
-
const isAuthenticated = await
|
|
2532
|
+
const isAuthenticated = await ensureAuthenticated();
|
|
2228
2533
|
if (!isAuthenticated) {
|
|
2229
|
-
logger.log(
|
|
2230
|
-
boxen5(
|
|
2231
|
-
`${chalk8.yellow.bold("\u26A0 Not authenticated")}
|
|
2232
|
-
|
|
2233
|
-
Please run ${chalk8.cyan("bragduck init")} to login first.`,
|
|
2234
|
-
{
|
|
2235
|
-
padding: 1,
|
|
2236
|
-
margin: 1,
|
|
2237
|
-
borderStyle: "round",
|
|
2238
|
-
borderColor: "yellow"
|
|
2239
|
-
}
|
|
2240
|
-
)
|
|
2241
|
-
);
|
|
2242
2534
|
process.exit(1);
|
|
2243
2535
|
}
|
|
2244
2536
|
const limit = options.limit || 50;
|
|
@@ -2259,7 +2551,7 @@ Please run ${chalk8.cyan("bragduck init")} to login first.`,
|
|
|
2259
2551
|
if (search || tags) {
|
|
2260
2552
|
logger.info("Try adjusting your filters or run without filters to see all brags");
|
|
2261
2553
|
} else {
|
|
2262
|
-
logger.info(
|
|
2554
|
+
logger.info(theme.secondary("Run ") + theme.command("bragduck scan") + theme.secondary(" to create your first brag!"));
|
|
2263
2555
|
}
|
|
2264
2556
|
return;
|
|
2265
2557
|
}
|
|
@@ -2273,7 +2565,7 @@ Please run ${chalk8.cyan("bragduck init")} to login first.`,
|
|
|
2273
2565
|
`Showing ${offset + 1}-${offset + response.brags.length} of ${response.total} total brags`
|
|
2274
2566
|
);
|
|
2275
2567
|
logger.info(
|
|
2276
|
-
|
|
2568
|
+
theme.secondary("Run ") + theme.command(`bragduck list --offset ${nextOffset}`) + theme.secondary(" to see more")
|
|
2277
2569
|
);
|
|
2278
2570
|
logger.log("");
|
|
2279
2571
|
} else if (offset > 0) {
|
|
@@ -2286,51 +2578,41 @@ Please run ${chalk8.cyan("bragduck init")} to login first.`,
|
|
|
2286
2578
|
const filterInfo = [];
|
|
2287
2579
|
if (search) filterInfo.push(`search: "${search}"`);
|
|
2288
2580
|
if (tags) filterInfo.push(`tags: ${tags.join(", ")}`);
|
|
2289
|
-
logger.info(
|
|
2581
|
+
logger.info(theme.info(`Filters applied: ${filterInfo.join(", ")}`));
|
|
2290
2582
|
logger.log("");
|
|
2291
2583
|
}
|
|
2292
2584
|
} catch (error) {
|
|
2293
2585
|
const err = error;
|
|
2294
2586
|
logger.log("");
|
|
2295
|
-
logger.log(
|
|
2296
|
-
boxen5(formatErrorMessage(err.message, getErrorHint3(err)), {
|
|
2297
|
-
padding: 1,
|
|
2298
|
-
margin: 1,
|
|
2299
|
-
borderStyle: "round",
|
|
2300
|
-
borderColor: "red"
|
|
2301
|
-
})
|
|
2302
|
-
);
|
|
2587
|
+
logger.log(boxen7(formatErrorMessage(err.message, getErrorHint3(err)), boxStyles.error));
|
|
2303
2588
|
process.exit(1);
|
|
2304
2589
|
}
|
|
2305
2590
|
}
|
|
2306
2591
|
function formatBragsTable(brags) {
|
|
2307
2592
|
const table = new Table2({
|
|
2308
2593
|
head: [
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2594
|
+
colors.primary("Date"),
|
|
2595
|
+
colors.primary("Title"),
|
|
2596
|
+
colors.primary("Description"),
|
|
2597
|
+
colors.primary("Tags"),
|
|
2598
|
+
colors.primary("Repository")
|
|
2314
2599
|
],
|
|
2315
2600
|
colWidths: [12, 30, 40, 20, 30],
|
|
2316
2601
|
wordWrap: true,
|
|
2317
|
-
style:
|
|
2318
|
-
head: [],
|
|
2319
|
-
border: ["gray"]
|
|
2320
|
-
}
|
|
2602
|
+
style: tableStyles.default.style
|
|
2321
2603
|
});
|
|
2322
2604
|
brags.forEach((brag) => {
|
|
2323
2605
|
const date = new Date(brag.date).toLocaleDateString();
|
|
2324
2606
|
const title = brag.title;
|
|
2325
2607
|
const description = truncateText(brag.description, 100);
|
|
2326
|
-
const tags = brag.tags.length > 0 ? brag.tags.join(", ") :
|
|
2327
|
-
const repository = brag.repository ? truncateText(extractRepoName(brag.repository), 25) :
|
|
2608
|
+
const tags = brag.tags.length > 0 ? brag.tags.join(", ") : colors.dim("none");
|
|
2609
|
+
const repository = brag.repository ? truncateText(extractRepoName(brag.repository), 25) : colors.dim("none");
|
|
2328
2610
|
table.push([
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2611
|
+
colors.highlight(date),
|
|
2612
|
+
colors.white(title),
|
|
2613
|
+
colors.dim(description),
|
|
2614
|
+
colors.primary(tags),
|
|
2615
|
+
colors.info(repository)
|
|
2334
2616
|
]);
|
|
2335
2617
|
});
|
|
2336
2618
|
return table.toString();
|
|
@@ -2367,8 +2649,8 @@ init_esm_shims();
|
|
|
2367
2649
|
init_storage_service();
|
|
2368
2650
|
init_logger();
|
|
2369
2651
|
init_constants();
|
|
2370
|
-
import
|
|
2371
|
-
import
|
|
2652
|
+
import boxen8 from "boxen";
|
|
2653
|
+
import chalk6 from "chalk";
|
|
2372
2654
|
import Table3 from "cli-table3";
|
|
2373
2655
|
init_errors();
|
|
2374
2656
|
var VALID_CONFIG_KEYS = Object.values(CONFIG_KEYS);
|
|
@@ -2400,9 +2682,10 @@ async function configCommand(subcommand, key, value) {
|
|
|
2400
2682
|
);
|
|
2401
2683
|
}
|
|
2402
2684
|
} catch (error) {
|
|
2685
|
+
const err = error;
|
|
2403
2686
|
logger.log("");
|
|
2404
2687
|
logger.log(
|
|
2405
|
-
|
|
2688
|
+
boxen8(formatErrorMessage(err.message, getConfigHint(err)), {
|
|
2406
2689
|
padding: 1,
|
|
2407
2690
|
margin: 1,
|
|
2408
2691
|
borderStyle: "round",
|
|
@@ -2418,7 +2701,7 @@ async function handleListConfig() {
|
|
|
2418
2701
|
autoVersionCheck: storageService.getConfig("autoVersionCheck")
|
|
2419
2702
|
};
|
|
2420
2703
|
const table = new Table3({
|
|
2421
|
-
head: [
|
|
2704
|
+
head: [chalk6.cyan("Key"), chalk6.cyan("Value"), chalk6.cyan("Default")],
|
|
2422
2705
|
colWidths: [25, 40, 40],
|
|
2423
2706
|
wordWrap: true,
|
|
2424
2707
|
style: {
|
|
@@ -2427,36 +2710,36 @@ async function handleListConfig() {
|
|
|
2427
2710
|
}
|
|
2428
2711
|
});
|
|
2429
2712
|
table.push([
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2713
|
+
chalk6.white("defaultCommitDays"),
|
|
2714
|
+
chalk6.yellow(String(config2.defaultCommitDays)),
|
|
2715
|
+
chalk6.dim(String(DEFAULT_CONFIG.defaultCommitDays))
|
|
2433
2716
|
]);
|
|
2434
2717
|
table.push([
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2718
|
+
chalk6.white("autoVersionCheck"),
|
|
2719
|
+
chalk6.yellow(String(config2.autoVersionCheck)),
|
|
2720
|
+
chalk6.dim(String(DEFAULT_CONFIG.autoVersionCheck))
|
|
2438
2721
|
]);
|
|
2439
2722
|
logger.info("Current configuration:");
|
|
2440
2723
|
logger.log("");
|
|
2441
2724
|
logger.log(table.toString());
|
|
2442
2725
|
logger.log("");
|
|
2443
|
-
logger.info(
|
|
2726
|
+
logger.info(chalk6.dim("To change a value: ") + chalk6.cyan("bragduck config set <key> <value>"));
|
|
2444
2727
|
logger.log("");
|
|
2445
2728
|
}
|
|
2446
2729
|
async function handleGetConfig(key) {
|
|
2447
2730
|
validateConfigKey(key);
|
|
2448
2731
|
const value = storageService.getConfig(key);
|
|
2449
2732
|
const defaultValue = DEFAULT_CONFIG[key];
|
|
2450
|
-
logger.info(`Configuration for ${
|
|
2733
|
+
logger.info(`Configuration for ${chalk6.cyan(key)}:`);
|
|
2451
2734
|
logger.log("");
|
|
2452
|
-
logger.log(` ${
|
|
2453
|
-
logger.log(` ${
|
|
2735
|
+
logger.log(` ${chalk6.white("Current:")} ${chalk6.yellow(String(value))}`);
|
|
2736
|
+
logger.log(` ${chalk6.white("Default:")} ${chalk6.dim(String(defaultValue))}`);
|
|
2454
2737
|
logger.log("");
|
|
2455
2738
|
if (value === defaultValue) {
|
|
2456
|
-
logger.info(
|
|
2739
|
+
logger.info(chalk6.dim("Using default value"));
|
|
2457
2740
|
} else {
|
|
2458
2741
|
logger.info(
|
|
2459
|
-
|
|
2742
|
+
chalk6.dim("Custom value set. Reset with: ") + chalk6.cyan(`bragduck config set ${key} ${defaultValue}`)
|
|
2460
2743
|
);
|
|
2461
2744
|
}
|
|
2462
2745
|
logger.log("");
|
|
@@ -2466,10 +2749,10 @@ async function handleSetConfig(key, value) {
|
|
|
2466
2749
|
const typedValue = validateAndConvertValue(key, value);
|
|
2467
2750
|
storageService.setConfig(key, typedValue);
|
|
2468
2751
|
logger.log(
|
|
2469
|
-
|
|
2470
|
-
`${
|
|
2752
|
+
boxen8(
|
|
2753
|
+
`${chalk6.green.bold("\u2713 Configuration updated")}
|
|
2471
2754
|
|
|
2472
|
-
${
|
|
2755
|
+
${chalk6.white(key)}: ${chalk6.yellow(String(typedValue))}`,
|
|
2473
2756
|
{
|
|
2474
2757
|
padding: 1,
|
|
2475
2758
|
margin: 1,
|
|
@@ -2491,7 +2774,7 @@ ${VALID_CONFIG_KEYS.map((k) => ` - ${k}`).join("\n")}`
|
|
|
2491
2774
|
}
|
|
2492
2775
|
function validateAndConvertValue(key, value) {
|
|
2493
2776
|
switch (key) {
|
|
2494
|
-
case CONFIG_KEYS.DEFAULT_COMMIT_DAYS:
|
|
2777
|
+
case CONFIG_KEYS.DEFAULT_COMMIT_DAYS: {
|
|
2495
2778
|
const days = parseInt(value, 10);
|
|
2496
2779
|
if (isNaN(days) || days < 1 || days > 365) {
|
|
2497
2780
|
throw new ValidationError(
|
|
@@ -2501,7 +2784,8 @@ Must be a number between 1 and 365`
|
|
|
2501
2784
|
);
|
|
2502
2785
|
}
|
|
2503
2786
|
return days;
|
|
2504
|
-
|
|
2787
|
+
}
|
|
2788
|
+
case CONFIG_KEYS.AUTO_VERSION_CHECK: {
|
|
2505
2789
|
const lowerValue = value.toLowerCase();
|
|
2506
2790
|
if (lowerValue === "true" || lowerValue === "1" || lowerValue === "yes") {
|
|
2507
2791
|
return true;
|
|
@@ -2513,6 +2797,7 @@ Must be a number between 1 and 365`
|
|
|
2513
2797
|
|
|
2514
2798
|
Must be one of: true, false, yes, no, 1, 0`
|
|
2515
2799
|
);
|
|
2800
|
+
}
|
|
2516
2801
|
default:
|
|
2517
2802
|
throw new ValidationError(`Unknown config key: "${key}"`);
|
|
2518
2803
|
}
|
|
@@ -2573,7 +2858,7 @@ program.command("config [subcommand] [key] [value]").description("Manage CLI con
|
|
|
2573
2858
|
process.exit(1);
|
|
2574
2859
|
}
|
|
2575
2860
|
});
|
|
2576
|
-
program.hook("preAction", async (
|
|
2861
|
+
program.hook("preAction", async () => {
|
|
2577
2862
|
const options = program.opts();
|
|
2578
2863
|
if (options.debug) {
|
|
2579
2864
|
process.env.DEBUG = "true";
|
|
@@ -2583,7 +2868,7 @@ program.hook("preAction", async (_thisCommand) => {
|
|
|
2583
2868
|
if (!options.skipVersionCheck && !isVersionOrHelp) {
|
|
2584
2869
|
try {
|
|
2585
2870
|
await checkForUpdates({ silent: false });
|
|
2586
|
-
} catch
|
|
2871
|
+
} catch {
|
|
2587
2872
|
logger.debug("Version check failed, continuing...");
|
|
2588
2873
|
}
|
|
2589
2874
|
}
|