@chemx/starter-kit 26.9.9-720 → 26.9.11-362

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 (40) hide show
  1. package/cli/audit/ast-visitors.js +243 -0
  2. package/cli/audit/discussion-store.js +39 -0
  3. package/cli/audit/history.d.ts +63 -0
  4. package/cli/audit/history.js +352 -0
  5. package/cli/audit/metrics.js +179 -0
  6. package/cli/audit/prompts.js +174 -0
  7. package/cli/audit/reporter-grades.js +259 -0
  8. package/cli/audit/reporter-markdown.js +136 -0
  9. package/cli/audit/reporter-utils.js +107 -0
  10. package/cli/audit/reporter.js +459 -0
  11. package/cli/audit/rules-helpers.js +109 -0
  12. package/cli/audit/rules-registry.js +97 -0
  13. package/cli/audit/rules.js +123 -0
  14. package/cli/audit/social-constants.js +4 -0
  15. package/cli/audit/social-gh.js +126 -0
  16. package/cli/audit/social-git.js +214 -0
  17. package/cli/audit/social-http.js +222 -0
  18. package/cli/audit/social-publisher.js +193 -0
  19. package/cli/audit/social.d.ts +66 -0
  20. package/cli/audit/social.js +254 -0
  21. package/cli/audit/types.d.ts +131 -0
  22. package/cli/audit.d.ts +3 -24
  23. package/cli/audit.js +195 -210
  24. package/cli/help.d.ts +1 -0
  25. package/cli/help.js +136 -0
  26. package/cli/index.js +109 -438
  27. package/cli/installer-templates.js +80 -0
  28. package/cli/installer.d.ts +20 -0
  29. package/cli/installer.js +56 -0
  30. package/cli/license.js +320 -0
  31. package/cli/navigator-conversion.js +100 -0
  32. package/cli/navigator-grades.js +70 -0
  33. package/cli/navigator-paged.js +86 -0
  34. package/cli/navigator-share.js +129 -0
  35. package/cli/navigator.d.ts +24 -0
  36. package/cli/navigator.js +400 -0
  37. package/cli/scaffold.js +148 -0
  38. package/cli/terminal.js +126 -0
  39. package/docs/CHANGELOG.md +74 -1
  40. package/package.json +11 -10
package/cli/index.js CHANGED
@@ -2,481 +2,145 @@
2
2
 
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
- import os from 'node:os';
6
- import readline from 'node:readline';
7
- import { spawnSync } from 'node:child_process';
8
- import { runAudit as executeAstAudit, auditFile } from './audit.js';
5
+ import {
6
+ runAudit as executeAstAudit,
7
+ auditFile,
8
+ formatTerminalReport,
9
+ generateMarkdownReport,
10
+ saveAuditSnapshot,
11
+ copyToClipboard,
12
+ buildMasterPrompt
13
+ } from './audit.js';
14
+ import {
15
+ runScaffold,
16
+ runInit,
17
+ runGenerateCapsule,
18
+ printHelp
19
+ } from './scaffold.js';
20
+ import {
21
+ runInteractiveAuditNavigator,
22
+ showConversionMenu,
23
+ handleShareToDiscussions
24
+ } from './navigator.js';
25
+ import { runInstallWizard } from './installer.js';
9
26
 
10
27
  const rawArgs = process.argv.slice(2);
11
28
  const invokedBin = path.basename(process.argv[1] || '');
12
- const isCreateInvoked = invokedBin.includes('create-chemx') || (rawArgs[0] && rawArgs[0] === 'create');
13
29
 
