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,344 @@
1
+ /**
2
+ * Smart Test Strategy Generator
3
+ * Generates project-specific smart test strategy scripts
4
+ * Premium feature (Pro/Team/Enterprise tiers)
5
+ */
6
+
7
+ const fs = require('fs')
8
+ const path = require('path')
9
+
10
+ /**
11
+ * Project type configurations with risk patterns and test commands
12
+ */
13
+ const PROJECT_CONFIGS = {
14
+ // CLI tools (like create-qa-architect)
15
+ cli: {
16
+ name: 'CLI Tool',
17
+ highRiskRegex: 'setup\\.js|lib/.*|templates/.*|config/.*|bin/.*',
18
+ testCommands: {
19
+ comprehensive:
20
+ 'npm run test:comprehensive 2>/dev/null || npm run test 2>/dev/null || npm test',
21
+ medium:
22
+ 'npm run test:medium 2>/dev/null || npm run test:fast 2>/dev/null || npm test',
23
+ fast: 'npm run test:fast 2>/dev/null || npm run test:unit 2>/dev/null || npm test',
24
+ minimal: 'npm run lint && npm run format:check',
25
+ },
26
+ detection: projectPath => {
27
+ const pkg = readPackageJson(projectPath)
28
+ return pkg?.bin || pkg?.scripts?.setup
29
+ },
30
+ },
31
+
32
+ // Web applications (Next.js, React, Vue, etc.)
33
+ webapp: {
34
+ name: 'Web Application',
35
+ highRiskRegex:
36
+ 'auth|payment|security|crypto|api/|pages/api/|app/api/|middleware',
37
+ testCommands: {
38
+ comprehensive:
39
+ 'npm run test:comprehensive 2>/dev/null || (npm test && npm run test:e2e 2>/dev/null)',
40
+ medium:
41
+ 'npm run test:medium 2>/dev/null || npm run test -- --testPathIgnorePatterns=e2e',
42
+ fast: 'npm run test:fast 2>/dev/null || npm run test -- --watch=false --coverage=false',
43
+ minimal: 'npm run lint && npm run format:check',
44
+ },
45
+ detection: projectPath => {
46
+ const pkg = readPackageJson(projectPath)
47
+ const deps = { ...pkg?.dependencies, ...pkg?.devDependencies }
48
+ return (
49
+ deps?.next ||
50
+ deps?.react ||
51
+ deps?.vue ||
52
+ deps?.['@angular/core'] ||
53
+ deps?.svelte
54
+ )
55
+ },
56
+ },
57
+
58
+ // SaaS applications (payment, billing, auth heavy)
59
+ saas: {
60
+ name: 'SaaS Application',
61
+ highRiskRegex:
62
+ 'auth|payment|billing|stripe|subscription|prisma/schema|middleware|webhook',
63
+ testCommands: {
64
+ comprehensive:
65
+ 'npm run test:comprehensive 2>/dev/null || (npm test && npm run security:audit 2>/dev/null)',
66
+ medium: 'npm run test:medium 2>/dev/null || npm test',
67
+ fast: 'npm run test:fast 2>/dev/null || npm run test:unit 2>/dev/null || npm test',
68
+ minimal: 'npm run lint && npm run format:check',
69
+ },
70
+ detection: projectPath => {
71
+ const pkg = readPackageJson(projectPath)
72
+ const deps = { ...pkg?.dependencies, ...pkg?.devDependencies }
73
+ return deps?.stripe || deps?.['@stripe/stripe-js'] || deps?.prisma
74
+ },
75
+ },
76
+
77
+ // API services (Express, Fastify, etc.)
78
+ api: {
79
+ name: 'API Service',
80
+ highRiskRegex: 'routes/|controllers/|middleware/|auth|security|database',
81
+ testCommands: {
82
+ comprehensive:
83
+ 'npm run test:comprehensive 2>/dev/null || npm run test:integration 2>/dev/null || npm test',
84
+ medium:
85
+ 'npm run test:medium 2>/dev/null || npm run test -- --testPathIgnorePatterns=integration',
86
+ fast: 'npm run test:fast 2>/dev/null || npm run test:unit 2>/dev/null || npm test',
87
+ minimal: 'npm run lint && npm run format:check',
88
+ },
89
+ detection: projectPath => {
90
+ const pkg = readPackageJson(projectPath)
91
+ const deps = { ...pkg?.dependencies, ...pkg?.devDependencies }
92
+ return (
93
+ deps?.express ||
94
+ deps?.fastify ||
95
+ deps?.koa ||
96
+ deps?.hapi ||
97
+ deps?.restify
98
+ )
99
+ },
100
+ },
101
+
102
+ // Library/Package
103
+ library: {
104
+ name: 'Library/Package',
105
+ highRiskRegex: 'src/|lib/|index\\.(js|ts)|package\\.json',
106
+ testCommands: {
107
+ comprehensive:
108
+ 'npm run test:comprehensive 2>/dev/null || (npm test && npm run build 2>/dev/null)',
109
+ medium: 'npm run test:medium 2>/dev/null || npm test',
110
+ fast: 'npm run test:fast 2>/dev/null || npm run test:unit 2>/dev/null || npm test',
111
+ minimal: 'npm run lint && npm run format:check',
112
+ },
113
+ detection: projectPath => {
114
+ const pkg = readPackageJson(projectPath)
115
+ return pkg?.main || pkg?.module || pkg?.exports
116
+ },
117
+ },
118
+
119
+ // Documentation project
120
+ docs: {
121
+ name: 'Documentation',
122
+ highRiskRegex: 'guides/security|guides/deployment|setup-instructions',
123
+ testCommands: {
124
+ comprehensive:
125
+ 'npm run test:comprehensive 2>/dev/null || (npm run lint && npm run link:check 2>/dev/null)',
126
+ medium:
127
+ 'npm run test:medium 2>/dev/null || npm run lint && npm run spell:check 2>/dev/null',
128
+ fast: 'npm run test:fast 2>/dev/null || npm run lint',
129
+ minimal: 'npm run lint 2>/dev/null || npx markdownlint "**/*.md"',
130
+ },
131
+ detection: projectPath => {
132
+ const hasReadme = fs.existsSync(path.join(projectPath, 'README.md'))
133
+ const hasDocs = fs.existsSync(path.join(projectPath, 'docs'))
134
+ const pkg = readPackageJson(projectPath)
135
+ return hasDocs && !pkg?.dependencies && hasReadme
136
+ },
137
+ },
138
+
139
+ // Default fallback
140
+ default: {
141
+ name: 'General Project',
142
+ highRiskRegex: 'src/|lib/|config/|package\\.json',
143
+ testCommands: {
144
+ comprehensive:
145
+ 'npm run test:comprehensive 2>/dev/null || npm test 2>/dev/null || echo "No test script found"',
146
+ medium:
147
+ 'npm run test:medium 2>/dev/null || npm test 2>/dev/null || echo "No test script found"',
148
+ fast: 'npm run test:fast 2>/dev/null || npm run test:unit 2>/dev/null || npm test 2>/dev/null || echo "No test script found"',
149
+ minimal: 'npm run lint && npm run format:check',
150
+ },
151
+ detection: () => true, // Fallback always matches
152
+ },
153
+ }
154
+
155
+ /**
156
+ * Read package.json from project path
157
+ */
158
+ function readPackageJson(projectPath) {
159
+ try {
160
+ const pkgPath = path.join(projectPath, 'package.json')
161
+ if (fs.existsSync(pkgPath)) {
162
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
163
+ }
164
+ } catch {
165
+ // Ignore errors
166
+ }
167
+ return null
168
+ }
169
+
170
+ /**
171
+ * Detect project type based on dependencies and structure
172
+ */
173
+ function detectProjectType(projectPath) {
174
+ // Check each project type in priority order
175
+ const typeOrder = ['saas', 'webapp', 'api', 'cli', 'library', 'docs']
176
+
177
+ for (const type of typeOrder) {
178
+ const config = PROJECT_CONFIGS[type]
179
+ if (config.detection(projectPath)) {
180
+ return type
181
+ }
182
+ }
183
+
184
+ return 'default'
185
+ }
186
+
187
+ /**
188
+ * Generate smart test strategy script for a project
189
+ */
190
+ function generateSmartStrategy(options = {}) {
191
+ const {
192
+ projectPath = process.cwd(),
193
+ projectName = path.basename(projectPath),
194
+ projectType = null, // Auto-detect if not provided
195
+ customHighRiskRegex = null,
196
+ customTestCommands = null,
197
+ } = options
198
+
199
+ // Detect or use provided project type
200
+ const detectedType = projectType || detectProjectType(projectPath)
201
+ const config = PROJECT_CONFIGS[detectedType] || PROJECT_CONFIGS.default
202
+
203
+ // Allow custom overrides
204
+ const highRiskRegex = customHighRiskRegex || config.highRiskRegex
205
+ const testCommands = { ...config.testCommands, ...customTestCommands }
206
+
207
+ // Read template
208
+ const templatePath = path.join(
209
+ __dirname,
210
+ '..',
211
+ 'templates',
212
+ 'scripts',
213
+ 'smart-test-strategy.sh'
214
+ )
215
+
216
+ if (!fs.existsSync(templatePath)) {
217
+ throw new Error(`Smart strategy template not found at ${templatePath}`)
218
+ }
219
+
220
+ let template = fs.readFileSync(templatePath, 'utf8')
221
+
222
+ // Replace placeholders
223
+ template = template.replace(/\{\{PROJECT_NAME\}\}/g, projectName)
224
+ template = template.replace(/\{\{HIGH_RISK_REGEX\}\}/g, highRiskRegex)
225
+ template = template.replace(
226
+ /\{\{HIGH_RISK_PATTERN\}\}/g,
227
+ `Project type: ${config.name}`
228
+ )
229
+ template = template.replace(
230
+ /\{\{TEST_COMPREHENSIVE\}\}/g,
231
+ testCommands.comprehensive
232
+ )
233
+ template = template.replace(/\{\{TEST_MEDIUM\}\}/g, testCommands.medium)
234
+ template = template.replace(/\{\{TEST_FAST\}\}/g, testCommands.fast)
235
+ template = template.replace(/\{\{TEST_MINIMAL\}\}/g, testCommands.minimal)
236
+ template = template.replace(
237
+ /\{\{COMPREHENSIVE_COMMAND\}\}/g,
238
+ `Runs: ${testCommands.comprehensive}`
239
+ )
240
+ template = template.replace(
241
+ /\{\{MEDIUM_COMMAND\}\}/g,
242
+ `Runs: ${testCommands.medium}`
243
+ )
244
+ template = template.replace(
245
+ /\{\{FAST_COMMAND\}\}/g,
246
+ `Runs: ${testCommands.fast}`
247
+ )
248
+ template = template.replace(
249
+ /\{\{MINIMAL_COMMAND\}\}/g,
250
+ `Runs: ${testCommands.minimal}`
251
+ )
252
+
253
+ return {
254
+ script: template,
255
+ projectType: detectedType,
256
+ projectTypeName: config.name,
257
+ highRiskRegex,
258
+ testCommands,
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Write smart strategy script to project
264
+ */
265
+ function writeSmartStrategy(projectPath, script) {
266
+ const scriptsDir = path.join(projectPath, 'scripts')
267
+ const scriptPath = path.join(scriptsDir, 'smart-test-strategy.sh')
268
+
269
+ // Create scripts directory if needed
270
+ if (!fs.existsSync(scriptsDir)) {
271
+ fs.mkdirSync(scriptsDir, { recursive: true })
272
+ }
273
+
274
+ // Write script
275
+ fs.writeFileSync(scriptPath, script)
276
+ fs.chmodSync(scriptPath, 0o755)
277
+
278
+ return scriptPath
279
+ }
280
+
281
+ /**
282
+ * Generate pre-push hook that uses smart strategy
283
+ */
284
+ function generateSmartPrePushHook() {
285
+ return `#!/bin/sh
286
+ . "$(dirname "$0")/_/husky.sh"
287
+
288
+ echo "šŸ” Running smart pre-push validation..."
289
+
290
+ # Check if smart test strategy script exists
291
+ if [ -f "scripts/smart-test-strategy.sh" ]; then
292
+ bash scripts/smart-test-strategy.sh
293
+ else
294
+ # Fallback to basic validation
295
+ echo "šŸ“ Linting..."
296
+ npm run lint || {
297
+ echo "āŒ Lint failed! Fix errors before pushing."
298
+ exit 1
299
+ }
300
+
301
+ echo "✨ Checking formatting..."
302
+ npm run format:check || {
303
+ echo "āŒ Format check failed! Run 'npm run format' to fix."
304
+ exit 1
305
+ }
306
+
307
+ # Run tests if they exist
308
+ if node -e "const pkg=require('./package.json');process.exit(pkg.scripts.test?0:1)" 2>/dev/null; then
309
+ echo "🧪 Running tests..."
310
+ npm test || {
311
+ echo "āŒ Tests failed! Fix failing tests before pushing."
312
+ exit 1
313
+ }
314
+ fi
315
+
316
+ echo "āœ… Pre-push validation passed!"
317
+ fi
318
+ `
319
+ }
320
+
321
+ /**
322
+ * Add test tier scripts to package.json
323
+ */
324
+ function getTestTierScripts(_projectType) {
325
+ // const config = PROJECT_CONFIGS[projectType] || PROJECT_CONFIGS.default
326
+
327
+ return {
328
+ 'test:fast': 'vitest run --reporter=basic --coverage=false',
329
+ 'test:medium':
330
+ 'vitest run --reporter=basic --testPathIgnorePatterns=e2e,integration',
331
+ 'test:comprehensive':
332
+ 'vitest run && npm run lint && npm run format:check && npm run security:audit 2>/dev/null || true',
333
+ 'test:smart': 'bash scripts/smart-test-strategy.sh',
334
+ }
335
+ }
336
+
337
+ module.exports = {
338
+ PROJECT_CONFIGS,
339
+ detectProjectType,
340
+ generateSmartStrategy,
341
+ writeSmartStrategy,
342
+ generateSmartPrePushHook,
343
+ getTestTierScripts,
344
+ }
@@ -0,0 +1,323 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Telemetry module for usage tracking (opt-in only)
5
+ *
6
+ * Privacy principles:
7
+ * - Completely opt-in (ENV var or explicit flag)
8
+ * - No personal information collected (no paths, usernames, IPs)
9
+ * - Local storage only (no network calls)
10
+ * - Easy to inspect and delete
11
+ * - Anonymous session IDs
12
+ *
13
+ * Data collected:
14
+ * - Event types (setup_started, setup_completed, setup_failed)
15
+ * - Timestamps
16
+ * - Template selection (custom vs default)
17
+ * - Features used (validate, deps, dry-run, interactive)
18
+ * - Node version and OS platform
19
+ * - Error types (if failed)
20
+ * - Setup duration
21
+ */
22
+
23
+ const fs = require('fs')
24
+ const path = require('path')
25
+ const os = require('os')
26
+ const crypto = require('crypto')
27
+ const { REPORTING_LIMITS } = require('../config/constants')
28
+
29
+ const getTelemetryDir = () =>
30
+ process.env.QAA_TELEMETRY_DIR ||
31
+ path.join(os.homedir(), '.create-qa-architect')
32
+ const getTelemetryFile = () => path.join(getTelemetryDir(), 'telemetry.json')
33
+ const TELEMETRY_FILE = getTelemetryFile()
34
+ const MAX_EVENTS = REPORTING_LIMITS.MAX_TELEMETRY_EVENTS
35
+
36
+ /**
37
+ * Check if telemetry is enabled
38
+ * Opt-in via environment variable or explicit flag
39
+ */
40
+ function isTelemetryEnabled() {
41
+ // Check environment variable
42
+ const envEnabled =
43
+ process.env.QAA_TELEMETRY === 'true' || process.env.QAA_TELEMETRY === '1'
44
+
45
+ // Check config file (future: allow persistent opt-in)
46
+ // For now, only ENV var
47
+
48
+ return envEnabled
49
+ }
50
+
51
+ /**
52
+ * Generate anonymous session ID
53
+ */
54
+ function generateSessionId() {
55
+ return crypto.randomBytes(16).toString('hex')
56
+ }
57
+
58
+ /**
59
+ * Ensure telemetry directory exists
60
+ */
61
+ function ensureTelemetryDir() {
62
+ const dir = getTelemetryDir()
63
+ if (!fs.existsSync(dir)) {
64
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Load existing telemetry data
70
+ */
71
+ function loadTelemetryData() {
72
+ try {
73
+ const file = getTelemetryFile()
74
+ if (fs.existsSync(file)) {
75
+ const data = fs.readFileSync(file, 'utf8')
76
+ return JSON.parse(data)
77
+ }
78
+ } catch {
79
+ // If corrupted, start fresh
80
+ console.warn('āš ļø Telemetry data corrupted, starting fresh')
81
+ }
82
+
83
+ return {
84
+ version: 1,
85
+ events: [],
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Save telemetry data (with rotation)
91
+ */
92
+ function saveTelemetryData(data) {
93
+ try {
94
+ ensureTelemetryDir()
95
+ const file = getTelemetryFile()
96
+
97
+ // Rotate: keep only last MAX_EVENTS
98
+ if (data.events.length > MAX_EVENTS) {
99
+ data.events = data.events.slice(-MAX_EVENTS)
100
+ }
101
+
102
+ fs.writeFileSync(file, JSON.stringify(data, null, 2), {
103
+ mode: 0o600, // Owner read/write only
104
+ })
105
+ } catch (error) {
106
+ // Silently fail - telemetry should never break the tool
107
+ // Only log in debug mode
108
+ if (process.env.DEBUG) {
109
+ console.error('Telemetry save error:', error.message)
110
+ }
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Record a telemetry event
116
+ *
117
+ * @param {string} eventType - Event type (setup_started, setup_completed, setup_failed)
118
+ * @param {object} metadata - Additional event metadata
119
+ * @param {string} sessionId - Session ID for grouping related events
120
+ */
121
+ function recordEvent(eventType, metadata = {}, sessionId = null) {
122
+ if (!isTelemetryEnabled()) {
123
+ return
124
+ }
125
+
126
+ try {
127
+ const data = loadTelemetryData()
128
+
129
+ const event = {
130
+ eventType,
131
+ timestamp: new Date().toISOString(),
132
+ sessionId: sessionId || generateSessionId(),
133
+ metadata: {
134
+ nodeVersion: process.version,
135
+ platform: os.platform(),
136
+ arch: os.arch(),
137
+ ...metadata,
138
+ },
139
+ }
140
+
141
+ data.events.push(event)
142
+ saveTelemetryData(data)
143
+ } catch (error) {
144
+ // Silently fail - telemetry should never break the tool
145
+ if (process.env.DEBUG) {
146
+ console.error('Telemetry record error:', error.message)
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Create a telemetry session for tracking related events
153
+ */
154
+ class TelemetrySession {
155
+ constructor() {
156
+ this.sessionId = generateSessionId()
157
+ this.startTime = Date.now()
158
+ this.enabled = isTelemetryEnabled()
159
+ this.telemetryFile = getTelemetryFile()
160
+ }
161
+
162
+ /**
163
+ * Record setup start event
164
+ */
165
+ recordStart(metadata = {}) {
166
+ if (!this.enabled) return
167
+
168
+ recordEvent(
169
+ 'setup_started',
170
+ {
171
+ ...metadata,
172
+ timestamp: new Date(this.startTime).toISOString(),
173
+ },
174
+ this.sessionId
175
+ )
176
+ }
177
+
178
+ /**
179
+ * Record setup completion event
180
+ */
181
+ recordComplete(metadata = {}) {
182
+ if (!this.enabled) return
183
+
184
+ const duration = Date.now() - this.startTime
185
+
186
+ recordEvent(
187
+ 'setup_completed',
188
+ {
189
+ ...metadata,
190
+ durationMs: duration,
191
+ durationSec: Math.round(duration / 1000),
192
+ },
193
+ this.sessionId
194
+ )
195
+ }
196
+
197
+ /**
198
+ * Record setup failure event
199
+ */
200
+ recordFailure(error, metadata = {}) {
201
+ if (!this.enabled) return
202
+
203
+ const duration = Date.now() - this.startTime
204
+
205
+ recordEvent(
206
+ 'setup_failed',
207
+ {
208
+ ...metadata,
209
+ errorType: error?.constructor?.name || 'Unknown',
210
+ errorMessage: error?.message || 'Unknown error',
211
+ durationMs: duration,
212
+ },
213
+ this.sessionId
214
+ )
215
+ }
216
+
217
+ /**
218
+ * Record validation event
219
+ */
220
+ recordValidation(validationType, passed, metadata = {}) {
221
+ if (!this.enabled) return
222
+
223
+ recordEvent(
224
+ 'validation',
225
+ {
226
+ ...metadata,
227
+ validationType,
228
+ passed,
229
+ },
230
+ this.sessionId
231
+ )
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Get telemetry statistics (for debugging/testing)
237
+ */
238
+ function getTelemetryStats() {
239
+ const data = loadTelemetryData()
240
+
241
+ const stats = {
242
+ totalEvents: data.events.length,
243
+ eventTypes: {},
244
+ platforms: {},
245
+ nodeVersions: {},
246
+ recentEvents: data.events.slice(-10),
247
+ }
248
+
249
+ data.events.forEach(event => {
250
+ // Count event types
251
+ stats.eventTypes[event.eventType] =
252
+ (stats.eventTypes[event.eventType] || 0) + 1
253
+
254
+ // Count platforms
255
+ const platform = event.metadata?.platform || 'unknown'
256
+ stats.platforms[platform] = (stats.platforms[platform] || 0) + 1
257
+
258
+ // Count Node versions
259
+ const nodeVersion = event.metadata?.nodeVersion || 'unknown'
260
+ stats.nodeVersions[nodeVersion] = (stats.nodeVersions[nodeVersion] || 0) + 1
261
+ })
262
+
263
+ return stats
264
+ }
265
+
266
+ /**
267
+ * Clear all telemetry data
268
+ */
269
+ function clearTelemetry() {
270
+ try {
271
+ if (fs.existsSync(TELEMETRY_FILE)) {
272
+ fs.unlinkSync(TELEMETRY_FILE)
273
+ return true
274
+ }
275
+ return false
276
+ } catch (error) {
277
+ console.error('Failed to clear telemetry:', error.message)
278
+ return false
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Show telemetry status and opt-in instructions
284
+ */
285
+ function showTelemetryStatus() {
286
+ const enabled = isTelemetryEnabled()
287
+
288
+ console.log('\nšŸ“Š Telemetry Status')
289
+ console.log('─'.repeat(50))
290
+ console.log(
291
+ `Status: ${enabled ? 'āœ… Enabled' : 'āŒ Disabled (opt-in required)'}`
292
+ )
293
+
294
+ if (enabled) {
295
+ const stats = getTelemetryStats()
296
+ console.log(`Events collected: ${stats.totalEvents}`)
297
+ console.log(`Storage: ${TELEMETRY_FILE}`)
298
+ } else {
299
+ console.log('\nTo enable telemetry (opt-in):')
300
+ console.log(' export QAA_TELEMETRY=true')
301
+ console.log(' # or add to ~/.bashrc or ~/.zshrc')
302
+ console.log('\nWhy enable telemetry?')
303
+ console.log(' - Helps improve the tool based on real usage patterns')
304
+ console.log(' - All data stays local (no network calls)')
305
+ console.log(' - No personal information collected')
306
+ console.log(
307
+ ' - Easy to inspect: cat ~/.create-qa-architect/telemetry.json'
308
+ )
309
+ console.log(' - Easy to delete: rm ~/.create-qa-architect/telemetry.json')
310
+ }
311
+
312
+ console.log('─'.repeat(50))
313
+ }
314
+
315
+ module.exports = {
316
+ isTelemetryEnabled,
317
+ recordEvent,
318
+ TelemetrySession,
319
+ getTelemetryStats,
320
+ clearTelemetry,
321
+ showTelemetryStatus,
322
+ TELEMETRY_FILE,
323
+ }