@paulduvall/claude-dev-toolkit 0.0.1-alpha.2 → 0.0.1-alpha.21

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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -37
  3. package/bin/claude-commands +307 -65
  4. package/commands/active/xarchitecture.md +393 -0
  5. package/commands/active/xconfig.md +127 -0
  6. package/commands/active/xcontinue.md +92 -0
  7. package/commands/active/xdebug.md +130 -0
  8. package/commands/active/xdocs.md +178 -0
  9. package/commands/active/xexplore.md +94 -0
  10. package/commands/active/xgit.md +149 -0
  11. package/commands/active/xpipeline.md +152 -0
  12. package/commands/active/xquality.md +96 -0
  13. package/commands/active/xrefactor.md +198 -0
  14. package/commands/active/xrelease.md +142 -0
  15. package/commands/active/xsecurity.md +92 -0
  16. package/commands/active/xspec.md +174 -0
  17. package/commands/active/xtdd.md +151 -0
  18. package/commands/active/xtest.md +89 -0
  19. package/commands/active/xverify.md +80 -0
  20. package/commands/experiments/xact.md +742 -0
  21. package/commands/experiments/xanalytics.md +113 -0
  22. package/commands/experiments/xanalyze.md +70 -0
  23. package/commands/experiments/xapi.md +161 -0
  24. package/commands/experiments/xatomic.md +112 -0
  25. package/commands/experiments/xaws.md +85 -0
  26. package/commands/experiments/xcicd.md +337 -0
  27. package/commands/experiments/xcommit.md +122 -0
  28. package/commands/experiments/xcompliance.md +182 -0
  29. package/commands/experiments/xconstraints.md +89 -0
  30. package/commands/experiments/xcoverage.md +90 -0
  31. package/commands/experiments/xdb.md +102 -0
  32. package/commands/experiments/xdesign.md +121 -0
  33. package/commands/experiments/xdevcontainer.md +238 -0
  34. package/commands/experiments/xevaluate.md +111 -0
  35. package/commands/experiments/xfootnote.md +12 -0
  36. package/commands/experiments/xgenerate.md +117 -0
  37. package/commands/experiments/xgovernance.md +149 -0
  38. package/commands/experiments/xgreen.md +66 -0
  39. package/commands/experiments/xiac.md +118 -0
  40. package/commands/experiments/xincident.md +137 -0
  41. package/commands/experiments/xinfra.md +115 -0
  42. package/commands/experiments/xknowledge.md +115 -0
  43. package/commands/experiments/xmaturity.md +120 -0
  44. package/commands/experiments/xmetrics.md +118 -0
  45. package/commands/experiments/xmonitoring.md +128 -0
  46. package/commands/experiments/xnew.md +903 -0
  47. package/commands/experiments/xobservable.md +114 -0
  48. package/commands/experiments/xoidc.md +165 -0
  49. package/commands/experiments/xoptimize.md +115 -0
  50. package/commands/experiments/xperformance.md +112 -0
  51. package/commands/experiments/xplanning.md +131 -0
  52. package/commands/experiments/xpolicy.md +115 -0
  53. package/commands/experiments/xproduct.md +98 -0
  54. package/commands/experiments/xreadiness.md +75 -0
  55. package/commands/experiments/xred.md +55 -0
  56. package/commands/experiments/xrisk.md +128 -0
  57. package/commands/experiments/xrules.md +124 -0
  58. package/commands/experiments/xsandbox.md +120 -0
  59. package/commands/experiments/xscan.md +102 -0
  60. package/commands/experiments/xsetup.md +123 -0
  61. package/commands/experiments/xtemplate.md +116 -0
  62. package/commands/experiments/xtrace.md +212 -0
  63. package/commands/experiments/xux.md +171 -0
  64. package/commands/experiments/xvalidate.md +104 -0
  65. package/commands/experiments/xworkflow.md +113 -0
  66. package/hooks/.smellrc.example.json +19 -0
  67. package/hooks/README.md +263 -0
  68. package/hooks/check-commit-signing.py +127 -0
  69. package/hooks/check-complexity.py +38 -0
  70. package/hooks/check-security.py +37 -0
  71. package/hooks/claude-wrapper.sh +29 -0
  72. package/hooks/config.py +110 -0
  73. package/hooks/file-logger.sh +100 -0
  74. package/hooks/lib/argument-parser.sh +427 -0
  75. package/hooks/lib/config-constants.sh +230 -0
  76. package/hooks/lib/context-manager.sh +560 -0
  77. package/hooks/lib/error-handler.sh +423 -0
  78. package/hooks/lib/execution-engine.sh +444 -0
  79. package/hooks/lib/execution-results.sh +113 -0
  80. package/hooks/lib/execution-simulation.sh +114 -0
  81. package/hooks/lib/field-validators.sh +104 -0
  82. package/hooks/lib/file-utils.sh +398 -0
  83. package/hooks/lib/subagent-discovery.sh +468 -0
  84. package/hooks/lib/subagent-validator.sh +407 -0
  85. package/hooks/lib/validation-reporter.sh +134 -0
  86. package/hooks/on-error-debug.sh +226 -0
  87. package/hooks/pre-commit-quality.sh +204 -0
  88. package/hooks/pre-commit-test-runner.sh +132 -0
  89. package/hooks/pre-write-security.sh +115 -0
  90. package/hooks/prevent-credential-exposure.sh +279 -0
  91. package/hooks/security_bandit.py +177 -0
  92. package/hooks/security_checks.py +97 -0
  93. package/hooks/security_secrets.py +81 -0
  94. package/hooks/security_trojan.py +61 -0
  95. package/hooks/settings.example.json +52 -0
  96. package/hooks/smell_checks.py +238 -0
  97. package/hooks/smell_javascript.py +231 -0
  98. package/hooks/smell_python.py +110 -0
  99. package/hooks/smell_ruff.py +70 -0
  100. package/hooks/smell_types.py +72 -0
  101. package/hooks/subagent-trigger-simple.sh +202 -0
  102. package/hooks/subagent-trigger.sh +253 -0
  103. package/hooks/suppression.py +82 -0
  104. package/hooks/tab-color.sh +70 -0
  105. package/hooks/verify-before-edit.sh +135 -0
  106. package/lib/backup-restore-command.js +140 -0
  107. package/lib/base/base-command.js +252 -0
  108. package/lib/base/command-result.js +184 -0
  109. package/lib/config/constants.js +255 -0
  110. package/lib/config.js +48 -6
  111. package/lib/configure-command.js +428 -0
  112. package/lib/dependency-validator.js +64 -5
  113. package/lib/hook-installer-core.js +2 -2
  114. package/lib/installation-instruction-generator.js +213 -495
  115. package/lib/installer.js +134 -56
  116. package/lib/oidc-command.js +740 -0
  117. package/lib/services/backup-list-service.js +226 -0
  118. package/lib/services/backup-service.js +230 -0
  119. package/lib/services/command-installer-service.js +217 -0
  120. package/lib/services/logger-service.js +201 -0
  121. package/lib/services/package-manager-service.js +319 -0
  122. package/lib/services/platform-instruction-service.js +294 -0
  123. package/lib/services/recovery-instruction-service.js +348 -0
  124. package/lib/services/restore-service.js +221 -0
  125. package/lib/setup-command.js +359 -0
  126. package/lib/setup-wizard.js +155 -262
  127. package/lib/uninstall-command.js +100 -0
  128. package/lib/utils/claude-path-config.js +184 -0
  129. package/lib/utils/file-system-utils.js +152 -0
  130. package/lib/utils.js +8 -4
  131. package/lib/verify-command.js +430 -0
  132. package/package.json +7 -3
  133. package/scripts/postinstall.js +172 -157
  134. package/subagents/debug-specialist.md +7 -0
  135. package/templates/README.md +115 -0
  136. package/templates/basic-settings.json +30 -0
  137. package/templates/comprehensive-settings.json +57 -0
  138. package/templates/global-claude.md +344 -0
  139. package/templates/hybrid-hook-config.yaml +132 -0
  140. package/templates/security-focused-settings.json +62 -0
  141. package/templates/subagent-hooks.yaml +188 -0
  142. package/lib/package-manager-service.js +0 -270
  143. package/subagents/debug-context.md +0 -197
