@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.
Files changed (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,539 @@
1
+ /**
2
+ * Project Scanners
3
+ *
4
+ * Orchestrates all project scanners to build a complete project context
5
+ */
6
+
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import { execSync } from 'child_process';
10
+ import type {
11
+ ProjectContext,
12
+ ProjectType,
13
+ ScanOptions,
14
+ AggregateScanResult,
15
+ LanguageInfo,
16
+ FrameworkInfo,
17
+ PackageManagerInfo,
18
+ IaCInfo,
19
+ CICDInfo,
20
+ CloudInfo,
21
+ GitInfo,
22
+ } from './types';
23
+
24
+ import { createLanguageScanner, type LanguageScanner } from './language-scanner';
25
+ import { createFrameworkScanner, type FrameworkScanner } from './framework-scanner';
26
+ import { createPackageManagerScanner, type PackageManagerScanner } from './package-manager-scanner';
27
+ import { createIaCScanner, type IaCScanner } from './iac-scanner';
28
+ import { createCICDScanner, type CICDScanner } from './cicd-scanner';
29
+ import { createCloudScanner, type CloudScanner } from './cloud-scanner';
30
+
31
+ // Re-export types
32
+ export * from './types';
33
+
34
+ // Re-export scanners
35
+ export { LanguageScanner, createLanguageScanner } from './language-scanner';
36
+ export { FrameworkScanner, createFrameworkScanner } from './framework-scanner';
37
+ export { PackageManagerScanner, createPackageManagerScanner } from './package-manager-scanner';
38
+ export { IaCScanner, createIaCScanner } from './iac-scanner';
39
+ export { CICDScanner, createCICDScanner } from './cicd-scanner';
40
+ export { CloudScanner, createCloudScanner } from './cloud-scanner';
41
+
42
+ /**
43
+ * Default scan options
44
+ */
45
+ const DEFAULT_SCAN_OPTIONS: ScanOptions = {
46
+ depth: 'standard',
47
+ maxFiles: 1000,
48
+ includeHidden: false,
49
+ instructions: '',
50
+ maxDepth: 10,
51
+ };
52
+
53
+ /**
54
+ * Project Scanner Orchestrator
55
+ *
56
+ * Coordinates all individual scanners to build a complete project context
57
+ */
58
+ export class ProjectScanner {
59
+ private languageScanner: LanguageScanner;
60
+ private frameworkScanner: FrameworkScanner;
61
+ private packageManagerScanner: PackageManagerScanner;
62
+ private iacScanner: IaCScanner;
63
+ private cicdScanner: CICDScanner;
64
+ private cloudScanner: CloudScanner;
65
+
66
+ constructor() {
67
+ this.languageScanner = createLanguageScanner();
68
+ this.frameworkScanner = createFrameworkScanner();
69
+ this.packageManagerScanner = createPackageManagerScanner();
70
+ this.iacScanner = createIaCScanner();
71
+ this.cicdScanner = createCICDScanner();
72
+ this.cloudScanner = createCloudScanner();
73
+ }
74
+
75
+ /**
76
+ * Scan a project directory and return complete context
77
+ */
78
+ async scan(cwd: string, options: Partial<ScanOptions> = {}): Promise<ProjectContext> {
79
+ const opts = { ...DEFAULT_SCAN_OPTIONS, ...options };
80
+
81
+ // Run all scanners in parallel
82
+ const [
83
+ languageResult,
84
+ frameworkResult,
85
+ packageManagerResult,
86
+ iacResult,
87
+ cicdResult,
88
+ cloudResult,
89
+ gitInfo,
90
+ ] = await Promise.all([
91
+ this.languageScanner.scan(cwd, opts),
92
+ this.frameworkScanner.scan(cwd, opts),
93
+ this.packageManagerScanner.scan(cwd, opts),
94
+ this.iacScanner.scan(cwd, opts),
95
+ this.cicdScanner.scan(cwd, opts),
96
+ this.cloudScanner.scan(cwd, opts),
97
+ this.getGitInfo(cwd),
98
+ ]);
99
+
100
+ // Extract detailed results
101
+ const languages = languageResult.details.languages as LanguageInfo[];
102
+ const frameworks = frameworkResult.details.frameworks as FrameworkInfo[];
103
+ const packageManagers = packageManagerResult.details.packageManagers as PackageManagerInfo[];
104
+ const iac = iacResult.details.iac as IaCInfo[];
105
+ const cicd = cicdResult.details.cicd as CICDInfo[];
106
+ const cloud = cloudResult.details.cloud as CloudInfo[];
107
+
108
+ // Get file lists
109
+ const terraformFiles = await this.iacScanner.getTerraformFiles(cwd);
110
+ const kubernetesFiles = await this.iacScanner.getKubernetesFiles(cwd);
111
+ const dockerFiles = await this.iacScanner.getDockerFiles(cwd);
112
+ const cicdFiles = await this.cicdScanner.getCICDFiles(cwd);
113
+
114
+ // Determine project type
115
+ const projectType = this.determineProjectType({
116
+ languages,
117
+ frameworks,
118
+ packageManagers,
119
+ iac,
120
+ cicd,
121
+ cloud,
122
+ git: gitInfo,
123
+ projectType: 'unknown',
124
+ });
125
+
126
+ // Get primary CI/CD platform
127
+ const primaryCICD = await this.cicdScanner.getPrimaryCICDPlatform(cwd);
128
+
129
+ // Build project context
130
+ const context: ProjectContext = {
131
+ project: {
132
+ name: this.getProjectName(cwd),
133
+ path: cwd,
134
+ detected_at: new Date().toISOString(),
135
+ },
136
+ structure: {
137
+ type: projectType,
138
+ languages: languages.map(l => ({
139
+ name: l.name,
140
+ version: l.version,
141
+ confidence: l.confidence,
142
+ files: l.files,
143
+ })),
144
+ frameworks: frameworks.map(f => ({
145
+ name: f.name,
146
+ version: f.version,
147
+ confidence: f.confidence,
148
+ language: f.language,
149
+ })),
150
+ packageManagers: packageManagers.map(pm => ({
151
+ name: pm.name,
152
+ lockFile: pm.lockFile,
153
+ confidence: pm.confidence,
154
+ })),
155
+ },
156
+ files: {
157
+ terraform: terraformFiles,
158
+ kubernetes: kubernetesFiles,
159
+ docker: dockerFiles,
160
+ cicd: cicdFiles,
161
+ },
162
+ git: gitInfo,
163
+ cicd: {
164
+ platform: primaryCICD,
165
+ workflows: cicdFiles,
166
+ },
167
+ cloud: {
168
+ providers: cloud.map(c => c.provider),
169
+ regions: [...new Set(cloud.flatMap(c => c.regions))],
170
+ },
171
+ instructions: opts.instructions || '',
172
+ };
173
+
174
+ return context;
175
+ }
176
+
177
+ /**
178
+ * Run a quick scan (config files only)
179
+ */
180
+ async quickScan(cwd: string): Promise<AggregateScanResult> {
181
+ const [
182
+ languageResult,
183
+ frameworkResult,
184
+ packageManagerResult,
185
+ iacResult,
186
+ cicdResult,
187
+ cloudResult,
188
+ gitInfo,
189
+ ] = await Promise.all([
190
+ this.languageScanner.scan(cwd),
191
+ this.frameworkScanner.scan(cwd),
192
+ this.packageManagerScanner.scan(cwd),
193
+ this.iacScanner.scan(cwd),
194
+ this.cicdScanner.scan(cwd),
195
+ this.cloudScanner.scan(cwd),
196
+ this.getGitInfo(cwd),
197
+ ]);
198
+
199
+ const result: AggregateScanResult = {
200
+ languages: languageResult.details.languages as LanguageInfo[],
201
+ frameworks: frameworkResult.details.frameworks as FrameworkInfo[],
202
+ packageManagers: packageManagerResult.details.packageManagers as PackageManagerInfo[],
203
+ iac: iacResult.details.iac as IaCInfo[],
204
+ cicd: cicdResult.details.cicd as CICDInfo[],
205
+ cloud: cloudResult.details.cloud as CloudInfo[],
206
+ git: gitInfo,
207
+ projectType: 'unknown',
208
+ };
209
+
210
+ result.projectType = this.determineProjectType(result);
211
+
212
+ return result;
213
+ }
214
+
215
+ /**
216
+ * Get git repository information
217
+ */
218
+ private async getGitInfo(cwd: string): Promise<GitInfo> {
219
+ const gitDir = path.join(cwd, '.git');
220
+ const isRepo = fs.existsSync(gitDir);
221
+
222
+ if (!isRepo) {
223
+ return {
224
+ isRepo: false,
225
+ remote: null,
226
+ branch: '',
227
+ hasUncommittedChanges: false,
228
+ };
229
+ }
230
+
231
+ let remote: string | null = null;
232
+ let branch = '';
233
+ let hasUncommittedChanges = false;
234
+
235
+ try {
236
+ // Get remote URL
237
+ remote = execSync('git remote get-url origin', { cwd, encoding: 'utf-8' }).trim();
238
+ } catch {
239
+ remote = null;
240
+ }
241
+
242
+ try {
243
+ // Get current branch
244
+ branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd, encoding: 'utf-8' }).trim();
245
+ } catch {
246
+ branch = 'unknown';
247
+ }
248
+
249
+ try {
250
+ // Check for uncommitted changes
251
+ const status = execSync('git status --porcelain', { cwd, encoding: 'utf-8' }).trim();
252
+ hasUncommittedChanges = status.length > 0;
253
+ } catch {
254
+ hasUncommittedChanges = false;
255
+ }
256
+
257
+ return {
258
+ isRepo,
259
+ remote,
260
+ branch,
261
+ hasUncommittedChanges,
262
+ };
263
+ }
264
+
265
+ /**
266
+ * Get project name from package.json or directory name
267
+ */
268
+ private getProjectName(cwd: string): string {
269
+ // Try package.json
270
+ const packageJsonPath = path.join(cwd, 'package.json');
271
+ if (fs.existsSync(packageJsonPath)) {
272
+ try {
273
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
274
+ if (packageJson.name) {
275
+ return packageJson.name;
276
+ }
277
+ } catch {
278
+ // Ignore parse errors
279
+ }
280
+ }
281
+
282
+ // Try pyproject.toml
283
+ const pyprojectPath = path.join(cwd, 'pyproject.toml');
284
+ if (fs.existsSync(pyprojectPath)) {
285
+ try {
286
+ const content = fs.readFileSync(pyprojectPath, 'utf-8');
287
+ const match = content.match(/name\s*=\s*["']([^"']+)["']/);
288
+ if (match) {
289
+ return match[1];
290
+ }
291
+ } catch {
292
+ // Ignore parse errors
293
+ }
294
+ }
295
+
296
+ // Try Cargo.toml
297
+ const cargoPath = path.join(cwd, 'Cargo.toml');
298
+ if (fs.existsSync(cargoPath)) {
299
+ try {
300
+ const content = fs.readFileSync(cargoPath, 'utf-8');
301
+ const match = content.match(/name\s*=\s*["']([^"']+)["']/);
302
+ if (match) {
303
+ return match[1];
304
+ }
305
+ } catch {
306
+ // Ignore parse errors
307
+ }
308
+ }
309
+
310
+ // Fall back to directory name
311
+ return path.basename(cwd);
312
+ }
313
+
314
+ /**
315
+ * Determine project type based on scan results
316
+ */
317
+ private determineProjectType(result: AggregateScanResult): ProjectType {
318
+ const { languages, frameworks, iac, cloud: _cloud } = result;
319
+
320
+ // Check for monorepo indicators
321
+ const isMonorepo = this.isMonorepo(languages, frameworks);
322
+ if (isMonorepo) {
323
+ return 'monorepo';
324
+ }
325
+
326
+ // Check for infrastructure-only project
327
+ if (iac.length > 0 && languages.length === 0) {
328
+ return 'infrastructure';
329
+ }
330
+
331
+ // Check for mobile frameworks
332
+ const mobileFrameworks = ['react-native', 'flutter', 'expo'];
333
+ if (frameworks.some(f => mobileFrameworks.includes(f.name))) {
334
+ return 'mobile';
335
+ }
336
+
337
+ // Check for CLI indicators
338
+ const cliIndicators = ['commander', 'yargs', 'oclif', 'clap', 'cobra', 'click', 'typer'];
339
+ if (frameworks.some(f => cliIndicators.includes(f.name))) {
340
+ return 'cli';
341
+ }
342
+
343
+ // Check for fullstack
344
+ const frontendFrameworks = [
345
+ 'next.js',
346
+ 'nuxt',
347
+ 'remix',
348
+ 'angular',
349
+ 'vue',
350
+ 'react',
351
+ 'svelte',
352
+ 'astro',
353
+ ];
354
+ const backendFrameworks = [
355
+ 'express',
356
+ 'fastify',
357
+ 'nestjs',
358
+ 'django',
359
+ 'fastapi',
360
+ 'flask',
361
+ 'spring-boot',
362
+ 'gin',
363
+ 'actix-web',
364
+ ];
365
+
366
+ const hasFrontend = frameworks.some(f => frontendFrameworks.includes(f.name));
367
+ const hasBackend = frameworks.some(f => backendFrameworks.includes(f.name));
368
+
369
+ if (hasFrontend && hasBackend) {
370
+ return 'fullstack';
371
+ }
372
+
373
+ if (hasFrontend) {
374
+ return 'frontend';
375
+ }
376
+
377
+ if (hasBackend) {
378
+ return 'backend';
379
+ }
380
+
381
+ // Check for library indicators
382
+ if (this.isLibrary(languages)) {
383
+ return 'library';
384
+ }
385
+
386
+ return 'unknown';
387
+ }
388
+
389
+ /**
390
+ * Check if project is a monorepo
391
+ */
392
+ private isMonorepo(languages: LanguageInfo[], _frameworks: FrameworkInfo[]): boolean {
393
+ // Check for monorepo tools
394
+ const _monorepoTools = ['lerna', 'nx', 'turborepo', 'rush', 'pnpm-workspace'];
395
+ // This would need to be detected from package.json or config files
396
+ // For now, check if multiple languages with high confidence
397
+ const highConfidenceLanguages = languages.filter(l => l.confidence === 'high');
398
+ return highConfidenceLanguages.length >= 3;
399
+ }
400
+
401
+ /**
402
+ * Check if project is a library
403
+ */
404
+ private isLibrary(languages: LanguageInfo[]): boolean {
405
+ // Libraries typically have specific config files
406
+ // This is a simplified heuristic
407
+ return languages.some(l =>
408
+ l.files.some(
409
+ f => f.includes('.gemspec') || f.includes('setup.py') || f.includes('Cargo.toml')
410
+ )
411
+ );
412
+ }
413
+ }
414
+
415
+ /**
416
+ * Generate project.yaml content from project context
417
+ */
418
+ export function generateProjectYaml(context: ProjectContext): string {
419
+ const lines: string[] = [];
420
+
421
+ // Project section
422
+ lines.push('project:');
423
+ lines.push(` name: ${context.project.name}`);
424
+ lines.push(` detected_at: ${context.project.detected_at}`);
425
+ lines.push('');
426
+
427
+ // Structure section
428
+ lines.push('structure:');
429
+ lines.push(` type: ${context.structure.type}`);
430
+
431
+ if (context.structure.languages.length > 0) {
432
+ lines.push(' languages:');
433
+ for (const lang of context.structure.languages) {
434
+ if (lang.version) {
435
+ lines.push(` - name: ${lang.name}`);
436
+ lines.push(` version: "${lang.version}"`);
437
+ } else {
438
+ lines.push(` - name: ${lang.name}`);
439
+ }
440
+ }
441
+ }
442
+
443
+ if (context.structure.frameworks.length > 0) {
444
+ lines.push(' frameworks:');
445
+ for (const fw of context.structure.frameworks) {
446
+ lines.push(` - name: ${fw.name}`);
447
+ if (fw.version) {
448
+ lines.push(` version: "${fw.version}"`);
449
+ }
450
+ }
451
+ }
452
+
453
+ if (context.structure.packageManagers.length > 0) {
454
+ lines.push(
455
+ ` packageManagers: [${context.structure.packageManagers.map(pm => pm.name).join(', ')}]`
456
+ );
457
+ }
458
+
459
+ lines.push('');
460
+
461
+ // Files section
462
+ lines.push('files:');
463
+ if (context.files.terraform.length > 0) {
464
+ lines.push(` terraform: ["${context.files.terraform.slice(0, 10).join('", "')}"]`);
465
+ } else {
466
+ lines.push(' terraform: []');
467
+ }
468
+
469
+ if (context.files.kubernetes.length > 0) {
470
+ lines.push(` kubernetes: ["${context.files.kubernetes.slice(0, 10).join('", "')}"]`);
471
+ } else {
472
+ lines.push(' kubernetes: []');
473
+ }
474
+
475
+ if (context.files.docker.length > 0) {
476
+ lines.push(` docker: ["${context.files.docker.slice(0, 10).join('", "')}"]`);
477
+ } else {
478
+ lines.push(' docker: []');
479
+ }
480
+
481
+ if (context.files.cicd.length > 0) {
482
+ lines.push(` cicd: ["${context.files.cicd.slice(0, 10).join('", "')}"]`);
483
+ } else {
484
+ lines.push(' cicd: []');
485
+ }
486
+
487
+ lines.push('');
488
+
489
+ // Git section
490
+ lines.push('git:');
491
+ lines.push(` remote: ${context.git.remote || 'null'}`);
492
+ lines.push(` branch: ${context.git.branch}`);
493
+ lines.push(` isRepo: ${context.git.isRepo}`);
494
+ lines.push('');
495
+
496
+ // CI/CD section
497
+ lines.push('cicd:');
498
+ lines.push(` platform: ${context.cicd.platform || 'null'}`);
499
+ if (context.cicd.workflows.length > 0) {
500
+ lines.push(` workflows: ["${context.cicd.workflows.slice(0, 10).join('", "')}"]`);
501
+ } else {
502
+ lines.push(' workflows: []');
503
+ }
504
+
505
+ lines.push('');
506
+
507
+ // Cloud section
508
+ lines.push('cloud:');
509
+ if (context.cloud.providers.length > 0) {
510
+ lines.push(` providers: [${context.cloud.providers.join(', ')}]`);
511
+ } else {
512
+ lines.push(' providers: []');
513
+ }
514
+
515
+ if (context.cloud.regions.length > 0) {
516
+ lines.push(` regions: [${context.cloud.regions.join(', ')}]`);
517
+ } else {
518
+ lines.push(' regions: []');
519
+ }
520
+
521
+ // Instructions section (if provided)
522
+ if (context.instructions) {
523
+ lines.push('');
524
+ lines.push('# Custom project instructions');
525
+ lines.push(`instructions: |`);
526
+ for (const line of context.instructions.split('\n')) {
527
+ lines.push(` ${line}`);
528
+ }
529
+ }
530
+
531
+ return `${lines.join('\n')}\n`;
532
+ }
533
+
534
+ /**
535
+ * Create project scanner instance
536
+ */
537
+ export function createProjectScanner(): ProjectScanner {
538
+ return new ProjectScanner();
539
+ }