@chemx/starter-kit 26.9.9-632 → 26.9.9-720

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/cli/audit.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ export interface HazardViolation {
2
+ filePath: string;
3
+ line: number;
4
+ hazard: string;
5
+ rule: string;
6
+ directive: string;
7
+ }
8
+
9
+ export interface AuditReport {
10
+ scannedFiles: number;
11
+ totalViolations: number;
12
+ violations: HazardViolation[];
13
+ }
14
+
15
+ export declare function auditFile(filePath: string, relativePath: string): HazardViolation[];
16
+ export declare function scanDirectory(targetDir: string, baseDir: string): HazardViolation[];
17
+ export declare function runAudit(targetDir?: string): AuditReport;
18
+
19
+ declare const _default: {
20
+ auditFile: typeof auditFile;
21
+ scanDirectory: typeof scanDirectory;
22
+ runAudit: typeof runAudit;
23
+ };
24
+
25
+ export default _default;
package/cli/audit.js ADDED
@@ -0,0 +1,253 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parse } from '@babel/parser';
4
+ import traverse from '@babel/traverse';
5
+ import * as t from '@babel/types';
6
+
7
+ const countLogicalOperators = (node) => {
8
+ let count = 0;
9
+ if (t.isLogicalExpression(node)) {
10
+ count += 1;
11
+ count += countLogicalOperators(node.left);
12
+ count += countLogicalOperators(node.right);
13
+ } else if (t.isUnaryExpression(node) && node.operator === '!') {
14
+ count += 1;
15
+ count += countLogicalOperators(node.argument);
16
+ }
17
+ return count;
18
+ };
19
+
20
+ const extractParseableCode = (content, ext) => {
21
+ if (ext === '.vue') {
22
+ const scriptMatch = content.match(/<script\b[^>]*>([\s\S]*?)<\/script>/i);
23
+ return scriptMatch ? scriptMatch[1] : '';
24
+ }
25
+ return content;
26
+ };
27
+
28
+ export const auditFile = (filePath, relativePath) => {
29
+ const violations = [];
30
+ const content = fs.readFileSync(filePath, 'utf-8');
31
+ const lines = content.split('\n');
32
+ const lineCount = lines.length;
33
+ const ext = path.extname(filePath);
34
+
35
+ // 1. Line Budget Checks
36
+ const isMolecule = relativePath.includes('molecules') || relativePath.includes('/m-');
37
+ if (lineCount > 500) {
38
+ violations.push({
39
+ filePath: relativePath,
40
+ line: 1,
41
+ hazard: `File line budget exceeded (${lineCount} > 500 lines)`,
42
+ rule: 'LINE_BUDGET_FILE',
43
+ directive: 'Decompose monolith into domain capsules and molecules'
44
+ });
45
+ } else if (isMolecule && lineCount > 100) {
46
+ violations.push({
47
+ filePath: relativePath,
48
+ line: 1,
49
+ hazard: `Molecule capsule budget exceeded (${lineCount} > 100 lines)`,
50
+ rule: 'LINE_BUDGET_MOLECULE',
51
+ directive: 'Split molecule into focused sub-molecules or extract state to hook'
52
+ });
53
+ }
54
+
55
+ const codeToParse = extractParseableCode(content, ext);
56
+ if (!codeToParse.trim()) {
57
+ return violations;
58
+ }
59
+
60
+ let ast;
61
+ try {
62
+ ast = parse(codeToParse, {
63
+ sourceType: 'module',
64
+ plugins: ['typescript', 'jsx']
65
+ });
66
+ } catch (err) {
67
+ const errorMsg = err instanceof Error ? err.message : String(err);
68
+ violations.push({
69
+ filePath: relativePath,
70
+ line: 1,
71
+ hazard: `Parse error: ${errorMsg}`,
72
+ rule: 'SYNTAX_PARSE_ERROR',
73
+ directive: 'Fix syntax errors before static analysis'
74
+ });
75
+ return violations;
76
+ }
77
+
78
+ const traverseFn = traverse.default || traverse;
79
+
80
+ traverseFn(ast, {
81
+ // 2. Hook Saturation Check
82
+ Function(astPath) {
83
+ let hookCount = 0;
84
+ astPath.traverse({
85
+ CallExpression(callPath) {
86
+ if (t.isIdentifier(callPath.node.callee) && /^use[A-Z0-9]/.test(callPath.node.callee.name)) {
87
+ if (callPath.getFunctionParent() === astPath) {
88
+ hookCount += 1;
89
+ }
90
+ }
91
+ }
92
+ });
93
+
94
+ if (hookCount > 5) {
95
+ const line = astPath.node.loc?.start.line || 1;
96
+ violations.push({
97
+ filePath: relativePath,
98
+ line,
99
+ hazard: `Hook saturation detected (${hookCount} hooks > 5 limit)`,
100
+ rule: 'HOOK_SATURATION',
101
+ directive: 'Extract related state and effects into dedicated domain hooks'
102
+ });
103
+ }
104
+ },
105
+
106
+ // 3. Control Flow Complexity
107
+ JSXExpressionContainer(astPath) {
108
+ const expr = astPath.node.expression;
109
+ if (t.isLogicalExpression(expr) || t.isUnaryExpression(expr)) {
110
+ const opCount = countLogicalOperators(expr);
111
+ if (opCount > 2) {
112
+ const line = expr.loc?.start.line || astPath.node.loc?.start.line || 1;
113
+ violations.push({
114
+ filePath: relativePath,
115
+ line,
116
+ hazard: `Inline boolean complexity (${opCount} logical operators > 2 limit)`,
117
+ rule: 'CONTROL_FLOW_INLINE_BOOLEAN',
118
+ directive: 'Compose booleans into Stage 1 concepts and Stage 2 decision variables'
119
+ });
120
+ }
121
+ }
122
+ },
123
+
124
+ ConditionalExpression(astPath) {
125
+ if (t.isConditionalExpression(astPath.node.consequent) || t.isConditionalExpression(astPath.node.alternate)) {
126
+ const line = astPath.node.loc?.start.line || 1;
127
+ violations.push({
128
+ filePath: relativePath,
129
+ line,
130
+ hazard: 'Nested ternary operator detected',
131
+ rule: 'CONTROL_FLOW_NESTED_TERNARY',
132
+ directive: 'Extract display states into computed descriptor objects or early returns'
133
+ });
134
+ }
135
+ },
136
+
137
+ // 4. Timer Discipline
138
+ CallExpression(astPath) {
139
+ const callee = astPath.node.callee;
140
+ if (t.isIdentifier(callee) && (callee.name === 'setInterval' || callee.name === 'setTimeout')) {
141
+ const fnParent = astPath.getFunctionParent();
142
+ let hasCleanup = false;
143
+ if (fnParent) {
144
+ fnParent.traverse({
145
+ ReturnStatement(retPath) {
146
+ if (retPath.node.argument) {
147
+ hasCleanup = true;
148
+ }
149
+ }
150
+ });
151
+ }
152
+
153
+ if (!hasCleanup) {
154
+ const line = astPath.node.loc?.start.line || 1;
155
+ violations.push({
156
+ filePath: relativePath,
157
+ line,
158
+ hazard: `Raw ${callee.name} lacking lifecycle scope disposal`,
159
+ rule: 'TIMER_DISCIPLINE',
160
+ directive: 'Wrap timers in self-cleaning hooks returning cleanup disposers'
161
+ });
162
+ }
163
+ }
164
+ },
165
+
166
+ // 5. Type Co-location
167
+ TSTypeLiteral(astPath) {
168
+ if (astPath.node.members.length > 3) {
169
+ if (!astPath.findParent((p) => p.isTSTypeAliasDeclaration() || p.isTSInterfaceDeclaration())) {
170
+ const line = astPath.node.loc?.start.line || 1;
171
+ violations.push({
172
+ filePath: relativePath,
173
+ line,
174
+ hazard: `Inlined anonymous complex type (${astPath.node.members.length} members)`,
175
+ rule: 'TYPE_COLOCATION',
176
+ directive: 'Define co-located domain interfaces in types/*.d.ts'
177
+ });
178
+ }
179
+ }
180
+ }
181
+ });
182
+
183
+ return violations;
184
+ };
185
+
186
+ const IGNORED_DIRS = new Set(['node_modules', 'dist', 'build', 'vendor', '.git', '.next', '.turbo', '.output', 'out']);
187
+
188
+ const isSourceFile = (name) => {
189
+ return (
190
+ /\.(tsx|ts|jsx|js|vue)$/.test(name) &&
191
+ !name.endsWith('.d.ts') &&
192
+ !name.includes('.test.') &&
193
+ !name.includes('.spec.') &&
194
+ !name.includes('.min.')
195
+ );
196
+ };
197
+
198
+ export const scanDirectory = (targetDir, baseDir) => {
199
+ let results = [];
200
+ if (!fs.existsSync(targetDir)) return results;
201
+
202
+ const entries = fs.readdirSync(targetDir, { withFileTypes: true });
203
+ for (const entry of entries) {
204
+ const fullPath = path.join(targetDir, entry.name);
205
+ const relPath = path.relative(baseDir, fullPath);
206
+
207
+ if (entry.isDirectory()) {
208
+ if (!IGNORED_DIRS.has(entry.name)) {
209
+ results = results.concat(scanDirectory(fullPath, baseDir));
210
+ }
211
+ } else if (isSourceFile(entry.name)) {
212
+ results = results.concat(auditFile(fullPath, relPath));
213
+ }
214
+ }
215
+ return results;
216
+ };
217
+
218
+ const countTotalScannedFiles = (targetDir) => {
219
+ let count = 0;
220
+ if (!fs.existsSync(targetDir)) return count;
221
+
222
+ const entries = fs.readdirSync(targetDir, { withFileTypes: true });
223
+ for (const entry of entries) {
224
+ const fullPath = path.join(targetDir, entry.name);
225
+ if (entry.isDirectory()) {
226
+ if (!IGNORED_DIRS.has(entry.name)) {
227
+ count += countTotalScannedFiles(fullPath);
228
+ }
229
+ } else if (isSourceFile(entry.name)) {
230
+ count += 1;
231
+ }
232
+ }
233
+ return count;
234
+ };
235
+
236
+ export const runAudit = (targetDir = 'src') => {
237
+ const cwd = process.cwd();
238
+ const absoluteTarget = path.resolve(cwd, targetDir);
239
+ const violations = scanDirectory(absoluteTarget, cwd);
240
+ const scannedFiles = countTotalScannedFiles(absoluteTarget);
241
+
242
+ return {
243
+ scannedFiles,
244
+ totalViolations: violations.length,
245
+ violations
246
+ };
247
+ };
248
+
249
+ export default {
250
+ auditFile,
251
+ scanDirectory,
252
+ runAudit
253
+ };
package/cli/index.js CHANGED
@@ -5,6 +5,7 @@ import path from 'node:path';
5
5
  import os from 'node:os';
