gitlab-auto-reviewers 2.0.1 → 2.1.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.
Files changed (76) hide show
  1. package/LICENSE +1 -1
  2. package/dist/bin/cli.js +17 -29
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/bin/index.d.ts +1 -8
  5. package/dist/bin/index.d.ts.map +1 -1
  6. package/dist/bin/index.js +26 -9
  7. package/dist/bin/index.js.map +1 -1
  8. package/dist/bin/mcp.js +0 -0
  9. package/dist/cli/cicd-integration.d.ts.map +1 -1
  10. package/dist/cli/cicd-integration.js +12 -8
  11. package/dist/cli/cicd-integration.js.map +1 -1
  12. package/dist/cli/commands.d.ts.map +1 -1
  13. package/dist/cli/commands.js +113 -27
  14. package/dist/cli/commands.js.map +1 -1
  15. package/dist/cli/config.d.ts +7 -0
  16. package/dist/cli/config.d.ts.map +1 -1
  17. package/dist/cli/config.js +23 -12
  18. package/dist/cli/config.js.map +1 -1
  19. package/dist/cli/data-source-selector.d.ts +6 -0
  20. package/dist/cli/data-source-selector.d.ts.map +1 -1
  21. package/dist/cli/data-source-selector.js +58 -19
  22. package/dist/cli/data-source-selector.js.map +1 -1
  23. package/dist/cli/enhanced-cli.d.ts +104 -0
  24. package/dist/cli/enhanced-cli.d.ts.map +1 -0
  25. package/dist/cli/enhanced-cli.js +321 -0
  26. package/dist/cli/enhanced-cli.js.map +1 -0
  27. package/dist/cli/enhanced-data-source-resolver.d.ts +67 -0
  28. package/dist/cli/enhanced-data-source-resolver.d.ts.map +1 -0
  29. package/dist/cli/enhanced-data-source-resolver.js +249 -0
  30. package/dist/cli/enhanced-data-source-resolver.js.map +1 -0
  31. package/dist/cli/enhanced-parameter-resolver.d.ts +111 -0
  32. package/dist/cli/enhanced-parameter-resolver.d.ts.map +1 -0
  33. package/dist/cli/enhanced-parameter-resolver.js +233 -0
  34. package/dist/cli/enhanced-parameter-resolver.js.map +1 -0
  35. package/dist/cli/errors.d.ts.map +1 -1
  36. package/dist/cli/errors.js +4 -0
  37. package/dist/cli/errors.js.map +1 -1
  38. package/dist/cli/parameter-source-tracker.d.ts +169 -0
  39. package/dist/cli/parameter-source-tracker.d.ts.map +1 -0
  40. package/dist/cli/parameter-source-tracker.js +252 -0
  41. package/dist/cli/parameter-source-tracker.js.map +1 -0
  42. package/dist/config/config.service.d.ts +24 -2
  43. package/dist/config/config.service.d.ts.map +1 -1
  44. package/dist/config/config.service.js +39 -6
  45. package/dist/config/config.service.js.map +1 -1
  46. package/dist/datasources/local-git-data-source.d.ts +26 -0
  47. package/dist/datasources/local-git-data-source.d.ts.map +1 -1
  48. package/dist/datasources/local-git-data-source.js +97 -1
  49. package/dist/datasources/local-git-data-source.js.map +1 -1
  50. package/dist/enhanced-config.js +26 -0
  51. package/dist/index.js +0 -0
  52. package/dist/mcp/server.js +2 -2
  53. package/dist/mcp/server.js.map +1 -1
  54. package/dist/services/blacklist.service.d.ts +11 -5
  55. package/dist/services/blacklist.service.d.ts.map +1 -1
  56. package/dist/services/blacklist.service.js +28 -7
  57. package/dist/services/blacklist.service.js.map +1 -1
  58. package/dist/services/reviewer-service.d.ts +2 -0
  59. package/dist/services/reviewer-service.d.ts.map +1 -1
  60. package/dist/services/reviewer-service.js +99 -19
  61. package/dist/services/reviewer-service.js.map +1 -1
  62. package/dist/services/whitelist.service.js +1 -1
  63. package/dist/services/whitelist.service.js.map +1 -1
  64. package/dist/types/enhanced-config.d.ts +121 -0
  65. package/dist/types/enhanced-config.d.ts.map +1 -0
  66. package/dist/types/enhanced-config.js +27 -0
  67. package/dist/types/enhanced-config.js.map +1 -0
  68. package/package.json +9 -8
  69. package/dist/bin/deprecated-mcp.d.ts +0 -11
  70. package/dist/bin/deprecated-mcp.d.ts.map +0 -1
  71. package/dist/bin/deprecated-mcp.js +0 -44
  72. package/dist/bin/deprecated-mcp.js.map +0 -1
  73. package/dist/tools.d.ts +0 -22
  74. package/dist/tools.d.ts.map +0 -1
  75. package/dist/tools.js +0 -176
  76. package/dist/tools.js.map +0 -1
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Parameter Source Tracker
3
+ *
4
+ * Tracks and logs the source of each configuration parameter for debugging.
5
+ * Implements Requirement 3.5: THE System SHALL log the source of each configuration value for debugging
6
+ *
7
+ * @module cli/parameter-source-tracker
8
+ */
9
+ /**
10
+ * Parameter Source Tracker
11
+ *
12
+ * Tracks the source of each configuration parameter and provides
13
+ * comprehensive logging for debugging configuration issues.
14
+ */
15
+ export class ParameterSourceTracker {
16
+ parameters = new Map();
17
+ logger;
18
+ /**
19
+ * Creates a new ParameterSourceTracker instance
20
+ *
21
+ * @param logger - Logger instance for outputting parameter source information
22
+ */
23
+ constructor(logger) {
24
+ this.logger = logger;
25
+ }
26
+ /**
27
+ * Track a parameter with its value and source
28
+ */
29
+ track(name, value, source, options) {
30
+ this.parameters.set(name, {
31
+ name,
32
+ value,
33
+ source,
34
+ sourceDetail: options?.sourceDetail,
35
+ masked: options?.masked,
36
+ });
37
+ }
38
+ /**
39
+ * Resolve a parameter value with source tracking
40
+ * Implements the precedence: CLI argument > environment variable > auto-detected > config file > default
41
+ */
42
+ resolve(name, cliValue, envVarName, autoDetectFn, configValue, defaultValue, options) {
43
+ // Priority 1: CLI argument
44
+ if (cliValue !== undefined) {
45
+ this.track(name, cliValue, 'cli-argument', {
46
+ sourceDetail: `--${this.toKebabCase(name)}`,
47
+ masked: options?.masked,
48
+ });
49
+ return cliValue;
50
+ }
51
+ // Priority 2: Environment variable
52
+ if (envVarName) {
53
+ const envValue = process.env[envVarName];
54
+ if (envValue !== undefined && envValue !== '') {
55
+ const parsedValue = this.parseEnvValue(envValue);
56
+ this.track(name, parsedValue, 'environment', {
57
+ sourceDetail: envVarName,
58
+ masked: options?.masked,
59
+ });
60
+ return parsedValue;
61
+ }
62
+ }
63
+ // Priority 3: Auto-detection
64
+ if (autoDetectFn) {
65
+ const autoValue = autoDetectFn();
66
+ if (autoValue !== undefined) {
67
+ this.track(name, autoValue, 'auto-detected', {
68
+ masked: options?.masked,
69
+ });
70
+ return autoValue;
71
+ }
72
+ }
73
+ // Priority 4: Config file
74
+ if (configValue !== undefined) {
75
+ this.track(name, configValue, 'config-file', {
76
+ masked: options?.masked,
77
+ });
78
+ return configValue;
79
+ }
80
+ // Priority 5: Default value
81
+ if (defaultValue !== undefined) {
82
+ this.track(name, defaultValue, 'default', {
83
+ masked: options?.masked,
84
+ });
85
+ return defaultValue;
86
+ }
87
+ // Not set
88
+ this.track(name, undefined, 'not-set', {
89
+ masked: options?.masked,
90
+ });
91
+ return undefined;
92
+ }
93
+ /**
94
+ * Get all tracked parameters
95
+ */
96
+ getAll() {
97
+ return new Map(this.parameters);
98
+ }
99
+ /**
100
+ * Get a specific tracked parameter
101
+ *
102
+ * @param name - The name of the parameter to retrieve
103
+ * @returns The tracked parameter or undefined if not found
104
+ */
105
+ get(name) {
106
+ return this.parameters.get(name);
107
+ }
108
+ /**
109
+ * Log all parameter sources
110
+ * This implements Requirement 3.5
111
+ */
112
+ logParameterSources() {
113
+ this.logger.info('Configuration resolved - parameter sources:');
114
+ const sortedParams = Array.from(this.parameters.entries()).sort(([a], [b]) => a.localeCompare(b));
115
+ for (const [name, param] of sortedParams) {
116
+ const displayValue = this.formatValueForLog(param);
117
+ const sourceInfo = this.formatSourceForLog(param);
118
+ this.logger.info(` ${name}: ${displayValue} (${sourceInfo})`);
119
+ }
120
+ }
121
+ /**
122
+ * Log parameter sources in a structured format (for JSON output)
123
+ */
124
+ toStructuredLog() {
125
+ const result = {};
126
+ for (const [name, param] of this.parameters) {
127
+ result[name] = {
128
+ value: param.masked ? '***' : param.value,
129
+ source: param.source,
130
+ sourceDetail: param.sourceDetail,
131
+ };
132
+ }
133
+ return result;
134
+ }
135
+ /**
136
+ * Format value for logging (handles masking and undefined)
137
+ */
138
+ formatValueForLog(param) {
139
+ if (param.value === undefined) {
140
+ return '<not set>';
141
+ }
142
+ if (param.masked) {
143
+ return '***';
144
+ }
145
+ if (typeof param.value === 'string') {
146
+ // Truncate long strings
147
+ if (param.value.length > 50) {
148
+ return `"${param.value.substring(0, 47)}..."`;
149
+ }
150
+ return `"${param.value}"`;
151
+ }
152
+ return String(param.value);
153
+ }
154
+ /**
155
+ * Format source information for logging
156
+ */
157
+ formatSourceForLog(param) {
158
+ switch (param.source) {
159
+ case 'cli-argument':
160
+ return `CLI argument ${param.sourceDetail || ''}`.trim();
161
+ case 'environment':
162
+ return `environment variable ${param.sourceDetail || ''}`.trim();
163
+ case 'auto-detected':
164
+ return param.sourceDetail ? `auto-detected from ${param.sourceDetail}` : 'auto-detected';
165
+ case 'config-file':
166
+ return param.sourceDetail ? `config file ${param.sourceDetail}` : 'config file';
167
+ case 'default':
168
+ return 'default';
169
+ case 'not-set':
170
+ return 'not set';
171
+ default:
172
+ return param.source;
173
+ }
174
+ }
175
+ /**
176
+ * Convert camelCase to kebab-case
177
+ */
178
+ toKebabCase(str) {
179
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
180
+ }
181
+ /**
182
+ * Parse environment variable value to appropriate type
183
+ */
184
+ parseEnvValue(value) {
185
+ // Try to parse as number
186
+ const num = Number(value);
187
+ if (!isNaN(num) && value.trim() !== '') {
188
+ return num;
189
+ }
190
+ // Try to parse as boolean
191
+ if (value.toLowerCase() === 'true') {
192
+ return true;
193
+ }
194
+ if (value.toLowerCase() === 'false') {
195
+ return false;
196
+ }
197
+ // Return as string
198
+ return value;
199
+ }
200
+ }
201
+ /**
202
+ * Create a parameter source tracker and resolve all CLI parameters
203
+ *
204
+ * @param options - CLI options from argument parsing
205
+ * @param cicdEnv - CI/CD environment detection result
206
+ * @param logger - Logger instance
207
+ * @returns Resolved parameters with source tracking
208
+ */
209
+ export function resolveParametersWithTracking(options, cicdEnv, logger) {
210
+ const tracker = new ParameterSourceTracker(logger);
211
+ // Resolve each parameter with source tracking
212
+ // NOTE: We need to resolve project FIRST to determine if we should auto-detect repoPath
213
+ // When both project ID and project directory are available in CI, prefer API mode (project ID)
214
+ // because it provides more reliable data access in CI environments
215
+ const project = tracker.resolve('project', options.project, 'CI_PROJECT_ID', // Also check CI_PROJECT_ID environment variable
216
+ () => cicdEnv.isCI ? cicdEnv.projectId : undefined, undefined, undefined);
217
+ // Only auto-detect repoPath from CI_PROJECT_DIR if project is NOT already set
218
+ // This prevents the "Cannot specify both --repo-path and --project" conflict
219
+ const repoPath = tracker.resolve('repoPath', options.repoPath, project ? undefined : 'CI_PROJECT_DIR', // Only use CI_PROJECT_DIR if project is not set
220
+ undefined, undefined, undefined);
221
+ // Resolve mergeRequestIid FIRST to determine if we should auto-detect branch
222
+ // When both MR IID and branch are available in CI, prefer MR IID because
223
+ // it provides more specific context for reviewer suggestions
224
+ const mergeRequestIid = tracker.resolve('mergeRequestIid', options.mergeRequestIid, 'CI_MERGE_REQUEST_IID', () => cicdEnv.isCI ? cicdEnv.mergeRequestIid : undefined, undefined, undefined);
225
+ // Only auto-detect branch from CI_COMMIT_REF_NAME if mergeRequestIid is NOT already set
226
+ // This prevents the "Cannot specify both merge request IID and branch name" conflict
227
+ const branch = tracker.resolve('branch', options.branch, mergeRequestIid ? undefined : 'CI_COMMIT_REF_NAME', // Only use CI_COMMIT_REF_NAME if MR IID is not set
228
+ () => (cicdEnv.isCI && !mergeRequestIid) ? cicdEnv.branch : undefined, undefined, undefined);
229
+ const gitlabUrl = tracker.resolve('gitlabUrl', options.gitlabUrl, 'GITLAB_URL', () => process.env.CI_SERVER_URL, // Auto-detect from GitLab CI environment
230
+ undefined, 'https://gitlab.com') || 'https://gitlab.com';
231
+ const gitlabToken = tracker.resolve('gitlabToken', options.gitlabToken, 'GITLAB_TOKEN', () => process.env.CI_JOB_TOKEN, undefined, undefined, { masked: true });
232
+ const format = tracker.resolve('format', options.format, undefined, () => cicdEnv.isCI ? 'json' : undefined, undefined, 'text') || 'text';
233
+ const verbose = tracker.resolve('verbose', options.verbose, undefined, undefined, undefined, false) || false;
234
+ const dryRun = tracker.resolve('dryRun', options.dryRun, undefined, undefined, undefined, false) || false;
235
+ const quiet = tracker.resolve('quiet', options.quiet, undefined, () => cicdEnv.isCI ? true : undefined, undefined, false) || false;
236
+ return {
237
+ tracker,
238
+ resolved: {
239
+ repoPath,
240
+ project,
241
+ mergeRequestIid,
242
+ branch,
243
+ gitlabUrl,
244
+ gitlabToken,
245
+ format,
246
+ verbose,
247
+ dryRun,
248
+ quiet,
249
+ },
250
+ };
251
+ }
252
+ //# sourceMappingURL=parameter-source-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parameter-source-tracker.js","sourceRoot":"","sources":["../../src/cli/parameter-source-tracker.ts"],"names":[],"mappings":"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"}
@@ -37,17 +37,36 @@ export interface MCPServerConfig {
37
37
  *
38
38
  * Loads configuration from environment variables, applies defaults,
39
39
  * validates settings, and provides type-safe access to configuration.
40
+ *
41
+ * Supports singleton pattern to avoid multiple instantiations and duplicate logging.
40
42
  */
41
43
  export declare class ConfigService {
44
+ private static instance;
45
+ private static hasLoggedDefaults;
42
46
  private config;
43
47
  private appliedDefaults;
44
48
  /**
45
49
  * Create a new configuration service
46
50
  *
47
51
  * Automatically loads and validates configuration from environment variables.
48
- * Logs any default values that were applied.
52
+ * Logs any default values that were applied (only once per process).
53
+ *
54
+ * @param skipLogging - If true, skip logging applied defaults (useful for testing)
49
55
  */
50
- constructor();
56
+ constructor(skipLogging?: boolean);
57
+ /**
58
+ * Get the singleton instance of ConfigService
59
+ *
60
+ * This ensures configuration is only loaded and logged once per process,
61
+ * avoiding duplicate log messages when multiple components need config.
62
+ *
63
+ * @returns The shared ConfigService instance
64
+ */
65
+ static getInstance(): ConfigService;
66
+ /**
67
+ * Reset the singleton instance (primarily for testing)
68
+ */
69
+ static resetInstance(): void;
51
70
  /**
52
71
  * Get the current configuration (read-only)
53
72
  */
@@ -58,6 +77,8 @@ export declare class ConfigService {
58
77
  getAppliedDefaults(): ReadonlyMap<string, unknown>;
59
78
  /**
60
79
  * Load configuration from environment variables and validate
80
+ *
81
+ * @param skipLogging - If true, skip logging applied defaults
61
82
  */
62
83
  private loadAndValidate;
63
84
  /**
@@ -83,6 +104,7 @@ export declare class ConfigService {
83
104
  /**
84
105
  * Log which default values were applied
85
106
  * Uses console.error since MCP servers log to stderr
107
+ * Only logs once per process to avoid duplicate messages
86
108
  */
87
109
  private logAppliedDefaults;
88
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.service.d.ts","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAE9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAG5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IAGrB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,eAAe,CAAmC;IAE1D;;;;;OAKG;;IAKH;;OAEG;IACH,GAAG,IAAI,QAAQ,CAAC,eAAe,CAAC;IAIhC;;OAEG;IACH,kBAAkB,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;IAKlD;;OAEG;IACH,OAAO,CAAC,eAAe;IA6BvB;;OAEG;IACH,OAAO,CAAC,cAAc;IA2CtB;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAepB;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAY3B"}
1
+ {"version":3,"file":"config.service.d.ts","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAE9B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAG5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IAGrB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAS;IAEzC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,eAAe,CAAmC;IAE1D;;;;;;;OAOG;gBACS,WAAW,UAAQ;IAI/B;;;;;;;OAOG;IACH,MAAM,CAAC,WAAW,IAAI,aAAa;IAOnC;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAK5B;;OAEG;IACH,GAAG,IAAI,QAAQ,CAAC,eAAe,CAAC;IAIhC;;OAEG;IACH,kBAAkB,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC;IAKlD;;;;OAIG;IACH,OAAO,CAAC,eAAe;IA+BvB;;OAEG;IACH,OAAO,CAAC,cAAc;IA2CtB;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAepB;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;CAa3B"}
@@ -9,18 +9,45 @@
9
9
  *
10
10
  * Loads configuration from environment variables, applies defaults,
11
11
  * validates settings, and provides type-safe access to configuration.
12
+ *
13
+ * Supports singleton pattern to avoid multiple instantiations and duplicate logging.
12
14
  */
13
15
  export class ConfigService {
16
+ static instance = null;
17
+ static hasLoggedDefaults = false;
14
18
  config;
15
19
  appliedDefaults = new Map();
16
20
  /**
17
21
  * Create a new configuration service
18
22
  *
19
23
  * Automatically loads and validates configuration from environment variables.
20
- * Logs any default values that were applied.
24
+ * Logs any default values that were applied (only once per process).
25
+ *
26
+ * @param skipLogging - If true, skip logging applied defaults (useful for testing)
21
27
  */
22
- constructor() {
23
- this.config = this.loadAndValidate();
28
+ constructor(skipLogging = false) {
29
+ this.config = this.loadAndValidate(skipLogging);
30
+ }
31
+ /**
32
+ * Get the singleton instance of ConfigService
33
+ *
34
+ * This ensures configuration is only loaded and logged once per process,
35
+ * avoiding duplicate log messages when multiple components need config.
36
+ *
37
+ * @returns The shared ConfigService instance
38
+ */
39
+ static getInstance() {
40
+ if (!ConfigService.instance) {
41
+ ConfigService.instance = new ConfigService();
42
+ }
43
+ return ConfigService.instance;
44
+ }
45
+ /**
46
+ * Reset the singleton instance (primarily for testing)
47
+ */
48
+ static resetInstance() {
49
+ ConfigService.instance = null;
50
+ ConfigService.hasLoggedDefaults = false;
24
51
  }
25
52
  /**
26
53
  * Get the current configuration (read-only)
@@ -37,8 +64,10 @@ export class ConfigService {
37
64
  }
38
65
  /**
39
66
  * Load configuration from environment variables and validate
67
+ *
68
+ * @param skipLogging - If true, skip logging applied defaults
40
69
  */
41
- loadAndValidate() {
70
+ loadAndValidate(skipLogging = false) {
42
71
  const config = {
43
72
  // GitLab Configuration
44
73
  gitlabUrl: this.getEnvOrDefault('GITLAB_URL', 'https://gitlab.com'),
@@ -58,7 +87,9 @@ export class ConfigService {
58
87
  debug: this.getBooleanEnv('DEBUG', false),
59
88
  };
60
89
  this.validateConfig(config);
61
- this.logAppliedDefaults();
90
+ if (!skipLogging) {
91
+ this.logAppliedDefaults();
92
+ }
62
93
  return config;
63
94
  }
64
95
  /**
@@ -154,11 +185,13 @@ export class ConfigService {
154
185
  /**
155
186
  * Log which default values were applied
156
187
  * Uses console.error since MCP servers log to stderr
188
+ * Only logs once per process to avoid duplicate messages
157
189
  */
158
190
  logAppliedDefaults() {
159
- if (this.appliedDefaults.size === 0) {
191
+ if (this.appliedDefaults.size === 0 || ConfigService.hasLoggedDefaults) {
160
192
  return;
161
193
  }
194
+ ConfigService.hasLoggedDefaults = true;
162
195
  const timestamp = new Date().toISOString();
163
196
  console.error(`[${timestamp}] [INFO] [ConfigService] Applied default configuration values:`);
164
197
  for (const [key, value] of this.appliedDefaults.entries()) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.service.js","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.service.js","sourceRoot":"","sources":["../../src/config/config.service.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,12 @@ export declare class LocalGitDataSource extends GitLabAPIDataSource implements G
23
23
  * @param config - Optional configuration service for settings
24
24
  */
25
25
  constructor(gitlabApi: GitLabAPI, repoPath: string, logger?: Logger, config?: ConfigService);
26
+ /**
27
+ * Get the repository path
28
+ *
29
+ * @returns The local repository path
30
+ */
31
+ get repositoryPath(): string;
26
32
  /**
27
33
  * Get the project path from the git remote URL
28
34
  *
@@ -62,10 +68,30 @@ export declare class LocalGitDataSource extends GitLabAPIDataSource implements G
62
68
  * @returns Promise resolving to array of contributor email addresses (up to 100)
63
69
  */
64
70
  getProjectContributors(project: string, commitSha: string): Promise<string[]>;
71
+ /**
72
+ * Check if a path is a git submodule (gitlink with mode 160000)
73
+ *
74
+ * @param path - The file path to check
75
+ * @param ref - The commit SHA to check at
76
+ * @returns Promise resolving to true if path is a submodule
77
+ */
78
+ private isSubmodule;
79
+ /**
80
+ * Get blame-like data for a submodule by using commit history
81
+ *
82
+ * Since git blame doesn't work on submodule gitlinks, we get the
83
+ * commit history for who changed the submodule reference.
84
+ *
85
+ * @param path - The submodule path
86
+ * @param ref - The commit SHA
87
+ * @returns Promise resolving to array of blame lines from commit history
88
+ */
89
+ private getSubmoduleBlame;
65
90
  /**
66
91
  * Get git blame information from local repository
67
92
  *
68
93
  * Validates commit existence before attempting blame.
94
+ * For submodules, uses commit history instead of blame.
69
95
  * Falls back to API if local blame fails (graceful degradation).
70
96
  *
71
97
  * @param project - The project ID or path
@@ -1 +1 @@
1
- {"version":3,"file":"local-git-data-source.d.ts","sourceRoot":"","sources":["../../src/datasources/local-git-data-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,kBACX,SAAQ,mBACR,YAAW,aAAa;IActB,OAAO,CAAC,QAAQ;IAZlB,OAAO,CAAC,MAAM,CAAgB;IAE9B;;;;;;;OAOG;gBAED,SAAS,EAAE,SAAS,EACZ,QAAQ,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,aAAa;IAMxB;;;;;;;;OAQG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAyD7C;;;;;OAKG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAmCzC;;;;;;;;;OASG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwExD;;;;;;;;;OASG;IACG,sBAAsB,CAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC;IA+EpB;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAyFhF;;OAEG;YACW,oBAAoB;IAkBlC;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiHxE;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;OAEG;YACW,cAAc;IA2D5B;;;;;;;;;;OAUG;IACG,UAAU,CACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA8CzB;;;;;;;;OAQG;IACG,WAAW,CAAC,CAAC,GAAG,OAAO,EAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAsBpB;;;;;;;;;;;OAWG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,mBAAmB,EAAE,UAAU,CAAC;CAc7G"}
1
+ {"version":3,"file":"local-git-data-source.d.ts","sourceRoot":"","sources":["../../src/datasources/local-git-data-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,kBACX,SAAQ,mBACR,YAAW,aAAa;IActB,OAAO,CAAC,QAAQ;IAZlB,OAAO,CAAC,MAAM,CAAgB;IAE9B;;;;;;;OAOG;gBAED,SAAS,EAAE,SAAS,EACZ,QAAQ,EAAE,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,aAAa;IAMxB;;;;OAIG;IACH,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED;;;;;;;;OAQG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAyD7C;;;;;OAKG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAmCzC;;;;;;;;;OASG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwExD;;;;;;;;;OASG;IACG,sBAAsB,CAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC;IA+EpB;;;;;;OAMG;YACW,WAAW;IAezB;;;;;;;;;OASG;YACW,iBAAiB;IAiD/B;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAoGhF;;OAEG;YACW,oBAAoB;IAkBlC;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiHxE;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;OAEG;YACW,cAAc;IA2D5B;;;;;;;;;;OAUG;IACG,UAAU,CACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA8CzB;;;;;;;;OAQG;IACG,WAAW,CAAC,CAAC,GAAG,OAAO,EAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAsBpB;;;;;;;;;;;OAWG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,mBAAmB,EAAE,UAAU,CAAC;CAc7G"}
@@ -26,7 +26,15 @@ export class LocalGitDataSource extends GitLabAPIDataSource {
26
26
  constructor(gitlabApi, repoPath, logger, config) {
27
27
  super(gitlabApi, logger || new Logger('LocalGitDataSource'));
28
28
  this.repoPath = repoPath;
29
- this.config = config || new ConfigService();
29
+ this.config = config || ConfigService.getInstance();
30
+ }
31
+ /**
32
+ * Get the repository path
33
+ *
34
+ * @returns The local repository path
35
+ */
36
+ get repositoryPath() {
37
+ return this.repoPath;
30
38
  }
31
39
  /**
32
40
  * Get the project path from the git remote URL
@@ -240,10 +248,88 @@ export class LocalGitDataSource extends GitLabAPIDataSource {
240
248
  throw ErrorHandler.gitOperationError('Failed to get project contributors', context, error instanceof Error ? error : new Error(String(error)));
241
249
  }
242
250
  }
251
+ /**
252
+ * Check if a path is a git submodule (gitlink with mode 160000)
253
+ *
254
+ * @param path - The file path to check
255
+ * @param ref - The commit SHA to check at
256
+ * @returns Promise resolving to true if path is a submodule
257
+ */
258
+ async isSubmodule(path, ref) {
259
+ try {
260
+ const git = simpleGit(this.repoPath);
261
+ const result = await git.raw(['ls-tree', ref, '--', path]);
262
+ // Submodules have mode 160000 (gitlink)
263
+ return result.startsWith('160000');
264
+ }
265
+ catch (error) {
266
+ // Re-throw MCPErrors, otherwise return false (graceful degradation)
267
+ if (error instanceof Error && error.message.includes('MCP')) {
268
+ throw error;
269
+ }
270
+ return false;
271
+ }
272
+ }
273
+ /**
274
+ * Get blame-like data for a submodule by using commit history
275
+ *
276
+ * Since git blame doesn't work on submodule gitlinks, we get the
277
+ * commit history for who changed the submodule reference.
278
+ *
279
+ * @param path - The submodule path
280
+ * @param ref - The commit SHA
281
+ * @returns Promise resolving to array of blame lines from commit history
282
+ */
283
+ async getSubmoduleBlame(path, ref) {
284
+ const git = simpleGit(this.repoPath);
285
+ try {
286
+ // Get the last commit that modified this submodule reference
287
+ const logResult = await git.raw([
288
+ 'log',
289
+ '-1',
290
+ '--format=%H%n%ae%n%an%n%aI%n%s',
291
+ ref,
292
+ '--',
293
+ path,
294
+ ]);
295
+ const lines = logResult.trim().split('\n');
296
+ if (lines.length < 5) {
297
+ return [];
298
+ }
299
+ const [commitId, authorEmail, authorName, authoredDate, message] = lines;
300
+ return [{
301
+ commit: {
302
+ id: commitId,
303
+ message,
304
+ parent_ids: [],
305
+ authored_date: authoredDate,
306
+ author_name: authorName,
307
+ author_email: authorEmail,
308
+ committed_date: authoredDate,
309
+ committer_name: authorName,
310
+ committer_email: authorEmail,
311
+ },
312
+ lines: ['1'],
313
+ }];
314
+ }
315
+ catch (error) {
316
+ // Re-throw MCPErrors, otherwise log and return empty (graceful degradation)
317
+ if (error instanceof Error && error.message.includes('MCP')) {
318
+ throw error;
319
+ }
320
+ this.logger.debug('Failed to get submodule commit history', {
321
+ path,
322
+ ref: ref.substring(0, 8),
323
+ error: error instanceof Error ? error.message : String(error),
324
+ });
325
+ return [];
326
+ }
327
+ }
243
328
  /**
244
329
  * Get git blame information from local repository
245
330
  *
246
331
  * Validates commit existence before attempting blame.
332
+ * For submodules, uses commit history instead of blame.
247
333
  * Falls back to API if local blame fails (graceful degradation).
248
334
  *
249
335
  * @param project - The project ID or path
@@ -257,6 +343,16 @@ export class LocalGitDataSource extends GitLabAPIDataSource {
257
343
  try {
258
344
  // Validate commit exists before attempting blame (Requirement 6.1)
259
345
  await this.validateCommitExists(ref);
346
+ // Check if path is a submodule - use commit history instead of blame
347
+ if (await this.isSubmodule(path, ref)) {
348
+ this.logger.info('Path is a submodule, using commit history instead of blame', {
349
+ ...context,
350
+ isSubmodule: true,
351
+ });
352
+ const submoduleBlame = await this.getSubmoduleBlame(path, ref);
353
+ endTimer();
354
+ return submoduleBlame;
355
+ }
260
356
  const git = simpleGit(this.repoPath);
261
357
  // Wrap git operation with retry logic (Requirement 3.2)
262
358
  const result = await ErrorHandler.withRetry(async () => {