@@ -0,0 +1,740 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * OIDC Command Implementation
5
+ * Provides GitHub Actions OIDC configuration with AWS through the toolkit's CLI framework
6
+ */
7
+
8
+ const BaseCommand = require('./base/base-command');
9
+ const DependencyValidator = require('./dependency-validator');
10
+ const ErrorHandlerUtils = require('./error-handler-utils');
11
+
12
+ class OidcCommand extends BaseCommand {
13
+ constructor() {
14
+ super();
15
+ this.dependencyValidator = new DependencyValidator();
16
+ this.errorHandlerUtils = new ErrorHandlerUtils();
17
+ }
18
+
19
+ /**
20
+ * Get required tools for OIDC functionality
21
+ */
22
+ getRequiredTools() {
23
+ return [
24
+ {
25
+ name: 'aws',
26
+ description: 'AWS CLI for AWS operations',
27
+ required: true
28
+ },
29
+ {
30
+ name: 'git',
31
+ description: 'Git for repository operations',
32
+ required: true
33
+ },
34
+ {
35
+ name: 'gh',
36
+ description: 'GitHub CLI for GitHub operations',
37
+ required: true
38
+ }
39
+ ];
40
+ }
41
+
42
+ /**
43
+ * Validate required dependencies
44
+ */
45
+ async validateDependencies(options = {}) {
46
+ const requiredTools = this.getRequiredTools();
47
+ const result = this.dependencyValidator.checkDependencies(requiredTools);
48
+
49
+ return result;
50
+ }
51
+
52
+ /**
53
+ * Handle dependency errors with enhanced error information
54
+ */
55
+ handleDependencyError(error, context = {}) {
56
+ const enhancedError = this.errorHandlerUtils.createEnhancedError(error, {
57
+ operation: 'dependency validation',
58
+ component: 'OIDC command',
59
+ ...context
60
+ });
61
+
62
+ // Generate specific recovery suggestions for OIDC dependencies
63
+ const oidcSuggestions = this.generateOIDCRecoverySuggestions(context.missingTools || []);
64
+ const suggestions = this.errorHandlerUtils.generateRecoverySuggestions(enhancedError);
65
+
66
+ return {
67
+ ...enhancedError,
68
+ suggestions: [...oidcSuggestions, ...Array.from(suggestions || [])],
69
+ message: this.enhanceErrorMessage(enhancedError.message, context.missingTools || [])
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Generate OIDC-specific recovery suggestions
75
+ */
76
+ generateOIDCRecoverySuggestions(missingTools) {
77
+ const suggestions = [
78
+ "📋 OIDC Setup requires these prerequisites:",
79
+ " Run 'claude-commands oidc --help' for complete setup guide",
80
+ ""
81
+ ];
82
+
83
+ missingTools.forEach(tool => {
84
+ switch (tool.name) {
85
+ case 'aws':
86
+ suggestions.push(
87
+ "🔧 Install AWS CLI:",
88
+ " • macOS: brew install awscli",
89
+ " • Linux: curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o 'awscliv2.zip' && unzip awscliv2.zip && sudo ./aws/install",
90
+ " • Windows: Download from https://aws.amazon.com/cli/",
91
+ " • Configure: aws configure (requires Access Key ID and Secret)",
92
+ ""
93
+ );
94
+ break;
95
+ case 'gh':
96
+ suggestions.push(
97
+ "🔧 Install GitHub CLI:",
98
+ " • macOS: brew install gh",
99
+ " • Linux: curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg",
100
+ " • Windows: Download from https://github.com/cli/cli/releases",
101
+ " • Authenticate: gh auth login",
102
+ ""
103
+ );
104
+ break;
105
+ case 'git':
106
+ suggestions.push(
107
+ "🔧 Install Git:",
108
+ " • macOS: brew install git (or use Xcode Command Line Tools)",
109
+ " • Linux: sudo apt-get install git (Ubuntu/Debian) or sudo yum install git (RHEL/CentOS)",
110
+ " • Windows: Download from https://git-scm.com/download/win",
111
+ " • Ensure your repository has a GitHub remote origin",
112
+ ""
113
+ );
114
+ break;
115
+ }
116
+ });
117
+
118
+ suggestions.push(
119
+ "✅ After installation, verify with:",
120
+ " • aws --version && aws sts get-caller-identity",
121
+ " • gh --version && gh auth status",
122
+ " • git --version && git remote -v",
123
+ "",
124
+ "📖 For detailed setup instructions:",
125
+ " claude-commands oidc --help"
126
+ );
127
+
128
+ return suggestions;
129
+ }
130
+
131
+ /**
132
+ * Enhance error message with context
133
+ */
134
+ enhanceErrorMessage(originalMessage, missingTools) {
135
+ if (missingTools.length === 0) return originalMessage;
136
+
137
+ const toolNames = missingTools.map(t => t.name).join(', ');
138
+ return `${originalMessage}
139
+
140
+ 🎯 OIDC Setup Prerequisites Missing
141
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
142
+ Missing tools: ${toolNames}
143
+
144
+ The OIDC command requires AWS CLI, GitHub CLI, and Git to be installed and configured.
145
+ These tools enable secure authentication between GitHub Actions and AWS.
146
+
147
+ Run 'claude-commands oidc --help' for complete setup requirements.`;
148
+ }
149
+
150
+ /**
151
+ * Create context-aware error with operation details
152
+ */
153
+ createContextAwareError(error, context = {}) {
154
+ return this.errorHandlerUtils.createEnhancedError(error, context);
155
+ }
156
+
157
+ /**
158
+ * Process command arguments with defaults and validation
159
+ */
160
+ processArguments(options = {}) {
161
+ const processed = {
162
+ // Default values for common options
163
+ region: options.region || 'us-east-1',
164
+ dryRun: options.dryRun || false,
165
+ verbose: options.verbose || false,
166
+ help: options.help || false,
167
+
168
+ // OIDC-specific options with defaults
169
+ repositoryPath: options.repositoryPath || process.cwd(),
170
+ roleName: options.roleName || 'GitHubActionsRole',
171
+
172
+ // Copy other options as-is
173
+ ...options
174
+ };
175
+
176
+ // Special handling for help option
177
+ if (processed.help) {
178
+ processed.shouldShowHelp = true;
179
+ }
180
+
181
+ return processed;
182
+ }
183
+
184
+ /**
185
+ * Validate argument constraints and requirements
186
+ */
187
+ validateArguments(options = {}) {
188
+ const errors = [];
189
+ const result = {
190
+ valid: true,
191
+ errors,
192
+ warnings: []
193
+ };
194
+
195
+ // Validate region format
196
+ if (options.region && !/^[a-z0-9-]+$/.test(options.region)) {
197
+ errors.push('Region must contain only lowercase letters, numbers, and hyphens');
198
+ }
199
+
200
+ // Validate repository path if provided
201
+ if (options.repositoryPath && typeof options.repositoryPath !== 'string') {
202
+ errors.push('Repository path must be a string');
203
+ }
204
+
205
+
206
+ // Update validation status
207
+ result.valid = errors.length === 0;
208
+
209
+ return result;
210
+ }
211
+
212
+ /**
213
+ * Pre-execution validation
214
+ */
215
+ async preValidate(options = {}) {
216
+ try {
217
+ // Process and validate arguments first
218
+ const processedOptions = this.processArguments(options);
219
+ const argumentValidation = this.validateArguments(processedOptions);
220
+
221
+ if (!argumentValidation.valid) {
222
+ const error = new Error(`Invalid arguments: ${argumentValidation.errors.join(', ')}`);
223
+ error.code = 'VALIDATION_ERROR';
224
+
225
+ const enhancedError = this.createContextAwareError(error, {
226
+ operation: 'OIDC argument validation',
227
+ component: 'argument processor',
228
+ validationErrors: argumentValidation.errors
229
+ });
230
+
231
+ return {
232
+ success: false,
233
+ error: enhancedError.message,
234
+ enhancedError,
235
+ argumentValidation
236
+ };
237
+ }
238
+
239
+ this.showProgress('Validating dependencies...', processedOptions);
240
+
241
+ // Validate required tools are available
242
+ const dependencyResult = await this.validateDependencies(processedOptions);
243
+
244
+ if (!dependencyResult.valid) {
245
+ const missingTools = dependencyResult.missing.map(tool => tool.name).join(', ');
246
+
247
+ // Create enhanced error with context and recovery suggestions
248
+ const error = new Error(`Missing required tools: ${missingTools}`);
249
+ error.code = 'NOT_FOUND';
250
+
251
+ const enhancedError = this.handleDependencyError(error, {
252
+ operation: 'OIDC pre-validation',
253
+ component: 'dependency check',
254
+ missingTools: dependencyResult.missing
255
+ });
256
+
257
+ return {
258
+ success: false,
259
+ error: enhancedError.message,
260
+ enhancedError,
261
+ dependencyResult
262
+ };
263
+ }
264
+
265
+ this.showProgress('Dependencies validated successfully', processedOptions);
266
+ return {
267
+ success: true,
268
+ processedOptions,
269
+ argumentValidation,
270
+ dependencyResult
271
+ };
272
+
273
+ } catch (error) {
274
+ // Handle unexpected validation errors
275
+ const enhancedError = this.createContextAwareError(error, {
276
+ operation: 'OIDC pre-validation',
277
+ component: 'validation system'
278
+ });
279
+
280
+ return {
281
+ success: false,
282
+ error: enhancedError.message,
283
+ enhancedError
284
+ };
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Main command execution logic
290
+ */
291
+ async run(options = {}) {
292
+ const { dryRun = false } = options;
293
+
294
+ if (dryRun) {
295
+ await this.showDryRunWithDetection(options);
296
+ return {
297
+ message: '✅ Dry run completed successfully',
298
+ dryRun: true
299
+ };
300
+ }
301
+
302
+ // Phase 2: Auto-detect configuration
303
+ this.showProgress('🚀 Initializing OIDC command...', options);
304
+
305
+ const configResult = await this.autoDetectConfiguration(options);
306
+ if (!configResult.success) {
307
+ console.log(`❌ Configuration detection failed: ${configResult.error}`);
308
+ if (configResult.suggestions) {
309
+ configResult.suggestions.forEach(suggestion => console.log(suggestion));
310
+ }
311
+ return {
312
+ message: '❌ OIDC setup failed during configuration detection',
313
+ error: configResult.error
314
+ };
315
+ }
316
+
317
+ // Display detected configuration
318
+ console.log('✅ Configuration detected successfully:');
319
+ console.log(` 📂 Repository: ${configResult.git.owner}/${configResult.git.repo}`);
320
+ console.log(` 🌍 AWS Region: ${configResult.aws.region} (${configResult.aws.source})`);
321
+ console.log(` 🎭 IAM Role: ${configResult.roleName}`);
322
+ console.log('');
323
+
324
+ // For now, show that detection is working but full implementation is still in development
325
+ console.log('📋 OIDC Setup Status: Auto-detection implemented ✅');
326
+ console.log('⚠️ AWS resource creation is in development (Phase 3)');
327
+ console.log('💡 Use --dry-run to preview complete functionality');
328
+
329
+ return {
330
+ message: '✅ OIDC command executed successfully (Phase 2: Detection completed)',
331
+ configuration: configResult
332
+ };
333
+ }
334
+
335
+ /**
336
+ * Show dry run preview with Phase 2 detection
337
+ */
338
+ async showDryRunWithDetection(options) {
339
+ console.log('🔍 Dry Run - Preview of OIDC configuration actions:\n');
340
+
341
+ // Show what detection would find
342
+ console.log('📋 Phase 2: Auto-Detection Preview:');
343
+ try {
344
+ const configResult = await this.autoDetectConfiguration(options);
345
+ if (configResult.success) {
346
+ console.log(` ✅ Repository: ${configResult.git.owner}/${configResult.git.repo}`);
347
+ console.log(` ✅ AWS Region: ${configResult.aws.region} (${configResult.aws.source})`);
348
+ console.log(` ✅ IAM Role: ${configResult.roleName}`);
349
+ console.log(` ✅ Policy Template: ${configResult.policyTemplate.name}`);
350
+ } else {
351
+ console.log(` ❌ Configuration detection would fail: ${configResult.error}`);
352
+ }
353
+ } catch (error) {
354
+ console.log(` ❌ Detection error: ${error.message}`);
355
+ }
356
+
357
+ console.log('\n📋 Phase 3: AWS Resource Creation (Planned):');
358
+ console.log(' • Create AWS OIDC Identity Provider for GitHub');
359
+ console.log(' • Create IAM role with trust policy for GitHub Actions');
360
+ console.log(' • Attach permission policies to IAM role');
361
+ console.log(' • Set up GitHub repository variables (AWS_DEPLOYMENT_ROLE, AWS_REGION)');
362
+ console.log('\n💡 This was a dry run - no changes were made');
363
+ console.log(' Run without --dry-run to execute OIDC setup (Phase 2 detection only)');
364
+
365
+ return { dryRun: true, message: 'Dry run completed' };
366
+ }
367
+
368
+ /**
369
+ * Show dry run preview (legacy method)
370
+ */
371
+ showDryRun(options) {
372
+ console.log('🔍 Dry Run - Preview of OIDC configuration actions:\n');
373
+ console.log('📋 OIDC Setup:');
374
+ console.log(' • Detect GitHub repository context');
375
+ console.log(' • Validate AWS credentials and permissions');
376
+ console.log(' • Create AWS OIDC Identity Provider for GitHub');
377
+ console.log(' • Create IAM role with trust policy for GitHub Actions');
378
+ console.log(' • Set up GitHub repository variables (AWS_DEPLOYMENT_ROLE, AWS_REGION)');
379
+ console.log('\n💡 This was a dry run - no changes were made');
380
+ console.log(' Run without --dry-run to execute OIDC setup');
381
+
382
+ return { dryRun: true, message: 'Dry run completed' };
383
+ }
384
+
385
+ /**
386
+ * REQ-DETECT-001: Git Repository Detection
387
+ * Auto-detect GitHub org/repo from git remote
388
+ */
389
+ async detectGitRepository(options = {}) {
390
+ try {
391
+ const { execSync } = require('child_process');
392
+
393
+ // Get git remotes
394
+ const remotesOutput = execSync('git remote -v', {
395
+ encoding: 'utf8',
396
+ cwd: options.repositoryPath || process.cwd()
397
+ });
398
+
399
+ const remotes = this.parseGitRemotes(remotesOutput);
400
+ const selectedRemote = this.selectPreferredRemote(remotes);
401
+
402
+ if (!selectedRemote) {
403
+ throw new Error('No GitHub remote found. Please add a GitHub remote origin.');
404
+ }
405
+
406
+ const repoInfo = this.parseGitRemote(selectedRemote.url);
407
+ return {
408
+ success: true,
409
+ owner: repoInfo.owner,
410
+ repo: repoInfo.repo,
411
+ remote: selectedRemote.name,
412
+ url: selectedRemote.url
413
+ };
414
+
415
+ } catch (error) {
416
+ return {
417
+ success: false,
418
+ error: error.message,
419
+ suggestions: [
420
+ '🔧 Ensure you are in a git repository',
421
+ '🔗 Add a GitHub remote: git remote add origin <github-url>',
422
+ '✅ Verify remote: git remote -v'
423
+ ]
424
+ };
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Parse git remotes output into structured format
430
+ */
431
+ parseGitRemotes(remotesOutput) {
432
+ const remotes = [];
433
+ const lines = remotesOutput.split('\n').filter(line => line.trim());
434
+
435
+ lines.forEach(line => {
436
+ const match = line.match(/^(\w+)\s+(.+?)\s+\((fetch|push)\)$/);
437
+ if (match && match[3] === 'fetch') { // Only use fetch URLs
438
+ const [, name, url] = match;
439
+ if (url.includes('github.com')) {
440
+ remotes.push({ name, url });
441
+ }
442
+ }
443
+ });
444
+
445
+ return remotes;
446
+ }
447
+
448
+ /**
449
+ * Select preferred remote (prioritize 'origin')
450
+ */
451
+ selectPreferredRemote(remotes) {
452
+ if (remotes.length === 0) return null;
453
+
454
+ // Prefer 'origin' remote
455
+ const origin = remotes.find(remote => remote.name === 'origin');
456
+ if (origin) return origin;
457
+
458
+ // Fall back to first GitHub remote
459
+ return remotes[0];
460
+ }
461
+
462
+ /**
463
+ * Parse git remote URL to extract owner/repo
464
+ * Supports both SSH and HTTPS formats
465
+ */
466
+ parseGitRemote(url) {
467
+ // SSH format: git@github.com:owner/repo.git
468
+ const sshMatch = url.match(/^git@github\.com:([^\/]+)\/(.+?)(?:\.git)?$/);
469
+ if (sshMatch) {
470
+ return {
471
+ owner: sshMatch[1],
472
+ repo: sshMatch[2]
473
+ };
474
+ }
475
+
476
+ // HTTPS format: https://github.com/owner/repo.git
477
+ const httpsMatch = url.match(/^https:\/\/github\.com\/([^\/]+)\/(.+?)(?:\.git)?$/);
478
+ if (httpsMatch) {
479
+ return {
480
+ owner: httpsMatch[1],
481
+ repo: httpsMatch[2]
482
+ };
483
+ }
484
+
485
+ throw new Error(`Unsupported git remote URL format: ${url}`);
486
+ }
487
+
488
+ /**
489
+ * REQ-DETECT-002: AWS Configuration Detection
490
+ * Read AWS CLI config and environment variables
491
+ */
492
+ async detectAWSConfiguration(options = {}) {
493
+ try {
494
+ const fs = require('fs');
495
+ const path = require('path');
496
+ const os = require('os');
497
+
498
+ let region = null;
499
+ let profile = 'default';
500
+
501
+ // 1. Check environment variable first (highest priority)
502
+ region = this.getAWSRegionFromEnvironment();
503
+
504
+ // 2. If no env var, try to read from AWS config files
505
+ if (!region) {
506
+ const awsConfigResult = this.readAWSConfigFiles();
507
+ region = awsConfigResult.region;
508
+ profile = awsConfigResult.profile;
509
+ }
510
+
511
+ // 3. Default to us-east-1 if nothing found
512
+ if (!region) {
513
+ region = 'us-east-1';
514
+ }
515
+
516
+ // 4. Validate region
517
+ const regionValid = this.validateAWSRegion(region);
518
+
519
+ return {
520
+ success: true,
521
+ region: region,
522
+ profile: profile,
523
+ source: region === this.getAWSRegionFromEnvironment() ? 'environment' :
524
+ region === 'us-east-1' ? 'default' : 'config-file',
525
+ valid: regionValid
526
+ };
527
+
528
+ } catch (error) {
529
+ return {
530
+ success: false,
531
+ error: error.message,
532
+ region: 'us-east-1', // Fallback
533
+ suggestions: [
534
+ '🔧 Set AWS region: export AWS_DEFAULT_REGION=us-east-1',
535
+ '⚙️ Configure AWS CLI: aws configure',
536
+ '✅ Verify config: aws configure list'
537
+ ]
538
+ };
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Get AWS region from environment variables
544
+ */
545
+ getAWSRegionFromEnvironment() {
546
+ return process.env.AWS_DEFAULT_REGION || process.env.AWS_REGION || null;
547
+ }
548
+
549
+ /**
550
+ * Read AWS CLI config files (~/.aws/config and ~/.aws/credentials)
551
+ */
552
+ readAWSConfigFiles() {
553
+ const fs = require('fs');
554
+ const path = require('path');
555
+ const os = require('os');
556
+
557
+ const awsDir = path.join(os.homedir(), '.aws');
558
+ const configPath = path.join(awsDir, 'config');
559
+ const credentialsPath = path.join(awsDir, 'credentials');
560
+
561
+ let region = null;
562
+ let profile = 'default';
563
+
564
+ // Try to read config file first
565
+ try {
566
+ if (fs.existsSync(configPath)) {
567
+ const configContent = fs.readFileSync(configPath, 'utf8');
568
+ const regionMatch = configContent.match(/^\s*region\s*=\s*(.+)$/m);
569
+ if (regionMatch) {
570
+ region = regionMatch[1].trim();
571
+ }
572
+ }
573
+ } catch (error) {
574
+ // Ignore config file read errors
575
+ }
576
+
577
+ return { region, profile };
578
+ }
579
+
580
+ /**
581
+ * Validate AWS region format and existence
582
+ */
583
+ validateAWSRegion(region) {
584
+ if (!region) return false;
585
+
586
+ // Basic format validation: region should be like us-east-1, eu-west-1, etc.
587
+ const regionPattern = /^[a-z]{2,3}-[a-z]+-\d+$/;
588
+ if (!regionPattern.test(region)) {
589
+ return false;
590
+ }
591
+
592
+ // List of valid AWS regions (simplified list for validation)
593
+ const validRegions = [
594
+ 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2',
595
+ 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-central-1',
596
+ 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'ap-northeast-2',
597
+ 'sa-east-1', 'ca-central-1', 'ap-south-1'
598
+ ];
599
+
600
+ return validRegions.includes(region);
601
+ }
602
+
603
+ /**
604
+ * REQ-CLI-003: Zero Configuration Mode
605
+ * Combine git detection + AWS detection for zero-config experience
606
+ */
607
+ async autoDetectConfiguration(options = {}) {
608
+ try {
609
+ this.showProgress('🔍 Auto-detecting project configuration...', options);
610
+
611
+ // Detect Git repository information
612
+ const gitResult = await this.detectGitRepository(options);
613
+ if (!gitResult.success) {
614
+ return {
615
+ success: false,
616
+ error: 'Git repository detection failed',
617
+ details: gitResult,
618
+ suggestions: gitResult.suggestions
619
+ };
620
+ }
621
+
622
+ // Detect AWS configuration
623
+ const awsResult = await this.detectAWSConfiguration(options);
624
+ if (!awsResult.success) {
625
+ return {
626
+ success: false,
627
+ error: 'AWS configuration detection failed',
628
+ details: awsResult,
629
+ suggestions: awsResult.suggestions
630
+ };
631
+ }
632
+
633
+ // Generate role name based on repository
634
+ const roleName = this.generateRoleName(gitResult.owner, gitResult.repo, options);
635
+
636
+ // Get default policy template
637
+ const policyTemplate = this.getDefaultPolicyTemplate();
638
+
639
+ return {
640
+ success: true,
641
+ git: gitResult,
642
+ aws: awsResult,
643
+ roleName: roleName,
644
+ policyTemplate: policyTemplate,
645
+ zeroConfig: true
646
+ };
647
+
648
+ } catch (error) {
649
+ return {
650
+ success: false,
651
+ error: error.message,
652
+ suggestions: [
653
+ '🔧 Ensure you are in a git repository with GitHub remote',
654
+ '⚙️ Configure AWS CLI or set AWS_DEFAULT_REGION',
655
+ '✅ Run with --dry-run to see what would be configured'
656
+ ]
657
+ };
658
+ }
659
+ }
660
+
661
+ /**
662
+ * Auto-generate role name based on repository
663
+ */
664
+ generateRoleName(owner, repo, options = {}) {
665
+ // Use provided role name if specified
666
+ if (options.roleName && options.roleName !== 'GitHubActionsRole') {
667
+ return options.roleName;
668
+ }
669
+
670
+ // Generate role name: GitHubActions-owner-repo
671
+ const safeName = `GitHubActions-${owner}-${repo}`.replace(/[^a-zA-Z0-9-]/g, '-');
672
+
673
+ // Ensure it meets IAM role name requirements (max 64 chars, alphanumeric + hyphens)
674
+ if (safeName.length > 64) {
675
+ return `GitHubActions-${owner}`.substring(0, 64);
676
+ }
677
+
678
+ return safeName;
679
+ }
680
+
681
+ /**
682
+ * Get default policy template for standard use cases
683
+ */
684
+ getDefaultPolicyTemplate() {
685
+ return {
686
+ name: 'standard',
687
+ description: 'Standard permissions for common GitHub Actions workflows',
688
+ policies: [
689
+ {
690
+ name: 'GitHubActionsBasePolicy',
691
+ document: {
692
+ Version: '2012-10-17',
693
+ Statement: [
694
+ {
695
+ Effect: 'Allow',
696
+ Action: [
697
+ 'sts:GetCallerIdentity',
698
+ 'sts:TagSession'
699
+ ],
700
+ Resource: '*'
701
+ }
702
+ ]
703
+ }
704
+ }
705
+ ]
706
+ };
707
+ }
708
+
709
+ /**
710
+ * Get help text for OIDC command
711
+ */
712
+ getHelpText() {
713
+ return `
714
+ Configure GitHub Actions OIDC integration with AWS.
715
+
716
+ This command creates AWS OIDC identity provider, IAM role with trust policy,
717
+ and configures GitHub repository variables for secure passwordless authentication.
718
+
719
+ Usage:
720
+ claude-commands oidc [options]
721
+
722
+ Options:
723
+ --region <region> AWS region (default: us-east-1)
724
+ --role-name <name> IAM role name (default: GitHubActionsRole)
725
+ --repository-path <path> Repository path (default: current directory)
726
+ --dry-run Preview actions without making changes
727
+ --verbose Show detailed output
728
+ --help Show this help message
729
+
730
+ Examples:
731
+ claude-commands oidc --help
732
+ claude-commands oidc --dry-run
733
+ claude-commands oidc --region us-west-2 --role-name MyGitHubRole
734
+
735
+ This command creates direct IAM resources without CloudFormation.
736
+ `.trim();
737
+ }
738
+ }
739
+
740
+ module.exports = OidcCommand;