@bragduck/cli 2.38.1 → 2.39.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 +456 -12
- package/dist/bin/bragduck.js.map +1 -1
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/bragduck.js
CHANGED
|
@@ -63,7 +63,7 @@ var init_constants = __esm({
|
|
|
63
63
|
CLIENT_ID: "kJlsd66DLTnENE8p7Ru2JwqZg1Sie4yZ",
|
|
64
64
|
AUTH_URL: "https://auth.atlassian.com/authorize",
|
|
65
65
|
AUDIENCE: "api.atlassian.com",
|
|
66
|
-
SCOPES: "read:jira-work read:jira-user read:
|
|
66
|
+
SCOPES: "read:jira-work read:jira-user read:confluence-content.all read:confluence-user offline_access",
|
|
67
67
|
ACCESSIBLE_RESOURCES_URL: "https://api.atlassian.com/oauth/token/accessible-resources",
|
|
68
68
|
API_GATEWAY_URL: "https://api.atlassian.com"
|
|
69
69
|
};
|
|
@@ -104,6 +104,11 @@ var init_constants = __esm({
|
|
|
104
104
|
ORGANISATIONS: {
|
|
105
105
|
LIST: "/v1/users/{userId}/organisations"
|
|
106
106
|
},
|
|
107
|
+
STORIES: {
|
|
108
|
+
LIST: "/v1/users/{userId}/stories",
|
|
109
|
+
CREATE: "/v1/users/{userId}/stories",
|
|
110
|
+
DELETE: "/v1/users/{userId}/stories/{storyId}"
|
|
111
|
+
},
|
|
107
112
|
VERSION: "/v1/cli/version",
|
|
108
113
|
/**
|
|
109
114
|
* @deprecated Use BRAGS.REFINE instead
|
|
@@ -1622,6 +1627,70 @@ var init_api_service = __esm({
|
|
|
1622
1627
|
throw _error;
|
|
1623
1628
|
}
|
|
1624
1629
|
}
|
|
1630
|
+
/**
|
|
1631
|
+
* List user's stories
|
|
1632
|
+
*/
|
|
1633
|
+
async listStories(userId, params = {}) {
|
|
1634
|
+
const { page = 1, perPage = 20, search, competency, orgId } = params;
|
|
1635
|
+
logger.debug(`Listing stories: page=${page}, perPage=${perPage}`);
|
|
1636
|
+
try {
|
|
1637
|
+
const queryParams = new URLSearchParams6({
|
|
1638
|
+
page: page.toString(),
|
|
1639
|
+
perPage: perPage.toString()
|
|
1640
|
+
});
|
|
1641
|
+
if (search) queryParams.append("search", search);
|
|
1642
|
+
if (competency) queryParams.append("competency", competency);
|
|
1643
|
+
if (orgId) queryParams.append("orgId", orgId);
|
|
1644
|
+
const url = `${API_ENDPOINTS.STORIES.LIST.replace("{userId}", userId)}?${queryParams.toString()}`;
|
|
1645
|
+
const response = await this.makeRequest(url, {
|
|
1646
|
+
method: "GET"
|
|
1647
|
+
});
|
|
1648
|
+
logger.debug(
|
|
1649
|
+
`Successfully fetched ${response.items.length} stories (total: ${response.total})`
|
|
1650
|
+
);
|
|
1651
|
+
return response;
|
|
1652
|
+
} catch (_error) {
|
|
1653
|
+
logger.debug("Failed to list stories");
|
|
1654
|
+
throw _error;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Create a story
|
|
1659
|
+
*/
|
|
1660
|
+
async createStory(userId, body) {
|
|
1661
|
+
logger.debug(`Creating story: ${body.title}`);
|
|
1662
|
+
try {
|
|
1663
|
+
const url = API_ENDPOINTS.STORIES.CREATE.replace("{userId}", userId);
|
|
1664
|
+
const response = await this.makeRequest(url, {
|
|
1665
|
+
method: "POST",
|
|
1666
|
+
body
|
|
1667
|
+
});
|
|
1668
|
+
logger.debug(`Successfully created story: ${response.id}`);
|
|
1669
|
+
return response;
|
|
1670
|
+
} catch (_error) {
|
|
1671
|
+
logger.debug("Failed to create story");
|
|
1672
|
+
throw _error;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Delete a story (soft delete)
|
|
1677
|
+
*/
|
|
1678
|
+
async deleteStory(userId, storyId) {
|
|
1679
|
+
logger.debug(`Deleting story: ${storyId}`);
|
|
1680
|
+
try {
|
|
1681
|
+
const url = API_ENDPOINTS.STORIES.DELETE.replace("{userId}", userId).replace(
|
|
1682
|
+
"{storyId}",
|
|
1683
|
+
storyId
|
|
1684
|
+
);
|
|
1685
|
+
await this.makeRequest(url, {
|
|
1686
|
+
method: "DELETE"
|
|
1687
|
+
});
|
|
1688
|
+
logger.debug(`Successfully deleted story: ${storyId}`);
|
|
1689
|
+
} catch (_error) {
|
|
1690
|
+
logger.debug("Failed to delete story");
|
|
1691
|
+
throw _error;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1625
1694
|
/**
|
|
1626
1695
|
* List user's organisations
|
|
1627
1696
|
*/
|
|
@@ -1891,6 +1960,19 @@ var AtlassianAuthService = class {
|
|
|
1891
1960
|
callbackResult = await resultPromise;
|
|
1892
1961
|
} catch (error) {
|
|
1893
1962
|
storageService.deleteOAuthState();
|
|
1963
|
+
const rawMessage = error?.message ?? String(error);
|
|
1964
|
+
if (/invalid_scope/i.test(rawMessage)) {
|
|
1965
|
+
throw new AtlassianError(
|
|
1966
|
+
"Atlassian rejected the requested scopes. The app must list all of these classic scopes: read:jira-work, read:jira-user, read:confluence-content.all, read:confluence-user, offline_access.",
|
|
1967
|
+
{ originalError: rawMessage }
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
if (/redirect_uri|invalid_request/i.test(rawMessage)) {
|
|
1971
|
+
throw new AtlassianError(
|
|
1972
|
+
"Atlassian rejected the redirect URI. All of http://127.0.0.1:8000/callback through http://127.0.0.1:8004/callback must be registered in the Atlassian app callback URL list.",
|
|
1973
|
+
{ originalError: rawMessage }
|
|
1974
|
+
);
|
|
1975
|
+
}
|
|
1894
1976
|
throw error;
|
|
1895
1977
|
}
|
|
1896
1978
|
storageService.deleteOAuthState();
|
|
@@ -5957,6 +6039,12 @@ async function promptConfirm(message, defaultValue = true) {
|
|
|
5957
6039
|
default: defaultValue
|
|
5958
6040
|
});
|
|
5959
6041
|
}
|
|
6042
|
+
async function promptInput(message, defaultValue) {
|
|
6043
|
+
return await input2({
|
|
6044
|
+
message,
|
|
6045
|
+
default: defaultValue
|
|
6046
|
+
});
|
|
6047
|
+
}
|
|
5960
6048
|
async function promptDaysToScan(defaultDays = 30) {
|
|
5961
6049
|
const choices = [
|
|
5962
6050
|
{ name: "1 day", value: "1", description: "Last 24 hours" },
|
|
@@ -6204,6 +6292,13 @@ function createFetchSpinner(text) {
|
|
|
6204
6292
|
spinner: "bouncingBar"
|
|
6205
6293
|
});
|
|
6206
6294
|
}
|
|
6295
|
+
function createWriteSpinner(text) {
|
|
6296
|
+
return ora({
|
|
6297
|
+
text,
|
|
6298
|
+
color: "cyan",
|
|
6299
|
+
spinner: "dots"
|
|
6300
|
+
});
|
|
6301
|
+
}
|
|
6207
6302
|
function createValidateSpinner(text) {
|
|
6208
6303
|
return ora({
|
|
6209
6304
|
text,
|
|
@@ -6226,6 +6321,15 @@ function updateStepSpinner(spinner, currentStep, totalSteps, text) {
|
|
|
6226
6321
|
function fetchingBragsSpinner() {
|
|
6227
6322
|
return createFetchSpinner("Fetching your brags...");
|
|
6228
6323
|
}
|
|
6324
|
+
function fetchingStoriesSpinner() {
|
|
6325
|
+
return createFetchSpinner("Fetching your stories...");
|
|
6326
|
+
}
|
|
6327
|
+
function creatingStorySpinner() {
|
|
6328
|
+
return createWriteSpinner("Creating story...");
|
|
6329
|
+
}
|
|
6330
|
+
function deletingStorySpinner() {
|
|
6331
|
+
return createWriteSpinner("Deleting story...");
|
|
6332
|
+
}
|
|
6229
6333
|
function succeedSpinner(spinner, text) {
|
|
6230
6334
|
if (text) {
|
|
6231
6335
|
spinner.succeed(colors.success(text));
|
|
@@ -6522,6 +6626,7 @@ async function syncSingleRepository(repo, days, sortOption, scanMode, orgId, opt
|
|
|
6522
6626
|
);
|
|
6523
6627
|
createSpinner.start();
|
|
6524
6628
|
const createRequest = {
|
|
6629
|
+
mode: options.turbo ? "turbo" : "manual",
|
|
6525
6630
|
brags: acceptedBrags.map((refined, index) => {
|
|
6526
6631
|
const originalCommit = selectedCommits[index];
|
|
6527
6632
|
const repoTag = repo.name;
|
|
@@ -6950,6 +7055,7 @@ async function syncSingleService(sourceType, options, TOTAL_STEPS, sharedDays, s
|
|
|
6950
7055
|
);
|
|
6951
7056
|
createSpinner.start();
|
|
6952
7057
|
const createRequest = {
|
|
7058
|
+
mode: options.turbo ? "turbo" : "manual",
|
|
6953
7059
|
brags: acceptedBrags.map((refined, index) => {
|
|
6954
7060
|
const originalCommit = selectedCommits[index];
|
|
6955
7061
|
const isGitSource = sourceType === "github" || sourceType === "gitlab" || sourceType === "bitbucket" || sourceType === "atlassian";
|
|
@@ -7667,13 +7773,15 @@ function formatBragsTable(brags) {
|
|
|
7667
7773
|
return table.toString() + (links ? "\n\n" + links : "");
|
|
7668
7774
|
}
|
|
7669
7775
|
function formatBragLinks(brags) {
|
|
7670
|
-
const lines = brags.filter((brag) => brag.url || brag.
|
|
7776
|
+
const lines = brags.filter((brag) => brag.url || brag.keyResultIds && brag.keyResultIds.length > 0).map((brag) => {
|
|
7671
7777
|
const parts = [` ${colors.white(brag.title)}`];
|
|
7672
7778
|
if (brag.url) {
|
|
7673
7779
|
parts.push(` ${colors.info(brag.url)}`);
|
|
7674
7780
|
}
|
|
7675
|
-
if (brag.
|
|
7676
|
-
parts.push(
|
|
7781
|
+
if (brag.keyResultIds && brag.keyResultIds.length > 0) {
|
|
7782
|
+
parts.push(
|
|
7783
|
+
` ${colors.dim("Linked to key result:")} ${colors.primary(brag.keyResultIds[0])}`
|
|
7784
|
+
);
|
|
7677
7785
|
}
|
|
7678
7786
|
return parts.join("\n");
|
|
7679
7787
|
});
|
|
@@ -7685,9 +7793,9 @@ function formatBragsOneline(brags) {
|
|
|
7685
7793
|
const date = formatDate(brag.date);
|
|
7686
7794
|
const urlLine = brag.url ? `
|
|
7687
7795
|
${colors.info(brag.url)}` : "";
|
|
7688
|
-
const
|
|
7689
|
-
${colors.dim("Linked to key result:")} ${colors.primary(brag.
|
|
7690
|
-
return `${colors.highlight(date)} ${colors.white(brag.title)}${urlLine}${
|
|
7796
|
+
const keyResultLine = brag.keyResultIds && brag.keyResultIds.length > 0 ? `
|
|
7797
|
+
${colors.dim("Linked to key result:")} ${colors.primary(brag.keyResultIds[0])}` : "";
|
|
7798
|
+
return `${colors.highlight(date)} ${colors.white(brag.title)}${urlLine}${keyResultLine}`;
|
|
7691
7799
|
}).join("\n");
|
|
7692
7800
|
}
|
|
7693
7801
|
function truncateText(text, maxLength) {
|
|
@@ -7717,14 +7825,338 @@ function getErrorHint2(error) {
|
|
|
7717
7825
|
return "Try running with DEBUG=* for more information";
|
|
7718
7826
|
}
|
|
7719
7827
|
|
|
7828
|
+
// src/commands/story.ts
|
|
7829
|
+
init_esm_shims();
|
|
7830
|
+
init_api_service();
|
|
7831
|
+
init_auth_service();
|
|
7832
|
+
import boxen8 from "boxen";
|
|
7833
|
+
import Table3 from "cli-table3";
|
|
7834
|
+
init_logger();
|
|
7835
|
+
async function storyCommand(subcommand, idOrOptions, _options) {
|
|
7836
|
+
switch (subcommand) {
|
|
7837
|
+
case "list":
|
|
7838
|
+
case "ls":
|
|
7839
|
+
await storyListCommand(idOrOptions || {});
|
|
7840
|
+
break;
|
|
7841
|
+
case "create":
|
|
7842
|
+
case "add":
|
|
7843
|
+
await storyCreateCommand(idOrOptions || {});
|
|
7844
|
+
break;
|
|
7845
|
+
case "delete":
|
|
7846
|
+
case "rm":
|
|
7847
|
+
if (typeof idOrOptions === "string") {
|
|
7848
|
+
await storyDeleteCommand(idOrOptions);
|
|
7849
|
+
} else {
|
|
7850
|
+
logger.log("");
|
|
7851
|
+
logger.log(
|
|
7852
|
+
boxen8(
|
|
7853
|
+
formatErrorMessage("Missing story ID", "Usage: bragduck story delete <id>"),
|
|
7854
|
+
boxStyles.error
|
|
7855
|
+
)
|
|
7856
|
+
);
|
|
7857
|
+
process.exit(1);
|
|
7858
|
+
}
|
|
7859
|
+
break;
|
|
7860
|
+
default:
|
|
7861
|
+
logger.log("");
|
|
7862
|
+
logger.log(
|
|
7863
|
+
boxen8(
|
|
7864
|
+
colors.primary("Story Commands") + `
|
|
7865
|
+
|
|
7866
|
+
${theme.command("bragduck story list")} List your interview stories
|
|
7867
|
+
${theme.command("bragduck story create")} Create a new STAR story
|
|
7868
|
+
${theme.command("bragduck story delete")} Delete a story by ID
|
|
7869
|
+
|
|
7870
|
+
` + colors.dim("Aliases: ls, add, rm"),
|
|
7871
|
+
{ ...boxStyles.info, padding: 1 }
|
|
7872
|
+
)
|
|
7873
|
+
);
|
|
7874
|
+
break;
|
|
7875
|
+
}
|
|
7876
|
+
}
|
|
7877
|
+
async function storyListCommand(options) {
|
|
7878
|
+
logger.log("");
|
|
7879
|
+
try {
|
|
7880
|
+
const isAuthenticated = await ensureAuthenticated();
|
|
7881
|
+
if (!isAuthenticated) {
|
|
7882
|
+
process.exit(1);
|
|
7883
|
+
}
|
|
7884
|
+
const subSpinner = createValidateSpinner("Checking subscription...");
|
|
7885
|
+
subSpinner.start();
|
|
7886
|
+
let subscriptionStatus;
|
|
7887
|
+
try {
|
|
7888
|
+
subscriptionStatus = await apiService.getSubscriptionStatus();
|
|
7889
|
+
} catch (error) {
|
|
7890
|
+
failSpinner(subSpinner, "Failed to check subscription");
|
|
7891
|
+
throw error;
|
|
7892
|
+
}
|
|
7893
|
+
if (subscriptionStatus.tier === "FREE") {
|
|
7894
|
+
failSpinner(subSpinner, "Free plan \u2014 upgrade to use the CLI");
|
|
7895
|
+
showUpgradeMessage();
|
|
7896
|
+
return;
|
|
7897
|
+
}
|
|
7898
|
+
subSpinner.stop();
|
|
7899
|
+
const userInfo = authService.getUserInfo();
|
|
7900
|
+
if (!userInfo) {
|
|
7901
|
+
logger.log(boxen8(formatErrorMessage("Could not retrieve user info"), boxStyles.error));
|
|
7902
|
+
process.exit(1);
|
|
7903
|
+
}
|
|
7904
|
+
const perPage = options.limit || 20;
|
|
7905
|
+
const spinner = fetchingStoriesSpinner();
|
|
7906
|
+
spinner.start();
|
|
7907
|
+
const response = await apiService.listStories(userInfo.id, {
|
|
7908
|
+
perPage,
|
|
7909
|
+
search: options.search,
|
|
7910
|
+
competency: options.competency
|
|
7911
|
+
});
|
|
7912
|
+
if (response.items.length === 0) {
|
|
7913
|
+
failSpinner(spinner, "No stories found");
|
|
7914
|
+
logger.log("");
|
|
7915
|
+
if (options.search || options.competency) {
|
|
7916
|
+
logger.info("Try adjusting your filters or run without filters to see all stories");
|
|
7917
|
+
} else {
|
|
7918
|
+
logger.info(
|
|
7919
|
+
theme.secondary("Run ") + theme.command("bragduck story create") + theme.secondary(" to create your first interview story")
|
|
7920
|
+
);
|
|
7921
|
+
}
|
|
7922
|
+
return;
|
|
7923
|
+
}
|
|
7924
|
+
succeedSpinner(spinner, `Found ${response.total} stor${response.total > 1 ? "ies" : "y"}`);
|
|
7925
|
+
showCommandHeader(userInfo);
|
|
7926
|
+
logger.log("");
|
|
7927
|
+
logger.log(formatStoriesTable(response.items));
|
|
7928
|
+
logger.log("");
|
|
7929
|
+
if (response.total > response.items.length) {
|
|
7930
|
+
logger.info(`Showing ${response.items.length} of ${response.total} total stories`);
|
|
7931
|
+
logger.log("");
|
|
7932
|
+
}
|
|
7933
|
+
if (options.search || options.competency) {
|
|
7934
|
+
const filterInfo = [];
|
|
7935
|
+
if (options.search) filterInfo.push(`search: "${options.search}"`);
|
|
7936
|
+
if (options.competency) filterInfo.push(`competency: ${options.competency}`);
|
|
7937
|
+
logger.info(theme.info(`Filters applied: ${filterInfo.join(", ")}`));
|
|
7938
|
+
logger.log("");
|
|
7939
|
+
}
|
|
7940
|
+
} catch (error) {
|
|
7941
|
+
const err = error;
|
|
7942
|
+
logger.log("");
|
|
7943
|
+
logger.log(boxen8(formatErrorMessage(err.message, getErrorHint3(err)), boxStyles.error));
|
|
7944
|
+
process.exit(1);
|
|
7945
|
+
}
|
|
7946
|
+
}
|
|
7947
|
+
async function storyCreateCommand(options) {
|
|
7948
|
+
logger.log("");
|
|
7949
|
+
try {
|
|
7950
|
+
const isAuthenticated = await ensureAuthenticated();
|
|
7951
|
+
if (!isAuthenticated) {
|
|
7952
|
+
process.exit(1);
|
|
7953
|
+
}
|
|
7954
|
+
const subSpinner = createValidateSpinner("Checking subscription...");
|
|
7955
|
+
subSpinner.start();
|
|
7956
|
+
let subscriptionStatus;
|
|
7957
|
+
try {
|
|
7958
|
+
subscriptionStatus = await apiService.getSubscriptionStatus();
|
|
7959
|
+
} catch (error) {
|
|
7960
|
+
failSpinner(subSpinner, "Failed to check subscription");
|
|
7961
|
+
throw error;
|
|
7962
|
+
}
|
|
7963
|
+
if (subscriptionStatus.tier === "FREE") {
|
|
7964
|
+
failSpinner(subSpinner, "Free plan \u2014 upgrade to use the CLI");
|
|
7965
|
+
showUpgradeMessage();
|
|
7966
|
+
return;
|
|
7967
|
+
}
|
|
7968
|
+
subSpinner.stop();
|
|
7969
|
+
const userInfo = authService.getUserInfo();
|
|
7970
|
+
if (!userInfo) {
|
|
7971
|
+
logger.log(boxen8(formatErrorMessage("Could not retrieve user info"), boxStyles.error));
|
|
7972
|
+
process.exit(1);
|
|
7973
|
+
}
|
|
7974
|
+
logger.log(
|
|
7975
|
+
boxen8(
|
|
7976
|
+
colors.primary("Create Interview Story") + "\n\n" + colors.dim("Use the STAR format to structure your story:\n") + colors.dim("Situation, Task, Action, Result"),
|
|
7977
|
+
{ ...boxStyles.info, padding: 1 }
|
|
7978
|
+
)
|
|
7979
|
+
);
|
|
7980
|
+
logger.log("");
|
|
7981
|
+
const title = options.title || await promptInput("Story title (short summary):");
|
|
7982
|
+
if (!title.trim()) {
|
|
7983
|
+
logger.log(theme.error("Title is required"));
|
|
7984
|
+
process.exit(1);
|
|
7985
|
+
}
|
|
7986
|
+
const situation = await promptInput("Situation (context and background):");
|
|
7987
|
+
if (!situation.trim()) {
|
|
7988
|
+
logger.log(theme.error("Situation is required"));
|
|
7989
|
+
process.exit(1);
|
|
7990
|
+
}
|
|
7991
|
+
const task = await promptInput("Task (your responsibility or challenge):");
|
|
7992
|
+
if (!task.trim()) {
|
|
7993
|
+
logger.log(theme.error("Task is required"));
|
|
7994
|
+
process.exit(1);
|
|
7995
|
+
}
|
|
7996
|
+
const action = await promptInput("Action (what you did):");
|
|
7997
|
+
if (!action.trim()) {
|
|
7998
|
+
logger.log(theme.error("Action is required"));
|
|
7999
|
+
process.exit(1);
|
|
8000
|
+
}
|
|
8001
|
+
const result = await promptInput("Result (outcome and impact):");
|
|
8002
|
+
if (!result.trim()) {
|
|
8003
|
+
logger.log(theme.error("Result is required"));
|
|
8004
|
+
process.exit(1);
|
|
8005
|
+
}
|
|
8006
|
+
const competenciesInput = await promptInput(
|
|
8007
|
+
"Competencies (comma-separated, e.g. leadership, system design):",
|
|
8008
|
+
""
|
|
8009
|
+
);
|
|
8010
|
+
const competencies = competenciesInput ? competenciesInput.split(",").map((c) => c.trim()).filter(Boolean) : [];
|
|
8011
|
+
let orgId;
|
|
8012
|
+
try {
|
|
8013
|
+
const orgsResponse = await apiService.listUserOrganisations(userInfo.id);
|
|
8014
|
+
if (orgsResponse.items.length > 0) {
|
|
8015
|
+
const selectedOrg = await promptSelectOrganisationWithDefault(orgsResponse.items);
|
|
8016
|
+
if (selectedOrg) {
|
|
8017
|
+
orgId = selectedOrg;
|
|
8018
|
+
}
|
|
8019
|
+
}
|
|
8020
|
+
} catch {
|
|
8021
|
+
logger.debug("Failed to fetch organisations, skipping org selection");
|
|
8022
|
+
}
|
|
8023
|
+
logger.log("");
|
|
8024
|
+
logger.log(
|
|
8025
|
+
boxen8(
|
|
8026
|
+
colors.primary("Preview") + `
|
|
8027
|
+
|
|
8028
|
+
${theme.label("Title")} ${colors.white(title)}
|
|
8029
|
+
|
|
8030
|
+
${theme.label("Situation")} ${colors.white(situation)}
|
|
8031
|
+
|
|
8032
|
+
${theme.label("Task")} ${colors.white(task)}
|
|
8033
|
+
|
|
8034
|
+
${theme.label("Action")} ${colors.white(action)}
|
|
8035
|
+
|
|
8036
|
+
${theme.label("Result")} ${colors.white(result)}
|
|
8037
|
+
|
|
8038
|
+
${theme.label("Competencies")} ${competencies.length > 0 ? colors.primary(competencies.join(", ")) : colors.dim("none")}`,
|
|
8039
|
+
{ ...boxStyles.info, padding: 1 }
|
|
8040
|
+
)
|
|
8041
|
+
);
|
|
8042
|
+
logger.log("");
|
|
8043
|
+
const shouldCreate = await promptConfirm("Create this story?", true);
|
|
8044
|
+
if (!shouldCreate) {
|
|
8045
|
+
logger.log(theme.secondary("Story creation cancelled"));
|
|
8046
|
+
return;
|
|
8047
|
+
}
|
|
8048
|
+
const spinner = creatingStorySpinner();
|
|
8049
|
+
spinner.start();
|
|
8050
|
+
const body = {
|
|
8051
|
+
title,
|
|
8052
|
+
situation,
|
|
8053
|
+
task,
|
|
8054
|
+
action,
|
|
8055
|
+
result,
|
|
8056
|
+
competencies,
|
|
8057
|
+
source: "cli"
|
|
8058
|
+
};
|
|
8059
|
+
if (orgId) {
|
|
8060
|
+
body.orgId = orgId;
|
|
8061
|
+
}
|
|
8062
|
+
const story = await apiService.createStory(userInfo.id, body);
|
|
8063
|
+
succeedSpinner(spinner, `Story created: "${story.title}"`);
|
|
8064
|
+
logger.log("");
|
|
8065
|
+
} catch (error) {
|
|
8066
|
+
const err = error;
|
|
8067
|
+
logger.log("");
|
|
8068
|
+
logger.log(boxen8(formatErrorMessage(err.message, getErrorHint3(err)), boxStyles.error));
|
|
8069
|
+
process.exit(1);
|
|
8070
|
+
}
|
|
8071
|
+
}
|
|
8072
|
+
async function storyDeleteCommand(storyId) {
|
|
8073
|
+
logger.log("");
|
|
8074
|
+
try {
|
|
8075
|
+
const isAuthenticated = await ensureAuthenticated();
|
|
8076
|
+
if (!isAuthenticated) {
|
|
8077
|
+
process.exit(1);
|
|
8078
|
+
}
|
|
8079
|
+
const userInfo = authService.getUserInfo();
|
|
8080
|
+
if (!userInfo) {
|
|
8081
|
+
logger.log(boxen8(formatErrorMessage("Could not retrieve user info"), boxStyles.error));
|
|
8082
|
+
process.exit(1);
|
|
8083
|
+
}
|
|
8084
|
+
const shouldDelete = await promptConfirm(`Delete story ${storyId}?`, false);
|
|
8085
|
+
if (!shouldDelete) {
|
|
8086
|
+
logger.log(theme.secondary("Deletion cancelled"));
|
|
8087
|
+
return;
|
|
8088
|
+
}
|
|
8089
|
+
const spinner = deletingStorySpinner();
|
|
8090
|
+
spinner.start();
|
|
8091
|
+
await apiService.deleteStory(userInfo.id, storyId);
|
|
8092
|
+
succeedSpinner(spinner, "Story deleted");
|
|
8093
|
+
logger.log("");
|
|
8094
|
+
} catch (error) {
|
|
8095
|
+
const err = error;
|
|
8096
|
+
logger.log("");
|
|
8097
|
+
logger.log(boxen8(formatErrorMessage(err.message, getErrorHint3(err)), boxStyles.error));
|
|
8098
|
+
process.exit(1);
|
|
8099
|
+
}
|
|
8100
|
+
}
|
|
8101
|
+
function formatStoriesTable(stories) {
|
|
8102
|
+
const table = new Table3({
|
|
8103
|
+
head: [
|
|
8104
|
+
colors.primary("ID"),
|
|
8105
|
+
colors.primary("Title"),
|
|
8106
|
+
colors.primary("Competencies"),
|
|
8107
|
+
colors.primary("Updated")
|
|
8108
|
+
],
|
|
8109
|
+
colWidths: [12, 35, 30, 14],
|
|
8110
|
+
wordWrap: true,
|
|
8111
|
+
style: tableStyles.default.style
|
|
8112
|
+
});
|
|
8113
|
+
stories.forEach((story) => {
|
|
8114
|
+
const competencies = story.competencies.length > 0 ? story.competencies.join(", ") : colors.dim("none");
|
|
8115
|
+
const updated = formatDate(story.updatedAt);
|
|
8116
|
+
table.push([
|
|
8117
|
+
colors.dim(story.id),
|
|
8118
|
+
colors.white(story.title),
|
|
8119
|
+
colors.primary(competencies),
|
|
8120
|
+
colors.highlight(updated)
|
|
8121
|
+
]);
|
|
8122
|
+
});
|
|
8123
|
+
return table.toString();
|
|
8124
|
+
}
|
|
8125
|
+
function showUpgradeMessage() {
|
|
8126
|
+
logger.log("");
|
|
8127
|
+
logger.log(
|
|
8128
|
+
boxen8(
|
|
8129
|
+
theme.warning("CLI Access Requires Subscription") + "\n\nThe BragDuck CLI is available for Plus and Pro subscribers.\nUpgrade now to unlock:\n\n \u2022 List and manage your interview stories\n \u2022 Create STAR-format stories from the terminal\n \u2022 Automatic work item scanning\n\n" + colors.highlight("Upgrade at: https://bragduck.com/app/settings/plans"),
|
|
8130
|
+
{
|
|
8131
|
+
...boxStyles.warning,
|
|
8132
|
+
padding: 1,
|
|
8133
|
+
margin: 1
|
|
8134
|
+
}
|
|
8135
|
+
)
|
|
8136
|
+
);
|
|
8137
|
+
logger.log("");
|
|
8138
|
+
}
|
|
8139
|
+
function getErrorHint3(error) {
|
|
8140
|
+
if (error.name === "TokenExpiredError" || error.name === "AuthenticationError") {
|
|
8141
|
+
return 'Run "bragduck auth login" to login again';
|
|
8142
|
+
}
|
|
8143
|
+
if (error.name === "NetworkError") {
|
|
8144
|
+
return "Check your internet connection and try again";
|
|
8145
|
+
}
|
|
8146
|
+
if (error.name === "ApiError") {
|
|
8147
|
+
return "The server might be experiencing issues. Try again later";
|
|
8148
|
+
}
|
|
8149
|
+
return "Try running with DEBUG=* for more information";
|
|
8150
|
+
}
|
|
8151
|
+
|
|
7720
8152
|
// src/commands/config.ts
|
|
7721
8153
|
init_esm_shims();
|
|
7722
8154
|
init_storage_service();
|
|
7723
8155
|
init_logger();
|
|
7724
8156
|
init_constants();
|
|
7725
|
-
import
|
|
8157
|
+
import boxen9 from "boxen";
|
|
7726
8158
|
import chalk6 from "chalk";
|
|
7727
|
-
import
|
|
8159
|
+
import Table4 from "cli-table3";
|
|
7728
8160
|
init_errors();
|
|
7729
8161
|
var VALID_CONFIG_KEYS = Object.values(CONFIG_KEYS);
|
|
7730
8162
|
async function configCommand(subcommand, key, value) {
|
|
@@ -7758,7 +8190,7 @@ async function configCommand(subcommand, key, value) {
|
|
|
7758
8190
|
const err = error;
|
|
7759
8191
|
logger.log("");
|
|
7760
8192
|
logger.log(
|
|
7761
|
-
|
|
8193
|
+
boxen9(formatErrorMessage(err.message, getConfigHint(err)), {
|
|
7762
8194
|
padding: 1,
|
|
7763
8195
|
margin: 1,
|
|
7764
8196
|
borderStyle: "round",
|
|
@@ -7778,7 +8210,7 @@ async function handleListConfig() {
|
|
|
7778
8210
|
confluenceInstance: storageService.getConfig("confluenceInstance"),
|
|
7779
8211
|
gitlabInstance: storageService.getConfig("gitlabInstance")
|
|
7780
8212
|
};
|
|
7781
|
-
const table = new
|
|
8213
|
+
const table = new Table4({
|
|
7782
8214
|
head: [chalk6.cyan("Key"), chalk6.cyan("Value"), chalk6.cyan("Default")],
|
|
7783
8215
|
colWidths: [25, 40, 40],
|
|
7784
8216
|
wordWrap: true,
|
|
@@ -7852,7 +8284,7 @@ async function handleSetConfig(key, value) {
|
|
|
7852
8284
|
const typedValue = validateAndConvertValue(key, value);
|
|
7853
8285
|
storageService.setConfig(key, typedValue);
|
|
7854
8286
|
logger.log(
|
|
7855
|
-
|
|
8287
|
+
boxen9(
|
|
7856
8288
|
`${chalk6.green.bold("\u2713 Configuration updated")}
|
|
7857
8289
|
|
|
7858
8290
|
${chalk6.white(key)}: ${chalk6.yellow(String(typedValue))}`,
|
|
@@ -7984,6 +8416,18 @@ program.command("list").description("List your existing brags").option("-l, --li
|
|
|
7984
8416
|
process.exit(1);
|
|
7985
8417
|
}
|
|
7986
8418
|
});
|
|
8419
|
+
program.command("story [subcommand] [id]").description("Manage interview stories (subcommands: list, create, delete)").option("-l, --limit <number>", "Number of stories to display", (val) => parseInt(val, 10), 20).option("-s, --search <query>", "Search stories by keyword").option("-c, --competency <competency>", "Filter by competency").option("-t, --title <title>", "Story title (for create)").action(async (subcommand, id, options) => {
|
|
8420
|
+
try {
|
|
8421
|
+
if (subcommand === "delete" || subcommand === "rm") {
|
|
8422
|
+
await storyCommand(subcommand, id, options);
|
|
8423
|
+
} else {
|
|
8424
|
+
await storyCommand(subcommand, options);
|
|
8425
|
+
}
|
|
8426
|
+
} catch (error) {
|
|
8427
|
+
console.error(error);
|
|
8428
|
+
process.exit(1);
|
|
8429
|
+
}
|
|
8430
|
+
});
|
|
7987
8431
|
program.command("logout").description("Clear stored credentials").action(async () => {
|
|
7988
8432
|
try {
|
|
7989
8433
|
await logoutCommand();
|