create-qa-architect 5.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 (67) hide show
  1. package/.editorconfig +12 -0
  2. package/.github/CLAUDE_MD_AUTOMATION.md +248 -0
  3. package/.github/PROGRESSIVE_QUALITY_IMPLEMENTATION.md +408 -0
  4. package/.github/PROGRESSIVE_QUALITY_PROPOSAL.md +443 -0
  5. package/.github/RELEASE_CHECKLIST.md +100 -0
  6. package/.github/dependabot.yml +50 -0
  7. package/.github/git-sync.sh +48 -0
  8. package/.github/workflows/claude-md-validation.yml +82 -0
  9. package/.github/workflows/nightly-gitleaks-verification.yml +176 -0
  10. package/.github/workflows/pnpm-ci.yml.example +53 -0
  11. package/.github/workflows/python-ci.yml.example +69 -0
  12. package/.github/workflows/quality-legacy.yml.backup +165 -0
  13. package/.github/workflows/quality-progressive.yml.example +291 -0
  14. package/.github/workflows/quality.yml +436 -0
  15. package/.github/workflows/release.yml +53 -0
  16. package/.nvmrc +1 -0
  17. package/.prettierignore +14 -0
  18. package/.prettierrc +9 -0
  19. package/.stylelintrc.json +5 -0
  20. package/README.md +212 -0
  21. package/config/.lighthouserc.js +45 -0
  22. package/config/.pre-commit-config.yaml +66 -0
  23. package/config/constants.js +128 -0
  24. package/config/defaults.js +124 -0
  25. package/config/pyproject.toml +124 -0
  26. package/config/quality-config.schema.json +97 -0
  27. package/config/quality-python.yml +89 -0
  28. package/config/requirements-dev.txt +15 -0
  29. package/create-saas-monetization.js +1465 -0
  30. package/eslint.config.cjs +117 -0
  31. package/eslint.config.ts.cjs +99 -0
  32. package/legal/README.md +106 -0
  33. package/legal/copyright.md +76 -0
  34. package/legal/disclaimer.md +146 -0
  35. package/legal/privacy-policy.html +324 -0
  36. package/legal/privacy-policy.md +196 -0
  37. package/legal/terms-of-service.md +224 -0
  38. package/lib/billing-dashboard.html +645 -0
  39. package/lib/config-validator.js +163 -0
  40. package/lib/dependency-monitoring-basic.js +185 -0
  41. package/lib/dependency-monitoring-premium.js +1490 -0
  42. package/lib/error-reporter.js +444 -0
  43. package/lib/interactive/prompt.js +128 -0
  44. package/lib/interactive/questions.js +146 -0
  45. package/lib/license-validator.js +403 -0
  46. package/lib/licensing.js +989 -0
  47. package/lib/package-utils.js +187 -0
  48. package/lib/project-maturity.js +516 -0
  49. package/lib/security-enhancements.js +340 -0
  50. package/lib/setup-enhancements.js +317 -0
  51. package/lib/smart-strategy-generator.js +344 -0
  52. package/lib/telemetry.js +323 -0
  53. package/lib/template-loader.js +252 -0
  54. package/lib/typescript-config-generator.js +210 -0
  55. package/lib/ui-helpers.js +74 -0
  56. package/lib/validation/base-validator.js +174 -0
  57. package/lib/validation/cache-manager.js +158 -0
  58. package/lib/validation/config-security.js +741 -0
  59. package/lib/validation/documentation.js +326 -0
  60. package/lib/validation/index.js +186 -0
  61. package/lib/validation/validation-factory.js +153 -0
  62. package/lib/validation/workflow-validation.js +172 -0
  63. package/lib/yaml-utils.js +120 -0
  64. package/marketing/beta-user-email-campaign.md +372 -0
  65. package/marketing/landing-page.html +721 -0
  66. package/package.json +165 -0
  67. package/setup.js +2076 -0