14
- const CONFIG_DIR = path.join(os.homedir(), '.chemical-x');
15
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
16
- const DEVICE_FILE = path.join(CONFIG_DIR, 'device_id');
17
-
18
- const API_BASE = process.env.CHEMICAL_X_API_URL || 'https://chemicalx.xophz.com';
19
- const URL_STANDARD = 'https://mycompassconsulting.com/buy/chemical-x/standard';
20
- const URL_MASTER = 'https://mycompassconsulting.com/buy/chemical-x/master';
21
-
22
- if (!fs.existsSync(CONFIG_DIR)) {
23
- try { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } catch {}
24
- }
25
-
26
- const openBrowser = (url) => {
27
- const platform = process.platform;
28
- try {
29
- if (platform === 'darwin') spawnSync('open', [url], { stdio: 'ignore' });
30
- else if (platform === 'win32') spawnSync('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
31
- else spawnSync('xdg-open', [url], { stdio: 'ignore' });
32
- } catch {}
33
- };
34
-
35
- const hasGum = () => {
30
+ const loadProjectConfig = () => {
36
31
  try {
37
- return spawnSync('which', ['gum'], { stdio: 'ignore' }).status === 0;
38
- } catch {
39
- return false;
40
- }
41
- };
42
-
43
- const gumChoose = (options, header = '') => {
44
- const args = ['choose'];
45
- if (header) {
46
- args.push(`--header=${header}`, '--header.foreground=81');
47
- }
48
- args.push('--cursor.foreground=81', ...options);
49
- const res = spawnSync('gum', args, { encoding: 'utf-8', stdio: ['inherit', 'pipe', 'inherit'] });
50
- return (res.stdout || '').trim();
51
- };
52
-
53
- const gumInput = (promptText, placeholder = '', isPassword = false) => {
54
- const args = ['input', `--prompt=${promptText} `, `--placeholder=${placeholder}`];
55
- if (isPassword) args.push('--password');
56
- const res = spawnSync('gum', args, { encoding: 'utf-8', stdio: ['inherit', 'pipe', 'inherit'] });
57
- return (res.stdout || '').trim();
58
- };
59
-
60
- const promptQuestion = (query) => {
61
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
62
- return new Promise((resolve) => {
63
- rl.question(query, (answer) => {
64
- rl.close();
65
- resolve(answer.trim());
66
- });
67
- });
68
- };
69
-
70
- const getOrCreateDeviceId = () => {
71
- if (fs.existsSync(DEVICE_FILE)) {
72
- try {
73
- const id = fs.readFileSync(DEVICE_FILE, 'utf-8').trim();
74
- if (id) return id;
75
- } catch {}
76
- }
77
- const newId = `cli_${Math.random().toString(36).substring(2, 12)}_${Date.now()}`;
78
- try { fs.writeFileSync(DEVICE_FILE, newId, 'utf-8'); } catch {}
79
- return newId;
80
- };
81
-
82
- const getCachedLicenseKey = () => {
83
- if (fs.existsSync(CONFIG_FILE)) {
84
- try {
85
- const data = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
86
- return data.licenseKey || null;
87
- } catch {
88
- return null;
32
+ const cfgPath = path.resolve(process.cwd(), '.chemx', 'config.json');
33
+ if (fs.existsSync(cfgPath)) {
34
+ return JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
89
35
  }
90
- }
91
- return null;
92
- };
93
-
94
- const saveLicenseKey = (licenseKey) => {
95
- try {
96
- fs.writeFileSync(CONFIG_FILE, JSON.stringify({ licenseKey, updatedAt: new Date().toISOString() }, null, 2), 'utf-8');
97
36
  } catch {}
37
+ return {};
98
38
  };
39
+ const isCreateInvoked =
40
+ invokedBin.includes('create-chemx') || (rawArgs[0] && rawArgs[0] === 'create');
99
41
 
100
- const renderBanner = (title = 'Chemical X Protocol: Quantum Architecture') => {
101
- if (hasGum()) {
102
- spawnSync('gum', [
103
- 'style',
104
- '--border=normal',
105
- '--margin=1',
106
- '--padding=1 2',
107
- '--border-foreground=45',
108
- '--foreground=81',
109
- '--bold',
110
- ` ${title}\n Zero-Context-Rot Scaffolding & Engineering Directives`
111
- ], { stdio: 'inherit' });
112
- } else {
113
- process.stdout.write('\n\x1b[38;2;98;201;255m=====================================================\x1b[0m\n');
114
- process.stdout.write(`\x1b[1m\x1b[38;2;98;201;255m ${title}\x1b[0m\n`);
115
- process.stdout.write(' Zero-Context-Rot Scaffolding & Engineering Directives\n');
116
- process.stdout.write('\x1b[38;2;98;201;255m=====================================================\x1b[0m\n\n');
117
- }
42
+ const resolveTargetDir = (custom, flag) => {
43
+ if (custom) return custom;
44
+ if (flag) return flag.split('=')[1];
45
+ if (fs.existsSync('src')) return 'src';
46
+ return '.';
118
47
  };
119
48
 
120
- const obtainLicenseKey = async () => {
121
- let cached = getCachedLicenseKey();
122
- const cliFlagIdx = rawArgs.indexOf('--license');
123
- if (cliFlagIdx !== -1 && rawArgs[cliFlagIdx + 1]) {
124
- cached = rawArgs[cliFlagIdx + 1].trim();
125
- }
126
-
127
- if (cached) {
128
- return cached;
129
- }
130
-
131
- const useGum = hasGum();
132
-
133
- if (useGum) {
134
- const choice = gumChoose([
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)',
138
- '4. Run Free Public Audit (npx chemx audit)',
139
- '5. Exit'
140
- ], 'Chemical X Scaffolding Requires a Paid License:');
141
-
142
- if (choice.startsWith('1.')) {
143
- process.stdout.write(`\x1b[36mOpening checkout in default browser:\x1b[0m ${URL_STANDARD}\n`);
144
- openBrowser(URL_STANDARD);
145
- process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
146
- return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
147
- }
148
-
149
- if (choice.startsWith('2.')) {
150
- process.stdout.write(`\x1b[36mOpening checkout in default browser:\x1b[0m ${URL_MASTER}\n`);
151
- openBrowser(URL_MASTER);
152
- process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
153
- return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
154
- }
155
-
156
- if (choice.startsWith('3.')) {
157
- return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
158
- }
159
-
160
- if (choice.startsWith('4.')) {
161
- await runAudit(null, true);
162
- }
49
+ export const runAudit = async (customDir = null, isCli = false) => {
50
+ const projectConfig = loadProjectConfig();
51
+ const isJson = rawArgs.includes('--json');
52
+ const isMarkdown = rawArgs.includes('--markdown') || rawArgs.includes('--md');
53
+ const isUnroll = rawArgs.includes('--unroll') || rawArgs.includes('--all');
54
+ const isShare = rawArgs.includes('--share') || rawArgs.includes('--post');
55
+ const isStrict = rawArgs.includes('--strict');
56
+ const isPromptOnFail = rawArgs.includes('--prompt-on-fail');
57
+ const isCopyPrompt = rawArgs.includes('--copy-prompt');
163
58
 
164
- if (choice.startsWith('5.') || !choice) {
165
- process.exit(0);
166
- }
59
+ const dirFlag = rawArgs.find((arg) => arg.startsWith('--dir='));
60
+ const outputFlag = rawArgs.find((arg) => arg.startsWith('--output=') || arg.startsWith('-o='));
61
+ const outputFile = outputFlag ? outputFlag.split('=')[1] : null;
167
62
 
168
- return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
169
- }
63
+ const minGradeFlag = rawArgs.find((arg) => arg.startsWith('--min-grade='));
64
+ const minGrade = (minGradeFlag ? minGradeFlag.split('=')[1] : (process.env.CHEMX_MIN_GRADE || projectConfig.minGrade || '')).toUpperCase();
170
65
 
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');
175
- process.stdout.write(' [4] Run Free Public Audit (npx chemx audit)\n');
176
- process.stdout.write(' [5] Exit\n\n');
66
+ const minScoreFlag = rawArgs.find((arg) => arg.startsWith('--min-score='));
67
+ const minScoreRaw = minScoreFlag ? minScoreFlag.split('=')[1] : (process.env.CHEMX_MIN_SCORE || projectConfig.minScore || null);
68
+ const minScore = minScoreRaw !== null && minScoreRaw !== undefined ? parseInt(String(minScoreRaw), 10) : null;
177
69
 
178
- const selection = await promptQuestion('Select option [1-5]: ');
70
+ const modelFlag = rawArgs.find((arg) => arg.startsWith('--model='));
71
+ const model = modelFlag ? modelFlag.split('=')[1] : (projectConfig.model || 'blended');
72
+ const costFlag = rawArgs.find((arg) => arg.startsWith('--cost-per-million='));
73
+ const costPerMillion = costFlag ? parseFloat(costFlag.split('=')[1]) : null;
179
74
 
180
- if (selection === '1') {
181
- process.stdout.write(`Opening: ${URL_STANDARD}\n`);
182
- openBrowser(URL_STANDARD);
183
- return promptQuestion('Enter License Key after purchase: ');
184
- }
75
+ const targetDir = resolveTargetDir(customDir, dirFlag);
76
+ const report = executeAstAudit(targetDir, { outputFile, model, costPerMillion });
77
+ saveAuditSnapshot(report);
185
78
 
186
- if (selection === '2') {
187
- process.stdout.write(`Opening: ${URL_MASTER}\n`);
188
- openBrowser(URL_MASTER);
189
- return promptQuestion('Enter License Key after purchase: ');
190
- }
79
+ const GRADE_RANKS = { 'A+': 5, 'A': 4, 'B': 3, 'C': 2, 'D': 1, 'F': 0 };
80
+ const hasCriticalOrHigh = report.violations.some((v) => v.severity === 'CRITICAL' || v.severity === 'HIGH');
81
+ const isStrictFail = isStrict && report.violations.length > 0;
82
+ const isGradeFail = Boolean(minGrade && GRADE_RANKS[report.health.grade] !== undefined && GRADE_RANKS[minGrade] !== undefined && GRADE_RANKS[report.health.grade] < GRADE_RANKS[minGrade]);
83
+ const isScoreFail = minScore !== null && !isNaN(minScore) && report.health.score < minScore;
84
+ const hasFailingViolations = isStrictFail || hasCriticalOrHigh || isGradeFail || isScoreFail;
191
85
 
192
- if (selection === '3') {
193
- return promptQuestion('Enter License Key (CX-XXXX-XXXX-XXXX): ');
86
+ if (isJson) {
87
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
88
+ if (isCli) process.exit(hasFailingViolations ? 1 : 0);
89
+ return report;
194
90
  }
195
91
 
196
- if (selection === '4') {
197
- await runAudit(null, true);
92
+ if (isMarkdown) {
93
+ const md = generateMarkdownReport(report);
94
+ process.stdout.write(md + '\n');
95
+ if (isCli) process.exit(hasFailingViolations ? 1 : 0);
96
+ return report;
198
97
  }
199
98
 
200
- if (selection === '5') {
201
- process.exit(0);
99
+ if (outputFile) {
100
+ process.stdout.write(`\x1b[32m✔ Exported markdown audit report to:\x1b[0m ${outputFile}\n\n`);
202
101
  }
203
102
 
204
- return promptQuestion('Enter Chemical X Sponsor License Key (CX-XXXX-XXXX-XXXX): ');
205
- };
206
-
207
- const fetchStarterKitFiles = async (licenseKey) => {
208
- const normalizedKey = licenseKey.trim().toUpperCase();
209
- const deviceId = getOrCreateDeviceId();
210
-
211
- process.stdout.write(`\nVerifying license via edge: ${API_BASE}...\n`);
212
-
213
- try {
214
- const res = await fetch(`${API_BASE}/api/starter-kit/download`, {
215
- method: 'POST',
216
- headers: { 'Content-Type': 'application/json' },
217
- body: JSON.stringify({ licenseKey: normalizedKey, deviceId })
218
- });
219
-
220
- const responseData = await res.json();
221
-
222
- if (!res.ok || !responseData.valid) {
223
- process.stderr.write(`\x1b[31m✕ License Verification Failed: ${responseData.error || 'Invalid key.'}\x1b[0m\n`);
224
- process.stderr.write(`Purchase key at: ${URL_STANDARD}\n\n`);
225
- process.exit(1);
103
+ if (isCli) {
104
+ if (isShare) {
105
+ await handleShareToDiscussions(report);
106
+ process.exit(0);
226
107
  }
227
108
 
228
- saveLicenseKey(normalizedKey);
229
- process.stdout.write(`\x1b[32m✔ Verified License for @${responseData.githubUser || 'sponsor'}\x1b[0m\n\n`);
230
- return responseData.files || {};
231
- } catch (err) {
232
- process.stderr.write(`\x1b[31m✕ Network Error: Failed to reach edge server (${err.message}).\x1b[0m\n`);
233
- process.exit(1);
234
- }
235
- };
236
-
237
- const runScaffold = async (projectName) => {
238
- renderBanner('Chemical X: Quantum Scaffolder (npm create chemx)');
109
+ const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
239
110
 
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
-
246
- let targetName = projectName;
247
- if (!targetName) {
248
- if (hasGum()) {
249
- targetName = gumInput('Project directory name:', 'my-quantum-app');
111
+ if (isInteractive && !isUnroll) {
112
+ await runInteractiveAuditNavigator(report, () => runScaffold(undefined, rawArgs, runAudit));
250
113
  } else {
251
- targetName = await promptQuestion('Project directory name [my-quantum-app]: ');
252
- }
253
- }
254
-
255
- const finalDirName = targetName.trim() || 'my-quantum-app';
256
- const targetDir = path.resolve(process.cwd(), finalDirName);
257
-
258
- if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
259
- process.stderr.write(`\x1b[31m✕ Error: Directory '${finalDirName}' already exists and is not empty.\x1b[0m\n`);
260
- process.exit(1);
261
- }
262
-
263
- const files = await fetchStarterKitFiles(licenseKey);
264
-
265
- process.stdout.write(`Scaffolding Quantum Architecture into: \x1b[36m${finalDirName}/\x1b[0m\n`);
266
- fs.mkdirSync(targetDir, { recursive: true });
267
-
268
- for (const [relPath, content] of Object.entries(files)) {
269
- const fullPath = path.join(targetDir, relPath);
270
- const dirName = path.dirname(fullPath);
271
- if (!fs.existsSync(dirName)) {
272
- fs.mkdirSync(dirName, { recursive: true });
114
+ process.stdout.write(formatTerminalReport(report));
115
+ if (isInteractive && isUnroll) {
116
+ await showConversionMenu(() => runScaffold(undefined, rawArgs, runAudit));
117
+ }
273
118
  }
274
- fs.writeFileSync(fullPath, content, 'utf-8');
275
- process.stdout.write(` \x1b[32m✔\x1b[0m ${relPath}\n`);
276
- }
277
-
278
- const cursorRulesPath = path.join(targetDir, '.cursorrules');
279
- if (!fs.existsSync(cursorRulesPath)) {
280
- const rules = `# Chemical X Quantum Architecture Directives\nStrictly follow AGENTS.md rules. Never exceed 500 lines per file. All molecule capsules must stay under 100 lines.\n`;
281
- fs.writeFileSync(cursorRulesPath, rules, 'utf-8');
282
- process.stdout.write(` \x1b[32m✔\x1b[0m .cursorrules\n`);
283
- }
284
-
285
- process.stdout.write(`\n\x1b[1m\x1b[32m✔ Quantum project created successfully at ${finalDirName}!\x1b[0m\n\n`);
286
- process.stdout.write('Next Steps:\n');
287
- process.stdout.write(` 1. cd ${finalDirName}\n`);
288
- process.stdout.write(' 2. Review AGENTS.md for line budgets and architecture standards\n');
289
- process.stdout.write(' 3. Run npx chemx generate m-<feature> to create capsules\n');
290
- process.stdout.write(' 4. Run npx chemx audit to scan for line budget compliance\n\n');
291
- };
292
119
 
293
- const runInit = async (targetSubDir = 'src/chemical-x') => {
294
- renderBanner('Chemical X: In-Repo Capsule Drop-in');
295
-
296
- const targetDir = path.resolve(process.cwd(), targetSubDir);
297
- const licenseKey = await obtainLicenseKey();
298
- if (!licenseKey) {
299
- process.stderr.write('\x1b[31m✕ Valid license key is required.\x1b[0m\n');
300
- process.exit(1);
301
- }
302
-
303
- const files = await fetchStarterKitFiles(licenseKey);
304
-
305
- process.stdout.write(`Unpacking blueprints and hooks into: \x1b[36m${targetSubDir}/\x1b[0m\n`);
306
-
307
- let count = 0;
308
- for (const [relPath, content] of Object.entries(files)) {
309
- const fullPath = path.join(targetDir, relPath);
310
- const dirName = path.dirname(fullPath);
311
- if (!fs.existsSync(dirName)) {
312
- fs.mkdirSync(dirName, { recursive: true });
120
+ if (hasFailingViolations && (isPromptOnFail || isCopyPrompt)) {
121
+ const prompt = buildMasterPrompt(report);
122
+ const copied = copyToClipboard(prompt);
123
+ if (copied) {
124
+ process.stdout.write('\n\x1b[1m\x1b[32m✔ AI Agent refactoring prompt copied to clipboard!\x1b[0m\n');
125
+ process.stdout.write('\x1b[36mPaste directly into Cursor, Claude, or Windsurf to resolve architectural hazards.\x1b[0m\n\n');
126
+ }
313
127
  }
314
- fs.writeFileSync(fullPath, content, 'utf-8');
315
- process.stdout.write(` \x1b[32m✔\x1b[0m ${relPath}\n`);
316
- count++;
317
- }
318
-
319
- process.stdout.write(`\n\x1b[1m\x1b[32m✔ Successfully installed ${count} Chemical X assets into ${targetSubDir}!\x1b[0m\n\n`);
320
- };
321
-
322
- const runGenerateCapsule = (capsuleName) => {
323
- const normalizedName = capsuleName.startsWith('m-') ? capsuleName : `m-${capsuleName}`;
324
- const targetDir = path.resolve(process.cwd(), normalizedName);
325
128
 
326
- if (fs.existsSync(targetDir)) {
327
- process.stderr.write(`\x1b[31m✕ Error: Directory ${normalizedName} already exists.\x1b[0m\n`);
328
- process.exit(1);
129
+ process.exit(hasFailingViolations ? 1 : 0);
329
130
  }
330
131
 
331
- fs.mkdirSync(targetDir, { recursive: true });
332
-
333
- const pascalName = normalizedName
334
- .split('-')
335
- .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
336
- .join('');
337
-
338
- const componentCode = `import React from 'react';
339
- import type { ${pascalName}Props } from './types';
340
-
341
- export const ${pascalName}: React.FC<${pascalName}Props> = ({ label }) => {
342
- return (
343
- <div className="${normalizedName}">
344
- <span>{label}</span>
345
- </div>
346
- );
347
- };
348
-
349
- export default ${pascalName};
350
- `;
351
-
352
- const typesCode = `export interface ${pascalName}Props {
353
- readonly label: string;
354
- }
355
- `;
356
-
357
- const indexCode = `export { ${pascalName} } from './${normalizedName}';
358
- export type { ${pascalName}Props } from './types';
359
- `;
360
-
361
- fs.writeFileSync(path.join(targetDir, `${normalizedName}.tsx`), componentCode, 'utf-8');
362
- fs.writeFileSync(path.join(targetDir, 'types.d.ts'), typesCode, 'utf-8');
363
- fs.writeFileSync(path.join(targetDir, 'index.ts'), indexCode, 'utf-8');
364
-
365
- process.stdout.write(`\x1b[32m✔ Successfully generated crystalline capsule:\x1b[0m ${normalizedName}/\n`);
366
- process.stdout.write(` - ${normalizedName}/${normalizedName}.tsx (< 50 lines)\n`);
367
- process.stdout.write(` - ${normalizedName}/types.d.ts\n`);
368
- process.stdout.write(` - ${normalizedName}/index.ts\n\n`);
369
- };
370
-
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' : '.'));
375
-
376
- const report = executeAstAudit(targetDir);
377
-
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
- }
383
-
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`);
396
- }
397
- }
398
-
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;
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;
456
- }
457
- }
458
- process.exit(report.violations.length > 0 ? 1 : 0);
459
- }
132
+ process.stdout.write(formatTerminalReport(report));
460
133
  return report;
461
134
  };
462
135
 
463
136
  export { auditFile };
464
137
 
465
- const printHelp = () => {
466
- renderBanner();
467
- process.stdout.write('\x1b[1mAvailable Commands:\x1b[0m\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');
470
- process.stdout.write(' \x1b[36mnpx chemx generate <m-name>\x1b[0m Generate isolated molecule capsule (< 100 lines)\n');
471
- process.stdout.write(' \x1b[36mnpx chemx audit [--json] [--dir=src]\x1b[0m[FREE] Scan codebase for AST architectural hazards\n\n');
472
- };
473
-
474
138
  const main = async () => {
475
139
  const firstArg = rawArgs[0];
476
140
 
477
141
  if (isCreateInvoked) {
478
142
  const dirArg = firstArg === 'create' ? rawArgs[1] : firstArg;
479
- await runScaffold(dirArg);
143
+ await runScaffold(dirArg, rawArgs, runAudit);
480
144
  return;
481
145
  }
482
146
 
@@ -485,19 +149,27 @@ const main = async () => {
485
149
  await runAudit(null, true);
486
150
  break;
487
151
  case 'init':
488
- await runInit(rawArgs[1] || 'src/chemical-x');
152
+ await runInit(rawArgs[1] || 'src/chemical-x', rawArgs, runAudit);
489
153
  break;
490
154
  case 'create':
491
- await runScaffold(rawArgs[1]);
155
+ await runScaffold(rawArgs[1], rawArgs, runAudit);
156
+ break;
157
+ case 'hook':
158
+ case 'hooks':
159
+ case 'install-hooks':
160
+ case 'setup-ci':
161
+ await runInstallWizard(rawArgs[1] || process.cwd());
492
162
  break;
493
163
  case 'generate':
494
164
  case 'capsule':
495
165
  case 'add':
496
166
  if (!rawArgs[1]) {
497
- process.stderr.write('Usage: npx chemx generate <capsule-name>\nExample: npx chemx generate m-user-avatar\n');
167
+ process.stderr.write(
168
+ 'Usage: npx chemx generate <capsule-name>\nExample: npx chemx generate m-user-avatar\n'
169
+ );
498
170
  process.exit(1);
499
171
  }
500
- runGenerateCapsule(rawArgs[1]);
172
+ await runGenerateCapsule(rawArgs[1]);
501
173
  break;
502
174
  case 'help':
503
175
  case '--help':
@@ -506,9 +178,9 @@ const main = async () => {
506
178
  break;
507
179
  default:
508
180
  if (firstArg && firstArg.startsWith('m-')) {
509
- runGenerateCapsule(firstArg);
181
+ await runGenerateCapsule(firstArg);
510
182
  } else if (firstArg && !firstArg.startsWith('-')) {
511
- await runScaffold(firstArg);
183
+ await runScaffold(firstArg, rawArgs, runAudit);
512
184
  } else {
513
185
  printHelp();
514
186
  }
@@ -520,4 +192,3 @@ main().catch((err) => {
520
192
  process.stderr.write(`\x1b[31m✕ Unexpected Error: ${err.message}\x1b[0m\n`);
521
193
  process.exit(1);
522
194
  });
523
-