create-qa-architect 5.0.1 → 5.0.6

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.
@@ -0,0 +1,108 @@
1
+ # Preflight Review: QA Architect (create-qa-architect)
2
+
3
+ **Depth**: standard
4
+ **Date**: 2025-12-09
5
+ **Version**: 5.0.2
6
+
7
+ ---
8
+
9
+ ## Overall Status: ✅ PASS
10
+
11
+ All critical launch blockers pass. Minor issues documented below are acceptable for npm package release.
12
+
13
+ ---
14
+
15
+ ## Critical Issues (P0) - Must Fix
16
+
17
+ | Issue | Category | Location | Fix |
18
+ | ----- | -------- | -------- | --- |
19
+ | None | - | - | - |
20
+
21
+ ---
22
+
23
+ ## Important Issues (P1) - Should Fix
24
+
25
+ | Issue | Category | Location | Recommendation |
26
+ | ------------------------ | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------ |
27
+ | Gitleaks false positives | Security | tests/\*.test.js | Test fixtures use fake API key patterns (QAA-XXXX format); not real secrets. Consider adding `.gitleaksignore` for test files. |
28
+ | npm version mismatch | Release | package.json | Local 5.0.2, npm shows 5.0.1. Publish pending or recently published. |
29
+
30
+ ---
31
+
32
+ ## P0 Functional Checks
33
+
34
+ | Check | Status | Notes |
35
+ | ----------------- | ------ | ---------------------- |
36
+ | All tests passing | ✅ | Full test suite passes |
37
+ | npm audit | ✅ | 0 vulnerabilities |
38
+ | ESLint | ✅ | No errors |
39
+ | Build/validation | ✅ | Core validation passes |
40
+
41
+ ---
42
+
43
+ ## P0 Security Checks
44
+
45
+ | Check | Status | Notes |
46
+ | ------------------------- | ------ | -------------------------------------------------------------------- |
47
+ | npm audit (high/critical) | ✅ | 0 vulnerabilities found |
48
+ | Hardcoded secrets scan | ⚠️ | 4 findings - all in test files with fake keys (QAA-1234-XXXX format) |
49
+ | No production secrets | ✅ | No `.env` files, no real API keys |
50
+
51
+ ---
52
+
53
+ ## Product Packaging
54
+
55
+ | Item | Status | Notes |
56
+ | ------------ | ------ | ----------------------- |
57
+ | CHANGELOG.md | ✅ | Present |
58
+ | LICENSE | ✅ | Present |
59
+ | README.md | ✅ | Present |
60
+ | .env.example | N/A | Not needed for CLI tool |
61
+ | Version tags | ✅ | v4.3.0 - v5.0.2 |
62
+ | Git status | ✅ | Clean working tree |
63
+
64
+ ---
65
+
66
+ ## Quality Violations
67
+
68
+ | Type | Count | Assessment |
69
+ | ----------------------- | ----- | ------------------------------------------------------------ |
70
+ | eslint-disable comments | 24 | All have security justification comments explaining why safe |
71
+ | any types | 0 | JavaScript project, N/A |
72
+
73
+ **Note**: The `eslint-disable` comments are all for security-related ESLint rules (detect-unsafe-regex, detect-non-literal-fs-filename, detect-object-injection) and each includes a detailed safety justification explaining why the pattern is safe in context.
74
+
75
+ ---
76
+
77
+ ## Silent Killer Check (N/A for npm package)
78
+
79
+ | Integration | Status | Notes |
80
+ | --------------- | ------ | --------------------- |
81
+ | Stripe webhooks | N/A | CLI tool, no webhooks |
82
+ | OAuth redirects | N/A | CLI tool |
83
+ | API keys | N/A | CLI tool |
84
+ | CORS config | N/A | CLI tool |
85
+
86
+ ---
87
+
88
+ ## Next Steps
89
+
90
+ 1. **Optional**: Add `.gitleaksignore` to exclude test files with fake license keys
91
+ 2. **Verify**: Confirm npm publish completed for 5.0.2 (may be propagating)
92
+ 3. **Ready**: Proceed with launch/release announcement
93
+
94
+ ---
95
+
96
+ ## Recommendation
97
+
98
+ **✅ CLEARED FOR LAUNCH**
99
+
100
+ This is an npm CLI package, not a web application. All critical checks pass:
101
+
102
+ - Tests passing
103
+ - No security vulnerabilities
104
+ - No real secrets
105
+ - Clean git state
106
+ - Proper versioning and packaging
107
+
108
+ The gitleaks findings are false positives on intentional test fixtures using fake license key formats.
package/docs/TESTING.md CHANGED
@@ -48,7 +48,7 @@ Use real packages from the ecosystem, not toy examples:
48
48
  const TOP_PYTHON_PACKAGES = [
49
49
  'django-cors-headers',
50
50
  'scikit-learn',
51
- 'pytest-cov'
51
+ 'pytest-cov',
52
52
  ]
