create-qa-architect 5.13.2 → 5.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,11 +1,11 @@
1
1
  VIBE BUILD LAB COMMERCIAL LICENSE
2
2
 
3
- Copyright (c) 2025 Vibe Build Lab LLC. All rights reserved.
3
+ Copyright (c) 2025 BuildProven. All rights reserved.
4
4
 
5
5
  COMMERCIAL SOFTWARE - FREEMIUM MODEL
6
6
 
7
7
  This software and associated documentation files (the "Software") are
8
- proprietary commercial products of Vibe Build Lab LLC.
8
+ proprietary commercial products of BuildProven.
9
9
 
10
10
  TERMS OF USE:
11
11
 
@@ -62,5 +62,5 @@ For licensing inquiries: support@buildproven.ai
62
62
 
63
63
  ---
64
64
 
65
- Vibe Build Lab LLC (d/b/a BuildProven)
65
+ BuildProven
66
66
  https://buildproven.ai
package/README.md CHANGED
@@ -7,7 +7,7 @@ Quality automation CLI for JavaScript/TypeScript, Python, and shell script proje
7
7
  ---
8
8
 
9
9
  > **Maintainer & Ownership**
10
- > This project is maintained by **Vibe Build Lab LLC (d/b/a BuildProven)**, a studio focused on AI-assisted product development, micro-SaaS, and "vibe coding" workflows for solo founders and small teams.
10
+ > This project is maintained by **BuildProven**, a studio focused on AI-assisted product development, micro-SaaS, and "vibe coding" workflows for solo founders and small teams.
11
11
  > Learn more at **https://buildproven.ai**.
12
12
 
13
13
  ---
@@ -158,7 +158,7 @@ npx create-qa-architect@latest --workflow-minimal
158
158
 
159
159
  **Best for:** Small teams, client projects, production apps
160
160
 
161
- - Matrix testing (Node 20 + 22) **only on main branch**
161
+ - Single Node 22 testing **only on main branch**
162
162
  - Security scans run monthly
163
163
  - Path filters enabled
164
164
  - **Runtime:** ~15-20 min/commit
@@ -276,6 +276,8 @@ npm install
276
276
  npm run lint
277
277
  ```
278
278
 
279
+ `--update` refreshes the existing `quality.yml` from the latest template while preserving the detected workflow tier and existing matrix setting unless you explicitly override the tier with `--workflow-minimal`, `--workflow-standard`, or `--workflow-comprehensive`.
280
+
279
281
  ### Dependency Monitoring (Free)
280
282
 
281
283
  ```bash
@@ -453,4 +455,4 @@ Commercial freemium license — the base CLI is free to use; Pro features requir
453
455
 
454
456
  ---
455
457
 
