@chemx/starter-kit 26.9.9-786 → 26.9.11-481

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 (45) 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 +69 -0
  4. package/cli/audit/history.js +397 -0
  5. package/cli/audit/metrics.js +179 -0
  6. package/cli/audit/prompts.js +174 -0
  7. package/cli/audit/reporter-ascii.d.ts +4 -0
  8. package/cli/audit/reporter-ascii.js +99 -0
  9. package/cli/audit/reporter-grades.js +275 -0
  10. package/cli/audit/reporter-markdown.js +137 -0
  11. package/cli/audit/reporter-utils.js +125 -0
  12. package/cli/audit/reporter.js +497 -0
  13. package/cli/audit/rules-helpers.js +109 -0
  14. package/cli/audit/rules-registry.js +97 -0
  15. package/cli/audit/rules.js +123 -0
  16. package/cli/audit/social-constants.js +4 -0
  17. package/cli/audit/social-gh.js +126 -0
  18. package/cli/audit/social-git.js +214 -0
  19. package/cli/audit/social-http.js +222 -0
  20. package/cli/audit/social-publisher.js +193 -0
  21. package/cli/audit/social.d.ts +69 -0
  22. package/cli/audit/social.js +303 -0
  23. package/cli/audit/types.d.ts +140 -0
  24. package/cli/audit.d.ts +3 -24
  25. package/cli/audit.js +207 -210
  26. package/cli/help.d.ts +1 -0
  27. package/cli/help.js +136 -0
  28. package/cli/index.js +128 -566
  29. package/cli/installer-templates.js +91 -0
  30. package/cli/installer.d.ts +21 -0
  31. package/cli/installer.js +104 -0
  32. package/cli/license.js +320 -0
  33. package/cli/navigator-actions.js +274 -0
  34. package/cli/navigator-banner.js +98 -0
  35. package/cli/navigator-conversion.js +100 -0
  36. package/cli/navigator-grades.js +87 -0
  37. package/cli/navigator-menu.js +127 -0
  38. package/cli/navigator-paged.js +86 -0
  39. package/cli/navigator-share.js +129 -0
  40. package/cli/navigator.d.ts +43 -0
  41. package/cli/navigator.js +126 -0
  42. package/cli/scaffold.js +148 -0
  43. package/cli/terminal.js +126 -0
  44. package/docs/CHANGELOG.md +101 -0
  45. package/package.json +11 -10
package/cli/index.js CHANGED
@@ -1,624 +1,186 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from "node:fs";
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";
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
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
- const invokedBin = path.basename(process.argv[1] || "");
12
- const isCreateInvoked =
13
- invokedBin.includes("create-chemx") || (rawArgs[0] && rawArgs[0] === "create");
14
-
15
- const CONFIG_DIR = path.join(os.homedir(), ".chemical-x");
16
- const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
17
- const DEVICE_FILE = path.join(CONFIG_DIR, "device_id");
18
-
19
- const API_BASE = process.env.CHEMICAL_X_API_URL || "https://chemicalx.xophz.com";
20
- const URL_LEARN = "https://chemicalx.xophz.com";
21
- const URL_STANDARD = "https://mycompassconsulting.com/buy/chemical-x/standard";
22
- const URL_MASTER = "https://mycompassconsulting.com/buy/chemical-x/master";
23
-
24
- if (!fs.existsSync(CONFIG_DIR)) {
25
- try {
26
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
27
- } catch {}
28
- }
29
-
30
- const openBrowser = (url) => {
31
- const platform = process.platform;
32
- try {
33
- if (platform === "darwin") spawnSync("open", [url], { stdio: "ignore" });
34
- else if (platform === "win32")
35
- spawnSync("cmd.exe", ["/c", "start", '""', url], { stdio: "ignore" });
36
- else spawnSync("xdg-open", [url], { stdio: "ignore" });
37
- } catch {}
38
- };
39
-
40
- const hasGum = () => {
41
- try {
42
- return spawnSync("which", ["gum"], { stdio: "ignore" }).status === 0;
43
- } catch {
44
- return false;
45
- }
46
- };
47
-
48
- const gumChoose = (options, header = "") => {
49
- const args = ["choose"];
50
- if (header) {
51
- args.push(`--header=${header}`, "--header.foreground=81");
52
- }
53
- args.push("--cursor.foreground=81", ...options);
54
- const res = spawnSync("gum", args, { encoding: "utf-8", stdio: ["inherit", "pipe", "inherit"] });
55
- return (res.stdout || "").trim();
56
- };
57
-
58
- const gumInput = (promptText, placeholder = "", isPassword = false) => {
59
- const args = ["input", `--prompt=${promptText} `, `--placeholder=${placeholder}`];
60
- if (isPassword) args.push("--password");
61
- const res = spawnSync("gum", args, { encoding: "utf-8", stdio: ["inherit", "pipe", "inherit"] });
62
- return (res.stdout || "").trim();
63
- };
64
-
65
- const promptQuestion = (query) => {
66
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
67
- return new Promise((resolve) => {
68
- rl.question(query, (answer) => {
69
- rl.close();
70
- resolve(answer.trim());
71
- });
72
- });
73
- };
28
+ const invokedBin = path.basename(process.argv[1] || '');
74
29
 
