@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.
- package/cli/audit/ast-visitors.js +243 -0
- package/cli/audit/discussion-store.js +39 -0
- package/cli/audit/history.d.ts +69 -0
- package/cli/audit/history.js +397 -0
- package/cli/audit/metrics.js +179 -0
- package/cli/audit/prompts.js +174 -0
- package/cli/audit/reporter-ascii.d.ts +4 -0
- package/cli/audit/reporter-ascii.js +99 -0
- package/cli/audit/reporter-grades.js +275 -0
- package/cli/audit/reporter-markdown.js +137 -0
- package/cli/audit/reporter-utils.js +125 -0
- package/cli/audit/reporter.js +497 -0
- package/cli/audit/rules-helpers.js +109 -0
- package/cli/audit/rules-registry.js +97 -0
- package/cli/audit/rules.js +123 -0
- package/cli/audit/social-constants.js +4 -0
- package/cli/audit/social-gh.js +126 -0
- package/cli/audit/social-git.js +214 -0
- package/cli/audit/social-http.js +222 -0
- package/cli/audit/social-publisher.js +193 -0
- package/cli/audit/social.d.ts +69 -0
- package/cli/audit/social.js +303 -0
- package/cli/audit/types.d.ts +140 -0
- package/cli/audit.d.ts +3 -24
- package/cli/audit.js +207 -210
- package/cli/help.d.ts +1 -0
- package/cli/help.js +136 -0
- package/cli/index.js +128 -566
- package/cli/installer-templates.js +91 -0
- package/cli/installer.d.ts +21 -0
- package/cli/installer.js +104 -0
- package/cli/license.js +320 -0
- package/cli/navigator-actions.js +274 -0
- package/cli/navigator-banner.js +98 -0
- package/cli/navigator-conversion.js +100 -0
- package/cli/navigator-grades.js +87 -0
- package/cli/navigator-menu.js +127 -0
- package/cli/navigator-paged.js +86 -0
- package/cli/navigator-share.js +129 -0
- package/cli/navigator.d.ts +43 -0
- package/cli/navigator.js +126 -0
- package/cli/scaffold.js +148 -0
- package/cli/terminal.js +126 -0
- package/docs/CHANGELOG.md +101 -0
- 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
|
|
4
|
-
import path from
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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 (
|
|
231
|
-
|
|
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 (
|
|
235
|
-
|
|
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 (
|
|
239
|
-
process.
|
|
99
|
+
if (outputFile) {
|
|
100
|
+
process.stdout.write(`\x1b[32m✔ Exported markdown audit report to:\x1b[0m ${outputFile}\n\n`);
|
|
240
101
|
}
|
|
241
102
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
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
|
-
|
|
285
|
-
|
|
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
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ===
|
|
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
|
|
148
|
+
case 'audit':
|
|
593
149
|
await runAudit(null, true);
|
|
594
150
|
break;
|
|
595
|
-
case
|
|
596
|
-
await runInit(rawArgs[1] ||
|
|
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
|
|
599
|
-
|
|
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
|
|
602
|
-
case
|
|
603
|
-
case
|
|
163
|
+
case 'generate':
|
|
164
|
+
case 'capsule':
|
|
165
|
+
case 'add':
|
|
604
166
|
if (!rawArgs[1]) {
|
|
605
167
|
process.stderr.write(
|
|
606
|
-
|
|
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
|
|
613
|
-
case
|
|
614
|
-
case
|
|
174
|
+
case 'help':
|
|
175
|
+
case '--help':
|
|
176
|
+
case '-h':
|
|
615
177
|
printHelp();
|
|
616
178
|
break;
|
|
617
179
|
default:
|
|
618
|
-
if (firstArg && firstArg.startsWith(
|
|
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
|
}
|