6
6
  import readline from 'node:readline';
7
7
  import { spawnSync } from 'node:child_process';
8
+ import { runAudit as executeAstAudit, auditFile } from './audit.js';
8
9
 
9
10
  const rawArgs = process.argv.slice(2);
10
11
  const invokedBin = path.basename(process.argv[1] || '');
@@ -40,8 +41,11 @@ const hasGum = () => {
40
41
  };
41
42
 
42
43
  const gumChoose = (options, header = '') => {
43
- const args = ['choose', ...options];
44
- if (header) args.unshift(`--header=${header}`);
44
+ const args = ['choose'];
45
+ if (header) {
46
+ args.push(`--header=${header}`, '--header.foreground=81');
47
+ }
48
+ args.push('--cursor.foreground=81', ...options);
45
49
  const res = spawnSync('gum', args, { encoding: 'utf-8', stdio: ['inherit', 'pipe', 'inherit'] });
46
50
  return (res.stdout || '').trim();
47
51
  };
@@ -128,30 +132,33 @@ const obtainLicenseKey = async () => {
128
132
 
129
133
  if (useGum) {
130
134
  const choice = gumChoose([
131
- '1. Enter Chemical X License Key',
132
- '2. Buy Standard Edition ($49) [mycompassconsulting.com]',
133
- '3. Buy Master Bundle ($99) [mycompassconsulting.com]',
135
+ '1. Buy Standard Edition ($49) -> Launch Checkout',
136
+ '2. Buy Master Bundle ($99) -> Launch Checkout',
137
+ '3. Enter License Key (CX-XXXX-XXXX-XXXX)',
134
138
  '4. Run Free Public Audit (npx chemx audit)',
135
139
  '5. Exit'
136
- ], 'Select an option to proceed:');
140
+ ], 'Chemical X Scaffolding Requires a Paid License:');
137
141
 
