@jaguilar87/gaia-ops 1.0.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 (91) hide show
  1. package/CHANGELOG.md +315 -0
  2. package/CLAUDE.md +154 -0
  3. package/LICENSE +21 -0
  4. package/README.md +221 -0
  5. package/agents/aws-troubleshooter.md +50 -0
  6. package/agents/claude-architect.md +821 -0
  7. package/agents/devops-developer.md +92 -0
  8. package/agents/gcp-troubleshooter.md +50 -0
  9. package/agents/gitops-operator.md +360 -0
  10. package/agents/terraform-architect.md +289 -0
  11. package/bin/gaia-init.js +620 -0
  12. package/commands/architect.md +97 -0
  13. package/commands/restore-session.md +87 -0
  14. package/commands/save-session.md +88 -0
  15. package/commands/session-status.md +61 -0
  16. package/commands/speckit.add-task.md +144 -0
  17. package/commands/speckit.analyze-task.md +65 -0
  18. package/commands/speckit.implement.md +96 -0
  19. package/commands/speckit.init.md +237 -0
  20. package/commands/speckit.plan.md +88 -0
  21. package/commands/speckit.specify.md +161 -0
  22. package/commands/speckit.tasks.md +188 -0
  23. package/config/AGENTS.md +162 -0
  24. package/config/agent-catalog.md +604 -0
  25. package/config/context-contracts.md +682 -0
  26. package/config/git-standards.md +674 -0
  27. package/config/git_standards.json +69 -0
  28. package/config/orchestration-workflow.md +735 -0
  29. package/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
  30. package/hooks/__pycache__/pre_kubectl_security.cpython-312.pyc +0 -0
  31. package/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
  32. package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
  33. package/hooks/__pycache__/subagent_stop.cpython-312.pyc +0 -0
  34. package/hooks/post_tool_use.py +463 -0
  35. package/hooks/pre_kubectl_security.py +205 -0
  36. package/hooks/pre_tool_use.py +530 -0
  37. package/hooks/session_start.py +315 -0
  38. package/hooks/subagent_stop.py +549 -0
  39. package/index.js +92 -0
  40. package/package.json +59 -0
  41. package/speckit/README.en.md +648 -0
  42. package/speckit/README.md +353 -0
  43. package/speckit/governance.md +169 -0
  44. package/speckit/scripts/check-prerequisites.sh +194 -0
  45. package/speckit/scripts/common.sh +126 -0
  46. package/speckit/scripts/create-new-feature.sh +131 -0
  47. package/speckit/scripts/init.sh +42 -0
  48. package/speckit/scripts/setup-plan.sh +95 -0
  49. package/speckit/scripts/update-agent-context.sh +718 -0
  50. package/speckit/templates/adr-template.md +118 -0
  51. package/speckit/templates/agent-file-template.md +23 -0
  52. package/speckit/templates/plan-template.md +233 -0
  53. package/speckit/templates/spec-template.md +116 -0
  54. package/speckit/templates/tasks-template-bkp.md +136 -0
  55. package/speckit/templates/tasks-template.md +345 -0
  56. package/templates/CLAUDE.template.md +170 -0
  57. package/templates/code-examples/approval_gate_workflow.py +141 -0
  58. package/templates/code-examples/clarification_workflow.py +94 -0
  59. package/templates/code-examples/commit_validation.py +86 -0
  60. package/templates/project-context.template.json +126 -0
  61. package/templates/settings.template.json +307 -0
  62. package/tools/__pycache__/agent_router.cpython-312.pyc +0 -0
  63. package/tools/__pycache__/approval_gate.cpython-312.pyc +0 -0
  64. package/tools/__pycache__/clarify_engine.cpython-312.pyc +0 -0
  65. package/tools/__pycache__/clarify_patterns.cpython-312.pyc +0 -0
  66. package/tools/__pycache__/commit_validator.cpython-312.pyc +0 -0
  67. package/tools/__pycache__/context_section_reader.cpython-312.pyc +0 -0
  68. package/tools/__pycache__/routing_dashboard.cpython-312.pyc +0 -0
  69. package/tools/__pycache__/routing_feedback.cpython-312.pyc +0 -0
  70. package/tools/__pycache__/semantic_matcher.cpython-312.pyc +0 -0
  71. package/tools/__pycache__/task_manager.cpython-312.pyc +0 -0
  72. package/tools/agent_capabilities.json +231 -0
  73. package/tools/agent_invoker_helper.py +239 -0
  74. package/tools/agent_router.py +730 -0
  75. package/tools/approval_gate.py +318 -0
  76. package/tools/clarify_engine.py +511 -0
  77. package/tools/clarify_patterns.py +356 -0
  78. package/tools/commit_validator.py +338 -0
  79. package/tools/context_provider.py +181 -0
  80. package/tools/context_section_reader.py +301 -0
  81. package/tools/demo_clarify.py +104 -0
  82. package/tools/generate_embeddings.py +168 -0
  83. package/tools/quicktriage_aws_troubleshooter.sh +45 -0
  84. package/tools/quicktriage_devops_developer.sh +38 -0
  85. package/tools/quicktriage_gcp_troubleshooter.sh +51 -0
  86. package/tools/quicktriage_gitops_operator.sh +47 -0
  87. package/tools/quicktriage_terraform_architect.sh +40 -0
  88. package/tools/semantic_matcher.py +222 -0
  89. package/tools/task_manager.py +547 -0
  90. package/tools/task_manager_README.md +395 -0
  91. package/tools/task_manager_example.py +215 -0
