@build-astron-co/nimbus 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,1046 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init Command
|
|
3
|
+
*
|
|
4
|
+
* Initialize a Nimbus workspace in the current directory
|
|
5
|
+
* Performs comprehensive project scanning to detect:
|
|
6
|
+
* - Languages and versions
|
|
7
|
+
* - Frameworks
|
|
8
|
+
* - Package managers
|
|
9
|
+
* - Infrastructure as Code (Terraform, Pulumi, CDK, etc.)
|
|
10
|
+
* - CI/CD platforms
|
|
11
|
+
* - Cloud providers
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { ui } from '../wizard/ui';
|
|
17
|
+
import { select, input, confirm } from '../wizard/prompts';
|
|
18
|
+
import {
|
|
19
|
+
createProjectScanner,
|
|
20
|
+
generateProjectYaml,
|
|
21
|
+
type ProjectContext,
|
|
22
|
+
type ScanOptions,
|
|
23
|
+
} from '../scanners';
|
|
24
|
+
import { initContextDatabase } from '../context/context-db';
|
|
25
|
+
|
|
26
|
+
export interface InitOptions {
|
|
27
|
+
/** Non-interactive mode */
|
|
28
|
+
nonInteractive?: boolean;
|
|
29
|
+
/** Force overwrite existing configuration */
|
|
30
|
+
force?: boolean;
|
|
31
|
+
/** Project name */
|
|
32
|
+
name?: string;
|
|
33
|
+
/** Default cloud provider */
|
|
34
|
+
provider?: string;
|
|
35
|
+
/** Output directory */
|
|
36
|
+
output?: string;
|
|
37
|
+
/** Scan depth: quick, standard, or deep */
|
|
38
|
+
scanDepth?: 'quick' | 'standard' | 'deep';
|
|
39
|
+
/** Custom project instructions */
|
|
40
|
+
instructions?: string;
|
|
41
|
+
/** Template to use: vpc, eks, full-stack, minimal */
|
|
42
|
+
template?: 'vpc' | 'eks' | 'full-stack' | 'minimal';
|
|
43
|
+
/** Import from existing IaC directory */
|
|
44
|
+
fromExisting?: string;
|
|
45
|
+
/** Maximum directory depth for project scanning */
|
|
46
|
+
maxDepth?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const NIMBUS_DIR = '.nimbus';
|
|
50
|
+
const PROJECT_FILE = 'project.yaml';
|
|
51
|
+
const CONFIG_FILE = 'config.yaml';
|
|
52
|
+
|
|
53
|
+
// Template definitions
|
|
54
|
+
const TEMPLATES: Record<string, { description: string; files: Record<string, string> }> = {
|
|
55
|
+
vpc: {
|
|
56
|
+
description: 'AWS VPC with subnets, NAT gateway, and security groups',
|
|
57
|
+
files: {
|
|
58
|
+
'terraform/main.tf': `# VPC Infrastructure
|
|
59
|
+
# Generated by Nimbus
|
|
60
|
+
|
|
61
|
+
terraform {
|
|
62
|
+
required_providers {
|
|
63
|
+
aws = {
|
|
64
|
+
source = "hashicorp/aws"
|
|
65
|
+
version = "~> 5.0"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
provider "aws" {
|
|
71
|
+
region = var.aws_region
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module "vpc" {
|
|
75
|
+
source = "terraform-aws-modules/vpc/aws"
|
|
76
|
+
version = "~> 5.0"
|
|
77
|
+
|
|
78
|
+
name = var.project_name
|
|
79
|
+
cidr = var.vpc_cidr
|
|
80
|
+
|
|
81
|
+
azs = var.availability_zones
|
|
82
|
+
private_subnets = var.private_subnet_cidrs
|
|
83
|
+
public_subnets = var.public_subnet_cidrs
|
|
84
|
+
|
|
85
|
+
enable_nat_gateway = true
|
|
86
|
+
single_nat_gateway = var.single_nat_gateway
|
|
87
|
+
enable_dns_hostnames = true
|
|
88
|
+
enable_dns_support = true
|
|
89
|
+
|
|
90
|
+
tags = var.tags
|
|
91
|
+
}
|
|
92
|
+
`,
|
|
93
|
+
'terraform/variables.tf': `variable "project_name" {
|
|
94
|
+
description = "Name of the project"
|
|
95
|
+
type = string
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
variable "aws_region" {
|
|
99
|
+
description = "AWS region"
|
|
100
|
+
type = string
|
|
101
|
+
default = "us-east-1"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
variable "vpc_cidr" {
|
|
105
|
+
description = "CIDR block for VPC"
|
|
106
|
+
type = string
|
|
107
|
+
default = "10.0.0.0/16"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
variable "availability_zones" {
|
|
111
|
+
description = "Availability zones"
|
|
112
|
+
type = list(string)
|
|
113
|
+
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
variable "private_subnet_cidrs" {
|
|
117
|
+
description = "CIDR blocks for private subnets"
|
|
118
|
+
type = list(string)
|
|
119
|
+
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
variable "public_subnet_cidrs" {
|
|
123
|
+
description = "CIDR blocks for public subnets"
|
|
124
|
+
type = list(string)
|
|
125
|
+
default = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
variable "single_nat_gateway" {
|
|
129
|
+
description = "Use single NAT gateway for cost savings"
|
|
130
|
+
type = bool
|
|
131
|
+
default = true
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
variable "tags" {
|
|
135
|
+
description = "Tags to apply to resources"
|
|
136
|
+
type = map(string)
|
|
137
|
+
default = {}
|
|
138
|
+
}
|
|
139
|
+
`,
|
|
140
|
+
'terraform/outputs.tf': `output "vpc_id" {
|
|
141
|
+
description = "VPC ID"
|
|
142
|
+
value = module.vpc.vpc_id
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
output "private_subnet_ids" {
|
|
146
|
+
description = "Private subnet IDs"
|
|
147
|
+
value = module.vpc.private_subnets
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
output "public_subnet_ids" {
|
|
151
|
+
description = "Public subnet IDs"
|
|
152
|
+
value = module.vpc.public_subnets
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
output "nat_gateway_ips" {
|
|
156
|
+
description = "NAT Gateway IPs"
|
|
157
|
+
value = module.vpc.nat_public_ips
|
|
158
|
+
}
|
|
159
|
+
`,
|
|
160
|
+
'terraform/terraform.tfvars.example': `project_name = "my-project"
|
|
161
|
+
aws_region = "us-east-1"
|
|
162
|
+
vpc_cidr = "10.0.0.0/16"
|
|
163
|
+
|
|
164
|
+
tags = {
|
|
165
|
+
Environment = "dev"
|
|
166
|
+
ManagedBy = "terraform"
|
|
167
|
+
}
|
|
168
|
+
`,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
eks: {
|
|
172
|
+
description: 'AWS EKS cluster with VPC and node groups',
|
|
173
|
+
files: {
|
|
174
|
+
'terraform/main.tf': `# EKS Infrastructure
|
|
175
|
+
# Generated by Nimbus
|
|
176
|
+
|
|
177
|
+
terraform {
|
|
178
|
+
required_providers {
|
|
179
|
+
aws = {
|
|
180
|
+
source = "hashicorp/aws"
|
|
181
|
+
version = "~> 5.0"
|
|
182
|
+
}
|
|
183
|
+
kubernetes = {
|
|
184
|
+
source = "hashicorp/kubernetes"
|
|
185
|
+
version = "~> 2.0"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
provider "aws" {
|
|
191
|
+
region = var.aws_region
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module "vpc" {
|
|
195
|
+
source = "terraform-aws-modules/vpc/aws"
|
|
196
|
+
version = "~> 5.0"
|
|
197
|
+
|
|
198
|
+
name = "\${var.project_name}-vpc"
|
|
199
|
+
cidr = var.vpc_cidr
|
|
200
|
+
|
|
201
|
+
azs = var.availability_zones
|
|
202
|
+
private_subnets = var.private_subnet_cidrs
|
|
203
|
+
public_subnets = var.public_subnet_cidrs
|
|
204
|
+
|
|
205
|
+
enable_nat_gateway = true
|
|
206
|
+
single_nat_gateway = true
|
|
207
|
+
enable_dns_hostnames = true
|
|
208
|
+
|
|
209
|
+
public_subnet_tags = {
|
|
210
|
+
"kubernetes.io/role/elb" = 1
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private_subnet_tags = {
|
|
214
|
+
"kubernetes.io/role/internal-elb" = 1
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
tags = var.tags
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module "eks" {
|
|
221
|
+
source = "terraform-aws-modules/eks/aws"
|
|
222
|
+
version = "~> 19.0"
|
|
223
|
+
|
|
224
|
+
cluster_name = var.project_name
|
|
225
|
+
cluster_version = var.cluster_version
|
|
226
|
+
|
|
227
|
+
vpc_id = module.vpc.vpc_id
|
|
228
|
+
subnet_ids = module.vpc.private_subnets
|
|
229
|
+
|
|
230
|
+
cluster_endpoint_public_access = true
|
|
231
|
+
|
|
232
|
+
eks_managed_node_groups = {
|
|
233
|
+
default = {
|
|
234
|
+
min_size = var.node_min_size
|
|
235
|
+
max_size = var.node_max_size
|
|
236
|
+
desired_size = var.node_desired_size
|
|
237
|
+
|
|
238
|
+
instance_types = var.node_instance_types
|
|
239
|
+
capacity_type = "ON_DEMAND"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
tags = var.tags
|
|
244
|
+
}
|
|
245
|
+
`,
|
|
246
|
+
'terraform/variables.tf': `variable "project_name" {
|
|
247
|
+
description = "Name of the project / EKS cluster"
|
|
248
|
+
type = string
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
variable "aws_region" {
|
|
252
|
+
description = "AWS region"
|
|
253
|
+
type = string
|
|
254
|
+
default = "us-east-1"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
variable "cluster_version" {
|
|
258
|
+
description = "Kubernetes version"
|
|
259
|
+
type = string
|
|
260
|
+
default = "1.28"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
variable "vpc_cidr" {
|
|
264
|
+
description = "CIDR block for VPC"
|
|
265
|
+
type = string
|
|
266
|
+
default = "10.0.0.0/16"
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
variable "availability_zones" {
|
|
270
|
+
description = "Availability zones"
|
|
271
|
+
type = list(string)
|
|
272
|
+
default = ["us-east-1a", "us-east-1b"]
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
variable "private_subnet_cidrs" {
|
|
276
|
+
description = "CIDR blocks for private subnets"
|
|
277
|
+
type = list(string)
|
|
278
|
+
default = ["10.0.1.0/24", "10.0.2.0/24"]
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
variable "public_subnet_cidrs" {
|
|
282
|
+
description = "CIDR blocks for public subnets"
|
|
283
|
+
type = list(string)
|
|
284
|
+
default = ["10.0.101.0/24", "10.0.102.0/24"]
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
variable "node_instance_types" {
|
|
288
|
+
description = "Instance types for node group"
|
|
289
|
+
type = list(string)
|
|
290
|
+
default = ["t3.medium"]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
variable "node_min_size" {
|
|
294
|
+
description = "Minimum number of nodes"
|
|
295
|
+
type = number
|
|
296
|
+
default = 1
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
variable "node_max_size" {
|
|
300
|
+
description = "Maximum number of nodes"
|
|
301
|
+
type = number
|
|
302
|
+
default = 5
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
variable "node_desired_size" {
|
|
306
|
+
description = "Desired number of nodes"
|
|
307
|
+
type = number
|
|
308
|
+
default = 2
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
variable "tags" {
|
|
312
|
+
description = "Tags to apply to resources"
|
|
313
|
+
type = map(string)
|
|
314
|
+
default = {}
|
|
315
|
+
}
|
|
316
|
+
`,
|
|
317
|
+
'terraform/outputs.tf': `output "cluster_endpoint" {
|
|
318
|
+
description = "EKS cluster endpoint"
|
|
319
|
+
value = module.eks.cluster_endpoint
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
output "cluster_name" {
|
|
323
|
+
description = "EKS cluster name"
|
|
324
|
+
value = module.eks.cluster_name
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
output "cluster_certificate_authority_data" {
|
|
328
|
+
description = "Base64 encoded certificate data"
|
|
329
|
+
value = module.eks.cluster_certificate_authority_data
|
|
330
|
+
sensitive = true
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
output "configure_kubectl" {
|
|
334
|
+
description = "Configure kubectl"
|
|
335
|
+
value = "aws eks update-kubeconfig --region \${var.aws_region} --name \${module.eks.cluster_name}"
|
|
336
|
+
}
|
|
337
|
+
`,
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
'full-stack': {
|
|
341
|
+
description: 'Complete AWS stack with VPC, EKS, RDS, and ElastiCache',
|
|
342
|
+
files: {
|
|
343
|
+
'terraform/main.tf': `# Full Stack Infrastructure
|
|
344
|
+
# Generated by Nimbus
|
|
345
|
+
|
|
346
|
+
terraform {
|
|
347
|
+
required_providers {
|
|
348
|
+
aws = {
|
|
349
|
+
source = "hashicorp/aws"
|
|
350
|
+
version = "~> 5.0"
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
provider "aws" {
|
|
356
|
+
region = var.aws_region
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
# VPC
|
|
360
|
+
module "vpc" {
|
|
361
|
+
source = "terraform-aws-modules/vpc/aws"
|
|
362
|
+
version = "~> 5.0"
|
|
363
|
+
|
|
364
|
+
name = "\${var.project_name}-vpc"
|
|
365
|
+
cidr = "10.0.0.0/16"
|
|
366
|
+
|
|
367
|
+
azs = ["us-east-1a", "us-east-1b"]
|
|
368
|
+
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
|
|
369
|
+
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
|
|
370
|
+
database_subnets = ["10.0.201.0/24", "10.0.202.0/24"]
|
|
371
|
+
|
|
372
|
+
enable_nat_gateway = true
|
|
373
|
+
single_nat_gateway = true
|
|
374
|
+
enable_dns_hostnames = true
|
|
375
|
+
|
|
376
|
+
create_database_subnet_group = true
|
|
377
|
+
|
|
378
|
+
tags = var.tags
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
# EKS Cluster
|
|
382
|
+
module "eks" {
|
|
383
|
+
source = "terraform-aws-modules/eks/aws"
|
|
384
|
+
version = "~> 19.0"
|
|
385
|
+
|
|
386
|
+
cluster_name = "\${var.project_name}-cluster"
|
|
387
|
+
cluster_version = "1.28"
|
|
388
|
+
|
|
389
|
+
vpc_id = module.vpc.vpc_id
|
|
390
|
+
subnet_ids = module.vpc.private_subnets
|
|
391
|
+
|
|
392
|
+
cluster_endpoint_public_access = true
|
|
393
|
+
|
|
394
|
+
eks_managed_node_groups = {
|
|
395
|
+
default = {
|
|
396
|
+
min_size = 2
|
|
397
|
+
max_size = 10
|
|
398
|
+
desired_size = 3
|
|
399
|
+
instance_types = ["t3.medium"]
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
tags = var.tags
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
# RDS PostgreSQL
|
|
407
|
+
module "rds" {
|
|
408
|
+
source = "terraform-aws-modules/rds/aws"
|
|
409
|
+
version = "~> 6.0"
|
|
410
|
+
|
|
411
|
+
identifier = "\${var.project_name}-db"
|
|
412
|
+
|
|
413
|
+
engine = "postgres"
|
|
414
|
+
engine_version = "15"
|
|
415
|
+
family = "postgres15"
|
|
416
|
+
major_engine_version = "15"
|
|
417
|
+
instance_class = "db.t3.micro"
|
|
418
|
+
|
|
419
|
+
allocated_storage = 20
|
|
420
|
+
max_allocated_storage = 100
|
|
421
|
+
|
|
422
|
+
db_name = "app"
|
|
423
|
+
username = "dbadmin"
|
|
424
|
+
port = 5432
|
|
425
|
+
|
|
426
|
+
db_subnet_group_name = module.vpc.database_subnet_group_name
|
|
427
|
+
vpc_security_group_ids = [aws_security_group.rds.id]
|
|
428
|
+
|
|
429
|
+
maintenance_window = "Mon:00:00-Mon:03:00"
|
|
430
|
+
backup_window = "03:00-06:00"
|
|
431
|
+
backup_retention_period = 7
|
|
432
|
+
|
|
433
|
+
tags = var.tags
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
# ElastiCache Redis
|
|
437
|
+
resource "aws_elasticache_subnet_group" "main" {
|
|
438
|
+
name = "\${var.project_name}-cache-subnet"
|
|
439
|
+
subnet_ids = module.vpc.private_subnets
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
resource "aws_elasticache_cluster" "main" {
|
|
443
|
+
cluster_id = "\${var.project_name}-cache"
|
|
444
|
+
engine = "redis"
|
|
445
|
+
node_type = "cache.t3.micro"
|
|
446
|
+
num_cache_nodes = 1
|
|
447
|
+
parameter_group_name = "default.redis7"
|
|
448
|
+
port = 6379
|
|
449
|
+
subnet_group_name = aws_elasticache_subnet_group.main.name
|
|
450
|
+
security_group_ids = [aws_security_group.redis.id]
|
|
451
|
+
|
|
452
|
+
tags = var.tags
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
# Security Groups
|
|
456
|
+
resource "aws_security_group" "rds" {
|
|
457
|
+
name = "\${var.project_name}-rds-sg"
|
|
458
|
+
description = "Allow PostgreSQL from VPC"
|
|
459
|
+
vpc_id = module.vpc.vpc_id
|
|
460
|
+
|
|
461
|
+
ingress {
|
|
462
|
+
from_port = 5432
|
|
463
|
+
to_port = 5432
|
|
464
|
+
protocol = "tcp"
|
|
465
|
+
cidr_blocks = [module.vpc.vpc_cidr_block]
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
tags = var.tags
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
resource "aws_security_group" "redis" {
|
|
472
|
+
name = "\${var.project_name}-redis-sg"
|
|
473
|
+
description = "Allow Redis from VPC"
|
|
474
|
+
vpc_id = module.vpc.vpc_id
|
|
475
|
+
|
|
476
|
+
ingress {
|
|
477
|
+
from_port = 6379
|
|
478
|
+
to_port = 6379
|
|
479
|
+
protocol = "tcp"
|
|
480
|
+
cidr_blocks = [module.vpc.vpc_cidr_block]
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
tags = var.tags
|
|
484
|
+
}
|
|
485
|
+
`,
|
|
486
|
+
'terraform/variables.tf': `variable "project_name" {
|
|
487
|
+
description = "Name of the project"
|
|
488
|
+
type = string
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
variable "aws_region" {
|
|
492
|
+
description = "AWS region"
|
|
493
|
+
type = string
|
|
494
|
+
default = "us-east-1"
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
variable "tags" {
|
|
498
|
+
description = "Tags to apply to resources"
|
|
499
|
+
type = map(string)
|
|
500
|
+
default = {
|
|
501
|
+
ManagedBy = "terraform"
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
`,
|
|
505
|
+
'terraform/outputs.tf': `output "vpc_id" {
|
|
506
|
+
value = module.vpc.vpc_id
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
output "eks_cluster_endpoint" {
|
|
510
|
+
value = module.eks.cluster_endpoint
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
output "rds_endpoint" {
|
|
514
|
+
value = module.rds.db_instance_endpoint
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
output "redis_endpoint" {
|
|
518
|
+
value = aws_elasticache_cluster.main.cache_nodes[0].address
|
|
519
|
+
}
|
|
520
|
+
`,
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
minimal: {
|
|
524
|
+
description: 'Minimal Terraform setup with just provider configuration',
|
|
525
|
+
files: {
|
|
526
|
+
'terraform/main.tf': `# Minimal Terraform Configuration
|
|
527
|
+
# Generated by Nimbus
|
|
528
|
+
|
|
529
|
+
terraform {
|
|
530
|
+
required_version = ">= 1.0"
|
|
531
|
+
|
|
532
|
+
required_providers {
|
|
533
|
+
aws = {
|
|
534
|
+
source = "hashicorp/aws"
|
|
535
|
+
version = "~> 5.0"
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
# Uncomment to use S3 backend
|
|
540
|
+
# backend "s3" {
|
|
541
|
+
# bucket = "your-terraform-state-bucket"
|
|
542
|
+
# key = "terraform.tfstate"
|
|
543
|
+
# region = "us-east-1"
|
|
544
|
+
# }
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
provider "aws" {
|
|
548
|
+
region = var.aws_region
|
|
549
|
+
|
|
550
|
+
default_tags {
|
|
551
|
+
tags = {
|
|
552
|
+
ManagedBy = "terraform"
|
|
553
|
+
Project = var.project_name
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
`,
|
|
558
|
+
'terraform/variables.tf': `variable "project_name" {
|
|
559
|
+
description = "Name of the project"
|
|
560
|
+
type = string
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
variable "aws_region" {
|
|
564
|
+
description = "AWS region"
|
|
565
|
+
type = string
|
|
566
|
+
default = "us-east-1"
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
variable "environment" {
|
|
570
|
+
description = "Environment (dev, staging, prod)"
|
|
571
|
+
type = string
|
|
572
|
+
default = "dev"
|
|
573
|
+
}
|
|
574
|
+
`,
|
|
575
|
+
'terraform/outputs.tf': `# Add outputs here as you create resources
|
|
576
|
+
`,
|
|
577
|
+
'terraform/.gitignore': `# Terraform
|
|
578
|
+
*.tfstate
|
|
579
|
+
*.tfstate.*
|
|
580
|
+
.terraform/
|
|
581
|
+
.terraform.lock.hcl
|
|
582
|
+
*.tfvars
|
|
583
|
+
!*.tfvars.example
|
|
584
|
+
crash.log
|
|
585
|
+
override.tf
|
|
586
|
+
override.tf.json
|
|
587
|
+
*_override.tf
|
|
588
|
+
*_override.tf.json
|
|
589
|
+
`,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Generate template files
|
|
596
|
+
*/
|
|
597
|
+
function generateTemplateFiles(templateName: string, outputDir: string, projectName: string): void {
|
|
598
|
+
const template = TEMPLATES[templateName];
|
|
599
|
+
if (!template) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
for (const [filePath, content] of Object.entries(template.files)) {
|
|
604
|
+
const fullPath = path.join(outputDir, filePath);
|
|
605
|
+
const dir = path.dirname(fullPath);
|
|
606
|
+
|
|
607
|
+
if (!fs.existsSync(dir)) {
|
|
608
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Replace project name placeholder
|
|
612
|
+
const finalContent = content.replace(/\$\{var\.project_name\}/g, projectName);
|
|
613
|
+
fs.writeFileSync(fullPath, finalContent);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Create .gitignore entry for Nimbus
|
|
619
|
+
*/
|
|
620
|
+
function createGitignoreEntry(): string {
|
|
621
|
+
return `
|
|
622
|
+
# Nimbus
|
|
623
|
+
.nimbus/
|
|
624
|
+
*.nimbus-session
|
|
625
|
+
`;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Display scan results in a summary table
|
|
630
|
+
*/
|
|
631
|
+
function displayScanSummary(context: ProjectContext): void {
|
|
632
|
+
ui.newLine();
|
|
633
|
+
ui.section('Project Analysis');
|
|
634
|
+
|
|
635
|
+
// Languages
|
|
636
|
+
if (context.structure.languages.length > 0) {
|
|
637
|
+
ui.print(` ${ui.bold('Languages:')}`);
|
|
638
|
+
for (const lang of context.structure.languages.slice(0, 5)) {
|
|
639
|
+
const version = lang.version ? ` (${lang.version})` : '';
|
|
640
|
+
const confidence =
|
|
641
|
+
lang.confidence === 'high' ? ui.color('●', 'green') : ui.color('○', 'yellow');
|
|
642
|
+
ui.print(` ${confidence} ${lang.name}${version}`);
|
|
643
|
+
}
|
|
644
|
+
if (context.structure.languages.length > 5) {
|
|
645
|
+
ui.print(ui.dim(` ... and ${context.structure.languages.length - 5} more`));
|
|
646
|
+
}
|
|
647
|
+
ui.newLine();
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Frameworks
|
|
651
|
+
if (context.structure.frameworks.length > 0) {
|
|
652
|
+
ui.print(` ${ui.bold('Frameworks:')}`);
|
|
653
|
+
for (const fw of context.structure.frameworks.slice(0, 5)) {
|
|
654
|
+
const version = fw.version ? ` (${fw.version})` : '';
|
|
655
|
+
const confidence =
|
|
656
|
+
fw.confidence === 'high' ? ui.color('●', 'green') : ui.color('○', 'yellow');
|
|
657
|
+
ui.print(` ${confidence} ${fw.name}${version}`);
|
|
658
|
+
}
|
|
659
|
+
if (context.structure.frameworks.length > 5) {
|
|
660
|
+
ui.print(ui.dim(` ... and ${context.structure.frameworks.length - 5} more`));
|
|
661
|
+
}
|
|
662
|
+
ui.newLine();
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Package Managers
|
|
666
|
+
if (context.structure.packageManagers.length > 0) {
|
|
667
|
+
ui.print(` ${ui.bold('Package Managers:')}`);
|
|
668
|
+
const pmList = context.structure.packageManagers.map(pm => pm.name).join(', ');
|
|
669
|
+
ui.print(` ${pmList}`);
|
|
670
|
+
ui.newLine();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Infrastructure
|
|
674
|
+
const hasInfra =
|
|
675
|
+
context.files.terraform.length > 0 ||
|
|
676
|
+
context.files.kubernetes.length > 0 ||
|
|
677
|
+
context.files.docker.length > 0;
|
|
678
|
+
if (hasInfra) {
|
|
679
|
+
ui.print(` ${ui.bold('Infrastructure:')}`);
|
|
680
|
+
if (context.files.terraform.length > 0) {
|
|
681
|
+
ui.print(` ${ui.color('●', 'green')} Terraform (${context.files.terraform.length} files)`);
|
|
682
|
+
}
|
|
683
|
+
if (context.files.kubernetes.length > 0) {
|
|
684
|
+
ui.print(
|
|
685
|
+
` ${ui.color('●', 'green')} Kubernetes (${context.files.kubernetes.length} files)`
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
if (context.files.docker.length > 0) {
|
|
689
|
+
ui.print(` ${ui.color('●', 'green')} Docker (${context.files.docker.length} files)`);
|
|
690
|
+
}
|
|
691
|
+
ui.newLine();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// CI/CD
|
|
695
|
+
if (context.cicd.platform) {
|
|
696
|
+
ui.print(` ${ui.bold('CI/CD:')}`);
|
|
697
|
+
ui.print(
|
|
698
|
+
` ${ui.color('●', 'green')} ${context.cicd.platform} (${context.cicd.workflows.length} workflows)`
|
|
699
|
+
);
|
|
700
|
+
ui.newLine();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Cloud Providers
|
|
704
|
+
if (context.cloud.providers.length > 0) {
|
|
705
|
+
ui.print(` ${ui.bold('Cloud Providers:')}`);
|
|
706
|
+
for (const provider of context.cloud.providers) {
|
|
707
|
+
ui.print(` ${ui.color('●', 'green')} ${provider.toUpperCase()}`);
|
|
708
|
+
}
|
|
709
|
+
if (context.cloud.regions.length > 0) {
|
|
710
|
+
ui.print(` ${ui.dim('Regions:')} ${context.cloud.regions.slice(0, 5).join(', ')}`);
|
|
711
|
+
}
|
|
712
|
+
ui.newLine();
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Git Info
|
|
716
|
+
if (context.git.isRepo) {
|
|
717
|
+
ui.print(` ${ui.bold('Git:')}`);
|
|
718
|
+
ui.print(` Branch: ${context.git.branch}`);
|
|
719
|
+
if (context.git.remote) {
|
|
720
|
+
ui.print(` Remote: ${context.git.remote}`);
|
|
721
|
+
}
|
|
722
|
+
ui.newLine();
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Project Type
|
|
726
|
+
ui.print(` ${ui.bold('Project Type:')} ${context.structure.type}`);
|
|
727
|
+
ui.newLine();
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Create workspace config.yaml
|
|
732
|
+
*/
|
|
733
|
+
function createWorkspaceConfig(options: {
|
|
734
|
+
name: string;
|
|
735
|
+
provider?: string;
|
|
736
|
+
output?: string;
|
|
737
|
+
}): string {
|
|
738
|
+
const lines: string[] = [
|
|
739
|
+
'# Nimbus Workspace Configuration',
|
|
740
|
+
'# This file configures Nimbus for this project.',
|
|
741
|
+
'# See project.yaml for detected project context.',
|
|
742
|
+
'',
|
|
743
|
+
'workspace:',
|
|
744
|
+
` name: ${options.name}`,
|
|
745
|
+
];
|
|
746
|
+
|
|
747
|
+
if (options.provider) {
|
|
748
|
+
lines.push(` defaultProvider: ${options.provider}`);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (options.output) {
|
|
752
|
+
lines.push(` outputDirectory: ${options.output}`);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
lines.push('');
|
|
756
|
+
lines.push('# Safety settings');
|
|
757
|
+
lines.push('safety:');
|
|
758
|
+
lines.push(' requireApproval: true');
|
|
759
|
+
lines.push(' protectedEnvironments: [production, prod]');
|
|
760
|
+
lines.push(' costThreshold: 500');
|
|
761
|
+
|
|
762
|
+
return `${lines.join('\n')}\n`;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Init command handler
|
|
767
|
+
*/
|
|
768
|
+
export async function initCommand(options: InitOptions = {}): Promise<void> {
|
|
769
|
+
const cwd = process.cwd();
|
|
770
|
+
const nimbusDir = path.join(cwd, NIMBUS_DIR);
|
|
771
|
+
const projectPath = path.join(nimbusDir, PROJECT_FILE);
|
|
772
|
+
const configPath = path.join(nimbusDir, CONFIG_FILE);
|
|
773
|
+
|
|
774
|
+
// Check if already initialized
|
|
775
|
+
if (fs.existsSync(nimbusDir) && !options.force) {
|
|
776
|
+
ui.warning(`Nimbus workspace already exists at: ${nimbusDir}`);
|
|
777
|
+
|
|
778
|
+
if (!options.nonInteractive) {
|
|
779
|
+
const reinit = await confirm({
|
|
780
|
+
message: 'Reinitialize this workspace?',
|
|
781
|
+
defaultValue: false,
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
if (!reinit) {
|
|
785
|
+
ui.info('Workspace unchanged.');
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
} else {
|
|
789
|
+
ui.info('Use --force to reinitialize.');
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Start scanning
|
|
795
|
+
ui.newLine();
|
|
796
|
+
ui.header('Initialize Nimbus Workspace', cwd);
|
|
797
|
+
|
|
798
|
+
const scanDepth = options.scanDepth || 'standard';
|
|
799
|
+
ui.startSpinner({ message: `Scanning project (${scanDepth} mode)...` });
|
|
800
|
+
|
|
801
|
+
// Create scanner and run scan
|
|
802
|
+
const scanner = createProjectScanner();
|
|
803
|
+
const scanOptions: Partial<ScanOptions> = {
|
|
804
|
+
depth: scanDepth,
|
|
805
|
+
instructions: options.instructions,
|
|
806
|
+
maxDepth: options.maxDepth,
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
let context: ProjectContext;
|
|
810
|
+
try {
|
|
811
|
+
context = await scanner.scan(cwd, scanOptions);
|
|
812
|
+
ui.stopSpinnerSuccess('Project scan complete');
|
|
813
|
+
} catch (error) {
|
|
814
|
+
ui.stopSpinnerFail('Project scan failed');
|
|
815
|
+
ui.error((error as Error).message);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Display scan summary
|
|
820
|
+
displayScanSummary(context);
|
|
821
|
+
|
|
822
|
+
// Interactive configuration
|
|
823
|
+
let projectName = options.name || context.project.name;
|
|
824
|
+
let provider = options.provider;
|
|
825
|
+
let outputDir = options.output;
|
|
826
|
+
|
|
827
|
+
if (!options.nonInteractive) {
|
|
828
|
+
// Project name
|
|
829
|
+
projectName = await input({
|
|
830
|
+
message: 'Project name:',
|
|
831
|
+
defaultValue: context.project.name,
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
// Cloud provider - pre-select if detected
|
|
835
|
+
const detectedProvider = context.cloud.providers[0] || '';
|
|
836
|
+
const providerOptions = [
|
|
837
|
+
{ label: 'AWS', value: 'aws', description: 'Amazon Web Services' },
|
|
838
|
+
{ label: 'GCP', value: 'gcp', description: 'Google Cloud Platform' },
|
|
839
|
+
{ label: 'Azure', value: 'azure', description: 'Microsoft Azure' },
|
|
840
|
+
{ label: 'None', value: '', description: 'No default provider' },
|
|
841
|
+
];
|
|
842
|
+
|
|
843
|
+
// Move detected provider to top
|
|
844
|
+
if (detectedProvider) {
|
|
845
|
+
const idx = providerOptions.findIndex(o => o.value === detectedProvider);
|
|
846
|
+
if (idx > 0) {
|
|
847
|
+
const [detected] = providerOptions.splice(idx, 1);
|
|
848
|
+
detected.label += ' (detected)';
|
|
849
|
+
providerOptions.unshift(detected);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
provider = (await select({
|
|
854
|
+
message: 'Default cloud provider:',
|
|
855
|
+
options: providerOptions,
|
|
856
|
+
})) as string;
|
|
857
|
+
|
|
858
|
+
// Output directory
|
|
859
|
+
const defaultOutput = context.files.terraform.length > 0 ? './terraform' : './infrastructure';
|
|
860
|
+
outputDir = await input({
|
|
861
|
+
message: 'Output directory for generated code:',
|
|
862
|
+
defaultValue: defaultOutput,
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
// Update .gitignore
|
|
866
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
867
|
+
if (fs.existsSync(gitignorePath)) {
|
|
868
|
+
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf-8');
|
|
869
|
+
if (!gitignoreContent.includes('.nimbus/')) {
|
|
870
|
+
const updateGitignore = await confirm({
|
|
871
|
+
message: 'Add .nimbus/ to .gitignore?',
|
|
872
|
+
defaultValue: true,
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
if (updateGitignore) {
|
|
876
|
+
fs.appendFileSync(gitignorePath, createGitignoreEntry());
|
|
877
|
+
ui.success('Updated .gitignore');
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
} else {
|
|
882
|
+
// Non-interactive: use detected or default values
|
|
883
|
+
provider = provider || context.cloud.providers[0] || '';
|
|
884
|
+
outputDir =
|
|
885
|
+
outputDir || (context.files.terraform.length > 0 ? './terraform' : './infrastructure');
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Update context with user-provided name
|
|
889
|
+
context.project.name = projectName;
|
|
890
|
+
|
|
891
|
+
// Create .nimbus directory
|
|
892
|
+
if (!fs.existsSync(nimbusDir)) {
|
|
893
|
+
fs.mkdirSync(nimbusDir, { recursive: true });
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Generate and write project.yaml
|
|
897
|
+
const projectYaml = generateProjectYaml(context);
|
|
898
|
+
fs.writeFileSync(projectPath, projectYaml);
|
|
899
|
+
|
|
900
|
+
// Generate and write config.yaml
|
|
901
|
+
const configContent = createWorkspaceConfig({
|
|
902
|
+
name: projectName,
|
|
903
|
+
provider: provider || undefined,
|
|
904
|
+
output: outputDir || undefined,
|
|
905
|
+
});
|
|
906
|
+
fs.writeFileSync(configPath, configContent);
|
|
907
|
+
|
|
908
|
+
// Create .gitkeep for empty directories
|
|
909
|
+
const gitkeepPath = path.join(nimbusDir, '.gitkeep');
|
|
910
|
+
if (!fs.existsSync(gitkeepPath)) {
|
|
911
|
+
fs.writeFileSync(gitkeepPath, '');
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// Initialize context database
|
|
915
|
+
try {
|
|
916
|
+
const contextDb = initContextDatabase(cwd);
|
|
917
|
+
contextDb.recordCommand('init', `--name ${projectName}`, 'success');
|
|
918
|
+
contextDb.close();
|
|
919
|
+
} catch {
|
|
920
|
+
// Non-critical: context DB is optional
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Generate template files if specified
|
|
924
|
+
if (options.template) {
|
|
925
|
+
const template = TEMPLATES[options.template];
|
|
926
|
+
if (template) {
|
|
927
|
+
ui.startSpinner({ message: `Generating ${options.template} template...` });
|
|
928
|
+
generateTemplateFiles(options.template, cwd, projectName);
|
|
929
|
+
ui.stopSpinnerSuccess(`Generated ${options.template} template`);
|
|
930
|
+
}
|
|
931
|
+
} else if (!options.nonInteractive && context.files.terraform.length === 0) {
|
|
932
|
+
// Offer to generate a template if no existing Terraform
|
|
933
|
+
const useTemplate = await confirm({
|
|
934
|
+
message: 'No existing Terraform files found. Generate a starter template?',
|
|
935
|
+
defaultValue: true,
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
if (useTemplate) {
|
|
939
|
+
const templateChoice = await select({
|
|
940
|
+
message: 'Select a template:',
|
|
941
|
+
options: [
|
|
942
|
+
{ label: 'Minimal', value: 'minimal', description: 'Basic provider setup' },
|
|
943
|
+
{ label: 'VPC', value: 'vpc', description: 'AWS VPC with subnets and NAT' },
|
|
944
|
+
{ label: 'EKS', value: 'eks', description: 'EKS cluster with VPC' },
|
|
945
|
+
{ label: 'Full Stack', value: 'full-stack', description: 'VPC + EKS + RDS + Redis' },
|
|
946
|
+
],
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
ui.startSpinner({ message: `Generating ${templateChoice} template...` });
|
|
950
|
+
generateTemplateFiles(templateChoice as string, cwd, projectName);
|
|
951
|
+
ui.stopSpinnerSuccess(`Generated ${templateChoice} template`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// Prompt for telemetry opt-in
|
|
956
|
+
if (!options.nonInteractive) {
|
|
957
|
+
ui.newLine();
|
|
958
|
+
const enableTelemetry = await confirm({
|
|
959
|
+
message: 'Enable anonymous usage telemetry? (helps improve Nimbus)',
|
|
960
|
+
defaultValue: false,
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
if (enableTelemetry) {
|
|
964
|
+
try {
|
|
965
|
+
const { homedir } = await import('os');
|
|
966
|
+
const telemetryConfigPath = path.join(homedir(), '.nimbus', 'config.json');
|
|
967
|
+
const telemetryDir = path.join(homedir(), '.nimbus');
|
|
968
|
+
|
|
969
|
+
if (!fs.existsSync(telemetryDir)) {
|
|
970
|
+
fs.mkdirSync(telemetryDir, { recursive: true });
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
let telemetryConfig: any = {};
|
|
974
|
+
try {
|
|
975
|
+
if (fs.existsSync(telemetryConfigPath)) {
|
|
976
|
+
telemetryConfig = JSON.parse(fs.readFileSync(telemetryConfigPath, 'utf-8'));
|
|
977
|
+
}
|
|
978
|
+
} catch {
|
|
979
|
+
/* ignore */
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
const { randomUUID } = await import('crypto');
|
|
983
|
+
telemetryConfig.telemetry = {
|
|
984
|
+
...telemetryConfig.telemetry,
|
|
985
|
+
enabled: true,
|
|
986
|
+
anonymousId: telemetryConfig.telemetry?.anonymousId || randomUUID(),
|
|
987
|
+
};
|
|
988
|
+
fs.writeFileSync(telemetryConfigPath, JSON.stringify(telemetryConfig, null, 2));
|
|
989
|
+
ui.success('Telemetry enabled');
|
|
990
|
+
} catch {
|
|
991
|
+
// Non-critical
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
ui.newLine();
|
|
997
|
+
ui.success(`Nimbus workspace initialized!`);
|
|
998
|
+
ui.newLine();
|
|
999
|
+
ui.print(` ${ui.dim('Project:')} ${projectName}`);
|
|
1000
|
+
ui.print(` ${ui.dim('Type:')} ${context.structure.type}`);
|
|
1001
|
+
ui.print(` ${ui.dim('Config:')} ${configPath}`);
|
|
1002
|
+
ui.print(` ${ui.dim('Context:')} ${projectPath}`);
|
|
1003
|
+
if (provider) {
|
|
1004
|
+
ui.print(` ${ui.dim('Provider:')} ${provider}`);
|
|
1005
|
+
}
|
|
1006
|
+
if (outputDir) {
|
|
1007
|
+
ui.print(` ${ui.dim('Output:')} ${outputDir}`);
|
|
1008
|
+
}
|
|
1009
|
+
ui.newLine();
|
|
1010
|
+
|
|
1011
|
+
// Show detected summary
|
|
1012
|
+
const summaryItems: string[] = [];
|
|
1013
|
+
if (context.structure.languages.length > 0) {
|
|
1014
|
+
summaryItems.push(`${context.structure.languages.length} languages`);
|
|
1015
|
+
}
|
|
1016
|
+
if (context.structure.frameworks.length > 0) {
|
|
1017
|
+
summaryItems.push(`${context.structure.frameworks.length} frameworks`);
|
|
1018
|
+
}
|
|
1019
|
+
if (context.files.terraform.length > 0) {
|
|
1020
|
+
summaryItems.push('Terraform');
|
|
1021
|
+
}
|
|
1022
|
+
if (context.files.kubernetes.length > 0) {
|
|
1023
|
+
summaryItems.push('Kubernetes');
|
|
1024
|
+
}
|
|
1025
|
+
if (context.cicd.platform) {
|
|
1026
|
+
summaryItems.push(context.cicd.platform);
|
|
1027
|
+
}
|
|
1028
|
+
if (summaryItems.length > 0) {
|
|
1029
|
+
ui.print(ui.dim(` Detected: ${summaryItems.join(', ')}`));
|
|
1030
|
+
ui.newLine();
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
ui.print(ui.dim('Next steps:'));
|
|
1034
|
+
ui.print(` ${ui.dim('1.')} Run ${ui.color('nimbus login', 'cyan')} to configure authentication`);
|
|
1035
|
+
ui.print(` ${ui.dim('2.')} Run ${ui.color('nimbus chat', 'cyan')} to start a conversation`);
|
|
1036
|
+
if (context.files.terraform.length > 0) {
|
|
1037
|
+
ui.print(
|
|
1038
|
+
` ${ui.dim('3.')} Run ${ui.color('nimbus plan terraform', 'cyan')} to preview infrastructure`
|
|
1039
|
+
);
|
|
1040
|
+
} else {
|
|
1041
|
+
ui.print(
|
|
1042
|
+
` ${ui.dim('3.')} Run ${ui.color('nimbus generate terraform', 'cyan')} to generate infrastructure`
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
ui.newLine();
|
|
1046
|
+
}
|