@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.
- package/LICENSE +21 -0
- package/README.md +88 -37
- package/bin/claude-commands +307 -65
- package/commands/active/xarchitecture.md +393 -0
- package/commands/active/xconfig.md +127 -0
- package/commands/active/xcontinue.md +92 -0
- package/commands/active/xdebug.md +130 -0
- package/commands/active/xdocs.md +178 -0
- package/commands/active/xexplore.md +94 -0
- package/commands/active/xgit.md +149 -0
- package/commands/active/xpipeline.md +152 -0
- package/commands/active/xquality.md +96 -0
- package/commands/active/xrefactor.md +198 -0
- package/commands/active/xrelease.md +142 -0
- package/commands/active/xsecurity.md +92 -0
- package/commands/active/xspec.md +174 -0
- package/commands/active/xtdd.md +151 -0
- package/commands/active/xtest.md +89 -0
- package/commands/active/xverify.md +80 -0
- package/commands/experiments/xact.md +742 -0
- package/commands/experiments/xanalytics.md +113 -0
- package/commands/experiments/xanalyze.md +70 -0
- package/commands/experiments/xapi.md +161 -0
- package/commands/experiments/xatomic.md +112 -0
- package/commands/experiments/xaws.md +85 -0
- package/commands/experiments/xcicd.md +337 -0
- package/commands/experiments/xcommit.md +122 -0
- package/commands/experiments/xcompliance.md +182 -0
- package/commands/experiments/xconstraints.md +89 -0
- package/commands/experiments/xcoverage.md +90 -0
- package/commands/experiments/xdb.md +102 -0
- package/commands/experiments/xdesign.md +121 -0
- package/commands/experiments/xdevcontainer.md +238 -0
- package/commands/experiments/xevaluate.md +111 -0
- package/commands/experiments/xfootnote.md +12 -0
- package/commands/experiments/xgenerate.md +117 -0
- package/commands/experiments/xgovernance.md +149 -0
- package/commands/experiments/xgreen.md +66 -0
- package/commands/experiments/xiac.md +118 -0
- package/commands/experiments/xincident.md +137 -0
- package/commands/experiments/xinfra.md +115 -0
- package/commands/experiments/xknowledge.md +115 -0
- package/commands/experiments/xmaturity.md +120 -0
- package/commands/experiments/xmetrics.md +118 -0
- package/commands/experiments/xmonitoring.md +128 -0
- package/commands/experiments/xnew.md +903 -0
- package/commands/experiments/xobservable.md +114 -0
- package/commands/experiments/xoidc.md +165 -0
- package/commands/experiments/xoptimize.md +115 -0
- package/commands/experiments/xperformance.md +112 -0
- package/commands/experiments/xplanning.md +131 -0
- package/commands/experiments/xpolicy.md +115 -0
- package/commands/experiments/xproduct.md +98 -0
- package/commands/experiments/xreadiness.md +75 -0
- package/commands/experiments/xred.md +55 -0
- package/commands/experiments/xrisk.md +128 -0
- package/commands/experiments/xrules.md +124 -0
- package/commands/experiments/xsandbox.md +120 -0
- package/commands/experiments/xscan.md +102 -0
- package/commands/experiments/xsetup.md +123 -0
- package/commands/experiments/xtemplate.md +116 -0
- package/commands/experiments/xtrace.md +212 -0
- package/commands/experiments/xux.md +171 -0
- package/commands/experiments/xvalidate.md +104 -0
- package/commands/experiments/xworkflow.md +113 -0
- package/hooks/.smellrc.example.json +19 -0
- package/hooks/README.md +263 -0
- package/hooks/check-commit-signing.py +127 -0
- package/hooks/check-complexity.py +38 -0
- package/hooks/check-security.py +37 -0
- package/hooks/claude-wrapper.sh +29 -0
- package/hooks/config.py +110 -0
- package/hooks/file-logger.sh +100 -0
- package/hooks/lib/argument-parser.sh +427 -0
- package/hooks/lib/config-constants.sh +230 -0
- package/hooks/lib/context-manager.sh +560 -0
- package/hooks/lib/error-handler.sh +423 -0
- package/hooks/lib/execution-engine.sh +444 -0
- package/hooks/lib/execution-results.sh +113 -0
- package/hooks/lib/execution-simulation.sh +114 -0
- package/hooks/lib/field-validators.sh +104 -0
- package/hooks/lib/file-utils.sh +398 -0
- package/hooks/lib/subagent-discovery.sh +468 -0
- package/hooks/lib/subagent-validator.sh +407 -0
- package/hooks/lib/validation-reporter.sh +134 -0
- package/hooks/on-error-debug.sh +226 -0
- package/hooks/pre-commit-quality.sh +204 -0
- package/hooks/pre-commit-test-runner.sh +132 -0
- package/hooks/pre-write-security.sh +115 -0
- package/hooks/prevent-credential-exposure.sh +279 -0
- package/hooks/security_bandit.py +177 -0
- package/hooks/security_checks.py +97 -0
- package/hooks/security_secrets.py +81 -0
- package/hooks/security_trojan.py +61 -0
- package/hooks/settings.example.json +52 -0
- package/hooks/smell_checks.py +238 -0
- package/hooks/smell_javascript.py +231 -0
- package/hooks/smell_python.py +110 -0
- package/hooks/smell_ruff.py +70 -0
- package/hooks/smell_types.py +72 -0
- package/hooks/subagent-trigger-simple.sh +202 -0
- package/hooks/subagent-trigger.sh +253 -0
- package/hooks/suppression.py +82 -0
- package/hooks/tab-color.sh +70 -0
- package/hooks/verify-before-edit.sh +135 -0
- package/lib/backup-restore-command.js +140 -0
- package/lib/base/base-command.js +252 -0
- package/lib/base/command-result.js +184 -0
- package/lib/config/constants.js +255 -0
- package/lib/config.js +48 -6
- package/lib/configure-command.js +428 -0
- package/lib/dependency-validator.js +64 -5
- package/lib/hook-installer-core.js +2 -2
- package/lib/installation-instruction-generator.js +213 -495
- package/lib/installer.js +134 -56
- package/lib/oidc-command.js +740 -0
- package/lib/services/backup-list-service.js +226 -0
- package/lib/services/backup-service.js +230 -0
- package/lib/services/command-installer-service.js +217 -0
- package/lib/services/logger-service.js +201 -0
- package/lib/services/package-manager-service.js +319 -0
- package/lib/services/platform-instruction-service.js +294 -0
- package/lib/services/recovery-instruction-service.js +348 -0
- package/lib/services/restore-service.js +221 -0
- package/lib/setup-command.js +359 -0
- package/lib/setup-wizard.js +155 -262
- package/lib/uninstall-command.js +100 -0
- package/lib/utils/claude-path-config.js +184 -0
- package/lib/utils/file-system-utils.js +152 -0
- package/lib/utils.js +8 -4
- package/lib/verify-command.js +430 -0
- package/package.json +7 -3
- package/scripts/postinstall.js +172 -157
- package/subagents/debug-specialist.md +7 -0
- package/templates/README.md +115 -0
- package/templates/basic-settings.json +30 -0
- package/templates/comprehensive-settings.json +57 -0
- package/templates/global-claude.md +344 -0
- package/templates/hybrid-hook-config.yaml +132 -0
- package/templates/security-focused-settings.json +62 -0
- package/templates/subagent-hooks.yaml +188 -0
- package/lib/package-manager-service.js +0 -270
- 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;
|