53
53
  ```
54
54
 
@@ -59,4 +59,3 @@ Always run before release:
59
59
  ```bash
60
60
  npm run prerelease # Runs docs:check + all tests
61
61
  ```
62
-
@@ -1,10 +1,16 @@
1
1
  'use strict'
2
2
 
3
- const Ajv = require('ajv')
4
- const addFormats = require('ajv-formats')
3
+ const AjvImport = require('ajv')
4
+ const addFormatsImport = require('ajv-formats')
5
5
  const fs = require('fs')
6
6
  const path = require('path')
7
7
 
8
+ // Handle CJS/ESM interop for Ajv and ajv-formats in JS type-checking
9
+ const Ajv = /** @type {any} */ (AjvImport.default || AjvImport)
10
+ const addFormats = /** @type {(ajv: any) => void} */ (
11
+ addFormatsImport.default || addFormatsImport
12
+ )
13
+
8
14
  function validateQualityConfig(configPath) {
9
15
  const result = {
10
16
  valid: false,
@@ -289,7 +289,7 @@ function matchesPattern(depName, pattern) {
289
289
  if (!regex) {
290
290
  // Cache miss - compile and store
291
291
  const regexPattern = pattern.replace(/\*/g, '.*')
292
- // eslint-disable-next-line security/detect-non-literal-regexp
292
+ // eslint-disable-next-line security/detect-non-literal-regexp -- Safe: pattern from internal config, only allows * wildcards replaced with .*, anchored with ^$
293
293
  regex = new RegExp(`^${regexPattern}$`)
294
294
 
295
295
  // Implement size-limited cache with FIFO eviction
@@ -348,7 +348,7 @@ function generateReactGroups(_frameworkInfo) {
348
348
  /**
349
349
  * Generate dependency groups for Vue ecosystem
350
350
  *
351
- * @param {Object} frameworkInfo - Vue detection results
351
+ * @param {Object} _frameworkInfo - Vue detection results
352
352
  * @returns {Object} Dependabot groups configuration
353
353
  */
354
354
  function generateVueGroups(_frameworkInfo) {
@@ -376,7 +376,7 @@ function generateVueGroups(_frameworkInfo) {
376
376
  /**
377
377
  * Generate dependency groups for Angular ecosystem
378
378
  *
379
- * @param {Object} frameworkInfo - Angular detection results
379
+ * @param {Object} _frameworkInfo - Angular detection results
380
380
  * @returns {Object} Dependabot groups configuration
381
381
  */
382
382
  function generateAngularGroups(_frameworkInfo) {
@@ -404,7 +404,7 @@ function generateAngularGroups(_frameworkInfo) {
404
404
  /**
405
405
  * Generate dependency groups for testing frameworks
406
406
  *
407
- * @param {Object} frameworkInfo - Testing framework detection results
407
+ * @param {Object} _frameworkInfo - Testing framework detection results
408
408
  * @returns {Object} Dependabot groups configuration
409
409
  */
410
410
  function generateTestingGroups(_frameworkInfo) {
@@ -428,7 +428,7 @@ function generateTestingGroups(_frameworkInfo) {
428
428
  /**
429
429
  * Generate dependency groups for build tools
430
430
  *
431
- * @param {Object} frameworkInfo - Build tool detection results
431
+ * @param {Object} _frameworkInfo - Build tool detection results
432
432
  * @returns {Object} Dependabot groups configuration
433
433
  */
434
434
  function generateBuildToolGroups(_frameworkInfo) {
@@ -446,7 +446,7 @@ function generateBuildToolGroups(_frameworkInfo) {
446
446
  /**
447
447
  * Generate Storybook dependency groups
448
448
  *
449
- * @param {Object} frameworkInfo - Storybook detection results
449
+ * @param {Object} _frameworkInfo - Storybook detection results
450
450
  * @returns {Object} Dependabot groups configuration
451
451
  */
452
452
  function generateStorybookGroups(_frameworkInfo) {
@@ -490,7 +490,7 @@ function hasPythonProject(projectPath) {
490
490
  * attacks with maliciously large requirements files.
491
491
  *
492
492
  * @param {string} requirementsPath - Path to requirements.txt file
493
- * @returns {Object<string, string>} Map of package names to version specifiers
493
+ * @returns {Record<string, string>} Map of package names to version specifiers
494
494
  * @throws {Error} If file exceeds MAX_REQUIREMENTS_FILE_SIZE
495
495
  *
496
496
  * @example
@@ -512,6 +512,7 @@ function parsePipRequirements(requirementsPath) {
512
512
  }
513
513
 
514
514
  const content = fs.readFileSync(requirementsPath, 'utf8')
515
+ /** @type {Record<string, string>} */
515
516
  const dependencies = {}
516
517
 
517
518
  content.split('\n').forEach(line => {
@@ -529,7 +530,7 @@ function parsePipRequirements(requirementsPath) {
529
530
  // Support dotted names (zope.interface), hyphens (pytest-cov), underscores (google_cloud)
530
531
  // Also handle extras like fastapi[all] by capturing everything before the bracket
531
532
  // Fixed: Replaced (.*) with ([^\s]*) to prevent catastrophic backtracking
532
- // eslint-disable-next-line security/detect-unsafe-regex
533
+ // eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded character classes [\w.-], [\w,\s-], [^\s], anchored ^$, no nested quantifiers
533
534
  const match = line.match(/^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/)
534
535
  if (match) {
535
536
  const [, name, _extras, operator, version] = match
@@ -552,12 +553,13 @@ function parsePipRequirements(requirementsPath) {
552
553
  */
553
554
  function parsePyprojectToml(pyprojectPath) {
554
555
  const content = fs.readFileSync(pyprojectPath, 'utf8')
556
+ /** @type {Record<string, string>} */
555
557
  const dependencies = {}
556
558
 
557
559
  // Parse PEP 621 list-style dependencies: dependencies = ["package>=1.0.0", ...]
558
560
  // Match main dependencies array: dependencies = [...]
559
561
  // Allow optional whitespace/comments after ] to handle: ] # end of deps
560
- // eslint-disable-next-line security/detect-unsafe-regex
562
+ // eslint-disable-next-line security/detect-unsafe-regex -- Safe: lazy quantifier *? prevents backtracking, anchored ^$, bounded alternation
561
563
  const mainDepPattern = /^dependencies\s*=\s*\[([\s\S]*?)\]\s*(?:#.*)?$/m
562
564
  const mainMatch = mainDepPattern.exec(content)
563
565
 
@@ -574,7 +576,7 @@ function parsePyprojectToml(pyprojectPath) {
574
576
  // Support dotted names, hyphens, underscores, and extras
575
577
  // Fixed: Replaced ($|.*) with ([^\s]*) to prevent catastrophic backtracking
576
578
  const match = depString.match(
577
- /^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/ // eslint-disable-line security/detect-unsafe-regex
579
+ /^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/ // eslint-disable-line security/detect-unsafe-regex -- Safe: bounded character classes, anchored ^$, no nested quantifiers
578
580
  )
579
581
  if (match) {
580
582
  const [, name, _extras, operator, version] = match
@@ -606,7 +608,7 @@ function parsePyprojectToml(pyprojectPath) {
606
608
  const depString = pkgMatch[1].trim()
607
609
 
608
610
  const match = depString.match(
609
- /^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?($|.*)$/ // eslint-disable-line security/detect-unsafe-regex
611
+ /^([\w.-]+)(\[[\w,\s-]+\])?([><=!~]+)?([^\s]*)$/ // eslint-disable-line security/detect-unsafe-regex -- Safe: bounded character classes, anchored ^$, no nested quantifiers
610
612
  )
611
613
  if (match) {
612
614
  const [, name, _extras, operator, version] = match
@@ -829,7 +831,7 @@ function parseCargoToml(cargoPath) {
829
831
  if (!trimmed || trimmed.startsWith('#')) continue
830
832
 
831
833
  // Match simple pattern: name = "version"
832
- // eslint-disable-next-line security/detect-unsafe-regex
834
+ // eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded groups \w+, [^"']+, anchored ^, no nested quantifiers
833
835
  const simpleMatch = trimmed.match(/^(\w+(?:-\w+)*)\s*=\s*["']([^"']+)["']/)
834
836
  if (simpleMatch) {
835
837
  const [, name, version] = simpleMatch
@@ -840,7 +842,7 @@ function parseCargoToml(cargoPath) {
840
842
 
841
843
  // Match complex pattern: name = { version = "...", ... }
842
844
  const complexMatch = trimmed.match(
843
- // eslint-disable-next-line security/detect-unsafe-regex
845
+ // eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded negated class [^}]*, anchored ^, no nested quantifiers
844
846
  /^(\w+(?:-\w+)*)\s*=\s*\{[^}]*version\s*=\s*["']([^"']+)["']/
845
847
  )
846
848
  if (complexMatch) {
@@ -970,7 +972,7 @@ function parseGemfile(gemfilePath) {
970
972
 
971
973
  // Match: gem 'rails', '~> 7.0' or gem 'rails'
972
974
  const gemMatch = trimmed.match(
973
- // eslint-disable-next-line security/detect-unsafe-regex
975
+ // eslint-disable-next-line security/detect-unsafe-regex -- Safe: bounded negated class [^'"]+, no nested quantifiers, processed line-by-line
974
976
  /gem\s+['"]([^'"]+)['"]\s*(?:,\s*['"]([^'"]+)['"])?/
975
977
  )
976
978
  if (gemMatch) {
@@ -1270,11 +1272,11 @@ function generateBundlerGroups(bundlerFrameworks) {
1270
1272
  /**
1271
1273
  * Generate premium Dependabot configuration with multi-language framework-aware grouping
1272
1274
  *
1273
- * @param {Object} options - Configuration options
1274
- * @param {string} options.projectPath - Path to project directory
1275
- * @param {string} options.schedule - Update schedule (daily, weekly, monthly)
1276
- * @param {string} options.day - Day of week for updates
1277
- * @param {string} options.time - Time for updates
1275
+ * @param {Object} [options] - Configuration options
1276
+ * @param {string} [options.projectPath='.'] - Path to project directory
1277
+ * @param {string} [options.schedule='weekly'] - Update schedule (daily, weekly, monthly)
1278
+ * @param {string} [options.day='monday'] - Day of week for updates
1279
+ * @param {string} [options.time='09:00'] - Time for updates
1278
1280
  * @returns {Object|null} Dependabot configuration object or null if not licensed
1279
1281
  */
1280
1282
  function generatePremiumDependabotConfig(options = {}) {
@@ -0,0 +1,249 @@
1
+ /**
2
+ * GitHub API Integration for QA Architect
3
+ * Enables Dependabot alerts and security features via GitHub API
4
+ */
5
+
6
+ const https = require('https')
7
+ const { execSync } = require('child_process')
8
+
9
+ /**
10
+ * Get GitHub token from environment or gh CLI
11
+ */
12
+ function getGitHubToken() {
13
+ // Check environment variable first
14
+ if (process.env.GITHUB_TOKEN) {
15
+ return process.env.GITHUB_TOKEN
16
+ }
17
+
18
+ // Try to get from gh CLI
19
+ try {
20
+ const token = execSync('gh auth token', { encoding: 'utf8' }).trim()
21
+ if (token) return token
22
+ } catch {
23
+ // gh CLI not available or not authenticated
24
+ }
25
+
26
+ return null
27
+ }
28
+
29
+ /**
30
+ * Get repository info from git remote
31
+ */
32
+ function getRepoInfo(projectPath = '.') {
33
+ try {
34
+ const remoteUrl = execSync('git remote get-url origin', {
35
+ cwd: projectPath,
36
+ encoding: 'utf8',
37
+ }).trim()
38
+
39
+ // Parse GitHub URL (https or ssh format)
40
+ const httpsMatch = remoteUrl.match(
41
+ /github\.com[/:]([^/]+)\/([^/.]+)(\.git)?$/
42
+ )
43
+ if (httpsMatch) {
44
+ return { owner: httpsMatch[1], repo: httpsMatch[2] }
45
+ }
46
+
47
+ return null
48
+ } catch {
49
+ return null
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Make GitHub API request
55
+ */
56
+ function githubRequest(method, path, token, data = null) {
57
+ return new Promise((resolve, reject) => {
58
+ const options = {
59
+ hostname: 'api.github.com',
60
+ port: 443,
61
+ path: path,
62
+ method: method,
63
+ headers: {
64
+ Authorization: `Bearer ${token}`,
65
+ Accept: 'application/vnd.github+json',
66
+ 'User-Agent': 'qa-architect',
67
+ 'X-GitHub-Api-Version': '2022-11-28',
68
+ },
69
+ }
70
+
71
+ if (data) {
72
+ options.headers['Content-Type'] = 'application/json'
73
+ }
74
+
75
+ const req = https.request(options, res => {
76
+ let body = ''
77
+ res.on('data', chunk => (body += chunk))
78
+ res.on('end', () => {
79
+ if (res.statusCode >= 200 && res.statusCode < 300) {
80
+ resolve({
81
+ status: res.statusCode,
82
+ data: body ? JSON.parse(body) : null,
83
+ })
84
+ } else if (res.statusCode === 204) {
85
+ resolve({ status: 204, data: null })
86
+ } else {
87
+ reject(
88
+ new Error(
89
+ `GitHub API error: ${res.statusCode} - ${body || res.statusMessage}`
90
+ )
91
+ )
92
+ }
93
+ })
94
+ })
95
+
96
+ req.on('error', reject)
97
+
98
+ if (data) {
99
+ req.write(JSON.stringify(data))
100
+ }
101
+ req.end()
102
+ })
103
+ }
104
+
105
+ /**
106
+ * Check if Dependabot alerts are enabled
107
+ */
108
+ async function checkDependabotStatus(owner, repo, token) {
109
+ try {
110
+ await githubRequest(
111
+ 'GET',
112
+ `/repos/${owner}/${repo}/vulnerability-alerts`,
113
+ token
114
+ )
115
+ return true // 204 means enabled
116
+ } catch (error) {
117
+ if (error.message.includes('404')) {
118
+ return false // Not enabled
119
+ }
120
+ throw error
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Enable Dependabot alerts for a repository
126
+ */
127
+ async function enableDependabotAlerts(owner, repo, token) {
128
+ try {
129
+ await githubRequest(
130
+ 'PUT',
131
+ `/repos/${owner}/${repo}/vulnerability-alerts`,
132
+ token
133
+ )
134
+ return { success: true, message: 'Dependabot alerts enabled' }
135
+ } catch (error) {
136
+ return { success: false, message: error.message }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Enable Dependabot security updates
142
+ */
143
+ async function enableDependabotSecurityUpdates(owner, repo, token) {
144
+ try {
145
+ await githubRequest(
146
+ 'PUT',
147
+ `/repos/${owner}/${repo}/automated-security-fixes`,
148
+ token
149
+ )
150
+ return { success: true, message: 'Dependabot security updates enabled' }
151
+ } catch (error) {
152
+ return { success: false, message: error.message }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Full setup: Enable all Dependabot features
158
+ */
159
+ async function setupDependabot(projectPath = '.', options = {}) {
160
+ const { verbose = false } = options
161
+ const results = {
162
+ success: false,
163
+ repoInfo: null,
164
+ alerts: null,
165
+ securityUpdates: null,
166
+ errors: [],
167
+ }
168
+
169
+ // Get token
170
+ const token = getGitHubToken()
171
+ if (!token) {
172
+ results.errors.push(
173
+ 'No GitHub token found. Set GITHUB_TOKEN env var or run `gh auth login`'
174
+ )
175
+ return results
176
+ }
177
+
178
+ // Get repo info
179
+ const repoInfo = getRepoInfo(projectPath)
180
+ if (!repoInfo) {
181
+ results.errors.push('Could not determine GitHub repository from git remote')
182
+ return results
183
+ }
184
+ results.repoInfo = repoInfo
185
+
186
+ if (verbose) {
187
+ console.log(`📦 Repository: ${repoInfo.owner}/${repoInfo.repo}`)
188
+ }
189
+
190
+ // Check current status
191
+ try {
192
+ const isEnabled = await checkDependabotStatus(
193
+ repoInfo.owner,
194
+ repoInfo.repo,
195
+ token
196
+ )
197
+ if (isEnabled) {
198
+ if (verbose) console.log('✅ Dependabot alerts already enabled')
199
+ results.alerts = { success: true, message: 'Already enabled' }
200
+ } else {
201
+ // Enable alerts
202
+ results.alerts = await enableDependabotAlerts(
203
+ repoInfo.owner,
204
+ repoInfo.repo,
205
+ token
206
+ )
207
+ if (verbose) {
208
+ console.log(
209
+ results.alerts.success
210
+ ? '✅ Dependabot alerts enabled'
211
+ : `❌ Failed to enable alerts: ${results.alerts.message}`
212
+ )
213
+ }
214
+ }
215
+ } catch (error) {
216
+ results.errors.push(`Alerts check failed: ${error.message}`)
217
+ }
218
+
219
+ // Enable security updates
220
+ try {
221
+ results.securityUpdates = await enableDependabotSecurityUpdates(
222
+ repoInfo.owner,
223
+ repoInfo.repo,
224
+ token
225
+ )
226
+ if (verbose) {
227
+ console.log(
228
+ results.securityUpdates.success
229
+ ? '✅ Dependabot security updates enabled'
230
+ : `⚠️ Security updates: ${results.securityUpdates.message}`
231
+ )
232
+ }
233
+ } catch (error) {
234
+ results.errors.push(`Security updates failed: ${error.message}`)
235
+ }
236
+
237
+ results.success = results.alerts?.success && results.errors.length === 0
238
+
239
+ return results
240
+ }
241
+
242
+ module.exports = {
243
+ getGitHubToken,
244
+ getRepoInfo,
245
+ checkDependabotStatus,
246
+ enableDependabotAlerts,
247
+ enableDependabotSecurityUpdates,
248
+ setupDependabot,
249
+ }
@@ -1,5 +1,9 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * @typedef {import('./prompt').InteractivePrompt} InteractivePrompt
5
+ */
6
+
3
7
  /**
4
8
  * Question definitions and answer parsing for interactive mode
5
9
  */
@@ -26,7 +26,7 @@ class LicenseValidator {
26
26
  // Allow enterprises to host their own registry
27
27
  this.licenseDbUrl =
28
28
  process.env.QAA_LICENSE_DB_URL ||
29
- 'https://license.aibuilderlab.com/qaa/legitimate-licenses.json'
29
+ 'https://vibebuildlab.com/api/licenses/qa-architect.json'
30
30
  }
31
31
 
32
32
  ensureLicenseDir() {
package/lib/licensing.js CHANGED
@@ -216,7 +216,7 @@ function isDeveloperMode() {
216
216
  if (fs.existsSync(developerMarkerFile)) {
217
217
  return true
218
218
  }
219
- } catch (_error) {
219
+ } catch {
220
220
  // Ignore errors checking for marker file
221
221
  }
222
222
 
@@ -338,7 +338,7 @@ function verifyLicenseSignature(payload, signature) {
338
338
  Buffer.from(signature),
339
339
  Buffer.from(expectedSignature)
340
340
  )
341
- } catch (_error) {
341
+ } catch {
342
342
  // If signature verification fails, treat as invalid
343
343
  return false
344
344
  }
@@ -396,7 +396,7 @@ function showUpgradeMessage(feature) {
396
396
  console.log('')
397
397
  console.log(' 🎁 Start 14-day free trial - no credit card required')
398
398
  console.log('')
399
- console.log('🚀 Upgrade: https://vibebuildlab.com/qaa')
399
+ console.log('🚀 Upgrade: https://vibebuildlab.com/tools/qa-architect')
400
400
  console.log(
401
401
  '🔑 Activate: npx create-qa-architect@latest --activate-license'
402
402
  )
@@ -414,7 +414,7 @@ function showUpgradeMessage(feature) {
414
414
  console.log(' ✅ Slack/email alerts for failures')
415
415
  console.log(' ✅ Priority support (business hours)')
416
416
  console.log('')
417
- console.log('👥 Upgrade: https://vibebuildlab.com/qaa/team')
417
+ console.log('👥 Upgrade: https://vibebuildlab.com/tools/qa-architect')
418
418
  } else if (license.tier === LICENSE_TIERS.TEAM) {
419
419
  console.log('\n🏢 Upgrade to ENTERPRISE - $249/month (annual) + onboarding')
420
420
  console.log('')
@@ -553,7 +553,7 @@ async function addLegitimateKey(
553
553
  if (fs.existsSync(legitimateDBFile)) {
554
554
  try {
555
555
  database = JSON.parse(fs.readFileSync(legitimateDBFile, 'utf8'))
556
- } catch (_error) {
556
+ } catch {
557
557
  console.error(
558
558
  'Warning: Could not parse existing database, creating new one'
559
559
  )
@@ -654,7 +654,7 @@ async function promptLicenseActivation() {
654
654
  console.log(
655
655
  ' If you purchased this license, please contact support at:'
656
656
  )
657
- console.log(' Email: support@aibuilderlab.com')
657
+ console.log(' Email: support@vibebuildlab.com')
658
658
  console.log(
659
659
  ' Include your license key and purchase email for verification.'
660
660
  )
@@ -745,7 +745,7 @@ function loadUsage() {
745
745
 
746
746
  return data
747
747
  }
748
- } catch (_error) {
748
+ } catch {
749
749
  // Ignore errors reading usage file
750
750
  }
751
751
 
@@ -770,7 +770,7 @@ function saveUsage(usage) {
770
770
  }
771
771
  fs.writeFileSync(getUsageFile(), JSON.stringify(usage, null, 2))
772
772
  return true
773
- } catch (_error) {
773
+ } catch {
774
774
  return false
775
775
  }
776
776
  }
@@ -958,7 +958,7 @@ function showLicenseStatus() {
958
958
  // Show upgrade path
959
959
  if (license.tier === LICENSE_TIERS.FREE) {
960
960
  console.log('\n💡 Upgrade to PRO for unlimited access + security scanning')
961
- console.log(' → https://vibebuildlab.com/qaa')
961
+ console.log(' → https://vibebuildlab.com/tools/qa-architect')
962
962
  }
963
963
  }
964
964
 
@@ -50,17 +50,17 @@ function mergeDevDependencies(initialDevDeps = {}, defaultDevDeps) {
50
50
 
51
51
  /**
52
52
  * Merge lint-staged configuration, preserving existing patterns
53
- * @param {Object} existing - Existing lint-staged config
54
- * @param {Object} defaults - Default lint-staged config
55
- * @param {Object} options - Merge options
56
- * @param {Function} patternChecker - Function to check if a pattern matches certain criteria
57
- * @returns {Object} Merged lint-staged config
53
+ * @param {Record<string, string|string[]>} [existing] - Existing lint-staged config
54
+ * @param {Record<string, string|string[]>} defaults - Default lint-staged config
55
+ * @param {{stylelintTargets?: string[]}} [options] - Merge options
56
+ * @param {(pattern: string) => boolean} [patternChecker] - Function to check if a pattern matches certain criteria
57
+ * @returns {Record<string, string|string[]>} Merged lint-staged config
58
58
  */
59
59
  function mergeLintStaged(
60
+ defaults = {},
60
61
  existing = {},
61
- defaults,
62
62
  options = {},
63
- patternChecker = null
63
+ patternChecker = _pattern => false
64
64
  ) {
65
65
  const merged = { ...existing }
66
66
  const stylelintTargets = options.stylelintTargets || []
@@ -88,7 +88,8 @@ function mergeLintStaged(
88
88
  : [merged[pattern]]
89
89
 
90
90
  const newCommands = [...existingCommands]
91
- commands.forEach(command => {
91
+ const commandList = Array.isArray(commands) ? commands : [commands]
92
+ commandList.forEach(command => {
92
93
  if (!newCommands.includes(command)) {
93
94
  newCommands.push(command)
94
95
  }
@@ -435,7 +435,7 @@ class ProjectMaturityDetector {
435
435
 
436
436
  /**
437
437
  * Generate GitHub Actions outputs for maturity detection
438
- * @returns {string} GitHub Actions output format
438
+ * @returns {{maturity: string, sourceCount: number, testCount: number, hasDeps: boolean, hasDocs: boolean, hasCss: boolean, requiredChecks: string, optionalChecks: string, disabledChecks: string}} GitHub Actions output format
439
439
  */
440
440
  generateGitHubActionsOutput() {
441
441
  const maturity = this.detect()