@chemx/starter-kit 26.9.9-720 → 26.9.9-786
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/index.js +285 -176
- package/docs/CHANGELOG.md +1 -1
- package/package.json +1 -1
package/cli/index.js
CHANGED
|
@@ -1,60 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import fs from
|
|
4
|
-
import path from
|
|
5
|
-
import os from
|
|
6
|
-
import readline from
|
|
7
|
-
import { spawnSync } from
|
|
8
|
-
import { runAudit as executeAstAudit, auditFile } from
|
|
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";
|
|
9
9
|
|
|
10
10
|
const rawArgs = process.argv.slice(2);
|
|
11
|
-
const invokedBin = path.basename(process.argv[1] ||
|
|
12
|
-
const isCreateInvoked =
|
|
11
|
+
const invokedBin = path.basename(process.argv[1] || "");
|
|
12
|
+
const isCreateInvoked =
|
|
13
|
+
invokedBin.includes("create-chemx") || (rawArgs[0] && rawArgs[0] === "create");
|
|
13
14
|
|
|
14
|
-
const CONFIG_DIR = path.join(os.homedir(),
|
|
15
|
-
const CONFIG_FILE = path.join(CONFIG_DIR,
|
|
16
|
-
const DEVICE_FILE = path.join(CONFIG_DIR,
|
|
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");
|
|
17
18
|
|
|
18
|
-
const API_BASE = process.env.CHEMICAL_X_API_URL ||
|
|
19
|
-
const
|
|
20
|
-
const
|
|
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";
|
|
21
23
|
|
|
22
24
|
if (!fs.existsSync(CONFIG_DIR)) {
|
|
23
|
-
try {
|
|
25
|
+
try {
|
|
26
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
27
|
+
} catch {}
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
const openBrowser = (url) => {
|
|
27
31
|
const platform = process.platform;
|
|
28
32
|
try {
|
|
29
|
-
if (platform ===
|
|
30
|
-
else if (platform ===
|
|
31
|
-
|
|
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" });
|
|
32
37
|
} catch {}
|
|
33
38
|
};
|
|
34
39
|
|
|
35
40
|
const hasGum = () => {
|
|
36
41
|
try {
|
|
37
|
-
return spawnSync(
|
|
42
|
+
return spawnSync("which", ["gum"], { stdio: "ignore" }).status === 0;
|
|
38
43
|
} catch {
|
|
39
44
|
return false;
|
|
40
45
|
}
|
|
41
46
|
};
|
|
42
47
|
|
|
43
|
-
const gumChoose = (options, header =
|
|
44
|
-
const args = [
|
|
48
|
+
const gumChoose = (options, header = "") => {
|
|
49
|
+
const args = ["choose"];
|
|
45
50
|
if (header) {
|
|
46
|
-
args.push(`--header=${header}`,
|
|
51
|
+
args.push(`--header=${header}`, "--header.foreground=81");
|
|
47
52
|
}
|
|
48
|
-
args.push(
|
|
49
|
-
const res = spawnSync(
|
|
50
|
-
return (res.stdout ||
|
|
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();
|
|
51
56
|
};
|
|
52
57
|
|
|
53
|
-
const gumInput = (promptText, placeholder =
|
|
54
|
-
const args = [
|
|
55
|
-
if (isPassword) args.push(
|
|
56
|
-
const res = spawnSync(
|
|
57
|
-
return (res.stdout ||
|
|
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();
|
|
58
63
|
};
|
|
59
64
|
|
|
60
65
|
const promptQuestion = (query) => {
|
|
@@ -70,19 +75,21 @@ const promptQuestion = (query) => {
|
|
|
70
75
|
const getOrCreateDeviceId = () => {
|
|
71
76
|
if (fs.existsSync(DEVICE_FILE)) {
|
|
72
77
|
try {
|
|
73
|
-
const id = fs.readFileSync(DEVICE_FILE,
|
|
78
|
+
const id = fs.readFileSync(DEVICE_FILE, "utf-8").trim();
|
|
74
79
|
if (id) return id;
|
|
75
80
|
} catch {}
|
|
76
81
|
}
|
|
77
82
|
const newId = `cli_${Math.random().toString(36).substring(2, 12)}_${Date.now()}`;
|
|
78
|
-
try {
|
|
83
|
+
try {
|
|
84
|
+
fs.writeFileSync(DEVICE_FILE, newId, "utf-8");
|
|
85
|
+
} catch {}
|
|
79
86
|
return newId;
|
|
80
87
|
};
|
|
81
88
|
|
|
82
89
|
const getCachedLicenseKey = () => {
|
|
83
90
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
84
91
|
try {
|
|
85
|
-
const data = JSON.parse(fs.readFileSync(CONFIG_FILE,
|
|
92
|
+
const data = JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
|
|
86
93
|
return data.licenseKey || null;
|
|
87
94
|
} catch {
|
|
88
95
|
return null;
|
|
@@ -93,33 +100,45 @@ const getCachedLicenseKey = () => {
|
|
|
93
100
|
|
|
94
101
|
const saveLicenseKey = (licenseKey) => {
|
|
95
102
|
try {
|
|
96
|
-
fs.writeFileSync(
|
|
103
|
+
fs.writeFileSync(
|
|
104
|
+
CONFIG_FILE,
|
|
105
|
+
JSON.stringify({ licenseKey, updatedAt: new Date().toISOString() }, null, 2),
|
|
106
|
+
"utf-8"
|
|
107
|
+
);
|
|
97
108
|
} catch {}
|
|
98
109
|
};
|
|
99
110
|
|
|
100
|
-
const renderBanner = (title =
|
|
111
|
+
const renderBanner = (title = "Chemical X Protocol: Quantum Architecture") => {
|
|
101
112
|
if (hasGum()) {
|
|
102
|
-
spawnSync(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
+
);
|
|
112
127
|
} else {
|
|
113
|
-
process.stdout.write(
|
|
128
|
+
process.stdout.write(
|
|
129
|
+
"\n\x1b[38;2;98;201;255m=====================================================\x1b[0m\n"
|
|
130
|
+
);
|
|
114
131
|
process.stdout.write(`\x1b[1m\x1b[38;2;98;201;255m ${title}\x1b[0m\n`);
|
|
115
|
-
process.stdout.write(
|
|
116
|
-
process.stdout.write(
|
|
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
|
+
);
|
|
117
136
|
}
|
|
118
137
|
};
|
|
119
138
|
|
|
120
139
|
const obtainLicenseKey = async () => {
|
|
121
140
|
let cached = getCachedLicenseKey();
|
|
122
|
-
const cliFlagIdx = rawArgs.indexOf(
|
|
141
|
+
const cliFlagIdx = rawArgs.indexOf("--license");
|
|
123
142
|
if (cliFlagIdx !== -1 && rawArgs[cliFlagIdx + 1]) {
|
|
124
143
|
cached = rawArgs[cliFlagIdx + 1].trim();
|
|
125
144
|
}
|
|
@@ -131,77 +150,96 @@ const obtainLicenseKey = async () => {
|
|
|
131
150
|
const useGum = hasGum();
|
|
132
151
|
|
|
133
152
|
if (useGum) {
|
|
134
|
-
const choice = gumChoose(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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`);
|
|
144
174
|
openBrowser(URL_STANDARD);
|
|
145
|
-
process.stdout.write(
|
|
146
|
-
return gumInput(
|
|
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");
|
|
147
177
|
}
|
|
148
178
|
|
|
149
|
-
if (choice.startsWith(
|
|
150
|
-
process.stdout.write(`\x1b[36mOpening checkout in default browser:\x1b[0m ${URL_MASTER}\n`);
|
|
179
|
+
if (choice.startsWith("3.")) {
|
|
180
|
+
process.stdout.write(`\x1b[36mOpening Master Bundle checkout in default browser:\x1b[0m ${URL_MASTER}\n`);
|
|
151
181
|
openBrowser(URL_MASTER);
|
|
152
|
-
process.stdout.write(
|
|
153
|
-
return gumInput(
|
|
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");
|
|
154
184
|
}
|
|
155
185
|
|
|
156
|
-
if (choice.startsWith(
|
|
157
|
-
return gumInput(
|
|
186
|
+
if (choice.startsWith("4.")) {
|
|
187
|
+
return gumInput("License Key (CX-XXXX-XXXX-XXXX):", "CX-XXXX-XXXX-XXXX");
|
|
158
188
|
}
|
|
159
189
|
|
|
160
|
-
if (choice.startsWith(
|
|
190
|
+
if (choice.startsWith("5.")) {
|
|
161
191
|
await runAudit(null, true);
|
|
162
192
|
}
|
|
163
193
|
|
|
164
|
-
if (choice.startsWith(
|
|
194
|
+
if (choice.startsWith("6.") || !choice) {
|
|
165
195
|
process.exit(0);
|
|
166
196
|
}
|
|
167
197
|
|
|
168
|
-
return gumInput(
|
|
198
|
+
return gumInput("License Key (CX-XXXX-XXXX-XXXX):", "CX-XXXX-XXXX-XXXX");
|
|
169
199
|
}
|
|
170
200
|
|
|
171
|
-
process.stdout.write(
|
|
172
|
-
process.stdout.write(
|
|
173
|
-
process.stdout.write(
|
|
174
|
-
process.stdout.write(
|
|
175
|
-
process.stdout.write(
|
|
176
|
-
process.stdout.write(
|
|
177
|
-
|
|
178
|
-
|
|
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
|
+
}
|
|
179
217
|
|
|
180
|
-
if (
|
|
218
|
+
if (effectiveChoice === "2") {
|
|
181
219
|
process.stdout.write(`Opening: ${URL_STANDARD}\n`);
|
|
182
220
|
openBrowser(URL_STANDARD);
|
|
183
|
-
return promptQuestion(
|
|
221
|
+
return promptQuestion("Enter License Key after purchase: ");
|
|
184
222
|
}
|
|
185
223
|
|
|
186
|
-
if (
|
|
224
|
+
if (effectiveChoice === "3") {
|
|
187
225
|
process.stdout.write(`Opening: ${URL_MASTER}\n`);
|
|
188
226
|
openBrowser(URL_MASTER);
|
|
189
|
-
return promptQuestion(
|
|
227
|
+
return promptQuestion("Enter License Key after purchase: ");
|
|
190
228
|
}
|
|
191
229
|
|
|
192
|
-
if (
|
|
193
|
-
return promptQuestion(
|
|
230
|
+
if (effectiveChoice === "4") {
|
|
231
|
+
return promptQuestion("Enter License Key (CX-XXXX-XXXX-XXXX): ");
|
|
194
232
|
}
|
|
195
233
|
|
|
196
|
-
if (
|
|
234
|
+
if (effectiveChoice === "5") {
|
|
197
235
|
await runAudit(null, true);
|
|
198
236
|
}
|
|
199
237
|
|
|
200
|
-
if (
|
|
238
|
+
if (effectiveChoice === "6") {
|
|
201
239
|
process.exit(0);
|
|
202
240
|
}
|
|
203
241
|
|
|
204
|
-
return promptQuestion(
|
|
242
|
+
return promptQuestion("Enter Chemical X Sponsor License Key (CX-XXXX-XXXX-XXXX): ");
|
|
205
243
|
};
|
|
206
244
|
|
|
207
245
|
const fetchStarterKitFiles = async (licenseKey) => {
|
|
@@ -212,51 +250,61 @@ const fetchStarterKitFiles = async (licenseKey) => {
|
|
|
212
250
|
|
|
213
251
|
try {
|
|
214
252
|
const res = await fetch(`${API_BASE}/api/starter-kit/download`, {
|
|
215
|
-
method:
|
|
216
|
-
headers: {
|
|
253
|
+
method: "POST",
|
|
254
|
+
headers: { "Content-Type": "application/json" },
|
|
217
255
|
body: JSON.stringify({ licenseKey: normalizedKey, deviceId })
|
|
218
256
|
});
|
|
219
257
|
|
|
220
258
|
const responseData = await res.json();
|
|
221
259
|
|
|
222
260
|
if (!res.ok || !responseData.valid) {
|
|
223
|
-
process.stderr.write(
|
|
261
|
+
process.stderr.write(
|
|
262
|
+
`\x1b[31m✕ License Verification Failed: ${responseData.error || "Invalid key."}\x1b[0m\n`
|
|
263
|
+
);
|
|
224
264
|
process.stderr.write(`Purchase key at: ${URL_STANDARD}\n\n`);
|
|
225
265
|
process.exit(1);
|
|
226
266
|
}
|
|
227
267
|
|
|
228
268
|
saveLicenseKey(normalizedKey);
|
|
229
|
-
process.stdout.write(
|
|
269
|
+
process.stdout.write(
|
|
270
|
+
`\x1b[32m✔ Verified License for @${responseData.githubUser || "sponsor"}\x1b[0m\n\n`
|
|
271
|
+
);
|
|
230
272
|
return responseData.files || {};
|
|
231
273
|
} catch (err) {
|
|
232
|
-
process.stderr.write(
|
|
274
|
+
process.stderr.write(
|
|
275
|
+
`\x1b[31m✕ Network Error: Failed to reach edge server (${err.message}).\x1b[0m\n`
|
|
276
|
+
);
|
|
233
277
|
process.exit(1);
|
|
234
278
|
}
|
|
235
279
|
};
|
|
236
280
|
|
|
237
281
|
const runScaffold = async (projectName) => {
|
|
238
|
-
renderBanner(
|
|
282
|
+
renderBanner("Chemical X: Quantum Scaffolder (npm create chemx)");
|
|
239
283
|
|
|
240
284
|
const licenseKey = await obtainLicenseKey();
|
|
241
285
|
if (!licenseKey) {
|
|
242
|
-
process.stderr.write(
|
|
286
|
+
process.stderr.write(
|
|
287
|
+
"\x1b[31m✕ Valid license key is required to scaffold blueprints.\x1b[0m\n"
|
|
288
|
+
);
|
|
243
289
|
process.exit(1);
|
|
244
290
|
}
|
|
245
291
|
|
|
246
292
|
let targetName = projectName;
|
|
247
293
|
if (!targetName) {
|
|
248
294
|
if (hasGum()) {
|
|
249
|
-
targetName = gumInput(
|
|
295
|
+
targetName = gumInput("Project directory name:", "my-quantum-app");
|
|
250
296
|
} else {
|
|
251
|
-
targetName = await promptQuestion(
|
|
297
|
+
targetName = await promptQuestion("Project directory name [my-quantum-app]: ");
|
|
252
298
|
}
|
|
253
299
|
}
|
|
254
300
|
|
|
255
|
-
const finalDirName = targetName.trim() ||
|
|
301
|
+
const finalDirName = targetName.trim() || "my-quantum-app";
|
|
256
302
|
const targetDir = path.resolve(process.cwd(), finalDirName);
|
|
257
303
|
|
|
258
304
|
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
|
259
|
-
process.stderr.write(
|
|
305
|
+
process.stderr.write(
|
|
306
|
+
`\x1b[31m✕ Error: Directory '${finalDirName}' already exists and is not empty.\x1b[0m\n`
|
|
307
|
+
);
|
|
260
308
|
process.exit(1);
|
|
261
309
|
}
|
|
262
310
|
|
|
@@ -271,32 +319,34 @@ const runScaffold = async (projectName) => {
|
|
|
271
319
|
if (!fs.existsSync(dirName)) {
|
|
272
320
|
fs.mkdirSync(dirName, { recursive: true });
|
|
273
321
|
}
|
|
274
|
-
fs.writeFileSync(fullPath, content,
|
|
322
|
+
fs.writeFileSync(fullPath, content, "utf-8");
|
|
275
323
|
process.stdout.write(` \x1b[32m✔\x1b[0m ${relPath}\n`);
|
|
276
324
|
}
|
|
277
325
|
|
|
278
|
-
const cursorRulesPath = path.join(targetDir,
|
|
326
|
+
const cursorRulesPath = path.join(targetDir, ".cursorrules");
|
|
279
327
|
if (!fs.existsSync(cursorRulesPath)) {
|
|
280
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`;
|
|
281
|
-
fs.writeFileSync(cursorRulesPath, rules,
|
|
329
|
+
fs.writeFileSync(cursorRulesPath, rules, "utf-8");
|
|
282
330
|
process.stdout.write(` \x1b[32m✔\x1b[0m .cursorrules\n`);
|
|
283
331
|
}
|
|
284
332
|
|
|
285
|
-
process.stdout.write(
|
|
286
|
-
|
|
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");
|
|
287
337
|
process.stdout.write(` 1. cd ${finalDirName}\n`);
|
|
288
|
-
process.stdout.write(
|
|
289
|
-
process.stdout.write(
|
|
290
|
-
process.stdout.write(
|
|
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");
|
|
291
341
|
};
|
|
292
342
|
|
|
293
|
-
const runInit = async (targetSubDir =
|
|
294
|
-
renderBanner(
|
|
343
|
+
const runInit = async (targetSubDir = "src/chemical-x") => {
|
|
344
|
+
renderBanner("Chemical X: In-Repo Capsule Drop-in");
|
|
295
345
|
|
|
296
346
|
const targetDir = path.resolve(process.cwd(), targetSubDir);
|
|
297
347
|
const licenseKey = await obtainLicenseKey();
|
|
298
348
|
if (!licenseKey) {
|
|
299
|
-
process.stderr.write(
|
|
349
|
+
process.stderr.write("\x1b[31m✕ Valid license key is required.\x1b[0m\n");
|
|
300
350
|
process.exit(1);
|
|
301
351
|
}
|
|
302
352
|
|
|
@@ -311,16 +361,18 @@ const runInit = async (targetSubDir = 'src/chemical-x') => {
|
|
|
311
361
|
if (!fs.existsSync(dirName)) {
|
|
312
362
|
fs.mkdirSync(dirName, { recursive: true });
|
|
313
363
|
}
|
|
314
|
-
fs.writeFileSync(fullPath, content,
|
|
364
|
+
fs.writeFileSync(fullPath, content, "utf-8");
|
|
315
365
|
process.stdout.write(` \x1b[32m✔\x1b[0m ${relPath}\n`);
|
|
316
366
|
count++;
|
|
317
367
|
}
|
|
318
368
|
|
|
319
|
-
process.stdout.write(
|
|
369
|
+
process.stdout.write(
|
|
370
|
+
`\n\x1b[1m\x1b[32m✔ Successfully installed ${count} Chemical X assets into ${targetSubDir}!\x1b[0m\n\n`
|
|
371
|
+
);
|
|
320
372
|
};
|
|
321
373
|
|
|
322
374
|
const runGenerateCapsule = (capsuleName) => {
|
|
323
|
-
const normalizedName = capsuleName.startsWith(
|
|
375
|
+
const normalizedName = capsuleName.startsWith("m-") ? capsuleName : `m-${capsuleName}`;
|
|
324
376
|
const targetDir = path.resolve(process.cwd(), normalizedName);
|
|
325
377
|
|
|
326
378
|
if (fs.existsSync(targetDir)) {
|
|
@@ -331,9 +383,9 @@ const runGenerateCapsule = (capsuleName) => {
|
|
|
331
383
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
332
384
|
|
|
333
385
|
const pascalName = normalizedName
|
|
334
|
-
.split(
|
|
386
|
+
.split("-")
|
|
335
387
|
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
336
|
-
.join(
|
|
388
|
+
.join("");
|
|
337
389
|
|
|
338
390
|
const componentCode = `import React from 'react';
|
|
339
391
|
import type { ${pascalName}Props } from './types';
|
|
@@ -358,37 +410,46 @@ export default ${pascalName};
|
|
|
358
410
|
export type { ${pascalName}Props } from './types';
|
|
359
411
|
`;
|
|
360
412
|
|
|
361
|
-
fs.writeFileSync(path.join(targetDir, `${normalizedName}.tsx`), componentCode,
|
|
362
|
-
fs.writeFileSync(path.join(targetDir,
|
|
363
|
-
fs.writeFileSync(path.join(targetDir,
|
|
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");
|
|
364
416
|
|
|
365
|
-
process.stdout.write(
|
|
417
|
+
process.stdout.write(
|
|
418
|
+
`\x1b[32m✔ Successfully generated crystalline capsule:\x1b[0m ${normalizedName}/\n`
|
|
419
|
+
);
|
|
366
420
|
process.stdout.write(` - ${normalizedName}/${normalizedName}.tsx (< 50 lines)\n`);
|
|
367
421
|
process.stdout.write(` - ${normalizedName}/types.d.ts\n`);
|
|
368
422
|
process.stdout.write(` - ${normalizedName}/index.ts\n\n`);
|
|
369
423
|
};
|
|
370
424
|
|
|
371
425
|
export const runAudit = async (customDir = null, isCli = false) => {
|
|
372
|
-
const isJson = rawArgs.includes(
|
|
373
|
-
const dirFlag = rawArgs.find((arg) => arg.startsWith(
|
|
374
|
-
const targetDir =
|
|
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" : ".");
|
|
375
430
|
|
|
376
431
|
const report = executeAstAudit(targetDir);
|
|
377
432
|
|
|
378
433
|
if (isJson) {
|
|
379
|
-
process.stdout.write(JSON.stringify(report, null, 2) +
|
|
434
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
380
435
|
if (isCli) process.exit(report.violations.length > 0 ? 1 : 0);
|
|
381
436
|
return report;
|
|
382
437
|
}
|
|
383
438
|
|
|
384
|
-
process.stdout.write(
|
|
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
|
+
);
|
|
385
442
|
process.stdout.write(`Target Directory: ${targetDir}\n`);
|
|
386
443
|
process.stdout.write(`Scanned ${report.scannedFiles} source files.\n\n`);
|
|
387
444
|
|
|
388
445
|
if (report.violations.length === 0) {
|
|
389
|
-
process.stdout.write(
|
|
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
|
+
);
|
|
390
449
|
} else {
|
|
391
|
-
process.stdout.write(
|
|
450
|
+
process.stdout.write(
|
|
451
|
+
`\x1b[31m✕ FAILED: ${report.totalViolations} context hazard violations detected:\x1b[0m\n\n`
|
|
452
|
+
);
|
|
392
453
|
for (const v of report.violations) {
|
|
393
454
|
process.stdout.write(` \x1b[31m[${v.rule}]\x1b[0m \x1b[33m${v.filePath}:${v.line}\x1b[0m\n`);
|
|
394
455
|
process.stdout.write(` Hazard: ${v.hazard}\n`);
|
|
@@ -399,56 +460,95 @@ export const runAudit = async (customDir = null, isCli = false) => {
|
|
|
399
460
|
if (isCli) {
|
|
400
461
|
while (true) {
|
|
401
462
|
if (hasGum()) {
|
|
402
|
-
spawnSync(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
+
);
|
|
420
498
|
openBrowser(URL_STANDARD);
|
|
421
499
|
continue;
|
|
422
500
|
}
|
|
423
|
-
if (choice.startsWith(
|
|
424
|
-
process.stdout.write(
|
|
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
|
+
);
|
|
425
505
|
openBrowser(URL_MASTER);
|
|
426
506
|
continue;
|
|
427
507
|
}
|
|
428
|
-
if (choice.startsWith(
|
|
508
|
+
if (choice.startsWith("4.")) {
|
|
429
509
|
await runScaffold();
|
|
430
510
|
break;
|
|
431
511
|
}
|
|
432
512
|
break;
|
|
433
513
|
} else {
|
|
434
|
-
process.stdout.write(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
process.stdout.write(
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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") {
|
|
442
542
|
process.stdout.write(`\nOpening: ${URL_STANDARD}\n\n`);
|
|
443
543
|
openBrowser(URL_STANDARD);
|
|
444
544
|
continue;
|
|
445
545
|
}
|
|
446
|
-
if (
|
|
546
|
+
if (effectiveSelection === "3") {
|
|
447
547
|
process.stdout.write(`\nOpening: ${URL_MASTER}\n\n`);
|
|
448
548
|
openBrowser(URL_MASTER);
|
|
449
549
|
continue;
|
|
450
550
|
}
|
|
451
|
-
if (
|
|
551
|
+
if (effectiveSelection === "4") {
|
|
452
552
|
await runScaffold();
|
|
453
553
|
break;
|
|
454
554
|
}
|
|
@@ -464,50 +564,60 @@ export { auditFile };
|
|
|
464
564
|
|
|
465
565
|
const printHelp = () => {
|
|
466
566
|
renderBanner();
|
|
467
|
-
process.stdout.write(
|
|
468
|
-
process.stdout.write(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
process.stdout.write(
|
|
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
|
+
);
|
|
472
580
|
};
|
|
473
581
|
|
|
474
582
|
const main = async () => {
|
|
475
583
|
const firstArg = rawArgs[0];
|
|
476
584
|
|
|
477
585
|
if (isCreateInvoked) {
|
|
478
|
-
const dirArg = firstArg ===
|
|
586
|
+
const dirArg = firstArg === "create" ? rawArgs[1] : firstArg;
|
|
479
587
|
await runScaffold(dirArg);
|
|
480
588
|
return;
|
|
481
589
|
}
|
|
482
590
|
|
|
483
591
|
switch (firstArg) {
|
|
484
|
-
case
|
|
592
|
+
case "audit":
|
|
485
593
|
await runAudit(null, true);
|
|
486
594
|
break;
|
|
487
|
-
case
|
|
488
|
-
await runInit(rawArgs[1] ||
|
|
595
|
+
case "init":
|
|
596
|
+
await runInit(rawArgs[1] || "src/chemical-x");
|
|
489
597
|
break;
|
|
490
|
-
case
|
|
598
|
+
case "create":
|
|
491
599
|
await runScaffold(rawArgs[1]);
|
|
492
600
|
break;
|
|
493
|
-
case
|
|
494
|
-
case
|
|
495
|
-
case
|
|
601
|
+
case "generate":
|
|
602
|
+
case "capsule":
|
|
603
|
+
case "add":
|
|
496
604
|
if (!rawArgs[1]) {
|
|
497
|
-
process.stderr.write(
|
|
605
|
+
process.stderr.write(
|
|
606
|
+
"Usage: npx chemx generate <capsule-name>\nExample: npx chemx generate m-user-avatar\n"
|
|
607
|
+
);
|
|
498
608
|
process.exit(1);
|
|
499
609
|
}
|
|
500
610
|
runGenerateCapsule(rawArgs[1]);
|
|
501
611
|
break;
|
|
502
|
-
case
|
|
503
|
-
case
|
|
504
|
-
case
|
|
612
|
+
case "help":
|
|
613
|
+
case "--help":
|
|
614
|
+
case "-h":
|
|
505
615
|
printHelp();
|
|
506
616
|
break;
|
|
507
617
|
default:
|
|
508
|
-
if (firstArg && firstArg.startsWith(
|
|
618
|
+
if (firstArg && firstArg.startsWith("m-")) {
|
|
509
619
|
runGenerateCapsule(firstArg);
|
|
510
|
-
} else if (firstArg && !firstArg.startsWith(
|
|
620
|
+
} else if (firstArg && !firstArg.startsWith("-")) {
|
|
511
621
|
await runScaffold(firstArg);
|
|
512
622
|
} else {
|
|
513
623
|
printHelp();
|
|
@@ -520,4 +630,3 @@ main().catch((err) => {
|
|
|
520
630
|
process.stderr.write(`\x1b[31m✕ Unexpected Error: ${err.message}\x1b[0m\n`);
|
|
521
631
|
process.exit(1);
|
|
522
632
|
});
|
|
523
|
-
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -37,7 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
37
37
|
- Unlocked `npx chemx audit` command as 100% free, unauthenticated, and ungated public utility with conversion CTAs.
|
|
38
38
|
- Added dual project scaffolder (`npm create chemx` / `create-chemx`) and in-repo capsule drop-in (`init`).
|
|
39
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 ($
|
|
40
|
+
- Integrated interactive Gum CTA action buttons at the conclusion of public audit for default portal browsing (`chemicalx.xophz.com`), instant checkout launch ($27 eBook w/ AGENTS.md / $47 Master Bundle), and key-gated scaffolding for license holders.
|
|
41
41
|
|
|
42
42
|
### Fixed
|
|
43
43
|
- Removed embedded offline blueprint fallbacks and preview bypass keys from CLI executable (`cli/index.js`).
|