138
- if (choice.startsWith('2.')) {
142
+ if (choice.startsWith('1.')) {
139
143
  process.stdout.write(`\x1b[36mOpening checkout in default browser:\x1b[0m ${URL_STANDARD}\n`);
140
144
  openBrowser(URL_STANDARD);
141
145
  process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
142
146
  return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
143
147
  }
144
148
 
145
- if (choice.startsWith('3.')) {
149
+ if (choice.startsWith('2.')) {
146
150
  process.stdout.write(`\x1b[36mOpening checkout in default browser:\x1b[0m ${URL_MASTER}\n`);
147
151
  openBrowser(URL_MASTER);
148
152
  process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
149
153
  return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
150
154
  }
151
155
 
156
+ if (choice.startsWith('3.')) {
157
+ return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
158
+ }
159
+
152
160
  if (choice.startsWith('4.')) {
153
- runAudit();
154
- process.exit(0);
161
+ await runAudit(null, true);
155
162
  }
156
163
 
157
164
  if (choice.startsWith('5.') || !choice) {
@@ -161,30 +168,33 @@ const obtainLicenseKey = async () => {
161
168
  return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
162
169
  }
163
170
 
164
- process.stdout.write('\x1b[1mAuthentication Options:\x1b[0m\n');
165
- process.stdout.write(' [1] Enter License Key\n');
166
- process.stdout.write(' [2] Buy Standard Edition ($49) - Opens browser\n');
167
- process.stdout.write(' [3] Buy Master Bundle ($99) - Opens browser\n');
171
+ process.stdout.write('\x1b[1mChemical X Scaffolding Requires a Paid License:\x1b[0m\n');
172
+ process.stdout.write(' [1] Buy Standard Edition ($49) - Opens browser\n');
173
+ process.stdout.write(' [2] Buy Master Bundle ($99) - Opens browser\n');
174
+ process.stdout.write(' [3] Enter License Key\n');
168
175
  process.stdout.write(' [4] Run Free Public Audit (npx chemx audit)\n');
169
176
  process.stdout.write(' [5] Exit\n\n');
170
177
 
171
178
  const selection = await promptQuestion('Select option [1-5]: ');
172
179
 
173
- if (selection === '2') {
180
+ if (selection === '1') {
174
181
  process.stdout.write(`Opening: ${URL_STANDARD}\n`);
175
182
  openBrowser(URL_STANDARD);
176
183
  return promptQuestion('Enter License Key after purchase: ');
177
184
  }
178
185
 
179
- if (selection === '3') {
186
+ if (selection === '2') {
180
187
  process.stdout.write(`Opening: ${URL_MASTER}\n`);
181
188
  openBrowser(URL_MASTER);
182
189
  return promptQuestion('Enter License Key after purchase: ');
183
190
  }
184
191
 
192
+ if (selection === '3') {
193
+ return promptQuestion('Enter License Key (CX-XXXX-XXXX-XXXX): ');
194
+ }
195
+
185
196
  if (selection === '4') {
186
- runAudit();
187
- process.exit(0);
197
+ await runAudit(null, true);
188
198
  }
189
199
 
190
200
  if (selection === '5') {
@@ -227,6 +237,12 @@ const fetchStarterKitFiles = async (licenseKey) => {
227
237
  const runScaffold = async (projectName) => {
228
238
  renderBanner('Chemical X: Quantum Scaffolder (npm create chemx)');
229
239
 
240
+ const licenseKey = await obtainLicenseKey();
241
+ if (!licenseKey) {
242
+ process.stderr.write('\x1b[31m✕ Valid license key is required to scaffold blueprints.\x1b[0m\n');
243
+ process.exit(1);
244
+ }
245
+
230
246
  let targetName = projectName;
231
247
  if (!targetName) {
232
248
  if (hasGum()) {
@@ -244,12 +260,6 @@ const runScaffold = async (projectName) => {
244
260
  process.exit(1);
245
261
  }
246
262
 
247
- const licenseKey = await obtainLicenseKey();
248
- if (!licenseKey) {
249
- process.stderr.write('\x1b[31m✕ Valid license key is required to scaffold blueprints.\x1b[0m\n');
250
- process.exit(1);
251
- }
252
-
253
263
  const files = await fetchStarterKitFiles(licenseKey);
254
264
 
255
265
  process.stdout.write(`Scaffolding Quantum Architecture into: \x1b[36m${finalDirName}/\x1b[0m\n`);
@@ -358,80 +368,107 @@ export type { ${pascalName}Props } from './types';
358
368
  process.stdout.write(` - ${normalizedName}/index.ts\n\n`);
359
369
  };
360
370
 
361
- export const runAudit = () => {
362
- process.stdout.write('\n\x1b[38;2;98;201;255m[Chemical X Public Audit]\x1b[0m Scanning codebase for line-budget hazards...\n');
363
- const targetDir = process.cwd();
371
+ export const runAudit = async (customDir = null, isCli = false) => {
372
+ const isJson = rawArgs.includes('--json');
373
+ const dirFlag = rawArgs.find((arg) => arg.startsWith('--dir='));
374
+ const targetDir = customDir || (dirFlag ? dirFlag.split('=')[1] : (fs.existsSync('src') ? 'src' : '.'));
364
375
 
365
- let scanned = 0;
366
- let violations = 0;
367
- const offendingFiles = [];
376
+ const report = executeAstAudit(targetDir);
368
377
 
369
- const checkFile = (filePath) => {
370
- const ext = path.extname(filePath);
371
- if (!['.ts', '.tsx', '.js', '.jsx', '.vue'].includes(ext)) return;
372
- if (filePath.includes('node_modules') || filePath.includes('.next') || filePath.includes('dist') || filePath.includes('.git')) return;
378
+ if (isJson) {
379
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
380
+ if (isCli) process.exit(report.violations.length > 0 ? 1 : 0);
381
+ return report;
382
+ }
373
383
 
374
- try {
375
- const content = fs.readFileSync(filePath, 'utf-8');
376
- const lines = content.split('\n').length;
377
- scanned++;
378
-
379
- if (lines > 500) {
380
- const rel = path.relative(targetDir, filePath);
381
- offendingFiles.push({ file: rel, lines });
382
- violations++;
383
- }
384
- } catch {
385
- // Fallback
384
+ process.stdout.write('\n\x1b[38;2;98;201;255m[Chemical X Context Hazard Audit]\x1b[0m Scanning codebase for AST architectural hazards...\n');
385
+ process.stdout.write(`Target Directory: ${targetDir}\n`);
386
+ process.stdout.write(`Scanned ${report.scannedFiles} source files.\n\n`);
387
+
388
+ if (report.violations.length === 0) {
389
+ process.stdout.write('\x1b[1m\x1b[32m✔ 100% Quantum Compliant: Zero context hazard violations detected across all line budgets, hooks, and AST rules.\x1b[0m\n\n');
390
+ } else {
391
+ process.stdout.write(`\x1b[31m✕ FAILED: ${report.totalViolations} context hazard violations detected:\x1b[0m\n\n`);
392
+ for (const v of report.violations) {
393
+ process.stdout.write(` \x1b[31m[${v.rule}]\x1b[0m \x1b[33m${v.filePath}:${v.line}\x1b[0m\n`);
394
+ process.stdout.write(` Hazard: ${v.hazard}\n`);
395
+ process.stdout.write(` Directive: ${v.directive}\n\n`);
386
396
  }
387
- };
397
+ }
388
398
 
389
- const walk = (dir) => {
390
- try {
391
- const files = fs.readdirSync(dir);
392
- for (const file of files) {
393
- const full = path.join(dir, file);
394
- const stat = fs.statSync(full);
395
- if (stat.isDirectory()) {
396
- if (!['node_modules', '.git', '.next', 'dist', 'out'].includes(file)) {
397
- walk(full);
398
- }
399
- } else {
400
- checkFile(full);
399
+ if (isCli) {
400
+ while (true) {
401
+ if (hasGum()) {
402
+ spawnSync('gum', [
403
+ 'style',
404
+ '--border=rounded',
405
+ '--border-foreground=81',
406
+ '--padding=0 1',
407
+ '--bold',
408
+ 'Eliminate AI Context Rot with Chemical X Architecture'
409
+ ], { stdio: 'inherit' });
410
+
411
+ const choice = gumChoose([
412
+ '1. Buy Standard Edition ($49) -> Launch Checkout',
413
+ '2. Buy Master Bundle ($99) -> Launch Checkout',
414
+ '3. Enter License Key to Scaffold (Paid License Holders)',
415
+ '4. Exit'
416
+ ], 'Select a CTA action:');
417
+
418
+ if (choice.startsWith('1.')) {
419
+ process.stdout.write(`\n\x1b[36mOpening Standard Edition checkout in browser:\x1b[0m ${URL_STANDARD}\n\n`);
420
+ openBrowser(URL_STANDARD);
421
+ continue;
422
+ }
423
+ if (choice.startsWith('2.')) {
424
+ process.stdout.write(`\n\x1b[36mOpening Master Bundle checkout in browser:\x1b[0m ${URL_MASTER}\n\n`);
425
+ openBrowser(URL_MASTER);
426
+ continue;
427
+ }
428
+ if (choice.startsWith('3.')) {
429
+ await runScaffold();
430
+ break;
401
431
  }
432
+ break;
433
+ } else {
434
+ process.stdout.write('\x1b[1m\x1b[38;2;98;201;255mEliminate AI Context Rot with Chemical X Architecture:\x1b[0m\n');
435
+ process.stdout.write(` [1] Buy Standard Edition ($49) - ${URL_STANDARD}\n`);
436
+ process.stdout.write(` [2] Buy Master Bundle ($99) - ${URL_MASTER}\n`);
437
+ process.stdout.write(' [3] Enter License Key to Scaffold (Paid License Holders)\n');
438
+ process.stdout.write(' [4] Exit\n\n');
439
+
440
+ const selection = await promptQuestion('Select option [1-4]: ');
441
+ if (selection === '1') {
442
+ process.stdout.write(`\nOpening: ${URL_STANDARD}\n\n`);
443
+ openBrowser(URL_STANDARD);
444
+ continue;
445
+ }
446
+ if (selection === '2') {
447
+ process.stdout.write(`\nOpening: ${URL_MASTER}\n\n`);
448
+ openBrowser(URL_MASTER);
449
+ continue;
450
+ }
451
+ if (selection === '3') {
452
+ await runScaffold();
453
+ break;
454
+ }
455
+ break;
402
456
  }
403
- } catch {
404
- // Fallback
405
- }
406
- };
407
-
408
- walk(targetDir);
409
-
410
- process.stdout.write(`Scanned ${scanned} source files.\n\n`);
411
-
412
- if (violations === 0) {
413
- process.stdout.write('\x1b[1m\x1b[32m✔ 100% Quantum Compliant: All source files meet the 500-line budget ceiling.\x1b[0m\n\n');
414
- } else {
415
- process.stdout.write(`\x1b[31m✕ Found ${violations} Monolith Line-Budget Hazards (> 500 lines):\x1b[0m\n`);
416
- for (const item of offendingFiles) {
417
- process.stdout.write(` - \x1b[33m${item.file}\x1b[0m (${item.lines} lines)\n`);
418
457
  }
419
-
420
- process.stdout.write('\n\x1b[1m\x1b[38;2;98;201;255mEliminate AI Context Rot with Chemical X Architecture:\x1b[0m\n');
421
- process.stdout.write(` * Book & Standards: ${URL_STANDARD}\n`);
422
- process.stdout.write(` * Master Bundle: ${URL_MASTER}\n`);
423
- process.stdout.write(' * Create Starter: npm create chemx\n');
424
- process.stdout.write(' * Drop-in Capsules: npx @chemx/starter-kit init\n\n');
458
+ process.exit(report.violations.length > 0 ? 1 : 0);
425
459
  }
460
+ return report;
426
461
  };
427
462
 
463
+ export { auditFile };
464
+
428
465
  const printHelp = () => {
429
466
  renderBanner();
430
467
  process.stdout.write('\x1b[1mAvailable Commands:\x1b[0m\n');
431
- process.stdout.write(' \x1b[36mnpm create chemx [dir]\x1b[0m Scaffold complete Quantum Architecture project\n');
432
- process.stdout.write(' \x1b[36mnpx @chemx/starter-kit init [dir]\x1b[0m Drop blueprints & hooks into existing project\n');
468
+ process.stdout.write(' \x1b[36mnpm create chemx [dir]\x1b[0m [PAID] Scaffold complete Quantum Architecture project\n');
469
+ process.stdout.write(' \x1b[36mnpx @chemx/starter-kit init [dir]\x1b[0m [PAID] Drop blueprints & hooks into existing project\n');
433
470
  process.stdout.write(' \x1b[36mnpx chemx generate <m-name>\x1b[0m Generate isolated molecule capsule (< 100 lines)\n');
434
- process.stdout.write(' \x1b[36mnpx chemx audit\x1b[0m [FREE] Scan codebase for line-budget hazards\n\n');
471
+ process.stdout.write(' \x1b[36mnpx chemx audit [--json] [--dir=src]\x1b[0m[FREE] Scan codebase for AST architectural hazards\n\n');
435
472
  };
436
473
 
437
474
  const main = async () => {
@@ -445,7 +482,7 @@ const main = async () => {
445
482
 
446
483
  switch (firstArg) {
447
484
  case 'audit':
448
- runAudit();
485
+ await runAudit(null, true);
449
486
  break;
450
487
  case 'init':
451
488
  await runInit(rawArgs[1] || 'src/chemical-x');
package/docs/CHANGELOG.md CHANGED
@@ -21,9 +21,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
21
21
  ### Added
22
22
  - Edge-authenticated single-device project scaffolding command (`npx chemical-x init`) with machine fingerprinting and Cloudflare KV validation.
23
23
  - AST hazard line budget audit command (`npx chemical-x audit`) scanning project trees for > 500 line monolith hazards.
24
+ - AST Context Hazard Audit engine module (`cli/audit.js`, `cli/audit.d.ts`) supporting 5 AST rules: line budget, hook saturation, control flow complexity, timer discipline, and type co-location.
25
+ - `./audit` subpath export in `package.json` for programmatic consumption by test suites and benchmarks.
26
+ - CLI flags `--json` and `--dir=<path>` for `npx chemx audit`.
24
27
  - Automated NPM publish GitHub Actions workflow (`.github/workflows/publish.yml`) chained to `Auto Version` completion via `workflow_run`.
25
28
 
26
29
  ### Changed
30
+ - Upgraded `runAudit` in `cli/index.js` from basic line counting to full Babel AST static analysis engine.
27
31
  - Updated default API endpoint in CLI (`cli/index.js`) to production custom domain `https://chemicalx.xophz.com`.
28
32
  - Expanded `AGENTS.md` blueprint in CLI (`cli/index.js`) to include all 7 Quantum Engineering Architecture pillars.
29
33
  - Configured npm package distribution for `@chemx/starter-kit` with `create-chemx`, `chemx`, `chem-x`, and `chemical-x` binary aliases.
@@ -32,10 +36,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
32
36
  - Added cross-platform browser checkout launcher for `mycompassconsulting.com/buy/chemical-x/standard` and `master`.
33
37
  - Unlocked `npx chemx audit` command as 100% free, unauthenticated, and ungated public utility with conversion CTAs.
34
38
  - Added dual project scaffolder (`npm create chemx` / `create-chemx`) and in-repo capsule drop-in (`init`).
39
+ - Gated `runScaffold` (`npm create chemx`) with upfront license validation prior to project directory name prompt.
40
+ - Integrated interactive Gum CTA action buttons at the conclusion of public audit for instant checkout launch ($49 Standard / $99 Master) and key-gated scaffolding for license holders.
35
41
 
36
42
  ### Fixed
37
43
  - Removed embedded offline blueprint fallbacks and preview bypass keys from CLI executable (`cli/index.js`).
38
44
  - Restricted npm package distribution via `files` whitelist and `.npmignore` to prevent leaking private blueprints and hooks in public tarballs.
39
45
  - Added explicit `--tag` support and automatic default fallback for prerelease/CalVer versions in multi-target publisher (`scripts/publish-both.mjs`).
46
+ - Removed unscoped `chem-x` target from publisher script due to npm registry similarity protection with `chemx`.
47
+ - Excluded 3rd-party `vendor` and `build` directories, as well as minified bundles (`*.min.*`), from the AST context hazard audit to eliminate false positives on bundled external libraries.
48
+ - Fixed argument ordering in `gumChoose` to pass `--header` and styling flags to the `choose` subcommand rather than prepending to the parent binary.
40
49
 
41
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chemx/starter-kit",
3
- "version": "26.9.9-632",
3
+ "version": "26.9.9-720",
4
4
  "description": "Chemical X Protocol: Private drop-in architecture starter kit and capsule generator",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,16 +18,24 @@
18
18
  "README.md",
19
19
  "docs"
20
20
  ],
21
+ "exports": {
22
+ ".": "./cli/index.js",
23
+ "./audit": "./cli/audit.js"
24
+ },
21
25
  "scripts": {
22
26
  "publish:both": "node scripts/publish-both.mjs",
23
27
  "typecheck": "tsc --noEmit",
24
28
  "create-capsule": "node cli/index.js"
25
29
  },
26
30
  "dependencies": {
31
+ "@babel/parser": "^7.26.9",
32
+ "@babel/traverse": "^7.26.9",
33
+ "@babel/types": "^7.26.9",
27
34
  "react": "^18.3.1",
28
35
  "react-dom": "^18.3.1"
29
36
  },
30
37
  "devDependencies": {
38
+ "@types/babel__traverse": "^7.20.6",
31
39
  "@types/node": "^22.13.5",
32
40
  "@types/react": "^18.3.18",
33
41
  "@types/react-dom": "^18.3.5",