@@ -0,0 +1,989 @@
1
+ /**
2
+ * Licensing System for create-qa-architect
3
+ * Handles free/pro/enterprise tier validation
4
+ */
5
+
6
+ const fs = require('fs')
7
+ const path = require('path')
8
+ const os = require('os')
9
+ const crypto = require('crypto')
10
+
11
+ // License storage locations
12
+ // Support environment variable override for testing (like telemetry/error-reporter)
13
+ // Use getter functions to allow env override before module load
14
+ function getLicenseDir() {
15
+ return (
16
+ process.env.QAA_LICENSE_DIR ||
17
+ path.join(os.homedir(), '.create-qa-architect')
18
+ )
19
+ }
20
+
21
+ function getLicenseFile() {
22
+ return path.join(getLicenseDir(), 'license.json')
23
+ }
24
+
25
+ // Keep old constants for backward compatibility (but make them dynamic)
26
+ Object.defineProperty(exports, 'LICENSE_DIR', {
27
+ get: getLicenseDir,
28
+ })
29
+ Object.defineProperty(exports, 'LICENSE_FILE', {
30
+ get: getLicenseFile,
31
+ })
32
+
33
+ /**
34
+ * License tiers
35
+ *
36
+ * Standardized to use SCREAMING_SNAKE_CASE for both keys and values
37
+ * for consistency with ErrorCategory and other enums in the codebase.
38
+ *
39
+ * Pricing (effective Jan 15, 2026 - founder pricing retired):
40
+ * - FREE: $0 (Hobby/OSS - capped)
41
+ * - PRO: $59/mo or $590/yr (Solo Devs/Small Teams)
42
+ * - TEAM: $15/user/mo, 5-seat minimum (Organizations)
43
+ * - ENTERPRISE: $249/mo annual + $499 onboarding (Large Orgs)
44
+ */
45
+ const LICENSE_TIERS = {
46
+ FREE: 'FREE',
47
+ PRO: 'PRO',
48
+ TEAM: 'TEAM',
49
+ ENTERPRISE: 'ENTERPRISE',
50
+ }
51
+
52
+ /**
53
+ * Feature definitions by tier
54
+ *
55
+ * FREE: Hobby/OSS - capped usage, basic quality automation
56
+ * PRO: Solo devs/small teams - unlimited, full features
57
+ * TEAM: Organizations - per-seat, shared policies
58
+ * ENTERPRISE: Large orgs - SSO, compliance, SLA
59
+ */
60
+ const FEATURES = {
61
+ [LICENSE_TIERS.FREE]: {
62
+ // Caps (enforced in setup.js and CLI)
63
+ maxPrivateRepos: 1,
64
+ maxDependencyPRsPerMonth: 10,
65
+ maxPrePushRunsPerMonth: 50,
66
+ // Features
67
+ dependencyMonitoring: 'basic',
68
+ languages: ['npm'], // JS/TS only
69
+ frameworkGrouping: false,
70
+ smartTestStrategy: false, // āŒ PRO feature
71
+ typescriptProtection: false, // āŒ PRO feature (moved from FREE)
72
+ securityScanning: false, // āŒ PRO feature - Gitleaks, ESLint security
73
+ projectTypeDetection: false,
74
+ customSchedules: false,
75
+ advancedWorkflows: false,
76
+ notifications: false,
77
+ multiRepo: false,
78
+ roadmap: [
79
+ 'āœ… ESLint, Prettier, Stylelint configuration',
80
+ 'āœ… Basic Husky pre-commit hooks',
81
+ 'āœ… Basic npm dependency monitoring (10 PRs/month)',
82
+ 'āš ļø Limited: 1 private repo, JS/TS only',
83
+ 'āŒ No security scanning (Gitleaks, ESLint security)',
84
+ 'āŒ No Smart Test Strategy',
85
+ ],
86
+ },
87
+ [LICENSE_TIERS.PRO]: {
88
+ // No caps - unlimited
89
+ maxPrivateRepos: Infinity,
90
+ maxDependencyPRsPerMonth: Infinity,
91
+ maxPrePushRunsPerMonth: Infinity,
92
+ // Features
93
+ dependencyMonitoring: 'premium',
94
+ languages: ['npm', 'python', 'rust', 'ruby'], // Multi-language
95
+ frameworkGrouping: true, // React, Vue, Angular, Svelte grouping
96
+ smartTestStrategy: true, // āœ… KEY DIFFERENTIATOR: 70% faster pre-push
97
+ typescriptProtection: true, // āœ… tests/tsconfig.json generation
98
+ securityScanning: true, // āœ… Gitleaks + ESLint security rules
99
+ projectTypeDetection: true, // CLI, Web, SaaS, API, Library, Docs
100
+ advancedSecurity: true, // Rate limits, stricter audits
101
+ customSchedules: false,
102
+ advancedWorkflows: false,
103
+ notifications: false,
104
+ multiRepo: false,
105
+ roadmap: [
106
+ 'āœ… Unlimited repos and runs',
107
+ 'āœ… Smart Test Strategy (70% faster pre-push validation)',
108
+ 'āœ… Security scanning (Gitleaks + ESLint security rules)',
109
+ 'āœ… TypeScript production protection',
110
+ 'āœ… Multi-language (Python, Rust, Ruby)',
111
+ 'āœ… Framework-aware dependency grouping',
112
+ 'āœ… Email support (24-48h response)',
113
+ 'šŸ”„ Performance budgets - Coming Q1 2026',
114
+ ],
115
+ },
116
+ [LICENSE_TIERS.TEAM]: {
117
+ // Per-seat model (5-seat minimum)
118
+ minSeats: 5,
119
+ maxPrivateRepos: Infinity,
120
+ maxDependencyPRsPerMonth: Infinity,
121
+ maxPrePushRunsPerMonth: Infinity,
122
+ // All PRO features plus:
123
+ dependencyMonitoring: 'premium',
124
+ languages: ['npm', 'python', 'rust', 'ruby'],
125
+ frameworkGrouping: true,
126
+ smartTestStrategy: true,
127
+ typescriptProtection: true,
128
+ securityScanning: true,
129
+ projectTypeDetection: true,
130
+ advancedSecurity: true,
131
+ // Team-specific
132
+ perSeatLicensing: true,
133
+ sharedOrgQuota: true,
134
+ teamPolicies: true, // Team-wide config policies
135
+ roleBasedAccess: true,
136
+ teamAuditLog: true, // Local audit log
137
+ slackAlerts: true, // Slack/email alerts for failures
138
+ customSchedules: true,
139
+ advancedWorkflows: true,
140
+ notifications: true,
141
+ multiRepo: true,
142
+ roadmap: [
143
+ 'āœ… All PRO features included',
144
+ 'āœ… Per-seat licensing (5-seat minimum)',
145
+ 'āœ… Shared org quota & usage reporting',
146
+ 'āœ… Team-wide configuration policies',
147
+ 'āœ… Slack/email alerts for failures',
148
+ 'āœ… Team audit log',
149
+ 'āœ… Priority support (business hours)',
150
+ ],
151
+ },
152
+ [LICENSE_TIERS.ENTERPRISE]: {
153
+ // Annual only + onboarding
154
+ annualOnly: true,
155
+ onboardingFee: 499,
156
+ maxPrivateRepos: Infinity,
157
+ maxDependencyPRsPerMonth: Infinity,
158
+ maxPrePushRunsPerMonth: Infinity,
159
+ // All TEAM features plus:
160
+ dependencyMonitoring: 'enterprise',
161
+ languages: ['npm', 'python', 'rust', 'ruby', 'go', 'java'],
162
+ frameworkGrouping: true,
163
+ smartTestStrategy: true,
164
+ typescriptProtection: true,
165
+ securityScanning: true,
166
+ projectTypeDetection: true,
167
+ advancedSecurity: true,
168
+ perSeatLicensing: true,
169
+ sharedOrgQuota: true,
170
+ teamPolicies: true,
171
+ roleBasedAccess: true,
172
+ teamAuditLog: true,
173
+ slackAlerts: true,
174
+ customSchedules: true,
175
+ advancedWorkflows: true,
176
+ notifications: true,
177
+ multiRepo: true,
178
+ // Enterprise-specific
179
+ ssoIntegration: true, // SSO/SAML
180
+ scimReady: true,
181
+ customRiskPatterns: true, // Custom Smart Test Strategy patterns
182
+ customDependencyPolicies: true, // Deny/allow lists, license classes
183
+ webhookBilling: true,
184
+ auditLogsExport: true,
185
+ dataRetentionControls: true,
186
+ compliancePack: true,
187
+ dedicatedTAM: true,
188
+ incidentSLA: true, // 24Ɨ5 support
189
+ onPremOption: true, // Optional on-prem license server
190
+ roadmap: [
191
+ 'āœ… All TEAM features included',
192
+ 'āœ… SSO/SAML integration',
193
+ 'āœ… Custom risk patterns for Smart Test Strategy',
194
+ 'āœ… Custom dependency policies (deny/allow, license classes)',
195
+ 'āœ… Audit logs export & data retention controls',
196
+ 'āœ… Compliance pack (SOC2, GDPR ready)',
197
+ 'āœ… Dedicated TAM + 24Ɨ5 support with SLA',
198
+ 'āœ… Optional on-prem license server',
199
+ ],
200
+ },
201
+ }
202
+
203
+ /**
204
+ * Check if developer/owner mode is enabled
205
+ * Allows the tool creator to use all features without a license
206
+ */
207
+ function isDeveloperMode() {
208
+ // Check environment variable
209
+ if (process.env.QAA_DEVELOPER === 'true') {
210
+ return true
211
+ }
212
+
213
+ // Check for marker file in license directory
214
+ try {
215
+ const developerMarkerFile = path.join(getLicenseDir(), '.cqa-developer')
216
+ if (fs.existsSync(developerMarkerFile)) {
217
+ return true
218
+ }
219
+ } catch (_error) {
220
+ // Ignore errors checking for marker file
221
+ }
222
+
223
+ return false
224
+ }
225
+
226
+ /**
227
+ * Check if user has a valid license file (USER-FACING - NO STRIPE DEPENDENCIES)
228
+ */
229
+ function getLicenseInfo() {
230
+ try {
231
+ // Developer/owner bypass - full PRO access without license
232
+ if (isDeveloperMode()) {
233
+ return {
234
+ tier: LICENSE_TIERS.PRO,
235
+ valid: true,
236
+ email: 'developer@localhost',
237
+ isDeveloper: true,
238
+ }
239
+ }
240
+
241
+ // Use pure license validator
242
+ const { LicenseValidator } = require('./license-validator')
243
+ const validator = new LicenseValidator()
244
+
245
+ const localLicense = validator.getLocalLicense()
246
+
247
+ if (!localLicense) {
248
+ return { tier: LICENSE_TIERS.FREE, valid: true }
249
+ }
250
+
251
+ // Check if license is valid
252
+ if (!localLicense.valid) {
253
+ return {
254
+ tier: LICENSE_TIERS.FREE,
255
+ valid: true,
256
+ error:
257
+ 'License signature verification failed - license may have been tampered with',
258
+ }
259
+ }
260
+
261
+ // Check for required fields
262
+ const licenseKey = localLicense.licenseKey || localLicense.key
263
+ if (!licenseKey || !localLicense.email) {
264
+ return {
265
+ tier: LICENSE_TIERS.FREE,
266
+ valid: true,
267
+ error: 'Invalid license format',
268
+ }
269
+ }
270
+
271
+ // Check expiration
272
+ if (localLicense.expires && new Date(localLicense.expires) < new Date()) {
273
+ return {
274
+ tier: LICENSE_TIERS.FREE,
275
+ valid: true,
276
+ error: 'License expired',
277
+ }
278
+ }
279
+
280
+ // Validate license key format
281
+ if (!validateLicenseKey(licenseKey, localLicense.tier)) {
282
+ return {
283
+ tier: LICENSE_TIERS.FREE,
284
+ valid: true,
285
+ error: 'Invalid license key',
286
+ }
287
+ }
288
+
289
+ // Return license info
290
+ return {
291
+ tier: localLicense.tier || LICENSE_TIERS.FREE,
292
+ valid: true,
293
+ email: localLicense.email,
294
+ expires: localLicense.expires,
295
+ isFounder: localLicense.isFounder || false,
296
+ customerId: localLicense.customerId,
297
+ }
298
+ } catch (error) {
299
+ return {
300
+ tier: LICENSE_TIERS.FREE,
301
+ valid: true,
302
+ error: `License read error: ${error.message}`,
303
+ }
304
+ }
305
+ }
306
+
307
+ /**
308
+ * License key validation with Stripe integration
309
+ * Supports both legacy format and new Stripe-generated keys
310
+ */
311
+ function validateLicenseKey(key, tier) {
312
+ // New Stripe format: QAA-XXXX-XXXX-XXXX-XXXX
313
+ const stripeFormat = /^QAA-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$/
314
+
315
+ if (stripeFormat.test(key)) {
316
+ // Stripe-generated key - always valid if properly formatted
317
+ return true
318
+ }
319
+
320
+ // Legacy format validation for backward compatibility
321
+ const expectedPrefix = `QAA-${tier.toUpperCase()}-`
322
+ return key.startsWith(expectedPrefix) && key.length > 20
323
+ }
324
+
325
+ /**
326
+ * Verify license signature using the same algorithm as StripeIntegration
327
+ */
328
+ function verifyLicenseSignature(payload, signature) {
329
+ try {
330
+ const secret =
331
+ process.env.LICENSE_SIGNING_SECRET || 'cqa-dev-secret-change-in-prod'
332
+ const expectedSignature = crypto
333
+ .createHmac('sha256', secret)
334
+ .update(JSON.stringify(payload))
335
+ .digest('hex')
336
+
337
+ return crypto.timingSafeEqual(
338
+ Buffer.from(signature),
339
+ Buffer.from(expectedSignature)
340
+ )
341
+ } catch (_error) {
342
+ // If signature verification fails, treat as invalid
343
+ return false
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Check if a specific feature is available for current license
349
+ */
350
+ function hasFeature(featureName) {
351
+ const license = getLicenseInfo()
352
+ const tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
353
+ return tierFeatures[featureName] || false
354
+ }
355
+
356
+ /**
357
+ * Get the dependency monitoring level for current license
358
+ */
359
+ function getDependencyMonitoringLevel() {
360
+ const license = getLicenseInfo()
361
+ const tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
362
+ return tierFeatures.dependencyMonitoring
363
+ }
364
+
365
+ /**
366
+ * Get supported languages for current license
367
+ */
368
+ function getSupportedLanguages() {
369
+ const license = getLicenseInfo()
370
+ const tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
371
+ return tierFeatures.languages
372
+ }
373
+
374
+ /**
375
+ * Display upgrade message for premium features
376
+ */
377
+ function showUpgradeMessage(feature) {
378
+ const license = getLicenseInfo()
379
+ const _tierFeatures = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
380
+
381
+ console.log(`\nšŸ”’ ${feature} is a premium feature`)
382
+ console.log(`šŸ“Š Current license: ${license.tier.toUpperCase()}`)
383
+
384
+ if (license.tier === LICENSE_TIERS.FREE) {
385
+ console.log('\nšŸš€ Upgrade to PRO')
386
+ console.log('')
387
+ console.log(' šŸ’° $59/month or $590/year (save $118)')
388
+ console.log('')
389
+ console.log(' āœ… Unlimited repos, LOC, and runs')
390
+ console.log(' āœ… Smart Test Strategy (70% faster pre-push)')
391
+ console.log(' āœ… Security scanning (Gitleaks + ESLint security)')
392
+ console.log(' āœ… TypeScript production protection')
393
+ console.log(' āœ… Multi-language (Python, Rust, Ruby)')
394
+ console.log(' āœ… Framework-aware dependency grouping')
395
+ console.log(' āœ… Email support (24-48h response)')
396
+ console.log('')
397
+ console.log(' šŸŽ Start 14-day free trial - no credit card required')
398
+ console.log('')
399
+ console.log('šŸš€ Upgrade: https://vibebuildlab.com/cqa')
400
+ console.log(
401
+ 'šŸ”‘ Activate: npx create-qa-architect@latest --activate-license'
402
+ )
403
+ } else if (license.tier === LICENSE_TIERS.PRO) {
404
+ console.log('\nšŸ‘„ Upgrade to TEAM')
405
+ console.log('')
406
+ console.log(
407
+ ' šŸ’° $15/user/month (5-seat min) or $150/user/year (save $30/user)'
408
+ )
409
+ console.log('')
410
+ console.log(' āœ… All PRO features included')
411
+ console.log(' āœ… Per-seat licensing for your org')
412
+ console.log(' āœ… Shared quota & usage reporting')
413
+ console.log(' āœ… Team-wide configuration policies')
414
+ console.log(' āœ… Slack/email alerts for failures')
415
+ console.log(' āœ… Priority support (business hours)')
416
+ console.log('')
417
+ console.log('šŸ‘„ Upgrade: https://vibebuildlab.com/cqa/team')
418
+ } else if (license.tier === LICENSE_TIERS.TEAM) {
419
+ console.log('\nšŸ¢ Upgrade to ENTERPRISE - $249/month (annual) + onboarding')
420
+ console.log('')
421
+ console.log(' āœ… All TEAM features included')
422
+ console.log(' āœ… SSO/SAML integration')
423
+ console.log(' āœ… Custom risk patterns & dependency policies')
424
+ console.log(' āœ… Audit logs export & compliance pack')
425
+ console.log(' āœ… Dedicated TAM + 24Ɨ5 support with SLA')
426
+ console.log('')
427
+ console.log('šŸ¢ Contact: enterprise@vibebuildlab.com')
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Save license information (for testing or license activation)
433
+ */
434
+ function saveLicense(tier, key, email, expires = null) {
435
+ try {
436
+ const licenseDir = getLicenseDir()
437
+ const licenseFile = getLicenseFile()
438
+
439
+ if (!fs.existsSync(licenseDir)) {
440
+ fs.mkdirSync(licenseDir, { recursive: true })
441
+ }
442
+
443
+ const licenseData = {
444
+ tier,
445
+ key,
446
+ email,
447
+ expires,
448
+ activated: new Date().toISOString(),
449
+ }
450
+
451
+ fs.writeFileSync(licenseFile, JSON.stringify(licenseData, null, 2))
452
+ return { success: true }
453
+ } catch (error) {
454
+ return { success: false, error: error.message }
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Save license information with signature validation data
460
+ */
461
+ function saveLicenseWithSignature(tier, key, email, validation) {
462
+ try {
463
+ const licenseDir = getLicenseDir()
464
+ const licenseFile = getLicenseFile()
465
+
466
+ if (!fs.existsSync(licenseDir)) {
467
+ fs.mkdirSync(licenseDir, { recursive: true })
468
+ }
469
+
470
+ const licenseData = {
471
+ tier,
472
+ licenseKey: key, // āœ… Changed from 'key' to 'licenseKey' to match StripeIntegration
473
+ email,
474
+ expires: validation.expires,
475
+ activated: new Date().toISOString(),
476
+ customerId: validation.customerId,
477
+ isFounder: validation.isFounder,
478
+ // Include validation payload and signature for security
479
+ payload: validation.payload, // āœ… Changed from 'validationPayload' to 'payload'
480
+ signature: validation.signature, // āœ… Changed from 'validationSignature' to 'signature'
481
+ issued: validation.issued,
482
+ }
483
+
484
+ fs.writeFileSync(licenseFile, JSON.stringify(licenseData, null, 2))
485
+ return { success: true }
486
+ } catch (error) {
487
+ return { success: false, error: error.message }
488
+ }
489
+ }
490
+
491
+ /**
492
+ * Remove license (for testing)
493
+ */
494
+ function removeLicense() {
495
+ try {
496
+ const licenseFile = getLicenseFile()
497
+
498
+ if (fs.existsSync(licenseFile)) {
499
+ fs.unlinkSync(licenseFile)
500
+ }
501
+ return { success: true }
502
+ } catch (error) {
503
+ return { success: false, error: error.message }
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Activate license (USER-FACING - NO STRIPE DEPENDENCIES)
509
+ */
510
+ async function activateLicense(licenseKey, email) {
511
+ try {
512
+ // Use pure license validator (no Stripe dependencies)
513
+ const { LicenseValidator } = require('./license-validator')
514
+ const validator = new LicenseValidator()
515
+
516
+ // Initialize license directory/database
517
+ validator.initialize()
518
+
519
+ // Activate license using local database validation only
520
+ return await validator.activateLicense(licenseKey, email)
521
+ } catch (error) {
522
+ return {
523
+ success: false,
524
+ error: `License activation failed: ${error.message}. Please contact support if the issue persists.`,
525
+ }
526
+ }
527
+ }
528
+
529
+ /**
530
+ * Add a legitimate license key (admin function - uses local database)
531
+ */
532
+ async function addLegitimateKey(
533
+ licenseKey,
534
+ customerId,
535
+ tier,
536
+ isFounder = false,
537
+ purchaseEmail = null
538
+ ) {
539
+ try {
540
+ // Use the same license directory as the CLI
541
+ const licenseDir =
542
+ process.env.QAA_LICENSE_DIR ||
543
+ path.join(os.homedir(), '.create-qa-architect')
544
+ const legitimateDBFile = path.join(licenseDir, 'legitimate-licenses.json')
545
+
546
+ // Ensure directory exists
547
+ if (!fs.existsSync(licenseDir)) {
548
+ fs.mkdirSync(licenseDir, { recursive: true })
549
+ }
550
+
551
+ // Load existing database
552
+ let database = {}
553
+ if (fs.existsSync(legitimateDBFile)) {
554
+ try {
555
+ database = JSON.parse(fs.readFileSync(legitimateDBFile, 'utf8'))
556
+ } catch (_error) {
557
+ console.error(
558
+ 'Warning: Could not parse existing database, creating new one'
559
+ )
560
+ }
561
+ }
562
+
563
+ // Initialize metadata if needed
564
+ if (!database._metadata) {
565
+ database._metadata = {
566
+ version: '1.0',
567
+ created: new Date().toISOString(),
568
+ description: 'Legitimate license database - populated by admin/webhook',
569
+ }
570
+ }
571
+
572
+ // Add license
573
+ database[licenseKey] = {
574
+ customerId,
575
+ tier,
576
+ isFounder,
577
+ email: purchaseEmail,
578
+ addedDate: new Date().toISOString(),
579
+ addedBy: 'admin_tool',
580
+ }
581
+
582
+ // Update metadata
583
+ database._metadata.lastUpdate = new Date().toISOString()
584
+ database._metadata.totalLicenses = Object.keys(database).length - 1 // Exclude metadata
585
+
586
+ // Calculate SHA256 checksum for integrity verification (MANDATORY)
587
+ const { _metadata, ...licensesOnly } = database
588
+ const sha256 = crypto
589
+ .createHash('sha256')
590
+ .update(JSON.stringify(licensesOnly))
591
+ .digest('hex')
592
+ database._metadata.sha256 = sha256
593
+
594
+ // Save database
595
+ fs.writeFileSync(legitimateDBFile, JSON.stringify(database, null, 2))
596
+
597
+ console.log(`āœ… Added legitimate license: ${licenseKey}`)
598
+ console.log(` Customer: ${customerId}`)
599
+ console.log(` Tier: ${tier}`)
600
+ console.log(` Founder: ${isFounder ? 'Yes' : 'No'}`)
601
+ if (purchaseEmail) {
602
+ console.log(` Purchase Email: ${purchaseEmail}`)
603
+ }
604
+
605
+ return { success: true }
606
+ } catch (error) {
607
+ return { success: false, error: error.message }
608
+ }
609
+ }
610
+
611
+ /**
612
+ * Interactive license activation prompt
613
+ */
614
+ async function promptLicenseActivation() {
615
+ const readline = require('readline')
616
+
617
+ const rl = readline.createInterface({
618
+ input: process.stdin,
619
+ output: process.stdout,
620
+ })
621
+
622
+ return new Promise(resolve => {
623
+ console.log('\nšŸ”‘ License Activation')
624
+ console.log(
625
+ 'Enter your license key from the purchase confirmation email.\n'
626
+ )
627
+
628
+ rl.question('License key (QAA-XXXX-XXXX-XXXX-XXXX): ', licenseKey => {
629
+ if (!licenseKey.trim()) {
630
+ console.log('āŒ License key required')
631
+ rl.close()
632
+ resolve({ success: false })
633
+ return
634
+ }
635
+
636
+ rl.question('Email address: ', async email => {
637
+ if (!email.trim()) {
638
+ console.log('āŒ Email address required')
639
+ rl.close()
640
+ resolve({ success: false })
641
+ return
642
+ }
643
+
644
+ rl.close()
645
+
646
+ const result = await activateLicense(licenseKey.trim(), email.trim())
647
+
648
+ if (
649
+ !result.success &&
650
+ result.error &&
651
+ result.error.includes('not found')
652
+ ) {
653
+ console.log('\nšŸ“ž License activation assistance:')
654
+ console.log(
655
+ ' If you purchased this license, please contact support at:'
656
+ )
657
+ console.log(' Email: support@aibuilderlab.com')
658
+ console.log(
659
+ ' Include your license key and purchase email for verification.'
660
+ )
661
+ }
662
+
663
+ resolve(result)
664
+ })
665
+ })
666
+ })
667
+ }
668
+
669
+ /**
670
+ * Enable developer mode by creating the marker file
671
+ */
672
+ function enableDeveloperMode() {
673
+ try {
674
+ const licenseDir = getLicenseDir()
675
+ const developerMarkerFile = path.join(licenseDir, '.cqa-developer')
676
+
677
+ if (!fs.existsSync(licenseDir)) {
678
+ fs.mkdirSync(licenseDir, { recursive: true })
679
+ }
680
+
681
+ fs.writeFileSync(
682
+ developerMarkerFile,
683
+ `# QAA Developer Mode\n# Created: ${new Date().toISOString()}\n# This file grants full PRO access for development purposes.\n`
684
+ )
685
+
686
+ console.log('āœ… Developer mode enabled')
687
+ console.log(` Marker file: ${developerMarkerFile}`)
688
+ return { success: true }
689
+ } catch (error) {
690
+ return { success: false, error: error.message }
691
+ }
692
+ }
693
+
694
+ /**
695
+ * Disable developer mode by removing the marker file
696
+ */
697
+ function disableDeveloperMode() {
698
+ try {
699
+ const developerMarkerFile = path.join(getLicenseDir(), '.cqa-developer')
700
+
701
+ if (fs.existsSync(developerMarkerFile)) {
702
+ fs.unlinkSync(developerMarkerFile)
703
+ }
704
+
705
+ console.log('āœ… Developer mode disabled')
706
+ return { success: true }
707
+ } catch (error) {
708
+ return { success: false, error: error.message }
709
+ }
710
+ }
711
+
712
+ // ============================================================================
713
+ // FREE TIER CAP ENFORCEMENT
714
+ // ============================================================================
715
+
716
+ /**
717
+ * Get the path to the usage tracking file
718
+ */
719
+ function getUsageFile() {
720
+ return path.join(getLicenseDir(), 'usage.json')
721
+ }
722
+
723
+ /**
724
+ * Load current usage data
725
+ */
726
+ function loadUsage() {
727
+ try {
728
+ const usageFile = getUsageFile()
729
+ if (fs.existsSync(usageFile)) {
730
+ const data = JSON.parse(fs.readFileSync(usageFile, 'utf8'))
731
+
732
+ // Check if we need to reset monthly counters
733
+ const now = new Date()
734
+ const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
735
+
736
+ if (data.month !== currentMonth) {
737
+ // New month - reset monthly counters
738
+ return {
739
+ month: currentMonth,
740
+ prePushRuns: 0,
741
+ dependencyPRs: 0,
742
+ repos: data.repos || [],
743
+ }
744
+ }
745
+
746
+ return data
747
+ }
748
+ } catch (_error) {
749
+ // Ignore errors reading usage file
750
+ }
751
+
752
+ // Default usage data
753
+ const now = new Date()
754
+ return {
755
+ month: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`,
756
+ prePushRuns: 0,
757
+ dependencyPRs: 0,
758
+ repos: [],
759
+ }
760
+ }
761
+
762
+ /**
763
+ * Save usage data
764
+ */
765
+ function saveUsage(usage) {
766
+ try {
767
+ const licenseDir = getLicenseDir()
768
+ if (!fs.existsSync(licenseDir)) {
769
+ fs.mkdirSync(licenseDir, { recursive: true })
770
+ }
771
+ fs.writeFileSync(getUsageFile(), JSON.stringify(usage, null, 2))
772
+ return true
773
+ } catch (_error) {
774
+ return false
775
+ }
776
+ }
777
+
778
+ /**
779
+ * Check if usage is within FREE tier caps
780
+ * Returns { allowed: boolean, reason?: string, usage: object, caps: object }
781
+ */
782
+ function checkUsageCaps(operation = 'general') {
783
+ const license = getLicenseInfo()
784
+
785
+ // Non-FREE tiers have no caps
786
+ if (license.tier !== LICENSE_TIERS.FREE) {
787
+ return { allowed: true, usage: {}, caps: {} }
788
+ }
789
+
790
+ const caps = FEATURES[LICENSE_TIERS.FREE]
791
+ const usage = loadUsage()
792
+
793
+ const result = {
794
+ allowed: true,
795
+ usage: {
796
+ prePushRuns: usage.prePushRuns,
797
+ dependencyPRs: usage.dependencyPRs,
798
+ repos: usage.repos.length,
799
+ },
800
+ caps: {
801
+ maxPrePushRunsPerMonth: caps.maxPrePushRunsPerMonth,
802
+ maxDependencyPRsPerMonth: caps.maxDependencyPRsPerMonth,
803
+ maxPrivateRepos: caps.maxPrivateRepos,
804
+ },
805
+ }
806
+
807
+ // Check specific cap based on operation
808
+ if (operation === 'pre-push') {
809
+ if (usage.prePushRuns >= caps.maxPrePushRunsPerMonth) {
810
+ result.allowed = false
811
+ result.reason = `FREE tier limit reached: ${usage.prePushRuns}/${caps.maxPrePushRunsPerMonth} pre-push runs this month`
812
+ }
813
+ } else if (operation === 'dependency-pr') {
814
+ if (usage.dependencyPRs >= caps.maxDependencyPRsPerMonth) {
815
+ result.allowed = false
816
+ result.reason = `FREE tier limit reached: ${usage.dependencyPRs}/${caps.maxDependencyPRsPerMonth} dependency PRs this month`
817
+ }
818
+ } else if (operation === 'repo') {
819
+ if (usage.repos.length >= caps.maxPrivateRepos) {
820
+ result.allowed = false
821
+ result.reason = `FREE tier limit reached: ${usage.repos.length}/${caps.maxPrivateRepos} private repos`
822
+ }
823
+ }
824
+
825
+ return result
826
+ }
827
+
828
+ /**
829
+ * Increment usage counter for an operation
830
+ */
831
+ function incrementUsage(operation, amount = 1, repoId = null) {
832
+ const license = getLicenseInfo()
833
+
834
+ // Non-FREE tiers don't track usage
835
+ if (license.tier !== LICENSE_TIERS.FREE) {
836
+ return { success: true }
837
+ }
838
+
839
+ const usage = loadUsage()
840
+
841
+ if (operation === 'pre-push') {
842
+ usage.prePushRuns += amount
843
+ } else if (operation === 'dependency-pr') {
844
+ usage.dependencyPRs += amount
845
+ } else if (operation === 'repo' && repoId) {
846
+ if (!usage.repos.includes(repoId)) {
847
+ usage.repos.push(repoId)
848
+ }
849
+ }
850
+
851
+ saveUsage(usage)
852
+ return { success: true, usage }
853
+ }
854
+
855
+ /**
856
+ * Get usage summary for display
857
+ */
858
+ function getUsageSummary() {
859
+ const license = getLicenseInfo()
860
+ const usage = loadUsage()
861
+ const caps = FEATURES[LICENSE_TIERS.FREE]
862
+
863
+ if (license.tier !== LICENSE_TIERS.FREE) {
864
+ return {
865
+ tier: license.tier,
866
+ unlimited: true,
867
+ }
868
+ }
869
+
870
+ return {
871
+ tier: license.tier,
872
+ unlimited: false,
873
+ month: usage.month,
874
+ prePushRuns: {
875
+ used: usage.prePushRuns,
876
+ limit: caps.maxPrePushRunsPerMonth,
877
+ remaining: Math.max(0, caps.maxPrePushRunsPerMonth - usage.prePushRuns),
878
+ },
879
+ dependencyPRs: {
880
+ used: usage.dependencyPRs,
881
+ limit: caps.maxDependencyPRsPerMonth,
882
+ remaining: Math.max(
883
+ 0,
884
+ caps.maxDependencyPRsPerMonth - usage.dependencyPRs
885
+ ),
886
+ },
887
+ repos: {
888
+ used: usage.repos.length,
889
+ limit: caps.maxPrivateRepos,
890
+ remaining: Math.max(0, caps.maxPrivateRepos - usage.repos.length),
891
+ },
892
+ }
893
+ }
894
+
895
+ /**
896
+ * Display current license status
897
+ */
898
+ function showLicenseStatus() {
899
+ const license = getLicenseInfo()
900
+
901
+ console.log('\nšŸ“‹ License Status:')
902
+ if (license.isDeveloper) {
903
+ console.log(' Mode: šŸ› ļø DEVELOPER (full PRO access)')
904
+ }
905
+ console.log(` Tier: ${license.tier.toUpperCase()}`)
906
+
907
+ if (license.email) {
908
+ console.log(` Email: ${license.email}`)
909
+ }
910
+
911
+ if (license.expires) {
912
+ console.log(` Expires: ${license.expires}`)
913
+ }
914
+
915
+ if (license.error) {
916
+ console.log(` āš ļø Issue: ${license.error}`)
917
+ }
918
+
919
+ console.log('\nšŸŽÆ Available Features:')
920
+ const features = FEATURES[license.tier] || FEATURES[LICENSE_TIERS.FREE]
921
+
922
+ // Show caps and current usage for FREE tier
923
+ if (license.tier === LICENSE_TIERS.FREE) {
924
+ const usage = getUsageSummary()
925
+ console.log('\nšŸ“Š Usage This Month:')
926
+ console.log(
927
+ ` Pre-push Runs: ${usage.prePushRuns.used}/${usage.prePushRuns.limit} (${usage.prePushRuns.remaining} remaining)`
928
+ )
929
+ console.log(
930
+ ` Dependency PRs: ${usage.dependencyPRs.used}/${usage.dependencyPRs.limit} (${usage.dependencyPRs.remaining} remaining)`
931
+ )
932
+ console.log(` Private Repos: ${usage.repos.used}/${usage.repos.limit}`)
933
+ } else {
934
+ console.log(` Repos/Runs: Unlimited`)
935
+ }
936
+
937
+ console.log(` Dependency Monitoring: ${features.dependencyMonitoring}`)
938
+ console.log(` Languages: ${features.languages.join(', ')}`)
939
+ console.log(
940
+ ` Security Scanning: ${features.securityScanning ? 'āœ…' : 'āŒ'}`
941
+ )
942
+ console.log(
943
+ ` Smart Test Strategy: ${features.smartTestStrategy ? 'āœ…' : 'āŒ'}`
944
+ )
945
+ console.log(
946
+ ` Framework Grouping: ${features.frameworkGrouping ? 'āœ…' : 'āŒ'}`
947
+ )
948
+ console.log(
949
+ ` Advanced Workflows: ${features.advancedWorkflows ? 'āœ…' : 'āŒ'}`
950
+ )
951
+
952
+ if (features.roadmap && features.roadmap.length) {
953
+ console.log('\nšŸ“¦ Your Plan Features:')
954
+ features.roadmap.forEach(item => console.log(` ${item}`))
955
+ }
956
+
957
+ // Show upgrade path
958
+ if (license.tier === LICENSE_TIERS.FREE) {
959
+ console.log('\nšŸ’” Upgrade to PRO for unlimited access + security scanning')
960
+ console.log(' → https://vibebuildlab.com/cqa')
961
+ }
962
+ }
963
+
964
+ module.exports = {
965
+ LICENSE_TIERS,
966
+ FEATURES,
967
+ getLicenseInfo,
968
+ hasFeature,
969
+ getDependencyMonitoringLevel,
970
+ getSupportedLanguages,
971
+ showUpgradeMessage,
972
+ saveLicense,
973
+ saveLicenseWithSignature,
974
+ removeLicense,
975
+ showLicenseStatus,
976
+ activateLicense,
977
+ promptLicenseActivation,
978
+ verifyLicenseSignature,
979
+ LicenseValidator: require('./license-validator').LicenseValidator,
980
+ addLegitimateKey,
981
+ // Developer mode functions
982
+ isDeveloperMode,
983
+ enableDeveloperMode,
984
+ disableDeveloperMode,
985
+ // Usage tracking and cap enforcement (FREE tier)
986
+ checkUsageCaps,
987
+ incrementUsage,
988
+ getUsageSummary,
989
+ }