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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/cli/audit/ast-visitors.js +243 -0
  2. package/cli/audit/discussion-store.js +39 -0
  3. package/cli/audit/history.d.ts +63 -0
  4. package/cli/audit/history.js +352 -0
  5. package/cli/audit/metrics.js +179 -0
  6. package/cli/audit/prompts.js +174 -0
  7. package/cli/audit/reporter-grades.js +259 -0
  8. package/cli/audit/reporter-markdown.js +136 -0
  9. package/cli/audit/reporter-utils.js +107 -0
  10. package/cli/audit/reporter.js +459 -0
  11. package/cli/audit/rules-helpers.js +109 -0
  12. package/cli/audit/rules-registry.js +97 -0
  13. package/cli/audit/rules.js +123 -0
  14. package/cli/audit/social-constants.js +4 -0
  15. package/cli/audit/social-gh.js +126 -0
  16. package/cli/audit/social-git.js +214 -0
  17. package/cli/audit/social-http.js +222 -0
  18. package/cli/audit/social-publisher.js +193 -0
  19. package/cli/audit/social.d.ts +66 -0
  20. package/cli/audit/social.js +254 -0
  21. package/cli/audit/types.d.ts +131 -0
  22. package/cli/audit.d.ts +3 -24
  23. package/cli/audit.js +195 -210
  24. package/cli/help.d.ts +1 -0
  25. package/cli/help.js +136 -0
  26. package/cli/index.js +128 -566
  27. package/cli/installer-templates.js +80 -0
  28. package/cli/installer.d.ts +20 -0
  29. package/cli/installer.js +56 -0
  30. package/cli/license.js +320 -0
  31. package/cli/navigator-conversion.js +100 -0
  32. package/cli/navigator-grades.js +70 -0
  33. package/cli/navigator-paged.js +86 -0
  34. package/cli/navigator-share.js +129 -0
  35. package/cli/navigator.d.ts +24 -0
  36. package/cli/navigator.js +400 -0
  37. package/cli/scaffold.js +148 -0
  38. package/cli/terminal.js +126 -0
  39. package/docs/CHANGELOG.md +73 -0
  40. package/package.json +11 -10
