@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.
- package/cli/audit/ast-visitors.js +243 -0
- package/cli/audit/discussion-store.js +39 -0
- package/cli/audit/history.d.ts +63 -0
- package/cli/audit/history.js +352 -0
- package/cli/audit/metrics.js +179 -0
- package/cli/audit/prompts.js +174 -0
- package/cli/audit/reporter-grades.js +259 -0
- package/cli/audit/reporter-markdown.js +136 -0
- package/cli/audit/reporter-utils.js +107 -0
- package/cli/audit/reporter.js +459 -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 +66 -0
- package/cli/audit/social.js +254 -0
- package/cli/audit/types.d.ts +131 -0
- package/cli/audit.d.ts +3 -24
- package/cli/audit.js +195 -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 +80 -0
- package/cli/installer.d.ts +20 -0
- package/cli/installer.js +56 -0
- package/cli/license.js +320 -0
- package/cli/navigator-conversion.js +100 -0
- package/cli/navigator-grades.js +70 -0
- package/cli/navigator-paged.js +86 -0
- package/cli/navigator-share.js +129 -0
- package/cli/navigator.d.ts +24 -0
- package/cli/navigator.js +400 -0
- package/cli/scaffold.js +148 -0
- package/cli/terminal.js +126 -0
- package/docs/CHANGELOG.md +73 -0
- package/package.json +11 -10
package/cli/audit.js
CHANGED
|
@@ -1,189 +1,48 @@
|
|
|
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
|
-
violations.push({
|
|
47
|
-
filePath: relativePath,
|
|
48
|
-
line: 1,
|
|
49
|
-
hazard: `Molecule capsule budget exceeded (${lineCount} > 100 lines)`,
|
|
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
|
+
} from './audit/reporter.js';
|
|
32
|
+
|
|
33
|
+
const IGNORED_DIRS = new Set([
|
|
34
|
+
'node_modules',
|
|
35
|
+
'dist',
|
|
36
|
+
'build',
|
|
37
|
+
'vendor',
|
|
38
|
+
'.git',
|
|
39
|
+
'.next',
|
|
40
|
+
'.turbo',
|
|
41
|
+
'.output',
|
|
42
|
+
'.nuxt',
|
|
43
|
+
'.cache',
|
|
44
|
+
'out'
|
|
45
|
+
]);
|
|
187
46
|
|
|
188
47
|
const isSourceFile = (name) => {
|
|
189
48
|
return (
|
|
@@ -195,9 +54,19 @@ const isSourceFile = (name) => {
|
|
|
195
54
|
);
|
|
196
55
|
};
|
|
197
56
|
|
|
198
|
-
export const
|
|
199
|
-
|
|
200
|
-
|
|
57
|
+
export const auditFile = (filePath, relativePath) => {
|
|
58
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
59
|
+
return auditCode(content, filePath, relativePath);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const scanTree = (targetDir, baseDir) => {
|
|
63
|
+
let violations = [];
|
|
64
|
+
let fileStats = [];
|
|
65
|
+
let totalHooks = 0;
|
|
66
|
+
|
|
67
|
+
if (!fs.existsSync(targetDir)) {
|
|
68
|
+
return { violations, fileStats, totalHooks };
|
|
69
|
+
}
|
|
201
70
|
|
|
202
71
|
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
203
72
|
for (const entry of entries) {
|
|
@@ -206,48 +75,164 @@ export const scanDirectory = (targetDir, baseDir) => {
|
|
|
206
75
|
|
|
207
76
|
if (entry.isDirectory()) {
|
|
208
77
|
if (!IGNORED_DIRS.has(entry.name)) {
|
|
209
|
-
|
|
78
|
+
const sub = scanTree(fullPath, baseDir);
|
|
79
|
+
violations = violations.concat(sub.violations);
|
|
80
|
+
fileStats = fileStats.concat(sub.fileStats);
|
|
81
|
+
totalHooks += sub.totalHooks;
|
|
210
82
|
}
|
|
211
83
|
} else if (isSourceFile(entry.name)) {
|
|
212
|
-
|
|
84
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
85
|
+
const lines = content.split('\n');
|
|
86
|
+
const isMolecule = relPath.includes('molecules') || relPath.includes('/m-') || entry.name.startsWith('m-');
|
|
87
|
+
|
|
88
|
+
fileStats.push({
|
|
89
|
+
fullPath,
|
|
90
|
+
relativePath: relPath,
|
|
91
|
+
lineCount: lines.length,
|
|
92
|
+
charCount: content.length,
|
|
93
|
+
isMolecule
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Count hook declarations / invocations for metric
|
|
97
|
+
const hookMatches = content.match(/\buse[A-Z0-9]\w*\b/g);
|
|
98
|
+
if (hookMatches) {
|
|
99
|
+
totalHooks += hookMatches.length;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const fileViolations = auditCode(content, fullPath, relPath);
|
|
103
|
+
violations = violations.concat(fileViolations);
|
|
213
104
|
}
|
|
214
105
|
}
|
|
215
|
-
|
|
106
|
+
|
|
107
|
+
return { violations, fileStats, totalHooks };
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const scanDirectory = (targetDir, baseDir) => {
|
|
111
|
+
const { violations } = scanTree(targetDir, baseDir);
|
|
112
|
+
return violations;
|
|
216
113
|
};
|
|
217
114
|
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
115
|
+
export const runAudit = (targetDir = 'src', options = {}) => {
|
|
116
|
+
const cwd = process.cwd();
|
|
117
|
+
const absoluteTarget = path.resolve(cwd, targetDir);
|
|
118
|
+
const { violations, fileStats, totalHooks } = scanTree(absoluteTarget, cwd);
|
|
221
119
|
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
120
|
+
const scannedFiles = fileStats.length;
|
|
121
|
+
const totalLoc = fileStats.reduce((acc, f) => acc + f.lineCount, 0);
|
|
122
|
+
const avgLoc = scannedFiles > 0 ? Math.round(totalLoc / scannedFiles) : 0;
|
|
123
|
+
|
|
124
|
+
let largestFile = { filePath: '', lineCount: 0 };
|
|
125
|
+
let moleculeCount = 0;
|
|
126
|
+
let moleculeCompliantCount = 0;
|
|
127
|
+
|
|
128
|
+
for (const f of fileStats) {
|
|
129
|
+
if (f.lineCount > largestFile.lineCount) {
|
|
130
|
+
largestFile = { filePath: f.relativePath, lineCount: f.lineCount };
|
|
131
|
+
}
|
|
132
|
+
if (f.isMolecule) {
|
|
133
|
+
moleculeCount += 1;
|
|
134
|
+
if (f.lineCount <= 100) {
|
|
135
|
+
moleculeCompliantCount += 1;
|
|
228
136
|
}
|
|
229
|
-
} else if (isSourceFile(entry.name)) {
|
|
230
|
-
count += 1;
|
|
231
137
|
}
|
|
232
138
|
}
|
|
233
|
-
return count;
|
|
234
|
-
};
|
|
235
139
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
140
|
+
const moleculeCompliantPct = moleculeCount > 0
|
|
141
|
+
? Math.round((moleculeCompliantCount / moleculeCount) * 100)
|
|
142
|
+
: 100;
|
|
143
|
+
|
|
144
|
+
const metrics = {
|
|
145
|
+
scannedFiles,
|
|
146
|
+
totalLoc,
|
|
147
|
+
avgLoc,
|
|
148
|
+
largestFile,
|
|
149
|
+
moleculeCount,
|
|
150
|
+
moleculeCompliantCount,
|
|
151
|
+
moleculeCompliantPct,
|
|
152
|
+
hookCount: totalHooks
|
|
153
|
+
};
|
|
241
154
|
|
|
242
|
-
|
|
155
|
+
const health = calculateMolecularHealthScore(violations, scannedFiles);
|
|
156
|
+
const pillars = calculatePillarBreakdown(violations);
|
|
157
|
+
const contextAnalysis = calculateTokenBurnAnalytics(fileStats, options);
|
|
158
|
+
const hotspots = calculateHotspots(violations, fileStats, 5);
|
|
159
|
+
|
|
160
|
+
const report = {
|
|
243
161
|
scannedFiles,
|
|
244
162
|
totalViolations: violations.length,
|
|
163
|
+
metrics,
|
|
164
|
+
health,
|
|
165
|
+
pillars,
|
|
166
|
+
contextAnalysis,
|
|
167
|
+
hotspots,
|
|
245
168
|
violations
|
|
246
169
|
};
|
|
170
|
+
|
|
171
|
+
if (options.outputFile) {
|
|
172
|
+
const outPath = path.resolve(cwd, options.outputFile);
|
|
173
|
+
const mdContent = generateMarkdownReport(report);
|
|
174
|
+
fs.writeFileSync(outPath, mdContent, 'utf-8');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return report;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export * from './audit/social.js';
|
|
181
|
+
export * from './audit/prompts.js';
|
|
182
|
+
export * from './audit/history.js';
|
|
183
|
+
|
|
184
|
+
export {
|
|
185
|
+
calculateMolecularHealthScore,
|
|
186
|
+
calculateQuantumHealthScore,
|
|
187
|
+
calculatePillarBreakdown,
|
|
188
|
+
calculateTokenBurnAnalytics,
|
|
189
|
+
calculateHotspots,
|
|
190
|
+
PILLARS,
|
|
191
|
+
RULE_REGISTRY,
|
|
192
|
+
formatTerminalReport,
|
|
193
|
+
generateMarkdownReport,
|
|
194
|
+
groupViolationsBySeverity,
|
|
195
|
+
resolveTopSectionColor,
|
|
196
|
+
formatScorecardSection,
|
|
197
|
+
formatCriticalSection,
|
|
198
|
+
formatHighMediumSection,
|
|
199
|
+
formatLowSection,
|
|
200
|
+
formatPillarsSection,
|
|
201
|
+
formatHotspotsSection,
|
|
202
|
+
formatContextAnalysisSection,
|
|
203
|
+
formatFailuresSection,
|
|
204
|
+
formatPassesSection,
|
|
205
|
+
formatGradeFSection,
|
|
206
|
+
formatGradeDSection,
|
|
207
|
+
formatGradeCSection,
|
|
208
|
+
formatGradeBSection,
|
|
209
|
+
formatGradeASection,
|
|
210
|
+
getChemicalXAsciiBanner
|
|
247
211
|
};
|
|
248
212
|
|
|
249
213
|
export default {
|
|
250
214
|
auditFile,
|
|
251
215
|
scanDirectory,
|
|
252
|
-
runAudit
|
|
216
|
+
runAudit,
|
|
217
|
+
formatTerminalReport,
|
|
218
|
+
generateMarkdownReport,
|
|
219
|
+
groupViolationsBySeverity,
|
|
220
|
+
resolveTopSectionColor,
|
|
221
|
+
formatScorecardSection,
|
|
222
|
+
formatCriticalSection,
|
|
223
|
+
formatHighMediumSection,
|
|
224
|
+
formatLowSection,
|
|
225
|
+
formatPillarsSection,
|
|
226
|
+
formatHotspotsSection,
|
|
227
|
+
formatContextAnalysisSection,
|
|
228
|
+
formatFailuresSection,
|
|
229
|
+
formatPassesSection,
|
|
230
|
+
formatGradeFSection,
|
|
231
|
+
formatGradeDSection,
|
|
232
|
+
formatGradeCSection,
|
|
233
|
+
formatGradeBSection,
|
|
234
|
+
formatGradeASection,
|
|
235
|
+
getChemicalXAsciiBanner,
|
|
236
|
+
PILLARS,
|
|
237
|
+
RULE_REGISTRY
|
|
253
238
|
};
|
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 LOC)${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 💯 ($47: 100 Seats) 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 💯 ($47: 100 Seats) 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 💯 ($47: 100 Device Seats / Honor System).`,
|
|
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
|
+
};
|