456
- > **Vibe Build Lab LLC (d/b/a BuildProven)** · [buildproven.ai](https://buildproven.ai)
458
+ > **BuildProven** · [buildproven.ai](https://buildproven.ai)
@@ -19,7 +19,7 @@ const baseScripts = {
19
19
  'test:coverage': 'vitest run --coverage',
20
20
  'test:changed': 'vitest run --changed HEAD~1 --passWithNoTests',
21
21
  'security:audit':
22
- '[ -f pnpm-lock.yaml ] && pnpm audit --audit-level high || [ -f yarn.lock ] && yarn audit || npm audit --audit-level high',
22
+ 'if [ -f pnpm-lock.yaml ]; then pnpm audit --audit-level high; elif [ -f yarn.lock ]; then yarn audit; else npm audit --audit-level high; fi',
23
23
  'security:secrets':
24
24
  "node -e \"const fs=require('fs');const content=fs.readFileSync('package.json','utf8');if(/[\\\"\\'][a-zA-Z0-9+/]{20,}[\\\"\\']/.test(content)){console.error('❌ Potential hardcoded secrets in package.json');process.exit(1)}else{console.log('✅ No secrets detected in package.json')}\"",
25
25
  'security:config': 'npx create-qa-architect@latest --security-config',
@@ -28,8 +28,7 @@ const baseScripts = {
28
28
  'validate:docs': 'npx create-qa-architect@latest --validate-docs',
29
29
  'validate:comprehensive': 'npx create-qa-architect@latest --comprehensive',
30
30
  'validate:all': 'npm run validate:comprehensive && npm run security:audit',
31
- 'validate:pre-push':
32
- 'npm run test:patterns --if-present && npm run test:commands --if-present && npm run test:changed --if-present || npm test --if-present',
31
+ 'validate:pre-push': `npm run test:patterns --if-present && npm run test:commands --if-present && if node -e "const pkg=require('./package.json');process.exit(pkg.scripts&&pkg.scripts['test:changed']?0:1)" 2>/dev/null; then npm run test:changed; else npm test --if-present; fi`,
33
32
  }
34
33
 
35
34
  const normalizeStylelintTargets = stylelintTargets => {
@@ -691,4 +691,4 @@ class LicenseValidator {
691
691
  }
692
692
  }
693
693
 
694
- module.exports = { LicenseValidator }
694
+ module.exports = { LicenseValidator, validateLicenseDir }
package/lib/licensing.js CHANGED
@@ -16,15 +16,16 @@ const {
16
16
  verifyPayload,
17
17
  loadKeyFromEnv,
18
18
  } = require('./license-signing')
19
+ const { validateLicenseDir } = require('./license-validator')
19
20
 
20
21
  // License storage locations
21
22
  // Support environment variable override for testing (like telemetry/error-reporter)
22
23
  // Use getter functions to allow env override before module load
23
24
  function getLicenseDir() {
24
- return (
25
+ const requestedDir =
25
26
  process.env.QAA_LICENSE_DIR ||
26
27
  path.join(os.homedir(), '.create-qa-architect')
27
- )
28
+ return validateLicenseDir(requestedDir)
28
29
  }
29
30
 
30
31
  function getLicenseFile() {
@@ -586,10 +587,7 @@ async function addLegitimateKey(
586
587
  }
587
588
 
588
589
  const normalizedKey = normalizeLicenseKey(licenseKey)
589
- // Use the same license directory as the CLI
590
- const licenseDir =
591
- process.env.QAA_LICENSE_DIR ||
592
- path.join(os.homedir(), '.create-qa-architect')
590
+ const licenseDir = getLicenseDir()
593
591
  const legitimateDBFile = path.join(licenseDir, 'legitimate-licenses.json')
594
592
  const privateKey = loadKeyFromEnv(
595
593
  process.env.LICENSE_REGISTRY_PRIVATE_KEY,
@@ -65,6 +65,37 @@ function detectExistingWorkflowMode(projectPath) {
65
65
  }
66
66
  }
67
67
 
68
+ /**
69
+ * Detect whether matrix testing is enabled in an existing workflow.
70
+ * @param {string} projectPath - Path to project
71
+ * @returns {boolean} True when matrix testing is enabled
72
+ */
73
+ function detectExistingMatrix(projectPath) {
74
+ const workflowPath = path.join(
75
+ projectPath,
76
+ '.github',
77
+ 'workflows',
78
+ 'quality.yml'
79
+ )
80
+
81
+ if (!fs.existsSync(workflowPath)) {
82
+ return false
83
+ }
84
+
85
+ try {
86
+ const content = fs.readFileSync(workflowPath, 'utf8')
87
+ return (
88
+ content.includes('# MATRIX_ENABLED: true') ||
89
+ content.includes('node-version: [20, 22]')
90
+ )
91
+ } catch (error) {
92
+ console.warn(
93
+ `⚠️ Could not detect existing matrix configuration: ${error.message}`
94
+ )
95
+ return false
96
+ }
97
+ }
98
+
68
99
  /**
69
100
  * Strip a named section from workflow content.
70
101
  * Removes everything between # {{NAME_BEGIN}} and # {{NAME_END}} markers (inclusive).
@@ -135,6 +166,39 @@ function removeTriggerPathsIgnore(content, triggerName) {
135
166
  return output.join('\n')
136
167
  }
137
168
 
169
+ /**
170
+ * Restrict the tests job to main branch pushes in standard mode.
171
+ * @param {string} content - Workflow content
172
+ * @returns {string} Updated content
173
+ */
174
+ function addStandardTestsBranchGate(content) {
175
+ const lines = content.split('\n')
176
+ const output = []
177
+ let inTestsJob = false
178
+ let branchGateInserted = false
179
+
180
+ for (const line of lines) {
181
+ if (line === ' tests:') {
182
+ inTestsJob = true
183
+ } else if (
184
+ inTestsJob &&
185
+ line.startsWith(' ') &&
186
+ !line.startsWith(' ')
187
+ ) {
188
+ inTestsJob = false
189
+ }
190
+
191
+ output.push(line)
192
+
193
+ if (inTestsJob && line === ' if: |' && !branchGateInserted) {
194
+ output.push(" github.ref == 'refs/heads/main' &&")
195
+ branchGateInserted = true
196
+ }
197
+ }
198
+
199
+ return output.join('\n')
200
+ }
201
+
138
202
  /**
139
203
  * Inject workflow mode-specific configuration into quality.yml
140
204
  * Uses section markers (# {{SECTION_BEGIN/END}}) for reliable content removal
@@ -162,19 +226,13 @@ function injectWorkflowMode(workflowContent, mode) {
162
226
 
163
227
  // Mode-specific transformations
164
228
  if (mode === 'standard') {
165
- // Standard: Add main branch condition to tests job
229
+ // Standard: run tests on main only to keep CI costs bounded.
166
230
  if (
167
- updated.includes('tests:') &&
168
- updated.includes('fromJSON(needs.detect-maturity.outputs.test-count)')
231
+ updated.includes(' tests:') &&
232
+ updated.includes(' if: |') &&
233
+ !updated.includes("github.ref == 'refs/heads/main'")
169
234
  ) {
170
- updated = updated.replace(
171
- /(tests:\s+runs-on:[^\n]+\s+needs:[^\n]+\s+if: \|\s*\n\s+)fromJSON\(needs\.detect-maturity\.outputs\.test-count\)/,
172
- "$1github.ref == 'refs/heads/main' &&\n fromJSON(needs.detect-maturity.outputs.test-count)"
173
- )
174
- updated = updated.replace(
175
- /(\s+tests:\s+runs-on:[^\n]+\s+needs:[^\n]+\s+)if: fromJSON\(needs\.detect-maturity\.outputs\.test-count\) > 0/,
176
- "$1if: github.ref == 'refs/heads/main' && fromJSON(needs.detect-maturity.outputs.test-count) > 0"
177
- )
235
+ updated = addStandardTestsBranchGate(updated)
178
236
  }
179
237
  } else if (mode === 'comprehensive') {
180
238
  // Comprehensive: Remove paths-ignore blocks
@@ -284,6 +342,7 @@ function injectMatrix(workflowContent, enableMatrix) {
284
342
 
285
343
  module.exports = {
286
344
  detectExistingWorkflowMode,
345
+ detectExistingMatrix,
287
346
  injectWorkflowMode,
288
347
  injectMatrix,
289
348
  stripSection,
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "create-qa-architect",
3
- "version": "5.13.2",
3
+ "version": "5.13.3",
4
4
  "description": "QA Architect - Bootstrap quality automation for JavaScript/TypeScript and Python projects with GitHub Actions, pre-commit hooks, linting, formatting, and smart test strategy",
5
5
  "main": "setup.js",
6
6
  "bin": {
7
- "create-qa-architect": "./setup.js"
7
+ "create-qa-architect": "setup.js"
8
8
  },
9
9
  "scripts": {
10
10
  "prepare": "[ \"$CI\" = \"true\" ] && echo 'Skipping Husky in CI' || husky",
@@ -80,7 +80,7 @@
80
80
  "dependency-monitoring",
81
81
  "security-audit"
82
82
  ],
83
- "author": "Brett Stark",
83
+ "author": "BuildProven",
84
84
  "license": "SEE LICENSE IN LICENSE",
85
85
  "files": [
86
86
  "setup.js",
package/setup.js CHANGED
@@ -125,6 +125,7 @@ const { runInteractiveFlow } = require('./lib/interactive/questions')
125
125
  const { TemplateLoader } = require('./lib/template-loader')
126
126
  const {
127
127
  detectExistingWorkflowMode,
128
+ detectExistingMatrix,
128
129
  injectWorkflowMode,
129
130
  injectMatrix,
130
131
  } = require('./lib/workflow-config')
@@ -693,7 +694,7 @@ Usage: npx create-qa-architect@latest [options]
693
694
  SETUP OPTIONS:
694
695
  (no args) Run complete quality automation setup
695
696
  --interactive Interactive mode with guided configuration prompts
696
- --update Update existing configuration
697
+ --update Update existing configuration (refreshes quality.yml, preserves detected workflow tier)
697
698
  --deps Add basic dependency monitoring (Free Tier)
698
699
  --dependency-monitoring Same as --deps
699
700
  --ci <provider> Select CI provider: github (default) | gitlab | circleci
@@ -703,7 +704,7 @@ SETUP OPTIONS:
703
704
  WORKFLOW TIERS (GitHub Actions optimization):
704
705
  --workflow-minimal Minimal CI (default) - Single Node version, monthly security
705
706
  Budget-first mode: detection-only CI (target <1000 min/month)
706
- --workflow-standard Standard CI - Matrix testing on main, monthly security
707
+ --workflow-standard Standard CI - Main-branch tests, monthly security
707
708
  ~15-20 min/commit, ~$5-20/mo for typical projects
708
709
  --workflow-comprehensive Comprehensive CI - Matrix on every push, security inline
709
710
  ~50-100 min/commit, ~$100-350/mo for typical projects
@@ -1651,6 +1652,16 @@ HELP:
1651
1652
  }
1652
1653
  }
1653
1654
 
1655
+ const hasExplicitWorkflowMode =
1656
+ isWorkflowMinimal || isWorkflowStandard || isWorkflowComprehensive
1657
+ const existingMatrixEnabled =
1658
+ fs.existsSync(workflowFile) &&
1659
+ !isMatrixEnabled &&
1660
+ !hasExplicitWorkflowMode
1661
+ ? detectExistingMatrix(process.cwd())
1662
+ : false
1663
+ const matrixEnabled = isMatrixEnabled || existingMatrixEnabled
1664
+
1654
1665
  if (!fs.existsSync(workflowFile)) {
1655
1666
  let templateWorkflow =
1656
1667
  templateLoader.getTemplate(
@@ -1666,7 +1677,7 @@ HELP:
1666
1677
  templateWorkflow = injectWorkflowMode(templateWorkflow, workflowMode)
1667
1678
 
1668
1679
  // Inject matrix testing if enabled (for library authors)
1669
- templateWorkflow = injectMatrix(templateWorkflow, isMatrixEnabled)
1680
+ templateWorkflow = injectMatrix(templateWorkflow, matrixEnabled)
1670
1681
 
1671
1682
  // Inject collaboration steps
1672
1683
  templateWorkflow = injectCollaborationSteps(templateWorkflow, {
@@ -1677,58 +1688,47 @@ HELP:
1677
1688
  fs.writeFileSync(workflowFile, templateWorkflow)
1678
1689
  console.log(`✅ Added GitHub Actions workflow (${workflowMode} mode)`)
1679
1690
  } else if (isUpdateMode) {
1680
- // Update existing workflow with new mode if explicitly specified
1681
- if (
1682
- isWorkflowMinimal ||
1683
- isWorkflowStandard ||
1684
- isWorkflowComprehensive
1685
- ) {
1686
- // Load fresh template and re-inject
1687
- let templateWorkflow =
1688
- templateLoader.getTemplate(
1689
- templates,
1690
- path.join('.github', 'workflows', 'quality.yml')
1691
- ) ||
1692
- fs.readFileSync(
1693
- path.join(__dirname, '.github/workflows/quality.yml'),
1694
- 'utf8'
1695
- )
1696
-
1697
- // Inject workflow mode configuration
1698
- templateWorkflow = injectWorkflowMode(
1699
- templateWorkflow,
1700
- workflowMode
1691
+ // Refresh existing workflow from the current template.
1692
+ let templateWorkflow =
1693
+ templateLoader.getTemplate(
1694
+ templates,
1695
+ path.join('.github', 'workflows', 'quality.yml')
1696
+ ) ||
1697
+ fs.readFileSync(
1698
+ path.join(__dirname, '.github/workflows/quality.yml'),
1699
+ 'utf8'
1701
1700
  )
1702
1701
 
1703
- // Inject matrix testing if enabled (for library authors)
1704
- templateWorkflow = injectMatrix(templateWorkflow, isMatrixEnabled)
1702
+ // Inject workflow mode configuration
1703
+ templateWorkflow = injectWorkflowMode(templateWorkflow, workflowMode)
1704
+
1705
+ // Inject matrix testing if enabled (for library authors)
1706
+ templateWorkflow = injectMatrix(templateWorkflow, matrixEnabled)
1705
1707
 
1706
- // Inject collaboration steps (preserve from existing if present)
1707
- const existingWorkflow = fs.readFileSync(workflowFile, 'utf8')
1708
- const hasSlackAlerts =
1709
- existingWorkflow.includes('SLACK_WEBHOOK_URL')
1710
- const hasPrComments = existingWorkflow.includes(
1711
- 'PR_COMMENT_PLACEHOLDER'
1712
- )
1708
+ // Inject collaboration steps (preserve from existing if present)
1709
+ const existingWorkflow = fs.readFileSync(workflowFile, 'utf8')
1710
+ const hasSlackAlerts = existingWorkflow.includes('SLACK_WEBHOOK_URL')
1711
+ const hasPrComments = existingWorkflow.includes(
1712
+ 'PR_COMMENT_PLACEHOLDER'
1713
+ )
1713
1714
 
1714
- templateWorkflow = injectCollaborationSteps(templateWorkflow, {
1715
- enableSlackAlerts: hasSlackAlerts,
1716
- enablePrComments: hasPrComments,
1717
- })
1715
+ templateWorkflow = injectCollaborationSteps(templateWorkflow, {
1716
+ enableSlackAlerts: hasSlackAlerts,
1717
+ enablePrComments: hasPrComments,
1718
+ })
1718
1719
 
1719
- fs.writeFileSync(workflowFile, templateWorkflow)
1720
- if (workflowMode === 'minimal') {
1721
- console.log(
1722
- '⚠️ Minimal mode disables tests and security scans in CI (detection-only).'
1723
- )
1724
- console.log(
1725
- ' Use --workflow-standard to re-enable full CI checks.'
1726
- )
1727
- }
1720
+ fs.writeFileSync(workflowFile, templateWorkflow)
1721
+ if (workflowMode === 'minimal') {
1728
1722
  console.log(
1729
- `♻️ Updated GitHub Actions workflow to ${workflowMode} mode`
1723
+ '⚠️ Minimal mode disables tests and security scans in CI (detection-only).'
1724
+ )
1725
+ console.log(
1726
+ ' Use --workflow-standard to re-enable full CI checks.'
1730
1727
  )
1731
1728
  }
1729
+ console.log(
1730
+ `♻️ Updated GitHub Actions workflow to ${workflowMode} mode`
1731
+ )
1732
1732
  }
1733
1733
  }
1734
1734
 
@@ -1918,8 +1918,26 @@ const fs = require('fs')
1918
1918
  const path = require('path')
1919
1919
  const os = require('os')
1920
1920
 
1921
- const licenseDir =
1921
+ function validateLicenseDir(dirPath) {
1922
+ const resolved = path.resolve(dirPath)
1923
+ const home = os.homedir()
1924
+ const tmp = os.tmpdir()
1925
+ const isInHome = resolved.startsWith(home + path.sep) || resolved === home
1926
+ const isInTmp = resolved.startsWith(tmp + path.sep) || resolved === tmp
1927
+
1928
+ if (!isInHome && !isInTmp) {
1929
+ console.warn(
1930
+ '⚠️ QAA_LICENSE_DIR must be within home or temp directory, ignoring: ' + dirPath
1931
+ )
1932
+ return path.join(home, '.create-qa-architect')
1933
+ }
1934
+
1935
+ return resolved
1936
+ }
1937
+
1938
+ const requestedLicenseDir =
1922
1939
  process.env.QAA_LICENSE_DIR || path.join(os.homedir(), '.create-qa-architect')
1940
+ const licenseDir = validateLicenseDir(requestedLicenseDir)
1923
1941
  const licenseFile = path.join(licenseDir, 'license.json')
1924
1942
  const usageFile = path.join(licenseDir, 'usage.json')
1925
1943
  const now = new Date()