@@ -0,0 +1,620 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @jaguilar87/gaia-ops - Installer CLI
5
+ *
6
+ * Interactive installer for Gaia-Ops agent system
7
+ *
8
+ * Usage:
9
+ * npx @jaguilar87/gaia-ops init # Interactive mode
10
+ * npx @jaguilar87/gaia-ops init --non-interactive # Non-interactive mode
11
+ * gaia-init # If installed globally
12
+ *
13
+ * CLI Options:
14
+ * --non-interactive Skip interactive prompts, use defaults or provided values
15
+ * --gitops <path> GitOps directory path
16
+ * --terraform <path> Terraform directory path
17
+ * --app-services <path> App services directory path
18
+ * --project-id <id> GCP Project ID
19
+ * --region <region> Primary region (default: us-central1)
20
+ * --cluster <name> Cluster name
21
+ * --skip-claude-install Skip Claude Code installation
22
+ *
23
+ * Environment Variables:
24
+ * CLAUDE_GITOPS_DIR GitOps directory path
25
+ * CLAUDE_TERRAFORM_DIR Terraform directory path
26
+ * CLAUDE_APP_SERVICES_DIR App services directory path
27
+ * CLAUDE_PROJECT_ID GCP Project ID
28
+ * CLAUDE_REGION Primary region
29
+ * CLAUDE_CLUSTER_NAME Cluster name
30
+ *
31
+ * Features:
32
+ * - Auto-detects project structure (GitOps, Terraform, AppServices)
33
+ * - Installs Claude Code if not present
34
+ * - Generates CLAUDE.md with correct paths
35
+ * - Creates .claude/ symlinks to npm package
36
+ * - Non-invasive: works from any directory
37
+ */
38
+
39
+ import { fileURLToPath } from 'url';
40
+ import { dirname, join, relative } from 'path';
41
+ import fs from 'fs/promises';
42
+ import { existsSync } from 'fs';
43
+ import { exec } from 'child_process';
44
+ import { promisify } from 'util';
45
+ import prompts from 'prompts';
46
+ import chalk from 'chalk';
47
+ import ora from 'ora';
48
+ import yargs from 'yargs';
49
+ import { hideBin } from 'yargs/helpers';
50
+ import { PACKAGE_ROOT, getTemplatePath } from '@jaguilar87/gaia-ops';
51
+
52
+ const execAsync = promisify(exec);
53
+ const __filename = fileURLToPath(import.meta.url);
54
+ const __dirname = dirname(__filename);
55
+
56
+ const CWD = process.cwd();
57
+
58
+ // ============================================================================
59
+ // Auto-detection logic
60
+ // ============================================================================
61
+
62
+ /**
63
+ * Detect project structure by scanning for common directories
64
+ */
65
+ async function detectProjectStructure() {
66
+ const spinner = ora('Detecting project structure...').start();
67
+
68
+ const detected = {
69
+ gitops: null,
70
+ terraform: null,
71
+ appServices: null,
72
+ git: null
73
+ };
74
+
75
+ try {
76
+ const entries = await fs.readdir(CWD);
77
+
78
+ // GitOps candidates
79
+ const gitopsCandidates = [
80
+ 'gitops',
81
+ 'non-prod-rnd-gke-gitops',
82
+ 'k8s',
83
+ 'kubernetes',
84
+ 'manifests',
85
+ 'deployments'
86
+ ];
87
+ for (const candidate of gitopsCandidates) {
88
+ if (entries.includes(candidate)) {
89
+ detected.gitops = `./${candidate}`;
90
+ break;
91
+ }
92
+ }
93
+
94
+ // Terraform candidates
95
+ const terraformCandidates = [
96
+ 'terraform',
97
+ 'tf',
98
+ 'infrastructure',
99
+ 'iac',
100
+ 'infra'
101
+ ];
102
+ for (const candidate of terraformCandidates) {
103
+ if (entries.includes(candidate)) {
104
+ detected.terraform = `./${candidate}`;
105
+ break;
106
+ }
107
+ }
108
+
109
+ // AppServices candidates
110
+ const appServicesCandidates = [
111
+ 'app-services',
112
+ 'services',
113
+ 'apps',
114
+ 'applications',
115
+ 'src'
116
+ ];
117
+ for (const candidate of appServicesCandidates) {
118
+ if (entries.includes(candidate)) {
119
+ detected.appServices = `./${candidate}`;
120
+ break;
121
+ }
122
+ }
123
+
124
+ // Git root
125
+ if (entries.includes('.git')) {
126
+ detected.git = '.';
127
+ }
128
+
129
+ spinner.succeed('Project structure detected');
130
+ } catch (error) {
131
+ spinner.fail('Failed to detect project structure');
132
+ throw error;
133
+ }
134
+
135
+ return detected;
136
+ }
137
+
138
+ /**
139
+ * Check if Claude Code is installed
140
+ */
141
+ async function checkClaudeCodeInstalled() {
142
+ try {
143
+ // Try 'claude' command first (common alias)
144
+ await execAsync('claude --version');
145
+ return true;
146
+ } catch {
147
+ try {
148
+ // Fallback to 'claude-code' command
149
+ await execAsync('claude-code --version');
150
+ return true;
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+ }
156
+
157
+ // ============================================================================
158
+ // CLI Argument Parsing
159
+ // ============================================================================
160
+
161
+ /**
162
+ * Parse CLI arguments
163
+ */
164
+ function parseCliArguments() {
165
+ return yargs(hideBin(process.argv))
166
+ .usage('Usage: $0 [options]')
167
+ .option('non-interactive', {
168
+ alias: 'y',
169
+ type: 'boolean',
170
+ description: 'Skip interactive prompts, use defaults or provided values',
171
+ default: false
172
+ })
173
+ .option('gitops', {
174
+ type: 'string',
175
+ description: 'GitOps directory path'
176
+ })
177
+ .option('terraform', {
178
+ type: 'string',
179
+ description: 'Terraform directory path'
180
+ })
181
+ .option('app-services', {
182
+ type: 'string',
183
+ description: 'App services directory path'
184
+ })
185
+ .option('project-id', {
186
+ type: 'string',
187
+ description: 'GCP Project ID'
188
+ })
189
+ .option('region', {
190
+ type: 'string',
191
+ description: 'Primary region (default: us-central1)',
192
+ default: 'us-central1'
193
+ })
194
+ .option('cluster', {
195
+ type: 'string',
196
+ description: 'Cluster name'
197
+ })
198
+ .option('skip-claude-install', {
199
+ type: 'boolean',
200
+ description: 'Skip Claude Code installation',
201
+ default: false
202
+ })
203
+ .help('h')
204
+ .alias('h', 'help')
205
+ .version('1.0.0')
206
+ .alias('v', 'version')
207
+ .parse();
208
+ }
209
+
210
+ /**
211
+ * Get configuration from CLI args, environment variables, and detected values
212
+ */
213
+ function getConfiguration(detected, args) {
214
+ // Priority: CLI args > Environment variables > Detected values > Defaults
215
+
216
+ const config = {
217
+ gitops: args.gitops || process.env.CLAUDE_GITOPS_DIR || detected.gitops || './gitops',
218
+ terraform: args.terraform || process.env.CLAUDE_TERRAFORM_DIR || detected.terraform || './terraform',
219
+ appServices: args.appServices || process.env.CLAUDE_APP_SERVICES_DIR || detected.appServices || './app-services',
220
+ projectId: args.projectId || process.env.CLAUDE_PROJECT_ID || '',
221
+ region: args.region || process.env.CLAUDE_REGION || 'us-central1',
222
+ clusterName: args.cluster || process.env.CLAUDE_CLUSTER_NAME || '',
223
+ installClaudeCode: !args.skipClaudeInstall
224
+ };
225
+
226
+ return config;
227
+ }
228
+
229
+ /**
230
+ * Validate required configuration values
231
+ */
232
+ function validateConfiguration(config, nonInteractive) {
233
+ const errors = [];
234
+
235
+ if (!config.projectId && nonInteractive) {
236
+ errors.push('--project-id is required in non-interactive mode');
237
+ }
238
+
239
+ if (!config.clusterName && nonInteractive) {
240
+ errors.push('--cluster is required in non-interactive mode');
241
+ }
242
+
243
+ if (errors.length > 0) {
244
+ console.error(chalk.red('\nāŒ Configuration errors:\n'));
245
+ errors.forEach(error => console.error(chalk.red(` - ${error}`)));
246
+ console.error(chalk.gray('\nProvide values via CLI args or environment variables.\n'));
247
+ process.exit(1);
248
+ }
249
+
250
+ return true;
251
+ }
252
+
253
+ // ============================================================================
254
+ // Interactive prompts
255
+ // ============================================================================
256
+
257
+ /**
258
+ * Present interactive wizard to user
259
+ */
260
+ async function runInteractiveWizard(detected) {
261
+ console.log(chalk.cyan.bold('\nšŸ¤– Claude Code Agent System Installer\n'));
262
+ console.log(chalk.gray('This wizard will set up the Claude Code agent system for your project.\n'));
263
+
264
+ const questions = [
265
+ {
266
+ type: 'text',
267
+ name: 'gitops',
268
+ message: 'šŸ“¦ GitOps directory (Flux/Kubernetes manifests):',
269
+ initial: detected.gitops || './gitops',
270
+ validate: value => value.trim().length > 0
271
+ },
272
+ {
273
+ type: 'text',
274
+ name: 'terraform',
275
+ message: 'šŸ”§ Terraform directory (infrastructure as code):',
276
+ initial: detected.terraform || './terraform',
277
+ validate: value => value.trim().length > 0
278
+ },
279
+ {
280
+ type: 'text',
281
+ name: 'appServices',
282
+ message: 'šŸš€ App Services directory (application code):',
283
+ initial: detected.appServices || './app-services',
284
+ validate: value => value.trim().length > 0
285
+ },
286
+ {
287
+ type: 'text',
288
+ name: 'projectId',
289
+ message: '🌐 GCP Project ID (e.g., aaxis-rnd-non-prod):',
290
+ validate: value => value.trim().length > 0
291
+ },
292
+ {
293
+ type: 'text',
294
+ name: 'region',
295
+ message: 'šŸŒ Primary Region (e.g., us-central1):',
296
+ initial: 'us-central1',
297
+ validate: value => value.trim().length > 0
298
+ },
299
+ {
300
+ type: 'text',
301
+ name: 'clusterName',
302
+ message: 'ā˜øļø Cluster Name (e.g., non-prod-rnd-gke):',
303
+ validate: value => value.trim().length > 0
304
+ },
305
+ {
306
+ type: 'confirm',
307
+ name: 'installClaudeCode',
308
+ message: 'šŸ“„ Install Claude Code if not present?',
309
+ initial: true
310
+ }
311
+ ];
312
+
313
+ const responses = await prompts(questions);
314
+
315
+ // User cancelled
316
+ if (Object.keys(responses).length < questions.length) {
317
+ console.log(chalk.yellow('\nāš ļø Installation cancelled by user\n'));
318
+ process.exit(0);
319
+ }
320
+
321
+ return responses;
322
+ }
323
+
324
+ // ============================================================================
325
+ // Installation steps
326
+ // ============================================================================
327
+
328
+ /**
329
+ * Install Claude Code CLI
330
+ */
331
+ async function installClaudeCode() {
332
+ const spinner = ora('Installing Claude Code...').start();
333
+
334
+ try {
335
+ await execAsync('npm install -g @anthropic-ai/claude-code');
336
+ spinner.succeed('Claude Code installed successfully');
337
+ } catch (error) {
338
+ spinner.fail('Failed to install Claude Code');
339
+ console.error(chalk.red('\nError:'), error.message);
340
+ console.log(chalk.yellow('\nPlease install Claude Code manually:'));
341
+ console.log(chalk.gray(' npm install -g @anthropic-ai/claude-code\n'));
342
+ throw error;
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Create .claude/ directory structure with symlinks
348
+ */
349
+ async function createClaudeDirectory() {
350
+ const spinner = ora('Creating .claude/ directory...').start();
351
+
352
+ try {
353
+ const claudeDir = join(CWD, '.claude');
354
+
355
+ // Create base directory
356
+ await fs.mkdir(claudeDir, { recursive: true });
357
+
358
+ // Calculate relative path from .claude/ to node_modules/@jaguilar87/gaia-ops
359
+ const packagePath = join(CWD, 'node_modules', '@jaguilar87', 'gaia-ops');
360
+ const relativePath = relative(claudeDir, packagePath);
361
+
362
+ // Create symlinks to npm package
363
+ const symlinks = [
364
+ { target: join(relativePath, 'agents'), link: join(claudeDir, 'agents') },
365
+ { target: join(relativePath, 'tools'), link: join(claudeDir, 'tools') },
366
+ { target: join(relativePath, 'hooks'), link: join(claudeDir, 'hooks') },
367
+ { target: join(relativePath, 'commands'), link: join(claudeDir, 'commands') },
368
+ { target: join(relativePath, 'templates'), link: join(claudeDir, 'templates') },
369
+ { target: join(relativePath, 'config'), link: join(claudeDir, 'config') },
370
+ { target: join(relativePath, 'speckit'), link: join(claudeDir, 'speckit') },
371
+ { target: join(relativePath, 'CHANGELOG.md'), link: join(claudeDir, 'CHANGELOG.md') }
372
+ ];
373
+
374
+ for (const { target, link } of symlinks) {
375
+ // Remove existing symlink if present
376
+ if (existsSync(link)) {
377
+ await fs.unlink(link);
378
+ }
379
+ await fs.symlink(target, link);
380
+ }
381
+
382
+ // Create project-specific directories (NOT symlinked)
383
+ await fs.mkdir(join(claudeDir, 'logs'), { recursive: true });
384
+ await fs.mkdir(join(claudeDir, 'tests'), { recursive: true });
385
+
386
+ spinner.succeed('.claude/ directory created with symlinks');
387
+ } catch (error) {
388
+ spinner.fail('Failed to create .claude/ directory');
389
+ throw error;
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Generate CLAUDE.md from template
395
+ */
396
+ async function generateClaudeMd(config) {
397
+ const spinner = ora('Generating CLAUDE.md...').start();
398
+
399
+ try {
400
+ // Read template
401
+ const templatePath = getTemplatePath('CLAUDE.template.md');
402
+ let template = await fs.readFile(templatePath, 'utf-8');
403
+
404
+ // Replace placeholders
405
+ template = template.replace(/{{PROJECT_ID}}/g, config.projectId);
406
+ template = template.replace(/{{REGION}}/g, config.region);
407
+ template = template.replace(/{{CLUSTER_NAME}}/g, config.clusterName);
408
+ template = template.replace(/{{GITOPS_PATH}}/g, config.gitops);
409
+ template = template.replace(/{{TERRAFORM_PATH}}/g, config.terraform);
410
+ template = template.replace(/{{APP_SERVICES_PATH}}/g, config.appServices);
411
+
412
+ // Write to current directory
413
+ const claudeMdPath = join(CWD, 'CLAUDE.md');
414
+ await fs.writeFile(claudeMdPath, template, 'utf-8');
415
+
416
+ spinner.succeed('CLAUDE.md generated');
417
+ } catch (error) {
418
+ spinner.fail('Failed to generate CLAUDE.md');
419
+ throw error;
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Generate AGENTS.md symlink
425
+ */
426
+ async function generateAgentsMd() {
427
+ const spinner = ora('Creating AGENTS.md symlink...').start();
428
+
429
+ try {
430
+ const agentsMdLink = join(CWD, 'AGENTS.md');
431
+ const packagePath = join(CWD, 'node_modules', '@jaguilar87', 'gaia-ops', 'config', 'AGENTS.md');
432
+ const relativePath = relative(CWD, packagePath);
433
+
434
+ // Remove existing if present
435
+ if (existsSync(agentsMdLink)) {
436
+ await fs.unlink(agentsMdLink);
437
+ }
438
+
439
+ await fs.symlink(relativePath, agentsMdLink);
440
+
441
+ spinner.succeed('AGENTS.md symlink created');
442
+ } catch (error) {
443
+ spinner.fail('Failed to create AGENTS.md symlink');
444
+ throw error;
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Generate project-context.json
450
+ */
451
+ async function generateProjectContext(config) {
452
+ const spinner = ora('Generating project-context.json...').start();
453
+
454
+ try {
455
+ const projectContext = {
456
+ version: '1.0',
457
+ last_updated: new Date().toISOString(),
458
+ project_details: {
459
+ id: config.projectId,
460
+ region: config.region,
461
+ environment: 'non-prod',
462
+ cluster_name: config.clusterName
463
+ },
464
+ terraform_infrastructure: {
465
+ layout: {
466
+ base_path: config.terraform,
467
+ module_structure: 'terragrunt'
468
+ },
469
+ provider_credentials: {
470
+ gcp: {
471
+ project: config.projectId,
472
+ region: config.region
473
+ }
474
+ }
475
+ },
476
+ gitops_configuration: {
477
+ repository: {
478
+ path: config.gitops,
479
+ platform: 'flux'
480
+ },
481
+ flux_details: {
482
+ namespaces: []
483
+ }
484
+ },
485
+ application_services: {
486
+ base_path: config.appServices,
487
+ services: []
488
+ },
489
+ operational_guidelines: {
490
+ commit_standards: {
491
+ format: 'conventional_commits',
492
+ validation_required: true,
493
+ config_path: '.claude/config/git_standards.json'
494
+ }
495
+ }
496
+ };
497
+
498
+ const projectContextPath = join(CWD, '.claude', 'project-context.json');
499
+ await fs.writeFile(projectContextPath, JSON.stringify(projectContext, null, 2), 'utf-8');
500
+
501
+ spinner.succeed('project-context.json generated');
502
+ } catch (error) {
503
+ spinner.fail('Failed to generate project-context.json');
504
+ throw error;
505
+ }
506
+ }
507
+
508
+ /**
509
+ * Install @jaguilar87/gaia-ops as dependency
510
+ */
511
+ async function installClaudeAgentsPackage() {
512
+ const spinner = ora('Installing @jaguilar87/gaia-ops package...').start();
513
+
514
+ try {
515
+ // Check if package.json exists
516
+ const packageJsonPath = join(CWD, 'package.json');
517
+ if (!existsSync(packageJsonPath)) {
518
+ // Create minimal package.json
519
+ const packageJson = {
520
+ name: 'my-project',
521
+ version: '1.0.0',
522
+ private: true,
523
+ dependencies: {}
524
+ };
525
+ await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8');
526
+ }
527
+
528
+ // Install @jaguilar87/gaia-ops
529
+ // NOTE: In production, this would install from npm registry
530
+ // For now, we'll use local path
531
+ await execAsync('npm install @jaguilar87/gaia-ops');
532
+
533
+ spinner.succeed('@jaguilar87/gaia-ops package installed');
534
+ } catch (error) {
535
+ spinner.fail('Failed to install @jaguilar87/gaia-ops');
536
+ throw error;
537
+ }
538
+ }
539
+
540
+ // ============================================================================
541
+ // Main installer flow
542
+ // ============================================================================
543
+
544
+ async function main() {
545
+ try {
546
+ // Parse CLI arguments
547
+ const args = parseCliArguments();
548
+
549
+ // Clear console unless in non-interactive mode
550
+ if (!args.nonInteractive) {
551
+ console.clear();
552
+ }
553
+
554
+ // Step 1: Check Claude Code installation
555
+ const claudeCodeInstalled = await checkClaudeCodeInstalled();
556
+ if (!claudeCodeInstalled) {
557
+ console.log(chalk.yellow('āš ļø Claude Code is not installed\n'));
558
+ } else {
559
+ console.log(chalk.green('āœ… Claude Code is already installed\n'));
560
+ }
561
+
562
+ // Step 2: Auto-detect project structure
563
+ const detected = await detectProjectStructure();
564
+
565
+ // Step 3: Get configuration (CLI args, env vars, detected, or interactive)
566
+ let config;
567
+ if (args.nonInteractive) {
568
+ // Non-interactive mode: use CLI args, env vars, and detected values
569
+ config = getConfiguration(detected, args);
570
+ validateConfiguration(config, true);
571
+
572
+ // Display configuration being used
573
+ console.log(chalk.cyan('\nšŸ“‹ Configuration:\n'));
574
+ console.log(chalk.gray(` GitOps: ${config.gitops}`));
575
+ console.log(chalk.gray(` Terraform: ${config.terraform}`));
576
+ console.log(chalk.gray(` App Services: ${config.appServices}`));
577
+ console.log(chalk.gray(` Project ID: ${config.projectId}`));
578
+ console.log(chalk.gray(` Region: ${config.region}`));
579
+ console.log(chalk.gray(` Cluster: ${config.clusterName}\n`));
580
+ } else {
581
+ // Interactive mode: run wizard with pre-filled detected values
582
+ config = await runInteractiveWizard(detected);
583
+ }
584
+
585
+ // Step 4: Install Claude Code if requested
586
+ if (config.installClaudeCode && !claudeCodeInstalled) {
587
+ await installClaudeCode();
588
+ }
589
+
590
+ // Step 5: Install @jaguilar87/gaia-ops package
591
+ await installClaudeAgentsPackage();
592
+
593
+ // Step 6: Create .claude/ directory with symlinks
594
+ await createClaudeDirectory();
595
+
596
+ // Step 7: Generate CLAUDE.md
597
+ await generateClaudeMd(config);
598
+
599
+ // Step 8: Generate AGENTS.md symlink
600
+ await generateAgentsMd();
601
+
602
+ // Step 9: Generate project-context.json
603
+ await generateProjectContext(config);
604
+
605
+ // Success message
606
+ console.log(chalk.green.bold('\nāœ… Installation complete!\n'));
607
+ console.log(chalk.gray('Next steps:'));
608
+ console.log(chalk.gray(' 1. Review CLAUDE.md and adjust paths if needed'));
609
+ console.log(chalk.gray(' 2. Update .claude/project-context.json with your services'));
610
+ console.log(chalk.gray(' 3. Start Claude Code: claude-code\n'));
611
+ console.log(chalk.cyan('šŸ“š Documentation: .claude/config/\n'));
612
+
613
+ } catch (error) {
614
+ console.error(chalk.red('\nāŒ Installation failed\n'));
615
+ console.error(error);
616
+ process.exit(1);
617
+ }
618
+ }
619
+
620
+ main();