75
- const getOrCreateDeviceId = () => {
76
- if (fs.existsSync(DEVICE_FILE)) {
77
- try {
78
- const id = fs.readFileSync(DEVICE_FILE, "utf-8").trim();
79
- if (id) return id;
80
- } catch {}
81
- }
82
- const newId = `cli_${Math.random().toString(36).substring(2, 12)}_${Date.now()}`;
30
+ const loadProjectConfig = () => {
83
31
  try {
84
- fs.writeFileSync(DEVICE_FILE, newId, "utf-8");
85
- } catch {}
86
- return newId;
87
- };
88
-
89
- const getCachedLicenseKey = () => {
90
- if (fs.existsSync(CONFIG_FILE)) {
91
- try {
92
- const data = JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
93
- return data.licenseKey || null;
94
- } catch {
95
- 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'));
96
35
  }
97
- }
98
- return null;
99
- };
100
-
101
- const saveLicenseKey = (licenseKey) => {
102
- try {
103
- fs.writeFileSync(
104
- CONFIG_FILE,
105
- JSON.stringify({ licenseKey, updatedAt: new Date().toISOString() }, null, 2),
106
- "utf-8"
107
- );
108
36
  } catch {}
37
+ return {};
109
38
  };
39
+ const isCreateInvoked =
40
+ invokedBin.includes('create-chemx') || (rawArgs[0] && rawArgs[0] === 'create');
110
41
 
111
- const renderBanner = (title = "Chemical X Protocol: Quantum Architecture") => {
112
- if (hasGum()) {
113
- spawnSync(
114
- "gum",
115
- [
116
- "style",
117
- "--border=normal",
118
- "--margin=1",
119
- "--padding=1 2",
120
- "--border-foreground=45",
121
- "--foreground=81",
122
- "--bold",
123
- ` ${title}\n Zero-Context-Rot Scaffolding & Engineering Directives`
124
- ],
125
- { stdio: "inherit" }
126
- );
127
- } else {
128
- process.stdout.write(
129
- "\n\x1b[38;2;98;201;255m=====================================================\x1b[0m\n"
130
- );
131
- process.stdout.write(`\x1b[1m\x1b[38;2;98;201;255m ${title}\x1b[0m\n`);
132
- process.stdout.write(" Zero-Context-Rot Scaffolding & Engineering Directives\n");
133
- process.stdout.write(
134
- "\x1b[38;2;98;201;255m=====================================================\x1b[0m\n\n"
135
- );
136
- }
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 '.';
137
47
  };
138
48
 
139
- const obtainLicenseKey = async () => {
140
- let cached = getCachedLicenseKey();
141
- const cliFlagIdx = rawArgs.indexOf("--license");
142
- if (cliFlagIdx !== -1 && rawArgs[cliFlagIdx + 1]) {
143
- cached = rawArgs[cliFlagIdx + 1].trim();
144
- }
145
-
146
- if (cached) {
147
- return cached;
148
- }
149
-
150
- const useGum = hasGum();
151
-
152
- if (useGum) {
153
- const choice = gumChoose(
154
- [
155
- "1. Visit chemicalx.xophz.com to learn more",
156
- "2. Buy eBook w/ AGENTS.md Rule Book ($27) -> Launch Checkout",
157
- "3. Buy Master Bundle ($47) -> Launch Checkout",
158
- "4. Enter License Key (CX-XXXX-XXXX-XXXX)",
159
- "5. Run Free Public Audit (npx chemx audit)",
160
- "6. Exit"
161
- ],
162
- "Chemical X Scaffolding Requires a Paid License:"
163
- );
164
-
165
- if (choice.startsWith("1.")) {
166
- process.stdout.write(`\x1b[36mOpening Chemical X Portal in default browser:\x1b[0m ${URL_LEARN}\n`);
167
- openBrowser(URL_LEARN);
168
- process.stdout.write("\nOnce completed, paste your Sponsor / VIP License Key below.\n");
169
- return gumInput("License Key (CX-XXXX-XXXX-XXXX):", "CX-XXXX-XXXX-XXXX");
170
- }
171
-
172
- if (choice.startsWith("2.")) {
173
- process.stdout.write(`\x1b[36mOpening Standard Vault checkout in default browser:\x1b[0m ${URL_STANDARD}\n`);
174
- openBrowser(URL_STANDARD);
175
- process.stdout.write("\nOnce completed, paste your Sponsor / VIP License Key below.\n");
176
- return gumInput("License Key (CX-XXXX-XXXX-XXXX):", "CX-XXXX-XXXX-XXXX");
177
- }
178
-
179
- if (choice.startsWith("3.")) {
180
- process.stdout.write(`\x1b[36mOpening Master Bundle checkout in default browser:\x1b[0m ${URL_MASTER}\n`);
181
- openBrowser(URL_MASTER);
182
- process.stdout.write("\nOnce completed, paste your Sponsor / VIP License Key below.\n");
183
- return gumInput("License Key (CX-XXXX-XXXX-XXXX):", "CX-XXXX-XXXX-XXXX");
184
- }
185
-
186
- if (choice.startsWith("4.")) {
187
- return gumInput("License Key (CX-XXXX-XXXX-XXXX):", "CX-XXXX-XXXX-XXXX");
188
- }
189
-
190
- if (choice.startsWith("5.")) {
191
- await runAudit(null, true);
192
- }
193
-
194
- if (choice.startsWith("6.") || !choice) {
195
- process.exit(0);
196
- }
197
-
198
- return gumInput("License Key (CX-XXXX-XXXX-XXXX):", "CX-XXXX-XXXX-XXXX");
199
- }
200
-
201
- process.stdout.write("\x1b[1mChemical X Scaffolding Requires a Paid License:\x1b[0m\n");
202
- process.stdout.write(" [1] Visit chemicalx.xophz.com to learn more\n");
203
- process.stdout.write(" [2] Buy eBook w/ AGENTS.md Rule Book ($27) - Opens browser\n");
204
- process.stdout.write(" [3] Buy Master Bundle ($47) - Opens browser\n");
205
- process.stdout.write(" [4] Enter License Key\n");
206
- process.stdout.write(" [5] Run Free Public Audit (npx chemx audit)\n");
207
- process.stdout.write(" [6] Exit\n\n");
208
-
209
- const selection = await promptQuestion("Select option [1-6] (default: 1): ");
210
- const effectiveChoice = selection.trim() || "1";
211
-
212
- if (effectiveChoice === "1") {
213
- process.stdout.write(`Opening: ${URL_LEARN}\n`);
214
- openBrowser(URL_LEARN);
215
- return promptQuestion("Enter License Key after review (or press Enter to exit): ");
216
- }
217
-
218
- if (effectiveChoice === "2") {
219
- process.stdout.write(`Opening: ${URL_STANDARD}\n`);
220
- openBrowser(URL_STANDARD);
221
- return promptQuestion("Enter License Key after purchase: ");
222
- }
223
-
224
- if (effectiveChoice === "3") {
225
- process.stdout.write(`Opening: ${URL_MASTER}\n`);
226
- openBrowser(URL_MASTER);
227
- return promptQuestion("Enter License Key after purchase: ");
228
- }
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');
58
+
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;
62
+
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();
65
+
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;
69
+
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;
74
+
75
+ const targetDir = resolveTargetDir(customDir, dirFlag);
76
+ const report = executeAstAudit(targetDir, { outputFile, model, costPerMillion });
77
+ saveAuditSnapshot(report);
78
+
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;
229
85
 
230
- if (effectiveChoice === "4") {
231
- 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;
232
90
  }
233
91
 
234
- if (effectiveChoice === "5") {
235
- 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;
236
97
  }
237
98
 
238
- if (effectiveChoice === "6") {
239
- process.exit(0);
99
+ if (outputFile) {
100
+ process.stdout.write(`\x1b[32m✔ Exported markdown audit report to:\x1b[0m ${outputFile}\n\n`);
240
101
  }
241
102
 
242
- return promptQuestion("Enter Chemical X Sponsor License Key (CX-XXXX-XXXX-XXXX): ");
243
- };
244
-
245
- const fetchStarterKitFiles = async (licenseKey) => {
246
- const normalizedKey = licenseKey.trim().toUpperCase();
247
- const deviceId = getOrCreateDeviceId();
248
-
249
- process.stdout.write(`\nVerifying license via edge: ${API_BASE}...\n`);
250
-
251
- try {
252
- const res = await fetch(`${API_BASE}/api/starter-kit/download`, {
253
- method: "POST",
254
- headers: { "Content-Type": "application/json" },
255
- body: JSON.stringify({ licenseKey: normalizedKey, deviceId })
256
- });
257
-
258
- const responseData = await res.json();
259
-
260
- if (!res.ok || !responseData.valid) {
261
- process.stderr.write(
262
- `\x1b[31m✕ License Verification Failed: ${responseData.error || "Invalid key."}\x1b[0m\n`
263
- );
264
- process.stderr.write(`Purchase key at: ${URL_STANDARD}\n\n`);
265
- process.exit(1);
103
+ if (isCli) {
104
+ if (isShare) {
105
+ await handleShareToDiscussions(report);
106
+ process.exit(0);
266
107
  }
267
108
 
268
- saveLicenseKey(normalizedKey);
269
- process.stdout.write(
270
- `\x1b[32m✔ Verified License for @${responseData.githubUser || "sponsor"}\x1b[0m\n\n`
271
- );
272
- return responseData.files || {};
273
- } catch (err) {
274
- process.stderr.write(
275
- `\x1b[31m✕ Network Error: Failed to reach edge server (${err.message}).\x1b[0m\n`
276
- );
277
- process.exit(1);
278
- }
279
- };
280
-
281
- const runScaffold = async (projectName) => {
282
- renderBanner("Chemical X: Quantum Scaffolder (npm create chemx)");
109
+ const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
283
110
 
284
- const licenseKey = await obtainLicenseKey();
285
- if (!licenseKey) {
286
- process.stderr.write(
287
- "\x1b[31m✕ Valid license key is required to scaffold blueprints.\x1b[0m\n"
288
- );
289
- process.exit(1);
290
- }
291
-
292
- let targetName = projectName;
293
- if (!targetName) {
294
- if (hasGum()) {
295
- targetName = gumInput("Project directory name:", "my-quantum-app");
111
+ if (isInteractive && !isUnroll) {
112
+ await runInteractiveAuditNavigator(report, () => runScaffold(undefined, rawArgs, runAudit));
296
113
  } else {
297
- targetName = await promptQuestion("Project directory name [my-quantum-app]: ");
298
- }
299
- }
300
-
301
- const finalDirName = targetName.trim() || "my-quantum-app";
302
- const targetDir = path.resolve(process.cwd(), finalDirName);
303
-
304
- if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
305
- process.stderr.write(
306
- `\x1b[31m✕ Error: Directory '${finalDirName}' already exists and is not empty.\x1b[0m\n`
307
- );
308
- process.exit(1);
309
- }
310
-
311
- const files = await fetchStarterKitFiles(licenseKey);
312
-
313
- process.stdout.write(`Scaffolding Quantum Architecture into: \x1b[36m${finalDirName}/\x1b[0m\n`);
314
- fs.mkdirSync(targetDir, { recursive: true });
315
-
316
- for (const [relPath, content] of Object.entries(files)) {
317
- const fullPath = path.join(targetDir, relPath);
318
- const dirName = path.dirname(fullPath);
319
- if (!fs.existsSync(dirName)) {
320
- fs.mkdirSync(dirName, { recursive: true });
114
+ process.stdout.write(formatTerminalReport(report));
115
+ if (isInteractive && isUnroll) {
116
+ await showConversionMenu(() => runScaffold(undefined, rawArgs, runAudit));
117
+ }
321
118
  }
322
- fs.writeFileSync(fullPath, content, "utf-8");
323
- process.stdout.write(` \x1b[32m✔\x1b[0m ${relPath}\n`);
324
- }
325
119
 
326
- const cursorRulesPath = path.join(targetDir, ".cursorrules");
327
- if (!fs.existsSync(cursorRulesPath)) {
328
- 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`;
329
- fs.writeFileSync(cursorRulesPath, rules, "utf-8");
330
- process.stdout.write(` \x1b[32m✔\x1b[0m .cursorrules\n`);
331
- }
332
-
333
- process.stdout.write(
334
- `\n\x1b[1m\x1b[32m✔ Quantum project created successfully at ${finalDirName}!\x1b[0m\n\n`
335
- );
336
- process.stdout.write("Next Steps:\n");
337
- process.stdout.write(` 1. cd ${finalDirName}\n`);
338
- process.stdout.write(" 2. Review AGENTS.md for line budgets and architecture standards\n");
339
- process.stdout.write(" 3. Run npx chemx generate m-<feature> to create capsules\n");
340
- process.stdout.write(" 4. Run npx chemx audit to scan for line budget compliance\n\n");
341
- };
342
-
343
- const runInit = async (targetSubDir = "src/chemical-x") => {
344
- renderBanner("Chemical X: In-Repo Capsule Drop-in");
345
-
346
- const targetDir = path.resolve(process.cwd(), targetSubDir);
347
- const licenseKey = await obtainLicenseKey();
348
- if (!licenseKey) {
349
- process.stderr.write("\x1b[31m✕ Valid license key is required.\x1b[0m\n");
350
- process.exit(1);
351
- }
352
-
353
- const files = await fetchStarterKitFiles(licenseKey);
354
-
355
- process.stdout.write(`Unpacking blueprints and hooks into: \x1b[36m${targetSubDir}/\x1b[0m\n`);
356
-
357
- let count = 0;
358
- for (const [relPath, content] of Object.entries(files)) {
359
- const fullPath = path.join(targetDir, relPath);
360
- const dirName = path.dirname(fullPath);
361
- if (!fs.existsSync(dirName)) {
362
- 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
+ }
363
127
  }
364
- fs.writeFileSync(fullPath, content, "utf-8");
365
- process.stdout.write(` \x1b[32m✔\x1b[0m ${relPath}\n`);
366
- count++;
367
- }
368
-
369
- process.stdout.write(
370
- `\n\x1b[1m\x1b[32m✔ Successfully installed ${count} Chemical X assets into ${targetSubDir}!\x1b[0m\n\n`
371
- );
372
- };
373
-
374
- const runGenerateCapsule = (capsuleName) => {
375
- const normalizedName = capsuleName.startsWith("m-") ? capsuleName : `m-${capsuleName}`;
376
- const targetDir = path.resolve(process.cwd(), normalizedName);
377
-
378
- if (fs.existsSync(targetDir)) {
379
- process.stderr.write(`\x1b[31m✕ Error: Directory ${normalizedName} already exists.\x1b[0m\n`);
380
- process.exit(1);
381
- }
382
-
383
- fs.mkdirSync(targetDir, { recursive: true });
384
-
385
- const pascalName = normalizedName
386
- .split("-")
387
- .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
388
- .join("");
389
128
 
390
- const componentCode = `import React from 'react';
391
- import type { ${pascalName}Props } from './types';
392
-
393
- export const ${pascalName}: React.FC<${pascalName}Props> = ({ label }) => {
394
- return (
395
- <div className="${normalizedName}">
396
- <span>{label}</span>
397
- </div>
398
- );
399
- };
400
-
401
- export default ${pascalName};
402
- `;
403
-
404
- const typesCode = `export interface ${pascalName}Props {
405
- readonly label: string;
406
- }
407
- `;
408
-
409
- const indexCode = `export { ${pascalName} } from './${normalizedName}';
410
- export type { ${pascalName}Props } from './types';
411
- `;
412
-
413
- fs.writeFileSync(path.join(targetDir, `${normalizedName}.tsx`), componentCode, "utf-8");
414
- fs.writeFileSync(path.join(targetDir, "types.d.ts"), typesCode, "utf-8");
415
- fs.writeFileSync(path.join(targetDir, "index.ts"), indexCode, "utf-8");
416
-
417
- process.stdout.write(
418
- `\x1b[32m✔ Successfully generated crystalline capsule:\x1b[0m ${normalizedName}/\n`
419
- );
420
- process.stdout.write(` - ${normalizedName}/${normalizedName}.tsx (< 50 lines)\n`);
421
- process.stdout.write(` - ${normalizedName}/types.d.ts\n`);
422
- process.stdout.write(` - ${normalizedName}/index.ts\n\n`);
423
- };
424
-
425
- export const runAudit = async (customDir = null, isCli = false) => {
426
- const isJson = rawArgs.includes("--json");
427
- const dirFlag = rawArgs.find((arg) => arg.startsWith("--dir="));
428
- const targetDir =
429
- customDir || (dirFlag ? dirFlag.split("=")[1] : fs.existsSync("src") ? "src" : ".");
430
-
431
- const report = executeAstAudit(targetDir);
432
-
433
- if (isJson) {
434
- process.stdout.write(JSON.stringify(report, null, 2) + "\n");
435
- if (isCli) process.exit(report.violations.length > 0 ? 1 : 0);
436
- return report;
437
- }
438
-
439
- process.stdout.write(
440
- "\n\x1b[38;2;98;201;255m[Chemical X Context Hazard Audit]\x1b[0m Scanning codebase for AST architectural hazards...\n"
441
- );
442
- process.stdout.write(`Target Directory: ${targetDir}\n`);
443
- process.stdout.write(`Scanned ${report.scannedFiles} source files.\n\n`);
444
-
445
- if (report.violations.length === 0) {
446
- process.stdout.write(
447
- "\x1b[1m\x1b[32m✔ 100% Quantum Compliant: Zero context hazard violations detected across all line budgets, hooks, and AST rules.\x1b[0m\n\n"
448
- );
449
- } else {
450
- process.stdout.write(
451
- `\x1b[31m✕ FAILED: ${report.totalViolations} context hazard violations detected:\x1b[0m\n\n`
452
- );
453
- for (const v of report.violations) {
454
- process.stdout.write(` \x1b[31m[${v.rule}]\x1b[0m \x1b[33m${v.filePath}:${v.line}\x1b[0m\n`);
455
- process.stdout.write(` Hazard: ${v.hazard}\n`);
456
- process.stdout.write(` Directive: ${v.directive}\n\n`);
457
- }
129
+ process.exit(hasFailingViolations ? 1 : 0);
458
130
  }
459
131
 
460
- if (isCli) {
461
- while (true) {
462
- if (hasGum()) {
463
- spawnSync(
464
- "gum",
465
- [
466
- "style",
467
- "--border=rounded",
468
- "--border-foreground=81",
469
- "--padding=0 1",
470
- "--bold",
471
- "Chemical X: The Secret Sauce to Vibe Coding\nStop letting AI agents scour 2,000-line monoliths and hallucinate.\n25+ Years XP | Codified by Principal Systems Architect Xopher Pollard\nTarget File Budget: Max 500 lines/file (<100 lines/molecule) | 85% Token Burn Cut"
472
- ],
473
- { stdio: "inherit" }
474
- );
475
-
476
- const choice = gumChoose(
477
- [
478
- "1. Visit chemicalx.xophz.com to learn more",
479
- "2. Buy eBook w/ AGENTS.md Rule Book ($27) -> Launch Checkout",
480
- "3. Buy Master Bundle ($47) -> Launch Checkout",
481
- "4. Enter License Key to Scaffold (Paid License Holders)",
482
- "5. Exit"
483
- ],
484
- "Unlock the Awesome Secret Sauce of Chemical X:"
485
- );
486
-
487
- if (choice.startsWith("1.")) {
488
- process.stdout.write(
489
- `\n\x1b[36mOpening Chemical X Portal in browser:\x1b[0m ${URL_LEARN}\n\n`
490
- );
491
- openBrowser(URL_LEARN);
492
- continue;
493
- }
494
- if (choice.startsWith("2.")) {
495
- process.stdout.write(
496
- `\n\x1b[36mOpening Standard Vault checkout (eBook + AGENTS.md):\x1b[0m ${URL_STANDARD}\n\n`
497
- );
498
- openBrowser(URL_STANDARD);
499
- continue;
500
- }
501
- if (choice.startsWith("3.")) {
502
- process.stdout.write(
503
- `\n\x1b[36mOpening Master Bundle checkout (Repo + Hooks + Prompts):\x1b[0m ${URL_MASTER}\n\n`
504
- );
505
- openBrowser(URL_MASTER);
506
- continue;
507
- }
508
- if (choice.startsWith("4.")) {
509
- await runScaffold();
510
- break;
511
- }
512
- break;
513
- } else {
514
- process.stdout.write(
515
- "\n\x1b[1m\x1b[38;2;98;201;255mChemical X: The Secret Sauce to Vibe Coding\x1b[0m\n"
516
- );
517
- process.stdout.write(
518
- "Stop letting AI agents scour 2,000-line monoliths and hallucinate breaking changes.\n" +
519
- "25+ Years XP | Codified by Principal Systems Architect Xopher Pollard\n" +
520
- "Target File Budget: Max 500 lines/file (<100 lines per molecule capsule).\n\n"
521
- );
522
- process.stdout.write(` [1] Visit chemicalx.xophz.com to learn more\n`);
523
- process.stdout.write(
524
- ` [2] Buy eBook w/ AGENTS.md Rule Book ($27) - ${URL_STANDARD}\n` +
525
- ` Includes: Kindle/Print PDF eBook, 7 Quantum Chapters, Universal AGENTS.md & .cursorrules\n`
526
- );
527
- process.stdout.write(
528
- ` [3] Buy Master Bundle ($47) - ${URL_MASTER}\n` +
529
- ` Includes: Private Starter-Kit Repo, Pre-Commit Line Budget Hooks, 10x Prompts, VIP Discord\n`
530
- );
531
- process.stdout.write(" [4] Enter License Key to Scaffold (Paid License Holders)\n");
532
- process.stdout.write(" [5] Exit\n\n");
533
-
534
- const selection = await promptQuestion("Select option [1-5] (default: 1): ");
535
- const effectiveSelection = selection.trim() || "1";
536
- if (effectiveSelection === "1") {
537
- process.stdout.write(`\nOpening: ${URL_LEARN}\n\n`);
538
- openBrowser(URL_LEARN);
539
- continue;
540
- }
541
- if (effectiveSelection === "2") {
542
- process.stdout.write(`\nOpening: ${URL_STANDARD}\n\n`);
543
- openBrowser(URL_STANDARD);
544
- continue;
545
- }
546
- if (effectiveSelection === "3") {
547
- process.stdout.write(`\nOpening: ${URL_MASTER}\n\n`);
548
- openBrowser(URL_MASTER);
549
- continue;
550
- }
551
- if (effectiveSelection === "4") {
552
- await runScaffold();
553
- break;
554
- }
555
- break;
556
- }
557
- }
558
- process.exit(report.violations.length > 0 ? 1 : 0);
559
- }
132
+ process.stdout.write(formatTerminalReport(report));
560
133
  return report;
561
134
  };
562
135
 
563
136
  export { auditFile };
564
137
 
565
- const printHelp = () => {
566
- renderBanner();
567
- process.stdout.write("\x1b[1mAvailable Commands:\x1b[0m\n");
568
- process.stdout.write(
569
- " \x1b[36mnpm create chemx [dir]\x1b[0m [PAID] Scaffold complete Quantum Architecture project\n"
570
- );
571
- process.stdout.write(
572
- " \x1b[36mnpx @chemx/starter-kit init [dir]\x1b[0m [PAID] Drop blueprints & hooks into existing project\n"
573
- );
574
- process.stdout.write(
575
- " \x1b[36mnpx chemx generate <m-name>\x1b[0m Generate isolated molecule capsule (< 100 lines)\n"
576
- );
577
- process.stdout.write(
578
- " \x1b[36mnpx chemx audit [--json] [--dir=src]\x1b[0m[FREE] Scan codebase for AST architectural hazards\n\n"
579
- );
580
- };
581
-
582
138
  const main = async () => {
583
139
  const firstArg = rawArgs[0];
584
140
 
585
141
  if (isCreateInvoked) {
586
- const dirArg = firstArg === "create" ? rawArgs[1] : firstArg;
587
- await runScaffold(dirArg);
142
+ const dirArg = firstArg === 'create' ? rawArgs[1] : firstArg;
143
+ await runScaffold(dirArg, rawArgs, runAudit);
588
144
  return;
589
145
  }
590
146
 
591
147
  switch (firstArg) {
592
- case "audit":
148
+ case 'audit':
593
149
  await runAudit(null, true);
594
150
  break;
595
- case "init":
596
- await runInit(rawArgs[1] || "src/chemical-x");
151
+ case 'init':
152
+ await runInit(rawArgs[1] || 'src/chemical-x', rawArgs, runAudit);
153
+ break;
154
+ case 'create':
155
+ await runScaffold(rawArgs[1], rawArgs, runAudit);
597
156
  break;
598
- case "create":
599
- await runScaffold(rawArgs[1]);
157
+ case 'hook':
158
+ case 'hooks':
159
+ case 'install-hooks':
160
+ case 'setup-ci':
161
+ await runInstallWizard(rawArgs[1] || process.cwd());
600
162
  break;
601
- case "generate":
602
- case "capsule":
603
- case "add":
163
+ case 'generate':
164
+ case 'capsule':
165
+ case 'add':
604
166
  if (!rawArgs[1]) {
605
167
  process.stderr.write(
606
- "Usage: npx chemx generate <capsule-name>\nExample: npx chemx generate m-user-avatar\n"
168
+ 'Usage: npx chemx generate <capsule-name>\nExample: npx chemx generate m-user-avatar\n'
607
169
  );
608
170
  process.exit(1);
609
171
  }
610
- runGenerateCapsule(rawArgs[1]);
172
+ await runGenerateCapsule(rawArgs[1]);
611
173
  break;
612
- case "help":
613
- case "--help":
614
- case "-h":
174
+ case 'help':
175
+ case '--help':
176
+ case '-h':
615
177
  printHelp();
616
178
  break;
617
179
  default:
618
- if (firstArg && firstArg.startsWith("m-")) {
619
- runGenerateCapsule(firstArg);
620
- } else if (firstArg && !firstArg.startsWith("-")) {
621
- await runScaffold(firstArg);
180
+ if (firstArg && firstArg.startsWith('m-')) {
181
+ await runGenerateCapsule(firstArg);
182
+ } else if (firstArg && !firstArg.startsWith('-')) {
183
+ await runScaffold(firstArg, rawArgs, runAudit);
622
184
  } else {
623
185
  printHelp();
624
186
  }