@@ -0,0 +1,80 @@
1
+ export const buildPreCommitHookScript = (minGrade = 'B', minScore = 80) => `#!/bin/sh
2
+ # Chemical X Protocol: Pre-Commit Line Budget & Architecture Gatekeeper
3
+ # Free architectural guardrail preventing context bloat and monolith sprawl.
4
+
5
+ MIN_GRADE="\${CHEMX_MIN_GRADE:-${minGrade}}"
6
+ MIN_SCORE="\${CHEMX_MIN_SCORE:-${minScore}}"
7
+ MAX_LINES="\${CHEMX_MAX_LINES:-500}"
8
+ MAX_MOLECULE_LINES="\${CHEMX_MAX_MOLECULE_LINES:-100}"
9
+
10
+ REPO_ROOT="\$(git rev-parse --show-toplevel 2>/dev/null)"
11
+ [ -z "\$REPO_ROOT" ] && exit 0
12
+ cd "\$REPO_ROOT" || exit 1
13
+
14
+ STAGED_FILES=\$(git diff --cached --name-only --diff-filter=ACM | grep -E '\\.(jsx?|tsx?|vue|svelte)\$' | grep -vE '(\\.(d\\.ts|min\\.|test\\.|spec\\.))')
15
+ [ -z "\$STAGED_FILES" ] && exit 0
16
+
17
+ FAILED=0
18
+ ERRORS=""
19
+ for F in \$STAGED_FILES; do
20
+ [ ! -f "\$F" ] && continue
21
+ L=\$(wc -l < "\$F" | tr -d ' ')
22
+ case "\$F" in
23
+ *molecules*|*/m-*|*m-*)
24
+ [ "\$L" -gt "\$MAX_MOLECULE_LINES" ] && FAILED=1 && ERRORS="\${ERRORS}\\n \\033[31m✕\\033[0m \$F (\$L LOC > \$MAX_MOLECULE_LINES molecule limit)" ;;
25
+ *)
26
+ [ "\$L" -gt "\$MAX_LINES" ] && FAILED=1 && ERRORS="\${ERRORS}\\n \\033[31m✕\\033[0m \$F (\$L LOC > \$MAX_LINES file budget)" ;;
27
+ esac
28
+ done
29
+
30
+ if [ "\$FAILED" -eq 1 ]; then
31
+ printf "\\n\\033[1m\\033[31m[Chemical X] Commit Blocked: Staged files exceed architectural line budgets\\033[0m\\n\$ERRORS\\n\\n"
32
+ fi
33
+
34
+ if command -v npx >/dev/null 2>&1; then
35
+ printf "\\033[38;2;98;201;255m[Chemical X] Verifying architectural health (Min Grade: %s, Min Score: %s)...\\033[0m\\n" "\$MIN_GRADE" "\$MIN_SCORE"
36
+ if ! npx chemx audit --min-grade="\$MIN_GRADE" --min-score="\$MIN_SCORE" --prompt-on-fail; then
37
+ printf "\\n\\033[1m\\033[31m[Chemical X] Commit Blocked: Codebase falls below required Grade %s (Score %s)\\033[0m\\n" "\$MIN_GRADE" "\$MIN_SCORE"
38
+ printf "\\033[36m💡 Tip: Want crystalline drop-in templates? Run 'npm create chemx' or sponsor at https://github.com/sponsors/Chemical-X-Protocol\\033[0m\\n\\n"
39
+ exit 1
40
+ fi
41
+ fi
42
+
43
+ [ "\$FAILED" -eq 1 ] && exit 1
44
+ printf "\\033[32m✔ [Chemical X] Pre-commit architectural guardrails passed.\\033[0m\\n"
45
+ exit 0
46
+ `;
47
+
48
+ export const buildGitHubWorkflowScript = (minGrade = 'B', minScore = 80) => `name: Chemical X Architectural Gatekeeper
49
+
50
+ on:
51
+ push:
52
+ branches: [main, master]
53
+ pull_request:
54
+ branches: [main, master]
55
+
56
+ permissions:
57
+ contents: read
58
+ pull-requests: write
59
+
60
+ jobs:
61
+ molecular-audit:
62
+ name: Chemical X Architecture & Line Budget Audit
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - name: Checkout Code
66
+ uses: actions/checkout@v4
67
+ - name: Setup Node.js
68
+ uses: actions/setup-node@v4
69
+ with:
70
+ node-version: 20
71
+ cache: npm
72
+ - name: Run Chemical X Architectural Audit
73
+ run: npx chemx audit --min-grade=\${{ vars.CHEMX_MIN_GRADE || '${minGrade}' }} --min-score=\${{ vars.CHEMX_MIN_SCORE || ${minScore} }} --markdown --output=AUDIT_REPORT.md
74
+ - name: Upload Audit Scorecard Artifact
75
+ if: always()
76
+ uses: actions/upload-artifact@v4
77
+ with:
78
+ name: chemical-x-audit-report
79
+ path: AUDIT_REPORT.md
80
+ `;
@@ -0,0 +1,20 @@
1
+ export interface InstallOptions {
2
+ minGrade?: string;
3
+ minScore?: number;
4
+ maxLines?: number;
5
+ maxMoleculeLines?: number;
6
+ }
7
+
8
+ export interface ProjectConfig {
9
+ minGrade: string;
10
+ minScore: number;
11
+ maxLineCount: number;
12
+ maxMoleculeLineCount: number;
13
+ }
14
+
15
+ export declare function buildPreCommitHookScript(minGrade?: string, minScore?: number): string;
16
+ export declare function buildGitHubWorkflowScript(minGrade?: string, minScore?: number): string;
17
+ export declare function installPreCommitHook(targetDir?: string, options?: InstallOptions): boolean;
18
+ export declare function installGitHubWorkflow(targetDir?: string, options?: InstallOptions): boolean;
19
+ export declare function saveProjectConfig(targetDir?: string, config?: Partial<ProjectConfig>): void;
20
+ export declare function runInstallWizard(targetDir?: string): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { hasGum, gumChoose, gumInput, promptQuestion } from './terminal.js';
4
+ import { buildPreCommitHookScript, buildGitHubWorkflowScript } from './installer-templates.js';
5
+
6
+ export { buildPreCommitHookScript, buildGitHubWorkflowScript } from './installer-templates.js';
7
+
8
+ export const installPreCommitHook = (targetDir = '.', options = {}) => {
9
+ const gitHooksDir = path.resolve(targetDir, '.git', 'hooks');
10
+ if (!fs.existsSync(gitHooksDir)) fs.mkdirSync(gitHooksDir, { recursive: true });
11
+ const hookPath = path.join(gitHooksDir, 'pre-commit');
12
+ fs.writeFileSync(hookPath, buildPreCommitHookScript(options.minGrade, options.minScore), { mode: 0o755 });
13
+ process.stdout.write(` \x1b[32m✔\x1b[0m Installed git pre-commit hook: .git/hooks/pre-commit (chmod +x)\n`);
14
+ return true;
15
+ };
16
+
17
+ export const installGitHubWorkflow = (targetDir = '.', options = {}) => {
18
+ const wfDir = path.resolve(targetDir, '.github', 'workflows');
19
+ if (!fs.existsSync(wfDir)) fs.mkdirSync(wfDir, { recursive: true });
20
+ const wfPath = path.join(wfDir, 'chemx-audit.yml');
21
+ fs.writeFileSync(wfPath, buildGitHubWorkflowScript(options.minGrade, options.minScore), 'utf-8');
22
+ process.stdout.write(` \x1b[32m✔\x1b[0m Installed GitHub Actions CI workflow: .github/workflows/chemx-audit.yml\n`);
23
+ return true;
24
+ };
25
+
26
+ export const saveProjectConfig = (targetDir = '.', config = {}) => {
27
+ const chemxDir = path.resolve(targetDir, '.chemx');
28
+ if (!fs.existsSync(chemxDir)) fs.mkdirSync(chemxDir, { recursive: true });
29
+ fs.writeFileSync(path.join(chemxDir, 'config.json'), JSON.stringify(config, null, 2), 'utf-8');
30
+ process.stdout.write(` \x1b[32m✔\x1b[0m Saved project settings to: .chemx/config.json\n`);
31
+ };
32
+
33
+ export const runInstallWizard = async (targetDir = '.') => {
34
+ const isGit = fs.existsSync(path.resolve(targetDir, '.git'));
35
+ process.stdout.write('\n\x1b[1m\x1b[38;2;98;201;255mChemical X: Architecture Guardrail Installer\x1b[0m\n\n');
36
+ const targetChoice = hasGum()
37
+ ? gumChoose(['1. Install All Guardrails (Git Pre-Commit Hook + GitHub CI Workflow)', '2. Git Pre-Commit Hook only (.git/hooks/pre-commit)', '3. GitHub Actions CI Workflow only (.github/workflows/chemx-audit.yml)', '4. Cancel'])
38
+ : await promptQuestion('Select target: [1] All, [2] Hook, [3] CI, [4] Cancel (default: 1): ');
39
+ if (targetChoice?.includes('Cancel') || targetChoice === '4') return;
40
+
41
+ const minGrade = (hasGum() ? gumInput('Minimum required Grade [A+, A, B, C, D] (default: B):', 'B') : await promptQuestion('Minimum required Grade [default: B]: ')) || 'B';
42
+ const minScore = parseInt((hasGum() ? gumInput('Minimum required Score [0-100] (default: 80):', '80') : await promptQuestion('Minimum required Score [default: 80]: ')) || '80', 10);
43
+ const opts = { minGrade: minGrade.trim().toUpperCase(), minScore };
44
+
45
+ process.stdout.write('\n\x1b[1mInstalling guardrails...\x1b[0m\n');
46
+ const shouldHook = !targetChoice.includes('CI only') && targetChoice !== '3';
47
+ const shouldWf = !targetChoice.includes('Hook only') && targetChoice !== '2';
48
+
49
+ if (shouldHook) {
50
+ if (isGit) installPreCommitHook(targetDir, opts);
51
+ else process.stdout.write(' \x1b[33m⚠\x1b[0m Skipped .git/hooks (current directory is not a git repository root).\n');
52
+ }
53
+ if (shouldWf) installGitHubWorkflow(targetDir, opts);
54
+ saveProjectConfig(targetDir, { minGrade: opts.minGrade, minScore: opts.minScore, maxLineCount: 500, maxMoleculeLineCount: 100 });
55
+ process.stdout.write('\n\x1b[1m\x1b[32m✔ Chemical X guardrails installed successfully!\x1b[0m\n\n');
56
+ };
package/cli/license.js ADDED
@@ -0,0 +1,320 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import {
5
+ hasGum,
6
+ gumChoose,
7
+ gumInput,
8
+ promptQuestion,
9
+ openBrowser
10
+ } from './terminal.js';
11
+
12
+ export const CONFIG_DIR = path.join(os.homedir(), '.chemical-x');
13
+ export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
14
+ export const DEVICE_FILE = path.join(CONFIG_DIR, 'device_id');
15
+
16
+ export const API_BASE = process.env.CHEMICAL_X_API_URL || 'https://chemicalx.xophz.com';
17
+ export const URL_LEARN = 'https://chemicalx.xophz.com';
18
+ export const URL_SPONSOR = 'https://github.com/sponsors/Chemical-X-Protocol';
19
+ export const URL_STANDARD = 'https://mycompassconsulting.com/buy/chemical-x/standard';
20
+ export const URL_MASTER = 'https://mycompassconsulting.com/buy/chemical-x/master';
21
+
22
+ if (!fs.existsSync(CONFIG_DIR)) {
23
+ try {
24
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
25
+ } catch {}
26
+ }
27
+
28
+ export const getOrCreateDeviceId = () => {
29
+ if (fs.existsSync(DEVICE_FILE)) {
30
+ try {
31
+ const id = fs.readFileSync(DEVICE_FILE, 'utf-8').trim();
32
+ if (id) return id;
33
+ } catch {}
34
+ }
35
+ const newId = `cli_${Math.random().toString(36).substring(2, 12)}_${Date.now()}`;
36
+ try {
37
+ fs.writeFileSync(DEVICE_FILE, newId, 'utf-8');
38
+ } catch {}
39
+ return newId;
40
+ };
41
+
42
+ export const getCachedLicenseKey = () => {
43
+ if (!fs.existsSync(CONFIG_FILE)) {
44
+ return null;
45
+ }
46
+ try {
47
+ const data = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
48
+ return data.licenseKey || null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ };
53
+
54
+ export const saveLicenseKey = (licenseKey) => {
55
+ try {
56
+ fs.writeFileSync(
57
+ CONFIG_FILE,
58
+ JSON.stringify({ licenseKey, updatedAt: new Date().toISOString() }, null, 2),
59
+ 'utf-8'
60
+ );
61
+ } catch {}
62
+ };
63
+
64
+ const resolveFlagLicense = (args) => {
65
+ const cliFlagIdx = args.indexOf('--license');
66
+ const hasFlag = cliFlagIdx !== -1 && Boolean(args[cliFlagIdx + 1]);
67
+ if (!hasFlag) return null;
68
+ return args[cliFlagIdx + 1].trim();
69
+ };
70
+
71
+ export const obtainLicenseKey = async (rawArgs = [], onRunAudit = null) => {
72
+ const flagKey = resolveFlagLicense(rawArgs);
73
+ const effectiveKey = flagKey || getCachedLicenseKey();
74
+
75
+ if (effectiveKey) {
76
+ return effectiveKey;
77
+ }
78
+
79
+ const useGum = hasGum();
80
+
81
+ if (useGum) {
82
+ const choice = gumChoose(
83
+ [
84
+ '1. Visit chemicalx.xophz.com to learn more',
85
+ '2. Buy Standard Vault: Single Dev License ($27) -> Launch Checkout',
86
+ '3. Buy Team Power Puff: 100 Devs / Seats ($47) -> Launch Checkout',
87
+ '4. Enter License Key (CX-XXXX-XXXX-XXXX)',
88
+ '5. Run Free Public Audit (npx chemx audit)',
89
+ '6. Exit'
90
+ ],
91
+ 'Chemical X Scaffolding Requires a Paid License:'
92
+ );
93
+
94
+ if (choice.startsWith('1.')) {
95
+ process.stdout.write(`\x1b[36mOpening Chemical X Portal in default browser:\x1b[0m ${URL_LEARN}\n`);
96
+ openBrowser(URL_LEARN);
97
+ process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
98
+ return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
99
+ }
100
+
101
+ if (choice.startsWith('2.')) {
102
+ process.stdout.write(`\x1b[36mOpening Standard Vault checkout (Single Dev License) in default browser:\x1b[0m ${URL_STANDARD}\n`);
103
+ openBrowser(URL_STANDARD);
104
+ process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
105
+ return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
106
+ }
107
+
108
+ if (choice.startsWith('3.')) {
109
+ process.stdout.write(`\x1b[36mOpening Team Power Puff checkout (100 Devs / 100 Seats) in default browser:\x1b[0m ${URL_MASTER}\n`);
110
+ openBrowser(URL_MASTER);
111
+ process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
112
+ return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
113
+ }
114
+
115
+ if (choice.startsWith('4.')) {
116
+ return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
117
+ }
118
+
119
+ if (choice.startsWith('5.') && onRunAudit) {
120
+ await onRunAudit(null, true);
121
+ }
122
+
123
+ const isExitChoice = choice.startsWith('6.') || !choice;
124
+ if (isExitChoice) {
125
+ process.exit(0);
126
+ }
127
+
128
+ return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
129
+ }
130
+
131
+ process.stdout.write('\x1b[1mChemical X Scaffolding Requires a Paid License:\x1b[0m\n');
132
+ process.stdout.write(' [1] Visit chemicalx.xophz.com to learn more\n');
133
+ process.stdout.write(' [2] Buy Standard Vault: Single Dev License ($27) - Opens browser\n');
134
+ process.stdout.write(' [3] Buy Team Power Puff: 100 Devs / Seats ($47) - Opens browser\n');
135
+ process.stdout.write(' [4] Enter License Key\n');
136
+ process.stdout.write(' [5] Run Free Public Audit (npx chemx audit)\n');
137
+ process.stdout.write(' [6] Exit\n\n');
138
+
139
+ const selection = await promptQuestion('Select option [1-6] (default: 1): ');
140
+ const effectiveChoice = selection.trim() || '1';
141
+
142
+ if (effectiveChoice === '1') {
143
+ process.stdout.write(`Opening: ${URL_LEARN}\n`);
144
+ openBrowser(URL_LEARN);
145
+ return promptQuestion('Enter License Key after review (or press Enter to exit): ');
146
+ }
147
+
148
+ if (effectiveChoice === '2') {
149
+ process.stdout.write(`Opening: ${URL_STANDARD}\n`);
150
+ openBrowser(URL_STANDARD);
151
+ return promptQuestion('Enter License Key after purchase: ');
152
+ }
153
+
154
+ if (effectiveChoice === '3') {
155
+ process.stdout.write(`Opening: ${URL_MASTER}\n`);
156
+ openBrowser(URL_MASTER);
157
+ return promptQuestion('Enter License Key after purchase: ');
158
+ }
159
+
160
+ if (effectiveChoice === '4') {
161
+ return promptQuestion('Enter License Key (CX-XXXX-XXXX-XXXX): ');
162
+ }
163
+
164
+ if (effectiveChoice === '5' && onRunAudit) {
165
+ await onRunAudit(null, true);
166
+ }
167
+
168
+ if (effectiveChoice === '6') {
169
+ process.exit(0);
170
+ }
171
+
172
+ return promptQuestion('Enter Chemical X Sponsor License Key (CX-XXXX-XXXX-XXXX): ');
173
+ };
174
+
175
+ export const checkOrPromptEvaluation = async (actionLabel = 'generate capsule') => {
176
+ const cachedKey = getCachedLicenseKey();
177
+ if (cachedKey) {
178
+ return { licensed: true, key: cachedKey };
179
+ }
180
+
181
+ const useGum = hasGum();
182
+ if (useGum) {
183
+ spawnSync(
184
+ 'gum',
185
+ [
186
+ 'style',
187
+ '--border=rounded',
188
+ '--border-foreground=81',
189
+ '--padding=0 1',
190
+ '--bold',
191
+ '⚡ Chemical X: Evaluation Mode (Unlicensed)\n' +
192
+ 'Support vibe coding standards: chemicalx.xophz.com\n' +
193
+ 'Single Dev ($27) | Team Power Puff 💯 (100 Seats: $47)'
194
+ ],
195
+ { stdio: 'inherit' }
196
+ );
197
+
198
+ const choice = gumChoose(
199
+ [
200
+ `1. ⚡ Continue in Evaluation Mode (Press Enter to ${actionLabel})`,
201
+ '2. 🔑 Enter License Key (Power Puff Team 💯 or Single Dev)',
202
+ '3. 💎 Buy License ($27 Solo / $47 Team Power Puff 100 Seats)',
203
+ '4. 🚪 Cancel'
204
+ ],
205
+ `Evaluation Mode: ${actionLabel}`
206
+ );
207
+
208
+ if (choice.startsWith('1.') || !choice) {
209
+ return { licensed: false, proceed: true };
210
+ }
211
+
212
+ if (choice.startsWith('2.')) {
213
+ const enteredKey = gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
214
+ if (enteredKey && enteredKey !== 'CX-XXXX-XXXX-XXXX') {
215
+ saveLicenseKey(enteredKey.trim().toUpperCase());
216
+ process.stdout.write('\x1b[32m✔ License saved to ~/.chemical-x/config.json!\x1b[0m\n\n');
217
+ return { licensed: true, key: enteredKey.trim().toUpperCase() };
218
+ }
219
+ return { licensed: false, proceed: true };
220
+ }
221
+
222
+ if (choice.startsWith('3.')) {
223
+ openBrowser(URL_MASTER);
224
+ process.stdout.write(`\x1b[36mOpened checkout in default browser:\x1b[0m ${URL_MASTER}\n`);
225
+ const keyAfterBuy = gumInput('Enter License Key once purchased (or press Enter to skip):');
226
+ if (keyAfterBuy) {
227
+ saveLicenseKey(keyAfterBuy.trim().toUpperCase());
228
+ process.stdout.write('\x1b[32m✔ License saved to ~/.chemical-x/config.json!\x1b[0m\n\n');
229
+ return { licensed: true, key: keyAfterBuy.trim().toUpperCase() };
230
+ }
231
+ return { licensed: false, proceed: true };
232
+ }
233
+
234
+ if (choice.startsWith('4.')) {
235
+ process.exit(0);
236
+ }
237
+
238
+ return { licensed: false, proceed: true };
239
+ }
240
+
241
+ process.stdout.write(
242
+ '\n\x1b[38;5;208;1m⚡ Chemical X: Evaluation Mode (Unlicensed)\x1b[0m\n' +
243
+ 'Support vibe coding standards: chemicalx.xophz.com\n' +
244
+ ` [1] Continue in Evaluation Mode (Press Enter to ${actionLabel})\n` +
245
+ ' [2] Enter License Key (Power Puff Team 💯 or Single Dev)\n' +
246
+ ' [3] Buy License ($27 Solo / $47 Team Power Puff 100 Seats)\n' +
247
+ ' [4] Cancel\n\n'
248
+ );
249
+
250
+ const sel = await promptQuestion('Select option [1-4] (default: 1): ');
251
+ const choice = sel.trim() || '1';
252
+
253
+ if (choice === '1') {
254
+ return { licensed: false, proceed: true };
255
+ }
256
+
257
+ if (choice === '2') {
258
+ const entered = await promptQuestion('Enter License Key (CX-XXXX-XXXX-XXXX): ');
259
+ if (entered) {
260
+ saveLicenseKey(entered.trim().toUpperCase());
261
+ process.stdout.write('\x1b[32m✔ License saved to ~/.chemical-x/config.json!\x1b[0m\n\n');
262
+ return { licensed: true, key: entered.trim().toUpperCase() };
263
+ }
264
+ return { licensed: false, proceed: true };
265
+ }
266
+
267
+ if (choice === '3') {
268
+ openBrowser(URL_MASTER);
269
+ const entered = await promptQuestion('Enter License Key after purchase (or Enter to skip): ');
270
+ if (entered) {
271
+ saveLicenseKey(entered.trim().toUpperCase());
272
+ process.stdout.write('\x1b[32m✔ License saved to ~/.chemical-x/config.json!\x1b[0m\n\n');
273
+ return { licensed: true, key: entered.trim().toUpperCase() };
274
+ }
275
+ return { licensed: false, proceed: true };
276
+ }
277
+
278
+ if (choice === '4') {
279
+ process.exit(0);
280
+ }
281
+
282
+ return { licensed: false, proceed: true };
283
+ };
284
+
285
+ export const fetchStarterKitFiles = async (licenseKey) => {
286
+ const normalizedKey = licenseKey.trim().toUpperCase();
287
+ const deviceId = getOrCreateDeviceId();
288
+
289
+ process.stdout.write(`\nVerifying license via edge: ${API_BASE}...\n`);
290
+
291
+ try {
292
+ const res = await fetch(`${API_BASE}/api/starter-kit/download`, {
293
+ method: 'POST',
294
+ headers: { 'Content-Type': 'application/json' },
295
+ body: JSON.stringify({ licenseKey: normalizedKey, deviceId })
296
+ });
297
+
298
+ const responseData = await res.json();
299
+ const isSuccess = Boolean(res.ok && responseData.valid);
300
+
301
+ if (!isSuccess) {
302
+ process.stderr.write(
303
+ `\x1b[31m✕ License Verification Failed: ${responseData.error || 'Invalid key.'}\x1b[0m\n`
304
+ );
305
+ process.stderr.write(`Purchase key at: ${URL_STANDARD}\n\n`);
306
+ process.exit(1);
307
+ }
308
+
309
+ saveLicenseKey(normalizedKey);
310
+ process.stdout.write(
311
+ `\x1b[32m✔ Verified License for @${responseData.githubUser || 'sponsor'}\x1b[0m\n\n`
312
+ );
313
+ return responseData.files || {};
314
+ } catch (err) {
315
+ process.stderr.write(
316
+ `\x1b[31m✕ Network Error: Failed to reach edge server (${err.message}).\x1b[0m\n`
317
+ );
318
+ process.exit(1);
319
+ }
320
+ };
@@ -0,0 +1,100 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import {
3
+ hasGum,
4
+ gumChoose,
5
+ promptQuestion,
6
+ openBrowser
7
+ } from './terminal.js';
8
+ import {
9
+ URL_LEARN,
10
+ URL_SPONSOR,
11
+ URL_STANDARD,
12
+ URL_MASTER
13
+ } from './license.js';
14
+
15
+ export const showConversionMenu = async (onScaffold = null) => {
16
+ while (true) {
17
+ if (hasGum()) {
18
+ spawnSync('gum', [
19
+ 'style', '--border=rounded', '--border-foreground=81', '--padding=0 1', '--bold',
20
+ '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'
21
+ ], { stdio: 'inherit' });
22
+
23
+ const choice = gumChoose([
24
+ '1. Sponsor on GitHub: $9/mo Solo / $49/mo Team Unlimited -> Launch Sponsors',
25
+ '2. Buy Standard Vault: Single Dev License ($27) -> Launch Checkout',
26
+ '3. Buy Team Power Puff: 100 Devs / Team Honor System 💯 ($47) -> Launch Checkout',
27
+ '4. Enter License Key to Scaffold (Sponsors & License Holders)',
28
+ '5. Visit chemicalx.xophz.com to learn more',
29
+ '6. Back to Navigator'
30
+ ], 'Unlock Chemical X: Team Power Puff & Scaffolding:');
31
+
32
+ if (choice.startsWith('1.')) {
33
+ process.stdout.write(`\n\x1b[36mOpening GitHub Sponsors in browser:\x1b[0m ${URL_SPONSOR}\n\n`);
34
+ openBrowser(URL_SPONSOR);
35
+ continue;
36
+ }
37
+ if (choice.startsWith('2.')) {
38
+ process.stdout.write(`\n\x1b[36mOpening Standard Vault checkout (Single Dev License):\x1b[0m ${URL_STANDARD}\n\n`);
39
+ openBrowser(URL_STANDARD);
40
+ continue;
41
+ }
42
+ if (choice.startsWith('3.')) {
43
+ process.stdout.write(`\n\x1b[36mOpening Team Power Puff checkout (100 Devs / Team Honor System 💯):\x1b[0m ${URL_MASTER}\n\n`);
44
+ openBrowser(URL_MASTER);
45
+ continue;
46
+ }
47
+ if (choice.startsWith('4.')) {
48
+ if (onScaffold) await onScaffold();
49
+ break;
50
+ }
51
+ if (choice.startsWith('5.')) {
52
+ process.stdout.write(`\n\x1b[36mOpening Chemical X Portal in browser:\x1b[0m ${URL_LEARN}\n\n`);
53
+ openBrowser(URL_LEARN);
54
+ continue;
55
+ }
56
+ break;
57
+ }
58
+
59
+ process.stdout.write(
60
+ '\n\x1b[1m\x1b[38;2;98;201;255mChemical X: The Secret Sauce to Vibe Coding\x1b[0m\n' +
61
+ 'Stop letting AI agents scour 2,000-line monoliths and hallucinate breaking changes.\n' +
62
+ '25+ Years XP | Codified by Principal Systems Architect Xopher Pollard\n' +
63
+ 'Target File Budget: Max 500 lines/file (<100 lines per molecule capsule).\n\n'
64
+ );
65
+ process.stdout.write(` [1] Sponsor on GitHub ($9/mo Solo / $49/mo Team Unlimited) - ${URL_SPONSOR}\n Includes: Instant GitHub org access to private starter-kit, continuous model prompt updates\n`);
66
+ process.stdout.write(` [2] Buy Standard Vault: Single Dev License ($27) - ${URL_STANDARD}\n Includes: Kindle/Print PDF eBook, 7 Molecular Architecture Chapters, Universal AGENTS.md\n`);
67
+ process.stdout.write(` [3] Buy Team Power Puff: 100 Devs / Team Honor System 💯 ($47) - ${URL_MASTER}\n Includes: 100 Team Seats (Honor System), Private Starter-Kit Repo, Pre-Commit Line Budget Hooks, VIP Discord\n`);
68
+ process.stdout.write(' [4] Enter License Key to Scaffold (Sponsors & License Holders)\n');
69
+ process.stdout.write(` [5] Visit chemicalx.xophz.com to learn more - ${URL_LEARN}\n`);
70
+ process.stdout.write(' [6] Back to Navigator\n\n');
71
+
72
+ const selection = await promptQuestion('Select option [1-6] (default: 6): ');
73
+ const effectiveSelection = selection.trim() || '6';
74
+ if (effectiveSelection === '1') {
75
+ process.stdout.write(`\nOpening: ${URL_SPONSOR}\n\n`);
76
+ openBrowser(URL_SPONSOR);
77
+ continue;
78
+ }
79
+ if (effectiveSelection === '2') {
80
+ process.stdout.write(`\nOpening: ${URL_STANDARD}\n\n`);
81
+ openBrowser(URL_STANDARD);
82
+ continue;
83
+ }
84
+ if (effectiveSelection === '3') {
85
+ process.stdout.write(`\nOpening: ${URL_MASTER}\n\n`);
86
+ openBrowser(URL_MASTER);
87
+ continue;
88
+ }
89
+ if (effectiveSelection === '4') {
90
+ if (onScaffold) await onScaffold();
91
+ break;
92
+ }
93
+ if (effectiveSelection === '5') {
94
+ process.stdout.write(`\nOpening: ${URL_LEARN}\n\n`);
95
+ openBrowser(URL_LEARN);
96
+ continue;
97
+ }
98
+ break;
99
+ }
100
+ };
@@ -0,0 +1,70 @@
1
+ export const resolveGradeColor = (grade) => {
2
+ if (grade === 'A+' || grade === 'A') return '\x1b[32;1m';
3
+ if (grade === 'B') return '\x1b[33;1m';
4
+ if (grade === 'C') return '\x1b[33;1m';
5
+ if (grade === 'D') return '\x1b[38;5;208;1m';
6
+ return '\x1b[31;1m';
7
+ };
8
+
9
+ export const resolveGradeBadge = (grade) => {
10
+ const color = resolveGradeColor(grade);
11
+ const padded = grade.length === 1 ? `${grade} ` : grade;
12
+ return `${color}[Grade: ${padded}]\x1b[0m`;
13
+ };
14
+
15
+ export const resolveCriticalGrade = (count) => {
16
+ if (count === 0) return 'A+';
17
+ if (count === 1) return 'D';
18
+ return 'F';
19
+ };
20
+
21
+ export const resolveHighMedGrade = (count) => {
22
+ if (count === 0) return 'A+';
23
+ if (count <= 3) return 'B';
24
+ if (count <= 8) return 'C';
25
+ if (count <= 15) return 'D';
26
+ return 'F';
27
+ };
28
+
29
+ export const resolveLowGrade = (count) => {
30
+ if (count === 0) return 'A+';
31
+ if (count <= 5) return 'B';
32
+ if (count <= 15) return 'C';
33
+ return 'D';
34
+ };
35
+
36
+ const isFailedPillar = (p) => p.status === 'FAILED';
37
+ const isWarnPillar = (p) => p.status === 'WARN';
38
+
39
+ export const resolvePillarGrade = (pillars) => {
40
+ const values = Object.values(pillars || {});
41
+ const failed = values.filter(isFailedPillar).length;
42
+ const warn = values.filter(isWarnPillar).length;
43
+ if (failed === 0 && warn === 0) return 'A+';
44
+ if (failed === 0) return 'B';
45
+ if (failed <= 2) return 'C';
46
+ if (failed <= 4) return 'D';
47
+ return 'F';
48
+ };
49
+
50
+ const isExtremeFile = (h) => h.lineCount >= 2000;
51
+ const isSevereFile = (h) => h.lineCount >= 1000;
52
+ const isWarningFile = (h) => h.lineCount > 500;
53
+
54
+ export const resolveHotspotGrade = (hotspots) => {
55
+ if (!hotspots || hotspots.length === 0) return 'A+';
56
+ const hasExtreme = hotspots.some(isExtremeFile);
57
+ if (hasExtreme) return 'F';
58
+ const hasSevere = hotspots.some(isSevereFile);
59
+ if (hasSevere) return 'D';
60
+ const hasWarning = hotspots.some(isWarningFile);
61
+ if (hasWarning) return 'C';
62
+ return 'B';
63
+ };
64
+
65
+ export const resolveContextGrade = (riskTier) => {
66
+ if (riskTier === 'LOW') return 'A';
67
+ if (riskTier === 'MEDIUM') return 'B';
68
+ if (riskTier === 'HIGH') return 'D';
69
+ return 'F';
70
+ };