@chemx/starter-kit 26.9.9-786 → 26.9.11-481
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/audit/ast-visitors.js +243 -0
- package/cli/audit/discussion-store.js +39 -0
- package/cli/audit/history.d.ts +69 -0
- package/cli/audit/history.js +397 -0
- package/cli/audit/metrics.js +179 -0
- package/cli/audit/prompts.js +174 -0
- package/cli/audit/reporter-ascii.d.ts +4 -0
- package/cli/audit/reporter-ascii.js +99 -0
- package/cli/audit/reporter-grades.js +275 -0
- package/cli/audit/reporter-markdown.js +137 -0
- package/cli/audit/reporter-utils.js +125 -0
- package/cli/audit/reporter.js +497 -0
- package/cli/audit/rules-helpers.js +109 -0
- package/cli/audit/rules-registry.js +97 -0
- package/cli/audit/rules.js +123 -0
- package/cli/audit/social-constants.js +4 -0
- package/cli/audit/social-gh.js +126 -0
- package/cli/audit/social-git.js +214 -0
- package/cli/audit/social-http.js +222 -0
- package/cli/audit/social-publisher.js +193 -0
- package/cli/audit/social.d.ts +69 -0
- package/cli/audit/social.js +303 -0
- package/cli/audit/types.d.ts +140 -0
- package/cli/audit.d.ts +3 -24
- package/cli/audit.js +207 -210
- package/cli/help.d.ts +1 -0
- package/cli/help.js +136 -0
- package/cli/index.js +128 -566
- package/cli/installer-templates.js +91 -0
- package/cli/installer.d.ts +21 -0
- package/cli/installer.js +104 -0
- package/cli/license.js +320 -0
- package/cli/navigator-actions.js +274 -0
- package/cli/navigator-banner.js +98 -0
- package/cli/navigator-conversion.js +100 -0
- package/cli/navigator-grades.js +87 -0
- package/cli/navigator-menu.js +127 -0
- package/cli/navigator-paged.js +86 -0
- package/cli/navigator-share.js +129 -0
- package/cli/navigator.d.ts +43 -0
- package/cli/navigator.js +126 -0
- package/cli/scaffold.js +148 -0
- package/cli/terminal.js +126 -0
- package/docs/CHANGELOG.md +101 -0
- package/package.json +11 -10
package/cli/audit.js
CHANGED
|
@@ -1,189 +1,52 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
rule: 'LINE_BUDGET_MOLECULE',
|
|
51
|
-
directive: 'Split molecule into focused sub-molecules or extract state to hook'
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const codeToParse = extractParseableCode(content, ext);
|
|
56
|
-
if (!codeToParse.trim()) {
|
|
57
|
-
return violations;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let ast;
|
|
61
|
-
try {
|
|
62
|
-
ast = parse(codeToParse, {
|
|
63
|
-
sourceType: 'module',
|
|
64
|
-
plugins: ['typescript', 'jsx']
|
|
65
|
-
});
|
|
66
|
-
} catch (err) {
|
|
67
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
68
|
-
violations.push({
|
|
69
|
-
filePath: relativePath,
|
|
70
|
-
line: 1,
|
|
71
|
-
hazard: `Parse error: ${errorMsg}`,
|
|
72
|
-
rule: 'SYNTAX_PARSE_ERROR',
|
|
73
|
-
directive: 'Fix syntax errors before static analysis'
|
|
74
|
-
});
|
|
75
|
-
return violations;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const traverseFn = traverse.default || traverse;
|
|
79
|
-
|
|
80
|
-
traverseFn(ast, {
|
|
81
|
-
// 2. Hook Saturation Check
|
|
82
|
-
Function(astPath) {
|
|
83
|
-
let hookCount = 0;
|
|
84
|
-
astPath.traverse({
|
|
85
|
-
CallExpression(callPath) {
|
|
86
|
-
if (t.isIdentifier(callPath.node.callee) && /^use[A-Z0-9]/.test(callPath.node.callee.name)) {
|
|
87
|
-
if (callPath.getFunctionParent() === astPath) {
|
|
88
|
-
hookCount += 1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (hookCount > 5) {
|
|
95
|
-
const line = astPath.node.loc?.start.line || 1;
|
|
96
|
-
violations.push({
|
|
97
|
-
filePath: relativePath,
|
|
98
|
-
line,
|
|
99
|
-
hazard: `Hook saturation detected (${hookCount} hooks > 5 limit)`,
|
|
100
|
-
rule: 'HOOK_SATURATION',
|
|
101
|
-
directive: 'Extract related state and effects into dedicated domain hooks'
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
// 3. Control Flow Complexity
|
|
107
|
-
JSXExpressionContainer(astPath) {
|
|
108
|
-
const expr = astPath.node.expression;
|
|
109
|
-
if (t.isLogicalExpression(expr) || t.isUnaryExpression(expr)) {
|
|
110
|
-
const opCount = countLogicalOperators(expr);
|
|
111
|
-
if (opCount > 2) {
|
|
112
|
-
const line = expr.loc?.start.line || astPath.node.loc?.start.line || 1;
|
|
113
|
-
violations.push({
|
|
114
|
-
filePath: relativePath,
|
|
115
|
-
line,
|
|
116
|
-
hazard: `Inline boolean complexity (${opCount} logical operators > 2 limit)`,
|
|
117
|
-
rule: 'CONTROL_FLOW_INLINE_BOOLEAN',
|
|
118
|
-
directive: 'Compose booleans into Stage 1 concepts and Stage 2 decision variables'
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
|
|
124
|
-
ConditionalExpression(astPath) {
|
|
125
|
-
if (t.isConditionalExpression(astPath.node.consequent) || t.isConditionalExpression(astPath.node.alternate)) {
|
|
126
|
-
const line = astPath.node.loc?.start.line || 1;
|
|
127
|
-
violations.push({
|
|
128
|
-
filePath: relativePath,
|
|
129
|
-
line,
|
|
130
|
-
hazard: 'Nested ternary operator detected',
|
|
131
|
-
rule: 'CONTROL_FLOW_NESTED_TERNARY',
|
|
132
|
-
directive: 'Extract display states into computed descriptor objects or early returns'
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
// 4. Timer Discipline
|
|
138
|
-
CallExpression(astPath) {
|
|
139
|
-
const callee = astPath.node.callee;
|
|
140
|
-
if (t.isIdentifier(callee) && (callee.name === 'setInterval' || callee.name === 'setTimeout')) {
|
|
141
|
-
const fnParent = astPath.getFunctionParent();
|
|
142
|
-
let hasCleanup = false;
|
|
143
|
-
if (fnParent) {
|
|
144
|
-
fnParent.traverse({
|
|
145
|
-
ReturnStatement(retPath) {
|
|
146
|
-
if (retPath.node.argument) {
|
|
147
|
-
hasCleanup = true;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (!hasCleanup) {
|
|
154
|
-
const line = astPath.node.loc?.start.line || 1;
|
|
155
|
-
violations.push({
|
|
156
|
-
filePath: relativePath,
|
|
157
|
-
line,
|
|
158
|
-
hazard: `Raw ${callee.name} lacking lifecycle scope disposal`,
|
|
159
|
-
rule: 'TIMER_DISCIPLINE',
|
|
160
|
-
directive: 'Wrap timers in self-cleaning hooks returning cleanup disposers'
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
// 5. Type Co-location
|
|
167
|
-
TSTypeLiteral(astPath) {
|
|
168
|
-
if (astPath.node.members.length > 3) {
|
|
169
|
-
if (!astPath.findParent((p) => p.isTSTypeAliasDeclaration() || p.isTSInterfaceDeclaration())) {
|
|
170
|
-
const line = astPath.node.loc?.start.line || 1;
|
|
171
|
-
violations.push({
|
|
172
|
-
filePath: relativePath,
|
|
173
|
-
line,
|
|
174
|
-
hazard: `Inlined anonymous complex type (${astPath.node.members.length} members)`,
|
|
175
|
-
rule: 'TYPE_COLOCATION',
|
|
176
|
-
directive: 'Define co-located domain interfaces in types/*.d.ts'
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
return violations;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const IGNORED_DIRS = new Set(['node_modules', 'dist', 'build', 'vendor', '.git', '.next', '.turbo', '.output', 'out']);
|
|
3
|
+
import { auditCode, PILLARS, RULE_REGISTRY } from './audit/rules.js';
|
|
4
|
+
import {
|
|
5
|
+
calculateMolecularHealthScore,
|
|
6
|
+
calculateQuantumHealthScore,
|
|
7
|
+
calculatePillarBreakdown,
|
|
8
|
+
calculateTokenBurnAnalytics,
|
|
9
|
+
calculateHotspots
|
|
10
|
+
} from './audit/metrics.js';
|
|
11
|
+
import {
|
|
12
|
+
formatTerminalReport,
|
|
13
|
+
generateMarkdownReport,
|
|
14
|
+
groupViolationsBySeverity,
|
|
15
|
+
resolveTopSectionColor,
|
|
16
|
+
formatScorecardSection,
|
|
17
|
+
formatCriticalSection,
|
|
18
|
+
formatHighMediumSection,
|
|
19
|
+
formatLowSection,
|
|
20
|
+
formatPillarsSection,
|
|
21
|
+
formatHotspotsSection,
|
|
22
|
+
formatContextAnalysisSection,
|
|
23
|
+
formatFailuresSection,
|
|
24
|
+
formatPassesSection,
|
|
25
|
+
formatGradeFSection,
|
|
26
|
+
formatGradeDSection,
|
|
27
|
+
formatGradeCSection,
|
|
28
|
+
formatGradeBSection,
|
|
29
|
+
formatGradeASection,
|
|
30
|
+
getChemicalXAsciiBanner,
|
|
31
|
+
getAsciiGradeLines,
|
|
32
|
+
formatAsciiGrade,
|
|
33
|
+
getReportCardAsciiLines,
|
|
34
|
+
REPORT_CARD_ASCII
|
|
35
|
+
} from './audit/reporter.js';
|
|
36
|
+
|
|
37
|
+
const IGNORED_DIRS = new Set([
|
|
38
|
+
'node_modules',
|
|
39
|
+
'dist',
|
|
40
|
+
'build',
|
|
41
|
+
'vendor',
|
|
42
|
+
'.git',
|
|
43
|
+
'.next',
|
|
44
|
+
'.turbo',
|
|
45
|
+
'.output',
|
|
46
|
+
'.nuxt',
|
|
47
|
+
'.cache',
|
|
48
|
+
'out'
|
|
49
|
+
]);
|
|
187
50
|
|
|
188
51
|
const isSourceFile = (name) => {
|
|
189
52
|
return (
|
|
@@ -195,9 +58,19 @@ const isSourceFile = (name) => {
|
|
|
195
58
|
);
|
|
196
59
|
};
|
|
197
60
|
|
|
198
|
-
export const
|
|
199
|
-
|
|
200
|
-
|
|
61
|
+
export const auditFile = (filePath, relativePath) => {
|
|
62
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
63
|
+
return auditCode(content, filePath, relativePath);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const scanTree = (targetDir, baseDir) => {
|
|
67
|
+
let violations = [];
|
|
68
|
+
let fileStats = [];
|
|
69
|
+
let totalHooks = 0;
|
|
70
|
+
|
|
71
|
+
if (!fs.existsSync(targetDir)) {
|
|
72
|
+
return { violations, fileStats, totalHooks };
|
|
73
|
+
}
|
|
201
74
|
|
|
202
75
|
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
203
76
|
for (const entry of entries) {
|
|
@@ -206,48 +79,172 @@ export const scanDirectory = (targetDir, baseDir) => {
|
|
|
206
79
|
|
|
207
80
|
if (entry.isDirectory()) {
|
|
208
81
|
if (!IGNORED_DIRS.has(entry.name)) {
|
|
209
|
-
|
|
82
|
+
const sub = scanTree(fullPath, baseDir);
|
|
83
|
+
violations = violations.concat(sub.violations);
|
|
84
|
+
fileStats = fileStats.concat(sub.fileStats);
|
|
85
|
+
totalHooks += sub.totalHooks;
|
|
210
86
|
}
|
|
211
87
|
} else if (isSourceFile(entry.name)) {
|
|
212
|
-
|
|
88
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
89
|
+
const lines = content.split('\n');
|
|
90
|
+
const isMolecule = relPath.includes('molecules') || relPath.includes('/m-') || entry.name.startsWith('m-');
|
|
91
|
+
|
|
92
|
+
fileStats.push({
|
|
93
|
+
fullPath,
|
|
94
|
+
relativePath: relPath,
|
|
95
|
+
lineCount: lines.length,
|
|
96
|
+
charCount: content.length,
|
|
97
|
+
isMolecule
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Count hook declarations / invocations for metric
|
|
101
|
+
const hookMatches = content.match(/\buse[A-Z0-9]\w*\b/g);
|
|
102
|
+
if (hookMatches) {
|
|
103
|
+
totalHooks += hookMatches.length;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const fileViolations = auditCode(content, fullPath, relPath);
|
|
107
|
+
violations = violations.concat(fileViolations);
|
|
213
108
|
}
|
|
214
109
|
}
|
|
215
|
-
|
|
110
|
+
|
|
111
|
+
return { violations, fileStats, totalHooks };
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const scanDirectory = (targetDir, baseDir) => {
|
|
115
|
+
const { violations } = scanTree(targetDir, baseDir);
|
|
116
|
+
return violations;
|
|
216
117
|
};
|
|
217
118
|
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
119
|
+
export const runAudit = (targetDir = 'src', options = {}) => {
|
|
120
|
+
const cwd = process.cwd();
|
|
121
|
+
const absoluteTarget = path.resolve(cwd, targetDir);
|
|
122
|
+
const { violations, fileStats, totalHooks } = scanTree(absoluteTarget, cwd);
|
|
221
123
|
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
124
|
+
const scannedFiles = fileStats.length;
|
|
125
|
+
const totalLoc = fileStats.reduce((acc, f) => acc + f.lineCount, 0);
|
|
126
|
+
const avgLoc = scannedFiles > 0 ? Math.round(totalLoc / scannedFiles) : 0;
|
|
127
|
+
|
|
128
|
+
let largestFile = { filePath: '', lineCount: 0 };
|
|
129
|
+
let moleculeCount = 0;
|
|
130
|
+
let moleculeCompliantCount = 0;
|
|
131
|
+
|
|
132
|
+
for (const f of fileStats) {
|
|
133
|
+
if (f.lineCount > largestFile.lineCount) {
|
|
134
|
+
largestFile = { filePath: f.relativePath, lineCount: f.lineCount };
|
|
135
|
+
}
|
|
136
|
+
if (f.isMolecule) {
|
|
137
|
+
moleculeCount += 1;
|
|
138
|
+
if (f.lineCount <= 100) {
|
|
139
|
+
moleculeCompliantCount += 1;
|
|
228
140
|
}
|
|
229
|
-
} else if (isSourceFile(entry.name)) {
|
|
230
|
-
count += 1;
|
|
231
141
|
}
|
|
232
142
|
}
|
|
233
|
-
return count;
|
|
234
|
-
};
|
|
235
143
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
144
|
+
const moleculeCompliantPct = moleculeCount > 0
|
|
145
|
+
? Math.round((moleculeCompliantCount / moleculeCount) * 100)
|
|
146
|
+
: 100;
|
|
147
|
+
|
|
148
|
+
const metrics = {
|
|
149
|
+
scannedFiles,
|
|
150
|
+
totalLoc,
|
|
151
|
+
avgLoc,
|
|
152
|
+
largestFile,
|
|
153
|
+
moleculeCount,
|
|
154
|
+
moleculeCompliantCount,
|
|
155
|
+
moleculeCompliantPct,
|
|
156
|
+
hookCount: totalHooks
|
|
157
|
+
};
|
|
241
158
|
|
|
242
|
-
|
|
159
|
+
const health = calculateMolecularHealthScore(violations, scannedFiles);
|
|
160
|
+
const pillars = calculatePillarBreakdown(violations);
|
|
161
|
+
const contextAnalysis = calculateTokenBurnAnalytics(fileStats, options);
|
|
162
|
+
const hotspots = calculateHotspots(violations, fileStats, 5);
|
|
163
|
+
|
|
164
|
+
const report = {
|
|
243
165
|
scannedFiles,
|
|
244
166
|
totalViolations: violations.length,
|
|
167
|
+
metrics,
|
|
168
|
+
health,
|
|
169
|
+
pillars,
|
|
170
|
+
contextAnalysis,
|
|
171
|
+
hotspots,
|
|
245
172
|
violations
|
|
246
173
|
};
|
|
174
|
+
|
|
175
|
+
if (options.outputFile) {
|
|
176
|
+
const outPath = path.resolve(cwd, options.outputFile);
|
|
177
|
+
const mdContent = generateMarkdownReport(report);
|
|
178
|
+
fs.writeFileSync(outPath, mdContent, 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return report;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export * from './audit/social.js';
|
|
185
|
+
export * from './audit/prompts.js';
|
|
186
|
+
export * from './audit/history.js';
|
|
187
|
+
|
|
188
|
+
export {
|
|
189
|
+
calculateMolecularHealthScore,
|
|
190
|
+
calculateQuantumHealthScore,
|
|
191
|
+
calculatePillarBreakdown,
|
|
192
|
+
calculateTokenBurnAnalytics,
|
|
193
|
+
calculateHotspots,
|
|
194
|
+
PILLARS,
|
|
195
|
+
RULE_REGISTRY,
|
|
196
|
+
formatTerminalReport,
|
|
197
|
+
generateMarkdownReport,
|
|
198
|
+
groupViolationsBySeverity,
|
|
199
|
+
resolveTopSectionColor,
|
|
200
|
+
formatScorecardSection,
|
|
201
|
+
formatCriticalSection,
|
|
202
|
+
formatHighMediumSection,
|
|
203
|
+
formatLowSection,
|
|
204
|
+
formatPillarsSection,
|
|
205
|
+
formatHotspotsSection,
|
|
206
|
+
formatContextAnalysisSection,
|
|
207
|
+
formatFailuresSection,
|
|
208
|
+
formatPassesSection,
|
|
209
|
+
formatGradeFSection,
|
|
210
|
+
formatGradeDSection,
|
|
211
|
+
formatGradeCSection,
|
|
212
|
+
formatGradeBSection,
|
|
213
|
+
formatGradeASection,
|
|
214
|
+
getChemicalXAsciiBanner,
|
|
215
|
+
getAsciiGradeLines,
|
|
216
|
+
formatAsciiGrade,
|
|
217
|
+
getReportCardAsciiLines,
|
|
218
|
+
REPORT_CARD_ASCII
|
|
247
219
|
};
|
|
248
220
|
|
|
249
221
|
export default {
|
|
250
222
|
auditFile,
|
|
251
223
|
scanDirectory,
|
|
252
|
-
runAudit
|
|
224
|
+
runAudit,
|
|
225
|
+
formatTerminalReport,
|
|
226
|
+
generateMarkdownReport,
|
|
227
|
+
groupViolationsBySeverity,
|
|
228
|
+
resolveTopSectionColor,
|
|
229
|
+
formatScorecardSection,
|
|
230
|
+
formatCriticalSection,
|
|
231
|
+
formatHighMediumSection,
|
|
232
|
+
formatLowSection,
|
|
233
|
+
formatPillarsSection,
|
|
234
|
+
formatHotspotsSection,
|
|
235
|
+
formatContextAnalysisSection,
|
|
236
|
+
formatFailuresSection,
|
|
237
|
+
formatPassesSection,
|
|
238
|
+
formatGradeFSection,
|
|
239
|
+
formatGradeDSection,
|
|
240
|
+
formatGradeCSection,
|
|
241
|
+
formatGradeBSection,
|
|
242
|
+
formatGradeASection,
|
|
243
|
+
getChemicalXAsciiBanner,
|
|
244
|
+
getAsciiGradeLines,
|
|
245
|
+
formatAsciiGrade,
|
|
246
|
+
getReportCardAsciiLines,
|
|
247
|
+
REPORT_CARD_ASCII,
|
|
248
|
+
PILLARS,
|
|
249
|
+
RULE_REGISTRY
|
|
253
250
|
};
|
package/cli/help.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function printHelp(): void;
|
package/cli/help.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { renderBanner } from './terminal.js';
|
|
2
|
+
|
|
3
|
+
export const printHelp = () => {
|
|
4
|
+
renderBanner('Chemical X Protocol: CLI Usage & Reference');
|
|
5
|
+
|
|
6
|
+
const BOLD = '\x1b[1m';
|
|
7
|
+
const CYAN = '\x1b[36m';
|
|
8
|
+
const DIM = '\x1b[2m';
|
|
9
|
+
const YELLOW = '\x1b[33m';
|
|
10
|
+
const GREEN = '\x1b[32m';
|
|
11
|
+
const RESET = '\x1b[0m';
|
|
12
|
+
|
|
13
|
+
const out = [
|
|
14
|
+
`${BOLD}USAGE${RESET}`,
|
|
15
|
+
` ${CYAN}npx chemx${RESET} <command> [options]`,
|
|
16
|
+
` ${CYAN}npm create chemx${RESET} [directory]`,
|
|
17
|
+
'',
|
|
18
|
+
`${BOLD}COMMANDS${RESET}`,
|
|
19
|
+
` ${CYAN}audit${RESET} [options]`,
|
|
20
|
+
` Run the 7-Pillar Molecular Architecture static analysis audit.`,
|
|
21
|
+
` Scans AST for line budgets, control flow booleans, nested ternaries,`,
|
|
22
|
+
` hook limits, self-cleaning timers, and Anti-Tailwind-Soup compliance.`,
|
|
23
|
+
` Computes Molecular Health Index (MHI: 0-100) and letter grades (A+ to F).`,
|
|
24
|
+
` Defaults to interactive zero-scroll Gum terminal dashboard.`,
|
|
25
|
+
'',
|
|
26
|
+
` ${CYAN}generate${RESET} <m-name> ${DIM}(aliases: capsule, add)${RESET}`,
|
|
27
|
+
` Scaffold an isolated crystalline molecule capsule directory:`,
|
|
28
|
+
` ${DIM}- ${RESET}<m-name>/<m-name>.tsx ${DIM}(declarative component < 50 lines of code)${RESET}`,
|
|
29
|
+
` ${DIM}- ${RESET}<m-name>/types.d.ts ${DIM}(props and emits interfaces)${RESET}`,
|
|
30
|
+
` ${DIM}- ${RESET}<m-name>/index.ts ${DIM}(clean public barrel export)${RESET}`,
|
|
31
|
+
` Free evaluation mode supported with non-blocking license reminder.`,
|
|
32
|
+
` Example: ${CYAN}npx chemx generate m-user-avatar${RESET}`,
|
|
33
|
+
'',
|
|
34
|
+
` ${CYAN}init${RESET} [directory]`,
|
|
35
|
+
` ${YELLOW}[PAID]${RESET} Drop full Chemical X blueprints, core hooks (toResult,`,
|
|
36
|
+
` useAsyncData, useSelfCleaningTimer), and AGENTS.md directives into`,
|
|
37
|
+
` an existing codebase. Default destination: ${CYAN}src/chemical-x${RESET}.`,
|
|
38
|
+
` Requires Single Dev ($27) or Team Power Puff ($97: Unlimited Lifetime) license.`,
|
|
39
|
+
'',
|
|
40
|
+
` ${CYAN}create${RESET} [directory] ${DIM}(or: npm create chemx [directory])${RESET}`,
|
|
41
|
+
` ${YELLOW}[PAID]${RESET} Scaffold a complete, clean-slate project wired with full`,
|
|
42
|
+
` Chemical X molecular standards, pre-commit line budget hooks, and rules.`,
|
|
43
|
+
` Requires Single Dev ($27) or Team Power Puff ($97: Unlimited Lifetime) license.`,
|
|
44
|
+
'',
|
|
45
|
+
` ${CYAN}install-hooks${RESET} [directory] ${DIM}(aliases: hook, setup-ci)${RESET}`,
|
|
46
|
+
` ${GREEN}[FREE]${RESET} Install git pre-commit hook (.git/hooks/pre-commit) and`,
|
|
47
|
+
` GitHub Actions CI workflow (.github/workflows/chemx-audit.yml).`,
|
|
48
|
+
` Enforces 500-line file ceilings, 100-line molecule budgets, and configurable`,
|
|
49
|
+
` minimum grade/score thresholds with automatic AI prompt clipboard copying.`,
|
|
50
|
+
` Example: ${CYAN}npx chemx install-hooks${RESET}`,
|
|
51
|
+
'',
|
|
52
|
+
` ${CYAN}help${RESET}, ${CYAN}--help${RESET}, ${CYAN}-h${RESET}`,
|
|
53
|
+
` Display this comprehensive CLI documentation and options guide.`,
|
|
54
|
+
'',
|
|
55
|
+
`${BOLD}AUDIT OPTIONS & FLAGS${RESET}`,
|
|
56
|
+
` ${CYAN}--dir=<path>${RESET}`,
|
|
57
|
+
` Directory to analyze. Defaults to ${CYAN}src${RESET} if present, else current working directory.`,
|
|
58
|
+
` Example: ${CYAN}npx chemx audit --dir=apps/web/src${RESET}`,
|
|
59
|
+
'',
|
|
60
|
+
` ${CYAN}--json${RESET}`,
|
|
61
|
+
` Output structured machine-readable JSON report to stdout.`,
|
|
62
|
+
` Ideal for CI/CD pipelines, automated gating, and custom tooling.`,
|
|
63
|
+
'',
|
|
64
|
+
` ${CYAN}--markdown${RESET}, ${CYAN}--md${RESET}`,
|
|
65
|
+
` Output formatted GitHub Flavored Markdown report to stdout.`,
|
|
66
|
+
'',
|
|
67
|
+
` ${CYAN}--output=<file>${RESET}, ${CYAN}-o=<file>${RESET}`,
|
|
68
|
+
` Save formatted Markdown report directly to the specified file path.`,
|
|
69
|
+
` Example: ${CYAN}npx chemx audit --output=AUDIT_REPORT.md${RESET}`,
|
|
70
|
+
'',
|
|
71
|
+
` ${CYAN}--unroll${RESET}, ${CYAN}--all${RESET}`,
|
|
72
|
+
` Stream full unrolled report directly to terminal without interactive Gum menu.`,
|
|
73
|
+
'',
|
|
74
|
+
` ${CYAN}--share${RESET}, ${CYAN}--post${RESET}`,
|
|
75
|
+
` Publish or update report on GitHub Discussions without entering dashboard.`,
|
|
76
|
+
` Automatically archives prior audit snapshots as discussion comments.`,
|
|
77
|
+
'',
|
|
78
|
+
` ${CYAN}--strict${RESET}`,
|
|
79
|
+
` Strict CI failure mode: exits with status 1 on ANY violation, including`,
|
|
80
|
+
` Low hygiene issues (typography and console logging).`,
|
|
81
|
+
'',
|
|
82
|
+
` ${CYAN}--min-grade=<tier>${RESET}`,
|
|
83
|
+
` Fail audit if Molecular Health Index grade falls below tier (A+, A, B, C, D).`,
|
|
84
|
+
` Default: None (or B when executed via pre-commit gatekeeper).`,
|
|
85
|
+
'',
|
|
86
|
+
` ${CYAN}--min-score=<0-100>${RESET}`,
|
|
87
|
+
` Fail audit if Molecular Health Index score falls below number (e.g. 80).`,
|
|
88
|
+
'',
|
|
89
|
+
` ${CYAN}--model=<claude|gpt4o|blended>${RESET}`,
|
|
90
|
+
` Pricing baseline for context burn dollar calculations ($3.00/1M blended).`,
|
|
91
|
+
'',
|
|
92
|
+
` ${CYAN}--prompt-on-fail${RESET}, ${CYAN}--copy-prompt${RESET}`,
|
|
93
|
+
` Automatically copy the synthesized AI refactoring prompt to system clipboard`,
|
|
94
|
+
` when architectural violations or threshold failures are detected.`,
|
|
95
|
+
'',
|
|
96
|
+
` ${CYAN}--license=<key>${RESET}`,
|
|
97
|
+
` Pass Chemical X Sponsor or Team license key non-interactively via CLI.`,
|
|
98
|
+
` Tiers: Single Dev ($27) | Team Power Puff ($97: Unlimited Lifetime).`,
|
|
99
|
+
'',
|
|
100
|
+
`${BOLD}ENVIRONMENT VARIABLES${RESET}`,
|
|
101
|
+
` ${GREEN}CHEMX_MIN_GRADE${RESET}`,
|
|
102
|
+
` Default minimum grade threshold for pre-commit gatekeeper (default: B).`,
|
|
103
|
+
'',
|
|
104
|
+
` ${GREEN}CHEMX_MIN_SCORE${RESET}`,
|
|
105
|
+
` Default minimum score threshold for pre-commit gatekeeper (default: 80).`,
|
|
106
|
+
'',
|
|
107
|
+
` ${GREEN}GH_TOKEN${RESET} / ${GREEN}GITHUB_TOKEN${RESET}`,
|
|
108
|
+
` GitHub personal access token for automated discussion publishing via GraphQL.`,
|
|
109
|
+
` If omitted, the CLI automatically delegates to local ${CYAN}gh${RESET} CLI authentication.`,
|
|
110
|
+
'',
|
|
111
|
+
` ${GREEN}CHEMICAL_X_API_URL${RESET}`,
|
|
112
|
+
` Custom licensing edge verification endpoint. Default: ${CYAN}https://chemicalx.xophz.com${RESET}.`,
|
|
113
|
+
'',
|
|
114
|
+
`${BOLD}EXAMPLES${RESET}`,
|
|
115
|
+
` ${DIM}# Run interactive audit dashboard with ASCII banner and health scorecard:${RESET}`,
|
|
116
|
+
` ${CYAN}npx chemx audit${RESET}`,
|
|
117
|
+
'',
|
|
118
|
+
` ${DIM}# Scan a sub-package in strict mode for CI gating:${RESET}`,
|
|
119
|
+
` ${CYAN}npx chemx audit --dir=packages/core --strict${RESET}`,
|
|
120
|
+
'',
|
|
121
|
+
` ${DIM}# Export markdown scorecard to file:${RESET}`,
|
|
122
|
+
` ${CYAN}npx chemx audit --markdown --output=AUDIT.md${RESET}`,
|
|
123
|
+
'',
|
|
124
|
+
` ${DIM}# Pipe JSON metrics to jq:${RESET}`,
|
|
125
|
+
` ${CYAN}npx chemx audit --json | jq .health${RESET}`,
|
|
126
|
+
'',
|
|
127
|
+
` ${DIM}# Generate a new molecule capsule:${RESET}`,
|
|
128
|
+
` ${CYAN}npx chemx generate m-spark-kpi${RESET}`,
|
|
129
|
+
'',
|
|
130
|
+
` ${DIM}# Scaffold a new project:${RESET}`,
|
|
131
|
+
` ${CYAN}npm create chemx my-awesome-app${RESET}`,
|
|
132
|
+
''
|
|
133
|
+
].join('\n');
|
|
134
|
+
|
|
135
|
+
process.stdout.write(out + '\n');
|
|
136
|
+
};
|