@bragduck/cli 2.7.0 → 2.8.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/index.js CHANGED
@@ -22,7 +22,7 @@ var init_esm_shims = __esm({
22
22
  import { config } from "dotenv";
23
23
  import { fileURLToPath as fileURLToPath2 } from "url";
24
24
  import { dirname, join } from "path";
25
- var __filename, __dirname, APP_NAME, DEFAULT_CONFIG, OAUTH_CONFIG, API_ENDPOINTS, ENCRYPTION_CONFIG, STORAGE_PATHS, HTTP_STATUS;
25
+ var __filename, __dirname, APP_NAME, DEFAULT_CONFIG, OAUTH_CONFIG, API_ENDPOINTS, ENCRYPTION_CONFIG, STORAGE_PATHS, HTTP_STATUS, CONFIG_FILES;
26
26
  var init_constants = __esm({
27
27
  "src/constants.ts"() {
28
28
  "use strict";
@@ -33,7 +33,12 @@ var init_constants = __esm({
33
33
  APP_NAME = "bragduck";
34
34
  DEFAULT_CONFIG = {
35
35
  defaultCommitDays: 30,
36
- autoVersionCheck: true
36
+ autoVersionCheck: true,
37
+ defaultSource: void 0,
38
+ sourcePriority: void 0,
39
+ jiraInstance: void 0,
40
+ confluenceInstance: void 0,
41
+ gitlabInstance: void 0
37
42
  };
38
43
  OAUTH_CONFIG = {
39
44
  CLIENT_ID: "bragduck-cli",
@@ -88,6 +93,222 @@ var init_constants = __esm({
88
93
  NOT_FOUND: 404,
89
94
  INTERNAL_SERVER_ERROR: 500
90
95
  };
96
+ CONFIG_FILES = [".bragduckrc", ".bragduck/config.json", ".bragduckrc.json"];
97
+ }
98
+ });
99
+
100
+ // src/utils/env-loader.ts
101
+ function parseEnvNumber(key) {
102
+ const value = process.env[key];
103
+ if (!value) return void 0;
104
+ const parsed = parseInt(value, 10);
105
+ return isNaN(parsed) ? void 0 : parsed;
106
+ }
107
+ function parseEnvBoolean(key) {
108
+ const value = process.env[key]?.toLowerCase();
109
+ if (!value) return void 0;
110
+ if (["true", "yes", "1"].includes(value)) return true;
111
+ if (["false", "no", "0"].includes(value)) return false;
112
+ return void 0;
113
+ }
114
+ function parseEnvArray(key) {
115
+ const value = process.env[key];
116
+ if (!value) return void 0;
117
+ const items = value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
118
+ return items.length > 0 ? items : void 0;
119
+ }
120
+ function parseEnvSource(key) {
121
+ const value = process.env[key];
122
+ if (!value) return void 0;
123
+ const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
124
+ return validSources.includes(value) ? value : void 0;
125
+ }
126
+ function loadEnvConfig() {
127
+ return {
128
+ // Source configuration
129
+ source: parseEnvSource("BRAGDUCK_SOURCE"),
130
+ jiraInstance: process.env.BRAGDUCK_JIRA_INSTANCE,
131
+ confluenceInstance: process.env.BRAGDUCK_CONFLUENCE_INSTANCE,
132
+ gitlabInstance: process.env.BRAGDUCK_GITLAB_INSTANCE,
133
+ // Behavior configuration
134
+ defaultCommitDays: parseEnvNumber("BRAGDUCK_DEFAULT_COMMIT_DAYS"),
135
+ autoVersionCheck: parseEnvBoolean("BRAGDUCK_AUTO_VERSION_CHECK"),
136
+ sourcePriority: parseEnvArray("BRAGDUCK_SOURCE_PRIORITY"),
137
+ // Credential overrides (for CI/CD)
138
+ jiraToken: process.env.BRAGDUCK_JIRA_TOKEN,
139
+ confluenceToken: process.env.BRAGDUCK_CONFLUENCE_TOKEN,
140
+ gitlabToken: process.env.BRAGDUCK_GITLAB_TOKEN
141
+ };
142
+ }
143
+ var init_env_loader = __esm({
144
+ "src/utils/env-loader.ts"() {
145
+ "use strict";
146
+ init_esm_shims();
147
+ }
148
+ });
149
+
150
+ // src/utils/errors.ts
151
+ var BragduckError, AuthenticationError, ApiError, NetworkError, ValidationError, OAuthError, TokenExpiredError;
152
+ var init_errors = __esm({
153
+ "src/utils/errors.ts"() {
154
+ "use strict";
155
+ init_esm_shims();
156
+ BragduckError = class extends Error {
157
+ code;
158
+ details;
159
+ constructor(message, code, details) {
160
+ super(message);
161
+ this.name = "BragduckError";
162
+ this.code = code;
163
+ this.details = details;
164
+ Error.captureStackTrace(this, this.constructor);
165
+ }
166
+ };
167
+ AuthenticationError = class extends BragduckError {
168
+ constructor(message, details) {
169
+ super(message, "AUTH_ERROR", details);
170
+ this.name = "AuthenticationError";
171
+ }
172
+ };
173
+ ApiError = class extends BragduckError {
174
+ statusCode;
175
+ constructor(message, statusCode, details) {
176
+ super(message, "API_ERROR", details);
177
+ this.name = "ApiError";
178
+ this.statusCode = statusCode;
179
+ }
180
+ };
181
+ NetworkError = class extends BragduckError {
182
+ constructor(message, details) {
183
+ super(message, "NETWORK_ERROR", details);
184
+ this.name = "NetworkError";
185
+ }
186
+ };
187
+ ValidationError = class extends BragduckError {
188
+ constructor(message, details) {
189
+ super(message, "VALIDATION_ERROR", details);
190
+ this.name = "ValidationError";
191
+ }
192
+ };
193
+ OAuthError = class extends AuthenticationError {
194
+ constructor(message, details) {
195
+ super(message, details);
196
+ this.name = "OAuthError";
197
+ }
198
+ };
199
+ TokenExpiredError = class extends AuthenticationError {
200
+ constructor(message = "Authentication token has expired") {
201
+ super(message);
202
+ this.name = "TokenExpiredError";
203
+ this.code = "TOKEN_EXPIRED";
204
+ }
205
+ };
206
+ }
207
+ });
208
+
209
+ // src/utils/config-loader.ts
210
+ import { promises as fs } from "fs";
211
+ import path2 from "path";
212
+ async function findProjectConfig() {
213
+ const cwd = process.cwd();
214
+ if (configCache.has(cwd)) {
215
+ return configCache.get(cwd) || null;
216
+ }
217
+ for (const filename of CONFIG_FILES) {
218
+ const configPath = path2.join(cwd, filename);
219
+ try {
220
+ await fs.access(configPath);
221
+ const config2 = await loadAndValidateConfig(configPath);
222
+ configCache.set(cwd, config2);
223
+ return config2;
224
+ } catch {
225
+ continue;
226
+ }
227
+ }
228
+ configCache.set(cwd, null);
229
+ return null;
230
+ }
231
+ async function loadAndValidateConfig(configPath) {
232
+ try {
233
+ const content = await fs.readFile(configPath, "utf-8");
234
+ const config2 = JSON.parse(content);
235
+ validateProjectConfig(config2);
236
+ return config2;
237
+ } catch (error) {
238
+ if (error instanceof SyntaxError) {
239
+ throw new ValidationError(`Invalid JSON in config file: ${configPath}`, {
240
+ originalError: error.message
241
+ });
242
+ }
243
+ throw error;
244
+ }
245
+ }
246
+ function validateProjectConfig(config2) {
247
+ if (typeof config2 !== "object" || config2 === null) {
248
+ throw new ValidationError("Config must be a JSON object");
249
+ }
250
+ const cfg = config2;
251
+ if (cfg.defaultCommitDays !== void 0) {
252
+ if (typeof cfg.defaultCommitDays !== "number") {
253
+ throw new ValidationError("defaultCommitDays must be a number");
254
+ }
255
+ if (cfg.defaultCommitDays < 1 || cfg.defaultCommitDays > 365) {
256
+ throw new ValidationError("defaultCommitDays must be between 1 and 365");
257
+ }
258
+ }
259
+ if (cfg.autoVersionCheck !== void 0) {
260
+ if (typeof cfg.autoVersionCheck !== "boolean") {
261
+ throw new ValidationError("autoVersionCheck must be a boolean");
262
+ }
263
+ }
264
+ if (cfg.defaultSource !== void 0) {
265
+ const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
266
+ if (!validSources.includes(cfg.defaultSource)) {
267
+ throw new ValidationError(
268
+ `Invalid defaultSource: ${cfg.defaultSource}. Must be one of: ${validSources.join(", ")}`
269
+ );
270
+ }
271
+ }
272
+ if (cfg.sourcePriority !== void 0) {
273
+ if (!Array.isArray(cfg.sourcePriority)) {
274
+ throw new ValidationError("sourcePriority must be an array");
275
+ }
276
+ const validSources = ["github", "gitlab", "bitbucket", "jira", "confluence"];
277
+ for (const source of cfg.sourcePriority) {
278
+ if (!validSources.includes(source)) {
279
+ throw new ValidationError(
280
+ `Invalid source in sourcePriority: ${source}. Must be one of: ${validSources.join(", ")}`
281
+ );
282
+ }
283
+ }
284
+ }
285
+ const instanceFields = ["jiraInstance", "confluenceInstance", "gitlabInstance"];
286
+ for (const field of instanceFields) {
287
+ if (cfg[field] !== void 0) {
288
+ if (typeof cfg[field] !== "string") {
289
+ throw new ValidationError(`${field} must be a string`);
290
+ }
291
+ const value = cfg[field];
292
+ if (!value.match(/^[a-zA-Z0-9.-]+(:[0-9]+)?(\/.*)?$/)) {
293
+ throw new ValidationError(`Invalid ${field}: ${value}`);
294
+ }
295
+ }
296
+ }
297
+ const booleanFields = ["requireAuthentication", "skipSourceDetection"];
298
+ for (const field of booleanFields) {
299
+ if (cfg[field] !== void 0 && typeof cfg[field] !== "boolean") {
300
+ throw new ValidationError(`${field} must be a boolean`);
301
+ }
302
+ }
303
+ }
304
+ var configCache;
305
+ var init_config_loader = __esm({
306
+ "src/utils/config-loader.ts"() {
307
+ "use strict";
308
+ init_esm_shims();
309
+ init_constants();
310
+ init_errors();
311
+ configCache = /* @__PURE__ */ new Map();
91
312
  }
92
313
  });
93
314
 
@@ -103,6 +324,8 @@ var init_storage_service = __esm({
103
324
  "use strict";
104
325
  init_esm_shims();
105
326
  init_constants();
327
+ init_env_loader();
328
+ init_config_loader();
106
329
  StorageService = class {
107
330
  config;
108
331
  storageBackend;
@@ -341,7 +564,27 @@ var init_storage_service = __esm({
341
564
  this.config.delete("oauthState");
342
565
  }
343
566
  /**
344
- * Get configuration value
567
+ * Get configuration value with hierarchy resolution
568
+ * Priority: env vars > project config > user config > defaults
569
+ */
570
+ async getConfigWithHierarchy(key) {
571
+ const envConfig = loadEnvConfig();
572
+ const envKey = key === "defaultSource" ? "source" : key;
573
+ if (envConfig[envKey] !== void 0) {
574
+ return envConfig[envKey];
575
+ }
576
+ const projectConfig = await findProjectConfig();
577
+ if (projectConfig && projectConfig[key] !== void 0) {
578
+ return projectConfig[key];
579
+ }
580
+ const userValue = this.config.get(key);
581
+ if (userValue !== void 0) {
582
+ return userValue;
583
+ }
584
+ return DEFAULT_CONFIG[key];
585
+ }
586
+ /**
587
+ * Get configuration value (uses hierarchy resolution)
345
588
  */
346
589
  getConfig(key) {
347
590
  return this.config.get(key);
@@ -353,7 +596,32 @@ var init_storage_service = __esm({
353
596
  this.config.set(key, value);
354
597
  }
355
598
  /**
356
- * Get all configuration
599
+ * Get all configuration (merges all layers)
600
+ * Priority: env vars > project config > user config > defaults
601
+ */
602
+ async getAllConfigWithHierarchy() {
603
+ const envConfig = loadEnvConfig();
604
+ const projectConfig = await findProjectConfig();
605
+ const userConfig = this.config.store;
606
+ return {
607
+ ...DEFAULT_CONFIG,
608
+ ...userConfig,
609
+ ...projectConfig,
610
+ ...envConfig.source && { defaultSource: envConfig.source },
611
+ ...envConfig.jiraInstance && { jiraInstance: envConfig.jiraInstance },
612
+ ...envConfig.confluenceInstance && { confluenceInstance: envConfig.confluenceInstance },
613
+ ...envConfig.gitlabInstance && { gitlabInstance: envConfig.gitlabInstance },
614
+ ...envConfig.defaultCommitDays !== void 0 && {
615
+ defaultCommitDays: envConfig.defaultCommitDays
616
+ },
617
+ ...envConfig.autoVersionCheck !== void 0 && {
618
+ autoVersionCheck: envConfig.autoVersionCheck
619
+ },
620
+ ...envConfig.sourcePriority && { sourcePriority: envConfig.sourcePriority }
621
+ };
622
+ }
623
+ /**
624
+ * Get all configuration (user config only)
357
625
  */
358
626
  getAllConfig() {
359
627
  return this.config.store;
@@ -364,7 +632,9 @@ var init_storage_service = __esm({
364
632
  resetConfig() {
365
633
  this.config.clear();
366
634
  Object.entries(DEFAULT_CONFIG).forEach(([key, value]) => {
367
- this.config.set(key, value);
635
+ if (value !== void 0) {
636
+ this.config.set(key, value);
637
+ }
368
638
  });
369
639
  }
370
640
  /**
@@ -387,59 +657,6 @@ var init_storage_service = __esm({
387
657
  }
388
658
  });
389
659
 
390
- // src/utils/errors.ts
391
- var BragduckError, AuthenticationError, ApiError, NetworkError, OAuthError, TokenExpiredError;
392
- var init_errors = __esm({
393
- "src/utils/errors.ts"() {
394
- "use strict";
395
- init_esm_shims();
396
- BragduckError = class extends Error {
397
- code;
398
- details;
399
- constructor(message, code, details) {
400
- super(message);
401
- this.name = "BragduckError";
402
- this.code = code;
403
- this.details = details;
404
- Error.captureStackTrace(this, this.constructor);
405
- }
406
- };
407
- AuthenticationError = class extends BragduckError {
408
- constructor(message, details) {
409
- super(message, "AUTH_ERROR", details);
410
- this.name = "AuthenticationError";
411
- }
412
- };
413
- ApiError = class extends BragduckError {
414
- statusCode;
415
- constructor(message, statusCode, details) {
416
- super(message, "API_ERROR", details);
417
- this.name = "ApiError";
418
- this.statusCode = statusCode;
419
- }
420
- };
421
- NetworkError = class extends BragduckError {
422
- constructor(message, details) {
423
- super(message, "NETWORK_ERROR", details);
424
- this.name = "NetworkError";
425
- }
426
- };
427
- OAuthError = class extends AuthenticationError {
428
- constructor(message, details) {
429
- super(message, details);
430
- this.name = "OAuthError";
431
- }
432
- };
433
- TokenExpiredError = class extends AuthenticationError {
434
- constructor(message = "Authentication token has expired") {
435
- super(message);
436
- this.name = "TokenExpiredError";
437
- this.code = "TOKEN_EXPIRED";
438
- }
439
- };
440
- }
441
- });
442
-
443
660
  // src/utils/logger.ts
444
661
  import chalk from "chalk";
445
662
  var logger;