@chemx/starter-kit 26.9.11-362 → 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/history.d.ts +6 -0
- package/cli/audit/history.js +60 -15
- package/cli/audit/prompts.js +5 -5
- package/cli/audit/reporter-ascii.d.ts +4 -0
- package/cli/audit/reporter-ascii.js +99 -0
- package/cli/audit/reporter-grades.js +24 -8
- package/cli/audit/reporter-markdown.js +5 -4
- package/cli/audit/reporter-utils.js +24 -6
- package/cli/audit/reporter.js +49 -11
- package/cli/audit/social.d.ts +3 -0
- package/cli/audit/social.js +60 -11
- package/cli/audit/types.d.ts +9 -0
- package/cli/audit.js +14 -2
- package/cli/help.js +4 -4
- package/cli/installer-templates.js +13 -2
- package/cli/installer.d.ts +1 -0
- package/cli/installer.js +48 -0
- package/cli/license.js +8 -8
- package/cli/navigator-actions.js +274 -0
- package/cli/navigator-banner.js +98 -0
- package/cli/navigator-conversion.js +3 -3
- package/cli/navigator-grades.js +20 -3
- package/cli/navigator-menu.js +127 -0
- package/cli/navigator.d.ts +21 -2
- package/cli/navigator.js +46 -320
- package/cli/terminal.js +3 -3
- package/docs/CHANGELOG.md +29 -1
- package/package.json +1 -1
package/cli/audit/social.js
CHANGED
|
@@ -38,9 +38,9 @@ export const resolveBadgeColor = (score) => {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export const resolveHotspotTierText = (lineCount) => {
|
|
41
|
-
if (lineCount >= 2000) return '🔴 **CRITICAL (>= 2,000
|
|
42
|
-
if (lineCount >= 1000) return '🟠 **SEVERE (>= 1,000
|
|
43
|
-
if (lineCount > 500) return '🟡 **WARNING (> 500
|
|
41
|
+
if (lineCount >= 2000) return '🔴 **CRITICAL (>= 2,000 lines of code)**';
|
|
42
|
+
if (lineCount >= 1000) return '🟠 **SEVERE (>= 1,000 lines of code)**';
|
|
43
|
+
if (lineCount > 500) return '🟡 **WARNING (> 500 lines of code)**';
|
|
44
44
|
return '🟢 Compliant';
|
|
45
45
|
};
|
|
46
46
|
|
|
@@ -62,6 +62,25 @@ export const formatWebUrl = (url) => {
|
|
|
62
62
|
return `[${href}](${href})`;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
+
export const resolveExcessCostPerPass = (tokensObj, fallbackCostPerMillion = 3.0) => {
|
|
66
|
+
if (tokensObj?.excessCostPerPass !== undefined) return tokensObj.excessCostPerPass;
|
|
67
|
+
const costPerMillion = tokensObj?.costPerMillion || fallbackCostPerMillion;
|
|
68
|
+
const excess = tokensObj?.estimatedExcessTokens || 0;
|
|
69
|
+
return Number(((excess / 1000000) * costPerMillion).toFixed(3));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const resolveMonthlyWastePerDev = (tokensObj, fallbackCostPerMillion = 3.0) => {
|
|
73
|
+
if (tokensObj?.monthlyWastePerDev !== undefined) return tokensObj.monthlyWastePerDev;
|
|
74
|
+
const pass = resolveExcessCostPerPass(tokensObj, fallbackCostPerMillion);
|
|
75
|
+
return Number((pass * 20 * 5 * 4).toFixed(2));
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const resolveWeeklyWastePerDev = (tokensObj, fallbackCostPerMillion = 3.0) => {
|
|
79
|
+
if (tokensObj?.weeklyWastePerDev !== undefined) return tokensObj.weeklyWastePerDev;
|
|
80
|
+
const pass = resolveExcessCostPerPass(tokensObj, fallbackCostPerMillion);
|
|
81
|
+
return Number((pass * 20 * 5).toFixed(2));
|
|
82
|
+
};
|
|
83
|
+
|
|
65
84
|
export const generateDiscussionContent = (report, username, projectName = 'Codebase', repoUrl = '', liveUrl = '') => {
|
|
66
85
|
const { health, metrics, pillars, hotspots, contextAnalysis, violations } = report;
|
|
67
86
|
const isHighScoring = health.score >= 80;
|
|
@@ -90,8 +109,14 @@ export const generateDiscussionContent = (report, username, projectName = 'Codeb
|
|
|
90
109
|
}
|
|
91
110
|
}
|
|
92
111
|
lines.push(`* **Molecular Health Index**: **${health.score} / 100** (Grade: **${health.grade}** - ${health.label})`);
|
|
93
|
-
lines.push(`* **Source Files Analyzed**: ${metrics.scannedFiles} files (${metrics.totalLoc} total
|
|
112
|
+
lines.push(`* **Source Files Analyzed**: ${metrics.scannedFiles} files (${metrics.totalLoc} total lines of code)`);
|
|
94
113
|
lines.push(`* **Token Reduction Potential**: **${contextAnalysis.potentialSavingsPct}%** (Estimated ${contextAnalysis.estimatedTokens.toLocaleString()} tokens)`);
|
|
114
|
+
const costPass = resolveExcessCostPerPass(contextAnalysis);
|
|
115
|
+
const costMonth = resolveMonthlyWastePerDev(contextAnalysis);
|
|
116
|
+
const costWeek = resolveWeeklyWastePerDev(contextAnalysis);
|
|
117
|
+
const pricingModel = contextAnalysis?.pricingModel || 'Frontier Blended ($3.00/1M)';
|
|
118
|
+
lines.push(`* **Monolith Cost per Turn**: **$${costPass.toFixed(3)}** (${pricingModel})`);
|
|
119
|
+
lines.push(`* **Projected Dev Context Tax**: **$${costMonth.toFixed(2)} / mo** per engineer ($${costWeek.toFixed(2)} / wk)`);
|
|
95
120
|
lines.push('');
|
|
96
121
|
lines.push('---');
|
|
97
122
|
lines.push('');
|
|
@@ -130,11 +155,11 @@ export const generateDiscussionContent = (report, username, projectName = 'Codeb
|
|
|
130
155
|
lines.push('');
|
|
131
156
|
lines.push('## 2. Monolith & Architectural Hazard Summary');
|
|
132
157
|
lines.push('');
|
|
133
|
-
lines.push(`* **Monolith Files (> 500
|
|
158
|
+
lines.push(`* **Monolith Files (> 500 lines of code)**: **${totalMonoliths} files**`);
|
|
134
159
|
if (totalMonoliths > 0) {
|
|
135
|
-
lines.push(` * Warning Tier (500 - 999
|
|
136
|
-
lines.push(` * Severe Tier (1,000 - 1,999
|
|
137
|
-
lines.push(` * Extreme Monoliths (2,000+
|
|
160
|
+
lines.push(` * Warning Tier (500 - 999 lines of code): ${warningMonoliths} files`);
|
|
161
|
+
lines.push(` * Severe Tier (1,000 - 1,999 lines of code): ${severeMonoliths} files`);
|
|
162
|
+
lines.push(` * Extreme Monoliths (2,000+ lines of code): ${extremeMonoliths} files`);
|
|
138
163
|
}
|
|
139
164
|
lines.push(`* **Hazard Breakdown**: Critical: **${critical.length}** | High/Med: **${high.length + medium.length}** | Low: **${low.length}**`);
|
|
140
165
|
lines.push('');
|
|
@@ -146,7 +171,7 @@ export const generateDiscussionContent = (report, username, projectName = 'Codeb
|
|
|
146
171
|
lines.push('This project follows Chemical X Molecular Architecture principles with isolated capsules and self-cleaning hooks.');
|
|
147
172
|
} else {
|
|
148
173
|
lines.push('### Seeking Refactoring Feedback');
|
|
149
|
-
lines.push('Looking for recommendations on breaking down flagged monolithic debts into crystalline molecule capsules (< 100
|
|
174
|
+
lines.push('Looking for recommendations on breaking down flagged monolithic debts into crystalline molecule capsules (< 100 lines of code). Any advice is welcome!');
|
|
150
175
|
}
|
|
151
176
|
lines.push('');
|
|
152
177
|
lines.push('*Audited using [Chemical X Protocol Starter Kit](https://github.com/Chemical-X-Protocol/awesome-secret-sauce).*');
|
|
@@ -187,6 +212,28 @@ export const generateTransformationDiscussionContent = (
|
|
|
187
212
|
return isGood ? `🟢 **${sign}**` : `🔴 **${sign}**`;
|
|
188
213
|
};
|
|
189
214
|
|
|
215
|
+
const costPassBefore = resolveExcessCostPerPass(beforeSnapshot.tokens);
|
|
216
|
+
const costPassAfter = resolveExcessCostPerPass(afterSnapshot.tokens);
|
|
217
|
+
const costPassDelta = Number((costPassAfter - costPassBefore).toFixed(3));
|
|
218
|
+
|
|
219
|
+
const monthlyTaxBefore = resolveMonthlyWastePerDev(beforeSnapshot.tokens);
|
|
220
|
+
const monthlyTaxAfter = resolveMonthlyWastePerDev(afterSnapshot.tokens);
|
|
221
|
+
const monthlyTaxDelta = Number((monthlyTaxAfter - monthlyTaxBefore).toFixed(2));
|
|
222
|
+
|
|
223
|
+
const formatCostPassDelta = (val) => {
|
|
224
|
+
if (val === 0) return '0 (No change)';
|
|
225
|
+
const isGood = val < 0;
|
|
226
|
+
const sign = val > 0 ? `+$${val.toFixed(3)}` : `-$${Math.abs(val).toFixed(3)}`;
|
|
227
|
+
return isGood ? `🟢 **${sign}**` : `🔴 **${sign}**`;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const formatMonthlyTaxDelta = (val) => {
|
|
231
|
+
if (val === 0) return '0 (No change)';
|
|
232
|
+
const isGood = val < 0;
|
|
233
|
+
const sign = val > 0 ? `+$${val.toFixed(2)}/mo` : `-$${Math.abs(val).toFixed(2)}/mo`;
|
|
234
|
+
return isGood ? `🟢 **${sign}**` : `🔴 **${sign}**`;
|
|
235
|
+
};
|
|
236
|
+
|
|
190
237
|
const lines = [];
|
|
191
238
|
lines.push(`# 🚀 Architectural Transformation: ${projectName}`);
|
|
192
239
|
lines.push('');
|
|
@@ -214,8 +261,10 @@ export const generateTransformationDiscussionContent = (
|
|
|
214
261
|
lines.push(`| **Molecular Health (MHI)** | ${scoreBefore} / 100 (${beforeSnapshot.health.grade}) | ${scoreAfter} / 100 (${afterSnapshot.health.grade}) | ${formatDelta(scoreDelta)} |`);
|
|
215
262
|
lines.push(`| **Critical Hazards** | ${beforeSnapshot.violations.critical} | ${afterSnapshot.violations.critical} | ${formatDelta(critDelta, true)} |`);
|
|
216
263
|
lines.push(`| **Total Violations** | ${beforeSnapshot.violations.total} | ${afterSnapshot.violations.total} | ${formatDelta(totalDelta, true)} |`);
|
|
217
|
-
lines.push(`| **Monolith Files (>500
|
|
264
|
+
lines.push(`| **Monolith Files (> 500 lines of code)** | ${beforeSnapshot.monoliths.total} | ${afterSnapshot.monoliths.total} | ${formatDelta(monoDelta, true)} |`);
|
|
218
265
|
lines.push(`| **Excess Token Burn** | ${beforeSnapshot.tokens.estimatedExcessTokens.toLocaleString()} tok | ${afterSnapshot.tokens.estimatedExcessTokens.toLocaleString()} tok | ${formatDelta(tokensDelta, true)} |`);
|
|
266
|
+
lines.push(`| **Monolith Cost per Turn** | $${costPassBefore.toFixed(3)} | $${costPassAfter.toFixed(3)} | ${formatCostPassDelta(costPassDelta)} |`);
|
|
267
|
+
lines.push(`| **Dev Context Tax (Monthly)** | $${monthlyTaxBefore.toFixed(2)}/mo | $${monthlyTaxAfter.toFixed(2)}/mo | ${formatMonthlyTaxDelta(monthlyTaxDelta)} |`);
|
|
219
268
|
lines.push('');
|
|
220
269
|
lines.push('---');
|
|
221
270
|
lines.push('');
|
|
@@ -241,7 +290,7 @@ export const generateTransformationDiscussionContent = (
|
|
|
241
290
|
lines.push('---');
|
|
242
291
|
lines.push('');
|
|
243
292
|
lines.push('### Community Takeaway');
|
|
244
|
-
lines.push('Refactored using Chemical X Molecular Architecture standards. Monoliths decomposed into crystalline domain capsules (< 100
|
|
293
|
+
lines.push('Refactored using Chemical X Molecular Architecture standards. Monoliths decomposed into crystalline domain capsules (< 100 lines of code) with self-cleaning hooks.');
|
|
245
294
|
lines.push('');
|
|
246
295
|
lines.push('*Transformation tracked via [Chemical X Protocol Starter Kit](https://github.com/Chemical-X-Protocol/awesome-secret-sauce).*');
|
|
247
296
|
|
package/cli/audit/types.d.ts
CHANGED
|
@@ -58,6 +58,11 @@ export interface ContextTokenAnalysis {
|
|
|
58
58
|
readonly estimatedExcessTokens: number;
|
|
59
59
|
readonly potentialSavingsPct: number;
|
|
60
60
|
readonly riskLevel: RiskLevel;
|
|
61
|
+
readonly pricingModel?: string;
|
|
62
|
+
readonly costPerMillion?: number;
|
|
63
|
+
readonly excessCostPerPass?: number;
|
|
64
|
+
readonly weeklyWastePerDev?: number;
|
|
65
|
+
readonly monthlyWastePerDev?: number;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
export interface HotspotFile {
|
|
@@ -117,6 +122,10 @@ export declare function formatGradeCSection(report: AuditReport): string;
|
|
|
117
122
|
export declare function formatGradeBSection(report: AuditReport): string;
|
|
118
123
|
export declare function formatGradeASection(report: AuditReport): string;
|
|
119
124
|
export declare function getChemicalXAsciiBanner(gradeOrReport?: string | AuditReport | null): string;
|
|
125
|
+
export declare function getAsciiGradeLines(grade?: string, color?: string, withEquals?: boolean): readonly string[];
|
|
126
|
+
export declare function formatAsciiGrade(grade?: string, color?: string, indent?: string, withEquals?: boolean): string;
|
|
127
|
+
export declare const REPORT_CARD_ASCII: readonly string[];
|
|
128
|
+
export declare function getReportCardAsciiLines(color?: string): readonly string[];
|
|
120
129
|
|
|
121
130
|
export * from './social';
|
|
122
131
|
|
package/cli/audit.js
CHANGED
|
@@ -27,7 +27,11 @@ import {
|
|
|
27
27
|
formatGradeCSection,
|
|
28
28
|
formatGradeBSection,
|
|
29
29
|
formatGradeASection,
|
|
30
|
-
getChemicalXAsciiBanner
|
|
30
|
+
getChemicalXAsciiBanner,
|
|
31
|
+
getAsciiGradeLines,
|
|
32
|
+
formatAsciiGrade,
|
|
33
|
+
getReportCardAsciiLines,
|
|
34
|
+
REPORT_CARD_ASCII
|
|
31
35
|
} from './audit/reporter.js';
|
|
32
36
|
|
|
33
37
|
const IGNORED_DIRS = new Set([
|
|
@@ -207,7 +211,11 @@ export {
|
|
|
207
211
|
formatGradeCSection,
|
|
208
212
|
formatGradeBSection,
|
|
209
213
|
formatGradeASection,
|
|
210
|
-
getChemicalXAsciiBanner
|
|
214
|
+
getChemicalXAsciiBanner,
|
|
215
|
+
getAsciiGradeLines,
|
|
216
|
+
formatAsciiGrade,
|
|
217
|
+
getReportCardAsciiLines,
|
|
218
|
+
REPORT_CARD_ASCII
|
|
211
219
|
};
|
|
212
220
|
|
|
213
221
|
export default {
|
|
@@ -233,6 +241,10 @@ export default {
|
|
|
233
241
|
formatGradeBSection,
|
|
234
242
|
formatGradeASection,
|
|
235
243
|
getChemicalXAsciiBanner,
|
|
244
|
+
getAsciiGradeLines,
|
|
245
|
+
formatAsciiGrade,
|
|
246
|
+
getReportCardAsciiLines,
|
|
247
|
+
REPORT_CARD_ASCII,
|
|
236
248
|
PILLARS,
|
|
237
249
|
RULE_REGISTRY
|
|
238
250
|
};
|
package/cli/help.js
CHANGED
|
@@ -25,7 +25,7 @@ export const printHelp = () => {
|
|
|
25
25
|
'',
|
|
26
26
|
` ${CYAN}generate${RESET} <m-name> ${DIM}(aliases: capsule, add)${RESET}`,
|
|
27
27
|
` Scaffold an isolated crystalline molecule capsule directory:`,
|
|
28
|
-
` ${DIM}- ${RESET}<m-name>/<m-name>.tsx ${DIM}(declarative component < 50
|
|
28
|
+
` ${DIM}- ${RESET}<m-name>/<m-name>.tsx ${DIM}(declarative component < 50 lines of code)${RESET}`,
|
|
29
29
|
` ${DIM}- ${RESET}<m-name>/types.d.ts ${DIM}(props and emits interfaces)${RESET}`,
|
|
30
30
|
` ${DIM}- ${RESET}<m-name>/index.ts ${DIM}(clean public barrel export)${RESET}`,
|
|
31
31
|
` Free evaluation mode supported with non-blocking license reminder.`,
|
|
@@ -35,12 +35,12 @@ export const printHelp = () => {
|
|
|
35
35
|
` ${YELLOW}[PAID]${RESET} Drop full Chemical X blueprints, core hooks (toResult,`,
|
|
36
36
|
` useAsyncData, useSelfCleaningTimer), and AGENTS.md directives into`,
|
|
37
37
|
` an existing codebase. Default destination: ${CYAN}src/chemical-x${RESET}.`,
|
|
38
|
-
` Requires Single Dev ($27) or Team Power Puff
|
|
38
|
+
` Requires Single Dev ($27) or Team Power Puff ($97: Unlimited Lifetime) license.`,
|
|
39
39
|
'',
|
|
40
40
|
` ${CYAN}create${RESET} [directory] ${DIM}(or: npm create chemx [directory])${RESET}`,
|
|
41
41
|
` ${YELLOW}[PAID]${RESET} Scaffold a complete, clean-slate project wired with full`,
|
|
42
42
|
` Chemical X molecular standards, pre-commit line budget hooks, and rules.`,
|
|
43
|
-
` Requires Single Dev ($27) or Team Power Puff
|
|
43
|
+
` Requires Single Dev ($27) or Team Power Puff ($97: Unlimited Lifetime) license.`,
|
|
44
44
|
'',
|
|
45
45
|
` ${CYAN}install-hooks${RESET} [directory] ${DIM}(aliases: hook, setup-ci)${RESET}`,
|
|
46
46
|
` ${GREEN}[FREE]${RESET} Install git pre-commit hook (.git/hooks/pre-commit) and`,
|
|
@@ -95,7 +95,7 @@ export const printHelp = () => {
|
|
|
95
95
|
'',
|
|
96
96
|
` ${CYAN}--license=<key>${RESET}`,
|
|
97
97
|
` Pass Chemical X Sponsor or Team license key non-interactively via CLI.`,
|
|
98
|
-
` Tiers: Single Dev ($27) | Team Power Puff
|
|
98
|
+
` Tiers: Single Dev ($27) | Team Power Puff ($97: Unlimited Lifetime).`,
|
|
99
99
|
'',
|
|
100
100
|
`${BOLD}ENVIRONMENT VARIABLES${RESET}`,
|
|
101
101
|
` ${GREEN}CHEMX_MIN_GRADE${RESET}`,
|
|
@@ -31,9 +31,20 @@ if [ "\$FAILED" -eq 1 ]; then
|
|
|
31
31
|
printf "\\n\\033[1m\\033[31m[Chemical X] Commit Blocked: Staged files exceed architectural line budgets\\033[0m\\n\$ERRORS\\n\\n"
|
|
32
32
|
fi
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
AUDIT_BIN=""
|
|
35
|
+
if [ -f "./cli/index.js" ]; then
|
|
36
|
+
AUDIT_BIN="node ./cli/index.js"
|
|
37
|
+
elif [ -x "./node_modules/.bin/chemx" ]; then
|
|
38
|
+
AUDIT_BIN="./node_modules/.bin/chemx"
|
|
39
|
+
elif command -v chemx >/dev/null 2>&1; then
|
|
40
|
+
AUDIT_BIN="chemx"
|
|
41
|
+
elif command -v npx >/dev/null 2>&1; then
|
|
42
|
+
AUDIT_BIN="npx chemx"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [ -n "\$AUDIT_BIN" ]; then
|
|
35
46
|
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 !
|
|
47
|
+
if ! \$AUDIT_BIN audit --min-grade="\$MIN_GRADE" --min-score="\$MIN_SCORE" --prompt-on-fail; then
|
|
37
48
|
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
49
|
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
50
|
exit 1
|
package/cli/installer.d.ts
CHANGED
|
@@ -17,4 +17,5 @@ export declare function buildGitHubWorkflowScript(minGrade?: string, minScore?:
|
|
|
17
17
|
export declare function installPreCommitHook(targetDir?: string, options?: InstallOptions): boolean;
|
|
18
18
|
export declare function installGitHubWorkflow(targetDir?: string, options?: InstallOptions): boolean;
|
|
19
19
|
export declare function saveProjectConfig(targetDir?: string, config?: Partial<ProjectConfig>): void;
|
|
20
|
+
export declare function areGuardrailsInstalled(targetDir?: string): boolean;
|
|
20
21
|
export declare function runInstallWizard(targetDir?: string): Promise<void>;
|
package/cli/installer.js
CHANGED
|
@@ -30,6 +30,54 @@ export const saveProjectConfig = (targetDir = '.', config = {}) => {
|
|
|
30
30
|
process.stdout.write(` \x1b[32m✔\x1b[0m Saved project settings to: .chemx/config.json\n`);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
export const areGuardrailsInstalled = (targetDir = '.') => {
|
|
34
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
35
|
+
const wfPath = path.join(resolvedTarget, '.github', 'workflows', 'chemx-audit.yml');
|
|
36
|
+
const hasWf = fs.existsSync(wfPath);
|
|
37
|
+
|
|
38
|
+
const gitPath = path.join(resolvedTarget, '.git');
|
|
39
|
+
let hasGit = false;
|
|
40
|
+
let hooksDir = null;
|
|
41
|
+
|
|
42
|
+
if (fs.existsSync(gitPath)) {
|
|
43
|
+
try {
|
|
44
|
+
const stat = fs.statSync(gitPath);
|
|
45
|
+
if (stat.isDirectory()) {
|
|
46
|
+
hasGit = true;
|
|
47
|
+
hooksDir = path.join(gitPath, 'hooks');
|
|
48
|
+
} else if (stat.isFile()) {
|
|
49
|
+
hasGit = true;
|
|
50
|
+
const gitContent = fs.readFileSync(gitPath, 'utf-8');
|
|
51
|
+
const match = gitContent.match(/gitdir:\s*(.+)/);
|
|
52
|
+
if (match) {
|
|
53
|
+
hooksDir = path.resolve(resolvedTarget, match[1].trim(), 'hooks');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
hasGit = false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!hasGit) {
|
|
62
|
+
return hasWf;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let hasHook = false;
|
|
66
|
+
if (hooksDir) {
|
|
67
|
+
const hookPath = path.join(hooksDir, 'pre-commit');
|
|
68
|
+
if (fs.existsSync(hookPath)) {
|
|
69
|
+
try {
|
|
70
|
+
const hookContent = fs.readFileSync(hookPath, 'utf-8');
|
|
71
|
+
hasHook = hookContent.includes('Chemical X') || hookContent.includes('chemx');
|
|
72
|
+
} catch {
|
|
73
|
+
hasHook = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return hasWf && hasHook;
|
|
79
|
+
};
|
|
80
|
+
|
|
33
81
|
export const runInstallWizard = async (targetDir = '.') => {
|
|
34
82
|
const isGit = fs.existsSync(path.resolve(targetDir, '.git'));
|
|
35
83
|
process.stdout.write('\n\x1b[1m\x1b[38;2;98;201;255mChemical X: Architecture Guardrail Installer\x1b[0m\n\n');
|
package/cli/license.js
CHANGED
|
@@ -83,7 +83,7 @@ export const obtainLicenseKey = async (rawArgs = [], onRunAudit = null) => {
|
|
|
83
83
|
[
|
|
84
84
|
'1. Visit chemicalx.xophz.com to learn more',
|
|
85
85
|
'2. Buy Standard Vault: Single Dev License ($27) -> Launch Checkout',
|
|
86
|
-
'3. Buy Team Power Puff:
|
|
86
|
+
'3. Buy Team Power Puff: Unlimited Lifetime ($97) -> Launch Checkout',
|
|
87
87
|
'4. Enter License Key (CX-XXXX-XXXX-XXXX)',
|
|
88
88
|
'5. Run Free Public Audit (npx chemx audit)',
|
|
89
89
|
'6. Exit'
|
|
@@ -106,7 +106,7 @@ export const obtainLicenseKey = async (rawArgs = [], onRunAudit = null) => {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (choice.startsWith('3.')) {
|
|
109
|
-
process.stdout.write(`\x1b[36mOpening Team Power Puff checkout (
|
|
109
|
+
process.stdout.write(`\x1b[36mOpening Team Power Puff checkout (Unlimited Lifetime) in default browser:\x1b[0m ${URL_MASTER}\n`);
|
|
110
110
|
openBrowser(URL_MASTER);
|
|
111
111
|
process.stdout.write('\nOnce completed, paste your Sponsor / VIP License Key below.\n');
|
|
112
112
|
return gumInput('License Key (CX-XXXX-XXXX-XXXX):', 'CX-XXXX-XXXX-XXXX');
|
|
@@ -131,7 +131,7 @@ export const obtainLicenseKey = async (rawArgs = [], onRunAudit = null) => {
|
|
|
131
131
|
process.stdout.write('\x1b[1mChemical X Scaffolding Requires a Paid License:\x1b[0m\n');
|
|
132
132
|
process.stdout.write(' [1] Visit chemicalx.xophz.com to learn more\n');
|
|
133
133
|
process.stdout.write(' [2] Buy Standard Vault: Single Dev License ($27) - Opens browser\n');
|
|
134
|
-
process.stdout.write(' [3] Buy Team Power Puff:
|
|
134
|
+
process.stdout.write(' [3] Buy Team Power Puff: Unlimited Lifetime ($97) - Opens browser\n');
|
|
135
135
|
process.stdout.write(' [4] Enter License Key\n');
|
|
136
136
|
process.stdout.write(' [5] Run Free Public Audit (npx chemx audit)\n');
|
|
137
137
|
process.stdout.write(' [6] Exit\n\n');
|
|
@@ -190,7 +190,7 @@ export const checkOrPromptEvaluation = async (actionLabel = 'generate capsule')
|
|
|
190
190
|
'--bold',
|
|
191
191
|
'⚡ Chemical X: Evaluation Mode (Unlicensed)\n' +
|
|
192
192
|
'Support vibe coding standards: chemicalx.xophz.com\n' +
|
|
193
|
-
'Single Dev ($27) | Team Power Puff
|
|
193
|
+
'Single Dev ($27) | Team Power Puff ($97: Unlimited Lifetime)'
|
|
194
194
|
],
|
|
195
195
|
{ stdio: 'inherit' }
|
|
196
196
|
);
|
|
@@ -198,8 +198,8 @@ export const checkOrPromptEvaluation = async (actionLabel = 'generate capsule')
|
|
|
198
198
|
const choice = gumChoose(
|
|
199
199
|
[
|
|
200
200
|
`1. ⚡ Continue in Evaluation Mode (Press Enter to ${actionLabel})`,
|
|
201
|
-
'2. 🔑 Enter License Key (Power Puff
|
|
202
|
-
'3. 💎 Buy License ($27 Solo / $
|
|
201
|
+
'2. 🔑 Enter License Key (Team Power Puff or Single Dev)',
|
|
202
|
+
'3. 💎 Buy License ($27 Solo / $97 Team Power Puff Unlimited Lifetime)',
|
|
203
203
|
'4. 🚪 Cancel'
|
|
204
204
|
],
|
|
205
205
|
`Evaluation Mode: ${actionLabel}`
|
|
@@ -242,8 +242,8 @@ export const checkOrPromptEvaluation = async (actionLabel = 'generate capsule')
|
|
|
242
242
|
'\n\x1b[38;5;208;1m⚡ Chemical X: Evaluation Mode (Unlicensed)\x1b[0m\n' +
|
|
243
243
|
'Support vibe coding standards: chemicalx.xophz.com\n' +
|
|
244
244
|
` [1] Continue in Evaluation Mode (Press Enter to ${actionLabel})\n` +
|
|
245
|
-
' [2] Enter License Key (Power Puff
|
|
246
|
-
' [3] Buy License ($27 Solo / $
|
|
245
|
+
' [2] Enter License Key (Team Power Puff or Single Dev)\n' +
|
|
246
|
+
' [3] Buy License ($27 Solo / $97 Team Power Puff Unlimited Lifetime)\n' +
|
|
247
247
|
' [4] Cancel\n\n'
|
|
248
248
|
);
|
|
249
249
|
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
hasGum,
|
|
5
|
+
gumChoose,
|
|
6
|
+
gumInput,
|
|
7
|
+
promptQuestion
|
|
8
|
+
} from "./terminal.js";
|
|
9
|
+
import {
|
|
10
|
+
formatTerminalReport,
|
|
11
|
+
generateMarkdownReport,
|
|
12
|
+
groupViolationsBySeverity,
|
|
13
|
+
formatGradeFSection,
|
|
14
|
+
formatGradeDSection,
|
|
15
|
+
formatGradeCSection,
|
|
16
|
+
formatGradeBSection,
|
|
17
|
+
formatGradeASection,
|
|
18
|
+
getAuditHistory,
|
|
19
|
+
getAuditBaseline,
|
|
20
|
+
createSnapshotFromReport,
|
|
21
|
+
formatTransformationTerminal,
|
|
22
|
+
formatHistoryTimelineTerminal,
|
|
23
|
+
buildGradeFPrompt,
|
|
24
|
+
buildGradeDPrompt,
|
|
25
|
+
buildGradeCPrompt,
|
|
26
|
+
buildGradeBPrompt,
|
|
27
|
+
buildMasterPrompt,
|
|
28
|
+
copyToClipboard
|
|
29
|
+
} from "./audit.js";
|
|
30
|
+
import { resolveGradeBadge } from "./navigator-grades.js";
|
|
31
|
+
import { showPagedContent } from "./navigator-paged.js";
|
|
32
|
+
import { showConversionMenu } from "./navigator-conversion.js";
|
|
33
|
+
import { handleShareToDiscussions } from "./navigator-share.js";
|
|
34
|
+
import { runInstallWizard } from "./installer.js";
|
|
35
|
+
import { formatButtonTag } from "./navigator-menu.js";
|
|
36
|
+
|
|
37
|
+
const isExtremeMonolith = (h) => h.lineCount >= 2000;
|
|
38
|
+
const isSevereMonolith = (h) => h.lineCount >= 1000 && h.lineCount < 2000;
|
|
39
|
+
const isWarningMonolith = (h) => h.lineCount >= 500 && h.lineCount < 1000;
|
|
40
|
+
const isPassedPillar = (p) => p.status === "PASSED";
|
|
41
|
+
const hasGradeItems = (g) => g.count > 0;
|
|
42
|
+
|
|
43
|
+
export const buildActiveGrades = (report) => {
|
|
44
|
+
const { hotspots, violations, pillars } = report;
|
|
45
|
+
const { critical, high, medium, low } = groupViolationsBySeverity(violations);
|
|
46
|
+
|
|
47
|
+
const passedPillarsCount = Object.values(pillars || {}).filter(isPassedPillar).length;
|
|
48
|
+
|
|
49
|
+
const extremeMonoliths = hotspots.filter(isExtremeMonolith);
|
|
50
|
+
const severeMonoliths = hotspots.filter(isSevereMonolith);
|
|
51
|
+
const warningMonoliths = hotspots.filter(isWarningMonolith);
|
|
52
|
+
|
|
53
|
+
const gradeFCount = critical.length + extremeMonoliths.length;
|
|
54
|
+
const gradeDCount = high.length + severeMonoliths.length;
|
|
55
|
+
const gradeCCount = medium.length + warningMonoliths.length;
|
|
56
|
+
const gradeBCount = low.length;
|
|
57
|
+
const gradeACount = passedPillarsCount;
|
|
58
|
+
|
|
59
|
+
const handleGradeAAction = async () => {
|
|
60
|
+
await showPagedContent(formatGradeASection(report));
|
|
61
|
+
};
|
|
62
|
+
const handleGradeBAction = async () => {
|
|
63
|
+
await showPagedContent(formatGradeBSection(report), buildGradeBPrompt(report));
|
|
64
|
+
};
|
|
65
|
+
const handleGradeCAction = async () => {
|
|
66
|
+
await showPagedContent(formatGradeCSection(report), buildGradeCPrompt(report));
|
|
67
|
+
};
|
|
68
|
+
const handleGradeDAction = async () => {
|
|
69
|
+
await showPagedContent(formatGradeDSection(report), buildGradeDPrompt(report));
|
|
70
|
+
};
|
|
71
|
+
const handleGradeFAction = async () => {
|
|
72
|
+
await showPagedContent(formatGradeFSection(report), buildGradeFPrompt(report));
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const gradeTiers = [
|
|
76
|
+
{
|
|
77
|
+
grade: "A",
|
|
78
|
+
count: gradeACount,
|
|
79
|
+
icon: "✅",
|
|
80
|
+
description: "Compliant Checks & Passing Pillars",
|
|
81
|
+
action: handleGradeAAction
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
grade: "B",
|
|
85
|
+
count: gradeBCount,
|
|
86
|
+
icon: "💣",
|
|
87
|
+
description: "Low Hygiene Issues & Minor Debts",
|
|
88
|
+
action: handleGradeBAction
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
grade: "C",
|
|
92
|
+
count: gradeCCount,
|
|
93
|
+
icon: "⚡",
|
|
94
|
+
description: "Medium Severity Debts & Monolith Drift",
|
|
95
|
+
action: handleGradeCAction
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
grade: "D",
|
|
99
|
+
count: gradeDCount,
|
|
100
|
+
icon: "🔥",
|
|
101
|
+
description: "High Severity Debts & Severe Monoliths",
|
|
102
|
+
action: handleGradeDAction
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
grade: "F",
|
|
106
|
+
count: gradeFCount,
|
|
107
|
+
icon: "💥",
|
|
108
|
+
description: "Critical Hazards & Extreme Monoliths",
|
|
109
|
+
action: handleGradeFAction
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const createActiveGradeItem = (g) => ({
|
|
114
|
+
key: `grade_${g.grade.toLowerCase()}`,
|
|
115
|
+
grade: g.grade.toLowerCase(),
|
|
116
|
+
tag: resolveGradeBadge(g.grade, 11),
|
|
117
|
+
label: `${g.icon} Grade ${g.grade}: ${g.description} (${g.count} ${g.grade === "A" ? "clean" : "items"})`,
|
|
118
|
+
action: g.action
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return gradeTiers.filter(hasGradeItems).map(createActiveGradeItem);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export const buildDashboardActionGroups = ({ report, onScaffold = null }) => {
|
|
125
|
+
const handleInstallAction = async () => {
|
|
126
|
+
await runInstallWizard(process.cwd());
|
|
127
|
+
if (hasGum()) {
|
|
128
|
+
gumChoose(["<-- Back to Audit Dashboard"]);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const handleUpgradeAction = async () => {
|
|
133
|
+
await showConversionMenu(onScaffold);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const handleReportAction = async () => {
|
|
137
|
+
await showPagedContent(formatTerminalReport(report), buildMasterPrompt(report));
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const handleShareAction = async () => {
|
|
141
|
+
await handleShareToDiscussions(report);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const handleProgressAction = async () => {
|
|
145
|
+
const baseline = getAuditBaseline();
|
|
146
|
+
const history = getAuditHistory();
|
|
147
|
+
const currentSnapshot = createSnapshotFromReport(report);
|
|
148
|
+
|
|
149
|
+
let output = "";
|
|
150
|
+
if (baseline) {
|
|
151
|
+
output += formatTransformationTerminal(baseline, currentSnapshot);
|
|
152
|
+
} else {
|
|
153
|
+
output +=
|
|
154
|
+
"\n\x1b[33mNo baseline audit found. Current audit established as baseline.\x1b[0m\n";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (history.length > 0) {
|
|
158
|
+
output += "\n\n" + formatHistoryTimelineTerminal(history);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
await showPagedContent(output);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const handleExportAction = async () => {
|
|
165
|
+
const outName = hasGum()
|
|
166
|
+
? gumInput("Export file path:", "AUDIT_REPORT.md") || "AUDIT_REPORT.md"
|
|
167
|
+
: (await promptQuestion("Export file path [AUDIT_REPORT.md]: ")) || "AUDIT_REPORT.md";
|
|
168
|
+
const md = generateMarkdownReport(report);
|
|
169
|
+
fs.writeFileSync(path.resolve(process.cwd(), outName), md, "utf-8");
|
|
170
|
+
process.stdout.write(`\x1b[32m✔ Exported markdown audit report to ${outName}\x1b[0m\n\n`);
|
|
171
|
+
if (hasGum()) {
|
|
172
|
+
gumChoose(["<-- Back to Audit Dashboard"]);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const handleCopyPromptAction = async () => {
|
|
177
|
+
const masterPrompt = buildMasterPrompt(report);
|
|
178
|
+
if (!masterPrompt) {
|
|
179
|
+
process.stdout.write("\n\x1b[32m✔ Codebase is pristine (Grade A+). No refactoring prompt needed!\x1b[0m\n\n");
|
|
180
|
+
if (hasGum()) {
|
|
181
|
+
gumChoose(["<-- Back to Audit Dashboard"]);
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const success = copyToClipboard(masterPrompt);
|
|
186
|
+
if (success) {
|
|
187
|
+
process.stdout.write(
|
|
188
|
+
"\n\x1b[1m\x1b[32m✔ AI Agent refactoring prompt copied to clipboard!\x1b[0m\n" +
|
|
189
|
+
"\x1b[36mPaste directly into Cursor, Claude, or Windsurf to resolve architectural hazards.\x1b[0m\n\n"
|
|
190
|
+
);
|
|
191
|
+
} else {
|
|
192
|
+
process.stdout.write("\n\x1b[33m⚠ Could not access system clipboard.\x1b[0m\n\n");
|
|
193
|
+
}
|
|
194
|
+
if (hasGum()) {
|
|
195
|
+
gumChoose(["<-- Back to Audit Dashboard"]);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const handleExitAction = async () => {};
|
|
200
|
+
|
|
201
|
+
const installAction = {
|
|
202
|
+
key: "install",
|
|
203
|
+
tag: formatButtonTag("Install", "\x1b[32m"),
|
|
204
|
+
label: "🪝 Install Pre-Commit Hook & GitHub CI Workflow",
|
|
205
|
+
action: handleInstallAction
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const upgradeAction = {
|
|
209
|
+
key: "upgrade",
|
|
210
|
+
tag: formatButtonTag("Upgrade", "\x1b[33m"),
|
|
211
|
+
label: "💎 Unlock Full Molecular Rules & Scaffolding (Chemical X: Team Power Puff)",
|
|
212
|
+
action: handleUpgradeAction
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const reportAction = {
|
|
216
|
+
key: "report",
|
|
217
|
+
tag: formatButtonTag("Full Report", "\x1b[36m"),
|
|
218
|
+
label: "📋 Show Full Report (All Sections at Once)",
|
|
219
|
+
action: handleReportAction
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const shareAction = {
|
|
223
|
+
key: "share",
|
|
224
|
+
tag: formatButtonTag("Share", "\x1b[35m"),
|
|
225
|
+
label: "🕵 Plug your site and Post to GitHub Discussions (npx chemx audit)",
|
|
226
|
+
action: handleShareAction
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const progressAction = {
|
|
230
|
+
key: "progress",
|
|
231
|
+
tag: formatButtonTag("Progress", "\x1b[32m"),
|
|
232
|
+
label: "📈 View Before & After Transformation Progress",
|
|
233
|
+
action: handleProgressAction
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const exportAction = {
|
|
237
|
+
key: "export",
|
|
238
|
+
tag: formatButtonTag("Export", "\x1b[36m"),
|
|
239
|
+
label: "💾 Export Markdown Report to File",
|
|
240
|
+
action: handleExportAction
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const copyPromptAction = {
|
|
244
|
+
key: "prompt",
|
|
245
|
+
tag: formatButtonTag("Prompt", "\x1b[38;2;98;201;255m"),
|
|
246
|
+
label: "📋 Copy AI Prompt Fix to Clipboard",
|
|
247
|
+
action: handleCopyPromptAction
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const exitAction = {
|
|
251
|
+
key: "exit",
|
|
252
|
+
tag: formatButtonTag("Exit", "\x1b[90m"),
|
|
253
|
+
label: "🚪 Exit",
|
|
254
|
+
action: handleExitAction
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const masterPrompt = buildMasterPrompt(report);
|
|
258
|
+
const isGradeAPlus = report?.health?.grade === "A+" || report?.metrics?.grade === "A+";
|
|
259
|
+
const hasPromptToCopy = Boolean(masterPrompt && masterPrompt.trim().length > 0);
|
|
260
|
+
const isCleanAPlus = isGradeAPlus && !hasPromptToCopy;
|
|
261
|
+
const shouldShowPromptAction = hasPromptToCopy && !isCleanAPlus;
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
installAction,
|
|
265
|
+
upgradeAction,
|
|
266
|
+
reportAction,
|
|
267
|
+
shareAction,
|
|
268
|
+
progressAction,
|
|
269
|
+
exportAction,
|
|
270
|
+
copyPromptAction,
|
|
271
|
+
exitAction,
|
|
272
|
+
shouldShowPromptAction
|
|
273
|
+
};
|
|
274
|
+
};
|